--- name: openspec-repair description: Turns a classified gap — from openspec-verify, openspec-drift, or an executor's deviation tag — into a concrete, routed fix plan. Decides which of the four repair routes a gap takes (fix the code, update the spec, open a new change, or block), and never resolves a gap by weakening what was agreed. allowed-tools: Read, Write, Bash, Grep, Glob license: MIT metadata: author: openspec-gsd-integration version: "0.1.0" --- ## Purpose A gap with no owner and no route is just a warning people learn to scroll past. This skill exists so every finding produced anywhere in the integration terminates in exactly one of four named actions, with the "make the audit pass by lowering the bar" option structurally unavailable. ## Invocation conditions - Composed by `openspec-audit` whenever a composed skill reports a finding requiring a fix. - After `openspec-verify` writes `status: gaps_found` — this is the natural next step before re-running verification. - Directly, `/openspec-repair [finding-id]`, to plan the fix for one specific gap. ## Required inputs - One or more findings, each already carrying a classification. This skill classifies nothing itself — classification is `openspec-execute`'s (execution-time deviations) and `openspec-drift`'s (audit-time drift classes). An unclassified input is rejected, not guessed at. - `.planning/openspec/TRACEABILITY.md` for the affected requirement's current resolution. ## Files read - `.planning/openspec/DRIFT.md`, `AUDIT-SUMMARY.md` - `.planning/phases/-/-OPENSPEC-VERIFICATION.md`, `SUMMARY.md` - `openspec/changes//{proposal,design,tasks}.md` and `specs/**/spec.md` ## Files this skill may write - `.planning/openspec/REPAIR-PLAN.md` (overwritten per run) - Never writes to `openspec/` — spec changes go through `/opsx:update`, which is OpenSpec's own authoring path, so the spec's own validation and authorship conventions apply. - Never writes code. GSD's executor does that, from the plan this skill produces. ## Algorithm — the four routes For each finding, route by its classification: | Incoming classification | Route | What gets produced | |---|---|---| | `SPEC_AMBIGUITY` | **Ask** — the ambiguity is a human decision | A precisely-worded question naming both readings and what each implies for implementation. Never a recommendation dressed as a question. | | `SPEC_CONFLICT`, `DESIGN_CONFLICT`, drift classes 7–8 | **Update the spec** | A `/opsx:update ` invocation with the exact passage to reconcile. The spec moves to match reality *only* when reality is the correct behavior — otherwise this becomes a Fix-code route instead, and that determination is stated explicitly with its reasoning. | | Drift classes 1, 2, 3, 6; requirement status `FAIL`/`NOT_TESTED` | **Fix the code** | A GSD-shaped task block (requirement IDs, expected files, required tests, verification method) that `/gsd-plan-phase` can absorb directly — same schema as a `PLAN.md` task, so no translation step. | | `OUT_OF_SCOPE_DISCOVERY`, drift classes 4, 5, 9 | **New change** | A stub for `/opsx:propose ` capturing the discovered work. Explicitly *not* folded into the current change — this is what keeps the shipped change's scope equal to its agreed scope. | | `IMPLEMENTATION_BLOCKER` | **Block** | `gsd-tools state add-blocker` invocation; GSD's own blocker mechanism owns this, not OpenSpec. | Then order the resulting work: blocking findings first, spec-side updates before the code fixes that depend on them (a code fix written against a passage about to change is wasted work), and new-change stubs last since they do not gate the current ship. ## Outputs `.planning/openspec/REPAIR-PLAN.md` — findings grouped by route, in execution order, each with its citation, its concrete next command, and the requirement IDs whose status should change once it lands. ## Failure states - A finding arrives without a classification → rejected with a pointer to `openspec-execute`'s classification table. This skill does not infer intent from a freeform description. - A finding's correct route depends on a product decision (is the current behavior right, or is the spec right?) → routed to **Ask**, never resolved by picking the cheaper option. ## Guardrails - **Never weakens acceptance criteria to close a gap.** Deleting a requirement, downgrading a `MUST` to a `SHOULD`, marking something `NOT_APPLICABLE` without a written reason, or removing a failing test are all forbidden repair actions and must never appear in a repair plan. - Never marks a finding resolved. Resolution is proven by re-running `openspec-verify` and seeing the status change with fresh evidence — a repair plan is a proposal, not a state transition. - Never folds `OUT_OF_SCOPE_DISCOVERY` work into the current change to avoid the friction of a new one. - Never routes a spec update as a way to make failing code retroactively correct without that being stated in plain language as the decision it is. ## Idempotency Re-running against an unchanged finding set produces the same plan. Findings already fixed (verified by their requirement's current status in `OPENSPEC-VERIFICATION.md`) drop out of the plan automatically rather than accumulating as stale entries. ## Example ``` $ /openspec-repair [openspec-repair] 3 findings from AUDIT-SUMMARY.md (mode=strict) 1. BLOCKING · Fix code · REQ-HEALTH-002 NOT_TESTED → add task: tests/health.test.ts covers SCN-HEALTH-003 (liveness under dependency failure) 2. Update spec · drift class 4 — src/health/metrics.ts has no requirement → /opsx:update add-health-check-endpoint (add observability requirement, or…) 3. New change · OUT_OF_SCOPE_DISCOVERY — connection-pool retry logic → /opsx:propose add-db-retry-policy (NOT folded into the current change) → .planning/openspec/REPAIR-PLAN.md ```