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