# Codebase Audit Findings Status: in progress This document is the working ledger and final report for the audit defined by the [audit plan](audit-plan.md) and [audit sequence](audit-sequence.md). The audit is investigative: candidate findings below are not remediation changes. ## Audit Identity And Baseline | Field | Value | | --- | --- | | Audited revision | `74e2d21de5fb2ada0be5ef3fe9333e0d48ac7fb3` (`Close the completed roadmap documents`) | | Branch | `main`, attached worktree | | Initial worktree state | Untracked `docs/roadmap/audit-plan.md` and `docs/roadmap/audit-sequence.md`; no production or test changes | | Audit date | 2026-08-10 UTC | | Toolchain | `go version go1.26.5 linux/amd64` | | Platform | `GOOS=linux`, `GOARCH=amd64` | | Repository root | `/home/eric/Workspace/narratio` | The two initial untracked files are the audit specification supplied for this run. Adding this ledger and tracking those documents changes documentation only; all implementation and test evidence remains pinned to the revision above. If implementation or tests change, affected audit stages must be rerun and this section must record the new revision. ### Baseline Commands | Command | Result | Wall time | Evidence or limitation | | --- | --- | --- | --- | | `go test -count=1 ./...` | pass | 3.34 s | All 23 packages passed; `cmd/narratio` has no test files. | | `go test -race -count=1 ./...` | fail | 55.65 s | Race in `internal/adapters/whisperx.(*FakeClient).Transcribe` at `fake.go:45`, reached concurrently by `TestTranscribeStageTranscribesPreparedAudio`; candidate `TST-001`. All packages reported before `internal/stage` passed. | | `go vet ./...` | pass | 0.47 s | No diagnostics. | | `go build -o "$audit_build_dir/narratio" ./cmd/narratio` | pass | 1.03 s | Built outside the repository in `/tmp/tmp.x11pJL7014`. | | `go test -coverprofile="$audit_build_dir/coverage.out" ./...` | pass | 11.05 s | Diagnostic coverage only; no percentage is treated as a gate. | Coverage ranged from 69.8% (`internal/manifest`) to 100% (`internal/logging`) among tested packages. `cmd/narratio` reported 0% because it has no tests. The remaining package results ranged from 70.1% to 88.1%. The Stage 12 risk-based interpretation is recorded below; these numbers are diagnostic signals only. ### Code Graph Freshness And Structural Inventory The `narratio` graph was rebuilt in `moderate` mode after the revision was pinned. Its branch record reports the exact audited HEAD, `main`, and the repository root above. The index contains 2,407 nodes and 13,181 edges across 224 modeled files: 1,494 functions, 136 methods, 226 structs, 12 interfaces, and 20 modeled package nodes. The moderate filter excluded documentation, examples, `.git`, `.codex`, and `cmd/narratio`; the executable entry point was therefore verified through `go list` and direct inspection instead of graph evidence. Internal production code is represented at the pinned revision. Repository inventory at that revision: - 23 Go packages, including `cmd/narratio`; - 221 tracked Go files and 95 tracked `_test.go` files; - 278 tracked files total; - one process entry point, `cmd/narratio/main.go`, delegating to `internal/app.Execute`; - 11 canonical stages returned by `internal/stage.All`; and - 12 modeled interfaces, of which 11 are Narratio boundaries and one is the private AWS S3 client seam. Graph call tracing from `internal/app.Execute` confirms command dispatch into run, single-stage, clean, and session-helper paths, followed by configuration, artifact/path, manifest, stage, storage, restore, and cleanup owners. The production import inventory shows no lower-level package importing `internal/app`; apparent graph rollups such as `stage -> app`, `adapters -> app`, and `config -> app` came from test relationships or graph classification and are rejected as production dependency reversals at this mapping stage. ### Metric Signals For Later Review These are prioritization signals, not findings: | Signal | Evidence | Assigned review | | --- | --- | --- | | High fan-in | `app.Error` (207), stage `Run` symbols (151), `app.Execute` (108), `stage.sessionPathsForEnv` (105), `manifest.New` (72), `manifest.MarkStageSucceeded` (56), `app.executeStages` (48), `artifacts.S3SessionPrefix` (41), and `artifacts.SessionWorkDirForCampaign` (37) | Reviewed through Stage 11; stable ownership seams, interface dispatch, and graph name ambiguity explain the broad signal. `SIM-001` owns the only justified runner extraction. | | High complexity | `app.executeStages` cyclomatic 54/cognitive 96; `previouscache.BuildPlan` 22/38; `analyzeStage.Run` 20/27; `audita.NewSubprocessRunner` 17/25; `app.SessionInit` 20/21 | Reviewed through Stage 11. `SIM-001`, `SIM-003`, and `ARC-005` own narrow reductions; the remaining branching preserves distinct policy and validation. | | Exact similarity | `app.Analyze`/`app.Publish`, `manifest.Load`/`LoadRun`, `manifest.Create`/`CreateRun`, adapter constructors, and Seriatim fake methods | Reviewed through Stage 11. Thin command, typed-model, protocol-constructor, and fake similarities are coincidental or deliberately explicit; atomic file writing remains the shared mechanism in `DUP-001`/`DUP-005`. | | Test-heavy hotspot noise | Several test functions and fakes rank highly in transitive-depth and fan-in results | Stage 12; do not infer production risk from the metric | ### Automation And Fixture Inventory - `.woodpecker/release.yml` is tag-only release automation. It cross-builds Linux, macOS, and Windows binaries with Go 1.25, then publishes release assets. It does not run tests, race tests, vet, or example validation. - `examples/` contains 19 maintained files: pipeline, campaign, session, template, stable-input, and placeholder-audio fixtures. Configuration tests are documented as their validation owner. - No fuzz tests, golden files, golden-update switches, opt-in/live test tags, or `go:generate` test mechanisms were found. - Platform build constraints exist for the native no-replace directory tests and unsupported-platform fallback in `internal/fileops`. ## Execution Coverage Ledger | Stage | Status | Evidence and result | | --- | --- | --- | | 0: baseline | complete | Revision/environment pinned; graph refreshed; inventories and every prescribed baseline command recorded. `TST-001` owns the non-blocking race limitation. | | 1: contract and boundary map | complete | Canonical contracts and focused internal docs read; ownership, stage-contract, lifecycle, scenario, area, and preliminary risk-to-test matrices recorded below. | | 2: runner and manifest | complete | Full/single-stage entry paths, every lifecycle outcome, both manifest models/transitions, save disagreement states, canonical invalidation boundaries, and runner lock lifetime reviewed. Focused app/manifest test and race commands passed. Confirmed `COR-001` and `RSK-001`; assigned `DUP-001`, `SIM-001`, `COM-001`, `TST-002`, and lock-release details to later stages. | | 3: paths and filesystem | complete | Canonical local/remote paths, every artifact source family, filesystem mutations, directory promotion, cleanup confinement, permissions, and lock mechanics reviewed. Focused normal and race commands passed. Confirmed `COR-002`, `COR-003`, `RSK-002`, `RSK-003`, and `RSK-004`; added `DUP-002` and `TST-003`, and refined `DUP-001`. | | 4: publish and cleanup | complete | Publish prerequisites/source families, deterministic upload order, every partial remote outcome, retry semantics, effective locks, status/restore interpretation, and automatic/manual cleanup gates reviewed. Focused stage/app/artifacts/storage tests passed. Confirmed `COR-004` through `COR-007` and `RSK-005`; added `ARC-003`, `COM-002`, and `TST-004`. | | 5: restore and previous state | complete | Restore discovery/planning/execution/reporting, remote-current identity and scope, every local failure boundary, audio cache/spool identity, previous-cache planning/consumption, and status/validate policy reviewed. Focused app/previouscache/audio/artifacts/storage tests passed. Confirmed `COR-008` through `COR-011`, `RSK-006` through `RSK-008`, and `EFF-001`; added `DUP-003` and `TST-005`. | | 6: configuration and composition | complete | Discovery/precedence, strict loading, defaults/normalization, templates, cross-field validation, CLI selection, filesystem secrets, conditional adapter composition, and maintained examples reviewed. Exact focused tests and repository vet passed. Confirmed `COR-012` through `COR-015`, `RSK-009`, and `RSK-010`; added `ARC-004`, `DUP-004`, and `TST-006`. | | 7: adapters and shared support | complete | All HTTP, subprocess, notification, storage, audio, shared-model, and diagnostic boundaries were compared with their contracts and production callers. Focused normal and race commands passed. Confirmed `COR-016`, `RSK-011` through `RSK-014`, `EFF-002`, and `ARC-004`; added `ARC-005`, `DUP-005`, `COM-003`, and `TST-007`, and refined `TST-001`. | | 8: ordinary stages | complete | Prepare, transcribe, merge, polish, normalize, trim, and render were traced from resolved inputs through adapters, validation, run-local/canonical outputs, diagnostics, and manifest recording. The focused normal command passed; the required race command reproduced only `TST-001`. Confirmed `COR-017` through `COR-019`; added `ARC-006`, `DUP-006`, `COM-004`, and `TST-008`, and refined `ARC-001`, `ARC-002`, `ARC-005`, `RSK-013`, and scenario 8. | | 9: extraction | complete | Configuration, transcript resolution, fingerprinting, Notarius execution, receipt/index/lane validation, immutable promotion, manifest advertisement, catalog hydration, resume, and explicit analyze/publish consumption were traced as one slice. The exact focused command passed. Confirmed `COR-020`; added `DUP-007`, `SIM-002`, and `TST-009`, and refined `ARC-001`, `RSK-013`, and scenario 3. | | 10: analyze and dependencies | complete | All five source-policy families, seven built-in catalog entries, configured-artifact execution/reuse, dependency validation/order, previous-cache locality, lifecycle, and publish selection were traced as one slice. The exact focused command passed. Confirmed `COR-021` through `COR-024` and `RSK-015`; added `ARC-007`, `DUP-008`, `SIM-003`, `COM-005`, and `TST-010`; and refined `ARC-001`, `ARC-002`, `RSK-013`, and scenario 9. | | 11: maintainability | complete | Production graph metrics, change coupling, dead-code/static patterns, all structural candidates, efficiency workloads, comments, dependencies, and platform assumptions reviewed. Confirmed the narrow `ARC`, `DUP`, and `SIM` corrections recorded below, merged lifecycle wording and extraction proof candidates, rejected generic abstractions and micro-optimizations, added `SIM-004` and `COM-006`, and passed the full normal test suite and vet. | | 12: test policy | complete | All 749 tests were inventoried by behavior owner and consequential risk; every prior `TST` candidate was classified, five suite-wide candidates were added, and coverage, doubles, helpers, determinism, offline behavior, runtime, fuzzing, and automation were assessed. The prescribed shuffled run exposed `TST-011`; the prescribed race run reproduced only `TST-001`. | | 13: synthesis | not_started | No final ranking or accepted-risk decisions yet. | ## Area Coverage And Ownership Every area in the audit plan has a primary execution owner. `assigned` means it has been mapped but not behaviorally audited. | Inspection area | Canonical implementation owner | Primary audit stage | Status | | --- | --- | --- | --- | | Process and application boundary | `cmd/narratio`, `internal/app` | 6 (runner lifecycle portions in 2; publish/restore portions in 4-5) | reviewed | | Stage registry and runner | `internal/stage`, `internal/app` | 2 | reviewed | | Configuration | `internal/config` | 6 | reviewed | | Prepare and audio | `internal/stage`, `internal/audio`, `internal/previouscache` | 8 | reviewed | | Transcript stages | `internal/stage` plus tool adapters | 8 | reviewed | | Extraction | `internal/stage`, Notarius adapter, `internal/fileops` | 9 | reviewed | | Analyze and artifact dependencies | `internal/stage`, `internal/artifacts`, `internal/artifactpolicy` | 10 | reviewed | | Publish and cleanup | `internal/stage`, `internal/app` | 4 | reviewed | | Manifest state | `internal/manifest`, transition policy in `internal/app` | 2 | reviewed | | Artifacts, paths, and policy | `internal/artifacts`, `internal/artifactpolicy`, `internal/pathsafe` | 3 (resolution consumption revisited in 10) | reviewed | | Restore | `internal/app`, `internal/artifacts`, `internal/previouscache`, `internal/audio` | 5 | reviewed | | File operations | `internal/fileops`, `internal/pathsafe`, local artifact store | 3 (promotion vertical slice in 9) | reviewed | | External adapters and storage | `internal/adapters`, `internal/audio` | 7 | reviewed | | Shared models and diagnostics | `internal/artifactmodel`, `internal/contracts`, `internal/logging` | 7 (maintainability revisited in 11) | reviewed | | Tests, examples, and automation | package test owners, `examples/`, `.woodpecker/` | 12 | reviewed | ## Package And Interface Ownership Map | Package | Owned contract or policy | Important boundaries | Audit owner | | --- | --- | --- | --- | | `cmd/narratio` | Process entry and exit; CLI delegates behavior to app | `main -> app.Execute` | 6 | | `internal/app` | Command dispatch, composition, locking, planning, lifecycle, restore, cleanup, reporting | `Execute`, `executeStages`; consumes stage/artifact/manifest/adapter contracts | 2, 4-6 | | `internal/config` | Strict discovery, defaults, resolve, template, and validation rules | Config models and load/resolve/validate functions | 6 | | `internal/stage` | Canonical order and stage behavior | `Stage`, `ResumeValidator`, `Env`; adapter interfaces are injected | 2, 4, 8-10 | | `internal/manifest` | Session/run models, transitions, validation, atomic persistence | `Store`; transition methods record but do not choose policy | 2 | | `internal/artifacts` | Artifact identity/resolution, paths/keys, local store, remote current-state mechanics | `Store`; consumes explicit storage keys | 3, 5, 10 | | `internal/artifactpolicy` | Configured source/destination identity and safety policy | Narrow validators used by config, artifacts, app, and stages | 3, 10 | | `internal/artifactmodel` | Shared serialized artifact, contract, and provenance models | Data contract only | 3, 7 | | `internal/pathsafe` | Confined relative path and destination mechanics | Narrow validation helpers; no stage policy | 3 | | `internal/fileops` | Atomic files, copies, hashing, no-replace directory promotion | Filesystem mechanics receive explicit paths | 3, 9 | | `internal/previouscache` | Deterministic previous-session requirement planning/materialization | Uses explicit object-store and artifact contracts | 5, 8, 10 | | `internal/audio` | S3 audio spool/cache materialization | Uses `storage.ObjectStore`; no stage ordering | 5, 8 | | `internal/contracts` | Bounds and shared JSON validation models | Data contract only | 7, 8 | | `internal/logging` | Shared logger construction | `slog` composition | 7, 11 | | `internal/adapters/whisperx` | WhisperX HTTP protocol | `Client` | 7 | | `internal/adapters/seriatim` | Merge/normalize/trim/render subprocess protocol | `Runner` | 7 | | `internal/adapters/audita` | Audita subprocess protocol | `Runner` | 7 | | `internal/adapters/scriptorium` | Scriptorium run/render subprocess protocol | `Runner` | 7 | | `internal/adapters/notarius` | Notarius invocation and receipt boundary | `Runner` | 7 (vertical behavior in 9) | | `internal/adapters/notify` | Notification transport | `Sender` | 7 | | `internal/adapters/storage` | Explicit bucket-relative object-store operations and S3 mechanics | `ObjectStore`; private `s3API` test seam | 7 | | `internal/adapters/subprocess` | Shared bounded subprocess/config/log mechanics | Concrete helper package, not stage policy | 7 | The graph reported no inbound production callers of `Stage.Declares`; text search found definitions and test stubs but no production invocation. This reduces the current impact of `ARC-001` but makes the interface's intended owner and future use an explicit question rather than resolving the mismatch. ## Stage Contract Matrix The table separates declared/static contracts from dynamic behavior. All executed stages use the runner's session/run transitions. Unless noted, a successful result records returned outputs, diagnostics, generated configuration, and metadata; a different effective executed outcome can stale succeeded downstream work, while force pre-stales succeeded downstream work. | Order and stage | Inputs and outputs | Configuration and adapters | Skip/resume behavior | Materialization and manifest effects | | --- | --- | --- | --- | --- | | 1 `prepare` | Config, stable inputs, one audio mode, optional previous requirements -> canonical `inputs/**`, `audio/**`, optional `previous/**`, `manifest.inputs` | All resolved config; storage for S3/current previous state; audio/artifact/previous-cache services | No stage-specific resume validator or explicit self-skip | Writes canonical session inputs and sorted input records; unlike processing stages, `Declares` labels produced canonical files as inputs. Repeated explicit audio paths create duplicate records (`COR-019`), and zero previous requirements leave stale managed state (`COR-017`). | | 2 `transcribe` | Prepared FLAC files -> one raw JSON per unique filename-derived speaker | WhisperX language/retry/timeout/concurrency; `whisperx.Client` | Ordinary succeeded-record skip; no validator/self-skip | Bounded concurrent run-local writes, exact adapter path check, JSON validation, sorted results, then canonical materialization. Cancellation can nevertheless return a successful incomplete set (`COR-018`). | | 3 `merge` | Manifest raw transcripts or directory fallback, speakers, autocorrect -> base transcript, optional report | Seriatim merge fields; `seriatim.Runner` | Ordinary succeeded-record skip | Deterministically normalized scratch inputs and run-local transcript/report validate before canonical materialization; logs/config are diagnostics. | | 4 `polish` | Manifest base transcript or canonical fallback, glossary -> polished transcript, optional report | Audita fields/credential reference; `audita.Runner` | Ordinary succeeded-record skip | Run-local transcript/report validate before canonical materialization; logs/config are diagnostics. Static request/constructor ownership remains `ARC-005`. | | 5 `normalize` | Manifest polished transcript or canonical fallback -> final transcript, optional report | Normalize plus Seriatim fields; `seriatim.Runner` | Ordinary succeeded-record skip | Run-local schema/report validation then configured canonical materialization; logs/config are diagnostics. | | 6 `trim` | Manifest final transcript or configured canonical fallback -> final-trimmed transcript and, when enabled, bounds | Trim, bounds, Scriptorium, and Seriatim fields; both runners when enabled | Disabled trim copies and validates the normalized transcript, then succeeds; no explicit self-skip or resume validator | Enabled bounds/trim results validate before canonical materialization; render-debug and subprocess logs/config are diagnostics, not outputs. | | 7 `extract` | Final-trimmed source -> immutable index and configured lane outputs | Notarius executable/config/pipeline/timeout/output contracts; `notarius.Runner` | Disabled is explicit `notarius_disabled` self-skip; only current `ResumeValidator`; obsolete reruns, unsafe validation errors. The validator does not bind reuse to the current trimmed-transcript bytes (`COR-020`). | Validates the run-local receipt/index/configured lanes, promotes the complete regular-file bundle to a unique no-replace destination, rechecks promoted checksums, and records exact checksums/contracts/provenance. Only configured lanes are selectable; index and unconfigured bundle members remain audit state. | | 8 `render` | Manifest final/final-trimmed JSON or canonical fallback -> two Markdown transcripts | Render and Seriatim fields; `seriatim.Runner` | Disabled returns a zero-disposition no-output result, therefore durable success rather than explicit self-skip; later enablement needs force; no validator | Enabled run-local text validates non-empty before either canonical result is recorded; logs/config are diagnostics. Focused wording is imprecise under `COM-004`. | | 9 `analyze` | Dynamic built-in, prepared, extraction, configured, and previous sources -> selected configured artifact outputs | Scriptorium artifact graph/selection; `scriptorium.Runner` | Missing config or no executable artifacts returns ordinary success with skip metadata, not self-skip; later configuration enablement needs force; no validator | Selected artifacts run in stable topological order; non-executable configured outputs may be reused; each generated run-local output is validated, materialized canonically, and exposed to later dependents. Static `Declares` omits dynamic outputs and several input families. Optional built-ins, explicit selection/prerequisite planning, guidance, and inert input fields have confirmed defects below. | | 10 `publish` | Session/run state, selected output rules, locks, previous cache -> remote run/output/current objects | Publish/storage/selection fields; `storage.ObjectStore` | Disabled publish or run upload returns ordinary success with skip metadata, not a self-skip; force cannot bypass locks; no validator | Deterministic uploads; `current/manifest.json` before `current/run_id.txt`; post-commit local metadata gates cleanup. Static prerequisites omit extract because disabled extraction is valid and lane resolution enforces required extraction state when selected. | | 11 `notify` | No implemented persisted pipeline input/output | Optional `notify.Sender`; default no-op | Ordinary succeeded-record skip; no explicit self-skip or validator | Placeholder metadata and optional notification call; no returned output. `Declares` nevertheless advertises placeholder input/output paths. | Configuration, adapters, skip policy, and dynamic outputs are not represented by `IODecl`; their current canonical owners are the focused stage, configuration, and integration contracts. Whether `IODecl` should remain a partial display type or become an enforceable declaration is deferred as `ARC-001`. ## Lifecycle Matrix This began as the intended contract map and is now source-backed for both durable ledgers by the Stage 2 review. | Outcome | Session manifest intent | Invocation manifest intent | Downstream and next-invocation intent | | --- | --- | --- | --- | | First run | Pending/non-succeeded stage becomes running, then succeeded/failed/skipped; executing clears older result payload first | New run record; action `run`; terminal status records this invocation | Success enables later stages; failure stops current execution and an effective outcome change may stale succeeded downstream records. | | Already-succeeded skip | Existing succeeded session record and payload remain unchanged, subject to resume validation | Action/status record a skip and stable reason for this invocation | Reusable result remains authoritative; pipeline continues. | | Explicit self-skip | Session stage becomes skipped, clears older result payload, and may record bounded current skip details | Action was `run`, outcome is skipped with reason | Reconsidered later; a changed effective upstream outcome stales succeeded downstream work; identical extraction disabled skip is stable. | | Failure | Current stage becomes failed with error; current output/log/config/metadata payload is cleared | Action `run`, failed outcome and overall failed run | Current execution stops; affected succeeded downstream work is intended to stale; later invocation reruns non-succeeded stages. | | Interruption | Model admits `interrupted`, but production never writes it; process death leaves the last durable status `running` and the running transition has already cleared the target's prior result details | The run remains non-terminal at its last durable per-stage state; no load or startup reconciliation changes it | Non-succeeded session stages execute on the next included plan, so continuation is conservative; the old invocation record remains inaccurate under confirmed `RSK-001`. | | Forced replacement | Target execution starts fresh; succeeded downstream records are pre-marked stale; current target payload clears on running | Force flag and `run` action recorded | Replacement result determines later execution; locks and safety policy remain authoritative. | | Non-resumable success | Prior success becomes stale while retaining details long enough for diagnosis/validation, then running clears them | Current invocation records execution after validation rejects skip | Obsolete result reruns; unsafe inability to decide stops without silently replacing current success. | | Successful rerun | Target becomes succeeded with only new outputs/diagnostics/config/metadata | Current invocation records its own new success; earlier run manifests remain unchanged | A rerun after a non-succeeded state stales succeeded downstream work; force already stales it before execution. The runner does not compare output contents; identical repeated self-skip is the narrow no-invalidation case. | Stage 2 verified the matrix. The runner treats the session manifest as the only cross-invocation decision source and each run manifest as a record of one invocation. The resulting field behavior is: - entering `running` clears the session stage's former outputs, logs, generated configuration, metadata, completion, and error; success installs only the current result and clears the stage error, while failure and self-skip clear result data before bounded current skip diagnostics are reapplied; - staling deliberately retains prior result data and timestamps for diagnosis, changes status/error/updated time, and prevents the result from being reused; - an already-succeeded skip does not mutate the session record; the run record separately stores action `skip`, status `skipped`, and reason `already_succeeded` without copying the reusable outputs; - an executed self-skip stores action `run` and status `skipped` in the run record, so it remains distinguishable from an idempotent skip; - a stage failure marks the session stage and run stage failed, records the error in both ledgers, makes the run overall failed, and stops execution; - force is stored at run level and pre-stales all succeeded canonical downstream stages; non-resumable validation first stales and saves the target and succeeded downstream stages, then executes; and a successful execution following any non-succeeded prior state stales remaining succeeded downstream work; and - the session-level `last_error` is retained as historical information after a later success. No production reader treats it as current status; stage and run status are the operative fields. The invalidation helper derives position from the complete canonical registry, not the selected plan. Consequently a single-stage replacement has the same downstream effect as that stage in a full run. `prepare` can invalidate every later succeeded stage and `notify` has no downstream target. Only succeeded records need explicit staling: failed, skipped, stale, pending, running, and interrupted records already execute on the next included plan. ### Runner Entry, Lock, And Persistence Conclusions `Run` validates the assembled configuration and selection, builds the full canonical plan, and delegates to `executeStages`. `RunStage`, `Analyze`, and `Publish` select one canonical stage; the latter two force it. Single-stage execution still uses the same lifecycle, session lock, invalidation, dual manifests, cleanup check, and final run transition as a full run. The session lock is acquired after layout creation and before the session manifest is loaded, then held through stage execution, all manifest saves, post-publish cleanup, and the final run save. A competing same-session runner therefore cannot enter manifest decision-making while the first holds the lock. The deferred release error is discarded. Whether close/unlink failures can leave a blocking or misleading lock requires the filesystem implementation review assigned to Stage 3; the assembled runner suite has no concurrent-runner or release-failure case. Both manifest stores use temp-file write, file sync, close, and same-directory rename, so an error before rename leaves the prior individual file in place. There is no atomic transaction or reconciliation protocol across the two manifest files. The runner saves the run record first when announcing execution, then saves the session record; for terminal outcomes it saves the session authority first, then the run audit. The possible durable states and their later interpretation are: | Failure boundary | Durable state | Later invocation behavior | | --- | --- | --- | | New session or identity save fails before initial run save | No run record; session is absent or remains at its prior contents. An identity-save attempt mutates the in-memory identity and `updated_at`, but none of it becomes durable. | The command stops before a stage. A later invocation loads/creates from the last durable session state. | | Initial run save fails after session identity save | Session points at the new run ID, but that run's audit file may not exist. | Session stage states still govern reuse; a later invocation creates a different run ID. | | Resume validation reports unsafe/indeterminate | Session success is preserved, but the already-created run remains overall `running`. | The prior success remains authoritative and validation is attempted again; the abandoned run is never reconciled. | | Saving non-resumable staleness fails | Run remains initially `running`; session remains at the prior success. No stage executes. | Validation is attempted again without silently replacing the prior success. | | Saving an ordinary skip to the run file fails | Session remains succeeded; the run file remains at its preceding state. | The stage is safely reconsidered as another skip. | | Run `running` save fails | Session is unchanged and the stage does not execute. | Session authority makes the next decision conservatively. | | Session `running` save fails after the run save | Run stage is `running`; session remains at its prior state and the stage does not execute. Forced downstream staleness is not durable. | A non-succeeded target retries; a prior success skips unless force/resume validation again requires replacement. | | Terminal session save fails after stage work | Both durable records remain `running`, although the stage may already have external or canonical effects. | The session stage reruns because `running` is not reusable. Stage-owned idempotency remains essential. | | Terminal run save fails after terminal session save | Session has the authoritative success, skip, failure, and downstream state; run stays `running`. | Execution resumes safely from the session, but the historical run remains inaccurate. | | Post-publish cleanup fails | Session publish remains succeeded; a successful run save marks the invocation failed, while a failed run save leaves its prior overall `running` state. | A normal later invocation skips publish, so the execution-based cleanup gate does not retry. A forced publish or explicit manual clean is required; confirmed `COR-006`. | | Final overall run save fails | Session and per-stage run records are terminal, but overall run status remains `running`. | A later invocation skips or reruns from session state and does not repair the old run. | Save errors are returned with both the stage error and persistence error when both exist. The session store is injectable, but run persistence is a concrete `LocalStore` outside the `manifest.Store` interface. This leaves the run-side failure rows above unexercised by focused runner tests and makes centralized terminalization/reconciliation harder to test. ### Lifecycle Scenario Conclusions - Scenario 1 is functionally safe for reuse: a non-resumable success and its succeeded downstream records are persisted stale before execution; a failed rerun leaves the target failed and downstream stale; an ordinary retry runs both. An inability to validate preserves the prior success rather than replacing it. `RSK-001` records the inaccurate invocation audit left by that controlled error. - Scenario 2 is conservative and source-backed: force pre-stales succeeded downstream work; failure and a changed effective outcome stale it; a changed self-skip stales it; and an identical repeated self-skip does not. Here identical means prior status skipped, zero outputs, and the same reason; diagnostic/metadata differences are not compared. Disabled stages represented as success remain reusable success, while an explicitly skipped downstream stage is naturally reconsidered because only success is ever skipped. The runner-level distinction is coherent; `ARC-002` remains assigned to Stages 4 and 8 for the stage-specific contract and wording. - The lock portion of Scenario 10 is resolved at the application boundary: acquisition occurs before manifest access and the lock spans the entire mutation lifetime. Stage 3 must decide the ignored-release and underlying lock-file questions. ## Cross-Boundary Scenario Assignments | Scenario | Primary audit stage | Supporting packages and focused tests | | --- | --- | --- | | 1. Success becomes non-resumable, rerun fails, later reuse decision | 2 | `internal/app`, `internal/manifest`, `stage.ResumeValidator`; `runner_test.go`, `extract_lifecycle_test.go`, manifest transition tests | | 2. Forced/changed upstream outcome with succeeded, self-skipped, disabled downstream | 2 | `internal/app`, `internal/stage`, `internal/manifest`; run-control, runner, extraction-lifecycle tests; disabled-stage detail revisited in 8 | | 3. Extraction bundle followed by configuration/transitive-input change | 9 | Extract/resume, Notarius adapter, artifacts/fileops tests; downstream resolution revisited in 10 | | 4. Published/restored/prepared previous state consumed locally by analyze | 5 | `internal/app`, `internal/previouscache`, `internal/audio`, `internal/artifacts`; restore/prepare tests; analyze consumption revisited in 10 | | 5. Publish failure at every upload boundary, then status/restore/retry | 4 | Publish stage, storage fake/adapter, app status/restore; publish and operator-helper tests; restore interpretation revisited in 5 | | 6. Restore identical/conflict/unsafe/cache/pre-manifest-install cases | 5 | Restore discovery/plan/execute/report, artifacts, previouscache, audio; restore test suite | | 7. Cleanup after skipped/failed/locked/partial/committed publish | 4 | Publish metadata, post-publish cleanup, cleanup targets, pathsafe; publish/cleanup tests | | 8. Cancellation through workers, HTTP, subprocess, storage, manifests | 7 | Adapter and subprocess tests; transcribe/stage tests in 8; runner reporting in 2 | | 9. Disabled/unselected/reused/generated/extraction/previous source then publish filtering | 10 | Analyze, artifact catalog/resolver/policy, publish tests; config ownership in 6 and publish result in 4 | | 10. Concurrent same-session invocation and lock cleanup failures | 3 | Runner lock lifetime in 2; local artifact store, path/file cleanup and lock tests in 3 | Scenario 3 is resolved. The fingerprint deterministically observes the resolved executable and top-level config *paths*, pipeline ID, normalized timeout, working directory, and sorted configured lane contracts. Changes to those values make the result non-resumable. It intentionally cannot observe the contents of the executable, Notarius configuration, profiles, prompts, modules, references, environment, or other external/transitive inputs; operations and extract documentation require `--force` after those changes. That is an explicit operator-owned limitation, although a same-path executable replacement deserves the same guidance as the documented configuration cases. The current fingerprint also omits the resolved final-trimmed transcript and its bytes. That is a direct Narratio-owned input rather than an unknowable external dependency and is confirmed as `COR-020`. Force always pre-stales succeeded downstream stages; automatically detected invocation-contract or bundle-evidence changes rerun extract and invalidate succeeded downstream work through the ordinary changed-outcome path. Scenario 8 is resolved through the ordinary stages. Parent cancellation reaches HTTP attempts, retry waits, every AWS call, audio downloads, and each direct child process. HTTP/S3/local-file resources are released on error, and a started direct child is waited. Cancellation is not complete for multipart body construction (`EFF-002`) or subprocess descendants (`RSK-011`), and a malformed repeated S3 continuation token needs cancellation to escape its otherwise non-progressing loop (`RSK-014`). Merge, polish, normalize, trim, and render invoke subprocess adapters synchronously and propagate their errors; they do not introduce another worker lifetime. Transcribe alone aggregates workers, and `COR-018` confirms that parent cancellation before an adapter records an error can be mistaken for successful zero or partial output. Runner reporting remains the Stage 2 dual-ledger behavior. Scenario 9 is resolved. Without an explicit filter, configured artifacts are executable exactly when enabled. With a non-empty filter, catalog registration makes exactly the named configured keys executable even when their `enabled` field is false; every other configured artifact becomes non-executable. A non-executable artifact is available for dependency/input reuse only when its configured canonical output is an existing non-empty file. That includes both truly disabled artifacts and enabled-but-unselected artifacts, although both receive provenance named `filesystem.disabled_artifact_output`. A selected artifact may depend on an unselected artifact only through that reusable local file. Selected dependencies are ordered before dependents; independent ready nodes and final results are lexically stable. Cycles in the enabled graph are rejected by configuration and cycles in an explicitly selected graph are rejected again at runtime. The preflight's first unavailable-dependency error is not stable when several selected nodes fail, which is `RSK-015`. Built-in transcript/bounds entries resolve manifest-first then canonical, prepared stable inputs resolve fixed `inputs/*.yml` paths, extraction entries hydrate only from a complete compatible current extract record, configured entries resolve only through catalog availability, and previous-session entries resolve only from manifest-backed or filesystem `previous/` cache state. No analyze resolver calls the object store. Required/optional behavior is coherent for prepared, extraction, configured, previous, polished-transcript, and bounds sources, but normalized/trimmed/Markdown built-ins bypass the optional policy (`COR-021`). Previous-input preparation scans enabled artifacts only while selection can execute disabled artifacts (`COR-022`), and its missing-input guidance is not an executable CLI command (`COR-023`). Input-level `artifact` and `path` fields are accepted and documented but never reach resolution or the adapter (`COR-024`). Each successful Scriptorium invocation validates a run-local non-empty output, materializes it to the configured canonical path, and marks it generated for later selected dependents. Generated/reused metadata and logs/configuration are sorted or deduplicated deterministically. Publish builds an independent availability catalog and filters only publish rules sourced from `narratio.artifact.`: an unselected configured rule is skipped even when required, while built-in and explicit extraction rules are unaffected. A selected configured rule must be locally available; publication never causes analyze execution. Missing/no-executable analyze returns ordinary successful no-output metadata so the pipeline can continue to publish; it is durably reused until forced, closing analyze's behavior portion of `ARC-002` and leaving the wording gap in `COM-005`. ## Intended Risk-To-Test Ownership Matrix This Stage 1 matrix identifies intended owners. The final Stage 12 sufficiency assessment follows the accumulated behavior-pass observations below. | Architectural invariant or risk | Implementation owner | Intended test owner | | --- | --- | --- | | One deterministic canonical stage order | `internal/stage`, planner in `internal/app` | `internal/app/planner_test.go`, narrow registry tests | | Session manifest is cross-invocation authority; run manifest is immutable invocation audit | `internal/app`, `internal/manifest` | Manifest transition tests plus assembled runner/run-stage tests | | First run, skip, self-skip, failure, force, invalidation, and rerun transitions | `internal/app`, `internal/manifest` | App lifecycle tests as primary; manifest helpers own field mutation | | Obsolete versus unsafe resume validation | Stage-specific `ResumeValidator`, runner | Extract resume tests plus runner integration tests | | Run-local validation before canonical materialization | Individual stages and `run_local.go` | Focused stage package behavioral tests; fileops owns atomic mechanism | | Strict config, defaults, identity, and cross-field validation | `internal/config` | Config package tests; example load/validation test samples assembly | | Canonical path/key ownership and traversal confinement | Artifacts, artifactpolicy, pathsafe | Owning package tests; app/stage tests only for assembled policy | | Immutable extraction promotion and provenance/checksum validation | Extract, fileops, artifacts, Notarius adapter | Fileops mechanism, extract behavior, artifact hydration, adapter contract tests | | Deterministic artifact dependency and source resolution | Artifacts, artifactpolicy, analyze | Artifact/package tests and analyze package behavior tests | | Previous-session consumption remains local in analyze | Previouscache/prepare/artifacts/analyze | Previouscache and prepare tests; one analyze boundary test for no remote call | | Remote current pointer is publish's final commit point | Publish stage | Publish tests with stateful object-store fake; storage tests own transport only | | Restore is confined, deterministic, conflict-safe, and installs manifest last | Restore app modules, artifacts/previouscache/audio | Restore plan/execution/workflow tests plus low-level path/file tests | | Cleanup requires explicit scope and committed publish metadata | App cleanup modules, pathsafe | Cleanup-target and post-publish integration tests | | Session single-writer lock and safe release | Local artifact store, app lifetime | Artifact local-store tests plus assembled concurrent runner tests | | Adapter cancellation, error adaptation, and resource closure | Each adapter and shared subprocess package | Focused adapter boundary tests; stage tests sample propagation | | Bounded deterministic transcription concurrency | Transcribe stage and WhisperX client | Stage concurrency/result-order tests; HTTP adapter retry/cancel tests | | Secrets never persist or appear in diagnostics | Config/app composition and each adapter/logging boundary | Owning config/adapter tests plus selected assembled redaction checks | | Default suite remains deterministic, offline, and credential-free | Every package; automation | Stage 12 repository-wide execution and test-policy audit | Stage 2 test observations for this matrix: | Risk | Existing focused protection | Gap or disposition | | --- | --- | --- | | Normal lifecycle and payload clearing | Manifest helper tests plus runner/run-stage/extraction-lifecycle tests cover first success, existing-success skip, force, failure, self-skip, repeated self-skip, unsafe and obsolete resume validation, retry, and canonical downstream invalidation. | Strong behavior coverage for successful persistence; no finding. | | Session/run invocation identity | Per-invocation runner test asserts distinct run IDs, manifest paths, and the latest session `run_id`. | It does not assert refreshed local/spool/remote derived fields or reject loaded identity conflicts; required by `COR-001`. | | Partial persistence and handled pre-stage errors | Session manifest is injectable and the unsafe-resume test proves old success is preserved. | Run persistence is concrete; no disagreement-boundary tests and no terminal run assertion on resume error; candidate `TST-002`. | | Interruption and restart | Non-succeeded action logic and retry tests indirectly prove `running` is rerunnable. | No kill/reload normalization, reconciliation, or abandoned-run status test; confirmed `RSK-001`. | | Same-session concurrency | Artifact store has focused lock tests. | No assembled concurrent runner or release-failure test; Stage 3 owns the mechanism and sufficiency decision. | Stage 7 test observations for this matrix: | Risk | Existing focused protection | Gap or disposition | | --- | --- | --- | | HTTP retry, cancellation, and response installation | WhisperX tests cover success, retryable/non-retryable statuses, attempt timeout, parent cancellation, malformed JSON, and absence of failed output. | No supported-scheme table, streaming/body-production cancellation, response close observation, or oversized response case; `COR-016`, `EFF-002`, and `TST-007`. | | Process launch, wait, and diagnostics | Shared tests cover successful separated/shared logs, start/exit context, a direct-child timeout, environment inheritance/override, bounded tail use, and override-secret error redaction. Each protocol adapter checks exact invocations and normal failures. | No process-descendant, explicit cancellation, inherited-secret, raw-log redaction, symlink, non-regular, or oversized-output case; `RSK-011` through `RSK-013` and `TST-007`. | | S3 resources and pagination | Focused tests cover one-page normalization, streamed download/upload, not-found adaptation, and credential option construction. Temporary-download tests cover failure cleanup and wrapped context causes. | No valid multi-page, repeated/empty-token, later-page failure, cancellation, or body-close probe; `RSK-014` and `TST-007`. | | Adapter fakes under production concurrency | No-op/fake tests cover cancellation/error and deterministic placeholder materialization. The focused adapter race command passes. | The WhisperX fake's unsynchronized request slice fails the full race suite when the transcribe stage uses it concurrently; `TST-001`. Other fakes currently have sequential production callers, so no blanket race finding. | | Shared models and logging | Artifact-model JSON/conversion tests and bounds success/error tables protect current serialized shapes; logger tests protect output/nil-writer construction. | Output acquisition bounds/type remain `RSK-013`; suite-wide fake/model/logging value and redundancy remain Stage 12 work. | Stage 8 test observations for this matrix: | Risk | Existing focused protection | Gap or disposition | | --- | --- | --- | | Stable prepare inputs and source modes | Prepare tests cover explicit files, directory enumeration, S3 sorting/cache behavior, provenance, local/S3 conflict, idempotence, required/optional previous hydration, and replacement while requirements remain. | The no-requirement test asserts stale previous state survives, and no repeated-explicit-audio case crosses into transcribe; `COR-017`, `COR-019`, and `TST-008`. | | Bounded deterministic transcription | Tests cover concurrency bounds, filename-derived identity, adapter error, invalid JSON, exact run-local output use, canonical materialization, and sorted result assertions. | No pre-canceled or mid-dispatch context case proves all jobs complete before success; `COR-018` and `TST-008`. The required race command also reproduces `TST-001` in the concurrent fake. | | Manifest-first transformation and schema/report validation | Merge, polish, normalize, trim, and render suites cover manifest-first and fallback sources, missing/invalid inputs, adapter failures, configured reports/schemas, disabled paths, diagnostics, run-local paths, and canonical materialization. | Alternate valid adapter-returned paths, link/non-regular/oversized results, and multi-output materialization failure boundaries are not coherently tested; `ARC-006`, `RSK-013`, and `TST-008`. | | Disabled ordinary-stage lifecycle | Trim tests prove disabled execution copies a valid canonical output; render tests prove disabled execution returns no outputs. Runner tests separately define durable success versus self-skip. | Render's focused document says only “skips,” without the durable-success and later-force consequence; `ARC-002` is resolved for these stages and `COM-004` owns the wording. Analyze remains Stage 10. | Stage 9 test observations for this matrix: | Risk | Existing focused protection | Gap or disposition | | --- | --- | --- | | Receipt, index, and configured-lane acceptance | Notarius adapter tables cover process errors, bounded receipt/index/summary parsing, exact management paths, bundle confinement, symlinks, required receipt fields, descriptor uniqueness, and optional descriptor contracts. Extract tests cover rejection, missing/duplicate/incompatible lanes, invalid/empty JSON, provenance construction, and deterministic output ordering. | Configured lane bodies have no acquisition bound, and extraction/catalog reread them without one; this extends `RSK-013`. Protocol cases otherwise have clear adapter or stage owners. | | Promotion and immutable identity | `fileops` tests cover regular nested trees, permissions, source symlink/non-regular/root and entry replacement, destination no-replace races, destination-inside-source, platform support, and cleanup. Stage tests prove promotion errors advertise no outputs. | The assembled extraction slice does not inject a failure after successful install or exercise an ancestor/root replacement during resume; coordinate `TST-009` with the lower-level coverage and `COR-003` rather than duplicating every filesystem case. | | Reuse and downstream invalidation | Stage and runner tests cover immediate and cross-invocation reuse, disabled-to-enabled reconsideration, configuration-value changes, missing/tampered payloads, source/contract/provenance mismatches, forced replacement, failed retry, unsafe resume errors, and changed-outcome invalidation. | No test changes the direct trimmed transcript beneath an otherwise succeeded record, so `COR-020` remains green. Same-path external dependency changes are documented force cases; one lifecycle contract test is sufficient if an explicit external revision mechanism is added. | | Catalog and explicit consumption | Catalog tables require one complete current bundle and reject unsafe, incomplete, mismatched, incidental, or tampered state. Analyze and publish tests prove only explicitly configured lanes are passed/uploaded and invalid required lanes fail before execution/upload. | Resume and catalog independently encode much of the same bundle proof (`DUP-007`); future shared evidence tests should preserve resume's obsolete-versus-unsafe result and catalog's fail-closed all-or-none behavior. | Stage 10 test observations for this matrix: | Risk | Existing focused protection | Gap or disposition | | --- | --- | --- | | Source-family resolution | Analyze tests cover successful built-in transcript variants, prepared stable inputs, configured generated/reused outputs, extraction lanes, previous-cache manifest/fallback paths, required failures, and optional absence for prepared/configured/extraction/previous sources. Artifact-policy/config tables protect accepted identities. | No missing optional normalized, trimmed, or Markdown built-in case exists, so `COR-021` remains green. The focused analyze document also omits extraction and successful no-output lifecycle consequences (`COM-005`). | | Selection, reuse, and dependency order | Catalog tests explicitly require selection to override `enabled`; analyze tests cover one selection filter, generated/reused metadata, independent lexical order, selected dependencies, cycles, and unavailable reused dependencies. Publish tests cover selected/unselected configured rules and prove built-in/extraction rules are unaffected. | No assembled test selects a disabled artifact, crosses that choice into previous-requirement preparation, or creates several simultaneously unavailable dependencies. `COR-022`, `RSK-015`, `ARC-007`, and `TST-010` own those seams. | | Previous-session locality and guidance | Requirement collection covers enabled/disabled, deduplication, required-wins, and stable ordering. Prepare/restore/app tests cover planned requirements; analyze proves manifest/fallback local resolution and explicitly asserts no object-store call. | Selection is absent from the collector API (`COR-022`), while the guidance test asserts only a fragment of the malformed command and therefore preserves `COR-023`. | | Scriptorium request and output materialization | Stage tests inspect exact named input paths, vars, generated/reused metadata, render-debug, logs/configuration, and canonical materialization. Adapter tests assert deterministic flags and generated invocation configuration. | Input `artifact`/`path` fields have no consumer or request representation (`COR-024`); no stage-side link/non-regular/oversized result case covers analyze, extending `RSK-013`; and the broad execution/resolution shape remains `SIM-003`. | ## Test-Suite Policy Conclusions The graph inventory contains 904 functions in 95 test files, including 749 `Test` functions and no fuzz tests or benchmarks. Review grouped those tests by the policy owner they protect rather than by filename. Parsing and validation are concentrated in `config`, `artifactpolicy`, manifest decoding, and adapter protocol tests; domain and durable state in `manifest`, `artifacts`, and app lifecycle tests; filesystem safety in `pathsafe`, `fileops`, and the local store; adapter contracts in their owning packages; orchestration and CLI in `app`; and representative assembled behavior in app/stage workflow tests. ### Final risk-to-test sufficiency matrix | Consequential invariant or risk | Current protection | Proper owner and realistic protected defect | Missing modes or cross-layer overlap | Sufficiency conclusion | | --- | --- | --- | --- | --- | | Canonical order, planning, and lifecycle transitions | Registry/planner tables, manifest transition tests, and app first-run/skip/force/failure/rerun cases | App lifecycle tests protect durable state-machine outcomes; manifest tests own field mutation | Run-store failures and resume-validation terminalization cannot be injected; several assembled tests repeat per-stage metadata | Strong ordinary protection; add the narrow persistence seam in `TST-002` and consolidate overlap under `TST-015` | | Session/run identity and immutable invocation audit | Per-invocation runner tests and typed manifest load/save tests | App owns identity synchronization; manifest owns serialization | Derived identity conflicts and disagreement states remain visible only through `COR-001`/`TST-002` | Insufficient at the cross-manifest boundary; `TST-002` is the intended addition | | Strict configuration, defaults, identity, and examples | Extensive strict-load/default/validation tables plus maintained-example loading | Config tests protect rejected language and normalized values; one example test protects assembly | A 949-line loader/validator table repeats complete YAML and broad default assertions already owned elsewhere | Behavior breadth is strong; restructure for clearer ownership under `TST-014` without reducing contract cases | | Confined identities, paths, locks, and atomic persistence | `pathsafe`, `fileops`, artifact, cleanup, and local-store tests | Low-level owners protect no-escape/no-follow/atomicity; one app or stage case protects composition | Unsafe identity, destination-ancestor links, directory sync, stale/release lock faults, and concurrent runner cases are absent; duplicating each at every caller would be wasteful | Insufficient for confirmed safety roots; add one owner-level case per root plus representative composition under `TST-003` | | Remote publish commit and cleanup recovery | Publish source/order/failure tests and cleanup path/effect tests | Publish owns pointer-last snapshot visibility; cleanup owns durable retry evidence | Current doubles record calls but do not model prior readable versions, accepted-with-error, barriers, or cleanup reload/retry | Insufficient for recovery/idempotency/concurrency; stateful fake and focused cases in `TST-004` | | Restore committed-state authority and partial replacement | Restore plan/execution/workflow tables, conflict/force, malformed manifest, cache tests | Restore package owns snapshot selection, conflict policy, manifest-last commit, and resumable failure | No generation change, committed-vs-stray scope, partial forced overwrite, plan/lock race, same-size replacement, or foreign-path case | Insufficient at durable transition boundaries; package-level stateful cases in `TST-005` | | Composition has one resolved configuration authority | Production assembly is exercised; injected environments generally leave `Env.Config` nil | App composition tests protect production-shaped dependency injection | No test intentionally diverges the two configs, so the impossible split fixture remains allowed | Insufficient seam fidelity; one authority test in `TST-006` | | Adapter protocol, cancellation, resource, output, and secret boundaries | Strong argument/schema, retry/status, direct-child timeout, ordinary output, S3 one-page, and override-secret tests | Each adapter owns protocol behavior; shared subprocess/file acquisition owns mechanical limits and redaction | Descendant kill, inherited/raw secret leakage, scheme restriction, streaming cancellation, close observation, pagination progress, and non-regular/oversized outputs are absent | Insufficient at adversarial boundaries; targeted additions in `TST-007`, not duplicated protocol matrices | | Bounded transcription concurrency and cancellation | Concurrency bound/order and ordinary adapter-error tests | Transcribe owns dispatch/cancel completion; WhisperX fake must honor concurrent interface use | The fake races and no barrier-controlled mid-dispatch cancellation exists | Insufficient; `TST-001` restores race signal and `TST-008` protects cancellation | | Ordinary-stage input transitions and run-local output authority | Broad prepare and transcript-stage behavior suites | Prepare owns source replacement/deduplication; stage/shared acquisition owns requested path and multi-output commit behavior | Stale previous state, duplicate audio, alternate adapter path, and partial multi-output materialization are absent; schema tables already overlap heavily | Add only the transition/authority cases in `TST-008`; current schema and protocol coverage is otherwise sufficient | | Extraction identity, immutable promotion, and consumer evidence | Strong adapter validation, lifecycle/reuse, catalog hydration, explicit-consumer, and low-level promotion tests | Extract owns direct-input fingerprint and lifecycle; artifact/file owners protect evidence and promotion | Direct input mutation and one assembled unsafe-root case are absent. Post-install orphan residue is not advertised authority and would require private choreography | Add the two marginal cases in `TST-009`; reject a dedicated orphan-residue test and reuse `TST-002`/`TST-003` owners | | Analyze effective selection, optional inputs, dependencies, and passthrough contract | Strong source-family, catalog, order/cycle, reuse, local-previous, and publish-filter tests | Analyze/app composition owns effective selection; artifact owners retain source parsing | Optional built-ins, selected-disabled prerequisites, simultaneous errors, actionable guidance, and accepted passthrough fields are not protected | Insufficient at cross-owner seams; focused additions in `TST-010`, without repeating all source spellings | | Artifact/source identifiers and remote/local mapping reject hostile structured input | Deterministic tables cover representative traversal, source families, and mappings | `pathsafe` and `artifactpolicy` are pure security-sensitive owners; restore mapping is a secondary seed source | No fuzz target probes arbitrary separators, normalization idempotence, round-trip mapping, or no-escape properties | Add focused seeded property fuzzing under `TST-013`; generic YAML/JSON parser fuzzing has lower marginal value | | CLI parsing and operator rendering remain compatible and actionable | Command tables cover valid/invalid flags, dispatch, status/validate rendering, and semantic error fragments | App command tests own supported invocation language and actionable output | A few broad workflow cases repeat downstream policy, but no consequential CLI gap was established beyond `COR-023` guidance | Sufficient once `TST-010` asserts repaired guidance; retain semantic fragments rather than exact full prose | | Representative assembled workflows prove boundary composition | App/stage tests run real filesystem/config/manifest collaborators with adapter fakes | App owns a small number of end-to-end success/failure cases; focused packages own details | Stage metadata checklists and a six-adapter generic failure matrix repeat focused suites and generic runner behavior | Overprotected and costly in places; consolidate under `TST-015`, retaining one assembled success and one generic failure | | Default suite is deterministic, isolated, offline, and credential-free | Normal full suite passes in about 3.5 seconds using temp dirs, loopback HTTP, and test-binary subprocesses | Each test owns cleanup of process-global state; repository execution owns the aggregate signal | A config-secret test leaves environment values behind and fails under repeated execution; no tests use `t.Parallel` | Offline/credential isolation is sufficient; determinism is not until `TST-011` is fixed | | Race diagnostics remain trustworthy | Baseline and shuffled race runs exercise all packages | Concurrent consumers and their doubles jointly own race-safe fixtures | WhisperX fake request capture races, causing the only observed race failure | Insufficient until `TST-001`; no second production race was observed | | Repository automation enforces supported validation | Tag release automation cross-builds Linux, macOS, and Windows binaries | Normal change automation should protect test/vet/build compatibility before release | No PR/push test, vet, or build job exists; tag publishing is not tied to a validated revision | Insufficient; record the proportional enforcement decision in `TST-012` | ### Doubles, helpers, redundancy, and brittleness Most tests follow the preferred collaborator order. Pure validation uses real values; filesystem and workflow tests use real temporary directories and local manifests; HTTP tests use loopback servers; and protocol subprocess tests run the current test binary. Those subprocess argument assertions are interaction contracts, not mock choreography, and should remain. Storage and publish are the important exception: call-recording stubs cannot express version visibility, accepted-with-error outcomes, pagination progress, or concurrent commit barriers. `TST-004` and `TST-005` therefore call for one shared stateful object-store fake. The WhisperX request-capture fake is the only double proven unsafe for its actual concurrent consumer (`TST-001`); sequential fakes do not need blanket synchronization. The largest test, `config.TestLoadAndValidate`, spans roughly 949 lines and combines strict YAML loading, normalization/default checks, and validation in one repeated full-document table. It matches semantic error fragments rather than exact full messages, but its fixture duplication obscures which layer failed and raises change cost; `TST-014` owns a split into strict-load cases, constructed validator tables, and a small assembly sample. By contrast, the large artifact requirement table and protocol helper-process tests express meaningful contract cases and should remain table-driven. Existing helpers are mostly domain-specific setup; no generic fixture framework is justified. The broad stage metadata checklist and six-adapter assembled failure matrix repeat focused per-stage protocol/behavior suites and generic runner result mapping. `TST-015` names the stronger protection that must remain before those rows are deleted: focused owners plus one representative assembled success and one representative terminal failure. No oversized snapshots or golden files were found. Exact full `err.Error()` equality is rare; semantic fragments, `errors.Is`, typed errors, and externally visible protocol arguments dominate, so no repository-wide error-assertion rewrite is warranted. ### Determinism, coverage, fuzzing, and automation `go test -count=1 -cover ./...` passed all packages in 3.49 seconds. Package coverage ranged from 69.8% in `internal/manifest` to 100% in `internal/logging`, with `cmd/narratio` at 0% because it has no test files. These percentages are not a quality score: manifest's lowest result aligns with the uninjectable save-failure branch in `TST-002`, while logging's 100% does not justify more tests and the placeholder notification path remains an architectural decision in `ARC-004`, not a coverage target. The default suite uses no live services, paid APIs, ambient credentials, or fixed external ports. It uses temporary paths, loopback servers, and helper subprocesses, and no test calls `t.Parallel`; normal runtime does not justify parallelizing process-global fixtures. A few bounded sleeps exercise actual time/process behavior, but the observed order defect is instead deterministic: `go test -shuffle=on -count=3 ./...` failed in 4.71 seconds with seed `1786373771816345415` because `TestLoadSecretsFromConfigLoadsValidFiles` leaves two secret environment variables set. The isolated command `go test -shuffle=1786373771816345415 -count=3 -run '^TestLoadSecretsFromConfigLoadsValidFiles$' ./internal/app` reproduced failures on repetitions two and three. `TST-011` owns restoration of the prior environment state. `go test -race -shuffle=on -count=1 ./...` failed in 54.21 seconds with seed `1786373816980315094`; the only reported race was the known WhisperX fake in `TST-001`, now reached by `TestTranscribeStageConcurrencyBounded`. Every other package passed. Barrier-controlled cancellation/concurrency tests should replace additional timing dependence where `TST-008` touches that path, but no general flakiness conclusion follows from the bounded evidence. There are no fuzz tests. The highest-value additions are seeded property tests for confined path normalization/join and artifact source identifiers, with properties such as no panic, no root escape, stable normalization, and valid remote/local round trips (`TST-013`). Existing YAML/JSON tables already protect Narratio's own strict schemas; indiscriminate fuzzing of standard-library decoders or every manifest/config wrapper would add less marginal value. `.woodpecker/release.yml` runs only for tags and cross-builds release binaries with Go 1.25 before publishing them. No automation runs the repository's local test, vet, or build requirements on ordinary changes. `TST-012` recommends a normal validation workflow and requires a release to consume or repeat the validated revision. The 54-second race suite belongs in automation after `TST-001` is fixed, at a frequency chosen against its cost; repeated shuffled runs are suitable for scheduled/audit diagnostics rather than necessarily every change. ## Path, Artifact, Filesystem, And Lock Conclusions ### Canonical ownership and normalization `internal/artifacts/paths.go` owns the campaign/session layout, run-local layout, previous cache, Notarius bundle, spool, and audio-cache constructors. `internal/artifacts/s3_keys.go` owns session/run/current and published-output keys. `internal/pathsafe` is the shared lexical boundary for slash-normalized relative destinations: it rejects empty, absolute, drive-qualified, traversal, and leading-backslash forms, normalizes mixed separators, and verifies a joined destination remains lexically under its root. `internal/artifactpolicy` adds configured source/destination policy without performing filesystem I/O. Most production callers consume those owners directly. Previous-cache planning normalizes configured and manifest-derived relative paths before calling the path constructor, and restore derives a normalized relative path before installing it. One ad hoc reconstruction remains in publish: `resolvePublishRunManifestSource` joins the literal `manifest.json` to an already-derived run root instead of using the canonical run-manifest helper; `DUP-002` assigns that maintainability decision to Stage 11. The constructors themselves do not enforce that campaign, session, run, or artifact-relative components are safe opaque segments. Configuration currently checks session identifiers and campaign identity only for presence. As a result, traversal-bearing operator identity reaches both local `filepath.Join` and remote `path.Join`; `COR-002` records the concrete namespace escape. The exported previous-artifact helper also promises a path under `previous/artifacts` while accepting a traversal value, although its current production callers validate first. That helper-level contract should be fixed with the same boundary rather than treated as a separate defect. ### Artifact resolution The complete resolution chain is deterministic and matches the documented source-family distinctions: - built-ins normalize against a fixed registry, prefer matching producer outputs from the manifest, validate content shape, then use a canonical session fallback; missing state returns `SessionArtifactNotFoundError`; - configured sources resolve only through explicit runtime-catalog availability, preserve generated-versus-disabled-file provenance, validate non-empty text, and return the same typed missing condition when planned but unavailable; - extraction hydration requires a successful current extract record, exact configured definitions, producer/receipt identity, compatible contract and external provenance, confined regular files, a complete index/lane set, and matching checksums before making any source available; - previous-session planning sorts requirements and records, validates relative cache destinations, loads current remote state with expected campaign, session, and run identity, and hydrates only explicit remote objects into the local cache. Analyze-time resolution is then local-only, preferring a manifest input path before the documented filesystem fallback and returning a typed missing error; and - current-state loading has distinct typed missing-pointer and missing-manifest errors, rejects empty or malformed state, and validates expected campaign, session, and run identity or pointer/manifest run consistency. Only extraction claims checksum/contract/external-provenance enforcement at resolution time, and its implementation enforces all three. Built-in, configured, and previous-cache resolvers enforce their documented content and availability rules but do not re-hash ordinary manifest records. That is not a Stage 3 contract mismatch; Stages 5 and 10 remain responsible for deciding whether restore or analyze threat models require stronger trust than the documented previous-cache fallback and manifest-aware built-in behavior. ### Filesystem mutation mechanics The mutation inventory separates lexical policy from explicit-path mechanics: | Mechanism | Normal-process atomicity and cleanup | Confinement and durability conclusion | | --- | --- | --- | | `WriteFileAtomic` | Same-directory temporary file; write, file sync, close, chmod, rename; removes a failed temporary file; replaces an existing destination where the platform rename permits it. | Accepts an explicit destination as intended, but follows symlinked destination ancestors and does not sync the parent after rename. | | `CopyFileAtomicWithChecksum` / `CopyFileAtomic` | Opens the explicit source, copies and hashes through a same-directory temporary file, syncs/closes/chmods, then renames; prior destination survives failures before rename. | Does not require a regular source, follows source and destination symlinks, and does not sync the destination parent. Current canonical materialization callers validate content but inherit destination confinement and crash-durability findings. | | `InstallDownloadedTempFile` | Chmods and renames a caller-supplied sibling temporary file; same-filesystem placement is established by current audio, previous-cache, and restore callers. | Does not sync the downloaded file or destination directory. The object-store downloads close their path-based files but expose no completed-data sync guarantee. | | `PromoteDirectory` | Copies to a temporary sibling, permits only regular files/directories, uses confined source handles and identity checks, syncs files/directories, atomically installs without replacement, syncs the destination parent, cleans failures, and reports unsupported platforms before creating the promotion tree. | Source replacement, source symlinks/non-regular entries, existing or concurrently-created destinations, same-filesystem placement, and platform behavior are strongly handled. Destination ancestors remain path-based and can be symlinked or replaced. | | Cleanup removal | Refuses empty roots/targets, lexical root deletion and traversal, wrong final-node type, and a final target that is a symlink. Missing targets are idempotent. | It does not inspect root or ancestor components and performs `RemoveAll` after a path-based check, so a symlinked ancestor can redirect deletion outside the root. | Low-level file and storage helpers correctly accept explicit destinations and do not infer stage, workspace, or cleanup policy. Policy belongs in their callers. The current caller-side checks are lexical and final-node checks, however, so they cannot prevent destination-ancestor symlink traversal or replacement. `COR-003` consolidates the write, promotion-destination, and destructive-cleanup variants under that single root cause; later stage and adapter audits should reference it rather than duplicate it. Single-file writers preserve the prior visible file on failures before rename, but unlike directory promotion they do not sync the containing directory after rename. Download installs additionally do not establish a file-sync boundary. `RSK-002` records the resulting crash/power-loss durability gap for canonical files and manifests. Stage 3 also confirmed that `DUP-001` duplicates this same incomplete persistence sequence across both manifest save paths. Runtime layout and copied/generated files use fixed requested modes `0755` and `0644` subject to process umask, including transcripts, artifacts, logs, manifests, and the lock. No operating contract requires a restrictive umask or documents an ownership/mode boundary. `RSK-004` records the private-data exposure risk rather than assuming a deployment-specific parent directory is always restrictive. ### Lock scenario and focused tests The local lock is an atomically created `O_CREATE|O_EXCL` sentinel. This is sufficient to serialize two live contenders before either reads a manifest, and the existing focused conflict test exercises that mechanism. It is not an OS-owned advisory lock: any existing sentinel conflicts without interpreting the recorded PID/time, process death cannot remove it, and recovery is manual. Release closes then unlinks, but the runner discards its error. An unlink failure can therefore let a command report success while every later invocation is blocked. `RSK-003` resolves Scenario 10 and the Stage 2 release question. Existing focused tests strongly cover lexical mixed-slash/traversal handling, artifact-source ordering and typed missing states, extraction trust checks, current-state identity, ordinary atomic replacement/temp cleanup, source-side promotion races and special files, no-replace installation, permissions, and basic lock acquire/conflict/release. They do not cover unsafe identity segments, destination-ancestor symlinks or replacement, cleanup through a symlinked ancestor, directory-sync/file-sync guarantees, stale-lock ownership/recovery, release failure, or assembled concurrent runners. `TST-003` assigns a narrow cross-package regression set to Stage 12; each confirmed finding below also identifies its smallest behavior-level test. ## Publish, Remote Commit, Lock, And Cleanup Conclusions ### Prerequisites, sources, and deterministic order Enabled publish requires successful `prepare`, `transcribe`, `merge`, `polish`, `normalize`, `trim`, `render`, and `analyze` session records before any remote write. `extract` is deliberately absent: disabled extraction is valid, while an explicit required extraction output still fails artifact resolution unless the current extraction state is complete and trusted. Publish disabled or `upload_run` disabled returns ordinary stage success with `skipped` metadata; it is not the self-skip claimed by the focused stage document. `COM-002` records that contract mismatch and resolves the publish portion of `ARC-002`. Publish builds all plans before uploading. Configured-artifact selection applies only to `narratio.artifact.*` rules; built-in and extraction rules are unaffected. Static and remote locks are merged before execution, with static rules winning. A matching lock intentionally skips an output even when required and preserves an existing published destination. An unselected configured output is recorded as unselected before lock handling. Missing unlocked optional outputs skip; missing unlocked required outputs fail. `--force` changes runner reuse and invalidation only: it never enters output resolution and cannot bypass either kind of lock. The upload sequence is deterministic: 1. run files sorted by slash-normalized relative path; 2. resolved published outputs in validated configuration order; 3. previous-cache files sorted by relative path; 4. the fixed `current/manifest.json`; and 5. `current/run_id.txt`, always the final object-store call. The run archive includes `manifest.json`, receipt/stderr diagnostics, and every other non-directory entry except exact `audio/**` and `extract/notarius-output/**` paths. Similarly named paths remain included. Audio is not uploaded. Previous-cache inclusion recursively includes every non-directory entry under the session's durable `previous` directory. Neither walk rejects symlink entries before storage opens the local path, which is the confidentiality defect in `COR-005`. ### Remote-current authority and partial outcomes The storage boundary accepts explicit local paths and keys and implements upload as unconditional object replacement. It has no transaction, conditional write, generation check, rollback, or indeterminate-outcome query. Those are publish protocol responsibilities rather than storage policy. Every successful upload before the pointer is durable partial state; retry uploads the complete plan again and overwrites the same destinations. Partial run/session objects are not removed. `COR-001` separately means a later invocation can combine a new run ID with a stale remote run prefix. `LoadCurrentState` reads the pointer first, then the fixed manifest, and can reject pointer/manifest run disagreement when its caller requests run validation. Previous-cache planning does request it; Stage 5 found that the shared restore/status discovery path does not. Thus the pointer is the intended commit marker, but `COR-008` confirms that two primary readers can accept a manifest from a different run. Independently, overwriting the fixed manifest before the pointer does not preserve the prior coherent pair; `COR-004` owns that publish-side root cause. | Publish boundary or durable remote state | Remote-current interpretation | Retry and cleanup consequence | | --- | --- | --- | | Planning/prerequisite/source resolution fails | No upload occurs; any prior pointer/manifest pair remains current. | Non-succeeded local publish reruns; cleanup is ineligible. | | A run, published-output, or previous-cache upload fails | Earlier objects remain or overwrite existing keys; the prior current pair is still intact because the fixed manifest was not reached. With no prior pair, no current state exists. | Retry unconditionally reuploads the whole plan. Cleanup is ineligible. | | Current-manifest construction fails | All planned data objects may exist, but the prior current pair remains intact. | Same retry behavior; cleanup is ineligible. | | Current-manifest upload definitely fails before remote acceptance | The prior pair remains intact; no pointer attempt occurs. | Same retry behavior; cleanup is ineligible. | | New current manifest is accepted while the old pointer remains | First publish has a manifest but no pointer and restore fails on the missing pointer. A replacement publish has old pointer/new manifest disagreement: previous-cache loading rejects it, but restore/status discovery accepts it under `COR-008`. | A successful retry repairs the pair; cleanup is ineligible until then. This includes the ordinary window between the final two uploads. | | Pointer upload returns an error | If not accepted, the disagreement above remains. If the service accepted the write but the response was lost, remote current is committed although local publish is marked failed. The interface cannot distinguish these outcomes. | Retry is conservative and overwrites again; automatic cleanup does not run for the failed local outcome. | | Pointer upload succeeds | Pointer and manifest identities agree and the new run is current. | The stage returns `uploaded=true` and `current_pointer_written=true`; automatic cleanup may become eligible. | | Remote commit succeeds, then terminal session/run persistence fails | Remote current remains committed. A session-save failure leaves local publish running and causes republish; a run-save failure leaves session publish succeeded and ordinary retry skips it. | The latter boundary can permanently miss automatic cleanup under `COR-006`; remote state itself remains authoritative. | The current manifest is generated before commit with `current_pointer_written=false`, while the local post-upload result records the same field as true. Current-state readers correctly derive authority from the actual pointer and ignore that remote metadata, and cleanup correctly uses the post-commit local record. `ARC-003` records the future-consumer ambiguity rather than treating the precommit snapshot as remote authority. ### Lock authority and cleanup truth tables Loaded static and remote locks are honored even for forced publish, but the remote lock store is an unsynchronized snapshot. Publish loads it before the local session lock, and lock add/remove performs an unconditional read-modify- write without a remote generation check or the runner's session lock. Concurrent operator mutations can lose one another, and a lock added after publish's load does not protect that in-flight upload. `RSK-005` records this limit; sequential lock behavior and static precedence are otherwise correct. Automatic cleanup uses the following gate. “Confined” here means the existing lexical/final-target validation; symlinked ancestors remain the shared `COR-003` defect, and stale run-derived targets remain a `COR-001` consequence. | Policy and local publish record | Automatic action | | --- | --- | | Both cleanup policies false | No cleanup lookup, deletion, or metadata mutation. | | Either policy true, but `publish` absent from this invocation's executed list | No cleanup, even if the session publish record is succeeded and records a commit. This one-shot behavior is `COR-006`. | | Publish executed but its session status is not succeeded | No cleanup. | | Publish succeeded, but publish/upload is disabled, `skipped=true`, `uploaded` is not true, `current_pointer_written` is not true, or pointer key is empty | No deletion; cleanup-skip reason is persisted. | | Publish succeeded with enabled upload, `uploaded=true`, pointer true/key present, and spool policy true | Validate the run-scoped spool target beneath the configured spool root, then delete it. Spool-only completion metadata is persisted. | | The same commit gate with workspace policy true | Validate the run-scoped work target beneath the workspace root, then delete it. Completion metadata is only mutated in memory and is lost under `COR-007`. | | Either requested deletion fails | Record best-effort failure metadata and fail the invocation. The succeeded publish remains reusable, so ordinary retry does not retry cleanup (`COR-006`). | Manual `clean` is a separate explicit operator boundary and correctly does not require a prior publish or commit metadata. Session cleanup requires an explicit campaign/session resolution and deletes the confined session work and spool trees. `clean --all` rejects session/campaign selectors, deletes the confined workspace `work` tree and non-symlink spool-root children, and optionally clears the configured audio-cache namespace. Cache deletion requires `--clear-cache`; `--dry-run` performs validation and reports without removing. There is no clean force flag. Publish `--force` therefore cannot reach or weaken manual or automatic target validation. Focused tests cover successful ordering and exact exclusions, source-family selection, required/optional/locked rules, force with a preloaded remote lock, pointer absence on selected failures, ordinary cleanup eligibility, unsafe final targets, and manual dry-run/scope behavior. They do not seed a prior current pair across a manifest/pointer failure, model ambiguous upload success, exercise retry after partial commit, reject symlink upload sources, assert workspace-cleanup metadata durability, retry failed cleanup, or model concurrent remote lock writers. `TST-004` assigns the smallest stateful protocol cases to Stage 12. ## Restore, Audio, And Previous-State Conclusions ### Discovery authority and caller policy Restore resolves configuration and storage, then delegates pointer/manifest loading to `artifacts.LoadCurrentState`. Campaign and session expectations are always checked. The helper's run check is optional, however: previous-cache planning and previous-readiness inspection set `ValidateRunID=true`, while restore/status discovery does not. Missing pointer or manifest is fatal to restore, displayed non-fatally by status, and is skipped only when every previous-artifact requirement is optional. Malformed or inconsistent previous state remains an error even for optional requirements. Those distinct missing- state policies are appropriate; omitted restore/status run validation is part of the broader snapshot defect in `COR-008`. Restore then lists the entire session prefix instead of deriving its file set from the discovered manifest. It maps the fixed current manifest to local `manifest.json`, includes `transcripts/**` and `artifacts/**`, optionally includes `audio/**`, and excludes current/run archives, logs, reports, generated configuration, inputs, and the current session's archived `previous/**`. Required previous-session cache objects are planned separately from that prior session's current manifest. Lexical path normalization and root-relative joins reject traversal and produce deterministic local paths; `COR-002` still owns unsafe identity components and `COR-003` owns filesystem-link confinement. Prefix-wide listing is not a committed snapshot. Failed publish objects, stale destinations left by older runs, and locked/unselected leftovers can all enter a restore even when absent from the accepted manifest. Execution later downloads the same mutable keys again, and downloaded current/previous manifests are not revalidated against the discovered run ID. `COR-008` consolidates these scope/version/run-binding failures and corrects the Stage 4 reader truth table. ### Planning, force, dry-run, and local serialization Actions sort by local relative path and then remote key. Classification is: | Local target and remote metadata | Without force | With force | | --- | --- | --- | | Missing | `download` | `download` | | Directory where a file is expected | `conflict` | Still `conflict`; command-level handling is defective under `COR-009`. | | Audio with positive remote size and equal local size | `skip_same` without content comparison | Same `skip_same`; force does not refresh it. | | Audio size mismatch or unavailable remote size | `conflict` | `download` | | Non-audio positive-size mismatch | `conflict` | `download` | | Non-audio equal/unknown size and equal downloaded checksum | `skip_same` | `skip_same` | | Non-audio equal/unknown size and different downloaded checksum | `conflict` | `download` | Normal differing files therefore require explicit force and identical files remain untouched. Audio's size-only shortcut and cache validation are the integrity risk in `RSK-007`. Final symlinks are followed by `os.Stat` and can be classified as same; ancestor/final-link trust should be repaired with the shared filesystem capability required by `COR-003` rather than with restore-only lexical checks. Dry-run returns after discovery, planning, and summary rendering. It does not create the session layout, acquire a lock, write a report, populate audio cache or spool, install a file, or mutate remote state. It does download pointer, manifest, and same-size non-audio bodies to system temporary files, which are removed. Thus it is durable-session pure, not literally free of local temporary writes. The same checksum classification can download a differing object once during planning and again during forced execution; current and previous manifests are also downloaded at discovery/planning and again for installation. `EFF-001` records this avoidable I/O and the documentation precision issue. Executable restore acquires the local session lock only after its plan is complete. It does not reclassify `skip_same`, `conflict`, or download decisions under the lock. Together with incremental installation and intentional lack of rollback, this creates the coherent-local-transition risk in `RSK-006`. `--force` cannot bypass path joining or the local session lock, but the command only blocks conflicts when force is false. A directory conflict therefore survives a forced plan, is ignored by execution, and can coexist with a successful report and newly installed manifest (`COR-009`). ### Execution, manifest-last behavior, and retry Execution filters to `download` actions, preserves their sorted order, moves the single current-session manifest action to the end, and rejects multiple manifest downloads. Ordinary files download to sibling temporary files and install by rename. The manifest temp is decoded and checked for requested and discovered campaign/session before rename, but not for the discovered run ID. Audio delegates to the shared spool/cache materializer. A successful execution then writes `reports/restore-latest.json`; the report is diagnostic and is the only intended write after manifest installation. | Failure boundary | Durable local result and retry behavior | | --- | --- | | Discovery or planning | No session layout/report/restored file is written; system temporary reads are cleaned. Fix remote/config state and retry. | | Dry-run | No durable session mutation; reported actions are recomputed on apply. | | Layout or lock acquisition | Layout creation can precede a lock failure, but no planned file or report is installed. Retry after lock recovery. | | Unforced conflicts | No planned file is installed; a failed conflict report is persisted under the lock. Resolve or retry with force. | | Non-manifest download/install | Earlier installs remain; the old local manifest remains; the failing temp is removed and a failure report is attempted. Retry reclassifies completed files as same. | | Audio download/validation/materialization | Download failure removes its temp and preserves any prior spool destination; a post-download validation failure can leave the newly installed invalid spool file. A destination copied before cache-population failure also remains. Retry overwrites/reuses those states deterministically. | | Manifest download/decode/identity/install | All earlier files remain; failures before rename preserve the prior local manifest. Retry must complete the remaining plan; forced partial replacement can make the old manifest describe changed files (`RSK-006`). | | Manifest installed, report write fails | Restored durable state is installed and authoritative despite command failure; report may be absent/stale. Retry normally classifies files as same and can recreate the report. | | Report succeeds, summary write fails | Restore state and success report remain complete; only command output failed. | There is no rollback, transaction marker, or runner check for an incomplete restore. Manifest-last prevents a new remote manifest from being installed before its files, but it cannot keep the old local manifest coherent after a forced partial overwrite. Individual rename visibility is good in an ordinary process; `RSK-002` remains the shared crash-durability gap for downloaded-file, manifest, and report installation. ### Audio and previous-cache identity `audio.MaterializeS3Audio` is correctly shared by prepare and restore. Its cache namespace includes bucket and full object key. A cache miss downloads through a sibling spool temp, validates non-empty/expected size, atomically copies to the destination with a computed checksum, then optionally populates cache. A cache hit avoids storage and copies directly. The stored ETag and computed checksum are not bound to cache validity: any nonempty cache file of expected size is accepted, and existing restore audio of equal size is skipped without reading either body. `RSK-007` records stale/corrupt same-size reuse. Failed downloads clean temporary files, while post-install validation/cache failures leave the explicit partial states in the table above. Previous requirements come only from enabled configured artifacts, deduplicate by artifact name with required winning, and sort deterministically. With no previous session ID, required requirements fail and optional ones skip. With an ID, planning strictly validates campaign/session/pointer-run identity, always maps the previous current manifest into `previous/manifest.json`, and maps available artifact objects beneath the current session's `previous/` cache. Missing pointer/manifest or artifact objects fail required requirements and skip optional ones. Prepare consumes the same plan but intentionally overwrites its managed cache and records checksums/inputs; restore applies local conflict policy. Analyze later resolves these files locally without storage, preferring a matching main-manifest input and otherwise using the deterministic cache path. The remote manifest does not retain a source-to-destination map for ordinary published outputs. Previous planning tries the producer's local relative path, then any `published_paths` entry with the same basename, then the current artifact output path. A custom publish destination with a different basename is unresolvable; duplicate basenames are ambiguous. `COR-011` owns that identity loss. Separately, status/validate only prove that the prior current pair exists: they neither apply `BuildPlan`'s artifact-object checks nor its optional missing- ID policy, so they can report missing optional state as an error or missing required objects as ready (`COR-010`). Restored manifests preserve remote host-local absolute path fields. Top-level run/work/spool fields trigger the already confirmed `COR-001` on the next invocation. Output/input records are also trusted preferentially when their old absolute path happens to exist, allowing later consumers to read outside the new workspace instead of the restored canonical copy; `RSK-008` records this distinct restored-reference risk. Focused tests strongly cover typed current-state failures when run validation is enabled, restore campaign/session mismatch, default/include-audio mapping, traversal rejection, normal same/conflict/force actions, deterministic previous requirements, required/optional missing remote state, cache miss/hit/refresh, download cleanup, manifest-last validation, conflicts, lock failure, reports, and restore-to-run/analyze workflows. They omit the exact committed-snapshot, forced-directory, plan-under-lock race, partial forced rollback, same-size audio mutation, custom publish destination, optional operator-readiness, and foreign absolute-path cases above. `TST-005` assigns one stable behavior test per root invariant to Stage 12. ## Configuration And Composition Conclusions ### Discovery, precedence, defaults, and validation order The process entry point delegates directly to `app.Execute`, which owns command parsing, exit classification, output streams, and dispatch. Pipeline selection uses an explicit path first and otherwise the first existing system default. Campaign selection rejects simultaneous ID and file selectors, loads an explicit file or registry/default ID, and checks that the selected ID agrees with the loaded campaign. Session selection uses an explicit path, then the first local default, then the configured campaign/session remote key when a session ID is available. Session stable inputs override campaign values; their resolved value retains the owning config path and source kind. Pipeline, campaign, rendered-template, and session inputs all use the same known-field YAML decoder. Defaults are applied before resolution and validation. Pointer booleans and integers distinguish omission from explicit false/zero; an explicitly empty module list remains empty. Ordinary empty scalar and publish-output values receive documented defaults, while `normalize.output_path` tracks YAML presence so an explicit empty value is rejected. Enabled Notarius paths are resolved relative to the pipeline file. A concrete session template is rendered from a narrow variable set, rejected for missing/unknown/unused values, decoded through the ordinary session loader, resolved, and fully validated before replacement. The final order is pipeline load/defaults, campaign load, session load/selection, stable-input resolution, then pipeline, campaign, session, and cross-config validation. The remote-session fallback necessarily builds enough pipeline/campaign/storage state to fetch the session before final validation; secrets are loaded before constructing that object store. This ordering is otherwise coherent, but the shared decoder's trailing-document check is incorrect under `COR-012`. ### Operator field-to-consumer trace | Operator field family | Default/normalization and validation | Runtime consumer and conclusion | | --- | --- | --- | | Stage enablement/order, concurrency, locks, workspace/cache/campaign/spool roots | Canonical stage order is fixed; enabled stages, positive global concurrency, lock settings, and non-empty principal roots are validated. Relative configured roots remain supported. | Planning selects enabled/requested stages; the runner derives layout, local locks, artifact/manifest stores, and worker bounds from the resolved config. No unconsumed execution selector was found. | | Campaign/session IDs, stable inputs, audio source, and previous session | Resolution enforces campaign consistency and stable-input precedence; validation requires one audio mode and required campaign/session values, with S3 bucket cross-checks. | Selection, layout, audio materialization, publish keys, and previous-cache planning consume these values. Unsafe ID segment syntax remains `COR-002`; the previous-session CLI expectation is incomplete under `COR-015`. | | WhisperX, Seriatim, Audita, and Scriptorium command/protocol settings | Defaults fill endpoints, commands, retry/concurrency/tuning, timeouts, and artifact contracts; enums, safe paths, environment-variable names, dependencies, and cycles are checked. | Adapter constructors and transcript/analyze/trim/render stages consume the settings. Parseable non-positive durations can pass configuration but fail at composition or stage execution under `COR-013`. | | Notarius extraction and lane settings | Disabled configurations stay lightweight; enabled configurations require command, config/work paths, declared inputs/outputs, unique lanes, timeout, and positive concurrency. Relative paths are anchored to the pipeline file. | The subprocess runner is constructed only when extraction is selected and Notarius is enabled. Stage 9 confirmed that resolved settings reach the invocation/fingerprint/output-contract boundary; no unconditional external work was found. | | Analyze artifact source/destination, previous requirements, trim/render bounds | Source/destination identities, normalized relative paths, uniqueness, dependency existence, cycles, and bounds syntax are validated. | Artifact resolution and Scriptorium/Seriatim stages consume the values. Deeper dependency semantics remain assigned to Stage 10 rather than being inferred from configuration shape. | | Publish outputs/backend, S3 storage, cleanup, and remote locks | Publish sources/destinations and S3/env/path prerequisites are cross-checked; cleanup and lock defaults are explicit. | Publish, storage construction, remote locking, restore, and cleanup consume these fields. The storage backend selector itself is not validated and is not authoritative under `COR-014`. | | Filesystem secrets directory and credential variable names | The directory is optional; relative values intentionally use process working directory. Entry names and configured env-var names use the environment-name grammar. Existing process values win. | Secrets are installed into the process environment before adapter/store construction; values are not copied into config, metadata, reports, or logs. Entry-type trust is unsafe under `RSK-010`. | | Notification backend, recipient, and timeout | Fields are accepted and the timeout is syntax-checked. | Production composition always injects the no-op sender, so these operator-facing settings currently have no behavioral consumer. `ARC-004` assigns the boundary decision to Stage 7. | Validation is strong for enumerations, artifact paths, cross-stage dependencies, unique destinations, session/campaign consistency, environment names, and positive concurrency/tuning. The deliberate empty-value/default behavior in the maintained examples agrees with `docs/config.md`. Four maintained pipeline examples are loaded with representative local or S3 sessions by the config suite, and extraction contract tests preserve their published Notarius shapes. Examples contain only fictional endpoints and credential variable names, not secret values. The material documentation drift is the notification block: the public annotated example presents settings as optional configuration while the internal overview alone explains that notification is a placeholder/no-op. ### Secrets, conditional composition, and lifecycle The filesystem secret loader reads directory entries in deterministic name order, skips directories and invalid environment names, trims only trailing line endings, preserves existing environment values, and reports only the directory and counts. Error and success messages name fields/files but never include secret content. S3 and Audita resolve values only at their adapter boundaries. No config serialization, manifest, report, stage metadata, or log path retaining a raw secret value was found. The runner supplies lightweight WhisperX, Seriatim, Audita, and Scriptorium wrappers when callers do not inject them. Those constructors do not connect to external services or start subprocesses. Notarius construction is conditional on selected extraction, and object-store/remote-lock construction is conditional on selected behavior that needs remote state. These collaborators own no closeable process-level resource: HTTP clients and AWS clients are reused value wrappers and subprocesses are owned per invocation. Thus there is no adapter shutdown leak at the composition boundary. Loading secrets again in the object-store helper is redundant but bounded and preserves ordering; it is not a standalone efficiency finding. Successful remote session discovery is the exception to otherwise explicit temporary-file ownership. It downloads `session.yml` to a system temporary file and retains that path in resolved provenance without any success cleanup. Full runs later copy the file into canonical inputs but still leave the private temporary copy; read-only and single-stage commands leak it directly. This is recorded as `RSK-009`. Tests can inject an `Env` and production supplies defaults only for nil collaborators. This is an effective no-live-credential seam, and focused tests cover enabled/disabled composition without network access. The seam can, however, retain an injected `Env.Config` different from the separately supplied resolved config, splitting layout/manifest identity from secrets, adapters, locks, and stage behavior. Production never exposes that combination, so `TST-006` assigns a guard/fixture decision to Stage 12 rather than treating it as a production defect. Mutable package-level constructor seams and small single-stage command wrappers are likewise test/dispatch mechanics, not new architectural findings. ## External Adapter And Shared-Support Conclusions ### Boundary and resource matrix Stages depend only on Narratio request/result interfaces. HTTP, multipart, AWS SDK, Smithy, `exec.Cmd`, and process-state types remain private to their adapter packages; no transport type or retry policy leaks into stage code. Arguments, working directories, environment changes, generated invocation configuration, stream routing, exit adaptation, and first-pass output validation are likewise adapter-owned. Production callers choose run-local paths and materialize validated results, which is the intended division of policy. | Boundary | Acquisition | Cancellation and waiting | Release and conclusion | | --- | --- | --- | --- | | WhisperX HTTP | Opens the audio file for each attempt, constructs multipart content, and obtains one response. | Each attempt has its own timeout; parent cancellation suppresses retries and interrupts retry timers and HTTP I/O. Building the multipart body itself is not cancellation-aware. | Audio files are closed and response bodies are closed on every response path. Responses are bounded to 10 MiB and installed only after successful JSON validation. Request prebuffering remains `EFF-002`; retry/status behavior otherwise matches the integration contract. | | Shared subprocess | Opens zero, one shared, or two separate log files, then starts one direct child and waits synchronously. All subprocess adapters use this owner. | Parent cancellation and optional timeout reach `exec.CommandContext`; `Wait` always runs for a successfully started direct child. Only that process is killed, not its descendants (`RSK-011`). | Open-failure cleanup is correct and log descriptors are closed after `Wait`, although close errors are intentionally discarded. Raw stream and diagnostic redaction is incomplete under `RSK-012`. | | S3 object storage | AWS client construction is lazy with respect to network I/O. Each download obtains a response body and destination file; upload opens and streams a source file. | Every SDK call receives the caller context. List pagination has no deadline or progress guard (`RSK-014`). | Response bodies and local files are closed; uploads stream rather than prebuffer. Missing-object errors are adapted to `(false, nil)`. Successful system-temp ownership remains the caller issue in `RSK-009`, not an adapter leak. | | Audio materialization | Creates a sibling temporary file and delegates one download on cache miss. | Context is checked before work and passed to storage. | Failure removes the temporary file and success renames it into place. Cache identity remains `RSK-007`; repeated install mechanics remain `DUP-003`. | | Notification | The no-op/fake sender acquires no external resource. | Both honor a canceled context before returning. | There is no production delivery resource to release because no production transport exists; the accepted operator configuration is therefore confirmed as `ARC-004`. | No adapter creates an internal goroutine or channel. WhisperX concurrency is owned by the transcribe stage, while every subprocess call is synchronous. The focused adapter race command consequently passes; the full baseline race still fails only when the unsynchronized WhisperX fake is exercised concurrently by the stage. `TST-001` therefore represents a fake/consumer contract defect, not an HTTP-client race. Stage 8 confirmed the worker's bounded concurrent contract; Stage 12 owns the test-double repair. ### HTTP, storage, retry, and malformed-response behavior WhisperX uses stable multipart field names, per-attempt timeouts, a bounded response reader, context-aware retry delay, and the documented retry classes: 429, 5xx, attempt timeout, and network errors retry; other 4xx, malformed successful JSON, and explicit cancellation do not. Failed attempts never install the output. The constructor does, however, accept any absolute URL with a host, including `ftp://`, although the concrete HTTP transport cannot execute that request; this is `COR-016`. The full audio file is copied into a `bytes.Buffer` before the HTTP request begins on every attempt. This both scales memory with concurrent input size and delays cancellation until after local copying, as recorded in `EFF-002`. S3 operations normalize bucket-relative keys and leave ordering policy to callers. The callers that need deterministic order sort their resulting object sets. Downloads and uploads stream, response bodies are closed, provider not-found shapes are adapted, and system-temporary download failure removes the partial file. Pagination continues only while the response is truncated and a next token exists, but a repeated non-empty token is accepted forever. A faulty or S3-compatible provider can therefore make one list call spin and append duplicate pages until cancellation or exhaustion (`RSK-014`). No focused test models multiple pages, token progress, or a malformed pagination response. ### Subprocess protocol and output validation Audita, Seriatim, Scriptorium, and Notarius all delegate executable launch, timeout, environment merging, stream capture, wait, exit metadata, and bounded diagnostic-tail mechanics to `internal/adapters/subprocess`. Their flag order is deterministic and their generated configuration stores credential environment names/presence rather than values. Audita deliberately maps its configured credential into `AUDITA_LLM_API_KEY`; Notarius and Scriptorium use the inherited environment documented by their protocols. No current adapter places a raw credential in arguments or generated configuration. Notarius has the strongest external-output boundary: stdout is not parsed after a process failure; receipt, index, warnings, and rejection payloads are size bounded; inputs and bundle/lane outputs must be regular, non-symlinked files; bundle roots cannot escape; and declared lane media/schema/module contracts are matched before results are exposed. The other subprocess adapters use unbounded `os.ReadFile` for known JSON/text results or `os.Stat` for presence and non-empty checks. Those calls follow symlinks and do not establish a regular-file handle before parsing. Shared bounds parsing has the same unbounded, link-following shape. `RSK-013` records this one external-output trust-boundary cause rather than separate findings for every adapter. The shared launcher correctly reports executable, argument, working-directory, timeout, exit, and log-path context, and limits the returned stderr tail to 2 KiB. It only redacts sensitive values supplied in `EnvOverrides`, while the actual stdout/stderr files are always raw and inherited sensitive environment values are unknown to the redactor. A downstream tool echoing either kind of credential can therefore place it in persisted logs, and an inherited value can also enter the returned error and both manifests. This contradicts the repository's explicit no-secrets-in-logs/manifests invariant (`RSK-012`). Request and result contracts otherwise match their external documents. Scriptorium's validation-failure exit is adapted distinctly; Audita and Seriatim validate their documented JSON shapes; Notarius preserves structured, bounded diagnostics; and failure results retain non-secret paths and process metadata. Audita's request object redundantly carries most static constructor settings even though the production runner reads only `Modules` from the request. Because production supplies equal values, this is not a current correctness defect; `ARC-005` assigns the contract/fake-fidelity decision to the later maintainability and test passes. ### Shared models, diagnostics, and repeated mechanics `internal/artifactmodel` and `internal/contracts` contain stable JSON-tagged Narratio models rather than provider objects. Artifact conversion clones slice state at the boundary, and no lossy transport conversion was found. Bounds parsing accepts compatible unknown fields and normalizes integer-like IDs, then validates ordering and membership against the transcript. Its unbounded external reads are included in `RSK-013`; no separate serialization defect was established. `internal/logging` is a deliberately small `slog` text-handler constructor and owns no resource. It does not offer redaction, so callers must not submit secret values; the concrete violation comes from subprocess error/log construction in `RSK-012`, not from logger construction itself. The two adapter-local atomic byte writers and the shared `fileops` writer repeat the same same-directory temp/write/sync/chmod/rename mechanism. This is a real mechanical duplication (`DUP-005`), and all variants inherit `RSK-002`'s missing parent-directory sync. Protocol-specific argument builders, output schemas, and error adaptations are meaningfully different and should remain local. Repeated fake placeholder materialization is test support whose suite-wide value and fidelity remain assigned to Stage 12; it is not a production abstraction candidate from this pass. ## Prepare And Transcript-Processing Conclusions ### Prepare inputs, previous state, and deterministic recording Prepare rechecks the resolved configuration and runtime collaborators, enforces local-versus-S3 audio exclusivity, resolves stable inputs according to campaign/ session precedence, and copies the selected campaign, session, resolved pipeline, stable-input, and audio bytes into canonical session paths. S3 object sets and local directory entries are sorted; colliding basenames from different sources are rejected; checksums and remote/session provenance are recorded; and the final manifest input slice is sorted by kind and path. Previous-cache planning and hydration use the Stage 5 owner, validate required artifacts, and add deterministic manifest/artifact input records. No secret value enters the resolved pipeline copy. Two configuration transitions violate that otherwise deterministic handoff. First, managed `previous/**` state is cleared only when the newly resolved configuration has at least one previous requirement. Removing the last requirement leaves old bytes outside `manifest.inputs`; publish later walks and uploads the directory independently of those records. This is `COR-017`, and the focused test currently encodes the stale-state behavior. Second, explicit `audio_files` entries are sorted but not deduplicated. Repeating the same source therefore lets prepare succeed with duplicate manifest records, while transcribe's manifest-first validator rejects the duplicate path. Configuration validation does not reject it; `COR-019` owns the inconsistent boundary. ### Transcription concurrency, cancellation, and result identity Transcribe derives one speaker identity from each prepared FLAC basename, rejects duplicate speakers/paths, clamps positive configured concurrency to the job count, requests one distinct run-local JSON output per speaker, and requires the adapter-returned path to equal that request. Successful files are validated before any canonical copy; metadata and outputs are ordered by speaker, and any recorded adapter/validation error cancels peers and prevents canonical materialization. This satisfies unique identity, output-path authority, bounded concurrency, and ordinary partial-error ordering. Cancellation itself is not included in the completion decision. A worker that observes the derived context before its adapter call exits silently, dispatch stops silently on that same context, and the coordinator checks only the first recorded adapter/validation error. A pre-canceled context can therefore return success with no outputs, and cancellation after some completions can return and materialize a successful subset. `COR-018` records the correctness defect. The required race test also confirms that the WhisperX fake is invoked under a legitimate concurrent interface contract and races while appending requests; this closes Stage 8's behavioral check for `TST-001` without suggesting a production HTTP-client race. ### Transformation, rendering, and output classification Merge, polish, normalize, trim, and render prefer recorded producer outputs and use documented canonical fallbacks. They pass run-local destinations to their synchronous adapters, validate JSON/transcript/report/bounds or non-empty text as appropriate, and materialize only validated results into canonical session paths. Merge sorts raw inputs and normalizes each into run scratch before its merge; trim validates bounds ordering, membership, and selector behavior; and render waits for both requested render calls before canonical materialization. Subprocess logs, generated invocation configuration, and trim render-debug are diagnostics, while requested transcript, report, bounds, and Markdown results are stage outputs. Their ordinary output acquisition still inherits `RSK-013`'s unbounded, link-following validators. Current production adapters return the requested destination, but authority is inconsistent at the stage seam: transcribe rejects any alternate returned path, while several transformation stages validate and materialize a result path returned by the adapter and others treat the request path as authoritative. No current production adapter intentionally redirects output, so this is the contract candidate `ARC-006`, not a confirmed data defect. Polish likewise has no need for per-invocation overrides of the static Audita values duplicated in its request; `ARC-005` should make constructor state authoritative and retain only truly invocation-specific request fields unless a later product contract introduces overrides. Disabled trim is real processing: it validates and copies normalized JSON to the configured trimmed output and is correctly durable success. Disabled render deliberately returns successful no-output metadata so the pipeline can continue without Markdown; like any previously succeeded stage, later enablement requires force. Those ordinary-stage outcomes are coherent and resolve their part of `ARC-002`; the focused render document's generic “skips” wording is retained as `COM-004`. Analyze's no-op outcome was deferred to Stage 10, which confirmed the same durable-success behavior under `COM-005`. ### Similarity classification - `resolveRunStageLayout`, `runLocalPathForCanonical`, and `materializeRunLocalOutput` already form the narrow shared owner for run-local isolation and canonical copying; keeping adapter calls explicit is intentional. - Raw-transcript discovery is meaningfully plural and directory-aware. The three singleton manifest-first transcript resolvers, however, repeat nearly identical candidate, local-path, existence, and fallback mechanics even though the artifact registry already owns the same policy shape; `DUP-006` assigns a narrow resolver decision to Stage 11. - JSON, transcript, report, bounds, and text semantic checks should remain contract-specific. Safe bounded regular-file acquisition is the shared mechanism already required by `RSK-013`, not another generic validator. - Stage metadata and adapter requests expose protocol-specific facts. A generic stage/template framework would hide important ordering and failure differences and is rejected from this audit pass. ## Extraction Vertical-Slice Conclusions ### Configuration, execution, validation, and promotion Enabled Notarius configuration is validated before composition and its relative paths are anchored to the pipeline document. Application composition constructs the runner only when extraction is selected and enabled. Extract resolves the final-trimmed artifact through the shared registry, normalizes every invocation path, creates run-local receipt/log/output locations, and passes one explicit request to the adapter. The adapter separates stdout receipt from stderr, requires successful process completion before decoding, bounds the receipt, index, rejection, and warning documents, and rejects a non-canonical or symlinked bundle tree. The stage then selects exactly one descriptor for every sorted configured output, rejects matching rejections or contract differences, and verifies regular non-empty JSON lane payloads before promotion. Promotion copies the complete regular-file source tree through verified source handles, syncs files and directories, and installs one unique Narratio-run-ID destination through the platform no-replace primitive. It never replaces an existing bundle. The stage derives promoted paths from previously confined relative names and rechecks index and lane checksums before returning one non-selectable index plus the exact configured lane set. Each lane carries its checksum, configured contract, producing Narratio run ID, and Notarius system, run, pipeline, and lane provenance. Unconfigured bundle members remain in the immutable audit bundle but never become manifest outputs. ### Promotion, advertisement, catalog, and resume authority The four relevant authorities are intentionally distinct: - `fileops.PromoteDirectory` establishes an all-regular, immutable, no-replacement durable directory; its existence alone is not success. - The runner's succeeded session-stage record advertises the current result. A failed replacement clears the old current payload, and an orphan promoted directory is not rediscovered by scanning. - Extraction resume accepts a succeeded record only after the current invocation fingerprint, canonical producer/bundle/receipt identity, exact source set, contracts/provenance, confinement, regular type, and checksums agree. Missing or obsolete evidence causes a rerun; unsafe paths, symlinks, and inspection failures stop execution without replacing the prior success. - Catalog hydration independently fails closed and marks no extraction lane available unless the whole configured record, index, bundle, provenance, checksums, and JSON payload set validate. Analyze and publish consume only those catalog entries named explicitly by artifact inputs or publish rules. This prevents a durable bundle, diagnostic file, unconfigured lane, or stale invocation manifest from becoming implicitly selectable. It also exposes the maintenance tension in `DUP-007`: resume and catalog repeat one evidence policy while deliberately mapping failures differently. ### Failure and residue classification Configuration, input, directory, and adapter-start failures can leave only run-local directories or diagnostics and return no result. A subprocess failure can leave its receipt/stderr and staging tree for inspection, but none is advertised or reusable. Receipt, index, rejection, descriptor, payload, or pre-install promotion failures likewise retain diagnostic/staging evidence without a current selectable result. If no-replace installation succeeds and a later parent-directory sync, promoted-path resolution, or checksum check fails, the complete uniquely named durable bundle can remain as orphan audit residue; it still has no manifest advertisement and catalog hydration never scans for it. A later successful invocation uses a new run identity. After a successful stage result, runner manifest persistence is the advertisement boundary. Its session/run disagreement risks remain the shared `RSK-001`, `RSK-002`, and `TST-002` findings rather than extraction-specific duplicates. On replacement, the running transition clears the earlier current payload; failure retains the previous immutable bundle and prior invocation record only for inspection. Downstream succeeded state is invalidated by force or a changed executed outcome, while identical repeated disabled self-skip is stable. ### Validation shape and bounded acquisition `ValidateResume` has cyclomatic complexity 40 and cognitive complexity 53 because it combines fingerprint comparison, canonical bundle identity, receipt/source-set proof, contract/provenance checks, and filesystem evidence. These checks are consequential and their order is largely justified. `SIM-002` therefore proposes only named evidence sub-decisions: cheap record and fingerprint checks first, confinement before filesystem access, exact set and identity proof before payload acceptance, and an explicit final all-or-none decision. It must preserve every obsolete-versus-unsafe classification. Notarius's bounded management-document reads do not bound configured lane payloads. Extract's `checksumRegularFile` reads each lane fully to validate JSON and hash it, and catalog hydration streams a checksum and then reads the same file fully again for JSON validation. This broadens the already confirmed `RSK-013`; safe regular-file shape is stronger here than in ordinary adapters, but an external multi-gigabyte JSON lane can still exhaust memory. ## Analyze And Artifact-Dependency Conclusions ### Catalog state, selection, and reuse The runtime catalog keeps registration, executability, availability, and provenance separate. Analyze registers every built-in, configured artifact, and configured extraction source. Extraction availability is manifest-backed and all-or-none. Configured availability is different: entries executable in the current invocation are initially unavailable, while every non-executable entry with a valid non-empty canonical output is marked reusable from disk. After one selected artifact succeeds, its canonical materialized output is marked generated so later selected dependents can consume it in the same run. Selection is authoritative over the enabled flag inside catalog registration, and a catalog unit test explicitly preserves that behavior. With no selection, enabled controls execution. With a selection, exact membership controls execution and may activate a disabled artifact or suppress an enabled one. The CLI validates only that selected names exist. Configuration, previous- requirement planning, and focused documentation do not consistently share this rule: disabled artifacts need not have an executable prompt/output contract, previous requirements scan enabled artifacts only, and the artifact document defines executable as both selected and enabled. `ARC-007` owns the unresolved authority choice, while `COR-022` records the already observable prerequisite failure. Reuse deliberately does not require an old analyze success record: a configured non-executable output is accepted by canonical path and non-empty-file shape. This supports operator-prepared or preserved disabled dependencies, but it is not freshness validation. Enabled-but-unselected files receive provenance named for disabled outputs, so metadata cannot distinguish those two causes. This is an observability consequence of `ARC-007`, not a separate correctness defect. Publish registers the same configured identities but independently marks any existing canonical configured output available, then applies selection only to configured-source publish rules. It neither executes artifacts nor treats selection as a filter for built-in/extraction sources. ### Source resolution and required policy Artifact policy classifies built-in, prepared-stable, extraction, configured, and previous-artifact families before analyze resolves them. The built-in catalog contains base, polished, final, final-trimmed, both rendered Markdown variants, and bounds. Built-ins prefer manifest output records and then their canonical path; the resolver applies transcript JSON, non-empty Markdown, or bounds JSON validation. Stable inputs map to fixed prepared files. Extraction and configured sources require catalog availability. Previous sources use a separate resolver that prefers matching manifest input records and falls back to local `previous/` paths; neither path reaches object storage. Optional absence is consistently omitted for prepared stable, extraction, configured, previous, polished-transcript, and bounds sources. Final, final-trimmed, and both Markdown cases instead return guidance errors inside the source-specific branch before the caller can apply `required=false`; this is `COR-021`. Required prepared and extraction errors identify the producing stage/configuration. Configured absence is clear but has no repair command. Required previous absence emits a syntactically invalid and sessionless command (`COR-023`). The accepted `artifact` and `path` input fields are not consulted by policy, resolution, execution, either adapter request, generated invocation configuration, or previous planning; `COR-024` owns that silent contract. ### Dependency order, execution, and lifecycle Configuration validates dependency identities, self-reference, configured- source/`depends_on` agreement, referenced output paths, and cycles among enabled artifacts. Analyze validates the executable subgraph again. A dependency outside that subgraph must already be catalog-available; a dependency inside it becomes a directed edge. Edges and ready nodes are sorted, so successful order is deterministic and dependencies precede dependents. The initial unavailable- dependency pass ranges over a map, however, so several invalid selected nodes can produce different first errors (`RSK-015`). Re-sorting the ready slice on every insertion is more work than necessary, but configured artifact counts are small and no material efficiency defect was established. For each plan, input names are sorted, optional omissions and reused sources are recorded, variables are normalized with Narratio's sticky session identifier, and optional render-debug completes before the run request. The result must be non-empty, then run-local bytes are materialized to the canonical artifact path. Logs/generated configs are deduplicated and sorted, execution metadata follows plan order, and reused metadata is stable. Output checks still inherit the link-following, non-regular, and unbounded acquisition risk in `RSK-013`. Failure after an earlier artifact materializes can leave that canonical output, but no stage result is advertised; an ordinary retry executes the same selected set again rather than implicitly resuming mid-graph. Missing Scriptorium configuration, an empty artifact map, or no executable entries returns zero-disposition metadata and is recorded as success by the runner. That permits full-pipeline continuation and satisfies publish's analyze prerequisite, but the success is reused after later configuration changes until forced. The dedicated `analyze` command force-runs, mitigating the explicit operator workflow. This settles analyze's state behavior for `ARC-002`; the focused document's generic “skips” wording and omitted extraction family remain `COM-005`. ### Representation and ownership `analyzeStage.Run` coordinates catalog, planning, transcript-reference diagnostics, execution, catalog mutation, and aggregation. Its artifact helper is 239 lines with ten parameters; the source resolver is 84 lines with six parameters and two unused contextual parameters; the dependency orderer is 86 lines with ten loops. These metrics support only the narrow `SIM-003`: a typed execution context/plan and source-resolution result could make state ownership visible without introducing a generic stage framework or collapsing distinct source guidance. `resolveInputPathForRead` has no production or test caller and belongs in that review. Analyze, publish, and app operator helpers also repeat built-in/configured/ extraction catalog registration and extraction hydration. Their final availability policies differ intentionally, but the shared registration mechanics and source-definition construction are one policy repeated in three places; `DUP-008` assigns a narrow common bootstrap decision to Stage 11. ## Maintainability And Structural Conclusions Stage 11 re-ran production-only similarity, complexity, fan, loop-depth, and change-coupling queries, then traced the resulting owners and callers. The graph reported no production function with a direct scan-in-loop or allocation-in-loop flag. High transitive loop depths came from composition and tests rather than a new credible hot path. Change coupling was dominated by an implementation and its focused tests, with expected cohesive changes among the runner, stage registry, configuration, and command wrappers. The resulting design rule is to share mechanics and typed evidence, not whole workflows: - `fileops` should own atomic replacement and durable installation mechanics; manifest serialization, remote acquisition, validation, conflicts, and reporting remain with their current policy owners; - `artifacts` should own canonical artifact resolution, catalog bootstrap, and extraction-bundle evidence; stages retain required/optional, lifecycle, and publication decisions; - the runner should expose one narrow terminal-failure transition while keeping running-versus-terminal ledger order visible; and - analyze should use a typed execution context and indexed effective plan, but its five source-policy branches should remain explicit. No generic stage, workflow, validator, resolver, or graph framework is justified. `previouscache.BuildPlan` is long because it visibly separates required and optional absence, remote-current validation, candidate selection, and deterministic ordering; extracting those branches without a new owner would only move complexity. The repeated command wrappers, typed manifest load/create methods, adapter constructors, and protocol fakes likewise share shape but not policy. The dependency inventory has six direct module dependencies. AWS configuration, credentials, S3, and Smithy are used by the S3 adapter; YAML is used by strict configuration and generated adapter files; and `x/sys` supplies native no-replace directory installation. No dependency can be replaced by the standard library without losing a current protocol or platform guarantee. Linux, macOS, and Windows have explicit file-operation implementations; unsupported systems fail the no-replace capability clearly. The remaining portability risks are already owned by `RSK-002`, `RSK-003`, and `RSK-011`, not new dependency findings. No benchmarks exist in the repository. That is not a general test defect: the two confirmed efficiency findings below now state representative workloads and specific byte-count, latency, allocation, and peak-memory measurements. No other sorting, copying, map/slice allocation, serialization, adapter construction, remote-call, filesystem-pass, or goroutine/channel pattern had a credible workload large enough to justify a performance finding. ## Confirmed Findings ### `COR-001`: session identity initialization preserves stale invocation paths and accepts conflicting identity - Category: confirmed correctness defect. - Locations/invariant: `internal/app/runner.go` in `ensureManifestIdentity` and `syncRunManifestIdentityFromSession`; consumers include prepare's work/spool resolution, publish's run prefix, and post-publish cleanup. The session and invocation manifests must describe one internally consistent campaign, session, and run. - Evidence: every invocation replaces `Manifest.RunID`, but `LocalWorkDir`, `LocalSpoolDir`, and `S3RunPrefix` are computed only when empty. A second invocation therefore records run B while retaining paths and the remote prefix derived from run A, and copies those stale values into run B's manifest. The same helper fills an empty campaign but neither rejects nor reconciles a loaded campaign/session that conflicts with the configured manifest path. `TestExecuteStagesCreatesRunManifestPerInvocation` proves IDs and run-manifest paths differ but does not assert the dependent identities. - Realistic scenario: a forced second prepare writes through run A's work/spool identity; a forced second publish can target run A's remote prefix while its manifests claim run B. A misplaced or incorrectly restored manifest can also make the runner hold session A's lock while stages derive paths from the manifest's session B identity. - Impact/likelihood/confidence: high integrity impact; stale derived identity occurs on every ordinary second invocation after the fields are initialized, while a conflicting loaded identity is less common; high confidence from the assignment guards and direct consumers. - Estimated remediation scope and owner: small-to-medium application/manifest change. Define whether run-scoped locations belong in the session manifest, recompute them as one identity unit whenever the run changes, and reject configured/persisted campaign or session conflicts before stage execution. - Test changes: extend the existing per-invocation test to load both manifests and assert every run-derived field against run B; add a loaded-identity mismatch test that proves no stage or cross-session path is touched. Stages 3 and 4 should add the path and remote-prefix boundary assertions after their focused review. - Dependencies: Stage 3 owns exact path confinement consequences, Stage 4 owns publish/cleanup impact, Stage 5 owns restored-manifest provenance, and Stage 6 owns configuration identity validation. They should reference this root finding rather than create duplicates. ### `COR-002`: unsafe identity components escape canonical local and remote namespaces - Category: confirmed correctness/security defect. - Locations/invariant: `internal/artifacts/paths.go` canonical constructors, `internal/artifacts/s3_keys.go` in `S3SessionPrefix`, and `internal/config/validate.go` in `validateCampaign`, `validateSession`, and `validateSessionIdentifier`. Canonical identities must be opaque safe segments, and traversal must not broaden local or remote operation scope. - Evidence: campaign, session, and previous-session identifiers are checked only for non-emptiness. Path/key constructors clean and join the raw values without rejecting `.`/`..`, separators, drive forms, or traversal. A value such as `../../outside` therefore changes the cleaned local workspace/spool destination and the S3 namespace. `EnsureLayoutFor` also validates only presence before creating the derived directories. The exported `SessionPreviousArtifactPath*` helpers similarly claim an under-root result but accept traversal; current production callers happen to validate their relative artifact values first. - Realistic scenario: an operator typo, generated session config, or untrusted restored identity containing traversal makes prepare create/copy files outside the intended workspace or spool root. Publish can read or write a different campaign/session prefix, potentially colliding with unrelated remote state. - Impact/likelihood/confidence: high local and remote integrity/confidentiality impact; low-to-moderate likelihood because ordinary date-like IDs are safe but configuration is operator-controlled; high confidence from direct data flow into canonical joins. - Estimated remediation scope and owner: small-to-medium shared config/artifacts change. Define one strict opaque-segment contract, validate identities before layout/key construction, and make canonical helpers fail closed rather than return an escaped path. Decide compatibility for existing non-segment IDs explicitly; Stage 6 found only safe segment-style IDs in maintained examples and documentation, but deployed configuration is unknown. - Test changes: table-test separator, traversal, absolute/drive, dot, and mixed- slash identities at the configuration boundary; add artifacts tests proving no local directory or S3 key can escape its expected namespace. Add a direct unsafe-relative test for the exported previous-artifact constructor if it remains public. - Dependencies: Stage 6 confirmed that no configuration validator closes this boundary. `COR-001` separately owns conflicts among otherwise valid identities; `COR-003` owns symlink-based escape after lexical identities are safe. ### `COR-003`: filesystem mutation confinement follows symlinked destination ancestors - Category: confirmed correctness/security defect. - Locations/invariant: lexical joins in `internal/pathsafe`, path-based writers in `internal/fileops/fileops.go`, destination setup in `internal/fileops/directory.go`, local layout/copy operations in `internal/artifacts/local.go`, and cleanup validation/removal in `internal/app/cleanup_targets.go`, `clean.go`, and `post_publish_cleanup.go`. Writes, replacements, promotions, and deletions must remain beneath an explicit root despite symlinks or replacement races. - Evidence: lexical `Rel` checks cannot observe filesystem links. File writers call `MkdirAll`/`CreateTemp`/`Rename` through destination paths. Promotion strongly confines and identity-checks its source, but only path-checks the destination parent before creating/installing the sibling tree. Cleanup `Lstat`s only the final target and then calls `RemoveAll`; it neither rejects a symlinked root/ancestor nor holds a confined directory handle. A target such as `root/campaign/session`, where `campaign` is a symlink to an outside tree, passes the lexical and final-node checks and deletes the outside session directory. The analogous ancestor redirects writes and promotion. - Realistic scenario: a stale, user-created, restored, or concurrently replaced workspace component redirects prepare/materialization into another tree; a later `clean session` or post-publish cleanup recursively removes data there. The same gap permits a time-of-check/time-of-use replacement of a destination ancestor. - Impact/likelihood/confidence: critical destructive and confidentiality impact; low-to-moderate likelihood depending on workspace ownership and multi-user exposure; high confidence from the path-based checks and standard symlink resolution semantics. - Estimated remediation scope and owner: medium shared filesystem change. Use root-relative directory handles/no-follow component traversal (with explicit platform behavior) for mutations and deletion, and carry validated handles through install/remove where feasible. Keep `fileops` policy-neutral by passing an explicit root/destination capability rather than inferring stage policy. - Test changes: real-filesystem tests for a symlinked root, intermediate component, destination parent replacement, and cleanup ancestor; assert an outside sentinel survives and no outside temporary/output is created. Retain the existing promotion source-race suite as the model for narrow hooks. - Dependencies: Stages 4, 5, 7-9 must reference this finding for their concrete cleanup, restore/download, adapter, stage-materialization, and extraction consequences. `COR-002` owns lexical identity traversal separately. ### `COR-004`: precommit current-manifest replacement invalidates the prior readable commit - Category: confirmed correctness/recovery defect. - Locations/invariant: `internal/stage/publish.go` upload order and `internal/artifacts/current_state.go` in `LoadCurrentState`. The pointer must be the sole commit point, and work before it must not destroy the previously committed state. - Evidence: publish unconditionally replaces the fixed `current/manifest.json`, then uploads `current/run_id.txt`. A replacement publish that uploads the new manifest but has not yet written, or fails to write, the pointer therefore leaves old pointer/new manifest disagreement. The shared loader can reject that pair, but only when the caller enables run validation; restore/status do not (`COR-008`). Existing publish tests prove no pointer call follows selected failures but do not seed and preserve a prior current pair. - Realistic scenario: run A is current. Publishing run B reaches the current manifest, then the pointer upload fails or a reader runs during the gap. Run A's coherent pair is gone and run B is not committed. Strict previous-cache readers report unavailable state, while restore/status can incorrectly accept run B's manifest under run A's pointer. An upload error after server acceptance also makes local outcome ambiguous. - Impact/likelihood/confidence: high recovery availability and integrity impact; upload failure likelihood is low per call but the disagreement window occurs on every replacement publish; high confidence from fixed keys, unconditional upload, and identity validation. - Estimated remediation scope and owner: medium publish/artifacts protocol change. Publish an immutable run-specific manifest first and make the final commit object select that immutable state, or use a versioned/conditional current representation that preserves the previous pair. Do not move commit policy into the generic storage adapter. - Test changes: use a stateful store seeded with run A, fail or pause every run B boundary, and assert run A remains readable until a successful final commit; add the first-publish and indeterminate-pointer-response cases. Existing pointer-last tests remain useful but are insufficient alone. - Dependencies: `COR-001` can misplace the immutable run prefix and must be fixed consistently. Stage 5 should consume the established current-state truth table rather than duplicate this publish root cause. ### `COR-005`: publish follows symlinked archive entries and can upload files outside its roots - Category: confirmed correctness/security defect. - Locations/invariant: `internal/stage/publish.go` in `collectPublishRunFiles` and `collectPublishPreviousFiles`, plus `internal/adapters/storage` upload implementations. Run and previous archives must be confined to the enumerated local trees and contain eligible regular files only. - Evidence: both `WalkDir` collectors append every non-directory entry without rejecting symlinks or other special files. Storage then opens the recorded path (`os.Open` in the S3 backend and file reads in the fake), which follows a symlink final component. The run-manifest check also uses `os.Stat` and accepts any non-directory. Existing exclusion tests cover path names, not entry type. - Realistic scenario: an adapter, restored tree, local user, or compromised subprocess leaves `runs//logs/debug.log` as a symlink to a credential or unrelated private file. Publish uploads its contents under the apparently harmless run key. A symlink in durable previous cache has the same effect. - Impact/likelihood/confidence: critical confidentiality impact; low-to-moderate likelihood depending on workspace ownership and subprocess trust; high confidence from standard open semantics and the missing type checks. - Estimated remediation scope and owner: small-to-medium publish/file-boundary change. Enumerate and open regular files without following links, keep the opened object tied to the verified entry where platform support permits, and reject unsafe roots/ancestors consistently. Storage should continue accepting explicit paths rather than infer archive policy. - Test changes: create run, previous, and manifest symlinks to an outside sentinel and prove publish fails before uploading sentinel contents; include an entry-replacement race case if the implementation adopts path-based `Lstat` only. Existing exact exclusion/order tests should remain. - Dependencies: `COR-003` owns symlinked ancestors for mutation and cleanup; this finding is distinct because it is a publish read/exfiltration boundary. `RSK-004` affects who can create the malicious entry but is not required for exploitation by a trusted subprocess gone wrong. ### `COR-006`: automatic cleanup is one-shot and is not retried after postcommit failure - Category: confirmed correctness/operational defect. - Locations/invariant: `internal/app/runner.go` around terminal publish saves and `runPostPublishCleanup`, and `post_publish_cleanup.go` in `publishStageRecordForCleanup`. An enabled cleanup policy should remain recoverable after a committed publish until its requested cleanup succeeds. - Evidence: cleanup requires `publish` to appear in the current invocation's `executed` list. Once the terminal session publish save succeeds, later ordinary invocations treat publish as already succeeded and omit it from that list. A terminal run-manifest save failure before cleanup, a cleanup deletion failure, or cleanup metadata-save failure can therefore leave requested data present while every normal retry silently bypasses cleanup. A forced publish or manual `clean` is the only retry path. - Realistic scenario: remote commit succeeds and the session publish record is saved, but saving the run record fails, so cleanup is never entered. The operator fixes the filesystem and reruns normally; publish skips and the sensitive spool remains despite `delete_audio_after_publish=true`. The same occurs when the first removal attempt itself fails. - Impact/likelihood/confidence: medium confidentiality/storage and operator- expectation impact; low-to-moderate lifetime likelihood around filesystem failures; high confidence from the execution-list gate and session reuse policy. - Estimated remediation scope and owner: medium app/manifest change. Persist a cleanup obligation/state independently of “publish executed this invocation” and retry it idempotently whenever the committed local record proves eligibility. Keep explicit policy and confinement checks on every attempt. - Test changes: inject a failure immediately after publish session success, fail each requested deletion/save once, then run normally and assert cleanup retries without republishing or deleting an uncommitted target. Existing commit-gate tests remain useful. - Dependencies: `TST-002` notes that run-save boundaries are not injectable; `COR-001` and `COR-003` still govern target identity/confinement. `COR-007` separately owns missing success evidence after work deletion. ### `COR-007`: successful workspace cleanup metadata is never persisted - Category: confirmed correctness/diagnosability defect. - Locations/invariant: `internal/app/post_publish_cleanup.go` in the `workRequested` success path. Cleanup outcomes promised as manifest metadata must survive the invocation that performed the destructive action. - Evidence: spool-only cleanup sets completion metadata and saves the session manifest. When workspace cleanup is requested, the function removes the work directory, mutates `workdir_cleanup_deleted`, `cleanup_completed`, and `cleanup_skipped` only in memory, then returns without saving the session. The subsequent final run save copies identity fields, not the mutated session stage record. If both policies are enabled, the spool deletion metadata is lost with the same unsaved map. Existing cleanup tests assert paths only. - Realistic scenario: automatic cleanup successfully removes both run work and spool audio. The durable session and run records retain the pre-cleanup publish metadata, so status, incident review, or a future cleanup retry cannot distinguish completed cleanup from a path that was never considered. - Impact/likelihood/confidence: medium audit/recovery impact; occurs on every successful workspace cleanup; high confidence from the missing save and run record copy behavior. - Estimated remediation scope and owner: small app/manifest change. Persist one authoritative cleanup transition after requested deletions, with ordering that remains meaningful if saving fails after deletion. Coordinate that state with the retry obligation in `COR-006`. - Test changes: after workspace-only and combined cleanup, reload both durable ledgers and assert the chosen authoritative completion fields and deleted paths. Add a post-delete save-failure case to define retry/reporting behavior; no existing path-preservation test should be removed. - Dependencies: fixing `COR-006` and this finding together avoids inventing two competing cleanup state machines. `RSK-002` remains the crash-durability guarantee of the eventual manifest save. ### `COR-008`: restore is not bound to the pointer-selected committed remote run - Category: confirmed correctness/recovery defect. - Locations/invariant: `internal/app/restore_discovery.go`, `restore_plan.go`, and `restore_execute.go`; shared validation in `internal/artifacts/current_state.go`; previous object selection in `internal/previouscache`. Restore and status must interpret only the run committed by the current pointer, and one restore must install one coherent remote snapshot. - Evidence: restore/status discovery passes campaign/session expectations but omits `ValidateRunID`, so old-pointer/new-manifest disagreement is accepted. The planner then lists every `transcripts/**` and `artifacts/**` object under the mutable session prefix instead of limiting actions to manifest-declared published/locked state. Execution downloads those keys and the fixed manifest again; manifest validation checks campaign/session but not the discovered run ID or bytes. Previous planning enables pointer/run validation but selects and later downloads mutable published keys without generation binding. The unit mismatch test exercises the helper only with `ValidateRunID=true`. - Realistic scenario: run A is current. A failed run B publish replaces the fixed manifest and uploads one artifact but never advances the pointer. Restore reports run A, accepts manifest B, includes B's partial artifact plus any stale prefix objects, and installs them locally. A concurrent successful publish can similarly change fixed objects between discovery, planning, and execution, producing a mixed A/B restore. - Impact/likelihood/confidence: critical recovery integrity impact; the disagreement window occurs on every replacement publish and partial objects are retained by design, while concurrent/failed publish likelihood is low-to-moderate; high confidence from validation flags, list scope, and repeated unconditional downloads. - Estimated remediation scope and owner: medium-to-large publish/restore/ artifacts protocol change. Pair `COR-004`'s immutable run-specific commit representation with restore planning from explicit manifest source-to-object records, bind object versions/digests through execution, validate run identity everywhere, and preserve an explicit compatibility policy for old manifests. Storage should expose only the narrow conditional/version metadata the protocol requires, not infer current-state policy. - Test changes: seed pointer A with manifest/object state A plus uncommitted and stale B keys; assert restore/status reject mismatch and restore only declared A objects. Use barriers/versioned fake objects to change pointer, manifest, ordinary output, and previous output at every discovery/execute boundary and prove the result is all one run or fails before manifest installation. - Dependencies: `COR-004` is the publish-side fixed-pair root; both should share one protocol repair. `COR-011` separately owns the missing source/destination identity needed to construct an explicit restore set. Stage 7 owns transport- level version/checksum feasibility. ### `COR-009`: forced restore ignores unresolved directory conflicts and can report success - Category: confirmed correctness defect. - Locations/invariant: `internal/app/restore_plan.go` in `classifyRestoreAction`, `restore.go` conflict gating, and `restore_execute.go` download filtering. Force may authorize file replacement, but it must not turn an unexecutable conflict into silent success. - Evidence: a local directory where a remote file is expected is always classified `conflict`, even with force. The command blocks conflicts only when `!force`; execution processes only `download` actions and silently ignores the remaining conflict. It can then install the current manifest, mark the report succeeded, and leave the directory in place. Existing force tests cover differing regular files only. - Realistic scenario: `artifacts/session_recap.md` is accidentally a directory. The operator reviews the conflict and reruns with `--force`. Restore exits successfully, its report still contains a conflict action, and the new manifest is installed although the required artifact was never restored. - Impact/likelihood/confidence: high local integrity/operator-trust impact; low-to-moderate likelihood from damaged or manually edited workspaces; high confidence from the action and command branches. - Estimated remediation scope and owner: small application-policy change. Require zero conflicts before execution regardless of force, or explicitly define and safely implement directory replacement as a separate destructive action. Successful reports must be impossible while any conflict remains. - Test changes: cover a directory at ordinary, previous-cache, and manifest targets with force; assert failure, old manifest preservation, and a failed report. One table-driven application test can own all target categories. - Dependencies: target removal would require `COR-003`'s confined deletion capability. Do not implement ad hoc `RemoveAll` in restore. ### `COR-010`: status and validation do not verify previous-artifact readiness - Category: confirmed correctness/operator defect. - Locations/invariant: `internal/app/operator_inspection.go` in `inspectPreviousArtifactReadiness`, `operator_status.go`, and `operator_session_validate.go`; canonical planning in `internal/previouscache.BuildPlan`. Operator readiness must match the required/optional and object-resolution policy that prepare/restore will use. - Evidence: inspection checks only that `previous_session_id` is nonempty and its remote current pair validates. It reports every requirement ready without resolving candidate keys or calling `Exists`. It also marks a missing ID as unavailable/error whenever any previous requirement exists, although `BuildPlan` correctly skips that state when all are optional. Focused operator tests cover a missing pointer for a required fixture, not missing artifact objects or optional requirements. - Realistic scenario: a previous session has a valid committed manifest but the required published recap was never uploaded or was removed. `session status` says ready and `session validate` succeeds; restore/prepare then fails. In the opposite case, an optional previous recap with no previous ID makes validation fail even though pipeline execution would intentionally omit it. - Impact/likelihood/confidence: medium operator and automation correctness impact; moderate likelihood as optional/missing published outputs are normal modeled states; high confidence from the inspection shortcut and canonical plan branches. - Estimated remediation scope and owner: small-to-medium application/ previouscache change. Share one read-only requirement-resolution result from `BuildPlan` (or a narrower readiness API), then let status remain non-fatal and validation choose finding severity without duplicating required/optional semantics. - Test changes: add required and optional matrices for missing ID, missing pointer/manifest, missing candidate object, custom destination, and ready state. Own resolution cases in `previouscache`; sample only status rendering and validation exit classification in app tests. - Dependencies: `COR-011` must be fixed for readiness to recognize every valid custom destination. `COR-008` owns remote version binding, not caller severity. ### `COR-011`: previous-cache planning loses source identity for custom publish destinations - Category: confirmed correctness defect. - Locations/invariant: `internal/previouscache/previouscache.go` in `artifactRelativePathCandidates`, `manifestPublishedPaths`, and candidate selection; publish metadata in `internal/stage/publish.go`. A configured previous-artifact source must resolve to the exact remote object that publish committed for that source. - Evidence: the remote manifest records only an ordered `published_paths` list, not ordinary source-to-destination pairs. Planning starts with the analyze output's local relative path, adds published destinations only when their basename matches, and finally tries the current artifact output path. A custom destination with a different basename is therefore invisible. Multiple destinations with the same basename are candidates for the wrong source, and the first existing key wins. Tests use identical output/published paths. - Realistic scenario: `session_recap` is produced at `artifacts/session_recap.md` and intentionally published as `history/recap-v2.txt`. A later required previous-session recap reports unavailable although the committed object exists. With two artifacts both published as different directories' `summary.md`, one requirement can hydrate the other's content under the expected local cache path. - Impact/likelihood/confidence: high cross-session artifact integrity impact; moderate likelihood because custom publish destinations are a supported configuration feature; high confidence from candidate construction. - Estimated remediation scope and owner: medium publish manifest/previouscache contract change. Persist a deterministic source/destination mapping for uploaded and intentionally locked preserved outputs, consume it by exact source ID, and define backward-compatible fallback behavior without basename guessing when identity is ambiguous. - Test changes: cover changed basename, duplicate basename, locked preserved output, absent mapping in an old manifest, and exact source mapping. Existing default-path/fallback tests remain as compatibility cases. - Dependencies: this mapping is also required for the committed restore scope in `COR-008`. Stage 6 confirmed that custom destinations are supported and validated, so remediation must preserve them; Stage 10 later confirmed that artifact-consumer semantics do not repair remote identity selection. ### `COR-012`: strict YAML loading silently ignores a valid trailing document - Category: confirmed correctness defect. - Locations/invariant: `internal/config/load.go` in `decodeStrictYAMLFromReader`, shared by pipeline, campaign, and session loaders. A strict configuration file must contain exactly one known-field document; content after it must not be silently discarded. - Evidence: after decoding the requested value, the loader decodes once into `extra` and returns an error only when that second decode returns a non-EOF error. A valid second document returns nil, so it is accepted and ignored. Existing strict-decode tests cover unknown fields but no multi-document input. - Realistic scenario: an operator or generated deployment file appends an override document with a different storage bucket, timeout, or stage setting. Validation succeeds against only the first document and the command runs with behavior different from the complete visible file. - Impact/likelihood/confidence: high operator-integrity impact; low-to-moderate likelihood because multi-document YAML is common in generated configuration; high confidence from the decoder control flow. - Estimated remediation scope and owner: small config-loader correction. Require the second decode to return EOF and reject any decoded value, including null, with a contextual trailing-document error. - Test changes: table-test pipeline, campaign, byte-session, and rendered template paths with a valid second mapping, null/empty separators, malformed trailing YAML, unknown fields, and ordinary single-document EOF. - Dependencies: none. Apply before reasoning about future configuration migration formats so compatibility is explicit rather than accidental. ### `COR-013`: configuration accepts non-positive durations that runtime consumers reject - Category: confirmed correctness defect. - Locations/invariant: `internal/config/validate.go` in `validateDuration` and its WhisperX, Seriatim, Audita, Scriptorium, trim/render-bounds, and related callers; corresponding adapter and stage duration parsing. A configuration accepted by validation should satisfy constructor/stage preconditions. - Evidence: the shared validator trims and parses duration syntax but imposes no sign constraint. Zero or negative request timeouts therefore pass config validation, while WhisperX, Seriatim, Audita, and Scriptorium execution reject non-positive timeouts. A negative WhisperX retry delay similarly passes config validation and is rejected by the HTTP client constructor. Notarius uses a separate positive check and does not have this defect. - Realistic scenario: `narratio session validate` or plan reports a deployment valid with `whisperx.timeout: 0s`; the subsequent run fails before useful work when composition rejects the same value. Scriptorium artifact timeouts can fail later only when their stage is selected. - Impact/likelihood/confidence: medium correctness/operability impact; moderate typo or generated-config likelihood; high confidence from paired validation and consumer checks. - Estimated remediation scope and owner: small config-policy correction. Give timeout and delay fields explicit sign rules, share helpers only where their contracts truly match, and keep empty artifact timeout as documented fallback. - Test changes: table-test zero and negative values for every consumed duration, positive/subsecond values, empty optional artifact timeout, and a focused assertion that validated config reaches constructors without sign errors. - Dependencies: Stage 7 may refine adapter error contracts, but config should reject values that no current consumer accepts independently of that work. ### `COR-014`: the storage backend selector is neither validated nor authoritative - Category: confirmed correctness defect. - Locations/invariant: `internal/config/validate.go` in `validateStorage` and cross-config S3 checks; `internal/adapters/storage/factory.go` in `NewObjectStoreFromConfig`. An operator-selected backend must be recognized and must determine the adapter that is constructed. - Evidence: storage validation checks S3 path, endpoint, and credential-name fields but never checks `storage.backend`. The factory selects S3 for backend `s3`, but also selects it for every other spelling whenever the defaulted S3 block has a bucket. An unknown backend with a bucket silently becomes S3; an unknown backend without one can pass config validation and fail later only when a command needs remote storage. - Realistic scenario: `backend: s33` with a production bucket validates and uploads to S3 despite the operator's invalid selector. The same typo in a locally exercised config may stay latent until remote session discovery, publish, restore, or status constructs the store. - Impact/likelihood/confidence: high boundary-selection impact; moderate typo or migration likelihood; high confidence from validator and factory branches. - Estimated remediation scope and owner: small config/factory correction. Define the supported backend enum and disabled/local meaning, validate it before cross-field checks, and make construction switch only on that normalized value. Do not infer backend from populated provider fields. - Test changes: cover exact/case policy for supported values, unknown and empty selectors with and without bucket, commands that need/do not need storage, and factory non-invocation after invalid config. - Dependencies: Stage 7 owns provider adapter behavior, not the operator-facing selection policy established here. ### `COR-015`: the previous-session expectation flag is ignored when the session omits the field - Category: confirmed correctness defect. - Locations/invariant: command flag construction and `internal/app/config_loader.go` session options; `internal/config/load.go` in `LoadSessionBytesWithOptions`. An option described and typed as an expected previous-session identifier must either establish that identity or reject a session that does not contain it. - Evidence: the loader rejects a mismatch only when both the expected option and decoded `previous_session_id` are non-empty. If the session omits the optional field, a supplied `--previous-session-id` neither fills it nor fails loading, so planning and execution behave exactly as if the flag were absent. Tests cover a non-empty mismatch but not omission. - Realistic scenario: an operator validates or runs session B while pinning previous session A at the command line. A session file that accidentally drops `previous_session_id` passes the expectation and runs without the intended previous-artifact context. - Impact/likelihood/confidence: medium cross-session behavior impact; moderate likelihood during generated/session-template edits; high confidence from the option contract and conditional comparison. - Estimated remediation scope and owner: small CLI/config-selection decision. Treat the flag as a strict expectation and reject omission, or explicitly define it as an override and populate before resolution; keep the chosen semantics consistent across local and remote session paths and documentation. - Test changes: cover absent/equal/different file values for local, remote, and rendered sessions, plus no-flag optional behavior. - Dependencies: `COR-011` owns remote artifact source mapping after an identity is selected; this finding owns selection of that identity. ### `COR-016`: the WhisperX HTTP client accepts schemes its transport cannot execute - Category: confirmed correctness defect. - Locations/invariant: `internal/adapters/whisperx/http.go` in `NewHTTPClient` and the WhisperX integration contract. A successfully constructed HTTP adapter should accept only endpoint schemes its transport supports. - Evidence: construction requires only a parsed URL with a non-empty scheme and host. Values such as `ftp://example.com/transcribe` pass, while the standard HTTP client later rejects the request as an unsupported protocol scheme. Constructor tests cover missing and syntactically malformed values but no non-HTTP absolute scheme. - Realistic scenario: a copied or templated endpoint uses `ftp` or another absolute scheme. Configuration and adapter construction succeed, but every speaker transcription fails only after pipeline work reaches the first request. - Impact/likelihood/confidence: medium operability impact; low-to-moderate configuration-error likelihood; high confidence from the constructor and concrete transport contract. - Estimated remediation scope and owner: small WhisperX adapter validation change. Admit only `http` and `https` after normalization and retain the existing absolute-host requirement. - Test changes: table-test supported HTTP/HTTPS endpoints and reject FTP, scheme-relative, hostless, and malformed values at construction. Add a config-to-constructor case only if config validation also chooses to own the scheme rule. - Dependencies: `COR-013` separately owns duration validation disagreement; neither finding requires changing retry behavior. ### `COR-017`: removing the final previous-artifact requirement leaves stale publishable state - Category: confirmed correctness and data-integrity defect. - Locations/invariant: `internal/stage/prepare.go` in `prepareStage.Run` and `clearManagedPreviousState`, plus `internal/stage/publish.go` previous-file collection. Prepare must make managed `previous/**` state match the current requirement set, and publish must not expose bytes excluded from that set. - Evidence: prepare calls `clearManagedPreviousState` only inside `len(previousRequirements) > 0`. With zero current requirements it replaces `manifest.inputs` without previous records but leaves the directory intact. `TestPrepareStageWithoutPreviousRequirementsDoesNotTouchPreviousState` explicitly requires a stale file to survive. Publish independently walks all files below the previous directory and uploads them under the current session prefix; it does not filter that walk through current input records. - Realistic scenario: one pipeline revision consumes a previous recap and prepare hydrates it. The operator removes the final previous input and forces prepare for the same session. Later publish uploads the old recap again even though current configuration and manifest inputs no longer declare it. - Impact/likelihood/confidence: high stale-data and possible confidentiality impact; moderate configuration-evolution likelihood; high confidence from the focused test and publish caller. - Estimated remediation scope and owner: small-to-medium prepare/publish invariant correction. Clear the managed previous tree on every prepare before optionally hydrating the current requirement set, or make publication consume one explicit current-set record. Preserve confined deletion requirements. - Test changes: replace the stale-survival assertion with a transition case that hydrates a requirement, removes the final requirement, reruns prepare, and proves both the local tree and publish upload set omit it. Retain the optional missing requirement clear case. - Dependencies: `COR-011` owns source identity for requirements that remain; `COR-003` governs safe recursive clearing. Stage 4's deterministic publish behavior is not reopened. ### `COR-018`: transcribe can report successful partial work after cancellation - Category: confirmed correctness and lifecycle defect. - Locations/invariant: `internal/stage/transcribe.go` worker, dispatcher, and completion decision; `internal/app/runner.go` success recording. A successful transcription result must represent every discovered audio job, while parent cancellation must produce an error rather than a reusable success. - Evidence: a worker returns without recording an error when `stageCtx.Err()` is already non-nil, and dispatch breaks silently on `stageCtx.Done()`. After waiting, the coordinator checks only `firstErr`; it never checks the parent context or verifies completed results equal planned jobs. A pre-canceled context therefore produces a successful result with zero outputs. If some workers complete before cancellation stops dispatch, their subset is sorted, materialized, and returned as success. The runner has no independent context check and records that result as succeeded. - Realistic scenario: an operator cancels a multi-speaker session while one fast request has completed but other work is queued. The command can report success and make the partial raw transcript set reusable by merge on the next invocation. - Impact/likelihood/confidence: high transcript completeness/integrity impact; moderate cancellation likelihood; high confidence from the explicit channel, context, and result-count control flow. - Estimated remediation scope and owner: small transcribe coordinator change. Track planned/dispatched/completed jobs and return the parent cancellation cause whenever the complete set was not produced, while retaining the first concrete adapter/validation error when it caused cancellation. Materialize canonical outputs only after complete success. - Test changes: add pre-canceled and barrier-controlled mid-dispatch cases that assert an error, no canonical partial materialization, and stable adapter-error precedence. Keep the bounded concurrency and deterministic ordering cases. - Dependencies: `EFF-002` and `RSK-011` govern transport/process cancellation latency; this finding owns worker aggregation after cancellation. `TST-001` separately owns the racing fake used under valid concurrency. ### `COR-019`: repeated explicit audio input is accepted by prepare and rejected by transcribe - Category: confirmed correctness and operability defect. - Locations/invariant: `internal/config.validateSession`, `internal/stage.resolveLocalAudioFiles`, `materializeLocalAudioInputs`, and `discoverPreparedAudio`. One accepted audio selection must have a consistent identity from configuration through prepared manifest consumption. - Evidence: configuration validation accepts any non-empty `audio_files` slice and does not check duplicates. Local resolution sorts but does not deduplicate it. Materialization rejects only when the same destination basename maps to a *different* source, so the same path repeated is copied and registered twice; prepare succeeds with duplicate audio input records. Transcribe then prefers those manifest records and `validateAudioFiles` rejects the duplicate clean path before any adapter call. - Realistic scenario: a generated or hand-merged session file repeats one FLAC entry. Validation and prepare both succeed, but the deterministic next stage fails with `duplicate audio file path`, requiring a configuration correction and rerun. - Impact/likelihood/confidence: medium delayed-configuration-failure impact; low-to-moderate authoring/tooling likelihood; high confidence from the consecutive producer/consumer checks. - Estimated remediation scope and owner: small config/prepare decision. Prefer rejecting duplicate cleaned sources during configuration/resolution with field context; alternatively deduplicate deterministically before copying and recording, but do not allow duplicate manifest identities. - Test changes: cover exact and clean-path-equivalent duplicates, distinct sources with the same basename, and a valid multi-file set at the earliest chosen owner; retain transcribe's defensive duplicate-manifest rejection. - Dependencies: `COR-002` owns unsafe identity segments and `RSK-008` foreign restored paths; neither changes duplicate semantics. ### `COR-020`: extraction reuse is not bound to the current trimmed transcript - Category: confirmed correctness and data-integrity defect. - Locations/invariant: `internal/stage/extract.go` in `extractionFingerprint` and result metadata construction, and `internal/stage/extract_resume.go` in `ValidateResume`. A reusable extraction must represent the current direct transcript input as well as the current invocation contract and durable output bytes. - Evidence: execution resolves `narratio.transcript.final_trimmed` and passes its absolute path to Notarius, but the fingerprint contains only executable and config paths, pipeline ID, timeout, working directory, and sorted output contracts. Neither the input path, producer identity, nor an input checksum is recorded. Resume never resolves or hashes the transcript; after comparing the configuration fingerprint it validates only the old promoted bundle and output record. Existing output checksums can therefore all agree while the direct input bytes no longer do. The focused extract documentation lists the fingerprint fields and external transitive limitations but does not assign direct transcript identity to the operator. - Realistic scenario: the canonical final-trimmed file is restored, repaired, or modified out of band while the trim and extract stage records remain succeeded. An ordinary run skips extract and continues with lanes derived from the previous transcript. This is especially difficult to observe because the immutable bundle and its checksums are internally valid. - Impact/likelihood/confidence: high artifact-integrity impact; low-to-moderate manual restore/repair or state-disagreement likelihood; high confidence from the complete fingerprint document and resume control flow. - Estimated remediation scope and owner: small-to-medium extraction identity change. Hash the resolved trimmed transcript before invocation, store its digest plus stable source/producer identity in extraction metadata, and recompute the same evidence during resume before accepting the bundle. Keep external/transitive Notarius dependencies under the documented force rule or add an explicit operator-controlled dependency revision; do not pretend an incomplete recursive file scan can prove them. - Test changes: add one lifecycle test that succeeds extraction, changes the resolved transcript bytes without changing the extraction record, and proves the next invocation reruns and invalidates succeeded downstream work. Keep configuration-change and output-tamper tables separate because they protect different evidence. - Dependencies: `RSK-008` concerns foreign restored absolute paths generally; this finding owns direct extraction-input identity even when the path is canonical. `DUP-007`/`SIM-002` must carry the new evidence into any shared validation structure. Same-path Notarius files remain the documented scenario-3 force limitation. ### `COR-021`: optional normalized, trimmed, and Markdown analyze inputs fail when absent - Category: confirmed correctness defect. - Locations/invariant: `internal/stage/analyze.go` in `resolveScriptoriumInput`. Every Scriptorium input with `required: false` must be omitted when its valid source is unavailable, independent of source family; `required: true` owns source-specific failure and repair guidance. - Evidence: after the catalog resolver returns `ErrSessionArtifactNotFound`, extraction and configured branches consult `inputCfg.Required`, while polished and default built-ins return unresolved for the caller to apply the flag. The cases for `narratio.transcript.final`, `final_trimmed`, `final_markdown`, and `final_trimmed_markdown` instead return errors unconditionally. The caller therefore never reaches its optional-omission branch. Focused tests cover successful values and required missing values for these sources, but no optional missing case. - Realistic scenario: one artifact can use an optional rendered transcript to improve a prompt while remaining valid from a prepared or extraction input. On a session without render output, analyze fails instead of invoking Scriptorium without that optional input. - Impact/likelihood/confidence: medium workflow-availability impact; moderate likelihood for optional prompt enrichment; high confidence from the direct branch ordering and the repository-wide required/optional contract. - Estimated remediation scope and owner: small analyze source-resolution change. Return source-specific errors only when required, otherwise return an unresolved optional result. Preserve the distinct normalize/trim/render guidance for required inputs. - Test changes: table all built-in transcript/bounds identities as missing with both required values, asserting omission for optional and the correct producer guidance for required. Avoid duplicating successful resolver tests. - Dependencies: use `SIM-003` only to make the decision shape clearer; do not merge the source-specific guidance policies. ### `COR-022`: explicit artifact selection bypasses previous-input prerequisite planning - Category: confirmed cross-boundary correctness defect. - Locations/invariant: `internal/artifacts/catalog.go` in `RegisterConfiguredArtifacts`, `internal/artifacts/previous_requirements.go` in `CollectPreviousArtifactRequirements`, and its app/prepare/restore/status/ validation callers. Every artifact that analyze can execute must contribute its required previous-session inputs to prerequisite planning. - Evidence: a non-empty selected set makes membership authoritative over `Enabled`; a focused catalog test explicitly selects and makes a disabled artifact executable. CLI validation accepts any configured key regardless of enabled state. Requirement collection has no selection input and skips every disabled artifact. Consequently prepare, restore planning, object-store composition, artifact listing, status, and validation all omit dependencies that analyze will require from an explicitly selected disabled artifact. - Realistic scenario: an operator keeps an occasional artifact disabled but selects it for one run. It requires a previous-session recap. The run does not compose/fetch/prepare that requirement, and analyze fails. Following its prepare guidance still cannot populate the file while the artifact remains disabled. - Impact/likelihood/confidence: medium-to-high workflow correctness impact; moderate likelihood because explicit selection is the natural one-off path; high confidence from the tested selection override and collector signature. - Estimated remediation scope and owner: medium app/artifact-planning change after `ARC-007` chooses authority. If selection can activate disabled entries, derive one effective artifact set and pass it to every prerequisite consumer. If selection must intersect enabled entries, reject disabled selections before planning. Do not let analyze and prepare recompute different sets. - Test changes: an assembled run/prepare/analyze test must select a disabled artifact with a required previous source and prove composition, planning, materialization, and execution agree. Also cover optional previous input and no-selection enabled behavior. - Dependencies: `COR-010` concerns readiness checks for requirements that were collected; this finding owns requirements omitted by selection. `ARC-007` owns the product choice, and `TST-010` owns regression placement. ### `COR-023`: required previous-input failure recommends an invalid command - Category: confirmed correctness/operability defect. - Locations/invariant: `internal/stage/analyze.go` in `resolveScriptoriumInput`, with syntax owned by `docs/cli.md` and `internal/app` command parsing. Actionable failure guidance must name an executable command for the current session. - Evidence: the error says `run narratio run-stage --force prepare`. The actual grammar is `narratio run-stage prepare --force`; the emitted form places a flag where the required stage argument belongs, reverses the stage/flag order, adds an extraneous leading `run`, and omits the session ID. The existing test checks only the fragment `run-stage --force prepare`, so it codifies rather than catches the bad syntax. Prepared-stable and Markdown guidance already include the session ID in the correct order. - Realistic scenario: analyze fails on an absent required previous artifact and the operator copies the suggested recovery command. Parsing fails before prepare runs, extending an already blocked recovery path. - Impact/likelihood/confidence: low data impact but direct recovery/operability impact; high likelihood whenever this failure occurs; high confidence from exact CLI grammar and emitted text. - Estimated remediation scope and owner: tiny analyze message change. Emit `narratio run-stage prepare --force`, or route producer guidance through a small command formatter if `SIM-003` establishes one. Include the session ID already available in `paths`. - Test changes: assert the complete command string, not an invalid fragment. A parser round-trip is optional if a shared formatter is introduced. - Dependencies: `COR-022` can make prepare itself omit the dependency; repair both before claiming selected-disabled recovery is actionable. ### `COR-024`: documented Scriptorium input passthrough fields are silently discarded - Category: confirmed configuration-contract defect. - Locations/invariant: `internal/config/config.go` in `ScriptoriumInputConfig`, `docs/config.md` in Scriptorium artifact entries, `internal/stage/analyze.go`, and `internal/adapters/scriptorium`. Accepted and documented operator fields must affect behavior or be rejected/reserved explicitly. - Evidence: the strict schema accepts `artifact` and `path`, and configuration documents both as optional passthrough adapter fields. Graph-augmented exact searches find no read of `inputCfg.Artifact` or `inputCfg.Path`. Resolution consumes only `Source` and `Required`; execution sends only a map from input name to resolved filesystem path. Neither Scriptorium request type nor its CLI/generated invocation representation has fields for the accepted values. - Realistic scenario: an operator sets either field based on the maintained configuration reference to select an upstream adapter artifact/path. Config loading and validation succeed, but the invocation is identical to one where the fields were absent, with no warning that intent was lost. - Impact/likelihood/confidence: medium configuration-trust impact; low-to- moderate use likelihood because no maintained example uses the fields; high confidence that current values are inert, with intended upstream semantics intentionally left unresolved. - Estimated remediation scope and owner: product/config-and-adapter decision. Define and implement exact Scriptorium wire semantics if the feature is supported; otherwise remove the fields from schema/documentation or reject non-empty values with a migration message. Do not guess how `artifact` and `path` combine with canonical `source` resolution. - Test changes: once authority is chosen, assert end-to-end adapter invocation semantics or strict rejection. A decode-only test is insufficient. - Dependencies: `COM-005` owns other analyze documentation gaps. `ARC-007` concerns selection, not these inert per-input fields. ### `RSK-001`: invocation audit records can remain indefinitely `running` - Category: confirmed correctness/operational risk. - Locations/invariant: `internal/app/runner.go` in `executeStages`, `internal/manifest/store.go` normalization, and the unused production `StatusInterrupted` model value. Every completed or handled invocation should have an intelligible terminal audit outcome, while process interruption must remain safely resumable. - Evidence: the initial run manifest is saved with overall status `running`. A resume-validator error returns directly without marking it failed; terminal session-save and run-save failures leave the last run state running; and a process death after either running save has the same effect. Neither load normalizer converts running records to interrupted, and later invocations consult only the session manifest and never reconcile older run manifests. `StatusInterrupted` has no production writer. The existing resume-validation error test checks preservation of the session success but not the run record. - Realistic scenario: extraction resume validation encounters an unsafe or unreadable receipt. The command returns a controlled error, the reusable session result is correctly preserved, and the run audit file remains `running` forever. A kill or persistence failure can leave analogous dual- ledger disagreement. - Impact/likelihood/confidence: medium operator/audit impact and low risk of unsafe reuse because non-succeeded session stages rerun; moderate likelihood over the life of a long-running pipeline; high confidence. - Estimated remediation scope and owner: medium application/manifest change. Terminalize handled post-creation errors when persistence is available and define an explicit startup/status reconciliation policy for abandoned running records. Preserve the current conservative session-authority rule. - Test changes: extend the resume-validation error integration test to assert a terminal failed run; add interruption/restart and injectable session/run-save boundary cases. Filesystem crash durability itself remains a Stage 3 concern. - Dependencies: `SIM-001` may provide one failure-finalization path and `TST-002` records the missing persistence seam. Stage 5 should check how status/restore presents abandoned runs; Stage 13 should decide whether true process interruption is accepted risk after handled errors are fixed. ### `RSK-002`: single-file atomic replacement is not crash-durable - Category: confirmed data-durability risk. - Locations/invariant: `internal/fileops.WriteFileAtomic`, `CopyFileAtomicWithChecksum`, `InstallDownloadedTempFile`, and both atomic manifest-save sequences in `internal/manifest/store.go`. Successful canonical files and durable ledgers must survive a crash/power-loss boundary consistent with reported success. - Evidence: writers that create their own temporary file sync its data before rename, but none syncs the containing directory after rename. Download callers close an initially empty sibling temp before object-store download, and neither the download interface nor `InstallDownloadedTempFile` syncs the completed file before rename. Directory promotion already demonstrates the stronger sequence by syncing copied files, temporary directories, and the destination parent after no-replace rename. - Realistic scenario: a command reports a saved session/run manifest or materialized canonical output, then the host loses power. The directory entry rename is not durable and can disappear or expose filesystem-dependent state; a downloaded restore/previous/audio file has an additional unsynced-data window. - Impact/likelihood/confidence: high integrity/recovery impact; low likelihood per invocation but cumulative operational exposure; high confidence that the sync calls are absent, with exact failure manifestation filesystem-dependent. - Estimated remediation scope and owner: small-to-medium shared fileops and manifest change. Centralize the durable temp-file install sequence, sync completed downloads before install, then sync the parent directory with the same explicit platform policy used by directory promotion. - Test changes: introduce a narrow injectable sync/rename seam or ordered filesystem-operation fake to assert file-sync-before-rename and directory- sync-after-rename for write, copy, download install, and both manifest types; retain real-filesystem overwrite/temp-cleanup tests for visible atomicity. - Dependencies: `DUP-001` is the maintainability multiplier. Stages 5 and 7 should reference this root for restore/audio/storage download behavior rather than create new durability findings. ### `RSK-003`: stale sentinel locks can block a session indefinitely and release failures are hidden - Category: confirmed availability/operational risk. - Locations/invariant: `internal/artifacts/local.go` lock acquisition/release, the ignored deferred release in `internal/app/runner.go`, and the manual stale lock procedure in `docs/troubleshooting.md`. A live same-session invocation must exclude competitors, while completed or dead ownership must have a safe, observable recovery path. - Evidence: `O_CREATE|O_EXCL` correctly serializes live contenders, but any existing `.lock` conflicts without checking whether its recorded PID/time is live. Process death leaves the file forever. `ReleaseSessionLock` can report close or unlink failure, but the runner defers it as `_ = ...`; an unlink failure can therefore be reported as command success while the next run is blocked. Recovery requires the operator to inspect process state and manually delete the file. - Realistic scenario: the process is killed or the filesystem rejects unlink after a successful long run. Every later invocation for the session fails at acquisition until an operator notices and safely removes the sentinel. - Impact/likelihood/confidence: medium-to-high availability impact; moderate lifetime likelihood for interruption and low likelihood for unlink failure; high confidence. Mutual exclusion itself is sound in the ordinary live- process case. - Estimated remediation scope and owner: medium artifact-store/application change. Prefer an OS-released lock while retaining useful metadata, or define a conservative ownership/lease protocol; surface release failures without obscuring an earlier command error and document automated versus manual recovery. - Test changes: add process/concurrency coverage for live exclusion and death recovery, an injectable close/unlink failure proving the command cannot silently succeed, and a subsequent-acquisition assertion. Do not encode unsafe PID reuse assumptions in a unit test. - Dependencies: `RSK-001` covers abandoned invocation audit state, not exclusion. Stage 5 should check operator status presentation; Stage 12 owns the smallest durable assembled-runner case. ### `RSK-004`: default runtime modes can expose private campaign material to other local users - Category: confirmed security/operational risk. - Locations/invariant: layout and mutation modes across `internal/artifacts/local.go`, `internal/fileops`, stage/download writers, and manifest persistence; security contract in `docs/policy/architecture.md`. Transcripts, prompts, artifacts, reports, logs, and manifests are private campaign material. - Evidence: runtime directories request `0755` and files request `0644`, subject only to ambient process umask. The default workspace is `/var/lib/narratio`; Narratio can create its layout with those modes, and no operations contract requires a restrictive umask, private parent, service-user ownership, or configurable mode policy. Fixed-mode promotion also normalizes bundle files to `0644` and directories to `0755`. - Realistic scenario: a service or operator runs with the common `0022` umask on a multi-user host. Other local accounts can traverse the workspace and read transcripts, prompts, generated artifacts, diagnostics, and manifests. - Impact/likelihood/confidence: high confidentiality impact; environment- dependent but realistic likelihood; high confidence in requested modes and documentation absence, moderate confidence in exposure on any particular deployment because parent ACLs can mitigate it. - Estimated remediation scope and owner: medium operations/configuration and shared-writer change. Establish secure directory/file defaults, preserve deliberate executability where needed, define ownership/ACL/umask expectations, and provide an explicit compatibility/migration story. - Test changes: assert privacy-oriented effective modes under a controlled permissive umask for representative layout, manifest, artifact, log, and promoted-bundle paths; document platform/ACL limitations instead of assuming POSIX bits are universal. - Dependencies: Stage 6 confirmed there is no mode/config override or documented deployment privacy guarantee. Stage 7 owns adapter diagnostics, and Stage 13 should rank the risk against deployment assumptions. ### `RSK-005`: remote publish locks are race-prone snapshots - Category: confirmed correctness/concurrency risk. - Locations/invariant: `internal/app/runner.go` remote-lock load order, `remote_locks.go`, `operator_locks.go`, and unconditional `storage.ObjectStore.Upload`. An operator lock intended to protect a published destination should not be silently lost or bypassed by concurrent control- plane activity. - Evidence: a publish-capable run loads and merges the remote lock document before acquiring its local session lock, then uses that in-memory slice for the entire invocation. Lock add/remove separately loads the full document, mutates it, and unconditionally uploads a replacement without a generation check or the runner lock. Two mutations can lose an update, and a lock added after publish's load cannot affect that in-flight upload. Existing tests are sequential and prove only static precedence, mutation validation, and loaded lock enforcement. - Realistic scenario: two operators lock different outputs at the same time; the last full-document upload drops the other lock. Or an operator locks a destination while a long run is approaching publish, but that run already loaded the old document and overwrites the destination despite the command reporting that it was locked. - Impact/likelihood/confidence: high protected-output integrity impact; low-to-moderate likelihood in multi-operator or multi-host use; high confidence in the lost-update/stale-read mechanics, moderate confidence that deployments rely on concurrent lock mutation because no concurrency contract is stated. - Estimated remediation scope and owner: medium app/storage-capability change. Define lock activation semantics and use object generation/ETag conditional replacement with retry, or a remote coordination primitive. Re-read or bind the effective generation at the publish commit boundary. Static locks remain immutable configuration and need no remote mutation protocol. - Test changes: add a version-aware stateful store and deterministic barriers for two add/remove writers plus add-during-publish; prove no lock is lost and define whether the in-flight publish aborts or observes a committed snapshot. Retain sequential force/static-lock tests. - Dependencies: `RSK-003` concerns only the local single-writer sentinel and cannot serialize other hosts or lock commands. `COR-004` may influence the appropriate remote compare-and-swap capability. ### `RSK-006`: restore does not protect one coherent local transition from plan through manifest install - Category: confirmed correctness/recovery risk. - Locations/invariant: `internal/app/restore.go`, `restore_plan.go`, and `restore_execute.go`; runner reuse of the session manifest. Local action decisions and the manifest-last transition must remain coherent despite competing local work and mid-restore failure. - Evidence: restore discovers and classifies local paths before acquiring the session lock, then never revalidates `skip_same` or conflicts after lock acquisition. Another completed runner/restore or local edit in that window can make the plan stale. During execution, files install incrementally with no rollback or incomplete marker. A forced failure before manifest installation leaves the old successful manifest in place even though some files it governs were overwritten with remote content. Later runners do not inspect the failed restore report. - Realistic scenario: planning marks a transcript `skip_same`; another runner acquires/releases the lock and changes it; restore then acquires the lock, skips the stale decision, and installs the remote manifest. Or forced restore overwrites that transcript, fails on a later artifact, and releases the lock with the old manifest still claiming success over changed content. A normal run can then reuse that manifest instead of completing restore. - Impact/likelihood/confidence: high pipeline integrity impact; low likelihood per restore but realistic under operator concurrency or recovery from damaged storage; high confidence in ordering/no-revalidation, with exact external edit likelihood environment-dependent. - Estimated remediation scope and owner: medium-to-large application/filesystem change. Acquire the session lock before executable classification (dry-run can remain unlocked/read-only), revalidate under lock, and make incomplete forced restore observable to the runner. Consider staging a complete tree or a durable restore transaction marker rather than attempting broad rollback. - Test changes: deterministic barriers around plan/lock plus a failure after one forced install; assert stale skip decisions cannot commit and ordinary runner reuse is blocked until retry completes. Preserve the current manifest-last and retry-idempotency tests. - Dependencies: `RSK-003` governs stale local lock recovery; `COR-003` and `RSK-002` govern confined and crash-durable installation. `COR-008` is the analogous remote snapshot problem. ### `RSK-007`: audio restore and cache hits use size as content identity - Category: confirmed correctness/data-integrity risk. - Locations/invariant: `internal/app/restore_plan.go` audio classification and `internal/audio/s3_audio.go` cache validation. Reused audio must correspond to the intended remote object generation, not merely have a plausible length. - Evidence: existing restore audio with the same positive size is `skip_same` without a body/checksum/ETag comparison, even under force. Cache validity accepts any nonempty non-directory path and, when available, equal size. Although `ObjectInfo.ETag` is carried into the materializer and newly copied files compute a checksum, neither participates in later cache identity. With unknown remote size, any nonempty cache entry is accepted. A focused test deliberately proves same-sized different strings skip without download. - Realistic scenario: an S3 audio key is replaced with corrected audio of the same byte length, or a cache file is corrupted without changing length. Restore/prepare silently reuse the old bytes; transcription proceeds from audio that no longer matches remote operator intent. - Impact/likelihood/confidence: high downstream content-integrity impact; low-to-moderate likelihood for same-size replacement/corruption; high confidence in the comparison rules. The shortcut is an evident performance tradeoff, so classification as risk rather than certain wrong output for every cache hit is appropriate. - Estimated remediation scope and owner: medium audio/storage metadata change. Bind cache entries to a stable object generation or trustworthy checksum via sidecar metadata, validate regular-file/no-follow status, and define multipart ETag limitations. Force should refresh or verify when explicitly requested. - Test changes: same-size remote replacement, corrupt same-size cache, unknown- size cache, ETag/generation change, and force semantics. Keep the current hit/ miss/invalid-size cases as lower-cost mechanism coverage. - Dependencies: Stage 7 owns which S3 metadata is trustworthy and portable. `COR-003` owns symlink-based cache/path escape; do not solve identity with path checks alone. ### `RSK-008`: restored manifests retain foreign absolute paths that later consumers prefer - Category: confirmed correctness/security risk. - Locations/invariant: manifest installation in `internal/app/restore_execute.go`, path preservation in `internal/manifest`, and manifest-first resolution in `internal/artifacts/artifact_resolver.go`. Restored state should resolve to the selected local workspace unless a trusted external path contract explicitly authorizes otherwise. - Evidence: restore installs current manifest bytes unchanged. Published manifests normally contain absolute producer-local output/input paths plus top-level work/spool/run paths. Artifact resolution returns an absolute manifest path unchanged and prefers it over the canonical restored fallback whenever it exists and validates. The extraction round-trip test deliberately preserves `/prior/workspace/...` metadata but does not run a consumer against an existing foreign path. Top-level fields already trigger `COR-001` on the next invocation. - Realistic scenario: restore moves a session to a host or workspace where the old absolute path exists with stale or attacker-controlled transcript content. Analyze/publish reads that file instead of the restored canonical copy. Even when it does not exist, session identity retains wrong run/work/spool metadata and can direct later work through `COR-001`. - Impact/likelihood/confidence: high integrity/confidentiality impact; low-to- moderate likelihood because path existence depends on host/layout reuse; high confidence in preservation and resolver precedence. - Estimated remediation scope and owner: medium restore/artifact/manifest change. Separate portable logical references from host-local diagnostics, rebase or reject restored absolute paths outside the selected session root, and preserve original values only as bounded provenance if useful. - Test changes: restore a real manifest with built-in/configured input/output records from a different root, create a conflicting outside sentinel, and assert downstream resolution uses the restored canonical path. Extend `COR-001` tests for top-level restored identity. - Dependencies: `COR-001` owns stale top-level run identity; `COR-003` owns filesystem symlink confinement. Stage 10 should reuse this result when reviewing manifest-first artifact trust. ### `RSK-009`: remotely discovered session configuration is left in system temporary storage - Category: confirmed confidentiality/lifecycle risk. - Locations/invariant: `internal/app/config_loader.go` in `loadCommandConfig` and `internal/adapters/storage/temp_download.go` in `DownloadObjectToTemp`. Every successful temporary download must have an explicit owner and cleanup point, especially when it contains private session configuration. - Evidence: failed downloads remove their temporary file, but successful remote session fallback returns the path into `Config.SessionPath` and `SessionSource`. No caller removes it. Full execution later copies it to canonical `inputs/session.yml` yet leaves the original; plan, status, validate, and single-stage commands can leak one on every invocation. The ephemeral path can also be retained as source/spool provenance in a manifest. - Realistic scenario: a long-running worker repeatedly uses remote-only session configs. System temporary storage accumulates readable copies containing S3 audio references and private campaign/session settings after commands finish, until an unrelated host cleanup policy happens to remove them. - Impact/likelihood/confidence: medium confidentiality and disk-lifecycle impact; deterministic for remote fallback; high confidence from all successful caller paths. - Estimated remediation scope and owner: medium app loader/command-lifetime change. Return an owned cleanup handle or bytes, defer cleanup at the command boundary after all consumers finish, and record canonical logical provenance rather than the ephemeral host path. - Test changes: assert removal after successful and failed full, read-only, and single-stage commands; preserve the existing failure cleanup assertion; verify manifests and reports do not retain a nonexistent system-temp source path. - Dependencies: do not remove the file before prepare or other consumers finish. `RSK-004` remains the broader runtime-mode disclosure boundary. ### `RSK-010`: filesystem secret loading follows links and reads non-regular entries - Category: confirmed confidentiality/availability risk. - Locations/invariant: `internal/app/secrets_env.go` in `loadSecretsFromConfig`. A configured secrets directory should define a bounded set of regular secret files, not grant an ambient read capability through entry replacement or special file types. - Evidence: the loader skips only directory entries and invalid names, then calls `os.ReadFile` on the joined path. It follows a validly named symlink and accepts any other non-directory entry that `ReadFile` can open, without a regular-file/type/size check. The unreadable-entry test uses a broken symlink only to assert an error; it does not reject a working link before reading it. - Realistic scenario: a writable or incorrectly provisioned secrets directory contains `AUDITA_TOKEN` linked to an unrelated readable file, or a large/special entry. A more privileged invocation reads the target into process environment inherited by subprocess adapters, or incurs unbounded I/O/memory and blocks composition. - Impact/likelihood/confidence: high confidentiality/availability impact when directory ownership is weak; deployment-dependent likelihood; high confidence from the entry-type and read flow. - Estimated remediation scope and owner: small-to-medium app/filesystem boundary correction. Inspect without following links, admit regular files only, enforce a documented size bound, and use a handle-relative/no-follow read where the supported platforms allow entry replacement races to be closed. - Test changes: cover working and broken symlinks, FIFO/special entries where portable, oversized files, replacement races at the chosen primitive, valid newline trimming, and existing-environment precedence. - Dependencies: Stage 7 should verify downstream environment propagation but must not duplicate filesystem policy. `COR-003` owns mutation confinement, whereas this finding owns reads from the secret directory. ### `RSK-011`: subprocess cancellation terminates only the direct child - Category: confirmed correctness/operational risk. - Locations/invariant: `internal/adapters/subprocess.Run`, used by every Audita, Seriatim, Scriptorium, and Notarius invocation; integration contracts state that parent cancellation and timeouts bound invocations. Cancellation must terminate all work started for one tool invocation, not merely return from waiting on its first process. - Evidence: the launcher uses `exec.CommandContext` without changing process attributes or `Cmd.Cancel`. In the audited Go toolchain that cancel function calls `Kill` on `cmd.Process`; no process group/job ownership or descendant cleanup exists. The timeout test starts a sleeping direct helper only. - Realistic scenario: an external CLI starts a worker subprocess and then the stage times out. Narratio kills and waits for the CLI, returns a timeout, and closes its descriptors, while the worker continues consuming CPU, writing run-local files, or making paid API requests after the manifest records failure. - Impact/likelihood/confidence: high resource/integrity impact; moderate likelihood for Python/worker-based external tools; high confidence in the launcher semantics, with exact descendant behavior dependent on each tool. - Estimated remediation scope and owner: medium, platform-aware subprocess mechanism. Establish an invocation-owned process group on Unix and equivalent job/process-tree behavior on supported Windows, terminate the group on cancellation, wait/reap deterministically, and document any unavoidable platform limit. - Test changes: add a helper that spawns a descendant, records its identity, times out, and proves the descendant cannot write a delayed sentinel. Keep the direct-child timeout test and add explicit parent cancellation. - Dependencies: keep process mechanics in the shared launcher; adapter packages should not each implement their own kill policy. ### `RSK-012`: subprocess diagnostics can persist raw credentials - Category: confirmed confidentiality risk. - Locations/invariant: `internal/adapters/subprocess.Run`, `openLogWriters`, `readRedactedTail`, and inherited-environment use by Notarius and Scriptorium. Architecture policy forbids raw secrets in logs and manifests. - Evidence: stdout and stderr are written directly to persisted log files with no filtering. The 2 KiB error tail replaces only values whose keys look sensitive in `RunRequest.EnvOverrides`; values inherited through `os.Environ()` are not considered. Audita's mapped key is protected only in the returned tail, not in its raw log. Scriptorium and Notarius intentionally inherit their environment, so a child that echoes an inherited credential can also place it in the wrapped stage error persisted to both manifests. - Realistic scenario: a downstream tool includes its API key in a debug/error dump. Narratio retains the value in a run log, and for an inherited key also embeds it in the durable failure text. Run archives or troubleshooting access then disclose the credential beyond its intended environment boundary. - Impact/likelihood/confidence: high confidentiality impact; low-to-moderate faulty/debug-tool likelihood; high confidence in the diagnostic flow. - Estimated remediation scope and owner: medium shared subprocess/adapter correction. Define the sensitive environment names supplied to each child, redact their values in streaming log writers and returned tails, minimize the inherited environment where protocol-compatible, and preserve useful bounded diagnostics without copying raw secret material into manifests. - Test changes: cover override and inherited sensitive values in stdout, stderr, on-disk logs, and returned errors; assert non-sensitive diagnostics remain readable. Avoid real credentials in fixtures. - Dependencies: `RSK-010` owns safe secret-file acquisition; this finding owns propagation after values enter the process. `RSK-004` owns broad file modes, not the prohibited content itself. ### `RSK-013`: ordinary subprocess output validation is unbounded and follows links - Category: confirmed correctness/availability risk. - Locations/invariant: Audita `validateProcessedOutput`/`validateJSONFile`, Seriatim `validateJSONFile`/`validateTranscriptFile`/render validation, Scriptorium output checks, and `internal/contracts` bounds/transcript reads. External output must be a bounded regular result at the requested path before a stage trusts or materializes it. - Evidence: these validators use unbounded `os.ReadFile` or link-following `os.Stat`. They do not reject a symlink before parsing, and most do not first establish a regular-file handle. A malformed or oversized result can allocate until memory exhaustion; a symlink to an existing valid JSON/text file can satisfy validation and be copied as the stage output. Notarius already uses `Lstat`, same-file checks, regular-file enforcement, and explicit size limits, while WhisperX bounds responses to 10 MiB, demonstrating a compatible local boundary pattern. Stage 8 confirmed the same acquisition path is used again by `validateTranscriptJSONFile`, `validateProcessedTranscriptOutput`, `copyTranscript`, `requireNonEmptyFile`, and run-local materialization before merge, polish, normalize, trim, and render record canonical outputs. The risk therefore crosses the adapter/stage boundary rather than ending at adapter first-pass validation. Stage 9 found the bounded Notarius management-file boundary stops short of configured lane bodies: extract's `checksumRegularFile` performs one unbounded `os.ReadFile` per lane to parse and hash it, while catalog hydration first streams the checksum and then performs another unbounded `os.ReadFile` for JSON validity. These paths reject links and non-regular files but still admit memory-exhausting external JSON. - Realistic scenario: a faulty external CLI writes a multi-gigabyte JSON result or leaves the requested output as a symlink to stale data. It exits zero; Narratio then exhausts memory or records unrelated bytes as a successful canonical transcript/artifact. - Impact/likelihood/confidence: high availability/integrity impact; low-to- moderate faulty-tool or filesystem-reuse likelihood; high confidence in the validators, with appropriate limits requiring contract decisions. - Estimated remediation scope and owner: medium shared read-mechanism plus adapter-specific schema policy. Open without following links where supported, require a regular file, enforce documented per-contract limits, parse from the established handle, and leave semantic schema checks in each adapter. - Test changes: add one shared table for symlink/non-regular/oversized reads and focused adapter cases proving semantic errors retain their context. Include one configured Notarius lane/catalog case at the owner of the chosen bound; avoid duplicating the same large fixture for every JSON validator. - Dependencies: coordinate no-follow mechanics with `COR-003` and size policy with Stage 8/10 consumers. Do not weaken Notarius's stricter bundle boundary. ### `RSK-014`: S3 listing has no continuation-token progress guard - Category: confirmed availability/resource risk. - Locations/invariant: `internal/adapters/storage.S3Backend.List`. Pagination must either make observable progress, finish, fail, or honor cancellation without unbounded duplicate accumulation. - Evidence: the loop repeats while `IsTruncated` is true and a next token is non-nil, assigning that token without comparing it with the prior token. A provider that repeats one token returns the same page indefinitely. The production callers generally have no operation-specific deadline, and the storage fake/test returns only one page and ignores continuation behavior. - Realistic scenario: an S3-compatible endpoint emits a malformed truncated response with a repeated token. Session discovery, prepare, restore, status, or cleanup loops at full request rate and appends duplicate objects until an operator cancels or the process exhausts memory. - Impact/likelihood/confidence: high availability and possible request-cost impact; low provider-fault likelihood; high confidence in loop behavior. - Estimated remediation scope and owner: small storage-adapter correction. Reject an empty or repeated next token on a truncated response with contextual provider/protocol error; retain caller-context cancellation and caller-owned ordering. - Test changes: use a stateful S3 fake for two valid pages, repeated/empty token, later-page error, cancellation, and deterministic normalized aggregation. - Dependencies: snapshot/generation authority remains `COR-008`; this finding concerns completion of one list operation only. ### `RSK-015`: analyze dependency preflight chooses errors nondeterministically - Category: confirmed determinism/operability risk. - Locations/invariant: `internal/stage/analyze.go` in `orderSelectedScriptoriumArtifacts`. Given one configuration, selection, and filesystem state, dependency validation should return one stable diagnostic before any adapter or output mutation. - Evidence: selected names are copied into a map, then the initial unknown/ unavailable dependency preflight ranges directly over that map and returns the first failure. Go map order is unspecified. When two selected artifacts each have an unavailable unselected dependency, either artifact can own the returned error across executions. Later graph construction also ranges over maps, but it sorts edges and ready nodes before observable successful order; the nondeterminism is confined to preflight diagnostics. - Realistic scenario: a configuration deploy omits several reused dependency files. Repeated CI or operator invocations report different first blockers, making logs, snapshots, and one-at-a-time recovery unstable even though no external state changed. - Impact/likelihood/confidence: low execution-integrity impact but moderate diagnostic/reproducibility impact; moderate multi-error likelihood; high confidence from explicit map iteration before first return. - Estimated remediation scope and owner: tiny analyze ordering change. Iterate the already sorted selected slice or sort map keys before preflight. Preserve lexical successful topological ordering; do not introduce a generic graph package solely for this repair. - Test changes: construct at least two independently unavailable dependencies from intentionally shuffled insertion order and assert one stable exact error across repetitions. - Dependencies: `SIM-003` may provide an indexed plan, but this correction does not depend on structural refactoring. ### `EFF-001`: restore repeatedly downloads the same objects during planning and execution - Category: confirmed efficiency and clarity issue. - Locations/invariant: `internal/artifacts.LoadCurrentState`, `internal/app/restore_plan.go` checksum classification, `internal/previouscache.BuildPlan`, and `restore_execute.go`. Recovery should avoid redundant remote transfer while preserving conflict and snapshot correctness. - Evidence: discovery downloads the current manifest, planning can download it and every same-size non-audio object again for checksum comparison, and execution re-downloads every forced differing object plus current/previous manifests for installation. `RestorePlanOptions.DryRun` is otherwise unused, so dry-run performs the same temporary body downloads for classification. Temporary files are cleaned and no durable session write occurs, but the focused internal document's “performs no local writes” wording obscures these system-temp writes. - Realistic scenario: a forced restore of several large same-sized artifacts downloads each body to decide it differs, discards it, then downloads it again to install. High-latency or metered storage doubles transfer and lengthens the interval exposed to `COR-008` remote changes. - Impact/likelihood/confidence: low-to-medium cost/latency impact that scales with artifact size and remote pricing; occurs deterministically for same-size differing forced files and repeated manifests; high confidence. - Estimated remediation scope and owner: medium restore/storage-contract change. Couple snapshot/version repair with a verified downloaded candidate that can be retained for execution, or expose trustworthy digest/version metadata. Document dry-run as having no durable/session mutation unless truly streaming comparison eliminates all temporary writes. - Scale/current-versus-proposed cost: for `n` same-size non-audio objects with aggregate body size `B`, planning performs `n` full downloads and reads `B` remote plus `B` local bytes for checksums. A forced apply of differing bodies then downloads up to another `B`; current-state discovery also fetches the pointer and manifest before the listed manifest can be classified again. Retaining generation-bound verified candidates would keep apply to one body transfer per changed object rather than two, while dry-run necessarily keeps its one classification transfer unless trustworthy remote digests are available. Metadata/list and local checksum costs remain linear. - Measurement: use a counting/versioned object-store fake with representative 100 MiB and 1 GiB aggregate bodies to record calls, bytes, and elapsed time for missing, equal, size-different, and same-size-different objects in dry-run and apply modes. The repair should demonstrate the lower byte bound without weakening generation revalidation; no CPU-only microbenchmark is useful. - Test changes: count bytes/downloads for missing, equal, size-different, and same-size-different objects in dry-run and apply modes; assert relational upper bounds rather than exact private call choreography after snapshot design is chosen. - Dependencies: solve with `COR-008` so caching a plan download cannot install a stale generation. Stage 7 owns adapter metadata/cost tradeoffs. ### `EFF-002`: WhisperX buffers each complete multipart upload in memory - Category: confirmed efficiency/resource-use issue. - Locations/invariant: `internal/adapters/whisperx.doTranscribeAttempt` and the transcribe stage's configured concurrent use of `Client.Transcribe`. Upload memory should remain bounded independently of aggregate audio size. - Evidence: every attempt copies the complete audio file into a `bytes.Buffer`, adds the remaining multipart fields, and only then constructs and sends the request. The copy does not observe context cancellation. Concurrent speakers therefore retain roughly one full audio file each in memory, and every retry repeats the allocation/copy before network I/O. Response memory is separately and correctly capped at 10 MiB. - Realistic scenario: several long speaker tracks are transcribed with the configured worker concurrency. Narratio allocates their aggregate size at once and can be killed for memory pressure before the HTTP server receives a byte; canceling during a large local copy does not stop that work promptly. - Impact/likelihood/confidence: medium-to-high memory/operability impact that scales with ordinary media size and concurrency; high likelihood on long sessions; high confidence from the request construction path. - Estimated remediation scope and owner: medium WhisperX adapter change. Stream multipart content from a per-attempt reopenable audio source, propagate copy errors and cancellation through the request body, and preserve replay across retries without sharing a consumed reader. - Scale/current-versus-proposed cost: with worker concurrency `c` and average audio size `s`, request construction retains approximately `O(c*s)` bytes before network progress, plus multipart overhead, and repeats that allocation on each retry. A pipe-backed multipart producer with bounded copy buffers keeps application buffering at `O(c)` while total network I/O remains necessarily `O(c*s)` per attempt. This matters for ordinary multi-hour audio, where each speaker track can be hundreds of MiB; it is not a small-allocation optimization. - Measurement: add a benchmark or controlled transport test using several 100 MiB sparse/generated inputs at configured concurrency, reporting `-benchmem`, peak heap/RSS, time until the transport receives its first byte, cancellation latency, and retry reopen behavior. The expected win is bounded peak memory and earlier upload progress, not fewer transmitted bytes. - Test changes: use a blocking/counting reader or transport to prove the request begins before the complete source is buffered, cancellation interrupts body production, retries reopen cleanly, and response/output bounds remain intact. - Dependencies: Stage 8 confirmed the worker concurrency limit and stable successful result ordering; streaming should make each worker bounded rather than changing that stage policy. `COR-018` separately owns cancellation being mistaken for complete success. ### `ARC-004`: notification configuration has no production transport consumer - Category: confirmed architectural/operator-boundary defect. - Locations/invariant: `internal/config.NotificationConfig`, pipeline examples and configuration reference, `internal/app/runner.go` notifier composition, and `internal/adapters/notify`. Accepted operator settings must either select implemented behavior or be rejected/described as reserved. - Evidence: backend, recipient, and timeout are accepted and the timeout is parsed, but composition always installs `notify.NoopSender` when no test collaborator is injected. The adapter package contains only no-op and fake senders and has no mapping for those three fields. The notify stage can thus succeed with placeholder metadata regardless of configured recipient. Only the internal overview calls it a placeholder; the public config table and annotated example present ordinary optional settings. - Realistic scenario: an operator configures a backend and recipient, observes a succeeded final stage, and assumes a completion or failure notice was delivered when no external call occurred. - Impact/likelihood/confidence: high operator-expectation impact; moderate likelihood because the fields are publicly surfaced; high confidence from complete composition and adapter inventory. - Estimated remediation scope and owner: small config/documentation change if delivery remains deferred, or medium integration/composition work if delivery is required. The smallest safe correction is to reject non-empty backend/recipient values and label/remove reserved settings until a canonical notification integration contract exists; do not silently map them in the stage. - Test changes: assert non-placeholder settings cannot validate while no transport exists, or, after a transport is specified, add adapter contract tests for timeout/cancellation/error adaptation and an assembled composition test proving selection. Preserve the no-op path only when explicitly chosen. - Dependencies: the ordinary-success lifecycle vocabulary must be settled with the later `ARC-002`/maintainability synthesis; this finding owns transport selection and operator truthfulness, not stage-state mechanics. ## Candidate Register The `TST` entries now carry their Stage 12 classifications. Structural entries retain their original locations for traceability, but their Stage 11 classifications are also final for this investigative pass: confirmed recommendations remain for Stage 13 prioritization, merged entries defer to the named stronger root, and rejected entries require new evidence before reopening. ### `ARC-001`: `IODecl` is not a complete or consistently classified stage contract - Category: architectural boundary/ownership candidate. - Stage 11 classification: confirmed. Remove `Declares` and `IODecl` from the runtime `Stage` interface and implementations rather than expanding an unused partial model. Focused stage documentation already owns the complete dynamic contract. If a future planner needs machine-readable contracts, introduce a purpose-built model for that consumer rather than treating today’s static path hints as authoritative. - Evidence: `prepare.Declares` lists files it produces under `Inputs`; `analyze.Declares` omits dynamic input families and has no outputs; `publish.Declares` exposes only the manifest; and `notify.Declares` advertises placeholder paths although its result has no persisted output. Stage 8 also found unconditional optional report/bounds declarations, configured output paths that can differ from the static declaration, and render outputs declared even when disabled render succeeds with none. Stage 9 found extract's row is directionally accurate but necessarily uses wildcard/run placeholders, cannot declare the configured lane source IDs or contracts, and says nothing about the nonselectable index, diagnostics, adapter, fingerprint, or self-skip lifecycle. No production caller of `Declares` was found. - Contract tension: architecture says every stage declares required inputs, produced output state, configuration, adapters, lifecycle, and failure behavior; the Go interface declares only partial static artifacts. - Realistic risk: a future planner, validator, or operator view could treat the interface as authoritative and make incorrect dependency or readiness decisions. Current likelihood appears low because the method has no production caller. - Review history: Stages 8-10 completed ordinary, extraction, and analyze rows; the final removal decision follows from the absent production consumer and the model’s inability to describe those established contracts. ### `ARC-002`: disabled-stage “skip” terminology spans two different durable outcomes - Category: architectural/lifecycle ownership candidate. - Stage 11 classification: merged into the documentation root `COM-002`. Production behavior is coherent: `StageDispositionSkipped` means an executed self-skip, while zero-disposition no-output results are ordinary success. A new lifecycle abstraction or state change would obscure rather than repair that distinction; the remaining defect is inconsistent language. - Evidence: production use of `StageDispositionSkipped` was found only in extraction. Disabled render, absent/no-op analyze, and disabled publish return zero-disposition results with skip metadata, which the runner treats as success. Focused and operator docs use “skip” for several of these cases, while manifest docs reserve self-skip for a durable skipped state. - Realistic risk: maintainers or operator features may assume all disabled outcomes clear state, are reconsidered, and invalidate downstream work in the same way. Conversely, changing them to explicit self-skip could break valid pipeline continuation or cleanup semantics. - Review history: Stage 2 confirmed the runner truth table. Stage 4 confirmed publish's ordinary-success behavior is used deliberately by the cleanup gate, while `COM-002` owns its incorrect “self-skip” documentation. Stage 8 confirmed disabled trim is real successful copy processing and disabled render is deliberately successful with no output; `COM-004` owns render wording. Stage 10 confirmed absent/no-executable analyze is likewise successful with no output so the pipeline can continue and publish's prerequisite can be satisfied; broadened `COM-002` now owns the shared lifecycle vocabulary without changing those behaviors. ### `ARC-003`: committed and local manifests give `current_pointer_written` different meanings - Category: architectural boundary/ownership candidate. - Stage 11 classification: confirmed. The remote immutable snapshot should not serialize a postcommit assertion before commit. Prefer deriving commitment from the loaded pointer, or represent precommit publish metadata separately from local postcommit execution metadata. Never repair this with a post-pointer overwrite, which would break pointer-last atomicity. - Evidence: publish must serialize `current/manifest.json` before the commit marker, so `publishMetadataPreview` records `current_pointer_written=false`. After pointer success, the local session and invocation results record the same field as true. Current-state readers use the actual pointer and ignore the remote field; automatic cleanup uses the local true value. Existing behavior is therefore safe for current consumers. - Realistic risk: a future status, restore, reconciliation, or cleanup feature may treat the committed remote manifest's field literally and report a valid commit as incomplete, while another consumer interprets the local copy as an assertion about remote state. Updating the fixed manifest after pointer would instead violate pointer-last ordering. - Remediation boundary: use distinct precommit/local representation, omit the field remotely, or derive it from loaded pointer identity. Any implementation must preserve `COR-004`’s pointer-last protocol; do not add a post-pointer upload. ### `ARC-005`: Audita request and constructor both advertise ownership of static settings - Category: architectural boundary/ownership candidate. - Stage 11 classification: confirmed. Constructor state should own base URL, model, transcript description, config/schema paths, retention, and concurrency. `PolishRequest` should retain invocation paths and the genuinely per-run module override only. This matches the production runner and removes fake-only apparent overrides. - Evidence: `PolishRequest` and its integration document carry base URL, model, transcript description, config path, output schema, work-dir retention, and concurrency values. `SubprocessRunner.Run` ignores those request fields and builds arguments, generated config, and metadata from constructor state; only request `Modules` can override the configured list. The polish stage currently copies the same config values into both places, so production behavior agrees by convention. The fake captures the request and does not reveal that the real runner ignores most of it. - Realistic risk: a caller or focused stage test supplies a per-request setting and observes it in the fake, while the production runner silently uses its older constructor value. Future changes may update one representation only and make provenance disagree with the apparent request contract. - Review history: Stage 8 found no polish use case for per-invocation overrides and recommended constructor authority for static settings. Stage 12 should align the fake with the chosen contract. Do not merge genuinely request-specific paths/modules into static runner construction. ### `ARC-006`: stage authority for adapter-returned output paths is inconsistent - Category: architectural boundary/ownership candidate. - Stage 11 classification: confirmed. The stage-requested run-local destination is authoritative. Adapters should either return no path or return exactly the requested path, and stages should validate that identity before consuming the file. A redirect contract is not justified by any current adapter and would expand filesystem authority across the isolation boundary. - Evidence: transcribe requires the adapter result path to equal its requested run-local destination and then validates the requested path. Transformation stages all request run-local paths, but merge, polish, normalize, render, and Scriptorium bounds/render-debug prefer non-empty returned paths, while the Seriatim trim branch validates the requested destination. Current Audita, Seriatim, and Scriptorium production adapters return the requested path, so production agrees by convention rather than an explicit shared rule. - Contract tension: stages own run-local isolation and canonical materialization, while adapters own protocol execution. Allowing an adapter to redirect output gives it filesystem-placement authority that the request appears to reserve to the stage; ignoring a returned path makes that result field misleading. - Realistic risk: a future adapter version or fake returns a valid stale, canonical, or outside-work path. Depending on the stage, Narratio may consume it, ignore it, or reject it, making tests and isolation guarantees disagree. - Test boundary: Stage 12 should add one requested-path contract test across affected fakes/adapters rather than duplicating every stage case. ### `ARC-007`: enabled and selected artifact authority is split across boundaries - Category: architectural boundary/ownership candidate. - Stage 11 classification: confirmed. Preserve the CLI and catalog’s tested rule that an explicit selection is a one-invocation execution override; when absent, `enabled` defines the default set. Compute a typed effective artifact set before validation, prerequisite collection, catalog composition, and planning, and validate selected disabled definitions as fully executable. Publish keeps its distinct role as a filter over configured output rules. - Evidence: catalog registration makes `enabled` authoritative only when no explicit selection exists; a non-empty selection replaces it, and a unit test requires a selected disabled artifact to become executable. CLI validation accepts that choice. Configuration requires prompt/output fields and checks cycles only for enabled artifacts, previous-requirement collection scans only enabled artifacts, while `docs/internal/artifacts.md` defines executable as both selected and enabled. Publish uses selection as a filter over configured rules but availability ignores enabled state. - Contract tension: `enabled` can mean default execution, complete executable configuration, prerequisite participation, or publication availability, depending on the boundary. `selected` can mean an override or a filter. No single effective-artifact-set owner states which interpretation is canonical. - Realistic risk: `COR-022` is the concrete prior-input failure. A selected disabled artifact can also reach runtime with fields that configuration did not require, while an enabled-but-unselected artifact is reported with disabled-output provenance. Future validation/publish changes can widen the disagreement. - Remediation boundary: compute the one-off override’s typed effective set before configuration-dependent composition/planning and validate selected entries as executable. Preserve publish's documented rule that built-in and extraction sources are unaffected, and do not overload availability with executability. ### `TST-001`: full race baseline fails in the concurrent transcribe test - Category: test-suite execution candidate. - Evidence: the race detector reported concurrent slice access in `internal/adapters/whisperx/fake.go:45` from transcribe workers in `TestTranscribeStageTranscribesPreparedAudio`. - Observed impact: the canonical full race command exits nonzero, weakening its signal for other packages. The report currently points to a test fake, not a production data race. - Stage 7 refinement: the production HTTP client is stateless during requests and passes its focused race suite. The fake appends to `Requests` without synchronization, while the transcribe stage is contractually allowed to call the client concurrently. The defect is therefore in fake fidelity at the adapter/consumer seam, not evidence of a production HTTP-client race. - Stage 8 resolution: transcribe intentionally invokes the interface concurrently within a configured bound, and the required focused race command reproduces the fake's request-slice race. Worker cancellation has a separate correctness defect in `COR-018`; it does not make concurrent fake mutation valid. - Confirmation owner: Stage 12 should classify suite impact and the smallest durable fake fix. Do not change the fake during this investigative stage. - Stage 12 classification: confirmed. Protect request capture with the fake's own synchronization and expose a safe snapshot accessor. This restores the full race suite without changing legitimate production concurrency. ### `TST-002`: runner tests cannot exercise invocation-manifest save failures - Category: test-suite sufficiency candidate. - Evidence: `Env.ManifestStore` injects only session `Create`, `Load`, and `Save`; `executeStages` constructs a concrete `manifest.LocalStore` for run creation and every `SaveRun`. Focused tests cover normal and stage-failure transitions but no save disagreement row. The existing resume-validation error test also omits the surviving run status. - Realistic risk: future ordering or error-path changes can advertise a handled invocation as running, lose the audit half of a terminal transition, or weaken conservative retry behavior without an assembled test failing. - Confirmation owner: Stage 12 should decide the smallest persistence seam and representative boundary cases; avoid exhaustive choreography tests for every mechanically identical save call. - Stage 12 classification: confirmed. Inject the run-store/terminal-operation boundary and retain one session-first terminal save failure plus one resume-validation terminalization case. Repeating every `SaveRun` call order would test private choreography rather than a distinct defect. ### `DUP-001`: session save duplicates the shared atomic JSON writer - Category: duplicated mechanism candidate. - Stage 11 classification: confirmed shared mechanism. Both manifest models should serialize and normalize in their typed owner, then delegate atomic replacement to the same `fileops` capability while preserving the session save’s context checkpoint and caller-specific error prefix. Coordinate this with `DUP-005` and the directory-durability correction in `RSK-002`; do not create a manifest-generic persistence interface. - Evidence: `LocalStore.Save` contains its own temp/create/write/sync/close/ context-check/rename sequence, while `SaveRun` delegates the same mechanism to `writeJSONAtomically`. Error prefixes differ, but the durability mechanism is otherwise repeated. Stage 3 confirmed that both copies omit the same parent-directory sync required by `RSK-002`. - Realistic risk: a future durability, cleanup, permission, or platform fix may reach only one manifest type, creating different guarantees for the two ledgers. - Review history: Stage 3 established the shared filesystem guarantees; Stage 11 confirmed that caller error context can remain in wrappers over one mechanism. ### `DUP-002`: publish reconstructs the canonical run-manifest path - Category: duplicated path-ownership candidate. - Stage 11 classification: confirmed duplicated ownership. Publish should use the canonical artifacts path helper or receive the already-derived path. Keep the remote `manifest.json` spelling explicit as a publication protocol constant; only local canonical path construction moves to its existing owner. - Evidence: `internal/stage/publish.go` in `resolvePublishRunManifestSource` joins the literal `manifest.json` to an already-derived run root, while `internal/artifacts` owns `SessionRunManifestPath*` and the run-manifest path model. The same literal is repeated when constructing the upload-relative record. - Realistic risk: a layout/name change can update canonical path construction without updating publish discovery, causing a completed run to fail publish or upload the wrong record. Current values agree, so this is not a correctness defect at the audited revision. - Remediation boundary: publish may receive the canonical path or call the artifacts helper; preserve the explicit remote relative name separately as a protocol constant. ### `DUP-003`: sibling-temp download and install mechanics are repeated across restore and prepare - Category: duplicated mechanism candidate. - Stage 11 classification: confirmed shared mechanism. Add one narrow destination-confined sibling-temp acquisition/install capability, or compose a shared sibling-temp creator with the durable install primitive. Object selection, download validation, force/conflict decisions, cache records, manifest-last ordering, and reports remain in restore, audio, and prepare. - Evidence: restore's `downloadObjectToSiblingTemp` plus `InstallDownloadedTempFile`, audio's `downloadObjectAtomic`, and prepare's previous-cache loop each create a sibling directory/temp file, close it, download through `ObjectStore`, clean failure, and rename-install. The callers legitimately differ in conflict, cache, content-validation, input-record, and report policy. `storage.DownloadObjectToTemp` is a separate system-temp inspection primitive. All install variants inherit `RSK-002`'s durability requirements. - Realistic risk: a future sync, permission, cancellation-cleanup, no-follow, or download-size fix reaches restore but not prepare/audio, creating different guarantees for the same remote-to-canonical transition. Conversely, sharing the entire workflows would incorrectly merge caller policy. - Remediation boundary: leave validation, conflicts, cache, manifest-last, and reporting in their current owners. Coordinate with `DUP-001`/`RSK-002` rather than adding another incomplete atomic writer. ### `DUP-004`: configuration repeats lexical relative-path policy - Category: duplication/ownership candidate. - Stage 11 classification: rejected as a shared-mechanism extraction. Exact comparison shows config deliberately rejects every `..` segment, including `a/../b`, while `pathsafe.NormalizeRelativeDestination` accepts and cleans a non-escaping occurrence. Config also preserves field-specific errors. A parameterized shared validator would hide the stricter accepted language for only three small callers; keep this policy explicit and test its language. - Evidence: `internal/config.validateRelativeSafePath` independently checks absolute paths, cleaned dot/traversal forms, and separators already represented by `internal/pathsafe` primitives, while adding config-specific field/error context. This is not currently a behavior defect and identity/root validation has deliberately different rules. - Realistic risk: new artifact fields can be accepted by one validator and rejected by another, multiplying the path-policy drift behind `COR-002` and future artifact configuration work. - Rejection boundary: do not collapse identifier, filesystem-root, artifact relationship, or strict configuration path language into one generic validator. ### `DUP-005`: adapter packages repeat atomic byte-write mechanics - Category: duplicated mechanism candidate. - Stage 11 classification: confirmed shared mechanism and the preferred owner for the `DUP-001` family. Consolidate byte replacement in `fileops`, including permission, cleanup, destination safety, and directory-sync guarantees; callers wrap errors with protocol context. YAML serialization, context/state policy, and output validation remain outside the low-level primitive. - Evidence: `internal/adapters/subprocess.WriteFileAtomic` and WhisperX's unexported `writeFileAtomic` independently implement same-directory temp-file creation, write, file sync, close, chmod, rename, and failure cleanup. Fakes and generated-YAML writers use the subprocess copy. `internal/fileops` owns a third equivalent mechanism for application files. All three omit the directory sync identified by `RSK-002`. - Realistic risk: the durability or symlink-safe destination repair reaches the canonical fileops path but leaves adapter outputs/configuration with weaker guarantees, or adapter copies drift in permissions and cleanup behavior. - Remediation boundary: preserve caller context/error wrapping and keep YAML serialization and protocol output validation local. Coordinate with `DUP-001`, `DUP-003`, and `RSK-002` rather than creating another utilities package. ### `DUP-006`: singleton transcript stages repeat manifest-first resolution policy - Category: duplication/ownership candidate. - Stage 11 classification: confirmed duplicated policy. Route the three singleton source wrappers through typed `artifacts.ResolveSessionArtifact` identities, retaining only stage-specific provenance/guidance adapters. Preserve the existing content validation and keep plural raw-transcript discovery separate. This also removes current drift over whether a missing manifest candidate is returned or treated as unavailable. - Evidence: `discoverMergedTranscript`, `discoverProcessedTranscript`, and `discoverNormalizedTranscript` each scan one producer's manifest outputs, trim/resolve candidate local paths, deduplicate/sort, stat candidates, select one, and fall back to a canonical transcript path. Their output kinds, producer records, fallbacks, and ambiguity messages differ, but the policy skeleton is nearly identical. The artifact registry/resolver already owns a manifest-first/canonical-fallback abstraction used by render and later stages. - Realistic risk: restored-path handling, content validation, ambiguity, or provenance changes are applied to the registry resolver and one discovery helper but not the others, producing stage-specific source selection drift. - Remediation boundary: retain explicit stage wrappers only for provenance and guidance over `artifacts.ResolveSessionArtifact`. Keep plural raw-directory discovery separate and do not create a generic stage framework. ### `DUP-007`: extraction resume and catalog hydration duplicate bundle-evidence policy - Category: duplication/ownership candidate. - Stage 11 classification: confirmed duplicated policy. `artifacts` should own one typed extraction-bundle evidence proof whose result distinguishes absent, obsolete, unsafe, and valid evidence. Resume maps that proof to rerun/error lifecycle decisions; catalog hydration keeps its all-or-none fail-closed mapping. Confinement-before-read and exact identity/checksum ordering remain visible named proof steps, not a generic validation framework. - Evidence: `extractStage.ValidateResume` and `ArtifactCatalog.HydrateExtractionArtifacts` independently reconstruct the producer bundle, receipt identity, exact configured source/index set, contracts, Notarius provenance, confinement and symlink rules, regular-file shape, and checksums. They have separate segment, metadata, receipt, contract/provenance, component, and payload helpers. Some difference is intentional: resume distinguishes obsolete evidence from unsafe errors and can rely on a prior JSON-valid checksum, while catalog treats the record as untrusted and fails closed without returning errors. - Realistic risk: a new identity field such as the direct input digest required by `COR-020`, a contract compatibility change, or a path-safety repair is enforced by resume but not catalog (or vice versa). Extraction can then be skipped as reusable while its consumers refuse it, or a downstream catalog can expose evidence resume would reject. - Remediation boundary: the `internal/artifacts` evidence validator must accept explicit current definitions and return typed evidence reasons; resume should map missing/obsolete versus unsafe reasons, while the catalog retains all-or-none fail-closed hydration. Preserve canonical bundle identity, root confinement before reads, exact index/source count, contract/provenance checks, no-follow regular files, checksums, and catalog's JSON validity. Do not move lifecycle decisions into `fileops`. ### `DUP-008`: runtime catalog bootstrap policy is repeated across three consumers - Category: duplication/ownership candidate. - Stage 11 classification: confirmed duplicated bootstrap policy. Extract one deterministic definition-registration function for built-ins, configured definitions, and extraction definitions/evidence. Analyze, publish, and operator helpers then apply their own explicit executable selection, disk availability, publication filtering, and rendering policies. - Evidence: `buildAnalyzeRuntimeArtifactCatalog`, `buildPublishRuntimeArtifactCatalog`, and `buildHelperArtifactCatalog` each create a catalog, register built-ins, translate Scriptorium configuration to configured definitions, register extraction definitions, and conditionally hydrate extraction evidence. Analyze and publish additionally repeat local configured-output path resolution/availability loops. Their final policies intentionally differ: analyze applies executable selection and reuses only non-executable outputs; publish needs every existing configured source before applying output selection; operator helpers primarily render identities. - Realistic risk: a new built-in/source family, extraction evidence rule, or configured-definition field is registered in one consumer and omitted from another. Analyze can then accept a source that publish/status cannot render, or publication can expose availability analyze classifies differently. - Remediation boundary: extract only common deterministic registration/definition bootstrap, returning a catalog that callers enrich with explicit availability/executability policy. Coordinate extraction proof mechanics with `DUP-007` but do not collapse resume, analyze reuse, publish selection, and operator rendering into one mode-heavy builder. ### `TST-003`: filesystem safety tests omit destination and lock-recovery boundaries - Category: test-suite sufficiency candidate. - Evidence: focused path/file tests cover lexical traversal, mixed slashes, ordinary atomic overwrite/cleanup, promotion source symlinks and replacement, no-replace installation, and basic lock conflict/release. No test covers an unsafe identity component, symlinked destination ancestor, destination-parent replacement, cleanup through an ancestor symlink, file/directory sync order, stale lock recovery, lock release failure, or concurrent assembled runners. - Realistic risk: the confirmed `COR-002`, `COR-003`, `RSK-002`, and `RSK-003` mechanisms can regress or be only partially repaired while a broad focused suite remains green. - Confirmation owner: Stage 12 should select one narrow behavior-level case per distinct invariant and reuse shared low-level tests across callers. Avoid duplicating every path spelling or persistence call sequence. - Stage 12 classification: confirmed and narrowed. Put unsafe identity and symlink-ancestor cases at their low-level policy owners, retain one destructive and one writer composition case, add a durability interaction case at the injectable filesystem capability, and add stale/release failure plus one concurrent assembled runner case. Do not repeat the same ancestry table at every caller. ### `TST-004`: publish protocol tests do not preserve prior current state or exercise recovery - Category: test-suite sufficiency candidate. - Evidence: focused publish tests strongly cover successful upload contents, pointer-last order, and absence of a pointer call after output/current- manifest failures. Cleanup tests cover ordinary commit metadata and path effects. They do not seed a prior pointer/manifest pair, inspect readability between the last two calls, retry a partial publish, model an upload accepted with an error response, reload workspace-cleanup metadata, retry cleanup, use symlink archive entries, or coordinate concurrent lock mutations. - Realistic risk: tests can continue proving “pointer was not advanced” while a failed attempt has already made the prior commit unreadable (`COR-004`), or can prove directories disappeared without detecting lost cleanup evidence and retry obligations (`COR-006`/`COR-007`). The same suite would not prevent partial fixes to `COR-005` or `RSK-005`. - Confirmation owner: Stage 12 should add the smallest stateful publish fake with barriers/version semantics and one behavior-level case per distinct invariant. Prefer extending current order/cleanup fixtures over duplicating all source-family tables, which already have good focused coverage. - Stage 12 classification: confirmed. One versioned object-store fake should prove prior-pair readability across the manifest-before-pointer window and failure, accepted-with-error ambiguity, retry, cleanup evidence durability, and concurrent mutation. Existing source-family tables remain the stronger protection for selection and payload composition. ### `TST-005`: restore tests omit committed-snapshot and partial-transition invariants - Category: test-suite sufficiency candidate. - Evidence: the restore suite has strong happy-path, lexical traversal, ordinary conflict/force, typed missing state, cache, previous-cache, report, and workflow coverage. Run mismatch is tested only on the shared helper with validation enabled. No assembled case covers restore/status mismatch, uncommitted/stale prefix objects, remote generation changes, forced directory conflict, plan-before-lock changes, partial forced overwrite followed by a runner, same-size audio replacement, foreign absolute output paths, custom publish destinations, or optional readiness. Failure coverage centers on an invalid second manifest and lock conflict rather than every distinct durable boundary. - Realistic risk: `COR-008` through `COR-011` and `RSK-006` through `RSK-008` can remain or be partially repaired while broad restore tests stay green; several current tests explicitly encode size-only audio and prefix-wide scope without tying those choices to the stronger authority invariants. - Confirmation owner: Stage 12 should add one stateful package-level behavior case per root risk, reuse the publish fake/version barriers from `TST-004`, and consolidate caller rendering matrices. Avoid a mock for every mechanically identical download boundary; retain the existing manifest-invalid case as the representative pre-rename validation failure. - Stage 12 classification: confirmed. Reuse the stateful store and barriers from `TST-004` for one package-level case per committed-scope, generation, conflict/partial-transition, identity, and recovery root. Keep the existing manifest-invalid case; do not mock every download. ### `TST-006`: injected runner environments can split configuration authority - Category: test-seam fidelity candidate. - Evidence: `executeStages` receives both a resolved `cfg` argument and an optional injected `stage.Env`. It assigns `env.Config = cfg` only when the injected field is nil. With different non-nil values, layout, selected artifacts, and default stores derive from `cfg`, while secrets, adapter defaults, locks, and stage execution consume `env.Config`. Production passes no injected environment, and current tests generally use the same pointer by convention rather than an enforced invariant. - Realistic risk: a unit test can pass while exercising different stage enablement, credentials, paths, or timeouts from the manifest/layout under test, obscuring a production composition regression or creating an impossible fixture. - Confirmation owner: Stage 12 should inventory intentional divergent fixtures and either reject differing configs, always rebind to the resolved config, or replace the dual input with a constructor that makes ownership singular. - Stage 12 classification: confirmed. No intentional divergent fixture was found. One app composition test should prove that injected collaborators cannot introduce a second configuration authority; production should always bind the environment to the resolved configuration. ### `TST-007`: adapter tests omit adversarial liveness and output-trust boundaries - Category: test-suite sufficiency candidate. - Evidence: focused adapter tests cover successful invocations, direct-child timeout, override-value tail redaction, HTTP retry/status/cancellation and malformed JSON, ordinary S3 not-found, and normal/invalid subprocess outputs. They do not cover descendant termination, inherited-secret or on-disk log redaction, non-HTTP URL schemes, streaming/cancelable request production, symlink/non-regular/oversized outputs, response-body close observation, or valid and non-progressing multi-page S3 responses. Fakes generally append requests without synchronization and materialize valid placeholder outputs; only the WhisperX fake is currently called concurrently in production-shaped tests. - Realistic risk: fixes for `COR-016`, `RSK-011` through `RSK-014`, and `EFF-002` can be incomplete while broad adapter coverage remains green, and a fake can make a stage appear to validate output that the real adapter would reject or race under the real concurrency contract. - Confirmation owner: Stage 12 should add the smallest boundary test per root risk, share process/file/HTTP fixtures where the behavior is mechanical, and retain protocol-specific argument/schema assertions. It should classify automatic fake materialization by the stage risks it protects rather than mechanically rewriting every fake. - Stage 12 classification: confirmed. Add one focused case per process-tree, inherited/raw secret, URL scheme, streaming cancellation, bounded regular output, response closure, and pagination-progress root. Retain exact protocol argument/schema assertions; they are observable contracts, not brittle mock choreography. ### `TST-008`: ordinary-stage tests omit transition, cancellation, and output-authority boundaries - Category: test-suite sufficiency candidate. - Evidence: focused prepare and transcript-stage tests strongly cover ordinary sources, manifest-first fallback, deterministic concurrency/order, adapter errors, invalid schemas/reports/bounds, disabled behavior, diagnostics, run-local paths, and canonical materialization. They do not cover removing the final previous requirement, repeated explicit audio, pre-canceled or mid-dispatch transcription, a valid alternate adapter-returned output path, or failure while materializing the second of multiple validated outputs. One prepare test actively requires stale previous state to survive. - Realistic risk: `COR-017` through `COR-019`, `ARC-006`, and the stage-side reach of `RSK-013` can remain or receive partial fixes while broad normal stage tests pass. The existing race command is also unusable until `TST-001` is repaired. - Confirmation owner: Stage 12 should add behavior tests at the narrowest owner: one prepare transition, duplicate-input validation, barrier-controlled transcribe cancellation, and one cross-adapter output-authority contract. Reuse shared safe-output acquisition tests from `TST-007`; do not duplicate every schema error or stage fixture already covered. - Stage 12 classification: confirmed. Add exactly those transition, duplicate-input, barrier-cancellation, requested-path, and representative multi-output partial-materialization cases. Safe output shape/size stays at the shared owner from `TST-007`; existing schema tables remain sufficient. ### `TST-009`: extraction tests omit direct-input identity and assembled late-failure boundaries - Category: test-suite sufficiency candidate. - Evidence: focused extraction coverage is strong for process/receipt/index validation, required lanes, immediate and cross-invocation reuse, configuration-value change, missing/tampered outputs, source/contract/ provenance mismatches, disabled/forced/failed lifecycle outcomes, explicit consumers, and low-level promotion source replacement/no-replace behavior. No case mutates the resolved trimmed transcript beneath a succeeded extract record, so `COR-020` is invisible. Stage tests stop at an injected promotion error and do not exercise a failure after the durable directory has installed; resume has one outside-root error but no assembled ancestor/root replacement case. Configured-lane size is also untested at the acquisition boundary. - Realistic risk: the direct-input repair, orphan-bundle policy, unsafe-versus- obsolete mapping, or `RSK-013` lane bound can be incomplete while all current extraction/lifecycle tests pass. Conversely, copying every adversarial `fileops` case upward would add redundant suite friction. - Confirmation owner: Stage 12 should add the direct-input lifecycle regression, one deterministic post-install failure/residue assertion if a seam can expose it without private choreography, and one representative assembled unsafe-root case. Put the size case at the shared bounded-reader owner. Reuse the existing downstream invalidation and catalog fixtures rather than creating a broad new end-to-end framework; coordinate manifest-persistence faults with `TST-002` and destination ancestry with `TST-003`/`COR-003`. - Stage 12 classification: confirmed with one rejection. Add the direct-input lifecycle regression and one assembled unsafe-root case; keep lane size at the shared bounded reader. Reject a dedicated post-install orphan-residue assertion because an unadvertised orphan is not authority and exposing the point would test private choreography. Persistence and ancestry remain owned by `TST-002` and `TST-003`. ### `TST-010`: analyze tests omit effective-selection and optional built-in boundaries - Category: test-suite sufficiency candidate. - Evidence: analyze, catalog, artifact-policy, configuration, previous- requirement, publish, and command tests strongly cover ordinary source success, required/optional absence for four source families, generated/reused dependencies, successful lexical order, cycles, unavailable dependencies, selection propagation, local-only previous resolution, and publish filtering. They do not cover missing optional normalized/trimmed/Markdown built-ins, execution of a selected disabled artifact, prerequisite planning for that effective selection, multiple simultaneous dependency errors, or any behavioral use/rejection of accepted input `artifact`/`path` fields. The previous-guidance test asserts only the malformed command fragment. - Realistic risk: fixes for `COR-021` through `COR-024`, `RSK-015`, and `ARC-007` can be partial while each narrow package suite stays green. A broad end-to-end matrix would duplicate strong catalog/source tests and obscure which boundary owns a failure. - Confirmation owner: Stage 12 should add a built-in required/optional table at analyze, one assembled selected-disabled previous-requirement workflow at app or prepare/analyze composition, one deterministic multi-error dependency case, exact actionable guidance, and either adapter propagation or strict rejection for the passthrough fields after their contract is chosen. Reuse existing fixtures for publish/source success and do not retest every artifact-policy spelling. - Stage 12 classification: confirmed. Add the optional-built-in table, one selected-disabled assembled workflow, deterministic simultaneous-error ordering, semantic actionable guidance, and the chosen passthrough behavior. Existing source-family and publish tables remain the stronger protection and should not be repeated end to end. ### `TST-011`: filesystem-secret test leaks process environment across repetitions - Category: test-suite determinism defect. - Stage 12 classification: confirmed by isolated reproduction. - Evidence: `TestLoadSecretsFromConfigLoadsValidFiles` invokes the production loader, which sets `NARRATIO_TEST_SECRET_A` and `NARRATIO_TEST_SECRET_B`, but the test does not restore their prior state. `go test -shuffle=on -count=3 ./...` failed with seed `1786373771816345415`; an isolated same-seed, three-count invocation failed on its second and third repetitions because the variables were already present and correctly reported as preserved rather than loaded. - Realistic defect and marginal value: leaked secrets make the package depend on repetition/order and can conceal whether later cases loaded or preserved values. Restore the exact prior environment state in cleanup, including unset-versus-empty semantics. One cleanup repair protects the behavior; no production change or broad environment fixture is warranted. ### `TST-012`: automation does not enforce repository validation before release - Category: test-suite execution/automation risk. - Stage 12 classification: confirmed. - Evidence: `.woodpecker/release.yml` is tag-only and cross-builds/publishes binaries. No repository automation runs `go test ./...`, `go vet ./...`, or a normal build for pushes or proposed changes, and release publication is not tied to a revision that passed those checks. - Realistic defect and marginal value: a change or tagged revision can ship despite breaking tests, static analysis, or a supported build. Add ordinary test/vet/build validation and make release consume or repeat the validated revision. Add race execution after `TST-001` is repaired at a frequency justified by its roughly 54-second cost; reserve repeated shuffle runs for a scheduled/audit diagnostic unless CI capacity supports them. ### `TST-013`: security-sensitive path and source parsers have no property fuzzing - Category: test-suite sufficiency opportunity. - Stage 12 classification: confirmed narrow addition. - Evidence: no fuzz target exists. Deterministic tables cover known separators, traversal spellings, source families, and remote/local mappings, but the `pathsafe` and `artifactpolicy` boundaries accept attacker- or configuration-controlled strings whose combinations are larger than those tables. - Realistic defect and marginal value: an unanticipated separator, normalization sequence, or source token can escape a root, classify inconsistently, or break a round trip. Seed the existing tables and assert no panic, no root escape, normalization stability, and valid mapping round trips. Do not blanket-fuzz standard YAML/JSON decoders or every typed manifest wrapper; their Narratio-specific schema cases already have stronger tables. ### `TST-014`: configuration tests conflate loader, defaults, and validator ownership - Category: test-suite redundancy and maintainability issue. - Stage 12 classification: confirmed consolidation. - Evidence: `config.TestLoadAndValidate` is roughly 949 lines with a large table of repeated complete YAML documents followed by broad normalized/default assertions and validator checks. It already uses semantic error substrings, so exact prose is not the defect; fixture breadth and mixed ownership are. - Realistic defect and marginal value: changing an unrelated required/default field forces many validation fixtures to change and a failure does not identify whether strict decoding, defaulting, or domain validation owns the contract. Retain small strict-load cases, construct typed configurations for validator tables, and keep one representative load/default/validate assembly case. Those focused owners are the stronger remaining protection; do not introduce a generic fixture framework. ### `TST-015`: assembled stage matrices duplicate focused behavior owners - Category: test-suite redundancy and brittleness issue. - Stage 12 classification: confirmed consolidation. - Evidence: `TestStagesReturnExpectedMetadata` is a broad per-stage metadata checklist that overlaps focused stage suites and incidental metadata, while `TestAdapterBackedStageFailureMarksManifestFailed` repeats six adapters to prove the runner's generic terminal-failure mapping. Similar broad success assertions exist in `TestExecuteStagesPlaceholderSuccessUpdatesManifest`. - Realistic defect and marginal value: a harmless metadata/default change causes several layers to fail while real stage defects remain better diagnosed by focused owners. Retain focused stage/adapter protocol and behavior tests, one representative assembled success proving composition and durable manifests, and one representative adapter failure proving generic runner terminalization. Delete the per-stage checklist and repeated generic failure rows only after those stronger protections are explicit. ### `SIM-001`: runner terminalization and persistence ordering lack a narrow owner - Category: simplification candidate. - Stage 11 classification: confirmed narrow simplification. Introduce a typed terminal-failure operation that updates session authority first, then the run audit ledger, and compounds persistence errors without hiding the stage failure. Keep the running transition separate and visibly run-first, and route resume-validation failure through terminalization. Do not extract a generic lifecycle state machine. - Evidence: `executeStages` is 274 lines with cyclomatic complexity 54 and cognitive complexity 96. Much of the length is justified visible state- machine ordering, but session-first terminal save, run-first running save, result mapping, identity sync, and compound error handling are repeated inline. The resume-validation return bypasses run terminalization. - Realistic risk: adding another pre-stage or terminal error path can repeat the abandoned-run behavior in `RSK-001`; extracting too broadly could instead hide the critical order. - Remediation boundary: use only the typed terminal failure operation described above; Stages 3-10 established that broader transition extraction would hide required ordering. ### `SIM-002`: extraction resume validation conflates distinct evidence decisions - Category: simplification candidate. - Stage 11 classification: merged into `DUP-007`. The shared typed evidence proof is the smaller control flow: named ordered proof steps reduce resume complexity while leaving lifecycle mapping in `ValidateResume`. A second abstraction would duplicate that owner. - Evidence: `extractStage.ValidateResume` spans 154 lines with cyclomatic complexity 40 and cognitive complexity 53. It validates environment and record state, producer identity, parsed/resolved configuration and fingerprint, session layout, canonical bundle and symlink safety, receipt, exact output set, contracts/provenance, and payload evidence in one function. The branches are individually necessary, but the proof phases and the reason each branch is obsolete versus unsafe are difficult to review as a whole. - Realistic risk: adding `COR-020`'s input evidence or sharing catalog mechanics can place an I/O check before confinement, accidentally turn an unsafe state into an automatic replacement, or omit one exact-set/identity check. - Merge boundary: `DUP-007` should extract only named evidence decisions or a small typed proof result. Preserve visible ordering: cheap manifest/producer and fingerprint rejection; canonical root derivation and component confinement before filesystem access; receipt and exact index/source identity; contract/provenance; then regular-file/checksum evidence and one final completeness decision. Preserve every existing non-resumable outcome and every unsafe error, and keep `ValidateResume` as the lifecycle mapper rather than introducing a generic validation framework. ### `SIM-003`: analyze execution passes a wide context through repeated resolution branches - Category: simplification candidate. - Stage 11 classification: confirmed narrow simplification. Build a typed analyze execution context containing immutable session/run paths, diagnostics, manifest, catalog, and an indexed effective plan. Have each explicit source branch return a typed resolution result containing path, provenance, optional absence, or unavailability. Remove unused parameters and the uncalled helper; retain distinct policy/guidance and visible dependency/output commit order. - Evidence: `executeAnalyzeArtifact` spans 239 lines and accepts ten parameters for environment, manifest, session paths/identity, run layout, plan, transcript diagnostics, and mutable catalog state. `resolveScriptoriumInput` adds six parameters and 84 lines/cognitive complexity 71; `inputName` and `sessionDir` are unused there. It interleaves family dispatch, availability, required/optional decisions, previous-cache provenance, and operator guidance. `orderSelectedScriptoriumArtifacts` separately rebuilds selected sets and graph indexes. `resolveInputPathForRead` has no caller. - Realistic risk: another source family or guidance rule can bypass optional semantics as in `COR-021`, while new per-run context widens signatures and makes test fixtures construct states that production never uses. A generic resolver abstraction would create the opposite risk by erasing intentionally different source policies. - Remediation boundary: use a small typed analyze execution context, an indexed effective plan, and a resolution result carrying path, optional absence, provenance, and typed unavailability. Keep policy dispatch explicit and source-specific guidance close to its producer. Remove the dead helper and unused parameters if confirmed; do not create a generic stage or graph framework, and coordinate set authority with `ARC-007`. ### `SIM-004`: three private path-resolution helpers are dead - Category: confirmed dead-code simplification. - Evidence: production-only zero-inbound graph search followed by exact text search found `stage.resolveInputPathForRead`, `stage.resolvePublishSessionRoot`, and `app.resolveSessionConfigPath` only at their definitions. Their active replacements are source-family resolution, the publish session-path model, and `resolveSessionConfigPathWithCandidates`. No interface, reflection, registration, test, or command path refers to them. - Stage 11 classification: confirmed. Delete the three helpers when structural remediation begins. Do not preserve wrappers for hypothetical callers inside `internal` packages; their distinct fallback behavior otherwise suggests authority that production does not use. - Realistic risk and scope: low direct risk and tiny deletion scope, but the unused fallbacks distract reviews of already-sensitive path authority and made the analyze execution surface appear broader than it is. Existing focused stage/app tests are sufficient after deletion. ### `COM-001`: dual-ledger save order and partial-failure policy lack rationale - Category: comment/clarity candidate. - Stage 11 classification: confirmed. After `SIM-001`, preserve one concise why-comment at the transition owner: session state is resume authority, the run manifest is the invocation audit ledger, running must be durable before execution, and terminal disagreement must remain visible. A comment that merely restates save order is insufficient. - Evidence: the runner visibly saves run-before-session for `running` and session-before-run for terminal outcomes, but no local comment explains which file is authoritative, why the order differs, or how partial state is meant to be interpreted. - Realistic risk: a maintainer may make the calls symmetrical or reorder them, inadvertently allowing a stage to execute without a durable running session transition or preferring an audit record over resume authority. - Remediation boundary: prefer a named operation that makes the invariant obvious; add a concise rationale only where code structure cannot. ### `COM-002`: successful no-output stages are documented as skips - Category: comment/clarity candidate. - Stage 11 classification: confirmed and broadened to absorb `ARC-002` and `COM-004`. Publish, disabled render, and absent/no-executable analyze complete successfully with no outputs; they are not durable self-skips and are reused on ordinary reruns until forced. Documentation should reserve “self-skip” for explicit `StageDispositionSkipped`, explain reconsideration, and describe each no-output success directly. - Evidence: `docs/internal/stage-publish.md` says disabled publish or run upload can “self-skip.” The implementation returns zero disposition with skip metadata, so both manifests record succeeded and later normal runs reuse that success. True self-skip is a durable skipped outcome and is reconsidered by the runner. Focused tests check returned metadata but do not make the durable distinction obvious at the stage-document boundary. - Realistic risk: an operator or maintainer can expect enabling publish and rerunning normally to reconsider a skipped stage, or can change it to explicit self-skip and unintentionally alter downstream/cleanup behavior. The `publish` command currently forces execution, which mitigates the common explicit operator workflow but not the terminology. - Remediation boundary: state the durable outcome and reconsideration behavior precisely; no production lifecycle change is indicated. ### `COM-003`: Audita's adapter contract still says its real adapter is unimplemented - Category: stale comment candidate. - Stage 11 classification: confirmed stale comment. Remove the TODO. If `ARC-005` leaves a non-obvious constructor/request boundary, replace it with a rationale comment stating that constructor configuration is static and the request carries invocation data; do not restate argument construction. - Evidence: `internal/adapters/audita/runner.go` carries a TODO to implement a real subprocess/service adapter, while `SubprocessRunner` is production composed, documented, and covered by extensive invocation tests. - Realistic risk: a maintainer treats the adapter as placeholder-only, bypasses the existing implementation, or leaves genuinely missing notification work confused with already completed Audita work. - Remediation boundary: remove the stale TODO and, only if useful, replace it with rationale about static constructor settings versus invocation request data. No production behavior change is needed. ### `COM-004`: render documentation calls a durable success a skip - Category: comment/clarity candidate. - Stage 11 classification: merged into broadened `COM-002`; it is the same lifecycle-language defect and needs no separate implementation or comment. - Evidence: `docs/internal/stage-render.md` says disabled render “skips with stage metadata.” The implementation returns a zero-disposition result, so the runner records succeeded with no outputs and an ordinary later invocation reuses that success. Enabling render after that outcome requires force under the documented global lifecycle. This is intentionally different from extraction's explicit self-skip and from trim's successful copy output. - Realistic risk: an operator expects enablement to be automatically reconsidered, or a maintainer changes render to `StageDispositionSkipped` and unintentionally alters downstream lifecycle behavior. - Merge boundary: broadened `COM-002` describes successful no-output execution and the force consequence directly; no production behavior change is indicated by Stage 8. ### `COM-005`: analyze documentation omits one source family and durable no-op consequences - Category: documentation/clarity candidate. - Stage 11 classification: confirmed in part and merged in part. Add extraction to the source/catalog descriptions and align executable-set wording with `ARC-007`. The successful no-output/force consequence is owned by broadened `COM-002`. Do not document the inert `artifact`/`path` fields as working passthrough until `COR-024` is repaired. - Evidence: `docs/internal/stage-analyze.md` lists built-in, prepared, configured, and previous-session sources but omits supported `narratio.extraction.` inputs. It says missing configuration/no executable artifacts “skips with metadata,” while the result has no skipped disposition: both manifests record succeeded, publish's prerequisite is satisfied, and ordinary later runs reuse that success until force. The same document says the runtime catalog contains only built-ins and configured artifacts despite extraction registration/hydration. Separately, `docs/internal/artifacts.md` says executable means selected and enabled even though current selection overrides enabled; that authority question remains `ARC-007` rather than a wording-only repair. - Realistic risk: operators omit usable extraction context or expect newly configured artifacts to run without force; maintainers can change no-op analyze into an explicit self-skip and break pipeline/publish behavior. The dedicated `analyze` command force-runs, which mitigates but does not correct the contract. - Remediation boundary: add extraction to catalog/source lists and use `ARC-007`’s effective-selection language. Broadened `COM-002` owns successful no-output persistence and force wording. Do not describe the inert `artifact`/`path` fields until `COR-024` chooses implement versus reject. ### `COM-006`: source-classification comment omits extraction sources - Category: confirmed stale comment. - Evidence: `artifactpolicy.ClassifySource` says it classifies built-in, configured, or previous-session configured IDs, but its production branch also recognizes `narratio.extraction.` and returns `SourceKindExtraction`. - Stage 11 classification: confirmed. Update the doc comment to name extraction sources, because the list documents a supported compatibility vocabulary. Keep the why-comments in `run_control` and the notification placeholder: the former accurately records the explicit force/freshness limitation, and the latter accurately identifies the missing transport already owned by `ARC-004`. - Realistic risk and scope: low implementation risk and one-line scope, but the stale exported comment can cause new consumers to duplicate classification or reject a supported source family. ## Candidate Classification Log | Candidate signal | Classification | Reason | | --- | --- | --- | | Graph rollups `stage -> app`, `adapters -> app`, `config -> app` | rejected as a production reversal at Stage 1 | `go list` production imports contain no lower-level import of `internal/app`; graph connections include tests and ambiguous package grouping. Reopen only with a concrete production edge. | | Similar wrapper/manifest/adapter functions | rejected as broad abstractions at Stage 11 | Thin command wrappers, typed manifest models, distinct protocol constructors, and deliberately simple fakes share syntax rather than policy. Atomic byte writing is the narrow shared mechanism retained in `DUP-001`/`DUP-005`. | | Coverage percentages | deferred diagnostic signals, not findings | Stage 12 must reason from risk and test ownership, not a numeric target. | | Production fan-in/fan-out leaders | rejected as standalone Stage 11 findings | Stable owners such as configuration load/validate, canonical path helpers, manifest transitions, subprocess launch, and command dispatch should have many callers. Ambiguous method names and interface dispatch inflate graph rollups; caller tracing found no new ownership inversion. | | `previouscache.BuildPlan` complexity 22/38 | rejected as a simplification finding at Stage 11 | Its visible branches preserve required/optional absence, remote-current validation, candidate existence, and deterministic ordering. A helper would have no narrower policy owner; remote transfer cost is instead measured under `EFF-001`. | | Direct scan/allocation-in-loop graph signals | rejected as a production efficiency source at Stage 11 | Production filtering found no direct flagged occurrence. High transitive depth was composition/test propagation, and Stage 10 already rejected tiny artifact-set sorting as immaterial. | | Three private zero-inbound path helpers | confirmed as `SIM-004` | Exact text search found only their definitions and no registration/reflection seam; current production paths use newer typed/configurable owners. | | Relative-path validators have similar lexical checks | rejected as `DUP-004` | Config rejects any `..` segment while the shared normalizer accepts non-escaping cleanup. Preserving that stricter language explicitly is clearer than a mode-heavy helper. | | Source classifier comment lists every family | confirmed as `COM-006` | The exported comment omits the implemented extraction branch and compatibility spelling. | | Direct module dependencies can be replaced by the standard library | rejected at Stage 11 | Each direct dependency owns an active S3, YAML, or native no-replace platform contract; `go mod why -m` resolved all six. | | Session `last_error` survives a later stage success | rejected as a current-state defect at Stage 2 | No production reader was found; current status and per-stage error are authoritative, so the field can serve as historical context. Reopen only if an operator surface treats it as the active error. | | Minimal loaded-manifest status/timestamp validation | rejected as a standalone Stage 2 finding | Unknown/non-succeeded statuses fail conservatively into execution, nil maps/records are normalized, and no realistic unsafe caller was established. Configured-versus-persisted identity conflict is separately confirmed in `COR-001`. | | Ignored runner lock-release error | confirmed as `RSK-003` at Stage 3 | The lock is an exclusive-create sentinel, not an OS-released lock. An unlink failure leaves the conflicting file while the runner suppresses the error; process death does the same without a release attempt. | | Two durable meanings of “skip” | architecture candidate merged into `COM-002` at Stage 11 | Run action/status already distinguishes idempotent skip, executed self-skip, and ordinary successful no-output execution. The behavior is coherent; publish/render/analyze documentation must use those exact terms. | | Exported previous-artifact helper accepts traversal | consolidated into `COR-002` | Current production callers normalize first, but the helper's under-root contract is false in isolation. The identity/relative-segment boundary should be repaired once rather than as separate caller bugs. | | Built-in and previous resolvers do not re-hash manifest records | documented trust distinction; deferred to Stages 5 and 10 | Extraction explicitly requires checksum/contract/provenance validation and enforces it. Other source families explicitly use content validation and a previous-cache filesystem fallback; consumer/restore threat models must establish a stronger requirement before this becomes a finding. | | Promotion destination is path-based while source is handle-confined | consolidated into `COR-003` | Source hardening is strong, but destination ancestors share the same symlink/replacement root cause as ordinary writers and cleanup. | | Pointer is written last, so failed publish cannot advance current | rejected as sufficient atomic-commit proof at Stage 4; reader enforcement corrected at Stage 5 | The fixed current manifest is overwritten first (`COR-004`). Strict callers reject old-pointer/new-manifest disagreement, but restore/status omit run validation and accept it (`COR-008`). | | Remote current manifest records `current_pointer_written=false` | safe for current readers; confirmed as `ARC-003` at Stage 11 | The snapshot is necessarily precommit and current-state loaders use actual pointer identity, but the shared remote/local field remains ambiguous. Derive commitment from the pointer or split precommit/local metadata without a post-pointer upload. | | Post-publish cleanup is revisited by later invocations | corrected and confirmed as `COR-006` | The runner invokes the helper, but its gate requires publish in the current `executed` list. Once session publish is succeeded, ordinary retry skips publish and therefore skips cleanup. | | Manual clean should require publish commit metadata | rejected as a policy requirement at Stage 4 | Manual clean is explicit operator authorization with session/global scope, dry-run, cache opt-in, and confined targets. Publish execution/upload/pointer gates correctly apply only to automatic cleanup. | | Restore dry-run performs no local writes | rejected as a literal implementation guarantee; retained as documentation precision under `EFF-001` | Dry-run avoids durable workspace, spool, cache, report, layout, and lock writes, but equal-size/unknown-size classification downloads remote bodies to system temporary files for checksumming and removes them afterward. | | Force means every conflicting restore target is replaced | confirmed as `COR-009` | File conflicts become downloads, but a directory at a planned file path remains a conflict action. The force gate permits execution, which ignores that action and can still install the manifest and report success. | | Size equality is sufficient audio identity | confirmed as `RSK-007` | Restore skips existing audio with the same positive remote size, and the shared cache accepts same-size content without ETag or checksum validation. Focused tests lock in the same-size restore shortcut. | | Previous-artifact readiness is equivalent to loading the prior current pair | confirmed as `COR-010` | Status/validate stop after pointer/manifest validation and do not resolve or check required artifact objects; their missing-previous-session policy also disagrees with optional planning behavior. | | Restore should roll back files written before a later failure | rejected as the current contract; retained as `RSK-006` | The documented operation is incremental and explicitly has no transaction or rollback. The risk is that the old manifest remains authoritative over partially replaced files and planning is not revalidated under the local lock. | | Ordinary manifest and previous-cache reads must always re-hash bytes | rejected as a universal rule at Stage 5 | Restore verifies remote/local equality when needed during classification, and prepare validates required previous artifacts before analysis. The confirmed defects concern generation binding, incomplete readiness checks, and lost source identity rather than a blanket checksum requirement. | | Known-field YAML decoding makes configuration strictly single-document | confirmed as `COR-012` | Known fields are enforced in the first document, but the second decode treats a successfully decoded trailing document as acceptable instead of requiring EOF. | | Any parseable duration is executable | confirmed as `COR-013` | Several adapters and stage parsers require positive timeouts, and the WhisperX constructor rejects a negative retry delay, while shared config validation checks syntax only. | | Populated S3 fields are a compatible implicit backend selector | confirmed as `COR-014` | The documented backend field is operator authority. Silently selecting S3 after an unknown spelling hides invalid configuration and makes validation disagree with construction. | | `--previous-session-id` is only a conditional consistency hint | confirmed as `COR-015` | The option is described and modeled as an expected identifier. Ignoring it when the session omits the field defeats the only CLI-provided expectation. | | Successful temporary remote-session download has caller-owned cleanup | confirmed as `RSK-009` | The helper transfers ownership on success, but no command caller removes the file after config consumers finish and ephemeral provenance can be persisted. | | A trusted secrets directory makes link/type checks unnecessary | confirmed as `RSK-010` | Deployment ownership is not validated by the process and mistakes are realistic. The loader follows working links and admits non-regular entries without a bound. | | All adapter constructors are unconditional expensive/external work | rejected at Stage 6 | Default HTTP/subprocess wrappers do no connection or process work at construction. Notarius, object storage, and remote locks are conditional on selected behavior that needs them. | | Repeated adapter constructors and single-stage commands require immediate consolidation | rejected as standalone Stage 6 findings | Constructors translate distinct protocols, and single-stage dispatch already funnels through `runSingleStageCommand`. Similar shape does not establish duplicated policy. | | Repeated filesystem secret scans are an efficiency defect | rejected as material at Stage 6 | Object-store construction can repeat a deterministic bounded directory scan, but the scan is small, preserves secret-before-adapter ordering, and no material latency or external cost was established. | | Notification settings are consumed because notify succeeds | confirmed as `ARC-004` at Stage 7 | Production success is supplied by a no-op sender regardless of backend/recipient; no transport contract or composition path consumes the accepted public fields. Non-placeholder values must be rejected/reserved until a real integration exists, or a transport must be specified and composed. | | Injectable runner `Env` always represents the resolved config | retained as `TST-006` | Production composition does, but a non-nil injected `Env.Config` is retained and can differ from the explicit config used by other runner setup. | | Configuration relative-path validation is distinct from shared lexical safety | consolidation rejected as `DUP-004` at Stage 11 | Config rejects every `..` segment while `pathsafe` accepts non-escaping cleanup, and it owns field-specific diagnostics. A mode-heavy shared validator would obscure that deliberate policy. | | `exec.CommandContext` bounds a whole external-tool process tree | confirmed as `RSK-011` | The audited toolchain kills only `cmd.Process`; Narratio establishes no process group/job or descendant cleanup. | | Sensitive override-tail redaction makes subprocess diagnostics secret-safe | confirmed as `RSK-012` | Raw logs are unfiltered and inherited sensitive values are absent from the tail redaction set, so the architecture invariant does not hold. | | Successful exit plus JSON/non-empty validation establishes a safe subprocess result | confirmed as `RSK-013` | Ordinary adapters use unbounded, link-following reads/stats; Notarius's bounded regular-file checks show the stronger boundary is both necessary and locally expressible. | | A non-nil S3 continuation token guarantees pagination progress | confirmed as `RSK-014` | The adapter never compares tokens, so a malformed repeated token produces unbounded requests and duplicate accumulation. | | Similar subprocess adapter argument builders should be consolidated | rejected at Stage 7 | The shared launcher already owns common resource/process mechanics; protocol flags, schemas, exit mapping, and validation differ materially and remain clearer in their adapters. Only atomic byte writing is retained as `DUP-005`. | | Audita request fields are authoritative per invocation | rejected; constructor authority confirmed as `ARC-005` at Stage 11 | Polish has no per-invocation override behavior and the real runner already uses constructor state. Remove redundant static request fields; keep invocation paths and modules in the request. | | Adapter fake request slices all require synchronization now | rejected as a blanket Stage 7 finding; WhisperX confirmed under `TST-001` | Transcribe legitimately calls WhisperX concurrently and the required Stage 8 race command reproduces its fake's slice race. Other fakes still have sequential production callers; Stage 12 should assess future fidelity from actual consumers. | | Zero previous requirements means no previous state is managed | confirmed as `COR-017` | Prepare's current test treats the directory as untouched, but publish later uploads it independently of current manifest inputs. Removing the final requirement must remove or exclude stale managed bytes. | | Repeating an explicit audio path is harmless deterministic input | confirmed as `COR-019` | Prepare sorts and records the duplicate twice, while the manifest-first transcribe boundary rejects it. Configuration, producer, and consumer must share one duplicate policy. | | Context cancellation necessarily becomes a transcribe error | confirmed as `COR-018` | Workers and dispatch silently stop on the derived context, while completion checks only a recorded adapter/validation error. Zero or partial results can therefore be returned as success. | | Adapter result paths have one run-local authority rule | confirmed as `ARC-006` at Stage 11 | Stages own run-local destinations; adapters must return no path or the exact requested path. Current redirects have no production use case and would broaden filesystem authority. | | Transcript discovery helpers are intentionally all distinct | plural raw discovery rejected; singleton policy confirmed as `DUP-006` | Raw inputs need directory enumeration and plural ordering. Merged/processed/normalized singletons should use typed manifest-first/canonical resolution from the artifact owner. | | Similar ordinary-stage `Run` methods need a common framework | rejected at Stage 8 | Adapter sequencing, schemas, optional reports, disabled behavior, diagnostics, and multi-output failure order differ materially. Existing run-local helpers are the correct narrow shared mechanism. | | A valid immutable extraction bundle proves it represents the current transcript | confirmed as `COR-020` | Bundle checksums prove only promoted output integrity. The fingerprint and resume validator never resolve or hash the current direct transcript, so an internally valid old bundle can be reused after those input bytes change. | | Same-path external Notarius changes are automatically observable | documented force limitation, not a separate Stage 9 defect | Fingerprinting paths cannot prove executable, config, profile, prompt, module, reference, environment, provider, or runtime contents. Configuration/transitive changes are explicitly assigned to `--force`; operations should extend that wording to same-path executable replacement. Direct Narratio transcript identity is separately confirmed in `COR-020`. | | Every durable promoted bundle is advertised or reusable | rejected at Stage 9 | Promotion establishes immutable bytes, not success. A later sync/checksum/result-persistence failure may leave a uniquely named orphan bundle, but session-manifest success is advertisement authority and neither resume nor catalog scans incidental directories. | | Resume and catalog bundle checks are merely coincidental similarity | rejected; duplicated evidence policy confirmed as `DUP-007` at Stage 11 | They repeat one exact-set, identity, contract/provenance, path, type, and checksum proof. A typed proof can be shared while lifecycle and fail-closed mappings remain separate. | | Extraction resume complexity justifies a generic validation framework | rejected; `SIM-002` merged into `DUP-007` at Stage 11 | The shared evidence proof supplies named ordered decisions without hiding security ordering or moving lifecycle policy into a framework. | | Optional analyze inputs behave uniformly across source families | confirmed as `COR-021` | Final, final-trimmed, and both Markdown built-ins return hard producer-guidance errors before the caller can honor `required: false`; other missing optional families are omitted. | | Explicit selection is only a filter over enabled artifacts | rejected; explicit override confirmed as `ARC-007` at Stage 11 | CLI help and catalog tests establish selection as a one-invocation override. A typed effective set must make validation and prerequisite planning follow that authority; publish retains separate filter semantics. | | Successful topological order implies deterministic dependency validation | confirmed as `RSK-015` | Edges and ready nodes are sorted for success, but the first unavailable-dependency preflight returns from unsorted selected-set map iteration. | | Existing configured output needs a prior analyze success to be reusable | rejected as the current contract | Non-executable configured artifacts intentionally use canonical non-empty files without manifest provenance so disabled/operator-prepared dependencies can be reused. Freshness is operator-owned; misleading enabled-but-unselected provenance stays under `ARC-007`. | | Previous-session resolution can fetch remotely during analyze | rejected at Stage 10 | Analyze uses only manifest-backed and filesystem `previous/` paths. Remote discovery/download belongs to restore/prepare composition, and a boundary test proves the object store is not called. Selection can omit that earlier planning under `COR-022`. | | Input `artifact` and `path` are adapter passthrough fields | confirmed as `COR-024` | Strict config accepts and documents them, but no production read, request field, CLI argument, or generated invocation field exists; values are silently discarded. | | Repeated analyze resolution branches justify a generic resolver framework | rejected; narrow `SIM-003` confirmed at Stage 11 | Source families deliberately differ in authority, provenance, optional absence, and repair guidance. A typed context/result and indexed effective plan reduce width without erasing those policies. | ## Unresolved Questions And Follow-Up - Should startup/status reconcile abandoned `running` invocation records, mark them interrupted, or retain them as an explicitly accepted audit limitation? - Which manifest model should retain run-scoped identity after `COR-001` is repaired, given that publish archives and automatic cleanup both consume it? - Which cross-platform root-relative filesystem primitive should own the symlink-safe mutation capability required by `COR-003` without moving stage policy into low-level `fileops`? - Should sentinel locking be replaced by an OS-released lock, or can a conservative ownership/recovery protocol satisfy `RSK-003` across supported platforms? - Which existing identifier spellings constrain the strict segment validation required by `COR-002`? - Do deployment packaging and service definitions already enforce an umask, ownership, or ACL that reduces `RSK-004`, and where should that contract be documented and tested? - What exact Scriptorium wire semantics, if any, were intended for input-level `artifact` and `path`? Until `COR-024` is answered, non-empty values must not continue to be accepted as meaningful passthrough configuration. - Should remote lock activation be snapshot-based or immediate, and what conditional-write capability can enforce the chosen rule across hosts? - Should restore consume only objects declared by the pointer-selected manifest, or should the committed model carry an immutable generation map that can bind every downloaded artifact, audio object, and previous-cache source? - Should restore's local commit point use a staging tree/snapshot swap, or can a journal plus locked revalidation make incremental replacement and recovery sufficiently explicit under `RSK-006`? - What durable identity should govern audio cache and existing-file reuse: storage version, ETag with provider-specific rules, or a published checksum? - Should restored manifests be rewritten to canonical local paths, or should all consumers ignore persisted absolute paths in favor of workspace-relative identities? - How should publish metadata retain a stable source-to-destination mapping for previous artifacts without reintroducing mutable configuration authority? - Should `--previous-session-id` remain a strict expectation or become an explicit override, and which compatibility cases already depend on omission? - Is any storage backend other than explicit S3 or disabled/local behavior part of the supported configuration language? - What ownership and maximum-size guarantees do deployment packages establish for the filesystem secrets directory, and can all supported platforms enforce no-follow regular-file reads? - Should remote session loading retain bytes until canonical preparation or expose an owned temporary handle whose cleanup spans every command consumer? - Is notification delivery a product requirement, and if so which provider protocol should become the canonical integration contract? Until that is answered, `ARC-004` requires non-placeholder settings to fail explicitly. - Which supported-platform process-tree primitive can give `RSK-011` equivalent termination and wait semantics on Linux, macOS, and Windows? - Which per-contract maximum sizes are operationally valid for Seriatim, Audita, Scriptorium, and bounds outputs under `RSK-013`? - Can subprocess environments be reduced to explicit allowlists, or must `RSK-012` retain broad inheritance and stream-redact every known sensitive value? - Should Notarius expose or Narratio require an explicit dependency-generation digest that covers profiles, prompts, modules, references, executable build, and other transitive inputs, or is documented operator-forced invalidation the intended long-term contract? - Which automation service and cadence should carry the validation required by `TST-012`, particularly the roughly 54-second race run, and should tagged release builds consume an already validated revision or repeat validation? No accepted risks or final audit conclusions are recorded yet. ## Completed-Stage Evidence ### Stage 0 - Contracts and records: development guide, audit plan and sequence, all policy documents, repository/branch/toolchain state. - Graph evidence: refreshed moderate index at exact HEAD; architecture, interface, complexity, similarity, fan-in, and `Execute` call trace queries. - Commands: every baseline command listed above; Go/package/file/test and automation inventories. - Candidates: `TST-001`; metric signals assigned to later owners. - Explicit no-finding conclusion: no production dependency reversal into `internal/app` was found in the package import inventory. - Limitation disposition: the graph excludes the executable entry point, which was verified directly; the race failure is owned by Stages 8 and 12 and does not prevent read-only audit work. ### Stage 1 - Contracts reviewed: architecture, testing and documentation policy; internal overview and every focused internal document; CLI, configuration, operations, and every integration contract. - Code/evidence reviewed: canonical registry and stage declarations; all modeled interfaces; production import graph; application dispatch trace; explicit self-skip usages; interrupted-state usages; focused test ownership references. - Outputs: package/interface ownership, area coverage, stage contract, lifecycle, cross-boundary scenario, and preliminary risk-to-test matrices. - Candidates: `ARC-001`, `ARC-002`, `RSK-001`; no candidate was confirmed from mapping evidence alone. - Explicit no-finding conclusion: the canonical stage order agrees across the registry, internal overview, CLI, and operations contract. - Follow-up: all unresolved behavior has a named owner in Stages 2-12; every area and invariant has an implementation owner and intended test owner. ### Stage 2 - Contracts and code reviewed: planner and full/single-stage entry points; `executeStages`, run-control and identity helpers; session/run manifest models, creation, loading, validation, normalization, atomic persistence, and every transition method; runner lock lifetime; focused internal manifest documentation and Stage 1 matrices. - Graph/source evidence: call traces into full and selected execution; all identity-field consumers; manifest transition/save callers; status and `last_error` usages; runner complexity and atomic-save similarity; complete focused test-function inventory. - Validation: `go test -count=1 ./internal/app ./internal/manifest` passed (`internal/app` 0.708 s, `internal/manifest` 0.010 s; 1.60 s command wall time). `go test -race -count=1 ./internal/app ./internal/manifest` passed (`internal/app` 45.842 s, `internal/manifest` 1.026 s). - Conclusions: every lifecycle cell and dual-save boundary is recorded above; scenarios 1 and 2 are resolved at runner level; lock acquisition/lifetime is resolved and release mechanics assigned to Stage 3. Confirmed `COR-001` and `RSK-001`; added `DUP-001`, `SIM-001`, `COM-001`, and `TST-002` for named later owners. - Explicit no-finding conclusions: canonical invalidation works at both first and last stage and is independent of selected-plan width; stale transitions intentionally retain diagnosis data while running/failure/skip clear it; session authority makes all enumerated disagreement states retry or reuse conservatively; the runner's two skip forms are durably distinguishable. ### Stage 3 - Contracts and code reviewed: architecture/path/security policy; internal artifacts, workspace, manifest, operations, and troubleshooting contracts; all canonical local/S3/cache constructors; pathsafe and artifactpolicy; built-in, configured, extraction, previous, and current-state resolution; local-store layout/copy/lock code; atomic write/copy/download installation; directory promotion and platform-specific no-replace/directory-sync support; restore/audio/previous download callers; manual and post-publish cleanup. - Graph/source evidence: canonical-helper and direct-mutation inventories; callers of path/key, artifact-resolution, fileops, current-state, previous- cache, and lock helpers; focused test-function inventories; fallback text search for non-code policy and direct OS mutation sites where graph results were insufficient. - Validation: `go test -count=1 ./internal/artifacts ./internal/artifactpolicy ./internal/pathsafe ./internal/fileops` passed (0.98 s wall time). `go test -race -count=1 ./internal/artifacts ./internal/fileops` passed (2.25 s wall time). - Conclusions: canonical owners and artifact resolution order are recorded above; lexical normalization handles mixed separators, traversal, absolute, and drive forms when callers invoke it; extraction source trust and source- side promotion are strong; low-level helpers correctly consume explicit destinations. Scenario 10 is resolved: live contenders are excluded, while stale sentinel/release behavior is unsafe operationally. - Findings: confirmed `COR-002`, `COR-003`, `RSK-002`, `RSK-003`, and `RSK-004`; added `DUP-002` and `TST-003`; refined `DUP-001` with the shared durability gap. - Explicit no-finding conclusions: current-state helpers have typed missing cases and support strict identity checks when callers request them; artifact resolution is deterministic and matches its documented source-specific validation; promotion preserves an existing/concurrent destination and rejects unsafe source trees; temporary files/trees are cleaned on ordinary failures; unsupported promotion platforms fail before creating a durable bundle; fileops does not infer higher-level policy. - Follow-up: Stages 4-10 should cite the shared confinement/durability roots for concrete callers. Stages 5 and 10 must decide whether ordinary manifest and previous-cache checksum trust is sufficient. Stages 6, 11, and 12 own compatibility, simplification, and durable regression coverage respectively. ### Stage 4 - Contracts and code reviewed: architecture publish/cleanup/force invariants; focused publish, storage, workspace, manifest, operations, CLI, and troubleshooting contracts; publish stage prerequisites, artifact catalog and output resolution, selection, locks, run/previous collection, every upload, current snapshot/pointer generation, storage upload semantics, effective remote-lock loading/mutation, current-state discovery/identity validation, status/restore entry interpretation, runner terminal ordering, automatic cleanup, cleanup target validation, and manual session/global/cache cleanup. - Graph/source evidence: call traces from publish and lock commands into storage; exact source for current-state readers, cleanup gates, lock mutation, and runner persistence; complete focused publish/cleanup/lock/current-state test inventory; fallback source/text inspection for the generic stage method, S3 `os.Open`, and non-code contracts where graph modeling was insufficient. - Validation: `go test -count=1 ./internal/stage ./internal/app ./internal/artifacts ./internal/adapters/storage` passed (`internal/stage` 0.440 s, `internal/app` 0.749 s, `internal/artifacts` 0.028 s, `internal/adapters/storage` 0.019 s; 1.86 s command wall time). - Conclusions: publish plans fully before writing and uploads sorted run files, configuration-ordered outputs, sorted previous files, current manifest, then the pointer last. Output family, required/optional, selection, lock, exclusion, retry, existing-object, and force behavior is recorded above. Remote-current and automatic-cleanup truth tables resolve scenarios 5 and 7 at every boundary; manual cleanup is correctly a separate explicit authorization. - Findings: confirmed `COR-004`, `COR-005`, `COR-006`, `COR-007`, and `RSK-005`; added `ARC-003`, `COM-002`, and `TST-004`; resolved publish's portion of `ARC-002` and corrected Stage 2's cleanup-retry conclusion. - Explicit no-finding conclusions: pointer is unequivocally the final upload and intended current marker (Stage 5 later confirmed incomplete reader enforcement as `COR-008`); failure before current-manifest publication preserves any prior current pair; successful retry is idempotent by unconditional replacement; static and loaded remote locks, including required outputs, survive force; disabled extraction is safe because only explicit extraction rules resolve it; manual clean does not need publish metadata; storage correctly remains policy-neutral and consumes explicit paths/keys. - Follow-up: Stage 5 reused and corrected the remote-current reader truth table. Stage 11 owns metadata vocabulary/duplication decisions; Stage 12 owns the smallest stateful commit, cleanup-retry, symlink-read, and remote-lock concurrency tests. ### Stage 5 - Contracts and code reviewed: architecture and testing policy; restore, workspace, storage, artifact, manifest, operations, troubleshooting, and CLI contracts; remote-current discovery, restore planning/classification, execution/reporting, ordinary and audio download installation, cache/spool materialization, previous-artifact requirement collection/planning, prepare consumption, and status/validate readiness reporting. - Graph/source evidence: callers and exact options for current-state loading; restore plan/action and manifest-last traces; storage/list/download and temporary-install paths; audio cache-key and validation paths; previous-cache candidate resolution and consumer traces; focused test-function inventories. Direct source and text inspection covered non-code contracts and implementation details the graph could not distinguish. - Validation: `go test -count=1 ./internal/app ./internal/previouscache ./internal/audio ./internal/artifacts ./internal/adapters/storage` passed (`internal/app` 0.759 s, `internal/previouscache` 0.009 s, `internal/audio` 0.012 s, `internal/artifacts` 0.025 s, `internal/adapters/storage` 0.008 s; 1.87 s command wall time). - Conclusions: restore authority, complete remote-to-local mapping, deterministic ordering, action/force/dry-run behavior, manifest-last execution, report and every failure boundary are recorded above. Audio cache/spool identity and previous-session required/optional, candidate, and fallback policies are explicit. Shared mechanics are separated from restore, prepare, status, and validate caller policy. - Findings: confirmed `COR-008`, `COR-009`, `COR-010`, `COR-011`, `RSK-006`, `RSK-007`, `RSK-008`, and `EFF-001`; added `DUP-003` and `TST-005` with named later owners. - Explicit no-finding conclusions: relative target construction and ordering are deterministic and lexically confined; force does not bypass identity, traversal, or lock validation; dry-run creates no durable restore state; ordinary download failures remove the active temporary file; the storage adapter remains policy-neutral; prepare intentionally overwrites its private previous-cache destination while restore classifies existing destinations. - Scenario disposition: scenario 4 confirms the pointer is not sufficient authority because restore/status omit run validation and restore reads mutable prefix objects; scenario 6 confirms partial incremental replacement, old- manifest authority before the final install, and restored state despite a later report failure. Rollback and automatic retry are intentionally absent. ### Stage 6 - Contracts and code reviewed: development guide, all repository policy, configuration/CLI/example and internal adapter contracts; process entry and dispatch; pipeline/campaign/session discovery; strict YAML loading, defaults, resolution, templates, and every validation family; secrets loading; runner, adapter, object-store, lock, and remote-session composition. - Graph/source evidence: exact snippets and caller/data-flow traces for loaders, defaults, `Resolve`, `Validate`, duration and storage checks, command selection, remote fallback, secret loading, `executeStages`, conditional constructors, temporary downloads, and injected environments. Direct source/text inspection covered YAML tags, help text, documentation, maintained examples, and lifecycle details the graph could not distinguish. - Validation: `go test -count=1 ./internal/config ./internal/app ./cmd/narratio` passed (`internal/config` 0.099 s, `internal/app` 0.818 s, CLI has no test files; 2.11 s command wall time). `go vet ./...` passed (1.88 s command wall time). - Conclusions: explicit/default selection precedence, empty-value semantics, relative-path anchoring, resolution and validation order, every operator field family and runtime consumer, secret propagation, enabled/disabled adapter composition, resource ownership, and maintained example validity are recorded above. No live credentials or external services were required. - Findings: confirmed `COR-012`, `COR-013`, `COR-014`, `COR-015`, `RSK-009`, and `RSK-010`; added `ARC-004`, `DUP-004`, and `TST-006` with named later owners. - Explicit no-finding conclusions: pointer-valued defaults preserve explicit false/zero; explicit empty modules remain distinct from omission; Notarius paths are anchored to the pipeline; external work is conditionally composed; default client/runner construction opens no closeable resource; secret values are not directly persisted or logged by config/composition code; Stage 7 later found that child-produced logs and inherited-secret error tails violate the broader invariant under `RSK-012`. Maintained examples use non-secret placeholders and are executable under representative sessions. Repeated bounded secret scans and protocol-specific constructors do not establish material efficiency or duplication defects. - Documentation/example disposition: config defaults and maintained example structure match implementation. Notification backend/recipient settings are the material drift because production always uses a no-op sender; `ARC-004` assigns the transport/documentation decision to Stage 7. ### Stage 7 - Entry revision: `0920062` (`Document configuration and composition audit findings`). Commits since the pinned audit revision modify audit documentation only, so implementation/test evidence remains pinned to the identity recorded above. - Contracts and code reviewed: development guide, audit sequence, all repository policy, adapter/internal/storage/audio documentation, and every production file under `internal/adapters`, `internal/audio`, `internal/logging`, `internal/contracts`, and `internal/artifactmodel`; production stage/app callers were traced for each boundary. - Graph/source evidence: scoped architectures, symbol inventory, caller/callee and code searches for every adapter interface and external operation, then exact source inspection of HTTP request/retry handling, shared subprocess launch and diagnostics, all subprocess argument/config/result adapters, S3 pagination/body/file handling, audio temporary installation, notification composition, fakes, shared models, and focused tests. Local Go 1.26.5 `CommandContext` documentation/source confirmed direct-process kill behavior. - Validation: `go test -count=1 ./internal/adapters/... ./internal/audio ./internal/logging ./internal/contracts ./internal/artifactmodel` passed (all 13 package results passed; 1.62 s command wall time). `go test -race -count=1 ./internal/adapters/... ./internal/audio` passed (all 10 package results passed; 21.61 s command wall time). No live service, credential, destructive, or paid operation was exercised. - Resource conclusions: every HTTP response, S3 body, opened local file, retry timer, direct subprocess, log descriptor, and temporary download has an explicit normal/error release path. Context reaches HTTP/S3 operations and direct child processes; gaps are multipart pre-copy cancellation (`EFF-002`), descendant termination (`RSK-011`), and pagination progress (`RSK-014`). No adapter-owned goroutine/channel or process-level shutdown resource exists. - Findings: confirmed `COR-016`, `RSK-011`, `RSK-012`, `RSK-013`, `RSK-014`, `EFF-002`, and prior candidate `ARC-004`; added `ARC-005`, `DUP-005`, `COM-003`, and `TST-007`, and refined `TST-001` as a concurrent-fake defect rather than a production HTTP-client race. - Explicit no-finding conclusions: transport/SDK/process types and protocol retry policy do not leak into stages; ordinary response bodies/files/timers are closed; retry status classes and output install ordering match WhisperX; Notarius performs bounded regular-file/root/lane validation; arguments and generated configs are deterministic and contain credential names/presence, not values; S3 callers own sorting/policy and not-found adaptation is correct; shared artifact models have stable tags and non-lossy slice conversion; the logger constructor owns no resource. Protocol-specific adapter builders are justified rather than a consolidation target. - Later-stage assignments: Stage 8 owns transcribe worker/fake behavior, ordinary stage output consumption, notify lifecycle, and Audita override intent. Stage 11 owns `ARC-005`, `DUP-005`, and `COM-003`; Stage 12 owns the risk-based additions/consolidation in `TST-001` and `TST-007`. ### Stage 8 - Entry revision: `0a772e0` (`Document external adapter audit findings`). Commits since the pinned audit revision modify audit documentation only, so implementation/test evidence remains pinned to the identity recorded above. - Contracts and code reviewed: development guide, Stage 8 sequence, all repository policy, overview, focused prepare/transcribe/merge/polish/ normalize/trim/render documents, WhisperX/Seriatim/Audita/Scriptorium integration contracts, and the complete production vertical slices in `internal/stage`, with supporting config, audio, previous-cache, publish, and runner callers where they establish the scoped input/lifecycle boundary. - Graph/source evidence: scoped stage architecture and symbol inventory, manifest/config/caller traces, and exact source inspection of audio selection and materialization, previous hydration/clearing, every stage `Run`, run-local helpers, transcript discovery/validation, result path use, declarations, disabled branches, adapter fakes, and all focused test names/cases. Similarity was evaluated only after stage-specific differences were recorded. - Validation: the exact focused normal command passed all seven packages (1.34 s command wall time). `go test -race -count=1 ./internal/stage ./internal/audio` failed in `internal/stage` at the already registered WhisperX fake request-slice race from `TestTranscribeStageTranscribesPreparedAudio`; `internal/audio` passed (1.85 s command wall time). This is the expected `TST-001` limitation and no new race signature appeared. No live adapter, credential, destructive, or paid operation was exercised. - Contract conclusions: prepare's ordinary source selection, stable copying, provenance, sorting, S3/local exclusivity, and non-empty previous hydration are coherent. Transcribe has unique filename-derived speakers, bounded concurrency, exact returned-path identity, deterministic successful ordering, and all-or-nothing handling of recorded adapter failures. Transformations are manifest-first, run-local, schema/report aware, and preserve diagnostic/output classification. Disabled trim is successful copy processing; disabled render is successful no-output execution whose later enablement requires force. - Findings: confirmed `COR-017`, `COR-018`, and `COR-019`; added `ARC-006`, `DUP-006`, `COM-004`, and `TST-008`; refined `ARC-001`, `ARC-002`, `ARC-005`, `RSK-013`, and `TST-001`. Scenario 8 is resolved through worker aggregation and subprocess-backed ordinary stages. - Explicit no-finding conclusions: sorted current inputs and transformation results are deterministic; basename collisions from different sources fail; transcribe rejects redirected results; recorded adapter/schema failure does not canonically materialize a successful subset; subprocess-backed stages are synchronous and propagate adapter errors; trim debug render, logs, and generated configs remain diagnostics; a generic stage framework is not justified. Current production transformation adapters return their requested output paths, so inconsistent result-path authority remains architectural, not a present production data defect. - Later-stage assignments: Stage 10 later resolved analyze's `ARC-002` outcome. Stage 11 owns `ARC-001`, `ARC-005`, `ARC-006`, `DUP-006`, `COM-004`, and the narrow shared-owner decisions. Stage 12 owns the risk-based additions in `TST-001`, `TST-007`, and `TST-008`. ### Stage 9 - Entry revision: `57cac5d` (`Document ordinary stage audit findings`). Commits since the pinned audit revision modify audit documentation only, so implementation/test evidence remains pinned to the identity recorded above. - Contracts and code reviewed: development guide, exact Stage 9 sequence, all repository policy, overview, extract/artifacts/manifest/analyze/publish internal documents, Notarius integration, configuration, operations, troubleshooting, CLI run behavior, and the complete extraction production slice through configuration, composition, stage execution/resume, adapter, promotion, manifests, catalog, analyze, and publish. - Graph/source evidence: scoped stage architecture, extraction cohesion and hotspot inventory, caller/callee and symbol searches for configuration, fingerprint, execution, promotion, resume, and consumers, followed by exact source inspection of every production branch and focused test case. `ValidateResume` measured cyclomatic complexity 40/cognitive complexity 53; that metric was used only after its proof decisions were manually traced. - Validation: the exact required command, `go test -count=1 ./internal/stage ./internal/artifacts ./internal/fileops ./internal/adapters/notarius ./internal/app`, passed all five packages (1.94 s command wall time). No live Notarius process, credential, destructive, remote, or paid operation was exercised. - Contract conclusions: run-local receipt/log/staging state is distinct from the complete no-replace durable bundle; promotion is distinct from session-manifest advertisement; resume is distinct from fail-closed catalog hydration. Configured lanes require exact contracts and provenance and are the only selectable outputs. Missing/obsolete evidence reruns extraction, unsafe filesystem evidence stops it, and unadvertised bundles are never discovered incidentally. Scenario 3 is resolved with explicit external/ transitive force limits and a missing direct-input identity defect. - Findings: confirmed `COR-020`; added `DUP-007`, `SIM-002`, and `TST-009`; and refined `ARC-001` and `RSK-013`. The same-path Notarius dependency limitation is documented and classified rather than duplicated as another defect. - Explicit no-finding conclusions: output-map iteration is sorted; required lane descriptors are exact and rejection-aware; index and lane paths are confined; configured lanes are regular, non-empty JSON; checksums are taken before and after promotion; destination installation is atomic no-replace on supported platforms; contract/provenance survives explicit publication; unconfigured lanes and the index are neither selectable nor implicitly uploaded; disabled extraction self-skips coherently; process/receipt/ prepromotion failures advertise no result; a generic validation or stage framework is not justified. - Later-stage assignments: Stage 10 later completed the analyze source-family and publish-selection matrix. Stage 11 owns `ARC-001`, `DUP-007`, and `SIM-002`. Stage 12 owns the risk-based additions/consolidation in `TST-009` and the extraction reach of `RSK-013` alongside `TST-002`, `TST-003`, and `TST-007`. ### Stage 10 - Entry revision: `083decc` (`Document extraction audit findings`). Commits since the pinned audit revision modify audit documentation only, so implementation/test evidence remains pinned to the identity recorded above. - Contracts and code reviewed: development guide, exact Stage 10 sequence, all repository policy, overview, analyze/artifacts/publish/manifest internal documents, Scriptorium integration, configuration, CLI, operations, and troubleshooting; complete production flow through config validation, selection propagation, previous-requirement consumers, artifact policy and catalog/resolvers, analyze planning/execution/materialization, Scriptorium adapter requests, lifecycle persistence, and publish source filtering. - Graph/source evidence: scoped stage architecture and hotspot inventory; searches, snippets, and call/data-flow traces for source classification, catalog registration/availability, configured selection, previous requirements, dependency ordering, execution, resolution, and publish/helper catalog consumers; followed by exact production/test inspection. The main execution helper measured 239 lines/ten parameters, source resolution 84 lines/cognitive complexity 71, and dependency ordering 86 lines/ten loops; these were treated as review signals only after behavior was traced. - Validation: the exact required command, `go test -count=1 ./internal/stage ./internal/artifacts ./internal/artifactpolicy ./internal/config ./internal/adapters/scriptorium ./internal/app`, passed all six packages. The command completed in 2.01 s wall time. No live Scriptorium process, credential, remote, destructive, or paid operation was exercised. - Contract conclusions: registered, executable, available, generated, and reused states are distinct. Selection currently overrides enabled state; non-executable configured files can be reused; successful order is lexical and dependency-correct; generated outputs become immediately available to later plans. All source-policy families were traced, and previous-session consumption is local-only. Publish selection filters configured-source rules only and does not trigger analyze or suppress built-in/extraction rules. Missing/no-executable analyze is durable successful no-output execution. - Findings: confirmed `COR-021`, `COR-022`, `COR-023`, `COR-024`, and `RSK-015`; added `ARC-007`, `DUP-008`, `SIM-003`, `COM-005`, and `TST-010`; refined `ARC-001`, `ARC-002`, and `RSK-013`; and resolved scenario 9. - Explicit no-finding conclusions: unknown source/dependency identities and enabled cycles are configuration errors; explicit selected cycles are caught at runtime; successful plan/output/metadata order is deterministic; a missing unselected dependency cannot be silently executed; configured disk reuse is an intentional non-manifest freshness contract; extraction hydration remains fail-closed; previous resolution performs no remote call; publication cannot execute artifacts; partial failed analyze output is not advertised as stage success; no material dependency-order efficiency defect or generic resolver/ graph framework is justified. - Later-stage assignments: Stage 11 owns `ARC-001`, `ARC-002`, `ARC-007`, `DUP-008`, `SIM-003`, and `COM-005`, coordinated with earlier candidates. Stage 12 owns the risk-based additions/consolidation in `TST-010` and the analyze reach of `RSK-013` alongside existing safe-output test candidates. ### Stage 11 - Entry revision: `9cb9008` (`Document analyze dependency audit findings`). Commits since the pinned audit revision modify audit documentation only, so implementation/test evidence remains pinned to the identity recorded above. - Contracts and code reviewed: development guide, exact Stage 11 sequence, all repository policy, every accumulated `ARC`, `DUP`, `SIM`, `EFF`, and `COM` entry, production callers for each candidate, direct dependencies, platform implementations, TODO/FIXME/build-tag patterns, and existing benchmark inventory. - Graph/source evidence: production similarity, complexity, fan-in/fan-out, loop-depth, direct scan/allocation-in-loop, change-coupling, zero-inbound, and call-path queries followed by exact snippets and text search. Notable signals were runner complexity 54/96, `previouscache.BuildPlan` 22/38, `HydrateExtractionArtifacts` 17/25, analyze execution’s wide context, identical 41-43-line atomic writers, three 30-34-line singleton transcript resolvers, and three private zero-inbound helpers. Metrics were classified only after their complete policy/caller paths were compared. - Validation: `go test -count=1 ./...` passed all 22 packages in 3.22 s wall time, and `go vet ./...` passed in 0.46 s. No benchmarks exist, so no invented performance result is reported; `EFF-001` and `EFF-002` specify representative byte/latency and memory/allocation measurements. No live adapter, credential, remote, destructive, or paid operation was exercised. - Structural conclusions: confirmed removal of the unused partial `IODecl` interface, explicit-selection override authority, requested adapter output path authority, constructor-owned Audita static settings, and a correction to ambiguous remote/local commit metadata. Confirmed narrow shared owners for atomic replacement, sibling-temp installation, canonical paths/singleton artifacts, extraction evidence, catalog bootstrap, runner terminal failure, and analyze execution context. Rejected shared config path validation and broad workflow, resolver, graph, stage, adapter, or fake abstractions. - Candidate disposition: `ARC-001`, `ARC-003`, and `ARC-005` through `ARC-007`; `DUP-001` through `DUP-003` and `DUP-005` through `DUP-008`; `SIM-001` and `SIM-003`; and `COM-001` through `COM-003`, the source/catalog portion of `COM-005`, and new `SIM-004`/`COM-006` are confirmed. `ARC-002`, `COM-004`, and the no-output portion of `COM-005` merge into broadened `COM-002`; `SIM-002` merges into `DUP-007`; `DUP-004` is rejected after exact semantic comparison. `EFF-001`, `EFF-002`, and `ARC-004` remain confirmed with sharper remediation/measurement boundaries. - Explicit no-finding conclusions: no direct production scan/allocation-in-loop signal, material small-set sorting/copying issue, excessive adapter initialization, removable direct dependency, unsupported-platform silent fallback, generic framework opportunity, or additional concurrency/channel defect was established. The accurate stale-detection and notification TODOs remain rationale/future-contract markers; only the obsolete Audita TODO and incomplete exported source-family comment are clarity findings. - Later-stage assignments: Stage 12 owns only the risk-based `TST` inventory and test-policy audit. Stage 13 owns prioritization, dependency ordering, and accepted-risk decisions; it should treat the merged/rejected structural dispositions above as resolved rather than reopening them from metric shape. ### Stage 12 - Entry revision: `f387222` (`Document maintainability audit conclusions`). Commits since the pinned audit revision modify audit documentation only, so implementation/test evidence remains pinned to the identity recorded above. - Policy and scope reviewed: development guide, exact Stage 12 sequence, every repository policy document, the complete risk-to-test ledger and prior behavior-pass observations, all ten accumulated `TST` candidates, and the repository's test/example/automation inventory. - Graph/source evidence: 904 test-file functions, 749 `Test` functions, 95 test files, no fuzz tests, no benchmarks, no `t.Parallel` use, test-size and complexity hotspots, process-global environment/directory mutation, time-dependent cases, test doubles, helper subprocesses, loopback HTTP, and assembled workflow overlap. Exact source inspection followed the graph for large/mixed-owner tests, secret loading, fakes, and release automation. - Coverage diagnostic: `go test -count=1 -cover ./...` passed all packages in 3.49 s. Tested-package coverage ranged from 69.8% to 100%; `cmd/narratio` reported 0% because it has no tests. Percentages were used only to revisit weak consequential branches and high-coverage duplication. Manifest's low end aligns with `TST-002`; logging's 100% and the notification placeholder do not justify percentage-driven additions. - Determinism validation: `go test -shuffle=on -count=3 ./...` failed in 4.71 s with seed `1786373771816345415` because `TestLoadSecretsFromConfigLoadsValidFiles` leaked two environment values. The isolated same-seed, same-test, three-count command reproduced failure on repetitions two and three, confirming `TST-011` rather than random flakiness. - Race validation: `go test -race -shuffle=on -count=1 ./...` failed in 54.21 s with seed `1786373816980315094`. The only race was the already recorded WhisperX fake request-slice mutation in `TST-001`, reached by the bounded concurrent transcribe test; every other package passed and no second production race was observed. - Final documentation-only verification: `go test -count=1 ./...` passed all packages in 3.50 s and `go vet ./...` passed in 0.78 s. `git diff --check` reported no whitespace errors. - Suite conclusions: normal execution is fast, offline, credential-free, and based on temp files, loopback services, or the current test binary. Current focused tests strongly protect ordinary parsing, protocol, lifecycle, and source-family behavior. Confirmed additions target durable disagreement, destructive recovery, adversarial output/resource boundaries, cancellation, effective selection, and direct input identity. Stateful object-store behavior is preferable to additional call-recording mocks; protocol argument assertions remain contractual. - Candidate disposition: confirmed `TST-001` through `TST-008` and `TST-010`; confirmed `TST-009` after rejecting its low-value orphan-residue subcase; and added confirmed `TST-011` through `TST-015` for environment isolation, automation, focused fuzzing, configuration-test ownership, and assembled-test consolidation. Every proposed addition names its realistic defect and owner; every consolidation names the focused/representative protection that remains. - Explicit no-finding conclusions: no live network, paid-service, ambient credential, fixed-port, oversized snapshot, golden-file, pervasive exact- error, generic fixture-framework, blanket fake-synchronization, or general parallel-test opportunity was established. Existing YAML/JSON tables do not justify indiscriminate parser fuzzing, and bounded evidence did not establish flakiness beyond the reproducible environment leak and known fake race. - Later-stage assignment: Stage 13 owns only deduplication, ranking, remediation ordering, accepted-risk decisions, and final audit closeout. It should not reopen the risk-based test ownership and marginal-value decisions recorded here without new evidence.