# Operations Guide Operator workflow for running, recovering, and publishing Narratio sessions. For command syntax, see [docs/cli.md](./cli.md). For field-level config, see [docs/config.md](./config.md). ## Campaign and Session Selection Campaign selection priority: - `--campaign-file` - `--campaign` - `pipeline.campaigns.default_campaign_id` Session source priority: - `--session` - local default search paths - remote session object (S3) when local session file is not found and storage is configured ## Session Initialization Use `session init` to generate a concrete session file for local or remote use. Local file: ```bash narratio session init 2026-04-04 --output ./session.yml --date 2026-04-04 --title "Session 12" ``` Remote session object: ```bash narratio session init 2026-04-04 --remote --force ``` If `campaign.yml` sets `session_template_file`, `session init` renders it. Template variables must resolve to concrete values. Campaigns must provide stable input files for speakers, autocorrect, glossary, players, and party, and may provide an optional spell-catalog overlay. Session files may override those paths for one session. The `prepare` stage materializes them under `inputs/`; configured consumers use the prepared files, never the original campaign or session source paths. Field definitions and source IDs are in [Configuration](./config.md#notarius-reference-bindings). ## Standard Session Workflow 1. Select pipeline/campaign/session config. 2. Validate session readiness: ```bash narratio session validate 2026-04-04 ``` 3. (Optional) inspect stage decisions: ```bash narratio session plan 2026-04-04 ``` 4. Run the pipeline: ```bash narratio run 2026-04-04 ``` 5. Check state: ```bash narratio session status 2026-04-04 ``` Run, plan, and status output identify the resolved pipeline overlay (or `none`) and effective configuration digest. Status distinguishes the current resolved value from the last value persisted in the session manifest, which helps diagnose overlay switches without changing resume authority. Before switching an operational overlay, compare its effective meaning with the production base through `narratio config diff default `. The command is read-only and succeeds whether it finds differences or not. Its sorted records describe defaulted, expanded concrete configuration—not source file layout—so it can be used to review model, artifact, and publish changes without creating a session or run. Use `narratio config artifacts` to review the concrete artifact identities, definitions, outputs, and dependencies. Select the same campaign explicitly when overlays could resolve different campaign paths; see the [CLI reference](cli.md#config-validate-config-show-config-sources-config-artifacts-and-config-diff) for syntax and record format. ## Stage Execution and Continuation Behavior Canonical stage order: 1. `prepare` 2. `transcribe` 3. `merge` 4. `polish` 5. `normalize` 6. `trim` 7. `render` 8. `extract` 9. `analyze` 10. `publish` 11. `notify` 12. `cleanup` Execution rules: - succeeded stages are skipped unless `--force` is set; stages with semantic configuration contracts additionally require matching versioned evidence; - `run` continues interrupted or partially completed sessions by running non-succeeded stages; - forcing a stage marks succeeded transitive dependents as `stale` before the replacement runs; render and extract are independent siblings; and - an executed failure, changed self-skip, or success that replaces a different effective outcome uses the same fixed dependency relation. A repeated self-skip with the same reason and no outputs is stable and does not perpetually rerun dependent work. Every aggregate stage except analyze currently provides semantic-configuration evidence; transcribe additionally reconciles per-speaker completion evidence, while analyze retains its more precise per-artifact fingerprints and validator. Prepare additionally validates current stable/local/S3 source identity and the checksums of its durable prepared copies before reuse. Changed bytes, audio membership, S3 object identity, or missing/tampered copies rerun prepare and its fixed descendants without requiring `--force`. Changing prepare selection semantics likewise reruns all fixed descendants; changing WhisperX language/service identity reuses prepare; changing a Seriatim merge transformation reuses prepare and transcribe; and changing a selected Audita overlay, profile, or module sequence reuses prepare, transcribe, and merge while rebuilding transcript refinement. A trim change invalidates both render and extract through the fixed dependency relation, while a render-only change preserves the extract sibling. Operational timeouts, retry/concurrency tuning, executable paths, workspace/cache/spool placement, reports, diagnostics, and secret values are excluded. Configuration, models, prompts, modules, or resources loaded privately inside external tools remain unobservable to Narratio. If their contents change behind the same configured identifier, explicitly force the affected stage. An explicit self-skip is a durable `skipped` stage outcome that later runs reconsider. It differs from successful no-output execution: disabled `render` and `publish`, and absent or no-executable `analyze`, record `succeeded` with metadata and no outputs. Ordinary later runs reuse those successful results; force the affected stage after enabling or configuring it. Optional artifact inputs are omitted only from the consuming artifact invocation and do not make the stage self-skip. Single-stage execution: ```bash narratio run-stage normalize 2026-04-04 --force ``` Contiguous bounded execution uses inclusive canonical endpoints: ```bash narratio session plan 2026-04-04 --from extract --through analyze --force narratio run 2026-04-04 --from extract --through analyze --force ``` Omitting `--from` selects from `prepare`; omitting `--through` selects through `cleanup`. Force applies only within the selected range. Repeating `--from`, `--through`, or `--force` is rejected instead of resolving by argument order. The plan command uses the same selection contract and prints only the selected range. Planning is read-only: it clones the loaded manifest, models selected stage transitions and invalidation in memory, and invokes resume validation without writing the manifest, creating run directories, materializing files, or invoking pipeline adapters. Analyze detail separates explicit targets, prerequisite rebuilds, scheduled execution, and current reuse. This lets a coarsely stale aggregate analyze stage show zero artifact executions when its selected artifact evidence is still semantically current. Before a bounded run or plan whose range starts after `prepare`, every excluded prefix stage must already have a session-manifest status of `succeeded` or `skipped`. Narratio reports the first absent, pending, running, failed, stale, or interrupted prerequisite without creating a run-local directory or changing session state. Widen `--from` to include that stage, or recover it explicitly before retrying. Excluded prefix stages are not resume-validated or repaired as part of the bounded invocation; selected stages still reject missing, unsafe, or manifest-inconsistent inputs at their owning boundary. Stages after `--through` are not prerequisites and are never scheduled by the bounded invocation. A selected forced stage can mark one of those succeeded dependents stale through the fixed invalidation relation, but the dependent does not execute until a later invocation selects it. Production composition likewise initializes only collaborators needed by the selected range and shared session lifecycle. In particular, render does not require Notarius or Scriptorium, extract does not require Scriptorium, and analyze does not require the transcription, Seriatim, Audita, or Notarius adapters. ### Transcription continuation and replacement Transcribe checkpoints each validated speaker independently. If a later speaker exhausts HTTP retries, the process is canceled, or the aggregate stage save fails, the aggregate stage is not successful and downstream merge cannot consume the partial set. The last successfully saved speaker records and their canonical `transcripts/raw/.json` files remain durable, however. After correcting the underlying problem, retry normally: ```bash narratio run-stage transcribe 2026-04-04 ``` Narratio hashes the current prepared audio, validates recorded transcript JSON, checksum, size, and location, and calls WhisperX only for missing or obsolete speakers. A changed audio file replaces only its speaker; a changed WhisperX URL or language makes every current speaker non-reusable. Retry, timeout, and concurrency tuning do not invalidate completed speakers. Use force only when every current transcript must be replaced, including after a WhisperX model or private server-side configuration changes behind the same URL and language: ```bash narratio run-stage transcribe 2026-04-04 --force ``` Force durably clears the reusable collection before sending requests. If that forced attempt fails, a later ordinary retry reuses only replacement speakers checkpointed after the reset; it does not revive pre-force results. Do not edit speaker checksums or copy older JSON into the canonical directory to influence reuse. The first current Narratio invocation that selects transcribe for an older aggregate-only manifest retranscribes every current speaker once because the old record cannot prove which audio bytes produced each file. Subsequent runs reuse the new versioned evidence normally. The overall session-manifest schema identifier is unchanged, but older strict-decoding Narratio binaries may reject a manifest after current Narratio writes the additive speaker fields; retain a backup or complete the upgrade before relying on downgrade. Restoring a manifest does not implicitly publish or restore every raw speaker file. If a recorded canonical transcript is absent locally, ordinary reconciliation schedules that speaker again. Removing run directories does not remove canonical checkpointed transcripts and does not by itself invalidate them. ## Artifact Development Workflows Choose the narrowest workflow that matches the intended replacement: | Intent | Command | Preserved and rebuilt state | | --- | --- | --- | | Rebuild analysis only | [`narratio analyze 2026-04-04`](./cli.md#analyze) | Preserves transcripts and the current extraction bundle; force-runs selected analysis artifacts and their required analysis prerequisites. | | Rebuild extraction and analysis | [`narratio regenerate-artifacts 2026-04-04`](./cli.md#regenerate-artifacts) | Preserves transcripts; force-runs the complete configured extraction bundle, then selected analysis. `--artifacts` narrows analysis only. | | Continue bounded work normally | [`narratio run 2026-04-04 --from extract --through analyze`](./cli.md#run) | Uses ordinary resume decisions: current extraction and analysis can be skipped, while incomplete or obsolete work runs. | All three stop before publication. Inspect the results and run [`narratio publish`](./cli.md#publish) separately when delivery is intended. For the extraction-plus-analysis development loop, use: ```bash narratio regenerate-artifacts 2026-04-04 narratio regenerate-artifacts 2026-04-04 --artifacts session_recap,player_handout ``` This command is a transparent expansion to a forced bounded `run` from `extract` through `analyze`. Extraction always rebuilds its complete configured bundle. Analysis rebuilds the selected targets and their required analysis prerequisites, or uses the normal default selection when no artifact names are given. The command does not run publish, notify, or cleanup; delivery and retention remain separate operator actions. Inspect current artifact evidence, then publish explicitly when the regenerated set is ready: ```bash narratio session artifacts 2026-04-04 narratio publish 2026-04-04 ``` If planning or execution reports stale, missing, failed, or tampered analysis evidence, regenerate the affected target instead of copying an older canonical file into place or editing the manifest. See [Troubleshooting: Analysis artifact evidence is not current](./troubleshooting.md#analysis-artifact-evidence-is-not-current). Analyze checkpoints each completed configured artifact before starting later artifact work. After a partial failure or interruption, retry normally: valid earlier checkpoints are reused and execution resumes with failed, stale, or missing work in dependency order. `--force` is unnecessary unless the operator intends to replace otherwise-current artifacts. ### Developing With Audita And Notarius Overlays All three applications use ConfigKit, but each owns its root configuration and its own `overlays/` directory beside that file. A Narratio testing overlay can explicitly select downstream testing overlays without requiring their names to match. For example, Narratio's `overlays/testing.yml` may contain: ```yaml audita: overlay: testing notarius: config_overlay: downstream-economy ``` Place Audita's settings in `overlays/testing.yml` beside its configuration, and Notarius's settings in `overlays/downstream-economy.yml` beside its configuration. Use each tool's own schema for model/profile selection and other settings; Narratio neither copies those settings nor implicitly forwards its overlay name. Select the Narratio file with `--overlay testing` on the desired command. Each downstream selector is used only when its stage actually executes: `polish` for Audita and `extract` for Notarius. Production can omit both selectors. For Audita debug inspection, its testing overlay can include: ```yaml diagnostics: debug: true work_dir: /path/to/audita-debug ``` Audita retains detailed artifacts under that directory. Inspect them there; Narratio does not collect or manage them. Ordinary Audita reports remain a separate existing integration. Leave debug disabled in Audita's production configuration unless deliberately needed. Notarius's debug controls are different: `debug.directory` selects a location but does not enable capture. Capture requires its `--debug` flag, which Narratio does not currently forward. Selecting a Notarius testing overlay therefore does not by itself enable a debug bundle. See the upstream [Audita configuration](https://gitea.maximumdirect.net/eric/audita/src/tag/v2.0.1/docs/config.md) and [Notarius CLI](https://gitea.maximumdirect.net/eric/notarius/src/tag/v0.8.1/docs/cli.md) for their respective controls. Edits inside downstream configuration behind an unchanged selector are not automatically detected by Narratio. To inspect a fresh Audita debug run, explicitly rerun polish, for example: ```sh narratio run SESSION --overlay testing --force --from polish --through polish ``` This replaces polish output and invalidates dependent results; it is not a diagnostics-only operation. Selecting testing does not create an isolated workspace or retain a separate current result. Notarius v0.8+ defaults to two additional eligible transient provider attempts per generation. Review downstream retry settings and Narratio's extraction timeout together; retries can increase elapsed time and provider charges. Notarius v0.8.1 refreshes its built-in profile checkpoint identity, so older checkpoints may not be reused. Source-keyed chunk plans and already published outputs are unaffected; no manual cache deletion is required. Selecting Narratio's `testing` overlay then supplies `downstream-economy` only when extraction executes. Switching that explicit selector makes a recorded extraction non-current and reruns it when `extract` is in the selected stage range; the usual dependent invalidation then applies. Selecting an overlay does not create a separate workspace or retain a separate current extraction result. Narratio cannot observe edits inside Notarius configuration behind an unchanged selector. Force extraction after such an edit when a replacement is intended. Use `regenerate-artifacts` when both extraction and analysis must be rebuilt; use `analyze` when the existing extraction bundle should be preserved. Field semantics belong in [Configuration](./config.md#notarius-configuration-overlay), with Audita selectors covered under [Audita selection and credentials](./config.md#audita-selection-and-credentials). Exact downstream invocations belong in the [Audita](./integrations/audita.md) and [Notarius](./integrations/notarius.md#invocation) integration contracts. ## Artifact Selection For a configured artifact family, selecting its family key expands to every concrete character artifact. Select a concrete generated key to operate on one member only. Manifests and plan output retain the concrete key as the durable identity and include the family and character ID as optional provenance. `--artifacts` can be used on `run`, `session plan`, `run-stage`, `analyze`, and `publish`. For a bounded run or plan, the selected range must contain `analyze` or `publish`. Selection behavior: - validates names against `pipeline.scriptorium.artifacts`; - selects explicit analyze targets and permits their required configured prerequisites to be reused or rebuilt first; - filters publish rules for `narratio.artifact.` sources only; - does not suppress built-in transcript, bounds, or explicitly configured `narratio.extraction.` publish sources; and - never partially selects Notarius lanes. ## Extraction Workflow When Notarius is omitted or disabled, `extract` records an explicit skipped outcome with reason `notarius_disabled` and no outputs. A later invocation reconsiders the skipped stage, so enabling Notarius does not require force. When Notarius extraction is enabled, the stage consumes the final trimmed JSON and preserves the complete validated Notarius bundle at: - `artifacts/notarius/{narratio_run_id}/` The directory is immutable once promoted. Configured lanes become `narratio.extraction.` sources for Scriptorium and explicit publish rules; the bundle and `index.json` are retained for audit and resume validation but are not selectable or published implicitly. Configured Notarius references resolve only from the current manifest-backed prepared inputs. Their canonical locations are `inputs/party.yml`, `inputs/players.yml`, `inputs/glossary.yml`, and, when configured, `inputs/spell_catalog.json`. Extraction supplies Notarius with verified, content-addressed copies under `cache/notarius/references//`. The full lowercase SHA-256 digest identifies the reference bytes, so retries and later invocations reuse the same path while changed bytes select a new path. Older snapshots are retained and are not current artifact evidence. A concurrent refresh of canonical prepared files therefore cannot change the bytes consumed by an in-flight invocation. For a canonical party, preparation retains the validated authored party bytes at `inputs/party.yml` and generates `inputs/players.yml` from that roster. The manifest records their checksums separately, with the players input marked as derived from the party; refresh preparation after changing the roster rather than editing either prepared file. Inspect the effective stable-input inventory and prepared-file readiness with: ```bash narratio session status 2026-04-04 narratio session validate 2026-04-04 ``` Reference metadata records selector, source ID, session-relative path, checksum, and byte size, but never payload contents. Changing a prepared reference changes extraction identity: ordinary continuation rejects the old result, reruns Notarius, and marks successful downstream stages stale. If the prepared file is missing or inconsistent with its manifest checksum, repair the source configuration and refresh prepared state first: ```bash narratio run-stage prepare 2026-04-04 --force ``` Starting a replacement clears the previous extraction payload from the current session-stage record. If that replacement fails or self-skips, the current record does not fall back to the earlier outputs. The earlier immutable bundle remains available for inspection, but downstream resolution requires a new current successful extraction record. Notarius extraction validates its run-local bundle and moves it once to the unique durable bundle path. A pre-existing durable destination is a collision; no duplicate promotion tree is created. `pipeline.notarius.resume` and `pipeline.notarius.chunk_cache` control work inside an executed Notarius invocation. Ordinary extraction forwards their configured values. Forced extraction always disables checkpoint loading and requests chunk-plan `refresh`, even when the configured mode is `bypass`. Refresh regenerates and replaces only the active input's accepted chunk plan; it is not cache deletion. Notarius checkpoint recording must be enabled in the deployed Notarius configuration. Narratio neither creates a Notarius pipeline/configuration nor reads it to infer checkpoint availability. Configure explicit persistent Notarius chunk-plan and checkpoint roots when reuse must survive separate Narratio subprocesses. A changed reference, transcript, selected profile, profile content, endpoint, or other Notarius checkpoint identity input can produce a legitimate cold miss. There are two independent resume boundaries: Narratio may skip a completed `extract` stage without invoking Notarius, while `--resume` allows Notarius to reuse compatible accepted work only after Narratio has decided extraction must execute. Deleting Narratio's local reference snapshots does not invalidate a complete durable extraction; missing snapshots are reconstructed only when a later extraction actually runs. ## Migrating Manifest Overlay Provenance The pipeline-selection rename is a clean cutover. Manifests containing `effective_config.selected_profile` are rejected by strict decoding; Narratio does not silently discard or translate that retired field. Before using an affected local session with the new version: 1. Stop Narratio activity for the session and back up its manifest. 2. Rename only `effective_config.selected_profile` to `selected_overlay`, keeping its `name` and `source`, or remove the retired informational member. If a current `selected_overlay` member already exists, retain it and remove the retired member rather than creating duplicate keys. 3. Preserve the schema version, digest, stage records, checkpoint evidence, output references, and other session state. Retry the intended command. This metadata is informational, not resume authority. Do not delete session progress or force all stages merely to perform this migration. Other unknown fields still require investigation rather than blanket removal. Do not edit immutable remote committed snapshots. Restoring an older snapshot with retired provenance requires a compatible previous Narratio version, followed by migration of the restored local manifest before using the new version. ## Migrating Audita and Provider Credentials This is a breaking clean cutover to Audita v2. Narratio does not translate an Audita v1 configuration, retain old field aliases, or generate a downstream configuration. Update both the deployed Audita executable/configuration and the Narratio pipeline before running polish. Retired Narratio fields are rejected by strict configuration loading; account for this break when choosing a release version. | Previous Narratio setting | New ownership and selection | | --- | --- | | `audita.base_url` | Audita v2 `backends..endpoint` | | `audita.total_llm_concurrency`, `proposal_llm_concurrency`, `validation_llm_concurrency` | Audita v2 `backends..concurrency` and optional `queue_capacity`; backend capacity is shared across roles | | `audita.model`, `validation_model` | Audita PromptKit profile files; optionally select them from Narratio with `audita.proposal_profile` and `validation_profile` | | `audita.llm_api_key` | `audita.credentials: [PROVIDER_KEY_NAME]`, matching each Audita backend's `credential` name | | `notarius.llm_api_key` | `notarius.credentials: [PROVIDER_KEY_NAME]`, matching references in Notarius-owned profiles | | `audita.work_dir_retention`, `report` | Remove; Audita's external configuration owns diagnostics and reporting | An Audita configuration can also be selected through `audita.config_path`, and `audita.overlay` selects an Audita overlay independently of Narratio's `--overlay`. Narratio forwards no implicit provider key: a single provider-named secret source may be listed for both adapters, and an Audita-only backend key may be listed only for Audita. Audita resolves credentials for all configured backends, including those not currently selected. Notarius looks up its own profiles; Narratio does not discover their credential names. Credential lists may be cleared with `[]` in a Narratio overlay. Prepare the external file from Audita's maintained [local example](https://gitea.maximumdirect.net/eric/audita/src/tag/v2.0.0/examples/minimal-config.yml), [service example](https://gitea.maximumdirect.net/eric/audita/src/tag/v2.0.0/examples/production-config.yml), and [v2 migration guide](https://gitea.maximumdirect.net/eric/audita/src/tag/v2.0.0/docs/migrations/v2.md). Before switching the executable, validate the file offline: ```bash audita config validate --config /usr/local/etc/audita/config.yml ``` Use `audita config print-effective --config /usr/local/etc/audita/config.yml --view profiles` to inspect downstream profile selection without contacting a provider. For a production Audita configuration, leave ordinary reports enabled and debug disabled unless an operator deliberately chooses otherwise; Narratio does not override those settings. Audita writes `errors.yml`, `recoveries.yml`, and `report.md` beside the polished output when reporting is enabled. An exit-zero run with a valid transcript succeeds even if those reports describe recoverable errors or no applied corrections. See the [Audita integration contract](./integrations/audita.md) for report placement and limits. Narratio's polish resume evidence tracks selected config path, Audita overlay, profiles, ordered modules, description, schema, and output identity. Editing Audita-owned files behind unchanged selectors is invisible to Narratio; force polish with `narratio run-stage polish --force` after such edits. ## External Command Lifecycle When an external command is cancelled or times out, Narratio terminates its owned descendants as well as the command itself. Cancellation first requests termination where the platform supports it, then force terminates after a bounded wait. A command is not considered finished until its leader has been reaped, and descendants that keep standard output or error open cannot keep the invocation blocked. Other operating systems fail closed rather than launch a command without tree ownership. Subprocess stdout and stderr diagnostics are separately redacted and capped at 8 MiB per invocation. Narratio does not retain configured credential values in these logs or their error tails; reaching a capture limit terminates the command tree and reports which stream exceeded the limit. Run-local diagnostics are: - `runs/{run_id}/polish/outputs/transcripts/errors.yml`, `recoveries.yml`, and `report.md` when Audita writes them beside its run-local polished transcript - `runs/{run_id}/extract/notarius.receipt.json` - `runs/{run_id}/extract/notarius.stderr.log` - `runs/{run_id}/extract/notarius-output/` before durable promotion The run upload is an allowlist derived from the session manifest's declared current-run outputs, logs, and generated configurations, not a workspace scan. References to canonical session files and prior-run paths are excluded. Each declared source is opened without following symlinked ancestors or the leaf, verified as a regular file, and streamed from that verified descriptor. Unlisted files and unsafe entries are never uploaded; a run with no declared current-run diagnostics uploads no run-local files. No distinguished diagnostic file is uploaded. The durable bundle is never scanned for implicit publication; only lanes named by explicit `pipeline.publish.outputs` rules are uploaded. To inspect a published polish report remotely, read its declared object under the run archive's `polish/outputs/transcripts/` prefix. Session restore does not restore run-archive diagnostics as canonical session files; it restores only the committed manifest and declared transcript/artifact objects. To intentionally replace the current extraction result, run: ```bash narratio run-stage extract 2026-04-04 --force ``` Narratio automatically reruns extraction when its recorded invocation contract, prepared Narratio reference identities, or durable output validation changes. The semantic portion covers Notarius enablement, pipeline identity, declared reference mapping, output contracts, and the explicit Notarius configuration profile selector. Executable, timeout, working directory, and private config-file paths are operational and do not invalidate a current result. It cannot fingerprint configuration or profile contents, prompts, modules, or other references loaded transitively by Notarius itself. Force extraction after changing any of those inputs behind an unchanged selector, even when the top-level Narratio and Notarius config paths remain the same. A forced extract marks successful downstream stages stale. Ordinary extraction failures or outcome changes also stale affected downstream stages, while an identical repeated `notarius_disabled` self-skip does not repeatedly invalidate them. Publish reuse additionally tracks enabled/run-upload behavior, normalized output rules, and remote backend/bucket/region/endpoint/root identity. Credential environment names, local workspace placement, and run IDs are excluded. Regardless of semantic reuse evidence, executing publish reads independent remote locks directly and rechecks initially unlocked sources before commit selection. ## Publish Workflow Run publish only: ```bash narratio publish 2026-04-04 ``` Equivalent: ```bash narratio run-stage publish 2026-04-04 --force ``` This standalone command stops after publish and does not invoke cleanup. A default full `run` continues through notify and cleanup. Publish commit model: - uploads eligible run files under `{session_prefix}/runs/{run_id}/`, excluding audio and the run-local Notarius staging bundle; - uploads configured published outputs and `previous/**` cache files into the same immutable run scope, including only explicitly configured extraction lanes; - writes `{session_prefix}/runs/{run_id}/commit.json` after all declared immutable objects are uploaded and verified; and - writes `{session_prefix}/current/commit-pointer.json` once, last. `current/commit-pointer.json` is the remote current-state commit marker. It selects exactly one immutable commit, which declares the complete object set. ## Remote Commit Migration The immutable remote commit contract uses `runs/{run_id}/commit.json` to declare a run's complete object set and a small `current/commit-pointer.json` to select it. The pointer binds the selected commit by version, checksum, size, and storage generation; committed artifacts are also checksum- and generation-bound. Readers accept this contract now and strictly reject mismatched or unknown data. A selected commit may carry a `published_output` declaration from an earlier run in the same campaign/session. Restore, previous-session caching, status, and artifact inspection use that declaration's exact immutable key and stable source; they do not reconstruct a current-run key or scan the session prefix. Other artifact types remain confined to the selected commit's own run. When the pointer is absent, remote state is not published; republish the session to establish a committed snapshot. Remote commit state does not carry local `current_pointer_written` metadata. ## Publish Locks Each mutable lock is an independent strict JSON object at `{session_prefix}/locks/{sha256(source)}.lock`. The lowercase digest is computed from the exact canonical source. Listing validates every object in that reserved prefix and reports locks sorted by source. Upgrades do not read, migrate, or delete the retired `{session_prefix}/locks.yml` object. It has no effect on publication and may be removed manually after verifying lock commands and publication behavior with the upgraded Narratio version. No migration command is required. Operational rules: - add uses conditional creation and reports an existing lock; - add with `--force` observes the current generation and attempts one generation-matched replacement. A concurrent change is reported for an explicit operator retry; - remove is idempotent and reports whether a lock was present; - the initial publish lock snapshot controls output planning. A locked source carries its exact declaration from the prior current commit when available; a never-published locked source remains absent even when required; - carried bytes are not copied. Publish verifies the prior object's exact key, size, and generation without downloading it; and - after immutable uploads and the immutable commit, publish re-reads every initially unlocked participating source. A newly added lock prevents the current pointer from advancing, although the immutable attempt may remain. A removed initial lock may conservatively cause one carry. Examples: ```bash narratio session locks 2026-04-04 narratio session locks add 2026-04-04 narratio.artifact.session_recap --reason "manual edits" --force narratio session locks remove 2026-04-04 narratio.artifact.session_recap ``` ## Restore Workflow Use restore when local durable session state is missing or stale and remote committed current state is authoritative. Dry run: ```bash narratio session restore 2026-04-04 --dry-run ``` Apply: ```bash narratio session restore 2026-04-04 ``` `--dry-run` does not write durable session files. It still reads the selected remote current state and may read object identity/content needed to classify the plan, so it is not a network-free operation. Default restore scope: - the committed session manifest and the committed transcript/artifact objects declared by the selected remote commit - `previous/**` when needed by configured previous-session artifact inputs; each is selected from its exact committed `published_output` declaration Optional: - `--include-audio` to include `audio/**` - `--force` to overwrite eligible conflicting regular files; it never replaces directories, symlinks, or other non-regular local targets Restore treats a local leaf symlink or any symlinked ancestor beneath the selected session root as a conflict, even when the linked content matches the selected remote object. `--force` does not override that conflict. Before each install, restore validates the destination again beneath the selected session root. A target changed into a symlink, directory, or other non-regular entry is not overwritten. Restore writes an execution report at `reports/restore-latest.json`. If restore fails after beginning installation, it leaves a durable `.restore-incomplete.json` marker in the session root. Pipeline runs will stop until you rerun the same restore command and it completes. Restore intentionally does not try to roll back files already installed; retrying the selected remote snapshot is the recovery procedure. ## Local State Layout Session root: - `{workspace.root}/work/{campaign}/{session_id}` Durable session paths: - `manifest.json` - `inputs/**` - `audio/**` - `transcripts/**` - `artifacts/**` - `previous/**` - `reports/**` - `logs/**` - `config/**` - `runs/**` - `cache/notarius/references//` `manifest.json` is the versioned session-progress document. Narratio accepts only `narratio.session-manifest.v1`. Do not edit unsupported session state to add missing markers. Move it aside and regenerate it, restore a verified current committed snapshot, or republish healthy regenerated state as the relevant workflow requires. Validated Notarius bundles live below `artifacts/notarius/{run_id}/`; receipt, stderr, and pre-promotion output remain in the producing run's `extract` directory as described in [Extraction Workflow](#extraction-workflow). Stable reference snapshots live in the session cache path above. Ordinary published-run cleanup removes its selected run-local target but retains this cache, and publish/restore never enumerate it as an artifact. Remove a snapshot or digest directory manually only while no command is active for the session; regular missing or corrupt content is reconstructed on the next executed extraction, while symlinks, directories, and other unsafe conflicts must be investigated and removed deliberately. Run-local layout: - `runs/{run_id}/{stage}/outputs` - `runs/{run_id}/{stage}/logs` - `runs/{run_id}/{stage}/reports` - `runs/{run_id}/{stage}/config` - `runs/{run_id}/{stage}/scratch` Spool layout (runtime/transient): - `{spool.root}/{campaign}/{session_id}/{run_id}/...` - restore audio spool under `{spool.root}/{campaign}/{session_id}/restore/audio` Cache layout (durable S3 audio cache): - `{cache.root}/s3/{bucket}/...` Each cached audio file has an adjacent managed identity record. It binds the file to its remote object version and verified digest; deleting or altering the record simply causes Narratio to download and verify the object again. Remote audio and restored files are fully transferred into sibling temporary files and validated before they replace their canonical workspace paths. ### Workspace Permissions Ordinary Narratio workspace content is intentionally shareable with the workspace group. On POSIX systems, Narratio-created workspace, spool, and cache directories converge on setgid `02775`; ordinary files, including manifests, transcripts, generated configuration, logs, reports, and Notarius artifacts, converge on `0664`. Narratio explicitly applies these modes so a restrictive caller umask does not remove group write or setgid. It does not change file or directory ownership: the configured workspace's existing group is inherited. API keys are credentials, not ordinary workspace data. Store them outside the shared workspace or in a separately restricted credential location; ordinary workspace group access must never be treated as authorization to read keys. Provision a credential directory as `0700` and credential files as `0600`; these permission modes are an operator requirement and are not enforced by ConfigKit. Every pipeline-loading command bootstraps credentials once, before the first configuration load and before any adapter is constructed. `--secrets-dir` selects an explicit directory (relative paths use caller CWD). Otherwise a present `secrets/` beside the resolved root pipeline is used, while an absent conventional directory is skipped. Explicit missing/empty selections and invalid existing directories fail the command. ConfigKit follows a symlink used as the selected directory, but immediate entries must be regular non-symlink files. Existing environment variables—including empty values—always win. S3 `access_key_id` and `secret_access_key` are an optional pair of typed environment references. When both are absent, the AWS SDK retains its default credential provider chain. When both are configured, Narratio supplies their load-time resolved values through an explicit static provider. Audita and Notarius credential lists likewise resolve at pipeline load. Each adapter receives only its listed names and captured values, with no alias mapping or ambient environment inheritance. See [configuration](./config.md#audita-selection-and-credentials). External adapter results are individually bounded before Narratio validates or materializes them. These per-file limits do not reserve disk space: prevent hard disk exhaustion with filesystem, service, container, or volume quotas sized for the session workload. ## Cleanup Session-scoped cleanup: ```bash narratio clean 2026-04-04 ``` Global cleanup: ```bash narratio clean --all ``` Dry-run and cache variants: ```bash narratio clean 2026-04-04 --dry-run --clear-cache narratio clean --all --dry-run --clear-cache ``` Rules: - `clean` deletes work/spool session state; - cache is preserved unless `--clear-cache` is set; - each deletion is confined beneath its configured workspace, spool, or cache root; root, traversal, and out-of-root targets are refused; - the final cleanup stage is gated by successful publish evidence plus: - `pipeline.spool.delete_audio_after_publish=true` - `pipeline.workspace.cleanup_after_publish=true` - the manifest publish record has `uploaded=true`, the published run ID, the exact remote commit key, and the current commit-pointer key. Cleanup derives only the configured run-scoped spool and workspace targets, uses root-confined deletion, and does not read or mutate object storage. A failure leaves publish succeeded and the remote committed snapshot current. Retry with a normal full run: valid upstream work, including publish and notify, is skipped while the failed cleanup stage runs again. `run-stage cleanup` is also available when the manifest has valid publish evidence. A range ending at `notify` or earlier and the standalone `publish` command never delete local state. Older local manifests that contain the retired cleanup-obligation field are intentionally incompatible with strict manifest decoding. If such an obligation is still outstanding, complete it with the pre-upgrade binary. Otherwise verify that the remote publication is committed, move aside or regenerate the incompatible local manifest (or restore the verified remote snapshot), and then use the cleanup stage. Manual JSON editing is not the normal migration path. ## Operational Caveats - Local and S3 audio modes are mutually exclusive. - Publish requires prerequisite stages through `render` and `analyze` to be succeeded. - Markdown publish defaults require render outputs (`transcripts/final.md` and `transcripts/final.trimmed.md`). - Restore requires configured object storage and committed remote current state. - Pipeline-loading commands bootstrap filesystem secrets once before configuration resolution and object-store initialization.