Plan the Audita v2 migration

This commit is contained in:
2026-09-25 14:50:25 +00:00
parent 70f1dbfb59
commit 55c102c9ed
2 changed files with 554 additions and 271 deletions

250
docs/roadmap/audita.md Normal file
View File

@@ -0,0 +1,250 @@
# Adopt Audita v2
Status: agreed scope; implementation has not started.
## Goal
Migrate Narratio's `polish` integration from Audita's former command/configuration
contract to Audita v2.0.0. Keep Narratio responsible for orchestration and Audita
responsible for correction behavior and LLM execution configuration.
Adopt a consistent provider-named credential list for both Audita and Notarius,
so operators define each secret once and explicitly share it with the downstream
applications that need it. This is a clean cutover without backward-compatibility
shims.
Production runs use Audita's default reporting configuration: Narratio passes
neither `--debug` nor `--reporting=false`. Normal reports remain available without
enabling debug diagnostics. Adding debug reporting through Narratio's testing
profile is deferred.
This document defines the intended end state, migration scope, and policy.
The [implementation plan](implementation.md) owns implementation sequencing and
verification steps.
## Baseline and upstream contract
The baseline is Narratio v2.2.1 and the Audita checkout tagged v2.0.0. Upstream
references are Audita's `docs/migrations/v2.md`, `docs/cli.md`,
`docs/integrations/subprocess.md`, and `docs/integrations/output-files.md`,
available in the sibling `../audita/` checkout. Narratio's current contract is
documented in [Integration: Audita](../integrations/audita.md).
The current integration is not compatible with Audita v2:
- It requests `--report-json` by default; that flag has been removed.
- It can pass retired endpoint, model, concurrency, and work-directory-retention
flags.
- It expects one JSON report instead of the normal v2 report bundle.
- Its single forwarded `AUDITA_LLM_API_KEY` has no implicit meaning in v2;
an Audita backend must explicitly reference that environment variable.
- Its semantic fingerprint sorts modules, although Audita treats module order
and repetitions as meaningful.
Audita v2 requires a version-2 configuration file. An explicit `--config` selects
one; otherwise Audita performs its own documented discovery. Backend definitions
own endpoints, credentials, and capacity. PromptKit profiles own models and
per-attempt controls. Audita owns retry/repair policy and operation deadlines.
Narratio's generated invocation YAML is a diagnostic record, not a substitute
for that Audita configuration file.
## Target architecture
Retain the existing subprocess boundary and `polish` stage. Do not introduce a
second correction engine, an in-process Audita dependency, or a generic external
tool configuration framework.
Narratio owns:
- Selection of the executable and the overall invocation timeout/cancellation.
- Transcript and glossary inputs, run-local output placement, validation of the
corrected transcript, and promotion to the existing polished transcript
identity.
- Explicit configuration/profile selections and supported domain overrides.
- Deliberate credential forwarding, secret-safe invocation diagnostics, and
manifest references to results and reports.
- Existing stage completion, resume, force, publish, restore, and cleanup
behavior.
Audita owns its configuration schema and discovery, backend/model configuration,
provider execution, correction sequence, recovery behavior, and report contents.
Narratio does not translate old settings into a generated v2 configuration or
duplicate Audita's complete validation logic.
## Configuration and invocation migration
Keep binary, timeout, explicit config-path selection, and surviving explicit
domain overrides such as transcript description, output schema, and modules.
Allow Audita's native configuration discovery when no config path is selected;
do not impose an additional requirement to supply an explicit path.
Provide optional Audita overlay, proposal-profile, and validation-profile
selectors, passed through to their corresponding CLI flags. Omission leaves
Audita's own defaults and validation-profile inheritance in control. These are
Audita selectors, not automatic translations of Narratio profile names.
Retire the Narratio settings that mirror removed Audita endpoint/model/capacity
flags and work-directory retention. Move their operational meaning into the
operator-maintained Audita configuration and profiles. Remove stale adapter
fields, validation, invocation metadata, examples, and tests along with them.
Remove the old report toggle and both adapters' singular `llm_api_key` fields
from accepted Narratio configuration. Do not retain deprecated aliases,
automatic conversion, ignored fields, or a v1 execution path. Existing strict
configuration decoding rejects removed fields. Provide migration guidance and a
release compatibility notice rather than runtime compatibility machinery.
Preserve the distinction between omitted modules and an explicitly empty module
list: omission requests Audita's configured/default sequence; an empty list
requests no correction modules. Preserve order and repetitions rather than
sorting or deduplicating. Do not broaden this into a change to unrelated
adapters' module semantics.
Normal invocations no longer pass `--report-json`, `--work-dir-retention`, or
debug-work-directory flags as a way to obtain reports. They need no reporting
override: Audita writes its ordinary report bundle by default. The old
Narratio `report` toggle must not silently become `--reporting=false`.
The production Audita configuration must also retain reporting enabled and debug
disabled. Omitting CLI flags does not override an operator's explicit external
configuration. Document this distinction rather than silently adding forced
boolean overrides or rejecting otherwise valid Audita configuration.
## Credentials
Define secrets once under provider-oriented environment-variable names, such as
`OPENROUTER_API_KEY` and `RAKESTRAWHOME_API_KEY`, through the process environment
or Narratio's existing secrets-directory bootstrap. These names are examples,
not a fixed provider registry; application-specific names are not required.
Add `pipeline.audita.credentials` and `pipeline.notarius.credentials` as lists
of Narratio typed secret references. Each entry names the environment variable
whose captured value is forwarded under that exact name. Omission or an empty
list forwards no credentials. The same reference may appear in both adapters'
lists without duplicating its stored secret value. An illustrative configuration
fragment is:
```yaml
pipeline:
audita:
credentials:
- OPENROUTER_API_KEY
- RAKESTRAWHOME_API_KEY
notarius:
credentials:
- OPENROUTER_API_KEY
```
Resolve references through the existing configuration/secret-loading mechanism
before adapter execution, preserving its nonempty-value requirement. Forward
the captured values rather than rereading the parent environment during a run.
Retain the minimal child execution environment plus each adapter's explicit
credential list; do not forward every secret found during bootstrap.
Audita backends select these names through `credential`; the current Notarius
PromptKit profiles select them through `api_key_env`. Narratio neither inspects
downstream configuration to infer required credentials nor translates names,
registers providers, or duplicates backend configuration. Audita resolves
credentials for every surviving configured backend, even one not selected by
a profile, so its list must cover all those references.
Generalize Notarius's current single-name forwarding and replace Audita's fixed
`AUDITA_LLM_API_KEY` destination with this shared convention. Use existing shared
secret and subprocess facilities, extending narrow helpers where needed rather
than introducing a new secret-management framework. Register every forwarded
credential with captured-output redaction, including custom names and multiple
keys. Values must not enter arguments, generated configuration, manifests,
logs, or examples. As today, downstream tools remain responsible for files they
write directly; this does not add arbitrary downstream-file scanning.
## Ordinary reports and result handling
Replace the old `audita.report.json` handling with Audita's normal `errors.yml`,
`recoveries.yml`, and `report.md` bundle. With a file output, Audita writes these
beside the corrected transcript; an arbitrary report destination is not supplied
through the removed report flag.
Discover the reports at their defined run-local locations and retain them as
diagnostics using Narratio's existing manifest and diagnostic lifecycle. Keep
them distinguishable from the canonical polished transcript. Adapt report
references, publication/restore coverage, and operator inspection guidance so
the bundle is not lost or confused with an older run's JSON report. Do not
fabricate a legacy JSON report or delete historical diagnostics as part of the
migration.
Retain existing bounded, confined file handling. Replace JSON-specific report
handling with only the validation needed for consuming the v2 bundle; do not
introduce a second comprehensive report-schema validator or make incidental
Markdown wording a contract. Report locations must be discoverable without
parsing human-readable stderr. Detailed report-counter ingestion is not required
for this migration.
Audita's exit status remains authoritative. Exit zero with a valid corrected
transcript may include recoverable LLM failures, including a normalized result
with no model corrections. Preserve the reports so operators can assess that
outcome; do not add an unrequested fail-on-recovery or minimum-correction policy.
Conversely, a nonzero exit must fail polish even if a transcript file exists:
Audita can fail while writing required reports after writing the transcript.
Preserve available failure diagnostics without requiring early failures to have
produced a complete report bundle. Failed output must not become the canonical
successful result.
## Resume and semantic evidence
Update polish's semantic evidence for the new result-affecting selections and
remove retired endpoint/model fields. Include explicit config/overlay/profile
selection and preserve ordered module semantics, including omitted versus empty
selection. Version the changed evidence so incompatible older evidence is not
mistaken for a valid match when polish is considered for reuse.
Keep the existing boundary for opaque downstream configuration: Narratio does
not recursively hash Audita configuration, overlays, and profile contents.
Operators force polish when changing contents behind unchanged selectors.
Timeouts, credential values, reporting details, and other execution-only settings
must not become blanket semantic invalidation keys. Do not rerun unrelated or
unselected stages merely because the integration was upgraded.
## Migration guidance and verification
Update the canonical [configuration reference](../config.md),
[Audita integration contract](../integrations/audita.md),
[Notarius integration contract](../integrations/notarius.md),
[polish internals](../internal/stage-polish.md), maintained examples, and relevant
operations/troubleshooting guidance when implementation lands. Cover:
- Preparing and validating an Audita v2 configuration and its profiles before
switching the executable; copying an old v1 configuration is insufficient.
- Moving endpoint, model, capacity, and credential selections to their v2 owners.
- Replacing both singular `llm_api_key` settings with explicit `credentials`
lists, storing each provider key once, and matching downstream credential
references to those names. State clearly that old fields are removed.
- Using normal reports to inspect recoveries and failures without debug mode.
- Force/resume expectations and the treatment of existing completed sessions.
- The explicitly deferred nature of Narratio testing-profile debug support.
Verification should exercise the real adapter boundary with offline subprocess
fixtures and focused stage/configuration tests: accepted v2 arguments, omitted
defaults, ordered/empty modules, secret-safe forwarding, successful report
capture, recoverable-error success, nonzero exit with an existing transcript,
failure diagnostics, and semantic reuse/invalidation. Update existing behavioral
tests rather than retaining assertions for removed flags or exact stderr prose.
Where practical, verify invocation against the tagged Audita CLI without live
provider calls. Follow the repository's testing and documentation policies.
Credential tests must cover both adapters, multiple custom names, reuse of one
secret by both tools, no forwarding of unselected secrets, captured-value
semantics, and redaction of every forwarded value. Verify removal of obsolete
configuration fields without preserving their compatibility behavior.
## Scope boundaries
No changes to pipeline order, transcript identity, downstream artifact
definitions, or provider algorithms are intended. No automatic v1 configuration
converter, Audita-internal resume mechanism, runtime binary-version negotiation,
or live-service release gate is required. Debug-profile integration and ingestion
of Audita's debug-only execution artifacts are future work.
The Notarius change is limited to credential configuration, forwarding, and
associated diagnostics/documentation/tests; extraction and cache behavior are
unchanged. This migration breaks existing pipeline configurations using removed
fields; account for that explicitly in Narratio release versioning.

View File

@@ -1,316 +1,349 @@
# Implementation Plan: Shared Stage Checkpoint Mechanics
# Implementation Plan: Audita v2 and Shared Provider Credentials
Status: Complete.
Status: Not started.
## Scope and Authority
## Scope and working instructions
Implement the agreed follow-up to v2.2.0: consolidate the thin checkpoint
mechanism shared by transcribe and analyze without combining their progress
models or changing operator behavior. There is no separate consolidation
roadmap in this checkout. This plan uses the accompanying discussion as its
scope: persistence is already centralized; candidate isolation and
save-before-promotion are the duplication to remove. The completed
[transcription roadmap](transcribe-checkpoints-and-replay.md) provides background,
not authorization to reimplement transcription or HTTP replay. The preceding
completed implementation plan remains in Git history.
Implement [the Audita roadmap](audita.md). The baseline is Narratio commit
`70f1dbf` (v2.2.1), with Audita v2.0.0 as the upstream contract. This replaces
the completed checkpoint-refactoring plan, which remains in Git history.
Complete the numbered stages in order, one bounded implementation prompt per
stage for gpt-5.6-terra. Record completion and actual verification results here.
This planning pass changes documentation only; the steps below are for the
subsequent implementation sprint.
Complete Stage 1 through Stage 7 in order, one bounded implementation prompt per
stage for gpt-5.6-terra. Record completion and actual verification results under
each stage. These instructions apply to the implementation sprint; the planning
pass changes documentation only. Intermediate stages are not release candidates.
Do not add compatibility code to make intermediate states look like a supported
release.
Before each stage, read `AGENTS.md`, `docs/development.md`, and all three
documents in `docs/policy/`: architecture, documentation, and testing. Use
maintained Go 1.26. Prefer graph tools for code discovery; inspect current
files if their index is stale. Read the stage-specific preparation below,
rather than unrelated historical roadmaps.
Before each stage, read `AGENTS.md`, `docs/development.md`, and all three policies
under `docs/policy/`: architecture, documentation, and testing. Read this plan's
selected design and that stage's preparation. Use Go 1.26 and graph-first code
discovery; inspect current files when graph results are incomplete. Preserve
unrelated changes. Test at behavioral owners rather than replicating every case
across configuration, adapter, stage, and command tests.
## Target and Scope Boundaries
The user approved a clean cutover: no Audita v1 runner, deprecated aliases,
compatibility shims, generated configuration conversion, or silently ignored
old settings. Do not broaden that decision into a purge of unrelated legacy
code or historical session data.
The existing `internal/manifest.Store` and `LocalStore` remain the session
manifest persistence interface and implementation. Encoding, schemas,
validation, normalization, and atomic file replacement remain unchanged.
There is no new store, journal, file format, dependency, command, configuration,
or migration.
## Selected design
One private helper in `internal/stage` owns candidate preparation, calling the
existing store, and promoting the selected stage record only after successful
save. The existing typed analyze/transcribe checkpoint methods become thin
callers. This is execution plumbing: keep it beside the callers rather than
moving orchestration into the storage package or exporting a reusable API.
### Configuration and credentials
Keep these responsibilities stage-owned:
- Both `pipeline.audita.credentials` and `pipeline.notarius.credentials` are
lists of ConfigKit secret references. Use `[]configkit.Secret`; each element
is an actual reference rather than a nullable pointer. Existing ConfigKit
validation owns name/value resolution. Omitted, null, or empty lists mean no
additional credential forwarding.
- Define each key once through existing environment/secrets-directory bootstrap.
Forward it under its original name to each adapter whose list includes it.
No alias mappings, provider registry, downstream config inspection, extra
bootstrap, or inheritance of the entire environment.
- Repeated names need no new rejection rule: forwarding the same captured
reference once suffices. Arbitrary valid variable names remain supported;
provider names in the roadmap are examples, not an allowlist.
- Add a narrow shared helper in `internal/adapters/subprocess` converting resolved
references to `EnvOverrides` and `SensitiveEnvNames`. It checks the existing
resolved/nonempty requirement for direct adapter callers but never resolves
secrets or rereads environment values. Errors expose no values. Both adapters
use it; no dependency upgrade or credential service is needed.
- Configuration projections and diagnostic metadata contain reference names
only. Register all forwarded names for existing captured-stream redaction.
Downstream tools remain responsible for files they write themselves.
- Remove both singular `llm_api_key` fields. For Audita also remove `base_url`,
`model`, `validation_model`, `total_llm_concurrency`,
`proposal_llm_concurrency`, `validation_llm_concurrency`,
`work_dir_retention`, and `report`, including defaults and projections.
- Retain Audita `binary`, `timeout`, `config_path`, `transcript_description`,
`output_schema`, and `modules`. Add optional string selectors `overlay`,
`proposal_profile`, and `validation_profile`: empty/unset means no CLI
override. Audita owns downstream file/profile validation and native config
discovery. Narratio profile names are not implicitly Audita overlay names.
- Preserve nil versus non-nil empty `modules` through loading, projection,
cloning, composition, adapter defaults, and request overrides. Nil omits the
flag; empty passes `--modules` with an empty argument; nonempty joins in order
with repetitions intact. A nil request override uses the configured selection.
Avoid clones that collapse empty slices to nil. YAML null follows unset
behavior. Other adapters' module semantics do not change.
- Transcribe's completed-only speaker map, evidence checks, bounded concurrency,
mutex, checkpoint-failure latch, cancellation, pre-dispatch force reset,
output installation, and aggregate repair.
- Analyze's dispositions, selection, dependencies, catalog updates, working and
committed collections, failure reconciliation, invalidation, and outputs.
- Each stage's decision whether/when to checkpoint, typed validation, contextual
errors, and post-save working-state updates.
- Runner aggregate transitions, session locking, terminal failure saves, and
fixed dependent-stage invalidation.
### Invocation, reports, and failure
Do not introduce a common subtask model, scheduler, transaction manager, global
mutex, automatic retry, additional checkpoint, or batching policy. Do not
migrate publish/restore, planning clones, cleanup, or ordinary runner saves:
their operations are not the demonstrated duplication. Leave HTTP replay and
all adapters unchanged.
Use `process INPUT --glossary PATH --output PATH` plus selected surviving
overrides. Production passes no reporting or debug switches, including
`--debug`, `--reporting=false`, `--report-dir`, or `--work-dir`.
Remove required `WorkDir` and legacy `ReportPath` request/result plumbing.
Document production external-config defaults without forcibly overriding or
rejecting deliberate external reporting/debug choices.
## Selected Design
Replace Audita's long positional config-values constructor with typed runner
configuration. Keep timeout parsing in composition and subprocess cancellation
through the existing runner. No version probe or automatic config-validation
subprocess is required.
### Shared Save-and-Promote Operation
The corrected transcript remains the only canonical polish output, with existing
identity, path, validation, and bounded file handling. Discover exactly
`errors.yml`, `recoveries.yml`, and `report.md` beside the requested output.
Represent available paths in a small typed report bundle in `PolishResult`.
Fixed names belong to the adapter; do not parse stderr or recursively scan.
Add `internal/stage/checkpoint.go` with a private helper shaped as:
Reports are diagnostics, not a new correctness gate:
```go
func checkpointStageCollection(
ctx context.Context,
store manifest.Store,
path string,
live *manifest.Manifest,
stageName string,
update func(*manifest.StageRecord) error,
) error
```
- Bound reads of present reports using the existing named 16 MiB report limit;
retain confined regular-file handling and errors identifying Audita, the file,
and limit. No new smaller limits or report schema/status/count validation is
necessary when Narratio only preserves bytes; Markdown needs no parser.
- Missing files are not independently fatal: external configuration can disable
reports and early failures may produce none. Default-reporting success tests
must nevertheless demonstrate capture of all three files.
- On process failure, preserve available diagnostics and the original error;
report-inspection errors must not hide it. Never promote an output merely
because it exists following a nonzero exit.
- Exit zero with a valid transcript may include recoverable errors or no model
corrections. Accept it without a fail-on-recovery or minimum-correction rule.
Names are implementation choices, not public contracts. Use a function and
small copying helpers, not a factory/options/interface framework. The two
existing typed checkpoint methods are the only production callers. They pass
the already-resolved manifest path; the helper does not discover paths or
inspect configuration.
Keep reports next to the run-local transcript, not in a second canonical
destination. Register present report paths in the existing `StageResult.Logs`
diagnostic collection alongside stdout/stderr, with safe bundle-path metadata.
Reuse current manifest-driven diagnostic publication rather than adding a
manifest schema or new publish source IDs. Physical placement does not define
canonical artifact identity. Existing diagnostic retention/cleanup rules apply.
The helper performs this sequence:
On failure, retain run-local files and expose the output/report directory in
contextual errors. Do not add runner-wide failed-result persistence merely to
register those files. Do not fabricate legacy JSON reports or delete historical
reports. Normal successful stage-record replacement removes obsolete current
output references.
1. Check required live/store inputs without mutation. Copy a checkpoint
candidate and select a copied target record. If absent, provide an empty
candidate record to the caller, without inferring its name, status, or time.
2. Apply the supplied update to that candidate record only. On update error,
return without saving or promoting. The typed caller validates and fills
its version, collection, and stage-owned initialization/timestamps.
3. Invoke `Store.Save` once with the supplied context. Preserve error identity
through `%w` and useful path context. On error, do not promote, retry,
reload, or synthesize aggregate failure.
4. After success, install the saved target record and candidate manifest
`UpdatedAt` into live state. Preserve an existing target record's pointer
identity by assigning through it. Allocate missing live maps/records only
after success. Do not replace the entire live manifest or overwrite other
stage records. Copy mutable checkpoint collections on promotion so saved
candidates and caller working collections cannot mutate live progress.
Generated invocation YAML becomes `audita.generated.v2`, recording actual
arguments, selections, paths, and credential names, never values. This remains
a diagnostic invocation record, not an Audita configuration file.
Callers serialize the whole operation. Transcribe's current mutex does so;
analyze executes artifacts sequentially. Do not add a lock to the helper or
promise support for concurrent independent manifest writers. Atomic replacement
visibility does not become power-loss durability or a transaction covering
transcript files.
### Resume boundary
### Bounded Candidate Isolation
Bump only polish semantic-config version from 1 to 2. Record config path,
overlay/proposal/validation selectors, ordered modules with presence preserved,
transcript description, output schema, and existing output identity. Remove old
endpoint/model fields. Existing runner comparison reconsiders obsolete evidence
when polish is selected; do not synthesize equivalent fingerprints or expand
normal dependency/selection invalidation.
Factor `cloneManifestForTranscribeCheckpoint` into a shared checkpoint-scoped
copy helper in the new file. Use ordinary typed copies and existing
`manifest.CloneAnalyzeArtifactCollection` and
`manifest.CloneTranscribeSpeakerCollection`. Do not introduce reflection
cloning, JSON round trips, or a public general-purpose clone API.
Credentials, binary path, timeout, and diagnostics are not semantic keys. Do not
hash downstream config/profile contents; operators force polish after edits
behind unchanged selections. Notarius credential migration does not change
extraction fingerprints, references, force, resume, or chunk-cache behavior.
Copy the manifest value, stages map, stage records, and mutable collections
touched by these updates or `LocalStore.Save`. In particular, Save normalizes
analyze collections throughout the candidate, including on a transcribe
checkpoint. Clone those collections for every stage so normalization cannot
leak into live state on failure. Preserve the existing bounded copies of
input/artifact and output/log/config slices. Retain nil entries for existing
validation to reject, rather than silently repairing invalid manifests.
## Stage 1: Add shared resolved-credential forwarding
This is not arbitrary deep isolation of metadata. Neither the two update
callbacks nor the current Store mutates `map[string]any` metadata, so it may
remain shared read-only. Document that boundary and inspect every current
normalization write. Use existing typed clones for nested checkpoint evidence;
do not restrict legal metadata values or alter their Go/JSON representations
to simplify copying.
Status: Not started.
### Differences to Preserve
Preparation: `internal/adapters/subprocess/run.go`, its environment/redaction
helpers and tests; `internal/adapters/notarius/subprocess.go`;
`internal/config/credential_projection.go`; the pinned ConfigKit secret API.
- Analyze still requires an existing stage record. Its callback changes only
analyze version/collection, not transcribe-style initialization or timestamp
policy. It refreshes working and committed collections only after success.
- Transcribe retains missing-record initialization for direct stage calls:
its typed caller supplies name, running status, and initial timestamps when
needed. Its callback retains the existing stage `UpdatedAt` update. Copying
an existing record must not clear aggregate fields or invoke lifecycle
transitions.
- Existing transcribe live-record initialization is incomplete on the unusual
missing-record path; promoting the actual successfully saved candidate makes
its identity agree with disk. This does not add a new missing-record
rejection or change ordinary runner status transitions.
- Analyze rollback/failure handling and transcribe failure latching remain in
their execution owners. The shared operation never invents failed items.
- Preserve starting-checkpoint elision, force/reset semantics, empty versioned
collections, evidence and producer identity, legacy transcription handling,
supported zero-sized audio, and aggregate repair without HTTP calls.
- Do not freeze exact error wording, wall-clock values, copy choreography, or
iteration order into new contracts. No new schema, limits, filename rules,
statuses, or compatibility guarantees are authorized.
1. Add the narrow helper described above. Return an independent environment map
and selected sensitive names. No logging/serialization of values and no
changes to minimal child environment inheritance.
2. Test multiple custom names, empty lists, repeated names, unresolved references,
and captured values after the parent environment changes. Reuse existing
split/overlapping-stream redaction coverage rather than repeating its matrix.
3. Leave production callers unchanged until their migrations.
## Stage 1: Add the Shared Checkpoint Operation
Verify: `go test ./internal/adapters/subprocess` and
`go test -race ./internal/adapters/subprocess`.
Status: Complete.
Done when both adapters can reuse one small conversion mechanism without adding
a resolver, provider policy, or ambient-secret inheritance.
Completion marker (2026-09-22): Added the private stage-level checkpoint
save-and-promote helper and bounded candidate copying without migrating either
production caller. Focused real-store coverage proves live isolation through
updates, validation, save-time normalization and timestamps, save failures,
missing-target creation, pointer-preserving promotion, unrelated aggregate
preservation, caller-copy isolation, context/error identity, and exact one-save
behavior. Verified with `go test ./internal/manifest ./internal/stage`,
`go test -race ./internal/stage`, and `git diff --check`.
## Stage 2: Migrate Notarius to credential lists
Preparation: `internal/manifest/store.go`, `manifest.go`, `analyze_state.go`,
`transcribe_state.go`; `internal/stage/analyze_execution_state.go`,
`transcribe_execution_state.go`; `docs/internal/manifest.md`.
Status: Not started.
1. Add the private helper and bounded candidate copying described above.
Leave existing production checkpoint paths unchanged until their migration
stages. Do not duplicate manifest record-validation rules.
2. Add focused `internal/stage/checkpoint_test.go` coverage with real
`manifest.LocalStore` and temporary directories for successful save/reload.
Use a small Store wrapper only to observe/fail the persistence boundary.
3. Prove live progress remains old during Save, matches saved progress after
success, and does not change on update, validation, or save failure. Include
both typed collections and a transcribe checkpoint alongside analyze
records subject to save-time normalization. A failure test must exercise
candidate timestamp/normalization mutation before returning an error, not
merely fail before any work.
4. Prove unrelated stage/aggregate data survives, existing target references
observe updates, missing-target creation is invisible on failure, and later
caller collection edits do not mutate committed records. Preserve context
and error identity. Test the supported copy/write boundary, not arbitrary
malicious Store mutation.
Preparation: `internal/config/config.go`, `credential_projection.go`, loading/
inspection tests; `internal/adapters/notarius/runner.go`, `subprocess.go`;
`internal/stage/extract.go` and its request construction;
`docs/integrations/notarius.md` and secret handling in `docs/config.md`.
Verify: `go test ./internal/manifest ./internal/stage` and
`go test -race ./internal/stage`.
1. Replace the singular field with the typed list across configuration,
projection, request types, stage wiring, and adapter execution. Use Stage 1's
helper and register all selected names for redaction.
2. Update name-only metadata and config-show/diff/effective projections. Test
real ConfigKit loading, profile list replacement, and empty-list clearing.
Preserve bootstrap precedence and existing load-time reference resolution,
including references outside the selected stage range.
3. Migrate maintained examples, fixtures, and current Notarius configuration/
integration documentation for this field. Ordinary strict decoding rejects
the old field; do not add a compatibility parser.
4. Cover multi-key forwarding, unselected-key exclusion, empty selection,
captured-value behavior, and custom-name redaction on success/failure.
Keep extraction cache/reference/output tests as regression protection,
not occasions to redesign extraction.
Done when the shared integrity boundary is tested without changing existing
stage behavior. Temporary copying overlap is removed in Stage 3.
Verify: `go test ./internal/config ./internal/adapters/notarius ./internal/stage ./internal/app`
and `go test -race ./internal/adapters/notarius`.
## Stage 2: Migrate Analyze
Done when Notarius uses credential lists end to end and no active singular
credential path remains.
Status: Complete.
## Stage 3: Cut Audita configuration and adapter over to v2
Completion marker (2026-09-22): Replaced analyze's local candidate-copy,
save, and promotion choreography with the shared checkpoint operation while
retaining its existing-record requirement, typed validation, path resolution,
and post-save working and committed collection refresh. Existing recovery,
selection, invalidation, and rollback coverage required no duplication.
Verified with `go test ./internal/stage ./internal/app ./internal/manifest`,
`go test -race ./internal/stage ./internal/app`, and `git diff --check`.
Status: Not started.
Preparation: `internal/stage/analyze_execution_state.go`,
`analyze_checkpoint_test.go`, `analyze_failure_test.go`,
`internal/app/analyze_checkpoint_recovery_test.go`, and
`docs/internal/stage-analyze.md`.
Preparation: Audita v2.0.0 `docs/migrations/v2.md`, `docs/cli.md`,
`docs/integrations/subprocess.md`, and `docs/integrations/output-files.md` in
`../audita/`; Narratio `internal/adapters/audita/`,
`internal/app/runner.go` (`buildDefaultAuditaRunner`), and config types,
defaults, validation, and credential projection.
1. Replace manual candidate copying, saving, and live promotion in analyze
with the shared helper. Retain typed validation, required-record checking,
the typed update callback, and committed/working collection refresh.
2. Preserve starting-checkpoint elision, selected-artifact behavior, catalog
sequencing, dependent invalidation on changed output, and rollback after
checkpoint failure. Do not change the runner's terminal failure save or
joined errors.
3. Run existing tests before adding coverage. Retain tests proving earlier
artifacts survive failure/cancellation, unsaved output bytes gain no
authority, selected/forced work preserves appropriate unrelated artifacts,
and interrupted runs recover across store reloads. Add only missing
integration coverage; do not duplicate Stage 1's isolation matrix.
1. Replace the Audita configuration surface as selected above, updating defaults,
validation, safe projection, examples, and config fixtures. Prove nil/empty
modules survive real configuration loading and profile overrides.
2. Migrate runner construction, selectors, and credential forwarding. Remove
retired flags, fixed credential alias, required work-directory plumbing, and
old JSON-report validation. Add the typed report bundle.
3. Implement adjacent report discovery, bounded reads, failure results, and
generated invocation v2. Share metadata construction between success/failure
instead of maintaining duplicate field maps.
4. Rewrite the Audita subprocess fixture to model v2 argument acceptance and
output placement. Cover omitted/selected selectors, ordered/repeated/empty
modules, no report/debug switches, malformed/missing transcript, ordinary
reports, externally disabled reporting, recoverable-error success, and
nonzero exit after transcript creation. Cover multiple custom credential names
and redaction; preserve timeout/cancellation coverage at its existing owner.
5. Make mechanical polish and app-fixture adjustments needed to compile: remove
obsolete request/metadata fields and old report materialization. Stage 4 owns
complete diagnostic integration. Remove retired fingerprint members as needed
to compile; Stage 5 completes the replacement semantic contract. These are
bounded call-site changes, not permission to introduce transitional shims.
Verify: `go test ./internal/stage ./internal/app ./internal/manifest` and
Verify: `go test ./internal/config ./internal/adapters/audita ./internal/stage ./internal/app`
and `go test -race ./internal/adapters/audita`.
Done when configuration composes a v2-only adapter and subprocess tests prove its
credential/transcript/report boundary. Stages 4 and 5 are required before release.
## Stage 4: Integrate polish reports with diagnostic lifecycle
Status: Not started.
Preparation: `internal/stage/polish.go`, `polish_test.go`, `run_local.go`,
`stage.go`, `publish_sources.go`; result application in `internal/app/runner.go`;
publish/restore diagnostic tests; `docs/internal/stage-polish.md`.
1. Finish stage integration: materialize only the canonical polished transcript;
register available adjacent reports as diagnostics plus safe metadata and
existing logs/generated invocation references. Remove old `audita_report`
production and stale stage metadata.
2. Use existing authoritative-path checks for the requested transcript and fixed
adjacent report paths. Do not accept arbitrary fake-returned paths, scan
directories, or copy reports to a second destination. Preserve workspace
handling/modes.
3. On execution failure, preserve canonical transcript authority and run-local
diagnostics, with actionable directory/log context and original error identity.
Test failure after output creation.
4. Adapt the fake and app helper. With real manifest/store collaborators, prove
successful promotion, report references, absence of report canonical outputs,
and current-run diagnostic publication. Preserve restore's existing scope of
declared committed transcript/artifact objects: run-archive diagnostics do not
become automatically restored canonical objects. Test that report references
do not create new required canonical restore targets. Document remote diagnostic
inspection separately; no new diagnostic-restore feature is authorized.
5. Leave historical files untouched. Update tests expecting a second canonical
polish output without introducing a manifest-format migration.
Verify: `go test ./internal/stage ./internal/app ./internal/manifest ./internal/artifacts`
and `go test -race ./internal/stage ./internal/app`.
Done when reports use existing diagnostic lifecycle and failed execution cannot
promote an incidental transcript.
## Stage 5: Complete semantic evidence and resume behavior
Status: Not started.
Preparation: `internal/stage/semantic_contracts_refinement.go`, semantic
fingerprint tests, app runner semantic comparison tests, and extraction semantic
configuration tests (locate their current owners through graph discovery).
1. Implement polish evidence version 2 and the selected payload. Remove sorting;
preserve nil/empty distinction in payload encoding and copies.
2. Test changes from module order/repetition/presence and external selectors.
Credentials and execution-only settings do not change evidence. Avoid
assertions on incidental digest literals.
3. Exercise old-version evidence through runner resume behavior: selected polish
is reconsidered and normal dependent invalidation applies. Downstream-only
bounded execution does not unexpectedly invoke polish. Unchanged new evidence
reuses valid completed output.
4. Confirm credential-list changes leave extraction semantic/cache behavior
unchanged. No downstream file-content scanning or eager fingerprint migration.
Verify: `go test ./internal/stage ./internal/app` and
`go test -race ./internal/stage ./internal/app`.
Done when analyze delegates persistence choreography while progress, artifact
selection, and failure behavior remain unchanged.
Done when selections affect reuse accurately without blanket credential/config
invalidation or changes to unselected-stage execution.
## Stage 3: Migrate Transcribe and Remove Duplicate Machinery
## Stage 6: Complete operator migration documentation and examples
Status: Complete.
Status: Not started.
Completion marker (2026-09-22): Delegated transcription's locked checkpoint
operation to the shared helper while retaining typed validation, direct-call
record initialization, stage timestamp updates, post-save working collection
refresh, and the existing caller-owned mutex and failure latch. Removed the
duplicate production manifest clone; the read-only resume assertion now uses
the shared bounded snapshot. Existing real-store, concurrency, force/retry,
interruption, aggregate-repair, and zero-sized-audio coverage passed unchanged.
Verified with `go test ./internal/stage ./internal/app ./internal/manifest`,
`go test -race ./internal/stage ./internal/app`, and `git diff --check`.
Preparation: documentation policy; `docs/config.md`, `docs/operations.md`,
`docs/troubleshooting.md`, both integration guides,
`docs/internal/adapters.md`, `docs/internal/stage-polish.md`, and pipeline examples.
Preparation: `internal/stage/transcribe_execution_state.go`, `transcribe.go`,
`transcribe_resume.go`, `transcribe_checkpoint_test.go`,
`transcribe_reconciliation_test.go`,
`internal/app/transcribe_checkpoint_recovery_test.go`, and
`docs/internal/stage-transcribe.md`.
1. Complete current-behavior documentation as implementation lands. Configuration
owns fields/defaults; integrations own external contracts; operations owns
placement and migration; internal docs explain ownership and link onward.
2. Provide an old-to-new migration table: endpoints/capacity move to Audita
backends, models to profiles, role selection to selectors, and credentials to
provider-named lists. Remove report/retention settings. Explain Audita's
all-configured-backend credential resolution and Notarius's profile lookup.
3. Describe preparing and validating Audita v2 config offline with its documented
command before switching executables. Link upstream examples rather than
building a downstream configuration generator. Explain ordinary reports,
recoverable-error success, and force after edits behind unchanged selectors.
4. Production examples omit modules when upstream defaults are intended;
`modules: []` deliberately disables correction modules. Production external
Audita config keeps reports enabled/debug disabled, without Narratio flag
overrides. Testing-profile debug integration remains deferred.
5. Document the configuration-breaking clean cutover for release-version
consideration. Do not edit historical release records, tag a release, or
remove unrelated roadmaps.
1. Delegate `checkpointCollectionLocked` to the shared helper, retaining
typed initialization/update and successful working-collection refresh.
Keep the existing mutex across candidate creation, save, and promotion.
Do not move HTTP/hashing work into that critical section or add a lock.
2. Preserve failure-latch checks and worker joining, the parent versus worker
context distinction, force's durable empty starting collection, and
ordinary retry's reuse of committed forced replacements.
3. Remove `cloneManifestForTranscribeCheckpoint`. Its current use by a
read-only resume test is not a reason to retain duplicate production code.
Use an independent test snapshot or the shared bounded copy where sufficient,
without exporting a helper for tests.
4. Retain existing real-store and concurrency coverage: no lost completions;
pre-dispatch save failure prevents requests/canonical writes; later failure
retains prior authority; interrupted and aggregate-save recovery; failed
force followed by retry; run-directory independence; zero-sized audio;
removed/mismatched aggregate repair without additional WhisperX requests
or removed-speaker merge input. Do not weaken these regression assertions.
Verify: `go test ./internal/stage ./internal/app ./internal/manifest` and
`go test -race ./internal/stage ./internal/app`.
Done when both stages use one save/promote implementation and one bounded
copy implementation, with their different models and synchronization intact.
## Stage 4: Document, Review Scope, and Validate
Status: Complete.
Completion marker (2026-09-22): Documented the private shared checkpoint
persistence boundary and linked the stage guides back to it while retaining
their separate analyze and transcribe progress policies. The refactor removed
the two local candidate/save/promotion sequences and the transcription-only
bounded clone; the retained thin wrappers own typed validation, updates,
working-state refresh, and their existing serialization and failure behavior.
The collective test review retained focused helper integrity coverage plus
stage/application recovery tests, without duplicating the helper's isolation
matrix. A cumulative scope audit found no schema, validation-policy,
scheduling, force, concurrency, output-selection, stage-order, adapter-call,
configuration, or public-interface change.
Verified with `go test ./...`, `go test -race ./...`, `go vet ./...`,
`go build ./...`, `go test ./internal/doccheck`,
Verify: `go test ./internal/doccheck`,
`go test ./internal/config -run '^TestExamplesLoadAndValidate$'`, and
`git diff --check`; all passed.
`git diff --check`.
Preparation: `docs/internal/manifest.md`, `docs/internal/stage-transcribe.md`,
`docs/internal/stage-analyze.md`, documentation/testing policies, and the
implementation diff against this plan's starting commit.
Done when operators can migrate using valid examples without obsolete fields,
implicit credential aliases, or retired CLI/report expectations.
1. Update manifest internals to identify the shared checkpoint owner and copy/
save/promote boundary. Stage guides retain stage-specific policy and link
to that explanation instead of duplicating shared implementation details.
Do not rewrite immutable release notes or announce new operator behavior.
2. Assess tests collectively. Consolidate genuinely duplicated persistence
mechanics coverage, but retain stage/application recovery tests proving
correct integration. Avoid test-count/LOC quotas. Record a brief summary
here of duplicated mechanisms removed and thin wrappers retained.
3. Confirm no schema, validation policy, scheduling, force, concurrency, output
selection, stage order, adapter call, or configuration change slipped into
the refactor. Additional restrictions require a separate decision, not
tests that retrospectively declare new policy.
4. Run all gates below and record actual results. Mark this plan complete only
when both migrations and verification pass. Do not release, tag, push, delete
other roadmaps, or expand into a repository-wide cloning refactor.
## Stage 7: Verify assembled behavior and audit scope
Status: Not started.
Preparation: cumulative diff, this plan, feature roadmap, app configuration/
subprocess integration tests, and all policies.
1. Adapt or add one representative offline app-level scenario showing one
provider-named secret source supplying both adapters and another key selected
only for Audita. Use real config/bootstrap/composition with subprocess fixtures.
Prove safe inspection/logging and successful polish reports. Leave exhaustive
flag/redaction cases at lower layers.
2. Where the tagged Audita checkout is available, smoke-test CLI acceptance and
ordinary transcript/reports offline with an explicit minimal v2 config,
unauthenticated backend/profile fixtures, and an empty module sequence.
No provider requests or production credentials. This is optional local evidence,
not a default test dependent on sibling checkouts or installed tools.
3. Audit active references to removed fields/flags/aliases; historical release
notes and completed roadmaps are not compatibility code. Review tests
collectively for duplication while retaining consequential failure coverage.
4. Confirm no provider-name restrictions, duplicate-name rejection, downstream
content hashing, fail-on-recovery rule, mandatory explicit config path, debug
requirement, live-service gate, pipeline-order change, or Notarius cache change
slipped in. Do not require missing reports when disabled externally. Surface
additional policy choices rather than encoding them silently in tests.
5. Run every gate below and record actual results. Mark complete only after all
stages and gates pass. No release, tag, push, or unrelated cleanup in this plan.
```sh
go test ./...
@@ -322,12 +355,12 @@ go test ./internal/config -run '^TestExamplesLoadAndValidate$'
git diff --check
```
Done when duplication is removed, recovery remains covered, documentation
matches the implementation, and all gates pass.
Done when the assembled v2 integration and shared credentials meet the roadmap,
verification passes, and no compatibility machinery remains within scope.
## Open Questions
None requiring a product decision. A private stage-level helper is selected
because persistence already has an owner and the duplication is execution
plumbing. Model unification, library extraction, generalized transactions, and
broader cloning cleanup remain out of scope.
None requiring a product decision before implementation. The user settled the
clean cutover and shared provider-named lists. Reports use existing diagnostics
without new content-based success criteria; debug-profile work remains deferred.
Release version selection belongs to the later release procedure.