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
| 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.
| 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. |
| 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`. |
| 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. |
| 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 | complete | Revalidated the pinned implementation boundary, reconciled all registers and matrices, retained 77 confirmed IDs with four merged/rejected dispositions, recorded positive conclusions, dimensions and accepted risks, and produced an 11-workstream dependency-ordered remediation backlog. Report links and document integrity were validated; no production change occurred. |
| 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. |
| 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
| Scenario | Primary audit stage | Final conclusion | Principal findings or accepted boundary |
| --- | --- | --- | --- |
| 1. Success becomes non-resumable, rerun fails, later reuse decision | 2 | Session reuse is conservative and correct; handled validation errors leave inaccurate invocation audit state. | `RSK-001`, `TST-002`; abrupt-death residue is accepted below after handled errors are repaired. |
| 2. Forced/changed upstream outcome with succeeded, self-skipped, disabled downstream | 2 | Canonical invalidation is sound; durable success, self-skip, and idempotent skip are distinct, but documentation blurs them. | `COM-002`; no production lifecycle defect. |
| 3. Extraction bundle followed by configuration/transitive-input change | 9 | Configured values are fingerprinted and external same-path changes are operator-forced; the direct trimmed input is incorrectly omitted. | `COR-020`; accepted external-force boundary below. |
| 4. Published/restored/prepared previous state consumed locally by analyze | 5 | Analyze is correctly local-only, but commit binding, readiness, source mapping, and effective selection are incomplete. | `COR-008`, `COR-010`, `COR-011`, `COR-022`. |
| 5. Publish failure at every upload boundary, then status/restore/retry | 4 | Pointer-last order alone is insufficient because the fixed manifest replaces the prior pair and readers do not share one strict authority rule. | `COR-004`, `COR-008`, `TST-004`. |
| 7. Cleanup after skipped/failed/locked/partial/committed publish | 4 | Gates correctly require an explicit commit, but automatic cleanup is not durable/retryable and path mutation is not symlink-confined. | `COR-003`, `COR-006`, `COR-007`. |
| 8. Cancellation through workers, HTTP, subprocess, storage, manifests | 7 | Direct operations generally receive cancellation and release resources; transcribe can report partial success and subprocess descendants can survive. | `COR-018`, `RSK-011`, `RSK-014`, `EFF-002`. |
| 9. Disabled/unselected/reused/generated/extraction/previous source then publish filtering | 10 | Source families and publish filtering are deterministic, but effective selection does not consistently drive validation/prerequisites and some optional/passthrough contracts fail. | `COR-021` through `COR-024`, `RSK-015`, `ARC-007`. |
| 10. Concurrent same-session invocation and lock cleanup failures | 3 | Live local exclusion spans the full mutation lifetime; stale/release failures and remote lock snapshots remain operational risks. | `RSK-003`, `RSK-005`, `TST-003`. |
| 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 |
| 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 |
| 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. |
| 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. |
| 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. |
| 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. |
| 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`. |
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
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.
| 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
| 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
### 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
## 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
configuration, adapters, ordinary stages, extraction, analyze, and their
focused tests. Every detailed finding below still resolves to its recorded
implementation owner and consumer boundary. No item rests on a metric alone,
and no confirmed item was downgraded or rejected during revalidation.
### Completion criteria
| Criterion | Final evidence and conclusion |
| --- | --- |
| Every inspection area reviewed | All 15 area-ledger rows are `reviewed`; Stages 2-12 record contracts, production owners, focused tests, commands, findings, and explicit no-finding conclusions. |
| Lifecycle and scenarios concluded | Every lifecycle outcome is source-backed for both manifests. The ten-scenario matrix above now records a final conclusion and root finding or accepted boundary for each row. |
| Duplication classified | `DUP-001` through `DUP-003` and `DUP-005` through `DUP-008` are narrow shared mechanisms/policies; `DUP-004` is rejected because its accepted language differs. Broad stage, workflow, resolver, validator, adapter, fake, and manifest abstractions are explicitly rejected. |
| Simplification and efficiency bounded | `SIM-001`, `SIM-003`, and `SIM-004` name smaller owners without hiding policy; `SIM-002` merges into `DUP-007`. `EFF-001` and `EFF-002` name representative byte/latency/memory measurements; no other performance claim survived workload review. |
| Test suite assessed by risk | The final matrix covers integrity, destructive actions, compatibility, security, concurrency, idempotency, recovery, cancellation, partial success, redundancy, doubles, helpers, coverage, fuzzing, determinism, offline behavior, runtime, and automation. |
| Findings deduplicated and ranked | 77 IDs remain confirmed. Four stable IDs are non-independent dispositions: `ARC-002` and `COM-004` merge into `COM-002`, `SIM-002` merges into `DUP-007`, and `DUP-004` is rejected. The backlog below is authoritative for dependency/risk order. |
| Questions, accepted risks, and limitations explicit | The final decision table after the registers assigns an owner and safe interim boundary to every unresolved contract family, followed by intentionally accepted risks and evidence limitations. |
### Positive conclusions
- The canonical stage registry and planner are deterministic, and force,
changed outcomes, self-skip, ordinary success, failure, and retry invalidate
or preserve downstream state conservatively.
- Session state is consistently the cross-invocation authority; individual
atomic saves preserve the previous file on ordinary pre-rename failure, and
terminal session-first ordering preserves safe reuse even when the audit
ledger becomes inaccurate.
- Directory promotion strongly validates and identity-checks its source,
rejects replacement of an existing immutable destination, syncs its durable
tree, and fails explicitly on unsupported platforms.
- Publish uses deterministic upload ordering and writes the current pointer
last. Manual cleanup is explicit, scoped, dry-runnable, cache-preserving by
default, and correctly independent of automatic postcommit policy.
- Restore planning is deterministic, confines lexical remote mappings, keeps
dry-run free of durable local writes, validates downloads before install, and
installs the session manifest last. Analyze consumes previous-session state
locally and never performs an implicit remote read.
| `ARC-001` | Low current; medium future contract misuse | Low until a consumer appears | High | Small removal |
| `ARC-003` | Medium remote/local state ambiguity | Low-to-moderate as consumers grow | High | Small-to-medium model/protocol clarification |
| `ARC-005` | Medium adapter/test contract drift | Moderate during adapter change | High | Small request/constructor change |
| `ARC-006` | High run-local output-authority risk | Low currently; moderate with a divergent adapter | High | Medium shared contract and caller repair |
| `ARC-007` | High effective-selection correctness | Moderate for explicit selection | High | Medium app/config/artifact planning change |
| `DUP-001`, `DUP-003`, `DUP-005` | High durability/security drift inherited from `RSK-002`/`COR-003` | Moderate during shared repair | High | Medium shared fileops capability and caller migration |
| `DUP-002` | Medium run-identity drift | Low-to-moderate | High | Tiny path-owner correction |
| `DUP-006` | Medium transcript-source drift | Moderate during source changes | High | Small-to-medium artifact resolver migration |
| `DUP-007` | High extraction evidence drift | Moderate during compatibility/security changes | High | Medium typed evidence owner |
| `DUP-008` | High analyze/publish/operator catalog drift | Moderate as source families change | High | Medium shared bootstrap with explicit caller policy |
| `SIM-001` | Medium audit/persistence correctness support | Moderate on new error paths | High | Small-to-medium runner extraction |
| `SIM-003` | Medium maintainability and ordering support | Moderate during analyze changes | High | Medium typed plan/context refactor |
| `SIM-004` | Low dead-code cost | Certain but harmless | High | Tiny deletion |
| `COM-001` | Medium risk of weakening ledger order | Moderate during runner repair | High | Tiny comment after `SIM-001` |
| `COM-002`, `COM-005` | Medium operator/developer lifecycle misunderstanding | Present in current docs | High | Small documentation correction |
| `COM-003`, `COM-006` | Low stale-contract friction | Present in current comments | High | Tiny comment deletion/update |
| `TST-001`, `TST-011` | Medium loss of diagnostic determinism | Certain under race/repetition runs | High, reproduced | Small fake/environment cleanup |
| `TST-002` through `TST-010`, `TST-013` | Inherited critical/high confidence for named integrity, recovery, security, and concurrency roots | Same plausible scenarios as linked findings | High after cross-layer inventory | Medium distributed regression additions, each at its named owner |
| `TST-012` | High release-validation exposure | Ongoing on every unvalidated change/tag | High | Small-to-medium automation addition |
| `TST-014`, `TST-015` | Low-to-medium recurring maintenance friction | High during legitimate config/stage changes | High | Medium test consolidation |
### Dependency-ordered remediation backlog
This order is authoritative over the category-number order used by the detailed
registers. Regression tests named by a workstream should land with that repair;
standalone test-suite and documentation cleanup follows the owning behavior.
1. Establish safe identity, filesystem, secret, and durable-write foundations:
| 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 | rejected as standalone findings | Stage 12 used them only to revisit consequential branches and high-coverage duplication; the final test assessment sets no 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. |
## Open Decisions, Accepted Risks, And Limitations
The audit establishes the unsafe current behavior and smallest safe interim
boundary; it does not make product or platform choices that require deployment
knowledge. These decisions are inputs to the remediation sequence, not reasons
to leave the current defects implicit.
| Decision family | Owner | Required safe interim boundary | Related findings |
| --- | --- | --- | --- |
| Run-scoped manifest identity and abandoned invocation presentation | App/manifest maintainers | Recompute or validate one identity unit; terminalize every handled error. Status may label genuinely abandoned records without changing session progress authority. | `COR-001`, `RSK-001`, `SIM-001` |
| Cross-platform confined mutation, durable replacement, and local locking | Fileops/artifacts owners with supported-platform maintainers | No path-based destructive operation may follow an untrusted ancestor; unsupported safe primitives must fail closed. Lock release failure must be observable. | `COR-003`, `RSK-002`, `RSK-003` |
| Identifier compatibility | Config/artifacts owners with operators | Inventory deployed spellings before enforcing one opaque-segment grammar; meanwhile no constructor may return an escaped namespace. | `COR-002` |
| Runtime permissions and filesystem-secret directory guarantees | Packaging/operations owner with app/fileops | Use private defaults and no-follow, bounded regular-file acquisition unless a documented service ownership/ACL contract proves a stronger equivalent. | `RSK-004`, `RSK-010` |
| Scriptorium `artifact`/`path` semantics | Product owner and Scriptorium integration owner | Reject non-empty unsupported fields or remove them from the public contract until exact wire semantics are specified. | `COR-024` |
| Remote commit representation, lock activation, and committed restore scope | Publish/restore/artifacts owners with storage capability owner | Preserve the old readable commit until one final atomic selection; restore and status must validate the same selected identity. Do not emulate conditional writes with an unsafe load/replace race. | `COR-004`, `COR-008`, `RSK-005`, `ARC-003` |
| Restore local commit/recovery, content identity, canonical paths, and previous-source mapping | Restore/artifacts/audio/previouscache owners | Keep the manifest last, revalidate under the lock, never report unresolved conflicts as success, and prefer workspace-relative typed identity over foreign absolute paths or size-only reuse. | `COR-009` through `COR-011`, `RSK-006` through `RSK-008` |
| Previous-session expectation and storage backend language | CLI/config/product owner | Preserve the documented flag as a strict expectation unless a compatibility review deliberately changes it; accept only explicitly implemented backend values. | `COR-014`, `COR-015` |
| Remote session configuration lifetime | App configuration owner | Return an owned temporary handle/lifetime whose cleanup occurs after all command consumers finish; never persist ephemeral provenance as a durable path. | `RSK-009` |
| Notification product requirement | Product and integration owner | Reject or clearly reserve non-placeholder settings until a provider contract and production sender exist. A no-op must be an explicit operator choice. | `ARC-004` |
| Process trees, diagnostic redaction, and output size limits | Shared subprocess/adapter owners with platform and operations input | Kill and wait the owned tree, never persist known secret values, and use bounded no-follow regular-file acquisition. Choose limits per external contract rather than one global constant. | `RSK-011` through `RSK-013` |
| External Notarius dependency identity | Notarius integration and operations owners | Continue to require/document `--force` for same-path external/transitive changes unless a version/digest contract is introduced; direct Narratio input bytes must still join the fingerprint. | `COR-020` |
| Validation automation and race cadence | Repository maintainers | Every change and release revision must receive normal test/vet/build validation. Add race automation after `TST-001`; choose per-change versus scheduled cadence from measured capacity, not by omitting the check entirely. | `TST-001`, `TST-012` |
### Intentionally accepted risks
- The two manifest files do not form one atomic transaction. This is accepted
because the session manifest is the sole progress authority and a run
manifest is an invocation audit record. Remediation must test every durable
disagreement boundary and terminalize handled errors; it need not introduce
a distributed transaction.
- An uncatchable process or host death may leave an old invocation record
`running`. After handled errors are terminalized, that residual historical
inaccuracy is accepted provided startup/status can distinguish or document it
and the session manifest continues to drive conservative retry. It is not
acceptable for controlled errors to leave the same ambiguity.
- Restore remains an incremental operation without rollback. The cost and
portability of a full workspace transaction are not justified by current
evidence. This acceptance does not waive `RSK-006`: planning must be
revalidated under the lock, partial state must be diagnosable/retryable, and
the old manifest must not silently authorize replaced bytes.
- Same-path changes to external Notarius executables, profiles, prompts,
modules, references, environment, and provider behavior remain an
operator-forced invalidation boundary. Narratio cannot infer all transitive
external state without a new integration digest contract. Its direct trimmed
transcript is not part of this acceptance and remains `COR-020`.
- A configured artifact that is deliberately non-executable may be reused from
its canonical non-empty file without prior analyze-manifest provenance.
Freshness is operator-owned for that workflow. Explicit selection and
prerequisite planning must still use one effective-set authority under
`ARC-007`/`COR-022`.
- The normal test suite remains serial where process-global fixtures exist; no
blanket `t.Parallel` conversion is justified by its roughly 3.5-second
runtime. Repeated shuffled execution may be scheduled rather than run on
every change, provided `TST-011` is fixed and ordinary CI remains mandatory.
### Evidence limitations
- Dynamic validation ran on Linux/amd64 with Go 1.26.5. macOS and Windows were