- People can download the exact files that were published. Opening or downloading
- an artifact does not alter the stored version.
+ Review provides a Download action in standard and full-screen modes. For an
+ artifact with one file, it downloads the original file. For an artifact with
+ multiple files, it downloads a ZIP that preserves every path and byte from the
+ selected immutable version. The server streams the archive; the browser does not
+ rebuild or buffer all published files. Opening or downloading an artifact does
+ not alter the stored version.
diff --git a/project/spec/artifact-server-product-spec.md b/project/spec/artifact-server-product-spec.md
index 5011ee9..2733573 100644
--- a/project/spec/artifact-server-product-spec.md
+++ b/project/spec/artifact-server-product-spec.md
@@ -43,6 +43,8 @@ The server supports `GET`, `HEAD`, byte ranges where applicable, a stable `ETag`
The authenticated Review presents a selected image or video from one exact immutable version through native browser elements. A dedicated same-origin media route supplies typed image or video bytes only to an authorized matching subresource destination, supports ranged video playback, and rejects top-level document navigation. The download-only application route and version-scoped content origins keep their existing security roles. See [Image and video previews](./media-preview-spec.md).
+Review provides **Download** in standard and full-screen modes. For an artifact with one file, it downloads the original file. For an artifact with multiple files, it downloads a ZIP that preserves every path and byte from the selected immutable version. The server streams the archive; the browser does not rebuild or hold all source files in memory. Downloading does not change the selected version or open another Review panel.
+
Client-side sites may load scripts, styles, fonts, images, and data from other websites. Normal browser rules still apply. A remote service must allow the artifact's origin, and a private network may block internet access. Artifact Server does not copy dependencies loaded from other websites.
## Project, artifact, and version model
diff --git a/project/spec/conformance.yml b/project/spec/conformance.yml
index 1dadfbb..b415ce1 100644
--- a/project/spec/conformance.yml
+++ b/project/spec/conformance.yml
@@ -2532,6 +2532,30 @@ requirements:
run: project/evidence/local-foundation.json
recorded_at: "2026-08-27T00:19:45.636Z"
+ - id: CMT-021
+ kind: behavior
+ behavior: Review downloads an artifact's selected immutable version in standard and full-screen modes; one-file versions return the original file, while multi-file versions return a streamed path-preserving ZIP without opening another Review panel.
+ owner: artifact-comments
+ source: {file: artifact-comments-spec.md, anchor: complete-version-downloads}
+ acceptance:
+ behavior: {id: CMT-021-B, description: "Download one-file and multi-file current and historical versions from standard and full-screen Review; verify original filenames, ZIP paths, exact bytes, stable archive metadata, and unchanged Review state."}
+ failure: {id: CMT-021-F, description: "Anonymous, unauthorized, cross-project, cross-artifact, and mismatched-version requests return no bytes, and downloading cannot open comments, change the selected version, or leave full-screen mode."}
+ deployments: *all
+ status: behavior_verified
+ proof_gap: Local service and browser evidence proves exact one-file and multi-file downloads, immutable historical bytes, archive authorization, and unchanged Review state. Equivalent evidence for every team deployment remains unrecorded.
+ depends_on: [CMT-013, CMT-015, VER-001]
+ evidence:
+ - deployment: local
+ tests: [CMT-021-B, CMT-021-F]
+ result: pass
+ run: project/evidence/local-foundation.json
+ recorded_at: "2026-09-03T16:08:06.755Z"
+ - deployment: local
+ tests: [CMT-021-B, CMT-021-F]
+ result: pass
+ run: project/evidence/browser.json
+ recorded_at: "2026-09-03T16:09:11.243Z"
+
- id: ADM-001
kind: behavior
behavior: Artifact Server ships one canonical React application for Review and settings, plus the isolated Review frame; every former application route redirects to the equivalent canonical destination without serving or loading a legacy application bundle.
diff --git a/src/application/artifact-management.ts b/src/application/artifact-management.ts
index 58fc0dd..69e024e 100644
--- a/src/application/artifact-management.ts
+++ b/src/application/artifact-management.ts
@@ -56,6 +56,12 @@ export interface ArtifactDetails {
readonly current: ArtifactVersion;
}
+/** One artifact and one exact saved version selected for a version-level action. */
+export interface ArtifactVersionDetails {
+ readonly artifact: ArtifactRecord;
+ readonly saved: ArtifactVersion;
+}
+
/** Input for reading one artifact through authenticated application policy. */
export interface ReadArtifactCommand {
readonly artifactId: string;
@@ -211,6 +217,9 @@ interface ArtifactManagementOperations {
readonly getVersion: (
command: ReadArtifactVersionCommand,
) => Effect.Effect