365 KiB
Codebase Audit Findings
Status: complete
This document is the working ledger and final report for the audit defined by the audit plan and audit sequence. 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.gofiles; - 278 tracked files total;
- one process entry point,
cmd/narratio/main.go, delegating tointernal/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.ymlis 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:generatetest 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
runningclears 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, statusskipped, and reasonalready_succeededwithout copying the reusable outputs; - an executed self-skip stores action
runand statusskippedin 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_erroris 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-001records 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-002remains 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.<key>: 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:
- run files sorted by slash-normalized relative path;
- resolved published outputs in validated configuration order;
- previous-cache files sorted by relative path;
- the fixed
current/manifest.json; and 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, andmaterializeRunLocalOutputalready 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-006assigns 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.PromoteDirectoryestablishes 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:
fileopsshould own atomic replacement and durable installation mechanics; manifest serialization, remote acquisition, validation, conflicts, and reporting remain with their current policy owners;artifactsshould 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.
- Establish safe identity, filesystem, secret, and durable-write foundations:
COR-002,COR-003,COR-005,RSK-002,RSK-004,RSK-010,RSK-012, andRSK-013, together withDUP-001,DUP-003,DUP-005,TST-003, the relevantTST-007cases, andTST-013. This capability must exist before caller-specific restore, cleanup, adapter, or promotion fixes. - Make manifest identity and terminal persistence singular:
COR-001,RSK-001,SIM-001,COM-001,TST-002, andTST-006. Preserve session authority and visible running-versus-terminal save order; do not introduce a generic lifecycle framework or promise cross-file atomicity. - Repair remote publication and postcommit cleanup:
COR-004,COR-006,COR-007,RSK-005,ARC-003,DUP-002, andTST-004. Use one immutable commit model and stateful store tests; retain pointer-last semantics and make cleanup evidence durable/retryable. - Bind restore and previous state to that commit model:
COR-008throughCOR-011,RSK-006throughRSK-009,TST-005, and thenEFF-001. Reuse the safe filesystem capability and current-state truth table rather than creating restore-only variants. - Make configuration and composition truthful before execution:
COR-012throughCOR-016,COR-024, andARC-004, withTST-011andTST-014. Resolve the storage, notification, previous-session, and Scriptorium contract decisions explicitly; reject unsupported values rather than guessing. - Complete transport liveness and bounded resource behavior:
RSK-011,RSK-014,EFF-002,TST-001, and the remainingTST-007cases. Reuse the step-1 output/redaction owner and keep platform process mechanics in the shared launcher. - Correct ordinary-stage and extraction transitions:
COR-017throughCOR-020,ARC-006,DUP-006,DUP-007,TST-008, andTST-009. Preserve explicit stage policy while sharing only artifact resolution and typed extraction evidence. - Unify effective analyze selection and dependency behavior:
COR-021throughCOR-023,RSK-015,ARC-007,DUP-008,SIM-003, andTST-010. CoordinateCOR-024from step 5 rather than inventing wire semantics in this refactor. - 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. - Enforce and streamline the suite:
TST-012, thenTST-014andTST-015. Keep focused owners and representative assembled workflows; add the race job only afterTST-001makes its signal trustworthy. - Apply documentation/comment repairs after their contracts settle:
COM-002,COM-003,COM-005, andCOM-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.goinensureManifestIdentityandsyncRunManifestIdentityFromSession; 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, butLocalWorkDir,LocalSpoolDir, andS3RunPrefixare 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.TestExecuteStagesCreatesRunManifestPerInvocationproves 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.gocanonical constructors,internal/artifacts/s3_keys.goinS3SessionPrefix, andinternal/config/validate.goinvalidateCampaign,validateSession, andvalidateSessionIdentifier. 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../../outsidetherefore changes the cleaned local workspace/spool destination and the S3 namespace.EnsureLayoutForalso validates only presence before creating the derived directories. The exportedSessionPreviousArtifactPath*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-001separately owns conflicts among otherwise valid identities;COR-003owns 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 ininternal/fileops/fileops.go, destination setup ininternal/fileops/directory.go, local layout/copy operations ininternal/artifacts/local.go, and cleanup validation/removal ininternal/app/cleanup_targets.go,clean.go, andpost_publish_cleanup.go. Writes, replacements, promotions, and deletions must remain beneath an explicit root despite symlinks or replacement races. - Evidence: lexical
Relchecks cannot observe filesystem links. File writers callMkdirAll/CreateTemp/Renamethrough destination paths. Promotion strongly confines and identity-checks its source, but only path-checks the destination parent before creating/installing the sibling tree. CleanupLstats only the final target and then callsRemoveAll; it neither rejects a symlinked root/ancestor nor holds a confined directory handle. A target such asroot/campaign/session, wherecampaignis 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 sessionor 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
fileopspolicy-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-002owns 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.goupload order andinternal/artifacts/current_state.goinLoadCurrentState. 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 uploadscurrent/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-001can 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.goincollectPublishRunFilesandcollectPublishPreviousFiles, plusinternal/adapters/storageupload implementations. Run and previous archives must be confined to the enumerated local trees and contain eligible regular files only. - Evidence: both
WalkDircollectors append every non-directory entry without rejecting symlinks or other special files. Storage then opens the recorded path (os.Openin the S3 backend and file reads in the fake), which follows a symlink final component. The run-manifest check also usesos.Statand 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/<id>/logs/debug.logas 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
Lstatonly. Existing exact exclusion/order tests should remain. - Dependencies:
COR-003owns symlinked ancestors for mutation and cleanup; this finding is distinct because it is a publish read/exfiltration boundary.RSK-004affects 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.goaround terminal publish saves andrunPostPublishCleanup, andpost_publish_cleanup.goinpublishStageRecordForCleanup. An enabled cleanup policy should remain recoverable after a committed publish until its requested cleanup succeeds. - Evidence: cleanup requires
publishto appear in the current invocation'sexecutedlist. 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 manualcleanis 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-002notes that run-save boundaries are not injectable;COR-001andCOR-003still govern target identity/confinement.COR-007separately 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.goin theworkRequestedsuccess 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, andcleanup_skippedonly 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-006and this finding together avoids inventing two competing cleanup state machines.RSK-002remains 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, andrestore_execute.go; shared validation ininternal/artifacts/current_state.go; previous object selection ininternal/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 everytranscripts/**andartifacts/**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 withValidateRunID=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-004is the publish-side fixed-pair root; both should share one protocol repair.COR-011separately 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.goinclassifyRestoreAction,restore.goconflict gating, andrestore_execute.godownload 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 onlydownloadactions 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.mdis 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 hocRemoveAllin restore.
COR-010: status and validation do not verify previous-artifact readiness
- Category: confirmed correctness/operator defect.
- Locations/invariant:
internal/app/operator_inspection.goininspectPreviousArtifactReadiness,operator_status.go, andoperator_session_validate.go; canonical planning ininternal/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_idis nonempty and its remote current pair validates. It reports every requirement ready without resolving candidate keys or callingExists. It also marks a missing ID as unavailable/error whenever any previous requirement exists, althoughBuildPlancorrectly 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 statussays ready andsession validatesucceeds; 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-011must be fixed for readiness to recognize every valid custom destination.COR-008owns 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.goinartifactRelativePathCandidates,manifestPublishedPaths, and candidate selection; publish metadata ininternal/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_pathslist, 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_recapis produced atartifacts/session_recap.mdand intentionally published ashistory/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.goindecodeStrictYAMLFromReader, 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
extraand 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.goinvalidateDurationand 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 validateor plan reports a deployment valid withwhisperx.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.goinvalidateStorageand cross-config S3 checks;internal/adapters/storage/factory.goinNewObjectStoreFromConfig. 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 backends3, 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: s33with 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.gosession options;internal/config/load.goinLoadSessionBytesWithOptions. 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_idare non-empty. If the session omits the optional field, a supplied--previous-session-idneither 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_idpasses 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-011owns 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.goinNewHTTPClientand 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/transcribepass, 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
ftpor 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
httpandhttpsafter 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-013separately 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.goinprepareStage.RunandclearManagedPreviousState, plusinternal/stage/publish.goprevious-file collection. Prepare must make managedprevious/**state match the current requirement set, and publish must not expose bytes excluded from that set. - Evidence: prepare calls
clearManagedPreviousStateonly insidelen(previousRequirements) > 0. With zero current requirements it replacesmanifest.inputswithout previous records but leaves the directory intact.TestPrepareStageWithoutPreviousRequirementsDoesNotTouchPreviousStateexplicitly 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-011owns source identity for requirements that remain;COR-003governs 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.goworker, dispatcher, and completion decision;internal/app/runner.gosuccess 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 onstageCtx.Done(). After waiting, the coordinator checks onlyfirstErr; 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-002andRSK-011govern transport/process cancellation latency; this finding owns worker aggregation after cancellation.TST-001separately 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, anddiscoverPreparedAudio. One accepted audio selection must have a consistent identity from configuration through prepared manifest consumption. - Evidence: configuration validation accepts any non-empty
audio_filesslice 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 andvalidateAudioFilesrejects 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-002owns unsafe identity segments andRSK-008foreign 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.goinextractionFingerprintand result metadata construction, andinternal/stage/extract_resume.goinValidateResume. 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_trimmedand 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-008concerns foreign restored absolute paths generally; this finding owns direct extraction-input identity even when the path is canonical.DUP-007/SIM-002must 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.goinresolveScriptoriumInput. Every Scriptorium input withrequired: falsemust be omitted when its valid source is unavailable, independent of source family;required: trueowns source-specific failure and repair guidance. - Evidence: after the catalog resolver returns
ErrSessionArtifactNotFound, extraction and configured branches consultinputCfg.Required, while polished and default built-ins return unresolved for the caller to apply the flag. The cases fornarratio.transcript.final,final_trimmed,final_markdown, andfinal_trimmed_markdowninstead 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-003only 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.goinRegisterConfiguredArtifacts,internal/artifacts/previous_requirements.goinCollectPreviousArtifactRequirements, 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-007chooses 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-010concerns readiness checks for requirements that were collected; this finding owns requirements omitted by selection.ARC-007owns the product choice, andTST-010owns regression placement.
COR-023: required previous-input failure recommends an invalid command
- Category: confirmed correctness/operability defect.
- Locations/invariant:
internal/stage/analyze.goinresolveScriptoriumInput, with syntax owned bydocs/cli.mdandinternal/appcommand 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 isnarratio run-stage prepare <session_id> --force; the emitted form places a flag where the required stage argument belongs, reverses the stage/flag order, adds an extraneous leadingrun, and omits the session ID. The existing test checks only the fragmentrun-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 <session_id> --force, or route producer guidance through a small command formatter ifSIM-003establishes one. Include the session ID already available inpaths. - 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-022can 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.goinScriptoriumInputConfig,docs/config.mdin Scriptorium artifact entries,internal/stage/analyze.go, andinternal/adapters/scriptorium. Accepted and documented operator fields must affect behavior or be rejected/reserved explicitly. - Evidence: the strict schema accepts
artifactandpath, and configuration documents both as optional passthrough adapter fields. Graph-augmented exact searches find no read ofinputCfg.ArtifactorinputCfg.Path. Resolution consumes onlySourceandRequired; 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
artifactandpathcombine with canonicalsourceresolution. - Test changes: once authority is chosen, assert end-to-end adapter invocation semantics or strict rejection. A decode-only test is insufficient.
- Dependencies:
COM-005owns other analyze documentation gaps.ARC-007concerns 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.goinexecuteStages,internal/manifest/store.gonormalization, and the unused productionStatusInterruptedmodel 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.StatusInterruptedhas 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
runningforever. 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-001may provide one failure-finalization path andTST-002records 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 ininternal/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
InstallDownloadedTempFilesyncs 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-001is 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.golock acquisition/release, the ignored deferred release ininternal/app/runner.go, and the manual stale lock procedure indocs/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_EXCLcorrectly serializes live contenders, but any existing.lockconflicts without checking whether its recorded PID/time is live. Process death leaves the file forever.ReleaseSessionLockcan 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-001covers 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 indocs/policy/architecture.md. Transcripts, prompts, artifacts, reports, logs, and manifests are private campaign material. - Evidence: runtime directories request
0755and files request0644, 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 to0644and directories to0755. - Realistic scenario: a service or operator runs with the common
0022umask 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.goremote-lock load order,remote_locks.go,operator_locks.go, and unconditionalstorage.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-003concerns only the local single-writer sentinel and cannot serialize other hosts or lock commands.COR-004may 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, andrestore_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_sameor 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-003governs stale local lock recovery;COR-003andRSK-002govern confined and crash-durable installation.COR-008is 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.goaudio classification andinternal/audio/s3_audio.gocache 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_samewithout a body/checksum/ETag comparison, even under force. Cache validity accepts any nonempty non-directory path and, when available, equal size. AlthoughObjectInfo.ETagis 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-003owns 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 ininternal/manifest, and manifest-first resolution ininternal/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 triggerCOR-001on 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-001tests for top-level restored identity. - Dependencies:
COR-001owns stale top-level run identity;COR-003owns 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.goinloadCommandConfigandinternal/adapters/storage/temp_download.goinDownloadObjectToTemp. 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.SessionPathandSessionSource. No caller removes it. Full execution later copies it to canonicalinputs/session.ymlyet 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-004remains 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.goinloadSecretsFromConfig. 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.ReadFileon the joined path. It follows a validly named symlink and accepts any other non-directory entry thatReadFilecan 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_TOKENlinked 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-003owns 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.CommandContextwithout changing process attributes orCmd.Cancel. In the audited Go toolchain that cancel function callsKilloncmd.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 throughos.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-010owns safe secret-file acquisition; this finding owns propagation after values enter the process.RSK-004owns 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, SeriatimvalidateJSONFile/validateTranscriptFile/render validation, Scriptorium output checks, andinternal/contractsbounds/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.ReadFileor link-followingos.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 usesLstat, 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 byvalidateTranscriptJSONFile,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'schecksumRegularFileperforms one unboundedos.ReadFileper lane to parse and hash it, while catalog hydration first streams the checksum and then performs another unboundedos.ReadFilefor 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-003and 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
IsTruncatedis 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.goinorderSelectedScriptoriumArtifacts. 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-003may 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.gochecksum classification,internal/previouscache.BuildPlan, andrestore_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.DryRunis 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-008remote 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
nsame-size non-audio objects with aggregate body sizeB, planning performsnfull downloads and readsBremote plusBlocal bytes for checksums. A forced apply of differing bodies then downloads up to anotherB; 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-008so 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.doTranscribeAttemptand the transcribe stage's configured concurrent use ofClient.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
cand average audio sizes, request construction retains approximatelyO(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 atO(c)while total network I/O remains necessarilyO(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-018separately 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.gonotifier composition, andinternal/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.NoopSenderwhen 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
DeclaresandIODeclfrom the runtimeStageinterface 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.Declareslists files it produces underInputs;analyze.Declaresomits dynamic input families and has no outputs;publish.Declaresexposes only the manifest; andnotify.Declaresadvertises 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 ofDeclareswas 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:StageDispositionSkippedmeans 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
StageDispositionSkippedwas 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-002owns 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-004owns 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; broadenedCOM-002now 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.jsonbefore the commit marker, sopublishMetadataPreviewrecordscurrent_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.
PolishRequestshould retain invocation paths and the genuinely per-run module override only. This matches the production runner and removes fake-only apparent overrides. - Evidence:
PolishRequestand its integration document carry base URL, model, transcript description, config path, output schema, work-dir retention, and concurrency values.SubprocessRunner.Runignores those request fields and builds arguments, generated config, and metadata from constructor state; only requestModulescan 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,
enableddefines 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
enabledauthoritative 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, whiledocs/internal/artifacts.mddefines executable as both selected and enabled. Publish uses selection as a filter over configured rules but availability ignores enabled state. - Contract tension:
enabledcan mean default execution, complete executable configuration, prerequisite participation, or publication availability, depending on the boundary.selectedcan mean an override or a filter. No single effective-artifact-set owner states which interpretation is canonical. - Realistic risk:
COR-022is 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:45from transcribe workers inTestTranscribeStageTranscribesPreparedAudio. - 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
Requestswithout 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.ManifestStoreinjects only sessionCreate,Load, andSave;executeStagesconstructs a concretemanifest.LocalStorefor run creation and everySaveRun. 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
SaveRuncall 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
fileopscapability while preserving the session save’s context checkpoint and caller-specific error prefix. Coordinate this withDUP-005and the directory-durability correction inRSK-002; do not create a manifest-generic persistence interface. - Evidence:
LocalStore.Savecontains its own temp/create/write/sync/close/ context-check/rename sequence, whileSaveRundelegates the same mechanism towriteJSONAtomically. Error prefixes differ, but the durability mechanism is otherwise repeated. Stage 3 confirmed that both copies omit the same parent-directory sync required byRSK-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.jsonspelling explicit as a publication protocol constant; only local canonical path construction moves to its existing owner. - Evidence:
internal/stage/publish.goinresolvePublishRunManifestSourcejoins the literalmanifest.jsonto an already-derived run root, whileinternal/artifactsownsSessionRunManifestPath*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
downloadObjectToSiblingTempplusInstallDownloadedTempFile, audio'sdownloadObjectAtomic, and prepare's previous-cache loop each create a sibling directory/temp file, close it, download throughObjectStore, clean failure, and rename-install. The callers legitimately differ in conflict, cache, content-validation, input-record, and report policy.storage.DownloadObjectToTempis a separate system-temp inspection primitive. All install variants inheritRSK-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-002rather 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, includinga/../b, whilepathsafe.NormalizeRelativeDestinationaccepts 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.validateRelativeSafePathindependently checks absolute paths, cleaned dot/traversal forms, and separators already represented byinternal/pathsafeprimitives, 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-002and 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-001family. Consolidate byte replacement infileops, 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.WriteFileAtomicand WhisperX's unexportedwriteFileAtomicindependently 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/fileopsowns a third equivalent mechanism for application files. All three omit the directory sync identified byRSK-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, andRSK-002rather 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.ResolveSessionArtifactidentities, 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, anddiscoverNormalizedTranscripteach 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.
artifactsshould 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.ValidateResumeandArtifactCatalog.HydrateExtractionArtifactsindependently 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/artifactsevidence 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 intofileops.
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, andbuildHelperArtifactCatalogeach 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-007but 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, andRSK-003mechanisms 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 toCOR-005orRSK-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-008throughCOR-011andRSK-006throughRSK-008can 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-004for 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:
executeStagesreceives both a resolvedcfgargument and an optional injectedstage.Env. It assignsenv.Config = cfgonly when the injected field is nil. With different non-nil values, layout, selected artifacts, and default stores derive fromcfg, while secrets, adapter defaults, locks, and stage execution consumeenv.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-011throughRSK-014, andEFF-002can 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-017throughCOR-019,ARC-006, and the stage-side reach ofRSK-013can remain or receive partial fixes while broad normal stage tests pass. The existing race command is also unusable untilTST-001is 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-020is 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-013lane bound can be incomplete while all current extraction/lifecycle tests pass. Conversely, copying every adversarialfileopscase 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-002and destination ancestry withTST-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-002andTST-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/pathfields. The previous-guidance test asserts only the malformed command fragment. - Realistic risk: fixes for
COR-021throughCOR-024,RSK-015, andARC-007can 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:
TestLoadSecretsFromConfigLoadsValidFilesinvokes the production loader, which setsNARRATIO_TEST_SECRET_AandNARRATIO_TEST_SECRET_B, but the test does not restore their prior state.go test -shuffle=on -count=3 ./...failed with seed1786373771816345415; 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.ymlis tag-only and cross-builds/publishes binaries. No repository automation runsgo 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-001is 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
pathsafeandartifactpolicyboundaries 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.TestLoadAndValidateis 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:
TestStagesReturnExpectedMetadatais a broad per-stage metadata checklist that overlaps focused stage suites and incidental metadata, whileTestAdapterBackedStageFailureMarksManifestFailedrepeats six adapters to prove the runner's generic terminal-failure mapping. Similar broad success assertions exist inTestExecuteStagesPlaceholderSuccessUpdatesManifest. - 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:
executeStagesis 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 inValidateResume. A second abstraction would duplicate that owner. - Evidence:
extractStage.ValidateResumespans 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-007should 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 keepValidateResumeas 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:
executeAnalyzeArtifactspans 239 lines and accepts ten parameters for environment, manifest, session paths/identity, run layout, plan, transcript diagnostics, and mutable catalog state.resolveScriptoriumInputadds six parameters and 84 lines/cognitive complexity 71;inputNameandsessionDirare unused there. It interleaves family dispatch, availability, required/optional decisions, previous-cache provenance, and operator guidance.orderSelectedScriptoriumArtifactsseparately rebuilds selected sets and graph indexes.resolveInputPathForReadhas 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, andapp.resolveSessionConfigPathonly at their definitions. Their active replacements are source-family resolution, the publish session-path model, andresolveSessionConfigPathWithCandidates. 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
internalpackages; 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
runningand 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-002andCOM-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 explicitStageDispositionSkipped, explain reconsideration, and describe each no-output success directly. - Evidence:
docs/internal/stage-publish.mdsays 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
publishcommand 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-005leaves 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.gocarries a TODO to implement a real subprocess/service adapter, whileSubprocessRunneris 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.mdsays 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
StageDispositionSkippedand unintentionally alters downstream lifecycle behavior. - Merge boundary: broadened
COM-002describes 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 broadenedCOM-002. Do not document the inertartifact/pathfields as working passthrough untilCOR-024is repaired. - Evidence:
docs/internal/stage-analyze.mdlists built-in, prepared, configured, and previous-session sources but omits supportednarratio.extraction.<name>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.mdsays executable means selected and enabled even though current selection overrides enabled; that authority question remainsARC-007rather 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
analyzecommand 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. BroadenedCOM-002owns successful no-output persistence and force wording. Do not describe the inertartifact/pathfields untilCOR-024chooses implement versus reject.
COM-006: source-classification comment omits extraction sources
- Category: confirmed stale comment.
- Evidence:
artifactpolicy.ClassifySourcesays it classifies built-in, configured, or previous-session configured IDs, but its production branch also recognizesnarratio.extraction.<name>and returnsSourceKindExtraction. - 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_controland the notification placeholder: the former accurately records the explicit force/freshness limitation, and the latter accurately identifies the missing transport already owned byARC-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.Parallelconversion is justified by its roughly 3.5-second runtime. Repeated shuffled execution may be scheduled rather than run on every change, providedTST-011is 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
Executecall 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/appwas 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_errorusages; runner complexity and atomic-save similarity; complete focused test-function inventory. - Validation:
go test -count=1 ./internal/app ./internal/manifestpassed (internal/app0.708 s,internal/manifest0.010 s; 1.60 s command wall time).go test -race -count=1 ./internal/app ./internal/manifestpassed (internal/app45.842 s,internal/manifest1.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-001andRSK-001; addedDUP-001,SIM-001,COM-001, andTST-002for 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/fileopspassed (0.98 s wall time).go test -race -count=1 ./internal/artifacts ./internal/fileopspassed (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, andRSK-004; addedDUP-002andTST-003; refinedDUP-001with 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/storagepassed (internal/stage0.440 s,internal/app0.749 s,internal/artifacts0.028 s,internal/adapters/storage0.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, andRSK-005; addedARC-003,COM-002, andTST-004; resolved publish's portion ofARC-002and 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/storagepassed (internal/app0.759 s,internal/previouscache0.009 s,internal/audio0.012 s,internal/artifacts0.025 s,internal/adapters/storage0.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, andEFF-001; addedDUP-003andTST-005with 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/narratiopassed (internal/config0.099 s,internal/app0.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, andRSK-010; addedARC-004,DUP-004, andTST-006with 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-004assigns 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, andinternal/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
CommandContextdocumentation/source confirmed direct-process kill behavior. - Validation:
go test -count=1 ./internal/adapters/... ./internal/audio ./internal/logging ./internal/contracts ./internal/artifactmodelpassed (all 13 package results passed; 1.62 s command wall time).go test -race -count=1 ./internal/adapters/... ./internal/audiopassed (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 candidateARC-004; addedARC-005,DUP-005,COM-003, andTST-007, and refinedTST-001as 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, andCOM-003; Stage 12 owns the risk-based additions/consolidation inTST-001andTST-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/audiofailed ininternal/stageat the already registered WhisperX fake request-slice race fromTestTranscribeStageTranscribesPreparedAudio;internal/audiopassed (1.85 s command wall time). This is the expectedTST-001limitation 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, andCOR-019; addedARC-006,DUP-006,COM-004, andTST-008; refinedARC-001,ARC-002,ARC-005,RSK-013, andTST-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-002outcome. Stage 11 ownsARC-001,ARC-005,ARC-006,DUP-006,COM-004, and the narrow shared-owner decisions. Stage 12 owns the risk-based additions inTST-001,TST-007, andTST-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.
ValidateResumemeasured 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; addedDUP-007,SIM-002, andTST-009; and refinedARC-001andRSK-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, andSIM-002. Stage 12 owns the risk-based additions/consolidation inTST-009and the extraction reach ofRSK-013alongsideTST-002,TST-003, andTST-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, andRSK-015; addedARC-007,DUP-008,SIM-003,COM-005, andTST-010; refinedARC-001,ARC-002, andRSK-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, andCOM-005, coordinated with earlier candidates. Stage 12 owns the risk-based additions/consolidation inTST-010and the analyze reach ofRSK-013alongside 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, andCOMentry, 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.BuildPlan22/38,HydrateExtractionArtifacts17/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, andgo vet ./...passed in 0.46 s. No benchmarks exist, so no invented performance result is reported;EFF-001andEFF-002specify 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
IODeclinterface, 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, andARC-005throughARC-007;DUP-001throughDUP-003andDUP-005throughDUP-008;SIM-001andSIM-003; andCOM-001throughCOM-003, the source/catalog portion ofCOM-005, and newSIM-004/COM-006are confirmed.ARC-002,COM-004, and the no-output portion ofCOM-005merge into broadenedCOM-002;SIM-002merges intoDUP-007;DUP-004is rejected after exact semantic comparison.EFF-001,EFF-002, andARC-004remain 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
TSTinventory 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
TSTcandidates, and the repository's test/example/automation inventory. - Graph/source evidence: 904 test-file functions, 749
Testfunctions, 95 test files, no fuzz tests, no benchmarks, not.Paralleluse, 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/narratioreported 0% because it has no tests. Percentages were used only to revisit weak consequential branches and high-coverage duplication. Manifest's low end aligns withTST-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 seed1786373771816345415becauseTestLoadSecretsFromConfigLoadsValidFilesleaked two environment values. The isolated same-seed, same-test, three-count command reproduced failure on repetitions two and three, confirmingTST-011rather than random flakiness. - Race validation:
go test -race -shuffle=on -count=1 ./...failed in 54.21 s with seed1786373816980315094. The only race was the already recorded WhisperX fake request-slice mutation inTST-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 andgo vet ./...passed in 0.78 s.git diff --checkreported 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-001throughTST-008andTST-010; confirmedTST-009after rejecting its low-value orphan-residue subcase; and added confirmedTST-011throughTST-015for 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-002andCOM-004merge intoCOM-002;SIM-002merges intoDUP-007;DUP-004remains rejected;COM-005remains independently confirmed only for its source/catalog omission while its no-output wording is owned byCOM-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, andaudit-sequence.mdresolve;git diff --checkpasses. 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.