Repair-on-prepare and aggressive entity upgrade — self-healing posture vs strict downshift
Status: open/proposed
Summary
Section titled “Summary”- Current posture is mostly detect-and-defer:
task-ensure-readyanalyzes a task spec, flags drift from the readiness contract, downshifts toplanning/needs-definitionwith adefinition_gapfield, and exits. The one repair path today is the autonomy gate:autonomy: autonomous/prtasks get a single/sdlc:task-auto-definefill-and-re-verify pass before any downshift./sdlc:entities-migrateapplies deterministic-mechanical fixes only (schema_versionstamps plus the versioned body transforms); non-mechanical drift surfaces and waits for human attention. - Proposed posture is active-repair-with-confidence-gate: both surfaces gain an LLM-orchestrated repair pass that attempts to fix the drift inline. Mechanical fixes ship without ceremony; non-mechanical fixes either ship if the LLM’s confidence clears a gate, or fall back to today’s downshift/PR-for-review behavior.
- Three concrete surfaces affected: (1)
task-ensure-ready(or a newtask-preparestep) gains a repair mode that fixes spec drift before downshifting; (2)/sdlc:entities-migrateextends to handle non-mechanical upgrades via LLM orchestration; (3)/sdlc:task-workinvokes the repair-mode prepare step so a single dispatch can repair-and-continue without an out-of-band cycle. - This ADR sets the posture and the confidence-gate principle. Specific repair recipes, gate thresholds, and skill-or-flag shape are downstream tasks.
Context
Section titled “Context”The 2026-05-30 /loop /sdlc:orchestrate session surfaced a sharp version of the friction this ADR
addresses. Seven freshly-authored M0001 tasks (T0002, T0004, T0005, T0006, T0009, T0011, T0012) were
dispatched to /sdlc:task-work. All seven hit task-ensure-ready and downshifted to
planning/needs-definition with definition_gap populated. Each gap was almost identical in shape:
the ## Today section used a pre-v3-contract layout (bulleted-legacy, missing entirely, or a
3-column variant instead of the required | Location | Role today | 2-column shape).
These tasks were not wrong relative to their authoring moment — they predated the v3 readiness contract’s tightening. Schema evolved, instances did not. The downshift correctly surfaces the drift, but the operator burden is now:
- Read each downshift’s
definition_gapfield. - Open a
/sdlc:task-definesession per task (interactive prompts). - Edit each Today section, plus any other drift items in the gap.
- Re-validate, re-dispatch task-work.
- Wait for the next round of escapes.
The same pattern exists for entity instances. /sdlc:entities-audit flags drift;
/sdlc:entities-migrate mechanically fixes the auto-fixable subset (schema_version stamps and
the deterministic v_n→v_n+1 body transforms); everything else surfaces for manual edits. When a
schema bump adds a required field, every existing instance is “broken” until a human revisits each
one.
In both cases the drift is often deterministic-ish — most readiness-contract drift is reformatting; most schema-bump drift is adding-a-field-with-a-default-or-empty-value. An LLM with the spec and the gap could repair the high-confidence cases without operator round-tripping. The low-confidence cases keep today’s surface-and-defer behavior, so operator trust isn’t sacrificed for autonomy.
The current rigid posture also costs the orchestrator: every wasted parallel dispatch consumes a sub-agent run, lease churn, worktree creation, and operator attention to triage. Repairing inline within a single task-work pass would convert 6 downshift round-trips into 6 inline repairs.
Decision
Section titled “Decision”-
Adopt an active-repair posture for two surfaces: task readiness (
task-ensure-readyor a newtask-preparestep) and entity-instance migration (/sdlc:entities-migratenon-mechanical extension). Both default to repair-attempt-first, downshift-or-PR-on-low-confidence fallback. -
Confidence-gate every repair. A repair attempt classifies its proposed change as one of:
- high-confidence-mechanical — pure structural reformat against a known contract (e.g.,
bulleted-list → 2-col table when the spec already cites paths in backticks). Ships inline;
commits with a
repair=annotation. - medium-confidence-content — content edits with clear intent (e.g., filling a
## Todaysection based on the rest of the spec’s Files-to-touch + Goal). Ships inline iff a per-skill threshold is met; otherwise opens a PR for review. - low-confidence-or-ambiguous — substantive content authorship. Falls back to today’s
behavior (downshift +
definition_gap, or audit-flagged-not-migrated).
- high-confidence-mechanical — pure structural reformat against a known contract (e.g.,
bulleted-list → 2-col table when the spec already cites paths in backticks). Ships inline;
commits with a
-
Preserve the downshift signal. Repair never silently succeeds-but-wrongly: every shipped repair leaves a recoverable diff (commit annotation, definition_gap-resolved field, or PR), and every low-confidence case downshifts as today. The operator always has a trail.
-
Repair-and-continue inside task-work.
/sdlc:task-workinvokes repair-mode prepare BEFORE deciding to dispatch implementation. A successful inline repair flows directly into Steps 5b–10 without a separate cycle. A repair that downshifts halts task-work as today. -
Entity-migrate handles non-mechanical upgrades.
/sdlc:entities-migrategains an LLM-orchestration mode that attempts confidence-gated content repair on instance drift (filling new required fields with inferred values, rewriting deprecated field shapes, etc.). The mechanical-only path remains for the unsupervised auto-fix use case; the LLM mode is opt-in with--repair(default off until trust is established). -
Per-entity repair recipes. Each entity’s
definition.mdmay declare repair recipes per known drift class (e.g.,## Todayreformat). The recipes are advisory hints the LLM consumes; absence of a recipe falls through to general-purpose repair. -
Per-instance
allow_repair:opt-in. Every entity instance carries an optionalallow_repair:field in frontmatter. Three values:none— repair is disabled for this instance; the existing strict-downshift behavior is the only path. Use for safety-critical or human-authority artifacts.mechanical— only high-confidence-mechanical repairs are attempted; medium/content drifts downshift.content— full repair pass including medium-confidence content edits; only low-confidence ambiguous cases downshift.
The default is project-level:
sdlc.yaml’srepair_mode:(none/mechanical/content) sets the project default; the per-instance field overrides. Initial recommended defaults:repair_mode: mechanicalat project;allow_repair:unset at instance (inherits project). -
Codified repair audit trail at
.sdlc/repair-log.md. Every repair attempt — successful, downshifted, or skipped — appends one structured line to a gitignored project-local log. The line carries:- timestamp (ISO 8601 UTC)
- skill name (e.g.,
task-ensure-ready,entities-migrate) - target entity (type + id/basename)
- contract violation class (the rule name that triggered the repair attempt, drawn from a fixed enum per skill — see “Violation taxonomy” in the implementation task)
- repair classification (
mechanical/content/ambiguous) - outcome (
shipped/downshifted/skipped-by-opt-in/error) - confidence summary (if applicable; LLM self-report or heuristic)
- sub-agent run id (cross-reference to dispatch logs)
The log is the canonical source for the post-mortem feedback loop (item 9). Format mirrors the orchestrator’s tick digest at
.sdlc/orchestrator-log.md; one tool reads both. -
Post-mortem feedback loop via
/sdlc:repair-postmortem. A new skill aggregates.sdlc/repair-log.mdover a configurable window (default: last 30 days) and surfaces:- Recurring violation classes — which contract rules trigger the most repair attempts.
High-frequency rules are candidates for promotion to deterministic enforcement (move the rule
from LLM-judgment into a parser like
parse-touchpoints.ts). - Repair success rate per violation class — which violations are reliably auto-repaired vs which keep downshifting. Reliable repairs → widen the confidence gate; unreliable ones → tighten the prompt or clarify the contract.
- Per-entity repair pressure — entity types accumulating downshifts faster than fixes signal
a schema/contract that’s drifted too far ahead of its instances; surfaces as a candidate for an
aggressive
entities-migrate --repairsweep. - Recipe coverage gaps — violations with no documented per-entity recipe (item 6) get flagged; the post-mortem proposes recipe additions.
The post-mortem output is itself a markdown artifact (e.g.,
docs/planning/repair-postmortems/<date>.md) reviewable as a planning input, not a one-shot console dump. Running it is the project’s mechanism for closing the loop between observed friction and adjusted policy — the same posture this ADR proposes at the per-repair level, applied at the corpus level. - Recurring violation classes — which contract rules trigger the most repair attempts.
High-frequency rules are candidates for promotion to deterministic enforcement (move the rule
from LLM-judgment into a parser like
| Today | Cost |
|---|---|
| Every drift-surface event requires an operator round-trip (review the gap → run task-define → re-validate → re-dispatch) | Triage burden scales with schema/contract churn |
| Parallel orchestrator dispatch wastes sub-agent runs when half the queue is silently drifted | Token spend + operator-attention spend |
/sdlc:entities-migrate ignores non-mechanical drift | Schema bumps strand instances; the operator becomes the migration engine |
| Sub-agents that COULD have repaired the drift instead correctly diagnose it and bail | Wasted sub-agent runs + the verdict-contract-escape failure mode amplifies (sub-agents stop at intermediate markers when they didn’t reach the work) |
| Proposed | Win |
|---|---|
| High-confidence drift repairs inline, no round-trip | Operator round-trips drop by ~the high-confidence rate |
| Low-confidence drift still downshifts (preserves signal) | Trust posture unchanged for genuinely-ambiguous cases |
| Entity-migrate handles non-mechanical via LLM mode (opt-in) | Schema bumps become a single supervised PR vs N manual edits |
| Repair-and-continue inside task-work | One dispatch per task instead of {dispatch → downshift → triage → re-dispatch} |
Tensions and open questions
Section titled “Tensions and open questions”(Captured here so the implementation tasks don’t relitigate them; resolve in followups.)
-
Confidence-gate calibration. Mechanical-vs-content is a continuum, not a binary. Initial thresholds will be conservative; we’ll widen as evidence accumulates. The gate’s measurement is open — could be LLM self-report (“I’m 90% confident”), heuristics (number of fields touched, change size), or a separate critic pass.
-
Skill shape: extend or new? Three options for where the repair lives:
- Extend
task-ensure-readywith a--repairmode flag. - Promote a new
task-prepareskill that wraps ensure-ready + the repair pass. - Make repair an
Operation(per D0004) that any entity can declare and the framework dispatches. The last option is the most principled but the largest scope; the first is the smallest. Decide per skill.
- Extend
-
Violation taxonomy per skill. Decision item 8 commits to logging a “contract violation class” per repair event; that requires a fixed per-skill enum naming each rule. The taxonomy is the work of the implementation tasks (one enum per skill that runs repair:
task-ensure-ready,entities-migrate, etc.). Naming consistency across skills is the open call. -
Cascade with
--ready(T0015). Repair-on-prepare reduces the need for--readyfiltering by removing some drift before dispatch. But T0015’s--readystill applies for actual unmet deps. The two compose:--readyfilters dispatchable candidates; prepare repairs whatever it can on the remaining ones. -
Failure mode coupling with verdict contract. A repair that goes wrong inside a sub-agent risks the same verdict-contract escape we’ve seen — sub-agent does repair, emits intermediate marker, parent thinks no progress made. The repair design should specify its own terminal verdict shape (per the namespacing convention).
-
Post-mortem cadence. Decision item 9 ships the
/sdlc:repair-postmortemskill, but doesn’t fix how often to run it. Three modes: (a) manual on demand, (b) scheduled via/loopor/schedule, (c) auto-fired by the orchestrator when the repair-log accumulates N entries since the last post-mortem. Initial recommendation: manual + scheduled at milestone-close cadence. -
Promotion path from LLM-rule to deterministic-rule. When the post-mortem flags a recurring violation as “always-mechanical-always-shipped,” the recommendation is to promote that rule into deterministic enforcement (e.g., add a check to
parse-touchpoints.ts). This is a planning input, not an automatic action; the post-mortem proposes, a human authors the migration. Worth codifying as a Standard later.
Implementation sketch
Section titled “Implementation sketch”(Non-binding; the implementation task(s) will scope concretely.)
| Surface | Repair scope | Skill shape |
|---|---|---|
| Task readiness | Reformat ## Today per v3 contract; normalize Files to touch Kind values; rewrite obsolete dep references when schemas tell us the canonical shape | task-ensure-ready --repair flag (small) or task-prepare skill (medium) |
| Entity instance migration | Add required fields with type-appropriate defaults; rewrite deprecated field shapes; LLM-fill content for new required body sections | /sdlc:entities-migrate --repair flag |
| Task-work pre-flight | Invoke repair-mode prepare; on success → continue to Step 5b; on confidence-fall-through → today’s downshift | /sdlc:task-work Step 5a internal |
Migration / rollout
Section titled “Migration / rollout”- Ship the smallest repair surface first:
task-ensure-ready --repairfor the## Todayreformat recipe. Validate against the current backlog of needs-definition tasks (live test of T0002, T0004, T0005, T0006, T0009, T0011, T0012). Document hit/miss rate. - Extend the repair recipes per surfaced drift class.
- Wire
/sdlc:task-workto invoke repair-mode prepare by default. - Mirror to
/sdlc:entities-migrate --repair. - Calibrate confidence-gate thresholds based on accumulated evidence; adjust defaults.
Out of scope
Section titled “Out of scope”- A formal repair-recipe DSL. Repairs are LLM-orchestrated prose-driven; structured DSL is a future iteration once recipes stabilize.
- Replacing the validator with the repair pass. Validators stay deterministic and authoritative; repair is an attempt-to-pass-the-validator helper.
- Cross-entity repair coordination (e.g., a task’s repair affects a milestone’s
tasks:list). Each surface repairs its own entity; cross-references are out of scope here. - Removing the operator from the trust chain. Every shipped repair leaves a reviewable trail; this ADR does not propose silent autonomous mutation.
Evidence and discovery — 2026-05-30 session
Section titled “Evidence and discovery — 2026-05-30 session”The case for this ADR is concrete. A single /loop /sdlc:orchestrate session surfaced a cluster of
friction events that share one root pattern:
rigid contracts + immediate-strict-failure + no repair pass.
Where the rules actually live (the drift surface)
Section titled “Where the rules actually live (the drift surface)”Readiness rules for tasks are scattered across five layers, each authored independently:
| Layer | File | Authority | Drift risk |
|---|---|---|---|
| Contract doc | plugin/lib/model/entities/task/implementation-ready.md | Source-of-truth prose the LLM reads each run | High — single doc, but its intent is LLM-interpreted |
| Skill prose | plugin/skills/task-ensure-ready/SKILL.md | Tells the LLM how to walk the contract; restates several rules inline | High — duplicates contract content; drift between the two is exactly the bug class |
| Body parser | plugin/lib/model/entities/task/ops/parse-touchpoints.ts | Deterministic — validates table shape, location grammar, prose-vs-table detection | Medium — code-vs-prose drift, but at least testable |
| Placeholder scanner | plugin/lib/model/entities/task/ops/scan-placeholders.ts | Deterministic — finds <placeholder> and thin sections | Medium |
| Frontmatter schema | plugin/lib/model/entities/task/schema.ts | Deterministic frontmatter schema | Low — the validator binds to it directly |
Three flavors of drift were observed live:
| Drift class | Observed |
|---|---|
| Contract ↔ SKILL.md (two prose docs) | Both document the Files-to-touch existence check, with subtly different phrasings; the LLM working from one may infer different scope than working from the other |
| Contract ↔ deterministic parser (spec vs code) | The contract enumerates 5 Location forms; parse-touchpoints.ts implements them. Either side can change without the other catching up |
| Contract ↔ LLM inference (spec letter vs spirit) | “Today rows must resolve to existing files” is not in the contract text. The sub-agent inferred it from “describing the relevant area today.” Different sub-agent runs could legitimately infer different rules from the same contract document. This is invisible — no test catches it |
Failure-mode catalogue from a single session
Section titled “Failure-mode catalogue from a single session”Each row is a real event with a commit reference or PR number:
| Surface | Event | Root | Resolution this session |
|---|---|---|---|
| Validator path | The frontmatter validator’s default --entities-dir was narrowed by PR #176 but task/backlog/epic entities weren’t migrated to the new path → every task-ensure-ready invocation failed silently | Migration gap | Migration promoted into M0001 — commit 6328178 |
| Lease protocol | sdlc lease task claim writes a random UUID into owner; sub-agent’s discover_lease() compares against stable current_host_id() → always mismatch → spurious LEASE-CONFLICT for every orchestrator-pre-claim | Two subcommands diverged (slice-1 claim placeholder vs slice-2 acquire) | Switched orchestrate spec to task acquire — commit 287b0c4 |
| Verdict contract | Sub-agents emit intermediate sub-skill markers (ENSURE-READY-OK:) as their terminal verdict instead of TASK-WORK-DONE pr=#<N> | LLM literal-mindedness; namespacing helped but didn’t close gap | Added lenient last-match scan for verdict-buried-in-prose — commit af52619. The wrong-marker-class escape still recurs (T0001, T0004 lost their post-Step-5a work this session) |
| Readiness strictness | Verifier accepts */?/[...] globs but not {a,b,c} brace expansion; Today rows must resolve to existing files; one downshift becomes triage-edit-revalidate-redispatch with no convergence guarantee | Contract is strict, parser enforces what it can, LLM infers the rest | Hand-fixed 7 needs-definition tasks; 4-of-4 re-dispatches downshifted on previously-unknown sub-rules |
| Sub-agent reasoning | Two sub-agents (T0004 first dispatch, T0005 first dispatch) confidently claimed “no file exists for T000X” when the file did exist | File-resolution misread; no programmatic guard | Verdict shape was valid (ERROR), so orchestrator dispatched on it without catching the wrong premise |
| State drift | Multiple stuck leases per session; stale worktrees from escaped sub-agents; commits-needing-push from sub-agents that didn’t get there | Cumulative effect of every other failure mode | Manual cleanup; archive subcommand exists but no release |
Net session outcome
Section titled “Net session outcome”- 0 task-work PRs opened despite ~10 sub-agent dispatches against the 14-task M0001 queue.
- 7 task specs flagged
planning/needs-definitionfor near-identical Today-section drift. - 2 verdict-contract escapes lost their entire post-readiness work.
- 3 sibling repairs landed on main that should arguably have been the system’s job, not the
operator’s:
- Migration of task/backlog/epic to the new entities location.
- Lease subcommand switch in the orchestrate spec.
- Lenient parser for verdict-buried-in-prose.
- 2 sibling planning artifacts opened as separate PRs: T-0015 (sort
--readyfilter, PR #180), this ADR (D0005, PR #181).
The friction is the case for this ADR’s repair-on-prepare posture. The post-mortem loop (Decision item 9) is the case for never letting that friction recur silently.
The promotion path (LLM-rule → deterministic-rule)
Section titled “The promotion path (LLM-rule → deterministic-rule)”A through-line in the evidence: rules that started as “the LLM infers them from the contract” end up causing repeated friction because two sub-agent runs may infer differently. The natural lifecycle is:
- Rule lives in
implementation-ready.mdas prose — LLM-judged, flexible, but inconsistent. - Recurring violations of the rule surface in the repair-log — the post-mortem (Decision item 9) flags it as a recurring class.
- Rule promotes to deterministic enforcement — added to
parse-touchpoints.ts(or a sibling), removed from the LLM-judgment surface. - Contract doc updates to reference the deterministic check, not duplicate the rule.
This is exactly the inverse of how rules typically accumulate: usually they get added everywhere “just to be sure.” The repair-log is what makes the promotion path observable.
- Surfaced 2026-05-30 from the operator-side friction in the M0001 dispatch wave (7-of-7 task-work runs downshifted on similar drift). The user-quoted shape of the design: “a setting on a task to allow a prepare step to not just analyze the task but instead attempt an LLM-orchestrated repair or upgrade of its state and then letting it continue on to be worked” — extended in the same session to “our entity migration needs to handle non-mechanical upgrades as well” and “we may need to add a task field that indicates whether we allow auto-repair and then we codify where we capture found errors and repairs performed so we can do a post mortem on what problems seem to keep coming up.”
- Implementation milestone: M-0001.1-task-execution-consistency. This session’s friction
seeded the “task execution consistency” milestone slotting between M0001 (entity shapes + roster)
and M0002 (full S0005 surface for legacy entities). The repair-on-prepare work, the
--readyfilter (T0015), and a verdict-contract structural fix belong together as one milestone’s worth of consistency-focused work. - Complementary to T-0015 (sort
--readydep-satisfaction filter). T0015 filters out candidates that aren’t actionable; this ADR repairs candidates that could be actionable if their drift is fixable. Together they reduce the rate of wasted parallel dispatches. - Complementary to T-NP7H-task-work-sub-agent-verdict-contract-escape-recurrence. The verdict-contract recurrence is partly downstream of this: when repair lands, fewer sub-agents bail at the readiness gate, fewer verdict-contract escapes occur. Solving them in the same milestone tightens the loop.