Files
audita/docs/roadmap/implementation.md

382 lines
24 KiB
Markdown
Raw Normal View History

2026-11-11 04:00:47 +00:00
# Correction Repair Implementation Plan
Implement the stages in order, one assignment at a time. The [feature
roadmap](correction-repair.md) owns the intended behavior and operator choices.
The implementation baseline is `b036336`, with PromptKit v0.21.0 and ConfigKit
v1.2.0. Start with the [contributor guide](../development.md) and read the
applicable policies and component contracts as the work reaches them.
This is one end-to-end sprint. Intermediate stages may leave callers or tests
pending their later stages. Carry those changes forward directly and run a
focused check when its boundary is assembled. Complete the assembled checks in
Stage 9. Keep the existing fixed chunks, stage barriers, module scopes, provider
admission, and apply-time safeguards. Decision recovery for unavailable
validator responses remains [deferred](future.md#domain-aware-response-recovery).
## Stage 1 — Define the repair response and prove native conversation composition
**Completed.**
2026-11-11 04:00:47 +00:00
1. In `internal/framework/responseschema`, register `CorrectionRepairKey` with
schema ID `audita.correction_repair`, name `audita_correction_repair_v1`, and
asset `correction_repair.v1.json`. Use a closed JSON object with three
required fields below.
- `proposal_ref` is the one-based position in the **original correction
array**, including items the application later rejects. Each turn echoes
the same short reference for that proposal.
- `action` is `revise` or `withdraw`.
- `correction` is a nullable object. Its object shape uses the existing
correction item fields `id`, `original_text`, `corrected_text`, and
`confidence`. The `id` refers to a segment position in the original
section, independent of `proposal_ref`. Keep the existing correction
field constraints, including an empty replacement and a whitespace-only
original span.
2. Decode the response at the repair boundary. Require the expected reference
and the consistent action/payload pair (`revise` with an object,
`withdraw` with null). Treat an inconsistent pair or wrong reference as an
unusable response with the existing `invalid_output` reason. Keep source
span, segment applicability, and confidence threshold decisions with domain
validation. PromptKit owns structural validation and its configured repair
attempts.
3. Add a focused offline test at the PromptKit integration boundary, using the
new schema and a fixture with a multi-message proposal and a separate repair
definition. Use the original proposal ID and variables as the base of each
native request. Render the repair definition with `Engine.Prepare`. Append
the full assistant response from the proposal call, then the rendered
repair instruction using `RunRequest.AppendedMessages`. Set `Validation`
to the repair schema and execute via `PrepareExecution` and `RunPrepared`.
Exercise a second turn by appending the prior instruction and its full
assistant response once, followed by the newly rendered instruction. The
fixture should check the actual messages and schema received by a controlled
transport, including use of a separate repair profile.
4. Use this test to prove that the pinned API can reproduce the initial
prepared prefix and use a different output contract. The native renderer
uses the captured prompt and supplied template variables, and appends the
already-rendered suffix after that definition's messages. Check roles,
contents, and ordering in the prepared request. If the test exposes a
genuine gap, record the failing case and reproducer for a focused upstream
change. Keep rendering and execution with PromptKit.
## Stage 2 — Add and register the four module repair prompts
2026-11-11 04:13:55 +00:00
**Completed.**
2026-11-11 04:00:47 +00:00
1. Add the definitions `audita/modules.glossary.repair`,
`audita/modules.homophones.repair`, `audita/modules.spoken_word.repair`,
and `audita/modules.grammar.repair` beside the corresponding proposals in
`internal/prompts/assets/prompts`. Register them through `internal/prompts`
and the existing embedded PromptKit underlay. Each built-in module supplies
its repair prompt ID through a small `RepairPromptID() string` capability.
Retain the existing proposal interface for callers that only generate
candidates.
2. Use a user message in each shipped repair definition. Set its native output
contract to JSON schema using `correction_repair.v1.json` so standard
PromptKit inspection can prepare it with its own inputs. Put shared response
instructions in `RepairContract` and expose that fragment through the
existing shared-variable helpers. Render `RepairRequestJSON` from the
current proposal reference, current local segment reference, original and
replacement spans, confidence, and ordered findings. For findings, include
validator key, reason code, and available explanation. Convey an unavailable
assessment as such using safe facts from its decision or operation.
3. Make the instruction unambiguous after the earlier correction-set exchange.
Identify which proposal and current version it addresses, ask for one
revision or withdrawal, and describe the separate proposal and segment
references. Keep each module's existing edit scope, literal matching,
glossary/protection rules, and span deletion semantics. Treat transcript,
earlier model responses, and validator explanations as data in the prompt.
4. Cover schema shape, action and reference correspondence, empty-string
deletion, and real rendering of all four registered assets. Use the standard
inspection path for a replacement definition and a configured message file
through the existing source-loading path. The assembled branch test will
cover execution with that replacement.
## Stage 3 — Carry the original exchange through proposal generation and the adapter
**Completed.**
2026-11-11 04:00:47 +00:00
1. Use `internal/framework/structuredoutput` for internal conversation data
shared by the role client and runner. Extend the existing structured request
with optional repair replay data. Keep the repair definition as the
requested prompt ID in Audita and carry the original proposal ID, frozen
effective variables, and original prepared messages in a shared read-only
exchange. Retain the full final assistant content used to decode the
original correction set, including when PromptKit structurally repaired
earlier output. Capture the application-level exchange at the existing
prepared execution boundary.
2. Pass the exchange from `GenerateCandidates` through
`ExecuteModuleProposal` and `contracts.ProposalResult` to the section
worker in `runner`. Capture it on a successful proposal response when the
effective repair budget is positive, independently of debug settings.
Reuse the existing request-local segment mapping and keep each proposal's
one-based position in the response aligned with its runner-assigned
proposal index. Hold one initial exchange per section and share it among
branches. Keep the exchange out of ordinary serialized results and release
it when the section's repair work finishes.
3. In `internal/framework/llm/adapter.go`, render the current repair definition
against the repair profile via the same engine and append its rendered
message block to the branch suffix. Form the final request from the
captured proposal prompt ID and variables, the original assistant response,
prior turns from that branch, and the current instruction. Pass the repair
profile, per-call repair schema, timeout, transient retry budget, and
structural repair budget through the existing role client. Preserve
the original message roles and contents. Use the prepared handle that
passed the original-prefix comparison for execution. Treat a preparation
invariant failure as an application failure and discard the unused handle.
4. Return the rendered repair instruction block and the final response for
this turn, so a subsequent attempt appends that exact exchange once. Keep
the repair definition ID in debug attribution alongside native prepared
metadata for the base prompt. Reuse typed failure and terminal count
handling. Retain the existing distinction between caller cancellation,
local setup or invariant errors, and provider errors. An upstream API
rejection of a repair request follows the recoverable branch path even
when PromptKit supplies no usable model output.
5. Exercise the role client with the test from Stage 1 using a real registered
repair asset and an operator replacement. Verify full history, isolated
turns, selected repair backend/profile, and counts for one logical attempt
that uses a native retry or structural repair. Verify capture without
`--debug` and ordinary proposal behavior with repair disabled.
## Stage 4 — Resolve configuration and wire the repair role
**Completed.**
2026-11-11 04:00:47 +00:00
1. In `internal/core/config`, add `llm.correction_repair_attempts` as a
nonnegative integer, default `1`, and `llm.correction_repair_profile` as an
optional profile selection. Reuse the stage `llm` block. Omitted stage
attempts inherit the effective root value. An explicit stage zero clears
the inherited budget. Use ConfigKit's existing scalar validation and
presence rules, with no extra maximum for the new attempt count.
2. In `resolveSettings`, follow the recorded validation-profile pattern.
Resolve the effective stage proposal profile first. An explicit repair
profile selects that profile. An omitted stage repair profile inherits
an explicit nonnull root repair profile, otherwise it falls back to the
stage's effective proposal profile. An explicit stage null clears the root
override and uses that same fallback. At root, omission or null falls back
to the effective root proposal profile. Preserve recorded origins and
detached profile pointers. Use existing YAML, overlays, and native
application configuration inspection.
3. Extend `CheckReadiness` and the CLI processing wiring in
`internal/cli/process_inputs.go` and `process_pipeline.go`. Check the selected
repair profile and backend only for stages with a positive budget. The
existing captured engine validates registered prompt assets and their
overrides during loading. Retain that path for repair prompts. Allocate
a repair role client in the module run spec only for enabled stages,
using the same runtime, stage execution settings, admission, and optional
observation hooks. Unused repair profile IDs require no readiness check
when repair is disabled. A normalization-only run has no repair role.
4. Test root/stage inheritance including different proposal profiles, null and
explicit overrides, zero and positive budgets, native effective inspection,
and readiness before output preparation. Use the existing configuration
version and presence conventions.
## Stage 5 — Collect usable findings while preserving approval semantics
**Completed.**
2026-11-11 04:00:47 +00:00
1. Refactor `validateSectionCandidates` in `internal/framework/runner` so the
initial pass and later revisions use the same validation code. Keep the
existing shortlist flow when the effective repair budget is zero. In
feedback mode, track decisions by proposal index and version. Use
the current validator order and batching. Retain approval eligibility only
for candidates that satisfy all required checks. Collect additional findings
separately for candidates already rejected.
2. Make applicability explicit for the built-in checks using the current
candidate and the original section.
- Shape can evaluate malformed candidates. No-effect can evaluate literal
equality on a usable span, and confidence can evaluate a valid score
independently of target or span placement.
- Original-text presence needs a meaningful target and nonempty span.
Use the existing replacement preview and `ambiguous_original_text` reason
to provide an actionable finding when the span has multiple matches. In
feedback mode, record it before withholding the candidate from semantic
validation. A shape verdict for an unmapped reference already supplies
target feedback.
- Evaluate protection where the existing rule has usable before/after
evidence. Preserve active glossary match boundaries and spoken-word
repetition rules when they require full-segment context. Run the
nonempty-result guard when a replacement can be located, including a
preview that would leave a blank segment.
- Reuse the preview for semantic validators when it supplies usable
before/after text. Continue across independent semantic checks after
an actionable rejection or unavailable decision when the candidate still
supplies that input. Preserve the existing model batching budget.
3. Apply internal decision cardinality to the candidates actually submitted to
each validator. A skipped check supplies no decision. Keep missing,
duplicate, and unusable model decisions as unavailable under existing
reconciliation. Retain recoverable operation evidence. Route fatal errors
and cancellation through the existing peer cancellation and join path.
4. Build feedback from validator decisions and safe optional evidence that the
validator owns. Reuse existing reason codes and messages. Supply specific
evidence where a reason alone gives too little direction, such as the
applicable confidence threshold or a protected spelling when identified.
Use normal LLM decision explanations for semantic judgments. Preserve
debug redaction and avoid putting raw provider errors into feedback.
5. Test two independent meaningful findings after one rejects, a usable
rejected candidate that reaches the semantic checks, invalid target and
ambiguous span handling, and a no-effect candidate with no repair work.
Confirm that an unavailable decision alone remains unavailable and that
the zero-budget pass calls later validators with the existing shortlist.
## Stage 6 — Run independent repair branches and apply accepted revisions
**Completed.**
2026-11-11 04:00:47 +00:00
1. Extend the section validation worker in `runModulePipeline` after its
initial pass. Keep the approved candidates as they are. For each candidate
with an actionable rejection, initialize an independent branch on the
section exchange. No-effect and unavailable-only outcomes finish without a
branch. When an actionable rejection coexists with unavailable validation,
use the known findings and show the missing assessment as unavailable.
2. Visit repairable candidates in proposal-index order within each section
worker. Reuse existing section concurrency and engine admission. Each
logical repair call consumes one attempt from that proposal's budget.
Track attempts separately from native transient retries, structural
repairs, and revalidation calls. Send the full conversation to the selected
provider without local size checks or truncation. The validation batching
budget still applies to validator calls. End the branch on a provider error
after applicable native retries, including context-window rejection. Leave
other proposals eligible to continue. Keep the existing fatal/cancellation path for app
failures and parent cancellation.
3. Accept a valid withdrawal as a terminal correction outcome. For a
structurally usable revision with a matching proposal reference, map its
segment position using the original section, retaining the original
stage, chunk, and proposal index. Allow a changed target or span inside
that section. Run the full applicable chain from scratch against the
original section. Place a fully approved revision into the original
candidate's application position. A still-actionable rejection can take
another turn if budget remains. Use only the current version's findings
for the next instruction, retaining the earlier turns in that branch's
history.
4. End a rejected revision that has no further actionable finding with a
rejected disposition, and record an actionable rejection at the budget
boundary as exhausted. An unavailable-only revalidation, recoverable
repair-call failure, or unusable response ends the branch as unavailable.
Use the interrupted disposition when fatal failure or cancellation leaves a
branch in progress or prevents an approved revision from reaching
application. Preserve already-terminal dispositions. A revision needs its
own approval, and each response can supply at most one candidate.
5. Merge initial approvals and accepted revisions into the existing section
application path, keeping ascending proposal indices and all apply-time
checks against the text produced by earlier edits. Resolve applied versus
skipped status from the actual application result. Keep the stage-level
commit barrier. Count filtering once for a defective original that remains
excluded, using its original filtering reasons. An approved revision takes
that candidate's place in the existing application accounting. Attribute
defects in a later repair response to its own operation. Keep one terminal
proposal outcome per branch, with superseded versions available as debug
evidence.
6. Test a response with approved and rejected proposals on the same segment,
a successful retargeted revision, a valid span deletion, withdrawal,
exhaustion across two attempts, and overlapping edits where a repaired
candidate passes validation and then skips at application. Check unrelated
approvals survive a recoverable provider failure. Confirm revalidation
revisits a validator that approved an earlier version.
## Stage 7 — Account for operations and publish compact ordinary outcomes
**Completed.**
2026-11-11 04:00:47 +00:00
1. Add `correction_repair` as an operation kind in
`internal/framework/outcomes`. For each repair call retain its proposal
identity, attempt number, operation counts, and safe terminal reason.
Attribute revalidation calls to the proposal and attempt internally so
their local batch indices can repeat safely. Keep the normal proposal
and initial validation accounting, including proposal response counts.
Extend deterministic report sorting
to visit initial proposal and validation evidence first, then repairs by
proposal, attempt, and validator/batch order.
2. In `processreport`, `reporting`, and the runner's work-unit results,
count repair and revalidation executions as logical operations, including
failed provider requests. Keep existing accounting for pre-call failures.
Use the existing work-unit error paths for recoverable model
failures and the run-level path for fatal or canceled work. Keep failures
from earlier attempts visible when later work succeeds. A usable revision
or withdrawal is a successful model operation. Use existing native retry
and structural-repair reasons where applicable. Add `correction_revised`
to the repair operation that yields
a fully approved revision. This recovery identifies validation success.
Actual application is reflected in the final branch disposition and the
existing correction count. A clean withdrawal or ordinary rejected revision
adds no error or recovery by itself.
3. Add a single optional **Correction repair** section to `report.md`, with
one row per branch that attempted a repair. Use the same stage and chunk
numbering as the matrices, the original internal proposal index, the
semantic attempt count, and a final outcome. Use `applied`,
`approved_skipped`, `withdrawn`, `rejected`, `exhausted`, `unavailable`,
and `interrupted` as applicable. Derive the row from compact work-unit facts
after application or from the available interrupted evidence. Sort by stage,
chunk, and proposal index. Keep the section focused on these fields.
4. Keep the existing three output files, current YAML envelope versions, and
existing YAML fields. Document `correction_repair` and
`correction_revised` as additions to the operation and reason vocabulary.
Keep compact branch rows in the Markdown report. Reporting-disabled runs
still compute the existing CLI counts. Keep debug capture independently
selectable.
5. Verify the report for successful application, approval followed by skip,
withdrawal, exhaustion, provider failure, and partial evidence on fatal
interruption. Check that semantic attempts, actual edits, and PromptKit
retry/repair counts are distinct and that both YAML summaries agree with
the CLI on the counts they share.
## Stage 8 — Retain optional repair evidence and preserve ledger lineage
1. Extend the current interaction writers and logical execution observation
data with a repair role, original proposal index, and one-based semantic
attempt number. Use generated stage instance, section, proposal, and
attempt coordinates for repair filenames and include validator and batch
coordinates for revalidation. Original stage labels remain data. Reuse the
existing diagnostics failure and credential redaction path, including the
selected repair role's prepared credential references.
2. Save each repair attempt's rendered conversation, response, feedback,
and validation evidence through existing debug JSON artifacts. Keep these
writes conditional on an active debug directory and keep runtime repair
functional when preparation or writing of optional artifacts fails.
3. Extend `processreport.BuildCorrectionLedger` to emit one final row per
repaired proposal keyed by the original stage instance and proposal
index. Represent the original proposed edit and the final applied edit
separately, including when the revision retargets the proposal. Use
compact optional repair attempt metadata and artifact references to
connect versions and their respective decisions. Keep initial proposal
rejections as evidence within that lineage. Preserve the existing ledger
behavior for untouched candidates and the indexed interrupted join.
4. Test several repairs against a single segment and revalidation batches
that reuse local indices, then verify unique debug artifact names and
unambiguous ledger attribution. Check the terminal row and prior
decisions on interruption. Check redaction with synthetic credentials in
captured interaction material and verify that ordinary reports contain
only safe outcome data.
## Stage 9 — Validate the assembled command and update canonical documentation
1. Exercise the normal processing command with controlled provider responses,
real configuration and prompt loading, and the output session. Cover a
mixed proposal set with two independent repair branches, a second turn
on one branch, provider context rejection on the other, full revalidation,
and preserved original approvals. Inspect the wire order and contract.
Run the relevant behavior with debug off and with an operator repair prompt
replacement under the usual underlay. Cover zero budget and independent
report/debug selection. Reuse lower-level cases for individual failure and
disposition variants.
2. Add a controlled cancellation case during a repair or its revalidation.
Confirm active work joins, no completed transcript is published, and
failed reports preserve available partial evidence. Use the existing
subprocess fixtures and output assertions where they express the contract.
3. Update the owning [configuration](../config.md),
[pipeline](../internal/pipeline.md), [modules](../internal/modules.md),
[validators](../internal/validators.md), [prompts](../internal/prompts.md),
[LLM runtime](../internal/llm-runtime.md), and
[output files](../integrations/output-files.md) guides. Explain the default
attempt, per-stage control, profile fallback, complete replay to the
selected repair provider, provider failure behavior, and compact reporting.
Add minimal configuration or preview examples where needed in the existing
maintained material. Update [operations](../operations.md) if the new debug
evidence needs operator guidance. Keep the deferred decision-recovery
entry aligned with the implemented scope.
4. Validate examples and local links. Run the full offline suite and the
assembled CLI build under the [contributor guide](../development.md),
including race detection for the section workers. Check the focused
provider and ledger regressions in the normal suite. The sprint ends
with the implementation and its evidence.