Controlled export of plot images and R scripts
Source:vignettes/articles/11-controlled-export.Rmd
11-controlled-export.RmdControlled export publishes the accepted plot.png and
script.R pair to a registered workspace destination and
retains a receipt inside the evidence repository. The current
implementation permits only Reviewed revisions with an
accepted artifact bundle. The local workspace provider is explicitly
development-only.
The historical diagram separates workspace publication, internal
receipt recording, and ZIP creation; its labels show the earlier
two-button interface. The publication details were originally inspected
at 5e2fa6a; the interface and download description include
the combined action. This guide does not establish target-environment
qualification.
User actions and eligibility
The Export step contains revision history, review, and controlled
export, following the five-step
workflow. mod_export_ui() offers an actor selector, a
logical destination field, and one Export and download
ZIP action, disabled until both approvals are recorded.
mod_export_server() sends the current revision identifier
and version to new_export_service(), then creates the ZIP
and displays the result and receipt table. Export or ZIP errors appear
as messages.
For a new export command, .authorize_export_actor()
requires an active submission_sync_actor with at least one
of clinical_scientist, statistical_programmer,
biostatistician, or export_publisher. A
creator can export; the independent-reviewer restriction does not apply
here. Local actor selection does not authenticate a person, despite the
authorization error referring to an authenticated actor.
.assert_exportable_revision() compares the expected
version with the stored version and allows only Reviewed.
repository$get_artifact_bundle() must find an accepted
bundle; status alone is insufficient. A normal failed execution without
acceptance therefore has no exportable pair. See execution and human review for their
separate boundaries.
Publication and retained records
export_revision() in export_service.R
performs these operations:
- Validate identifiers and derive a receipt identifier from the idempotency key.
- Authorize the actor and check status and version. If a matching
receipt already exists, return it; otherwise obtain the accepted script
and image hashes. Read their bytes through the artifact store.
local_artifact_store()$get()verifies content hashes before returning bytes. - Inspect the destination for an already-published pair. Otherwise
stage the bytes through
workspace_provider$stage_bundle(). - After staging, retrieve and authorize the actor again, reread the
revision, and recheck status and version. If revision hashes differ from
the accepted bundle, quarantine the stage as
stale-revisionand abort. - Publish the stage, compare final hashes with the accepted bundle, and record the internal receipt.
local_workspace_provider()
writes script.R, plot.png, and a temporary
manifest.json beneath
.staging/<export_id>. publish_staged()
hashes both staged files again. Changed bytes cause quarantine and
failure. It removes the manifest, moves the directory to its final
location, and checks the published hashes. Only the script and PNG
remain in the published directory.
record_export_receipt() in evidence_repository_sqlite.R
checks receipt hashes against the immutable revision and writes the
receipt identifier, revision identifier, logical destination,
script/image hashes, timestamp, and idempotency key. This record stays
internal; no receipt, selected input data, review decisions, or
environment record accompanies the detached files.
Retries, recovery, and downloads
The receipt lookup makes repeated successful commands inspect and hash-check the previously published pair, then return the same complete path-bearing result without republishing after fresh actor, status, and revision-version checks. Reusing the idempotency key for another destination is rejected.
Publication precedes receipt recording. If receipt recording fails after publication, a retry can inspect the existing pair, compare hashes, and record the receipt. reconcile_workspace_exports() separately quarantines leftover staging directories; it does not complete their publication or create receipts. It is not wired into automatic application startup.
.write_export_zip() in mod_export.R
verifies that the export returned by the service belongs to the current
revision, copies its two files, and invokes utils::zip().
Every download first calls the export service, which rechecks the actor,
status, version, and hashes. A successful retry reuses the existing
receipt and published pair.
Policy differences and operating limits
Strategy and the draft-status
design learning require both independent approvals before export.
The service enforces this by accepting only Reviewed
revisions.
The pair is also not a complete reproduction package. compile_boxplot_script()
requires caller-supplied analysis_data and the controlled
package environment. Export copies that script unchanged; another
machine still needs the governed input and compatible environment to
reproduce the accepted plot.
Logical tokens reject traversal syntax and Windows reserved names; destinations must be registered existing roots. The provider checks the root for links/reparse points but does not establish qualified descendant-path, filesystem-race, access-control, or custody guarantees. app_server() creates the workspace under temporary storage. See integrity and lifecycle for retained-evidence controls.
Inspected coverage
Tests inspected: service, workspace contract, module and ZIP, reconciliation, and browser harness. They cover publication, receipts, repeated commands, actor/version/destination denials, experimental exclusion, missing bundles, ZIP contents, and orphan staging. No R tests or browser journeys were run for this documentation change; coverage inspection is not a passing result.
