Files
narratio/docs/roadmap/implementation.md

392 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Audit Remediation Implementation Plan
## Purpose and status
This document is the executable roadmap for remediating the confirmed findings in
[`audit-findings.md`](audit-findings.md) and the concrete gaps found during the
post-implementation code review. It is written for a `gpt-5.6-terra` coding agent
that will implement exactly one pending stage per prompt, in order.
The audit is an immutable requirements and evidence ledger. Do not edit
`audit-findings.md` or reinterpret its accepted findings. Stages 132 completed
the original audit-remediation plan and are summarized below instead of retaining
their now-obsolete implementation instructions. The repository history and audit
remain the detailed evidence for that work.
Stages 3335 address follow-up review findings in the completed implementation.
They are implementation gaps in the original remediation rather than new entries
in the immutable audit ledger. They do not require the implementing agent to read
the full audit or any audit line range.
## Stage status and completed summaries
| Stage | Outcome or pending work | Source | Status |
| ---: | --- | --- | --- |
| 1 | Aligned architecture policy and filesystem modes with collaborative, group-owned workspaces while preserving private API-key handling. | RSK-004 | Completed |
| 2 | Enforced portable safe-segment identifiers and added path/source fuzz coverage. | COR-002, TST-013 | Completed |
| 3 | Consolidated crash-durable atomic file replacement behind shared file-operation primitives. | RSK-002, DUP-001, DUP-005 | Completed |
| 4 | Added confined, no-follow destination and download/install operations. | COR-003, DUP-003, TST-003 | Completed |
| 5 | Confined recursive cleanup and replaced existence-based sentinel locks with OS-held locks. | RSK-003 | Completed |
| 6 | Made API-key acquisition private, bounded, regular-file-only, and no-follow. | RSK-010 | Completed |
| 7 | Bounded and verified externally produced adapter and stage result files. | RSK-013, TST-007 | Completed |
| 8 | Added owned subprocess-tree cancellation, timeout, and forced termination. | RSK-011 | Completed |
| 9 | Added streaming secret redaction and bounded subprocess diagnostic capture. | RSK-012 | Completed |
| 10 | Confined publish archive reads to validated workspace sources. | COR-005 | Completed |
| 11 | Established singular manifest and run identity ownership. | COR-001, TST-006 | Completed |
| 12 | Centralized handled terminal-failure persistence and removed competing failure paths. | RSK-001, TST-002, SIM-001, COM-001 | Completed |
| 13 | Introduced immutable remote commit manifests, a final current pointer, and an isolated removable legacy reader. | ARC-003 | Completed |
| 14 | Switched publishing to immutable commits, deterministic mappings, and a final pointer update. | COR-004, COR-011, DUP-002, TST-004 | Completed |
| 15 | Made remote locks generation-safe and rejected non-progressing storage pagination. | RSK-005, RSK-014 | Completed |
| 16 | Persisted retryable post-commit cleanup obligations. | COR-006, COR-007 | Completed |
| 17 | Bound restore and status to one verified committed snapshot and rejected conflicting state. | COR-008, COR-009, TST-005 | Completed |
| 18 | Serialized restore transitions, persisted interrupted-restore state, and made restored paths portable. | RSK-006, RSK-008 | Completed |
| 19 | Bound reusable audio cache entries to remote object identity. | RSK-007 | Completed |
| 20 | Unified previous-source readiness and eliminated duplicate transfers. | COR-010, EFF-001 | Completed |
| 21 | Tightened configuration parsing, validation, value domains, and test expectations. | COR-012COR-015, TST-011, TST-014 | Completed |
| 22 | Made product configuration truthful and assigned lifecycle ownership for downloaded temporary files. | COR-024, RSK-009, ARC-004 | Completed |
| 23 | Streamed WhisperX uploads and made its transport bounded, retry-correct, and race-safe. | COR-016, EFF-002, TST-001 | Completed |
| 24 | Corrected prepare/transcribe transition, retry, and optional-output semantics. | COR-017COR-019, TST-008 | Completed |
| 25 | Enforced authoritative output paths and shared singleton resolution. | ARC-006, DUP-006 | Completed |
| 26 | Centralized typed extraction-bundle evidence. | DUP-007 | Completed |
| 27 | Bound extraction reuse to direct transcript identity and explicit freshness evidence. | COR-020, TST-009 | Completed |
| 28 | Established one effective artifact set and deterministic catalog bootstrap behavior. | COR-022, ARC-007, DUP-008 | Completed |
| 29 | Made analyze input resolution typed, optional, actionable, and deterministic. | COR-021, COR-023, RSK-015, SIM-003, TST-010 | Completed |
| 30 | Removed misleading contracts and placed static Audita policy in adapter construction. | ARC-001, ARC-005, SIM-004, COM-003, COM-006 | Completed |
| 31 | Enforced repository-wide CI/release validation and streamlined redundant test matrices. | TST-012, TST-015 | Completed |
| 32 | Reconciled lifecycle/analyze documentation and closed the original audit traceability inventory. | COM-002, COM-005 | Completed |
| 33 | Confine diagnostic log destinations and eliminate pathname-based tail reads. | Follow-up review | Completed |
| 34 | Dispose of owned subprocess descendants after natural leader exit. | Follow-up review | Completed |
| 35 | Bound remote current-state and lock control-plane reads. | Follow-up review | Pending |
Completed stages must not be reimplemented wholesale. A pending stage may adjust
their code only where its stated remediation requires it.
## Governing decisions
The following decisions are settled requirements, not questions for the
implementing agent:
1. Ordinary Narratio and Notarius campaign/session data is deliberately
shareable, not private or sensitive. Group-owned, group-writable workspaces are
an operational requirement. API keys are the only sensitive data handled by
these applications and must remain private.
2. On POSIX systems, Narratio-managed ordinary workspace directories and files
should converge on setgid `02775` and `0664` respectively, inheriting the
workspace's existing group. API-key directories and files must be `0700` and
`0600`. Do not add ownership-changing behavior or assume Narratio may `chown`.
Windows behavior must preserve the same collaboration/security intent using
the platform's available guarantees and explicit operational guidance.
3. Remote current-state publication uses an immutable run-scoped commit manifest
selected by one final pointer update. Readers may accept coherent legacy state,
but all compatibility code must remain isolated behind a clearly named
boundary, covered by dedicated tests, and carry an in-code removal comment.
New writers must never produce the legacy format.
4. Explicit notification no-op is supported. Configured notification backends or
recipients are rejected until a real provider exists.
5. External-data limits are runaway safeguards, not normal operating limits.
Each limit must be a named, centrally discoverable constant in the package that
owns the contract and at least an order of magnitude above expected ordinary
data. Every limit error must identify the enforcing adapter or contract, the
configured byte limit, and what exceeded it. Hard whole-disk protection remains
an operations concern implemented with filesystem, service, container, or
volume quotas rather than output-root polling.
6. Narratio owns the complete process group or job created for every external
command. Leader completion does not transfer ownership of surviving
descendants. `Run` must not return while an owned descendant can continue
running, including when the leader exits successfully or closes its streams.
7. Diagnostic tails are derived only from content that passed through the
streaming redactor. Diagnostic files use the same confined, no-follow mutation
guarantees as other workspace destinations; a pathname must not be reopened to
obtain an error tail after execution.
8. Remote current pointers, commit manifests, selected session manifests, and
lock documents are bounded while being read. Object metadata may reject an
oversized value early but cannot replace enforcement against the actual byte
stream. Limits for these small control-plane contracts are separate from limits
for intentionally large artifact payloads.
9. Retain every accepted-risk disposition in the audit. In particular, do not
turn abrupt process-death detection into a distributed liveness system, add
rollback of arbitrary external side effects, or introduce generic speculative
abstractions rejected by the audit.
10. CI must run ordinary tests, the full race suite, vet, build, and example/docs
checks on every change and release path; shuffled tests run on a schedule. Add
native macOS/Windows jobs only when authoritative Woodpecker runner labels and
usable runners are available. Do not invent labels. Cross-compilation is
useful but does not count as native filesystem or process evidence.
## Instructions for every pending stage
For each implementation prompt, the coding agent must:
1. Read `docs/development.md`, then the current-behavior documents and source
files named by that stage. Follow the task-specific reading guide in
`docs/development.md`. Stages 3335 do not require reading
`audit-findings.md`; do not reread or modify the immutable audit ledger.
2. Inspect the current tree before editing. Earlier stages may have changed names
and ownership boundaries. Use the repository knowledge graph first for code
discovery and call tracing, then text search for documentation, string
literals, configuration, and evidence the graph cannot supply.
3. Confirm the worktree state and preserve unrelated user changes. Do not rewrite
or amend earlier commits unless explicitly instructed.
4. Implement the stage completely, including production code, focused regression
tests, platform-specific implementations where applicable, current-behavior
documentation, and removal of code made obsolete by the stage. Do not leave a
second competing path or defer required tests to a later stage unless this plan
explicitly says so.
5. Prefer typed, owner-specific contracts and existing package ownership. Keep
policy at the owner and reusable mechanism in `internal/fileops`, storage, or
the relevant adapter package. Do not create a generic abstraction solely
because two call sites look similar.
6. Treat path, race, process-lifecycle, and size checks as production behavior.
Validation followed by an unsafe pathname operation or unbounded read is not a
completed fix. Unsupported safe behavior must fail closed with an actionable
error.
7. Run focused tests while developing, then run at minimum:
```sh
go test ./...
go test -race ./...
go vet ./...
go build ./...
go test ./internal/doccheck
go test ./internal/config -run '^TestExamplesLoadAndValidate$'
```
Run the platform build/test checks named by the stage. Do not require live
cloud or third-party services.
8. Compare the final diff with the stage goal and exit criteria. Update only that
stage's row in the status table from `Pending` to `Completed`, adding the
implementing commit hash if the workflow supplies one. Do not mark a stage
complete while a required check is failing or required behavior is missing.
## Stage 33 — Confine diagnostic log destinations and eliminate pathname-based tail reads
**Read first:** `docs/policy/architecture.md`, `docs/policy/testing.md`,
`docs/internal/adapters.md`, `docs/internal/workspace.md`, and the current
subprocess diagnostic and confined file-operation implementations and tests.
**Depends on:** Completed Stages 3, 4, and 9.
**Goal:** Prevent diagnostic log setup from following a replaceable leaf symlink
or mutating a file outside the intended workspace, and ensure error diagnostics
cannot be redirected to read unrelated or secret content after a command runs.
**Work:**
- Replace direct diagnostic `os.Create` use with the established confined,
no-follow destination mechanism. Validate the leaf as part of the mutation,
reject symlink and non-regular destinations, preserve group-workspace modes, and
retain the platform-specific safety guarantees established by Stage 4. A prior
ancestor check followed by an ordinary pathname create is not sufficient.
- Remove pathname reopening from diagnostic-tail construction. Retain a small,
bounded per-stream tail from bytes that have already passed through streaming
redaction, preferably in the diagnostic writer itself, and obtain failure
diagnostics from that retained state after redaction has been flushed. Do not
read the log path again in the error path.
- Preserve stdout/stderr file behavior, shared-path behavior, byte caps, split-
chunk secret redaction, capture-limit signaling, and group collaboration modes.
Do not create an unbounded in-memory copy of either stream.
- Keep API-key values out of persisted logs and every returned error even if a
workspace peer renames or replaces a diagnostic pathname while the process is
running.
- Remove obsolete pathname-tail helpers and update current-behavior documentation
only if the existing documentation describes the replaced mechanism.
**Tests and exit criteria:** Add adversarial tests proving that an existing log
leaf symlink is rejected without truncating its target; a non-regular leaf is
rejected; pathname replacement after the log is opened cannot inject foreign
content into a returned error; and a known credential remains absent from files
and errors across split writes. Cover separate and shared stdout/stderr paths,
exact diagnostic caps, and cap-plus-one behavior. Run focused subprocess and
file-operation tests under `-race`, plus Linux, macOS, and Windows cross-builds for
affected packages. Native platform tests remain conditional on available runners.
**Status:** Completed.
## Stage 34 — Dispose of owned subprocess descendants after natural leader exit
**Read first:** `docs/policy/architecture.md`, `docs/policy/testing.md`,
`docs/internal/adapters.md`, the subprocess run/process-tree implementations for
Unix and Windows, and all process-tree test helpers.
**Depends on:** Completed Stage 8 and Stage 33.
**Goal:** Close the lifecycle gap in which `cmd.Wait` can complete before a
descendant in Narratio's process group or Windows job exits, allowing that
descendant to outlive `Run` after either a successful or failed leader exit.
**Work:**
- Route the normal `cmd.Wait` completion branch through bounded owned-tree
disposal before `Run` returns. Apply the ownership rule for successful exits,
non-zero exits, and `exec.ErrWaitDelay`, not only cancellation, timeout, or
diagnostic-cap paths.
- On Unix, detect and gracefully terminate any remaining process-group members,
wait only for the existing bounded grace period, then forcefully terminate
survivors. Treat an already-empty group as success and avoid imposing the grace
delay on ordinary commands with no descendants.
- On Windows, preserve job-object kill-on-close semantics and ensure natural
leader completion closes/disposes the job before return. Keep handles and
goroutines bounded on every exit path.
- Consolidate terminal cleanup so races among context cancellation, capture-limit
signaling, and leader completion cannot skip disposal or produce unsafe double
ownership. Preserve timeout/cancellation classification and join actionable
cleanup failures without hiding the original command result.
- Treat a descendant that deliberately leaves the owned process group or job as
outside this stage; do not build a system-wide process discovery mechanism.
**Tests and exit criteria:** Extend the real subprocess helper with cases where a
leader exits zero while a descendant (a) retains inherited streams and (b)
redirects or closes them, plus a non-zero leader case. Prove `Run` completes
within bounded time and no descendant reaches a delayed sentinel after return.
Retain cancellation, timeout, and capture-limit coverage and run the subprocess
suite repeatedly, shuffled, and under `-race`. Cross-build the affected packages
for Linux, macOS, and Windows; run native platform tests only where runners are
actually available.
**Status:** Completed.
## Stage 35 — Bound remote current-state and lock control-plane reads
**Read first:** `docs/policy/architecture.md`, `docs/policy/testing.md`,
`docs/internal/storage.md`, `docs/internal/artifacts.md`,
`docs/internal/command-restore.md`, the current-state commit reader, remote lock
loader, object-store contract and implementations, and their stateful fakes and
tests.
**Depends on:** Completed Stages 1317.
**Goal:** Prevent corrupt, misconfigured, or hostile object storage from forcing
unbounded temporary-disk or memory consumption before small remote control-plane
objects can be parsed or verified.
**Work:**
- Inventory the new-protocol read paths for the mutable current pointer, immutable
commit manifest, selected session manifest, and remote lock document. Include
the isolated legacy current-state reader only where it accepts equivalent small
control objects. Do not apply these limits to intentionally large published
artifact payloads.
- Define generous, documented, centrally discoverable constants in the packages
that own each control-plane contract. Use separate policy constants where the
credible sizes differ; do not embed numeric limits at call sites or replace the
owner policies with one unexplained global limit.
- Stream each object through an enforcing `limit+1` read before parsing or
retaining it. Reject oversized metadata before transfer when available, but
still enforce the limit against actual bytes because metadata can be missing,
stale, or inaccurate. Avoid downloading these small objects unboundedly to a
temporary file and then calling `os.ReadFile` or `io.ReadAll`.
- Preserve immutable commit checksum, declared-size, generation/ETag, identity,
and pointer-selection validation. Ensure the metadata used for validation
belongs to the opened object version; do not weaken coherence while removing
the temporary download path.
- Every over-limit error must identify the responsible control-plane contract,
object category, key or safe storage context, and configured byte limit without
including credentials. Maintain cancellation and reader-close behavior on all
exits.
- Factor a bounded storage-read mechanism only if it has a clear storage-layer
invariant; retain the owner-specific constants and errors in the artifact and
remote-lock owners.
**Tests and exit criteria:** Add exact-limit and limit-plus-one cases for each
object category, including inaccurate/absent size metadata, short reads,
cancellation, malformed JSON/YAML, checksum or generation mismatch, and readers
that verify they are closed. Prove oversized current-state downloads do not create
unbounded temporary files and oversized lock documents are rejected without
unbounded allocation. Retain coherent legacy-reader tests and stateful S3/fake
ordering and conditional-write tests. Run focused storage, artifacts, app, restore,
and publish tests under `-race`, followed by the complete repository validation.
**Status:** Completed.
## Finding traceability inventory
This inventory remains the closed traceability record for Stages 132. A pending
follow-up stage may strengthen the implementation of an original stage, but does
not change the immutable finding disposition or create a second audit finding.
| Finding(s) | Primary stage |
| --- | ---: |
| COR-001 | 11 |
| COR-002 | 2 |
| COR-003 | 4 |
| COR-004 | 14 |
| COR-005 | 10 |
| COR-006, COR-007 | 16 |
| COR-008, COR-009 | 17 |
| COR-010 | 20 |
| COR-011 | 14 |
| COR-012, COR-013, COR-014, COR-015 | 21 |
| COR-016 | 23 |
| COR-017, COR-018, COR-019 | 24 |
| COR-020 | 27 |
| COR-021, COR-023 | 29 |
| COR-022 | 28 |
| COR-024 | 22 |
| RSK-001 | 12 |
| RSK-002 | 3 |
| RSK-003 | 5 |
| RSK-004 | 1 |
| RSK-005 | 15 |
| RSK-006, RSK-008 | 18 |
| RSK-007 | 19 |
| RSK-009 | 22 |
| RSK-010 | 6 |
| RSK-011 | 8 |
| RSK-012 | 9 |
| RSK-013 | 7 |
| RSK-014 | 15 |
| RSK-015 | 29 |
| EFF-001 | 20 |
| EFF-002 | 23 |
| ARC-001 | 30 |
| ARC-003 | 13 |
| ARC-004 | 22 |
| ARC-005 | 30 |
| ARC-006 | 25 |
| ARC-007 | 28 |
| TST-001 | 23 |
| TST-002 | 12 |
| TST-003 | 4 |
| TST-004 | 14 |
| TST-005 | 17 |
| TST-006 | 11 |
| TST-007 | 7 |
| TST-008 | 24 |
| TST-009 | 27 |
| TST-010 | 28 |
| TST-011, TST-014 | 21 |
| TST-012, TST-015 | 31 |
| TST-013 | 2 |
| DUP-001, DUP-005 | 3 |
| DUP-002 | 14 |
| DUP-003 | 4 |
| DUP-006 | 25 |
| DUP-007 | 26 |
| DUP-008 | 28 |
| SIM-001 | 12 |
| SIM-003 | 29 |
| SIM-004 | 30 |
| COM-001 | 12 |
| COM-002, COM-005 | 32 |
| COM-003, COM-006 | 30 |
The remaining confirmed IDs have non-independent dispositions and must not receive
separate implementation work: ARC-002 and COM-004 are merged into COM-002;
SIM-002 is merged into DUP-007; DUP-004 is rejected. Their audit rationale remains
authoritative.
## Follow-up review traceability
| Review finding | Remediation stage | Source |
| --- | ---: | --- |
| Diagnostic leaf symlinks and pathname-reopened tails bypass confinement/redaction guarantees | 33 | Post-implementation review |
| Unix descendants survive natural leader exit | 34 | Post-implementation review |
| Remote current-state and lock control objects are read without acquisition limits | 35 | Post-implementation review |
## Open Questions
None. The remediation behavior and ownership decisions are specified above.
Native macOS/Windows runtime validation remains contingent on external runner
availability, but cross-build requirements and the rule against inventing runner
labels make implementation decision-complete.