# 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.** 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 **Completed.** 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.** 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 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 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 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 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.