From 0b869af75ecac255b6b5c7cae0ea09c288dfa9dd Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Thu, 13 Aug 2026 00:00:59 +0000 Subject: [PATCH] Add an implementation plan to address the audit findings --- docs/roadmap/audit-remediation.md | 603 -------------- docs/roadmap/audit.md | 4 +- docs/roadmap/implementation.md | 1292 ++++++++++++++++++++--------- 3 files changed, 904 insertions(+), 995 deletions(-) delete mode 100644 docs/roadmap/audit-remediation.md diff --git a/docs/roadmap/audit-remediation.md b/docs/roadmap/audit-remediation.md deleted file mode 100644 index 85fd6b2..0000000 --- a/docs/roadmap/audit-remediation.md +++ /dev/null @@ -1,603 +0,0 @@ -# Repository Audit Remediation Roadmap - -Status: Ready for implementation. - -## Purpose And Authority - -This roadmap translates the confirmed findings in the -[repository audit ledger](audit.md) into dependency-ordered implementation -stages. The ledger owns discovery evidence and final finding disposition. This -document owns remediation scope, ordering, locked behavior, verification, and -completion state until the work is finished. - -All 74 confirmed findings are accepted for remediation. None is priority- -deferred and none requires a separate product or architecture decision before -work can begin. Four rejected standalone findings remain consolidated into -their confirmed owners and are not separate work items. - -This roadmap describes future work. Until an individual stage is implemented, -canonical current-state documentation and executable behavior remain -authoritative. - -## Implementation Rules - -Every stage must: - -- read `docs/development.md`, all files under `docs/policy/`, this roadmap, and - the task-specific canonical documents identified below; -- implement only its listed findings and prerequisites; -- preserve the architecture policy's stateless pipeline, adapter boundaries, - curated prompt boundary, operator-owned outputs, comparison ordering, and - notification rules unless the stage explicitly corrects that contract; -- add durable tests at the narrowest stable boundary and avoid duplicating - behavior already owned elsewhere; -- update every listed canonical documentation owner in the same change as an - implemented contract change; -- use deterministic, offline, credential-free tests and synthetic fixtures; -- run `gofmt` on changed Go files, the stage's focused commands, - `GOWORK=off go test -count=1 ./...`, and `git diff --check`; and -- commit the completed stage separately when the implementing prompt requests - commits. - -Stages affecting concurrent execution, cancellation, filesystem mutation, or -process-global state must also run the affected packages with `-race`. No stage -may weaken a safety invariant merely to make a test pass. - -## Priority And Finding Allocation - -Each confirmed finding appears exactly once below. Priority describes -remediation order, not confidence; every listed finding has high confidence. - -| Priority | Stages | Findings | Decision | -| --- | --- | --- | --- | -| P0: generated forecast integrity | 1 | `AUD-029`, `AUD-030`, `AUD-031`, `AUD-033` | Accepted now | -| P1: security, correctness, and data integrity | 2-22 | `AUD-002` through `AUD-021` excluding rejected `AUD-022`; `AUD-023` through `AUD-028`; `AUD-034` through `AUD-038`; `AUD-040` through `AUD-058`; `AUD-073`, `AUD-074`, `AUD-077`, and `AUD-078` | Accepted now | -| P2: deterministic test and operational quality | 23-27 | `AUD-059` through `AUD-062`, `AUD-064`, `AUD-065`, and `AUD-066` | Accepted now | -| P3: behavior-preserving consolidation and cleanup | 28-32 | `AUD-039`, `AUD-067` through `AUD-072` | Accepted now | -| P4: documentation lifecycle and audit retirement | 33-35 | `AUD-075` and `AUD-076`; final audit-record retirement | Accepted now | - -Rejected `AUD-001`, `AUD-022`, and `AUD-032` are represented only through -`AUD-072`; rejected `AUD-063` is represented only through `AUD-039`. - -## Remediation Stages - -### Stage 1: Correct The Curated Prompt Package Contract - -- Findings: `AUD-029`, `AUD-030`, `AUD-031`, `AUD-033`. -- Affected areas: `internal/promptinput`, shared embedded prompt assets, and - focused prompt-input/prompt-asset tests. -- Work: remove raw transport-shaped warning details from provider input; make - shared prompt paths match serialized YAML; describe regional versus point- - matched hazard products accurately; and cover stanza serialization failure. -- Locked behavior: deterministic category/stanza order, exact prompt identity, - safe normal diagnostics, and byte-stable valid YAML remain unchanged. -- Documentation owners: `docs/internal/prompt-input.md` and - `docs/integrations/promptkit.md` if their implemented contract changes. -- Exit commands: focused tests for `./internal/promptinput` and - `./internal/promptassets`, then the standard repository commands. - -### Stage 2: Make Configuration Validation Atomic And Explicit - -- Findings: `AUD-004`, `AUD-006`, `AUD-007`, `AUD-078`. -- Affected areas: `internal/config`, report-key lookup, timezone parsing, - maintained examples, and configuration tests. -- Work: reject unsupported missing-source keys; make programmatic report - overrides obey the same presence semantics as loaded configuration; apply - secrets only after the complete load succeeds; and document accepted report- - key normalization and numeric-offset bounds. -- Locked behavior: file/CLI precedence, inactive-integration policy, exact - command-name lookup, and secret redaction remain unchanged. -- Documentation owners: `docs/config.md` and maintained examples only where - executable behavior requires them. -- Exit commands: `GOWORK=off go test -race -count=1 ./internal/config - ./internal/cli`, followed by the standard repository commands. - -### Stage 3: Require Complete Single-Report Notification Identity - -- Findings: `AUD-005`. -- Affected areas: Distributor notification configuration, bundle-ID rendering, - and focused config/app tests. -- Work: reject enabled single-report notification configurations that can - produce blank required identity fields before report work begins. -- Locked behavior: inactive notification settings remain optional; configured - templates and publication-before-notification ordering remain intact. -- Documentation owners: `docs/config.md`, `docs/operations.md`, and Distributor - integration docs if accepted values or failures change. -- Exit commands: focused config and app notification tests, then the standard - repository commands. - -### Stage 4: Correct CLI Cancellation, Preflight, And Visible Contracts - -- Findings: `AUD-008`, `AUD-009`, `AUD-010`, `AUD-074`. -- Affected areas: `cmd/weatherreporter`, `internal/cli`, assembled CLI tests, - help text, and CLI documentation. -- Work: propagate process interrupts through action contexts; validate report - dates before executor construction; add representative generate and batch - result/exit tests; and align help/internal guidance with quiet, date, and - comparison behavior. -- Locked behavior: command/flag ownership, structured summary shapes, stream - separation, and one executor per action remain unchanged. -- Documentation owners: `docs/cli.md` and `docs/internal/cli.md`. -- Exit commands: `GOWORK=off go test -race -count=1 ./internal/cli - ./internal/app ./cmd/weatherreporter`, both root and comparison help commands, - then the standard repository commands. - -### Stage 5: Harden The Weather API Transport Boundary - -- Findings: `AUD-011`, `AUD-012`, `AUD-013`, `AUD-014`, `AUD-015`. -- Affected areas: Weather API configuration/adapter, normalized hourly data, - local HTTP fixtures, and collection tests. -- Work: accept only supported endpoint forms; bound and redact non-success - diagnostics; reject rather than truncate oversized bodies; require usable - hourly time bounds; and retry warmup only for transient failures. -- Locked behavior: all eight source contracts, checked-empty optional products, - cancellation, provenance, and offline testing remain intact. -- Documentation owners: `docs/config.md`, - `docs/integrations/weatherapi.md`, `docs/internal/weather-data.md`, and - `docs/internal/collect.md` as applicable. -- Exit commands: focused adapter/collection tests including boundary-size and - cancellation cases, then the standard repository commands. - -### Stage 6: Correct Civil-Time And Forecast Derivation Boundaries - -- Findings: `AUD-002`, `AUD-003`, `AUD-016`, `AUD-017`, `AUD-018`, `AUD-019`. -- Affected areas: `internal/timeutil`, `internal/report`, `internal/forecast`, - `internal/facts`, and their focused tests. -- Work: construct configured clocks as local civil times across DST; protect - Daily run-ID uniqueness; preserve units in fallback values; include the full - overnight alert interval; reject invalid precipitation percentages; and - classify ordinary icy wording consistently. -- Locked behavior: half-open selection, chronological ordering, rolling Hourly - elapsed time, and source-period identity remain unchanged. -- Documentation owners: `docs/internal/report-registry.md`, - `docs/internal/forecast-derivation.md`, and `docs/internal/facts.md`. -- Exit commands: focused time/report/forecast/facts tests with both DST - transitions and metric cases, then the standard repository commands. - -### Stage 7: Correct Source-Facing Briefing Module Semantics - -- Findings: `AUD-020`, `AUD-021`. -- Affected areas: briefing module option validation, weather-story construction, - module registry tests, and normalized source fixtures. -- Work: make accepted AFD option shapes constructible and omit semantically - empty weather stories instead of publishing false availability. -- Locked behavior: report support, default compositions, checked-empty alerts, - and ordered module snapshots remain unchanged. -- Documentation owners: `docs/internal/module.md` and - `docs/internal/briefing.md`. -- Exit commands: focused briefing/module tests, then the standard repository - commands. - -### Stage 8: Correct Derived Briefing Language And Ranking - -- Findings: `AUD-023`, `AUD-026`, `AUD-027`. -- Affected areas: derived daily summaries, outdoor-window scoring, temperature - band wording, and briefing tests. -- Work: label apparent temperature accurately; include snow, ice, and fog in - outdoor-window ranking; and preserve negative-zero temperature meaning. -- Locked behavior: deterministic ranking, Today-only planning scope, and - existing module/schema identities remain unchanged. -- Documentation owners: `docs/internal/briefing.md` and prompt-facing field - documentation if names or meaning change. -- Exit commands: focused briefing tests with metric, subzero, and hazard cases, - then the standard repository commands. - -### Stage 9: Establish One Daypart Canonicalization Mechanism - -- Findings: `AUD-067`. -- Affected areas: duplicated canonical-key helpers in briefing and - generated-text packages plus their behavior tests. -- Work: establish one domain-owned canonical key contract and route both - current consumers through it without changing accepted ASCII behavior. -- Locked behavior: display labels, report-specific semantic roles, ordering, - and current valid output remain unchanged. -- Documentation owners: focused internal briefing/generated-text docs only if - ownership needs explanation. -- Exit commands: focused briefing and generated-text tests, then the standard - repository commands. - -### Stage 10: Correct Daypart Identity, Collision, And Display Behavior - -- Findings: `AUD-024`, `AUD-025`, `AUD-073`. -- Affected areas: derived daypart summaries, planning builders, display - capitalization, configuration fixtures, and rendering tests. -- Work: reject canonical-key collisions; use configured identities consistently - across planning; and make non-ASCII display capitalization rune-safe. -- Locked behavior: Stage 9's canonical identity remains separate from display - text; stable ordering and default dayparts remain unchanged. -- Documentation owners: `docs/config.md`, `docs/internal/briefing.md`, and - generated-text/template docs if visible labels change. -- Exit commands: focused config/briefing/generated-text/reporttemplate tests, - then the standard repository commands. - -### Stage 11: Refresh The Official SPC Definition Asset - -- Findings: `AUD-028`. -- Affected areas: embedded SPC category definitions, source attribution, and - risk-digest tests. -- Work: reconcile descriptions with the current authoritative SPC table and - record a durable source/review mechanism without changing risk thresholds - accidentally. -- Locked behavior: deterministic category ordering, threshold selection, and - overlap logic remain unchanged unless current official semantics require an - explicit reviewed correction. -- Documentation owners: `docs/internal/briefing.md` or another single - canonical maintainer owner for the external definition source. -- Exit commands: focused briefing asset/risk tests, then the standard - repository commands. - -### Stage 12: Secure Prompt-Debug Redaction And Filesystem Traversal - -- Findings: `AUD-034`, `AUD-035`. -- Affected areas: `internal/promptdebug`, Promptkit debug callbacks, endpoint - redaction, filesystem operations, and adversarial tests. -- Work: redact common credential aliases and URL credential forms; replace - pathname check/use gaps with a race-resistant secure creation strategy. -- Locked behavior: debug remains explicit opt-in with `0700` directories, - `0600` files, deterministic references, and no sensitive normal output. -- Documentation owners: `docs/operations.md` and - `docs/integrations/promptkit.md`. -- Exit commands: focused promptdebug/Promptkit tests plus - `GOWORK=off go test -race -count=1 ./internal/promptdebug - ./internal/adapters/promptkit ./internal/app`, then standard commands. - -### Stage 13: Enforce Generated-Text Identity, Shape, And Size - -- Findings: `AUD-036`, `AUD-037`, `AUD-038`. -- Affected areas: generated-text catalog, decoders, schemas, typed validators, - Promptkit result handling, and focused tests. -- Work: require report-specific schema/template pairs; reject case-variant - properties that schemas reject; and bound prose, arrays, and diagnostic - context before allocation or normal error projection. -- Locked behavior: typed normalization, exact registered identities, schema - copy isolation, and valid generated content remain unchanged. -- Documentation owners: `docs/internal/generatedtext.md`, template docs, and - Promptkit integration limits where externally relevant. -- Exit commands: focused generated-text/promptassets/app tests including limit - boundaries, then the standard repository commands. - -### Stage 14: Establish One Prepared Metadata Authority - -- Findings: `AUD-069`. -- Affected areas: briefing metadata construction, prepared reports, prompt - metadata projection, render-context inputs, and focused app/briefing tests. -- Work: establish one prepared identity/timing source and narrow projections - without changing serialized YAML or rendered Markdown. -- Locked behavior: prompt and output bytes for valid fixtures, immutable - prepared inputs, and module ordering remain unchanged. -- Documentation owners: `docs/internal/prepared-report.md`, - `docs/internal/briefing.md`, and `docs/internal/prompt-input.md`. -- Exit commands: focused briefing/app/promptinput/generatedtext tests, then the - standard repository commands. - -### Stage 15: Enforce Render Identity And Markdown Structure - -- Findings: `AUD-040`, `AUD-041`, `AUD-042`. -- Affected areas: render-context builders, repository templates/partials, - generated prose insertion, and rendering tests. -- Work: reject conflicting report identities; prevent dynamic prose from - impersonating deterministic Markdown structure; and suppress or replace an - empty Today daypart section. -- Locked behavior: repository templates remain Markdown owners, deterministic - facts remain authoritative, and normal section order remains unchanged. -- Documentation owners: `docs/templates.md`, - `docs/internal/generatedtext.md`, and `docs/internal/reporttemplate.md`. -- Exit commands: focused generated-text/reporttemplate/app tests including - adversarial heading content, then the standard repository commands. - -### Stage 16: Move Prompt Compatibility And Provenance Checks Before Work - -- Findings: `AUD-043`, `AUD-044`. -- Affected areas: application prompt inspection/preparation/execution, - generated-text catalog preflight, provenance values, and app tests. -- Work: validate catalog compatibility before collection and reconcile - inspected, prepared, callback, completed, and schema identities before - accepting generated output. -- Locked behavior: one inspection/preparation, profile precedence, immutable - execution copies, and partial execution result ownership remain unchanged. -- Documentation owners: `docs/internal/prepared-report.md`, - `docs/internal/app-orchestration.md`, and Promptkit integration docs. -- Exit commands: focused app/promptexec/generatedtext tests, then the standard - repository commands. - -### Stage 17: Make Single-Report Publication Type-Safe And Cancelable - -- Findings: `AUD-045`, `AUD-046`, `AUD-047`. -- Affected areas: `internal/fileutil`, app output planning/publication, - destination error projection, and filesystem tests. -- Work: reject non-regular final destinations; use bounded temporary naming - without leaving new directories on precommit failure; and check cancellation - at the rename linearization point. -- Locked behavior: same-directory atomic visibility, preservation of prior - regular files, restrictive new-file mode, truthful committed paths, and - notification-after-publication remain unchanged. -- Documentation owners: `docs/operations.md`, architecture policy if an - invariant needs clarification, and app orchestration internals. -- Exit commands: focused fileutil/app/CLI filesystem tests plus - `GOWORK=off go test -race -count=1 ./internal/fileutil ./internal/app - ./internal/cli`, then standard commands. - -### Stage 18: Bound And Validate The Distributor Adapter Boundary - -- Findings: `AUD-048`, `AUD-049`, `AUD-050`, `AUD-052`. -- Affected areas: Distributor config validation, repository adapter, pinned - client interaction, local HTTP tests, and safe batch diagnostics. -- Work: reject unsupported endpoints before publication; bound response reads; - redact or classify remote text before normal results; and exercise the real - production HTTP boundary with a local server. -- Locked behavior: exactly-once upload/status mapping, published-file source - selection, token redaction, and dependency types remaining adapter-local. -- Documentation owners: `docs/config.md`, `docs/operations.md`, - `docs/internal/distributor-adapter.md`, and Distributor integration docs. -- Exit commands: focused config/distributor/app tests including oversized and - malicious response bodies, then the standard repository commands. - -### Stage 19: Preserve Batch Cancellation As A Distinct Outcome - -- Findings: `AUD-051`. -- Affected areas: batch orchestration, result/error types, CLI projection, and - cancellation tests. -- Work: stop starting later reports after cancellation and preserve - cancellation separately from independent report failures and notification - state. -- Locked behavior: collect once, deterministic planned order, continuation - after non-cancellation failures, report-only counters, and all-success - notification gating remain unchanged. -- Documentation owners: `docs/cli.md`, `docs/operations.md`, and - `docs/internal/app-orchestration.md`. -- Exit commands: focused app/CLI tests plus - `GOWORK=off go test -race -count=1 ./internal/app ./internal/cli`, then - standard commands. - -### Stage 20: Tighten Comparison Manifest Compatibility - -- Findings: `AUD-053`, `AUD-054`. -- Affected areas: comparison JSON decoding, manifest/logical-bundle validation, - filename helpers, and compatibility tests. -- Work: reject duplicate and case-variant fields and require each successful - report path to equal the canonical ordinal/profile filename. -- Locked behavior: current schema version, deterministic encoding/hashing, - flat exact file set, ordered profiles, and bounded recognition errors remain. -- Documentation owners: `docs/integrations/comparison-bundle.md` and - `docs/internal/comparison-publication.md`. -- Exit commands: focused comparison compatibility tests, then the standard - repository commands. - -### Stage 21: Make Comparison Replacement Cancellation And Recovery Truthful - -- Findings: `AUD-055`, `AUD-056`, `AUD-057`. -- Affected areas: comparison planning/publication, backup cleanup/result state, - sibling naming, app/CLI recovery projection, and filesystem tests. -- Work: honor cancellation before replacement commit; distinguish a complete - retained recovery bundle from a partially removed backup; and fail long-name - constraints during preflight without residual directories. -- Locked behavior: post-move authorization, rollback, no forced destructive - cleanup, committed-new-bundle truth, and actionable recovery paths remain. -- Documentation owners: comparison bundle/publication docs, operations, and - CLI result docs where state representation changes. -- Exit commands: focused comparison/app/CLI tests plus - `GOWORK=off go test -race -count=1 ./internal/comparison ./internal/app - ./internal/cli`, then standard commands. - -### Stage 22: State Executor Concurrency And Preserve Profile Failures - -- Findings: `AUD-058`, `AUD-077`. -- Affected areas: `promptexec.Executor` contract, comparison execution/result - aggregation, Promptkit adapter, app fakes, and CLI tests. -- Work: document and test concurrent-call substitutability and preserve - already-completed profile failures when later command cancellation occurs. -- Locked behavior: one shared executor, one goroutine per explicit profile, - provider-owned capacity, joined workers, selection ordering, and no - Distributor notification remain unchanged. -- Documentation owners: Promptkit integration and comparison execution/app - orchestration internals. -- Exit commands: focused promptexec/Promptkit/app/CLI tests plus - `GOWORK=off go test -race -count=1 ./internal/adapters/promptkit - ./internal/app ./internal/cli`, then standard commands. - -### Stage 23: Make Tests Independent Of Ambient Host State - -- Findings: `AUD-059`, `AUD-060`. -- Affected areas: Promptkit credential tests and symlink tests in app, - promptdebug, and comparison packages. -- Work: explicitly control the credential environment and treat inability to - create symlinks as a tested capability prerequisite rather than product - failure. -- Locked behavior: real supported-host symlink safety assertions and missing- - credential behavior remain fully protected. -- Documentation owners: testing policy only if supported-platform policy is - deliberately changed; normally none. -- Exit commands: focused packages under scrubbed and ordinary environments, - race-enabled focused tests, then standard commands. - -### Stage 24: Remove Redundant Assertions And Production Test Waits - -- Findings: `AUD-061`, `AUD-062`. -- Affected areas: briefing SPC tests, collection wrapper tests, Distributor - polling tests, and narrow timing seams. -- Work: remove the private-constant assertion while retaining observable risk - behavior; inject only the wait controls necessary to avoid production delays - in deterministic tests. -- Locked behavior: production retry/poll intervals and observable SPC policy - remain unchanged. -- Documentation owners: none unless a contributor-facing test seam warrants a - focused internal note. -- Exit commands: focused briefing/collect/distributor tests with repeated runs, - then the standard repository commands. - -### Stage 25: Make Comparison Test Failure Cleanup Total - -- Findings: `AUD-064`. -- Affected areas: comparison execution test barriers and worker cleanup in - `internal/app` tests. -- Work: release and join workers on timeout, early completion, duplicate start, - and every diagnostic exit without weakening liveness detection. -- Locked behavior: production comparison scheduling and five-second deadlock - diagnostics remain unchanged. -- Documentation owners: none. -- Exit commands: repeat focused comparison-execution tests and run them with - `-race`, then the standard repository commands. - -### Stage 26: Remove Redundant Weather Fetches And Safe Serialization - -- Findings: `AUD-065`. -- Affected areas: Weather API bundle acquisition, request-result aggregation, - provenance ordering, cancellation, and adapter/collection tests. -- Work: reuse or remove the discarded warmup current response and execute - independent source requests concurrently through one deterministic merge. -- Locked behavior: source-local validation/errors, exact source order, - required/optional policy, retry policy after Stage 5, and context cancellation - remain authoritative. -- Documentation owners: Weather API integration and collection internals if - operational request behavior changes materially. -- Exit commands: focused latency/request-count and cancellation tests plus - `GOWORK=off go test -race -count=1 ./internal/adapters/weatherapi - ./internal/collect`, then standard commands. - -### Stage 27: Eliminate Repeated Comparison Bundle Reads - -- Findings: `AUD-066`. -- Affected areas: comparison recognition/planning/publication and app - comparison preflight. -- Work: pass or cache only the recognition evidence needed to avoid four full - reads while retaining reauthorization at every mutation boundary. -- Locked behavior: early rejection, exact file/type validation, post-move - authorization, deterministic hashes, and concurrent destination safety after - Stage 21 remain unchanged. -- Documentation owners: comparison publication internals if ownership or - transaction phases change. -- Exit commands: focused read-count and adversarial replacement tests plus - `GOWORK=off go test -race -count=1 ./internal/comparison ./internal/app`, then - standard commands. - -### Stage 28: Consolidate Distributor Template Parsing - -- Findings: `AUD-068`. -- Affected areas: single and batch notification template rendering and config - tests. -- Work: extract one private grammar/parser with typed callbacks while - preserving each template family's variable allowlist and error context. -- Locked behavior: accepted syntax, rejected malformed forms, exact rendering, - and single-versus-batch policy remain unchanged. -- Documentation owners: none unless the user-visible grammar changes, which is - outside this behavior-preserving stage. -- Exit commands: focused config tests, then the standard repository commands. - -### Stage 29: Centralize Fact Requirement Policy - -- Findings: `AUD-070`. -- Affected areas: module requirement vocabulary, briefing definitions, - availability checks, constructor validation, and tests. -- Work: define requirement identity/category/predicate once while leaving each - module's requirement list and missing-data policy explicit. -- Locked behavior: all current compositions, supported reports, availability, - and diagnostic identity remain byte-for-byte or semantically unchanged. -- Documentation owners: module and briefing internals if ownership changes. -- Exit commands: focused module/briefing/config tests, then the standard - repository commands. - -### Stage 30: Consolidate CLI Report-Date Policy - -- Findings: `AUD-071`. -- Affected areas: generate/comparison flag registration and date resolution, - CLI parser/action tests, and private CLI descriptors. -- Work: establish one private report-date policy/resolver used by both actions - after Stage 4 has locked corrected behavior. -- Locked behavior: accepted flags, required/default dates, action-specific - errors, and request types remain unchanged. -- Documentation owners: none unless implementation reveals a current contract - mismatch; CLI docs remain authoritative. -- Exit commands: focused CLI parser/action tests and help commands, then the - standard repository commands. - -### Stage 31: Retire Dormant Compatibility And Persistence Surfaces - -- Findings: `AUD-072`. -- Affected areas: unused prompt YAML loading/saving, Weather API bundle saving, - JSON atomic helper if orphaned, module snapshot compatibility implication, - forecast threshold exports, full alert envelope copies, normalized generated - JSON returns, and sole-purpose tests/docs. -- Work: remove the no-owner surfaces identified by the audit. If an actual - production consumer appears before implementation, stop this stage and - replace the retirement with a separately approved narrow contract; do not - retain speculative compatibility. -- Locked behavior: normal Build/Validate/MarshalYAML, report publication, - comparison bundles, prompt debugging, alert item provenance, real forecast - decisions, typed normalization, and all workflows remain unchanged. -- Documentation owners: affected focused internal documents; remove stale - descriptions rather than documenting retired APIs. -- Exit commands: graph/text caller checks showing no remaining references, - focused affected package tests, then the standard repository commands. - -### Stage 32: Consolidate Generated-Text Test Ownership - -- Findings: `AUD-039`. -- Affected areas: generated-text day-style and render-context tests, - promptassets schema tests, and reporttemplate tests. -- Work: keep one shared semantic suite plus thin report-identity cases; put - schema ownership in promptassets and Markdown ownership in reporttemplate; - retain one real typed-context render integration. -- Locked behavior: every public validator identity, distinct Hourly behavior, - schema/type agreement, typed projection, template semantics, and composition - remain protected. -- Documentation owners: none. -- Exit commands: focused promptassets/generatedtext/reporttemplate tests, then - the standard repository commands. - -### Stage 33: Reconcile Stale Internal Implementation Guides - -- Findings: `AUD-075`. -- Affected areas: forecast derivation, collection, report registry, and prompt- - input internal guides plus incoming links. -- Work: replace nonexistent API/flow/test claims with concise descriptions of - the implemented owners and link to canonical contracts instead of duplicating - volatile details. -- Locked behavior: documentation-only; no executable contract changes. -- Documentation owners: the four affected `docs/internal/` files and - `docs/development.md` only if task routing changes. -- Exit commands: repository link/fence checks, focused test inventories used to - verify claims, `GOWORK=off go test -count=1 ./...`, and `git diff --check`. - -### Stage 34: Retire Completed Comparison Roadmaps - -- Findings: `AUD-076`. -- Affected areas: `docs/roadmap/profile-comparison.md`, - `docs/roadmap/implementation.md`, and all incoming links. -- Work: verify useful implemented contracts have one durable canonical owner, - repair incoming navigation, then remove the completed roadmaps so they no - longer duplicate current behavior. -- Locked behavior: documentation lifecycle only; no code, tests, dependencies, - schemas, or current contracts change. -- Documentation owners: CLI, operations, comparison integration/publication, - comparison execution, Promptkit integration, architecture, and development - docs only where an incoming link or missing durable fact requires correction. -- Exit commands: repository link/fence checks, both CLI help commands, - `GOWORK=off go test -count=1 ./...`, and `git diff --check`. - -### Stage 35: Close Remediation And Retire Audit Records - -- Findings: none; this is the roadmap lifecycle gate. -- Prerequisite: Stages 1-34 are complete and every referenced finding's tests - and canonical documentation have landed. -- Affected areas: `docs/roadmap/audit.md`, `audit-plan.md`, this roadmap, and - incoming links. -- Work: verify every finding is resolved, superseded, or explicitly moved to a - newly approved roadmap; move any durable rationale to its canonical owner; - verify no active work or incoming link depends on these coordination files; - then remove the temporary audit plan, ledger, and this roadmap in the final - lifecycle change. -- Locked behavior: documentation lifecycle only; do not rewrite historical - ADRs or release notes and do not create a second current-state reference. -- Documentation owners: all durable owners touched by earlier stages, solely - to fill a demonstrated ownership gap. -- Exit commands: repository link/fence and secret-pattern checks; all focused - stage exits; `GOWORK=off go test -count=1 ./...`, - `GOWORK=off go test -race -count=1 ./...`, both CLI help commands, - `GOWORK=off go vet ./...`, and `git diff --check`. - -## Completion Tracking - -Update a stage heading or add a concise completion note only after its code, -tests, canonical documentation, and exit commands pass together. Do not copy -implemented contracts into this roadmap. When all work is complete, follow -Stage 35 rather than retaining this file as a parallel reference. diff --git a/docs/roadmap/audit.md b/docs/roadmap/audit.md index cd2984c..98217fa 100644 --- a/docs/roadmap/audit.md +++ b/docs/roadmap/audit.md @@ -280,7 +280,7 @@ evidence and the architecture's stated boundaries. | P3 | `AUD-039`, `AUD-067` through `AUD-072` | Behavior-preserving consolidation and dormant-surface retirement accepted after the behavior they touch is corrected and locked by tests. | | P4 | `AUD-075`, `AUD-076`, and audit-record retirement | Documentation lifecycle work accepted last, after implemented contracts have durable canonical owners. | -The remediation roadmap's allocation table and 35 stages are the authoritative +The remediation roadmap's allocation table and 56 stages are the authoritative finding-to-stage mapping. All 74 confirmed findings are accepted now; none is deferred and none requires a prerequisite decision. Rejected `AUD-001`, `AUD-022`, and `AUD-032` flow only through `AUD-072`; rejected `AUD-063` flows @@ -6210,7 +6210,7 @@ directly established. - Accepted all 74 confirmed findings for implementation. None was priority- deferred, and the evidence was sufficient to choose a corrective direction without a separate product or architecture decision. -- Created `docs/roadmap/audit-remediation.md` with 35 independently executable +- Created `docs/roadmap/audit-remediation.md` with 56 independently executable stages. Each stage names its findings, affected areas, work, locked behavior, test expectations, canonical documentation owners, and exit commands. - Allocated every confirmed finding exactly once. Correctness, security, data diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index cad7f8a..fb24e1c 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -1,398 +1,910 @@ -# LLM Profile Comparison Implementation Plan +# Repository Audit Remediation Roadmap -Status: Complete. +Status: Ready for implementation. ## Purpose And Authority -This document is the ordered implementation plan for the accepted [LLM -Profile Comparison Roadmap](profile-comparison.md). The roadmap owns the -feature purpose, policy, scope, and desired end state. This plan records the -completed implementation and records the corrective work completed during -post-implementation review. +This roadmap translates the confirmed findings in the +[repository audit ledger](audit.md) into dependency-ordered implementation +stages. The ledger owns discovery evidence and final finding disposition. This +document owns remediation scope, ordering, locked behavior, verification, and +completion state until the work is finished. -All implementation work in this plan is complete. The recorded work leaves the -repository compiling, tested, documented to its implemented boundary, and -internally coherent. +All 74 confirmed findings are accepted for remediation. None is priority- +deferred and none requires a separate product or architecture decision before +work can begin. Four rejected standalone findings remain consolidated into +their confirmed owners and are not separate work items. + +This roadmap describes future work. Until an individual stage is implemented, +canonical current-state documentation and executable behavior remain +authoritative. ## Implementation Rules -The following rules governed every implementation stage: - -- Read `docs/development.md`, the task-specific documents it identifies, all - files under `docs/policy/`, and the feature roadmap before changing code. -- Preserve the existing `generate`, `run`, and `compare` command contracts - except for the explicit comparison corrections defined below. -- Keep Promptkit types and calls behind `internal/adapters/promptkit` and the - dependency-neutral `internal/promptexec` interface. -- Keep comparison artifacts operator-owned and explicit. They are not durable - application state and must never be discovered or consumed implicitly by a - later invocation. -- Preserve comparison's prepare-once, execute-concurrently, order-results-by- - selection, publish-on-profile-failure, and never-notify invariants. -- Do not add a Weatherreporter concurrency limit. Promptkit owns backend - capacity. -- Never expose provider bodies, prompts, schemas, model output, endpoints, - credentials, or arbitrary wrapped error text in normal JSON summaries or - manifests. -- Use deterministic, offline, credential-free tests. Test filesystem safety, - concurrency, and recovery through the narrowest stable behavioral boundary; - do not rely on timing-only sleeps or host permission behavior. -- Run `gofmt` on changed Go files and `git diff --check` in every stage. Run - focused tests while developing and `GOWORK=off go test -count=1 ./...` before - completing each stage. Stages involving concurrency or filesystem mutation - must also run affected packages with `-race`. -- Do not commit, tag, push, or prepare a release unless the implementing prompt - separately requests it. - -## Completed Stages - -### Stage 1: Comparison Artifact And Naming Contracts - -Added the dependency-neutral comparison model, schema version, manifest -validation and encoding, safe errors, deterministic profile filenames, -comparison identities, default directory names, and content hashing. - -### Stage 2: Destination Recognition And Transactional Publication - -Added read-only destination planning, strict recognition of current comparison -bundles, private sibling staging, guarded replacement, rollback, and atomic -directory publication. - -### Stage 3: Ordered Multi-Profile Preflight - -Added exact prompt inspection followed by sequential profile inspection before -weather collection, including effective backend, model, and credential checks. - -### Stage 4: Immutable Shared Report Preparation - -Extracted one immutable prepared-report value so comparison collection, -derivation, module construction, and data-package serialization happen once. - -### Stage 5: Profile Execution And In-Memory Rendering - -Separated profile-specific Promptkit execution, generated-text validation, and -Markdown rendering from output publication while preserving ordinary report -generation behavior. - -### Stage 6: Concurrent Ordered Profile Execution - -Added one goroutine per selected profile using one shared executor and one -prepared input, deterministic debug identities, isolated profile failures, -joined cancellation, and selection-ordered results. - -### Stage 7: Application-Level Comparison - -Added `app.CompareDetailed`, coherent complete and partial bundle construction, -aggregate profile-failure behavior, absolute published paths, and the -application-level guarantee that comparison never notifies Distributor. - -### Stage 8: Compare Command Parsing - -Added the `compare` command request path, repeatable ordered `--profile`, exact -`--out-dir`, guarded `--replace`, applicable common flags, validation, and one -executor construction per invocation. - -### Stage 9: CLI Results And Exit Behavior - -Added structured success and failure summaries, quiet-mode suppression, -ordered per-profile results, safe bounded errors, and nonzero exit behavior for -partial or command-level failure. - -### Stage 10: Canonical Documentation And Initial Validation - -Documented the implemented CLI, operations, Promptkit integration, comparison -bundle, application orchestration, execution, publication, architecture, and -development contracts, then passed the original repository-wide validation -gate. - -## Stage 11: Make Concurrent Prompt Debug Creation Race-Safe - -### Goal - -Ensure concurrent comparison profiles can create their distinct debug runs -under one new report/date directory without spuriously failing or leaving a -test goroutine blocked. - -### Work - -1. Update `internal/promptdebug.ensureSecureDirectory` so concurrent creation - of the same missing directory is idempotent. If `os.Mkdir` reports that the - path already exists, inspect the path with `Lstat` and accept it only when it - is the expected real directory. Continue to reject symlinks, non-directories, - unsafe modes, and every unrelated filesystem error. -2. Preserve the existing absolute-path, containment, `0700` directory, `0600` - file, and no-symlink guarantees. Do not weaken debug-root validation or make - all `EEXIST` errors successful. -3. Add a focused prompt-debug concurrency regression that starts multiple - writers beneath a shared missing ancestor, joins every goroutine, and - verifies every expected artifact and permission invariant. -4. Make comparison execution test barriers time-bounded and failure-aware. A - callback failure before executor entry must fail the test promptly rather - than leave `waitForProfileStarts` waiting forever. -5. Retain distinct deterministic debug references and profile-local debug - failure behavior. - -### Tests And Exit Criteria - -- The focused prompt-debug concurrency test passes repeatedly and with the race - detector. -- `TestExecuteComparisonProfilesUsesDistinctDeterministicDebugReferences` - cannot hang when a profile fails before reaching the fake executor. -- Run, at minimum: - - ```sh - GOWORK=off go test -count=100 ./internal/promptdebug - GOWORK=off go test -count=100 -run TestExecuteComparisonProfilesUsesDistinctDeterministicDebugReferences ./internal/app - GOWORK=off go test -race -count=1 ./internal/promptdebug ./internal/app - GOWORK=off go test -count=1 ./... - ``` - -## Stage 12: Make Replacement Authorization Commit-Safe - -### Goal - -Prevent a destination changed after the final read-only preflight from being -treated as the previously authorized empty directory or recognized bundle and -then deleted during replacement. - -### Work - -1. Replace `DestinationPlan.Exists` as the publication decision with an - explicit destination-state classification: absent, empty real directory, or - recognized current-schema bundle. Keep `Replace` in the plan so publication - can apply the same authorization policy at commit time. -2. Continue to call `PlanDestination` immediately before publication. For an - absent target, install staging with one rename; a concurrently created - target must cause that rename to fail without modifying the new target. -3. For an existing target, rename that exact filesystem entry to the unique - sibling backup before deleting or installing anything. Classify the moved - backup while it is at its stable backup path and authorize it under the - original replacement policy: - - - an empty real directory is allowed with or without `Replace`; - - a recognized current-schema comparison bundle is allowed only with - `Replace`; and - - a file, symlink, unrecognized/nonempty directory, unreadable entry, or - other classification failure is not allowed. - -4. Treat this post-move classification as the destructive-action - authorization point. If it fails, restore the moved entry to the target and - return an error without installing staging. If the target has concurrently - reappeared or restoration otherwise fails, retain the backup and return an - actionable joined or typed error that identifies its recovery path; never - delete either entry to force restoration. -5. Install staging only after the moved target has passed authorization. Never - remove a backup that did not pass post-move authorization. -6. Preserve the existing final cancellation linearization rule: cancellation - observed before the rename transaction prevents replacement; after the - transaction starts, finish commit or rollback rather than abandoning it. -7. Add a package-private filesystem-operation seam only if needed for - deterministic tests. Keep the public destination and publication APIs free - of test-only hooks. - -### Tests And Exit Criteria - -- Deterministically replace an initially accepted destination after final - preflight but before its move with each consequential unauthorized type: - unrelated nonempty directory, regular file, and symlink. Publication must - fail, staging must not become the target, and the moved entry must be restored - or retained at a reported recovery path. -- Cover an initially empty directory whose contents change before its move and - a recognized bundle swapped for an unrecognized directory. -- Retain coverage for absent targets, empty directories, recognized bundle - replacement, cancellation before commit, install failure, successful - rollback, failed rollback, and cleanup of ordinary staging failures. -- Run `GOWORK=off go test -race -count=1 ./internal/comparison` and the - repository-wide standard test command. - -## Stage 13: Represent Committed Publication Cleanup Failures Accurately - -### Goal - -Keep application and CLI results truthful when the new comparison bundle has -been committed but removal of the old sibling backup fails. - -### Work - -1. Change comparison publication to return a dependency-neutral result as well - as an error: - - ```go - type PublicationResult struct { - Committed bool - RetainedBackupPath string - } - - func Publish( - ctx context.Context, - plan DestinationPlan, - bundle LogicalBundle, - ) (PublicationResult, error) - ``` - -2. Define `Committed` as meaning the complete staged bundle is now installed - at the target. Pre-commit, staging, authorization, install, and successful- - rollback failures return `Committed == false`. A successful install returns - `Committed == true` even if later backup cleanup fails. -3. Add a typed post-commit cleanup error that unwraps its filesystem cause and - records the retained backup path for operator recovery. On this error, - return `Committed == true` and the absolute retained backup path. Do not - roll back or remove the newly committed valid bundle merely because old - backup cleanup failed. -4. In `app.CompareDetailed`, populate `ManifestPath`, `DataPackagePath`, and - successful profile `ReportPath` values whenever publication reports - `Committed == true`, before returning any cleanup error. -5. Treat post-commit cleanup failure as a command-level operational failure: - return the non-nil structured result plus an error, produce status `failed`, - and exit nonzero even though the published artifact paths are present. The - ordinary safe JSON error must not contain the raw filesystem cause or backup - path; the wrapped diagnostic returned on stderr may identify the retained - backup for recovery. -6. Keep `RetainedBackupPath` out of the versioned comparison manifest. It - describes an incomplete local transaction cleanup, not the logical bundle. - -### Tests And Exit Criteria - -- Inject a deterministic backup-removal failure after successful installation - and assert the target is the new recognized bundle, the old bundle remains - at the reported backup, `Committed` is true, and the error is inspectable by - type. -- At the application boundary, assert all committed artifact paths are - absolute and populated while the method still returns an error. -- At the CLI boundary, assert status `failed`, nonzero return, present artifact - paths, and a bounded generic safe error with no raw filesystem detail. -- Retain tests showing every pre-commit or rolled-back failure omits published - artifact paths. -- Run comparison, application, and CLI tests with `-race`, then the - repository-wide standard test command. - -## Stage 14: Complete Structured Failure Metadata And Classification - -### Goal - -Make every non-nil comparison result a reliable description of the attempted -run and preserve useful safe error categories in the top-level CLI summary. - -### Work - -1. In `app.CompareDetailed`, assign the absolute resolved - `OutputDirectory` immediately after output-directory resolution and before - destination preflight. Do not wait for `PlanDestination` to succeed. -2. Once the initial `ComparisonResult` exists, guarantee that every return path - sets a nonzero UTC `FinishedAt` that is not before `StartedAt`. Use one - centralized finalization path or a defer; do not scatter timestamp writes - across individual failures. -3. Continue to omit manifest, data-package, and report paths until publication - commits. Preserve whatever prompt identity fields have actually been - resolved; never invent a hash or profile result for a phase that did not - run. -4. Update `safeComparisonSummaryError` to use this stable mapping, always - passing messages through `comparison.NewSafeError`: - - | Error | Category | Safe message | - | --- | --- | --- | - | aggregate profile failure | `application` | existing bounded aggregate message | - | `context.Canceled` | `canceled` | `comparison canceled` | - | `context.DeadlineExceeded` | `deadline_exceeded` | `comparison deadline exceeded` | - | categorized `promptexec` error | exact `promptexec.CategoryOf` value | `comparison prompt operation failed` | - | `comparison.DestinationError` | `destination_` | `comparison destination preflight failed` | - | post-commit cleanup error | `publication_cleanup` | `comparison published but cleanup did not complete` | - | any unknown error | `application` | `comparison did not complete` | - -5. Apply the most specific mapping before a more general wrapped match. In - particular, detect the post-commit cleanup and destination types before - falling back to a nested filesystem or context cause. -6. Do not copy `DestinationError.Target`, wrapped causes, or arbitrary - `error.Error()` text into normal JSON. Detailed returned errors remain - available on stderr and through Go error inspection. - -### Tests And Exit Criteria - -- Add application tests for destination-preflight, debug initialization, - prompt-preflight, collection, and preparation failures. Whenever a non-nil - result is returned, assert an absolute output directory, nonzero ordered UTC - timestamps, and omission of unpublished artifact paths. -- Add table-driven CLI tests for every mapping row, including wrapped errors, - and assert that unsafe sentinel text cannot enter serialized output. -- Preserve existing ordered profile-level categories and safe messages. -- Run application and CLI tests with `-race`, then the repository-wide standard - test command. - -## Stage 15: Remove Temporary Seams, Reconcile Documentation, And Validate - -### Goal - -Remove review-discovered maintenance debt, document the corrected implemented -behavior in its canonical owners, and complete the release-equivalent gate. - -### Work - -1. Remove the unused `Runner.resolveComparison` wrapper. Remove the - test-oriented `Runner.executeComparison` seam if it has no production - caller, and rewrite its remaining coverage through `Runner.Run`, - `resolveComparisonAction`, or another stable behavioral boundary. -2. Remove `comparisonProfileOutcome.err` if production code still does not use - it. Keep raw failures in returned/wrapped errors or explicit internal error - types; do not retain an otherwise dead field solely for private test - assertions. -3. Correct the `internal/comparison` package comment so it describes the - package's actual ownership of both logical comparison contracts and - filesystem destination/publication behavior. -4. Update only the canonical current-state documents affected by Stages 11 - through 14: - - - `docs/internal/comparison-publication.md` owns post-move authorization, - commit state, rollback, retained backups, and cleanup mechanics; - - `docs/internal/comparison-execution.md` owns concurrency and debug-write - behavior; - - `docs/internal/app-orchestration.md` owns partial results and committed - publication error handling; - - `docs/cli.md` owns structured status, safe category, path, and exit - behavior; and - - `docs/operations.md` owns operator recovery for a retained sibling backup. - - Link rather than duplicating complete contracts, and update architecture or - integration documentation only if its existing invariant is inaccurate. -5. Mark this plan `Complete` and restore the feature roadmap's implemented - status after every exit criterion below passes. Retain or remove the two - roadmap documents only according to a later maintainer-directed roadmap - cleanup; do not archive them as a second current-state reference in this - stage. - -### Tests And Exit Criteria - -- Confirm no production-only helper or field remains solely to support tests, - and no test loses meaningful behavioral coverage during cleanup. -- Verify changed relative links and fenced examples. Search current-state docs - for stale claims about comparison publication, debug behavior, results, or - recovery. -- Run the release-equivalent local gate: - - ```sh - set -eu - test -z "$(git ls-files go.work go.work.sum)" - test ! -e vendor - if grep -Eq '^[[:space:]]*replace([[:space:]]|\()' go.mod; then - printf '%s\n' 'go.mod contains a replacement' >&2 - exit 1 - fi - GOWORK=off go test -count=1 ./... - GOWORK=off go test -race -count=1 ./... - GOWORK=off go vet ./... - GOWORK=off go build ./... - GOWORK=off go mod tidy -diff - unformatted="$(git ls-files '*.go' | while IFS= read -r file; do gofmt -l "$file"; done)" - test -z "$unformatted" - git diff --check - ``` - -- Run `GOWORK=off go run ./cmd/weatherreporter --help` and - `GOWORK=off go run ./cmd/weatherreporter compare --help` without credentials - or network access, and confirm that help agrees with `docs/cli.md`. -- Inspect the final diff for accidental generated artifacts, secrets, - workspaces, vendored dependencies, release notes, or unrelated changes. - -## Open Questions - -None. The roadmap and the contracts in Stages 11 through 15 define the -remaining decisions needed to complete the corrective work. +Every stage must: + +- read `docs/development.md`, all files under `docs/policy/`, this roadmap, and + the task-specific canonical documents identified below; +- implement only its listed findings and prerequisites; +- preserve the architecture policy's stateless pipeline, adapter boundaries, + curated prompt boundary, operator-owned outputs, comparison ordering, and + notification rules unless the stage explicitly corrects that contract; +- add durable tests at the narrowest stable boundary and avoid duplicating + behavior already owned elsewhere; +- update every listed canonical documentation owner in the same change as an + implemented contract change; +- use deterministic, offline, credential-free tests and synthetic fixtures; +- run `gofmt` on changed Go files, the stage's focused commands, + `GOWORK=off go test -count=1 ./...`, and `git diff --check`; and +- commit the completed stage separately when the implementing prompt requests + commits. + +Stages affecting concurrent execution, cancellation, filesystem mutation, or +process-global state must also run the affected packages with `-race`. No stage +may weaken a safety invariant merely to make a test pass. + +## Priority And Finding Allocation + +Each confirmed finding appears exactly once below. Priority describes risk and +the default remediation order, not confidence; dependency prerequisites may +place a lower-priority consolidation mechanism before the P1 correction that +depends on it. Every listed finding has high confidence. + +| Priority | Stages | Findings | Decision | +| --- | --- | --- | --- | +| P0: generated forecast integrity | 1 | `AUD-029`, `AUD-030`, `AUD-031`, `AUD-033` | Accepted now | +| P1: security, correctness, and data integrity | 2-14, 16-21, 24-37 | `AUD-002` through `AUD-021` excluding rejected `AUD-022`; `AUD-023` through `AUD-028`; `AUD-034` through `AUD-038`; `AUD-040` through `AUD-058`; `AUD-073`, `AUD-074`, `AUD-077`, and `AUD-078` | Accepted now | +| P2: deterministic test and operational quality | 38-43 | `AUD-059` through `AUD-062`, `AUD-064`, `AUD-065`, and `AUD-066` | Accepted now | +| P3: behavior-preserving consolidation and cleanup | 15, 22-23, 44-52 | `AUD-039`, `AUD-067` through `AUD-072` | Accepted now; Stages 15 and 22-23 run early because P1 corrections depend on them | +| P4: documentation lifecycle and audit retirement | 53-56 | `AUD-075` and `AUD-076`; final audit-record retirement | Accepted now | + +Rejected `AUD-001`, `AUD-022`, and `AUD-032` are represented only through +`AUD-072`; rejected `AUD-063` is represented only through `AUD-039`. + +## Remediation Stages + +### Stage 1: Correct The Curated Prompt Package Contract + +- Findings: `AUD-029`, `AUD-030`, `AUD-031`, `AUD-033`. +- Affected areas: `internal/promptinput`, shared embedded prompt assets, and + focused prompt-input/prompt-asset tests. +- Work: remove raw transport-shaped warning details from provider input; make + shared prompt paths match serialized YAML; describe regional versus point- + matched hazard products accurately; and cover stanza serialization failure. +- Locked behavior: deterministic category/stanza order, exact prompt identity, + safe normal diagnostics, and byte-stable valid YAML remain unchanged. +- Documentation owners: `docs/internal/prompt-input.md` and + `docs/integrations/promptkit.md` if their implemented contract changes. +- Exit commands: focused tests for `./internal/promptinput` and + `./internal/promptassets`, then the standard repository commands. + +### Stage 2: Make Configuration Keys And Overrides Explicit + +- Findings: `AUD-004`, `AUD-006`, `AUD-078`. +- Affected areas: `internal/config`, report-key lookup, timezone parsing, + maintained examples, and configuration tests. +- Work: reject unsupported missing-source keys; make programmatic report + overrides obey the same presence semantics as loaded configuration; and + document accepted report-key normalization and numeric-offset bounds. +- Locked behavior: file/CLI precedence, inactive-integration policy, exact + command-name lookup, and secret redaction remain unchanged. +- Documentation owners: `docs/config.md` and maintained examples only where + executable behavior requires them. +- Exit commands: focused config and CLI tests, followed by the standard + repository commands. + +### Stage 3: Make Secret Loading Atomic + +- Findings: `AUD-007`. +- Affected areas: configuration loading, secret-directory application, load + failure state, and focused configuration tests. +- Work: stage secret values and apply them to the returned configuration only + after the complete load and validation sequence succeeds. +- Locked behavior: secret precedence, configured secret-directory discovery, + environment independence, actionable errors, and redaction remain unchanged. +- Documentation owners: `docs/config.md` only if the observable load contract + needs clarification. +- Exit commands: focused config tests plus + `GOWORK=off go test -race -count=1 ./internal/config`, followed by the + standard repository commands. + +### Stage 4: Require Complete Single-Report Notification Identity + +- Findings: `AUD-005`. +- Affected areas: Distributor notification configuration, bundle-ID rendering, + and focused config/app tests. +- Work: reject enabled single-report notification configurations that can + produce blank required identity fields before report work begins. +- Locked behavior: inactive notification settings remain optional; configured + templates and publication-before-notification ordering remain intact. +- Documentation owners: `docs/config.md`, `docs/operations.md`, and Distributor + integration docs if accepted values or failures change. +- Exit commands: focused config and app notification tests, then the standard + repository commands. + +### Stage 5: Propagate Process Interrupt Cancellation + +- Findings: `AUD-008`. +- Affected areas: `cmd/weatherreporter`, action contexts, CLI wiring, and + interrupt/cancellation tests. +- Work: convert operating-system interrupts into action-context cancellation + and verify active generation, batch, and comparison workflows observe it. +- Locked behavior: command/flag ownership, structured summary shapes, stream + separation, and one executor per action remain unchanged. +- Documentation owners: `docs/cli.md`, `docs/operations.md`, and + `docs/internal/cli.md` if signal behavior needs clarification. +- Exit commands: `GOWORK=off go test -race -count=1 ./internal/cli + ./internal/app ./cmd/weatherreporter`, then the standard repository commands. + +### Stage 6: Correct CLI Preflight, Coverage, And Visible Contracts + +- Findings: `AUD-009`, `AUD-010`, `AUD-074`. +- Affected areas: `internal/cli`, executor construction, assembled CLI tests, + help text, and CLI documentation. +- Work: validate report dates before executor construction; add representative + generate and batch result/exit tests; and align help/internal guidance with + quiet, date, and comparison behavior. +- Locked behavior: command/flag ownership, structured summary shapes, stream + separation, cancellation from Stage 5, and one executor per action remain + unchanged. +- Documentation owners: `docs/cli.md` and `docs/internal/cli.md`. +- Exit commands: focused CLI/app tests, both root and comparison help commands, + then the standard repository commands. + +### Stage 7: Validate Weather API Endpoints And Retry Classification + +- Findings: `AUD-011`, `AUD-015`. +- Affected areas: Weather API configuration and adapter construction, warmup + retry classification, local HTTP fixtures, and focused tests. +- Work: accept only supported endpoint forms and retry warmup only for + transient failures. +- Locked behavior: all eight source contracts, checked-empty optional products, + cancellation, provenance, and offline testing remain intact. +- Documentation owners: `docs/config.md`, `docs/integrations/weatherapi.md`, + and `docs/internal/collect.md` as applicable. +- Exit commands: focused configuration, adapter, and collection tests, then the + standard repository commands. + +### Stage 8: Bound And Redact Weather API Diagnostics + +- Findings: `AUD-012`, `AUD-013`. +- Affected areas: Weather API response reading, non-success error projection, + size limits, local HTTP fixtures, and focused adapter tests. +- Work: replace upstream response text with bounded safe diagnostics and reject + rather than silently truncate oversized bodies. +- Locked behavior: HTTP status classification, source-local error context, + cancellation, and offline tests remain intact. +- Documentation owners: `docs/integrations/weatherapi.md` and + `docs/internal/collect.md` if observable limits or diagnostics change. +- Exit commands: focused boundary-size and malicious-response adapter tests, + then the standard repository commands. + +### Stage 9: Require Usable Hourly Period Bounds + +- Findings: `AUD-014`. +- Affected areas: Weather API hourly decoding, normalized hourly data, + required-source validation, fixtures, and focused tests. +- Work: reject required hourly products whose periods do not contain usable + time bounds before they enter collection and derivation. +- Locked behavior: required/optional source policy, source provenance, + chronological selection, and valid hourly fixtures remain unchanged. +- Documentation owners: `docs/integrations/weatherapi.md`, + `docs/internal/weather-data.md`, and `docs/internal/collect.md`. +- Exit commands: focused weather-data, adapter, and collection tests, then the + standard repository commands. + +### Stage 10: Correct Civil-Time Windows And Daily Run Identity + +- Findings: `AUD-002`, `AUD-003`. +- Affected areas: `internal/timeutil`, Daily report identity, DST and run-ID + tests, and report-registry documentation. +- Work: construct configured clocks as local civil times across both DST + transitions and add durable protection for Daily run-ID uniqueness. +- Locked behavior: half-open selection, civil-day report periods, rolling + Hourly elapsed time, and source-period identity remain unchanged. +- Documentation owners: `docs/internal/report-registry.md`. +- Exit commands: focused time/report tests with both DST transitions, then the + standard repository commands. + +### Stage 11: Preserve Units And Overnight Alert Coverage + +- Findings: `AUD-016`, `AUD-017`. +- Affected areas: `internal/forecast`, `internal/facts`, metric fallback + values, overnight dayparts, alerts, and focused tests. +- Work: preserve units in fallback values and include alerts over the complete + overnight interval. +- Locked behavior: half-open overlap semantics, chronological ordering, source + periods, and valid US-customary results remain unchanged. +- Documentation owners: `docs/internal/forecast-derivation.md` and + `docs/internal/facts.md`. +- Exit commands: focused forecast/facts tests with metric and overnight cases, + then the standard repository commands. + +### Stage 12: Validate Precipitation And Ice Classification + +- Findings: `AUD-018`, `AUD-019`. +- Affected areas: precipitation-domain validation, weather-text + classification, derived summaries, and focused forecast/facts tests. +- Work: reject precipitation probabilities outside the percentage domain and + classify ordinary icy wording consistently. +- Locked behavior: valid percentage handling, summary ordering, and existing + weather-condition categories remain unchanged. +- Documentation owners: `docs/internal/forecast-derivation.md` and + `docs/internal/facts.md`. +- Exit commands: focused forecast/facts boundary tests, then the standard + repository commands. + +### Stage 13: Correct Source-Facing Briefing Module Semantics + +- Findings: `AUD-020`, `AUD-021`. +- Affected areas: briefing module option validation, weather-story construction, + module registry tests, and normalized source fixtures. +- Work: make accepted AFD option shapes constructible and omit semantically + empty weather stories instead of publishing false availability. +- Locked behavior: report support, default compositions, checked-empty alerts, + and ordered module snapshots remain unchanged. +- Documentation owners: `docs/internal/module.md` and + `docs/internal/briefing.md`. +- Exit commands: focused briefing/module tests, then the standard repository + commands. + +### Stage 14: Correct Derived Briefing Language And Ranking + +- Findings: `AUD-023`, `AUD-026`, `AUD-027`. +- Affected areas: derived daily summaries, outdoor-window scoring, temperature + band wording, and briefing tests. +- Work: label apparent temperature accurately; include snow, ice, and fog in + outdoor-window ranking; and preserve negative-zero temperature meaning. +- Locked behavior: deterministic ranking, Today-only planning scope, and + existing module/schema identities remain unchanged. +- Documentation owners: `docs/internal/briefing.md` and prompt-facing field + documentation if names or meaning change. +- Exit commands: focused briefing tests with metric, subzero, and hazard cases, + then the standard repository commands. + +### Stage 15: Establish One Daypart Canonicalization Mechanism + +- Findings: `AUD-067`. +- Affected areas: duplicated canonical-key helpers in briefing and + generated-text packages plus their behavior tests. +- Work: establish one domain-owned canonical key contract and route both + current consumers through it without changing accepted ASCII behavior. +- Locked behavior: display labels, report-specific semantic roles, ordering, + and current valid output remain unchanged. +- Documentation owners: focused internal briefing/generated-text docs only if + ownership needs explanation. +- Exit commands: focused briefing and generated-text tests, then the standard + repository commands. + +### Stage 16: Correct Daypart Identity, Collision, And Display Behavior + +- Findings: `AUD-024`, `AUD-025`, `AUD-073`. +- Affected areas: derived daypart summaries, planning builders, display + capitalization, configuration fixtures, and rendering tests. +- Work: reject canonical-key collisions; use configured identities consistently + across planning; and make non-ASCII display capitalization rune-safe. +- Locked behavior: Stage 15's canonical identity remains separate from display + text; stable ordering and default dayparts remain unchanged. +- Documentation owners: `docs/config.md`, `docs/internal/briefing.md`, and + generated-text/template docs if visible labels change. +- Exit commands: focused config/briefing/generated-text/reporttemplate tests, + then the standard repository commands. + +### Stage 17: Refresh The Official SPC Definition Asset + +- Findings: `AUD-028`. +- Affected areas: embedded SPC category definitions, source attribution, and + risk-digest tests. +- Work: reconcile descriptions with the current authoritative SPC table and + record a durable source/review mechanism without changing risk thresholds + accidentally. +- Locked behavior: deterministic category ordering, threshold selection, and + overlap logic remain unchanged unless current official semantics require an + explicit reviewed correction. +- Documentation owners: `docs/internal/briefing.md` or another single + canonical maintainer owner for the external definition source. +- Exit commands: focused briefing asset/risk tests, then the standard + repository commands. + +### Stage 18: Harden Prompt-Debug Redaction + +- Findings: `AUD-034`. +- Affected areas: `internal/promptdebug`, Promptkit debug callbacks, endpoint + redaction, and adversarial content tests. +- Work: redact common credential aliases and URL credential forms from every + explicit debug artifact and error projection. +- Locked behavior: debug remains explicit opt-in with `0700` directories, + `0600` files, deterministic references, and no sensitive normal output. +- Documentation owners: `docs/operations.md` and + `docs/integrations/promptkit.md`. +- Exit commands: focused promptdebug/Promptkit redaction tests, then the + standard repository commands. + +### Stage 19: Make Prompt-Debug Filesystem Traversal Race-Resistant + +- Findings: `AUD-035`. +- Affected areas: `internal/promptdebug`, secure directory and file creation, + Promptkit debug callbacks, and adversarial filesystem tests. +- Work: replace pathname check/use gaps with a race-resistant secure traversal + and creation strategy while preserving atomic replacement and actionable + failures. +- Locked behavior: Stage 18 redaction, explicit opt-in, `0700` directories, + `0600` files, deterministic references, containment, and ordinary concurrent + execution remain unchanged. +- Documentation owners: `docs/operations.md` and + `docs/integrations/promptkit.md`. +- Exit commands: focused promptdebug/Promptkit tests plus + `GOWORK=off go test -race -count=1 ./internal/promptdebug + ./internal/adapters/promptkit ./internal/app`, then standard commands. + +### Stage 20: Enforce Generated-Text Identity And Shape + +- Findings: `AUD-036`, `AUD-037`. +- Affected areas: generated-text catalog, decoders, schemas, typed validators, + and focused tests. +- Work: require report-specific schema/template pairs; reject case-variant + properties that schemas reject. +- Locked behavior: typed normalization, exact registered identities, schema + copy isolation, and valid generated content remain unchanged. +- Documentation owners: `docs/internal/generatedtext.md` and template docs. +- Exit commands: focused generated-text and promptassets tests, then the + standard repository commands. + +### Stage 21: Bound Generated Content And Diagnostics + +- Findings: `AUD-038`. +- Affected areas: generated-text decoders and typed validators, schemas, + Promptkit result handling, safe error projection, and boundary tests. +- Work: bound prose, arrays, total generated content, and diagnostic context + before allocation or normal error projection. +- Locked behavior: Stage 20 identity/shape enforcement, typed normalization, + valid generated content, and content-safe normal summaries remain unchanged. +- Documentation owners: `docs/internal/generatedtext.md`, template docs, and + Promptkit integration limits where externally relevant. +- Exit commands: focused generated-text/promptassets/app tests at and around + every limit, then the standard repository commands. + +### Stage 22: Establish The Prepared Metadata Authority + +- Findings: `AUD-069`. +- Affected areas: briefing metadata construction, prepared reports, identity + and timing values, and focused app/briefing tests. +- Work: define one prepared identity/timing authority, populate it at the + preparation boundary, and lock its equivalence to current valid output. +- Locked behavior: prompt and output bytes for valid fixtures, immutable + prepared inputs, and module ordering remain unchanged. +- Documentation owners: `docs/internal/prepared-report.md`, + `docs/internal/briefing.md`. +- Exit commands: focused briefing and app tests, then the standard repository + commands. + +### Stage 23: Migrate Prepared Metadata Projections + +- Findings: none; this completes the remediation begun in Stage 22. +- Affected areas: prompt metadata projection, render-context inputs, superseded + parallel fields, and focused prompt/render tests. +- Work: route prompt and render projections through Stage 22's authority, then + remove or narrow superseded fields without changing serialized YAML or + rendered Markdown. +- Locked behavior: Stage 22's authority, prompt and output bytes for valid + fixtures, immutable prepared inputs, and module ordering remain unchanged. +- Documentation owners: `docs/internal/prepared-report.md`, + `docs/internal/briefing.md`, and `docs/internal/prompt-input.md`. +- Exit commands: focused briefing/app/promptinput/generatedtext tests, then the + standard repository commands. + +### Stage 24: Enforce Render Identity And Empty-Section Behavior + +- Findings: `AUD-040`, `AUD-042`. +- Affected areas: render-context builders, Today templates/partials, and + focused context/rendering tests. +- Work: reject conflicting report identities and suppress or replace a Today + daypart section that has no rows or fallback. +- Locked behavior: repository templates remain Markdown owners, deterministic + facts remain authoritative, and normal section order remains unchanged. +- Documentation owners: `docs/templates.md`, + `docs/internal/generatedtext.md`, and `docs/internal/reporttemplate.md`. +- Exit commands: focused generated-text/reporttemplate/app tests, then the + standard repository commands. + +### Stage 25: Prevent Generated Prose From Impersonating Markdown Structure + +- Findings: `AUD-041`. +- Affected areas: generated prose insertion, Markdown templates/partials, + renderer policy, and adversarial rendering tests. +- Work: define and enforce the smallest content-handling rule that prevents + generated prose from creating structure that impersonates deterministic + report sections. +- Locked behavior: repository templates remain Markdown owners, deterministic + facts remain authoritative, ordinary generated prose remains readable, and + normal section order remains unchanged. +- Documentation owners: `docs/templates.md`, + `docs/internal/generatedtext.md`, and `docs/internal/reporttemplate.md`. +- Exit commands: focused generated-text/reporttemplate/app tests with + adversarial headings and related Markdown constructs, then the standard + repository commands. + +### Stage 26: Move Prompt Compatibility Checks Before Collection + +- Findings: `AUD-043`. +- Affected areas: application prompt inspection and preparation, + generated-text catalog preflight, collection ordering, and app tests. +- Work: validate catalog compatibility before collection or other expensive + report work begins. +- Locked behavior: one inspection/preparation, profile precedence, immutable + execution copies, and partial execution result ownership remain unchanged. +- Documentation owners: `docs/internal/prepared-report.md`, + `docs/internal/app-orchestration.md`, and Promptkit integration docs. +- Exit commands: focused app and generatedtext tests proving failure ordering, + then the standard repository commands. + +### Stage 27: Reconcile Prompt Execution Provenance + +- Findings: `AUD-044`. +- Affected areas: prompt inspection, prepared execution values, callbacks, + completed execution results, schema validation, and app tests. +- Work: reconcile inspected, prepared, callback, completed, and schema + identities before accepting generated output. +- Locked behavior: Stage 26 preflight ordering, one inspection/preparation, + profile precedence, immutable execution copies, and partial result ownership + remain unchanged. +- Documentation owners: `docs/internal/prepared-report.md`, + `docs/internal/app-orchestration.md`, and Promptkit integration docs. +- Exit commands: focused app/promptexec/generatedtext tests with mismatched + provenance at each boundary, then the standard repository commands. + +### Stage 28: Preflight Single-Report Temporary Names + +- Findings: `AUD-046`. +- Affected areas: app output planning, bounded temporary naming, destination + validation, directory creation, and focused filesystem tests. +- Work: detect temporary-name and basename limits during preflight without + leaving newly created directories after failure. +- Locked behavior: destination selection, same-directory atomic visibility, + final filenames, and existing regular-file preservation remain unchanged. +- Documentation owners: `docs/operations.md` and app orchestration internals if + preflight behavior needs clarification. +- Exit commands: focused fileutil/app/CLI long-name tests, then the standard + repository commands. + +### Stage 29: Make Single-Report Publication Type-Safe And Cancelable + +- Findings: `AUD-045`, `AUD-047`. +- Affected areas: `internal/fileutil`, app output planning/publication, + destination error projection, and filesystem tests. +- Work: reject non-regular final destinations; use bounded temporary naming + from Stage 28; and check cancellation at the rename linearization point. +- Locked behavior: same-directory atomic visibility, preservation of prior + regular files, restrictive new-file mode, truthful committed paths, and + notification-after-publication remain unchanged. +- Documentation owners: `docs/operations.md`, architecture policy if an + invariant needs clarification, and app orchestration internals. +- Exit commands: focused fileutil/app/CLI filesystem tests plus + `GOWORK=off go test -race -count=1 ./internal/fileutil ./internal/app + ./internal/cli`, then standard commands. + +### Stage 30: Validate Distributor Endpoints Before Publication + +- Findings: `AUD-048`. +- Affected areas: Distributor configuration validation, app preflight, + endpoint construction, and focused config/app tests. +- Work: reject unsupported endpoint forms before report publication. +- Locked behavior: exactly-once upload/status mapping, published-file source + selection, token redaction, and dependency types remaining adapter-local. +- Documentation owners: `docs/config.md`, `docs/operations.md`, and Distributor + integration docs. +- Exit commands: focused config and app preflight tests, then the standard + repository commands. + +### Stage 31: Bound Distributor Diagnostics And Exercise The HTTP Boundary + +- Findings: `AUD-049`, `AUD-050`, `AUD-052`. +- Affected areas: Distributor adapter, pinned client interaction, response + reading and classification, local HTTP tests, and safe batch diagnostics. +- Work: bound response reads; redact or classify remote text before normal + results; and exercise the real production HTTP boundary with a local server. +- Locked behavior: Stage 30 endpoint validation, exactly-once upload/status + mapping, published-file source selection, token redaction, and adapter-local + dependency types remain unchanged. +- Documentation owners: `docs/operations.md`, + `docs/internal/distributor-adapter.md`, and Distributor integration docs. +- Exit commands: focused distributor/app tests including oversized and + malicious response bodies, then the standard repository commands. + +### Stage 32: Preserve Batch Cancellation As A Distinct Outcome + +- Findings: `AUD-051`. +- Affected areas: batch orchestration, result/error types, CLI projection, and + cancellation tests. +- Work: stop starting later reports after cancellation and preserve + cancellation separately from independent report failures and notification + state. +- Locked behavior: collect once, deterministic planned order, continuation + after non-cancellation failures, report-only counters, and all-success + notification gating remain unchanged. +- Documentation owners: `docs/cli.md`, `docs/operations.md`, and + `docs/internal/app-orchestration.md`. +- Exit commands: focused app/CLI tests plus + `GOWORK=off go test -race -count=1 ./internal/app ./internal/cli`, then + standard commands. + +### Stage 33: Tighten Comparison Manifest Compatibility + +- Findings: `AUD-053`, `AUD-054`. +- Affected areas: comparison JSON decoding, manifest/logical-bundle validation, + filename helpers, and compatibility tests. +- Work: reject duplicate and case-variant fields and require each successful + report path to equal the canonical ordinal/profile filename. +- Locked behavior: current schema version, deterministic encoding/hashing, + flat exact file set, ordered profiles, and bounded recognition errors remain. +- Documentation owners: `docs/integrations/comparison-bundle.md` and + `docs/internal/comparison-publication.md`. +- Exit commands: focused comparison compatibility tests, then the standard + repository commands. + +### Stage 34: Preflight Comparison Sibling Names + +- Findings: `AUD-057`. +- Affected areas: comparison destination planning, staging and backup sibling + naming, parent directory creation, and focused filesystem tests. +- Work: validate derived sibling-name constraints during preflight and fail + without leaving residual directories. +- Locked behavior: exact target selection, sibling staging, broad-target + rejection, and valid destination names remain unchanged. +- Documentation owners: comparison publication internals and operations if + preflight behavior needs clarification. +- Exit commands: focused comparison/app long-name tests, then the standard + repository commands. + +### Stage 35: Honor Cancellation At Comparison Replacement Commit + +- Findings: `AUD-055`. +- Affected areas: comparison replacement transaction, cancellation + linearization, rollback, and deterministic filesystem tests. +- Work: honor cancellation after moving the prior bundle but before installing + the replacement, restoring or retaining the prior bundle according to the + transaction's recovery rules. +- Locked behavior: Stage 34 preflight, post-move authorization, no forced + destructive cleanup, rollback, and committed-new-bundle truth remain. +- Documentation owners: comparison bundle/publication docs and operations. +- Exit commands: focused comparison/app cancellation tests plus + `GOWORK=off go test -race -count=1 ./internal/comparison ./internal/app`, + then the standard repository commands. + +### Stage 36: Represent Comparison Backup Recovery Truthfully + +- Findings: `AUD-056`. +- Affected areas: comparison backup cleanup, publication result/error state, + app/CLI recovery projection, and deterministic filesystem tests. +- Work: distinguish a complete retained recovery bundle from a partially + removed backup and report the actual recoverable state without deleting the + committed new bundle. +- Locked behavior: post-move authorization, rollback, no forced destructive + cleanup, Stage 35 cancellation behavior, committed-new-bundle truth, and + actionable recovery paths remain. +- Documentation owners: comparison bundle/publication docs, operations, and + CLI result docs where state representation changes. +- Exit commands: focused comparison/app/CLI tests plus + `GOWORK=off go test -race -count=1 ./internal/comparison ./internal/app + ./internal/cli`, then standard commands. + +### Stage 37: State Executor Concurrency And Preserve Profile Failures + +- Findings: `AUD-058`, `AUD-077`. +- Affected areas: `promptexec.Executor` contract, comparison execution/result + aggregation, Promptkit adapter, app fakes, and CLI tests. +- Work: document and test concurrent-call substitutability and preserve + already-completed profile failures when later command cancellation occurs. +- Locked behavior: one shared executor, one goroutine per explicit profile, + provider-owned capacity, joined workers, selection ordering, and no + Distributor notification remain unchanged. +- Documentation owners: Promptkit integration and comparison execution/app + orchestration internals. +- Exit commands: focused promptexec/Promptkit/app/CLI tests plus + `GOWORK=off go test -race -count=1 ./internal/adapters/promptkit + ./internal/app ./internal/cli`, then standard commands. + +### Stage 38: Make Tests Independent Of Ambient Host State + +- Findings: `AUD-059`, `AUD-060`. +- Affected areas: Promptkit credential tests and symlink tests in app, + promptdebug, and comparison packages. +- Work: explicitly control the credential environment and treat inability to + create symlinks as a tested capability prerequisite rather than product + failure. +- Locked behavior: real supported-host symlink safety assertions and missing- + credential behavior remain fully protected. +- Documentation owners: testing policy only if supported-platform policy is + deliberately changed; normally none. +- Exit commands: focused packages under scrubbed and ordinary environments, + race-enabled focused tests, then standard commands. + +### Stage 39: Remove Redundant Assertions And Production Test Waits + +- Findings: `AUD-061`, `AUD-062`. +- Affected areas: briefing SPC tests, collection wrapper tests, Distributor + polling tests, and narrow timing seams. +- Work: remove the private-constant assertion while retaining observable risk + behavior; inject only the wait controls necessary to avoid production delays + in deterministic tests. +- Locked behavior: production retry/poll intervals and observable SPC policy + remain unchanged. +- Documentation owners: none unless a contributor-facing test seam warrants a + focused internal note. +- Exit commands: focused briefing/collect/distributor tests with repeated runs, + then the standard repository commands. + +### Stage 40: Make Comparison Test Failure Cleanup Total + +- Findings: `AUD-064`. +- Affected areas: comparison execution test barriers and worker cleanup in + `internal/app` tests. +- Work: release and join workers on timeout, early completion, duplicate start, + and every diagnostic exit without weakening liveness detection. +- Locked behavior: production comparison scheduling and five-second deadlock + diagnostics remain unchanged. +- Documentation owners: none. +- Exit commands: repeat focused comparison-execution tests and run them with + `-race`, then the standard repository commands. + +### Stage 41: Remove The Redundant Weather Warmup Fetch + +- Findings: `AUD-065`. +- Affected areas: Weather API warmup and current-conditions acquisition, + retry semantics from Stage 7, request counts, and adapter/collection tests. +- Work: reuse the successful warmup response or remove the redundant warmup so + current conditions are not fetched and decoded twice. +- Locked behavior: source-local validation/errors, exact source order, + required/optional policy, Stage 7 retry policy, and context cancellation + remain authoritative. +- Documentation owners: Weather API integration and collection internals if + operational request behavior changes materially. +- Exit commands: focused request-count and retry tests, then the standard + repository commands. + +### Stage 42: Acquire Independent Weather Sources Concurrently + +- Findings: none; this completes the efficiency remediation begun in Stage 41. +- Affected areas: Weather API bundle acquisition, request-result aggregation, + provenance ordering, cancellation, and adapter/collection tests. +- Work: execute independent source requests concurrently and merge their + results in one deterministic source order. +- Locked behavior: Stage 41 current-response handling, source-local + validation/errors, exact source order, required/optional policy, Stage 7 + retry policy, and context cancellation remain authoritative. +- Documentation owners: Weather API integration and collection internals if + operational request behavior changes materially. +- Exit commands: focused latency, deterministic-order, failure, and + cancellation tests plus + `GOWORK=off go test -race -count=1 ./internal/adapters/weatherapi + ./internal/collect`, then standard commands. + +### Stage 43: Eliminate Repeated Comparison Bundle Reads + +- Findings: `AUD-066`. +- Affected areas: comparison recognition/planning/publication and app + comparison preflight. +- Work: pass or cache only the recognition evidence needed to avoid four full + reads while retaining reauthorization at every mutation boundary. +- Locked behavior: early rejection, exact file/type validation, post-move + authorization, deterministic hashes, and concurrent destination safety after + Stages 34-36 remain unchanged. +- Documentation owners: comparison publication internals if ownership or + transaction phases change. +- Exit commands: focused read-count and adversarial replacement tests plus + `GOWORK=off go test -race -count=1 ./internal/comparison ./internal/app`, then + standard commands. + +### Stage 44: Consolidate Distributor Template Parsing + +- Findings: `AUD-068`. +- Affected areas: single and batch notification template rendering and config + tests. +- Work: extract one private grammar/parser with typed callbacks while + preserving each template family's variable allowlist and error context. +- Locked behavior: accepted syntax, rejected malformed forms, exact rendering, + and single-versus-batch policy remain unchanged. +- Documentation owners: none unless the user-visible grammar changes, which is + outside this behavior-preserving stage. +- Exit commands: focused config tests, then the standard repository commands. + +### Stage 45: Define One Fact Requirement Vocabulary + +- Findings: `AUD-070`. +- Affected areas: module requirement vocabulary, current briefing definitions, + identity/category metadata, and focused module/briefing tests. +- Work: define each requirement's identity and category once, preserve each + module's explicit requirement list, and lock equivalence with current valid + compositions. +- Locked behavior: all current compositions, supported reports, availability, + and diagnostic identity remain byte-for-byte or semantically unchanged. +- Documentation owners: module and briefing internals if ownership changes. +- Exit commands: focused module and briefing catalog tests, then the standard + repository commands. + +### Stage 46: Migrate Fact Availability And Constructor Validation + +- Findings: none; this completes the remediation begun in Stage 45. +- Affected areas: availability predicates, briefing constructors, legacy + switches, configuration validation, and focused tests. +- Work: route availability and constructor validation through Stage 45's + requirement vocabulary and remove superseded policy implementations. +- Locked behavior: all current compositions, supported reports, missing-data + policy, availability, and diagnostic identity remain unchanged. +- Documentation owners: module and briefing internals if ownership changes. +- Exit commands: focused module/briefing/config tests, then the standard + repository commands. + +### Stage 47: Consolidate CLI Report-Date Policy + +- Findings: `AUD-071`. +- Affected areas: generate/comparison flag registration and date resolution, + CLI parser/action tests, and private CLI descriptors. +- Work: establish one private report-date policy/resolver used by both actions + after Stage 6 has locked corrected behavior. +- Locked behavior: accepted flags, required/default dates, action-specific + errors, and request types remain unchanged. +- Documentation owners: none unless implementation reveals a current contract + mismatch; CLI docs remain authoritative. +- Exit commands: focused CLI parser/action tests and help commands, then the + standard repository commands. + +### Stage 48: Retire Dormant Prompt And Generated-Text Surfaces + +- Findings: `AUD-072`. +- Affected areas: unused prompt YAML loading/saving, normalized generated JSON + returns, and their sole-purpose tests and documentation. +- Work: verify these prompt/generated-text compatibility surfaces still have no + production owner, then remove them. If a production consumer appears, stop + and replace this retirement sequence with a separately approved contract. +- Locked behavior: normal Build/Validate/MarshalYAML, report publication, + comparison data packages, prompt debugging, typed normalization, and all + workflows remain unchanged. +- Documentation owners: prompt-input and generated-text internal documents; + remove stale descriptions rather than documenting retired APIs. +- Exit commands: graph/text caller checks, focused promptinput/generatedtext + tests, then the standard repository commands. + +### Stage 49: Retire Dormant Weather Persistence Helpers + +- Findings: none; this continues the dormant-surface remediation begun in + Stage 48. +- Affected areas: Weather API bundle saving, the JSON atomic helper if it + becomes orphaned, and sole-purpose tests and documentation. +- Work: verify these persistence helpers still have no workflow owner, then + remove them and any helper made unreachable by that removal. If a production + consumer appears, stop and seek a separately approved contract. +- Locked behavior: weather collection, normalized in-memory bundles, prompt + debugging, operator-owned publication, and all workflows remain unchanged. +- Documentation owners: weather-data and collection internals where stale + persistence claims exist. +- Exit commands: graph/text caller checks, focused weatherapi/weatherdata/ + fileutil tests, then the standard repository commands. + +### Stage 50: Retire Dormant Module And Forecast Compatibility Exports + +- Findings: none; this continues the dormant-surface remediation begun in + Stage 48. +- Affected areas: module snapshot compatibility implications, unused forecast + threshold exports, and sole-purpose tests and documentation. +- Work: verify the identified exports still have no production owner, then + remove them without broadening the supported compatibility contract. If a + production consumer appears, stop and seek a separately approved contract. +- Locked behavior: current module snapshots, real forecast decisions, prompt + exports, report rendering, and all workflows remain unchanged. +- Documentation owners: module, briefing, and forecast internals where stale + compatibility claims exist. +- Exit commands: graph/text caller checks, focused module/briefing/forecast + tests, then the standard repository commands. + +### Stage 51: Narrow Dormant Alert Envelope Copies + +- Findings: none; this completes the dormant-surface remediation begun in + Stage 48. +- Affected areas: unused full alert-envelope copies, retained alert item + provenance, and sole-purpose tests and documentation. +- Work: remove unconsumed alert-envelope data while retaining exactly the item + and provenance values required by current facts, briefing, and report paths. + If a production consumer appears, stop and seek a separately approved + contract. +- Locked behavior: alert selection, alert item provenance, briefing digests, + report rendering, and all workflows remain unchanged. +- Documentation owners: weather-data, facts, and briefing internals where the + narrowed value is described. +- Exit commands: graph/data-flow checks plus focused weatherdata/facts/ + briefing tests, then the standard repository commands. + +### Stage 52: Consolidate Generated-Text Test Ownership + +- Findings: `AUD-039`. +- Affected areas: generated-text day-style and render-context tests, + promptassets schema tests, and reporttemplate tests. +- Work: keep one shared semantic suite plus thin report-identity cases; put + schema ownership in promptassets and Markdown ownership in reporttemplate; + retain one real typed-context render integration. +- Locked behavior: every public validator identity, distinct Hourly behavior, + schema/type agreement, typed projection, template semantics, and composition + remain protected. +- Documentation owners: none. +- Exit commands: focused promptassets/generatedtext/reporttemplate tests, then + the standard repository commands. + +### Stage 53: Reconcile Stale Internal Implementation Guides + +- Findings: `AUD-075`. +- Affected areas: forecast derivation, collection, report registry, and prompt- + input internal guides plus incoming links. +- Work: replace nonexistent API/flow/test claims with concise descriptions of + the implemented owners and link to canonical contracts instead of duplicating + volatile details. +- Locked behavior: documentation-only; no executable contract changes. +- Documentation owners: the four affected `docs/internal/` files and + `docs/development.md` only if task routing changes. +- Exit commands: repository link/fence checks, focused test inventories used to + verify claims, `GOWORK=off go test -count=1 ./...`, and `git diff --check`. + +### Stage 54: Retire Completed Comparison Roadmaps + +- Findings: `AUD-076`. +- Affected areas: `docs/roadmap/profile-comparison.md`, + `docs/roadmap/implementation.md`, and all incoming links. +- Work: verify useful implemented contracts have one durable canonical owner, + repair incoming navigation, then remove the completed roadmaps so they no + longer duplicate current behavior. +- Locked behavior: documentation lifecycle only; no code, tests, dependencies, + schemas, or current contracts change. +- Documentation owners: CLI, operations, comparison integration/publication, + comparison execution, Promptkit integration, architecture, and development + docs only where an incoming link or missing durable fact requires correction. +- Exit commands: repository link/fence checks, both CLI help commands, + `GOWORK=off go test -count=1 ./...`, and `git diff --check`. + +### Stage 55: Verify Remediation Completion And Durable Ownership + +- Findings: none; this is the roadmap lifecycle gate. +- Prerequisite: Stages 1-54 are complete and every referenced finding's tests + and canonical documentation have landed. +- Affected areas: every remediation completion note, canonical documentation + owner, audit finding, retained decision, and incoming audit-document link. +- Work: verify every finding is resolved, superseded, or explicitly moved to a + newly approved roadmap; verify durable rationale lives with its canonical + owner; and produce a concise removal checklist for the temporary audit plan, + ledger, and remediation roadmap. Do not remove those records in this stage. +- Locked behavior: documentation lifecycle only; do not rewrite historical + ADRs or release notes and do not create a second current-state reference. +- Documentation owners: all durable owners touched by earlier stages, solely + to fill a demonstrated ownership gap. +- Exit commands: verify every stage/finding mapping and completion note; + repository link/fence and secret-pattern checks; all focused stage exits; + `GOWORK=off go test -count=1 ./...`, + `GOWORK=off go test -race -count=1 ./...`, both CLI help commands, + `GOWORK=off go vet ./...`, and `git diff --check`. + +### Stage 56: Retire The Completed Audit Records + +- Findings: none; this is the final documentation lifecycle change. +- Prerequisite: Stage 55 has verified completion and supplied the exact removal + and link-repair checklist. +- Affected areas: `docs/roadmap/audit.md`, `audit-plan.md`, this roadmap, and + their incoming links. +- Work: apply Stage 55's checklist, repair incoming navigation, and remove the + temporary audit plan, ledger, and remediation roadmap without changing + executable behavior or historical records. +- Locked behavior: documentation lifecycle only; durable current contracts and + rationale remain with their canonical owners, and ADRs and release notes are + unchanged. +- Documentation owners: only the navigation or durable owner files identified + by Stage 55. +- Exit commands: repository link/fence and secret-pattern checks; + `GOWORK=off go test -count=1 ./...`, + `GOWORK=off go test -race -count=1 ./...`, both CLI help commands, + `GOWORK=off go vet ./...`, and `git diff --check`. + +## Completion Tracking + +Update a stage heading or add a concise completion note only after its code, +tests, canonical documentation, and exit commands pass together. A stage that +begins a multi-stage remediation sequence must not mark its finding resolved +until the final continuation stage succeeds. Do not copy implemented contracts +into this roadmap. When all work is complete, follow Stages 55 and 56 rather +than retaining this file as a parallel reference.