Files
narratio/docs/internal/stage-publish.md

133 lines
6.5 KiB
Markdown
Raw Normal View History

# Stage: publish
2026-05-18 22:03:59 -05:00
## Purpose
Upload run/session outputs to object storage and atomically advance remote current state.
## Inputs
- successful preceding stages from the [canonical stage set](overview.md#pipeline-stage-set)
- declared run-local diagnostic files
- resolved publish output rules
- effective publish locks (static + remote merged lock set), revalidated at the
remote commit boundary
- durable previous-session cache files when present
## Outputs
- uploaded session manifest, declared run-local diagnostics, and selected
publish outputs;
- uploaded durable previous-session cache files when present;
2026-08-10 20:05:24 +00:00
- immutable run-scoped commit manifest; and
- current commit pointer, written last.
Exact remote placement and the operator workflow belong in
[Operations](../operations.md#publish-workflow).
## Key Behavior
`publish.go` retains the transaction-owning `Run` path. Source discovery,
manifest-derived archive metadata, confined opening, and checksum validation
live in `publish_sources.go`; artifact-catalog matching, selection, required
and optional rules, destination resolution, and collision inputs live in
`publish_outputs.go`. Copying, hashing, immutable upload, reuse checks, and
remote verification live in `publish_transfer.go`; committed-manifest,
immutable-commit, and current-pointer encoding and writes live in
`publish_commit.go`.
2026-08-10 23:18:29 +00:00
- when publishing or run upload is disabled, completes successfully with no
outputs and records explanatory metadata. This is not an explicit self-skip:
the session manifest records success. Enablement and upload policy are
fingerprinted, so changing either automatically makes the prior result
non-resumable.
- validates prerequisite stage success and object-store availability.
2026-09-03 04:00:25 +00:00
- derives a deterministic run-archive allowlist from the session manifest's
declared current-run outputs, logs, and generated configurations. References
outside the current run root, including canonical session files and prior-run
diagnostics, are not archive candidates. No distinguished diagnostic file is
read or uploaded.
2026-08-10 19:16:18 +00:00
- opens each archive candidate beneath its archive root without following
symlinked ancestors or leaf entries, verifies that it is a regular file and
checks a declared checksum when present, then streams the opened descriptor.
- derives the durable previous-cache archive from its validated manifest using
the same confinement and regular-file checks.
- resolves publish output sources through runtime artifact catalog and
manifest-aware resolution. Configured Scriptorium outputs are publishable
only from validated `current` per-artifact analyze evidence; an incidental
canonical file, aggregate output, stale/failed/unselected record, or
mismatched path, size, or checksum remains unavailable. Built-in and
extraction sources likewise require their own current manifest evidence.
- publishes extraction lanes only through explicit configured output rules;
neither run-local nor durable Notarius bundles are scanned or uploaded wholesale.
- selected artifact filter applies to configured artifact sources only.
- locked outputs are skipped intentionally (including required ones).
- optional missing outputs are skipped; required missing unlocked outputs fail.
2026-08-10 20:05:24 +00:00
- creates one complete immutable source-to-destination mapping before upload;
- uploads and verifies every declared immutable object and the commit manifest;
- updates `current/commit-pointer.json` exactly once, last; and
2026-09-02 13:47:20 +00:00
- writes only the selected `current/commit-pointer.json` control record.
- rechecks remote lock state immediately before the pointer update. A newly
committed lock aborts selection, leaving any uploaded immutable attempt
unselected.
2026-08-11 03:43:18 +00:00
- reads the mutable remote lock document through a direct limit-plus-one read
capped by `MaxRemoteLockStoreBytes` (1 MiB), retaining the generation returned
with the opened body for conditional updates. Oversized lock documents fail
before YAML decoding; published artifact payloads do not use this limit.
## Metadata Signals
Includes counts/lists for:
- run uploads
- published output uploads
- previous uploads
- skipped optional outputs
- skipped unselected outputs
- locked outputs
2026-08-10 20:05:24 +00:00
- remote commit and current-pointer key paths
- the run identifier selected by the commit
## Invariants
2026-08-10 20:05:24 +00:00
- `current/commit-pointer.json` is the remote commit marker and is written last.
- run files, selected outputs, previous-cache files, and the committed session
manifest are all declared by an immutable commit under the run prefix.
2026-08-10 19:16:18 +00:00
- run and previous uploads contain only manifest-declared regular files opened
from verified descriptors; symlinks, special files, replacement races, and
undeclared entries are rejected or ignored before uploads begin.
- run-local diagnostics, including Notarius receipt and stderr files, are
2026-09-03 04:00:25 +00:00
archived only when recorded by the session manifest. A run with no declared
current-run diagnostics legitimately uploads none.
- publish locks are not overridden by `--force`; remote locks are revalidated
immediately before current-state selection.
- post-commit local cleanup is authorized by the committed publish metadata and
is durably recorded by the application lifecycle before any local deletion.
## Resume Semantics
The versioned semantic fingerprint covers enabled behavior, run-upload policy,
normalized source/destination/required output rules, static lock policy, and
the remote backend, bucket, region, endpoint, and root-prefix identity. Rule
and lock ordering is canonicalized. Credential environment names,
path-addressing transport mode, local workspace placement, and run identifiers
are excluded. Remote locks remain mutable state and are still revalidated at
the commit boundary; semantic evidence does not replace that safety check.
The commit boundary and cleanup gate are normative architecture invariants; see
[Architecture](../policy/architecture.md#publish-commit-boundary).
## Related Contracts And Tests
- [Configuration](../config.md#publish-configuration-summary) owns output and
static-lock fields.
- [Operations](../operations.md#publish-locks) owns remote lock lifecycle and
physical remote state.
- [Artifact Internals](artifacts.md) explains source resolution and current-state
helpers.
- Implementation and tests: `internal/stage/publish.go`,
`internal/stage/publish_sources.go`, `internal/stage/publish_outputs.go`,
`internal/stage/publish_transfer.go`, `internal/stage/publish_commit.go`,
`internal/stage/publish_test.go`,
`internal/stage/semantic_contracts_delivery.go`,
`internal/app/operator_helpers_test.go`, and
`internal/app/post_publish_cleanup_test.go`