Commit cfb6ecc
[FIX] Rectify: Capability Admission Control at Recipe Load — Refuse DOA Pipelines (#4094)
## Summary
Codex-backend implementation pipelines repeatedly die at
`gate_backend_write` (step 12) after executing 11 irreversible
side-effect steps (clone, claim, branch publish, implement). 84 `[FIX]`
commits over ~3 weeks have patched individual links in the capability
injection chain without closing the architectural gap: **there is no
admission control that refuses a dead-on-arrival (DOA) pipeline at
recipe load time**. The `gate_backend_write` step is a mid-pipeline
runtime firewall — by the time it fires, the damage (wasted time,
orphaned branches, claimed issues) is done.
The immunity approach: extend `load_and_validate` to compute a
`dispatch_feasible` signal based on whether capability-gated steps that
route to terminal failure are reachable in the post-prune step graph.
All four content-serving surfaces (`open_kitchen` normal, `open_kitchen`
deferred-recall, `get_recipe`, `load_recipe`) gate on this signal. The
pipeline is refused before any tool is revealed.
## Requirements
**REQ-SCHEMA-001:** `RecipeStep` must support a purpose-critical
annotation parsed from recipe YAML, plumbed through `recipe/schema.py`,
`_PARSE_STEP_HANDLED_FIELDS`, and `_parse_step` in `recipe/io.py` so the
import-time handled-fields assertion passes.
**REQ-SCHEMA-002:** All bundled implementation-family recipes
(`implementation.yaml`, `remediation.yaml`,
`implementation-groups.yaml`) must declare their implement-family steps
as purpose-critical; every other bundled-recipe step invoking a
`git_metadata_write`-class skill (`merge-prs.yaml`
`implement`/`retry_worktree`, `research-implement.yaml`
`implement_phase`) must carry an explicit purpose-critical or
degraded-ok classification — no unclassified git-write step may remain.
User-authored recipes without annotations are fail-open (admitted
vacuously); this default must be documented.
**REQ-SCHEMA-003:** `LoadRecipeResult` must gain `dispatch_feasible:
bool` and `infeasible_steps: list[str]` fields, mirrored in
`OpenKitchenResult`; structural `valid` semantics must remain unchanged.
**REQ-ADMIT-001:** `load_and_validate` must classify a recipe as
infeasible when any purpose-critical step is pruned by
`skip_when_false`, keyed on `backend_name` rather than the presence of
capability ingredient overrides.
**REQ-ADMIT-002:** The infeasibility result must be machine-readable,
naming the backend, the missing capability, and the pruned
purpose-critical steps.
**REQ-ADMIT-003:** The infeasibility result must include the
`ingredients_table` so `ingredients_only=True` discovery flows continue
to function (the existing `_recipe_validation_error_response` builder
does not carry it — a new refusal response builder is required).
**REQ-ADMIT-004:** The refusal's `user_visible_message` must name the
backend, the missing capability, and the pruned purpose-critical steps,
and must include an actionable escape hatch (e.g., switch the backend to
claude-code), following the `_preflight.py:95-110` hook-class precedent.
**REQ-SURF-001:** Both `open_kitchen` paths (normal and deferred-recall)
must fail closed on an infeasible recipe — calling both `gate.disable()`
and `disable_components(tags={"kitchen"})`, returning a structured
refusal envelope — before any side-effect tool is callable.
**REQ-SURF-002:** `get_recipe` must gate on `dispatch_feasible` in
addition to its existing `valid` gate and refuse to serve an infeasible
recipe's content.
**REQ-SURF-003:** `load_recipe` must fail closed on infeasibility
instead of returning recipe content with only a `validation_failed`
flag.
**REQ-SURF-004:** The dispatch-feasibility preflight consumed by
`dispatch_food_truck` must refuse infeasible recipes before any issue
claim or branch publication.
**REQ-SURF-005:** The `gate_backend_write` recipe step must remain in
place as defense-in-depth.
**REQ-TEST-001:** An arch test must cross-reference
`SKILL_CAPABILITY_REGISTRY` to require that every bundled-recipe step
invoking a `git_metadata_write`-class skill carries an explicit
purpose-critical or degraded-ok classification.
**REQ-TEST-002:** An integration test must assert that every
recipe-content-serving surface refuses an infeasible recipe.
**REQ-TEST-003:** The tests currently pinning Codex+implementation
admission (`test_backend_ingredient_injection.py`,
`test_bundled_recipes_dispatch_ready.py`,
`test_recipe_backend_composition_matrix.py`) must assert the precise
refusal instead.
Closes #4090
## Implementation Plan
Plan file:
`/home/talon/projects/autoskillit-runs/remediation-20260612-092948-265956/.autoskillit/temp/rectify/rectify_capability_admission_control_2026-06-12_093500.md`
🤖 Generated with [Claude Code](https://claude.com/claude-code) via
AutoSkillit
<!-- autoskillit:pipeline-signature
steps=prepare_pr,run_arch_lenses,compose_pr,annotate_pr_diff,review_pr
-->
## Token Usage Summary
| Step | Model | count | uncached | output | cache_read | peak_ctx |
turns | cache_write | time |
|------|-------|-------|----------|--------|------------|----------|-------|-------------|------|
| rectify* | opus[1m] | 1 | 7.4k | 25.2k | 1.7M | 126.5k | 61 | 105.2k |
18m 38s |
| review_approach* | opus[1m] | 1 | 24 | 6.2k | 264.7k | 70.9k | 14 |
28.4k | 9m 44s |
| dry_walkthrough* | opus | 2 | 4.1k | 22.5k | 2.7M | 100.6k | 109 |
163.3k | 13m 37s |
| implement* | MiniMax-M3 | 2 | 10.7M | 24.6k | 0 | 0 | 182 | 0 | 16m 4s
|
| assess* | opus[1m] | 1 | 46 | 9.7k | 1.1M | 81.0k | 53 | 59.4k | 9m
10s |
| audit_impl* | opus[1m] | 2 | 71 | 35.7k | 788.0k | 76.8k | 43 | 109.0k
| 22m 19s |
| make_plan* | opus[1m] | 1 | 77 | 15.2k | 1.9M | 112.4k | 50 | 91.3k |
16m 50s |
| retry_worktree* | opus[1m] | 1 | 28 | 2.4k | 385.2k | 44.2k | 19 |
22.7k | 2m 0s |
| prepare_pr* | MiniMax-M3 | 1 | 349.4k | 5.0k | 0 | 0 | 19 | 0 | 1m 22s
|
| compose_pr* | MiniMax-M3 | 1 | 312.6k | 2.1k | 0 | 0 | 15 | 0 | 1m 1s
|
| **Total** | | | 11.4M | 148.5k | 8.9M | 126.5k | | 579.2k | 1h 50m |
\* *Step used a non-Anthropic provider; caching behavior may differ.*
## Token Efficiency
| Step | LoC Changed | cache_read/LoC | cache_write/LoC | output/LoC |
|------|-------------|----------------|-----------------|------------|
| rectify | 0 | — | — | — |
| review_approach | 0 | — | — | — |
| dry_walkthrough | 0 | — | — | — |
| implement | 492 | 0.0 | 0.0 | 50.0 |
| assess | 22 | 50917.0 | 2700.9 | 441.6 |
| audit_impl | 0 | — | — | — |
| make_plan | 0 | — | — | — |
| retry_worktree | 0 | — | — | — |
| prepare_pr | 0 | — | — | — |
| compose_pr | 0 | — | — | — |
| **Total** | **514** | 17378.8 | 1126.9 | 289.0 |
## Model Usage Breakdown
| Model | steps | uncached | output | cache_read | cache_write | time |
|-------|-------|----------|--------|------------|-------------|------|
| opus[1m] | 6 | 7.7k | 94.4k | 6.2M | 416.0k | 1h 18m |
| opus | 1 | 4.1k | 22.5k | 2.7M | 163.3k | 13m 37s |
| MiniMax-M3 | 3 | 11.3M | 31.7k | 0 | 0 | 18m 29s |
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>1 parent cadfc1e commit cfb6ecc
22 files changed
Lines changed: 531 additions & 27 deletions
File tree
- src/autoskillit
- config
- core
- types
- hooks/formatters
- recipe
- server/tools
- tests
- arch
- infra
- recipe
- server
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
13 | 12 | | |
14 | 13 | | |
15 | 14 | | |
| |||
57 | 56 | | |
58 | 57 | | |
59 | 58 | | |
| 59 | + | |
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
125 | 125 | | |
126 | 126 | | |
127 | 127 | | |
128 | | - | |
129 | | - | |
130 | | - | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | 128 | | |
135 | 129 | | |
136 | 130 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
131 | 131 | | |
132 | 132 | | |
133 | 133 | | |
| 134 | + | |
134 | 135 | | |
| 136 | + | |
135 | 137 | | |
136 | 138 | | |
137 | 139 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| 36 | + | |
| 37 | + | |
36 | 38 | | |
37 | 39 | | |
38 | 40 | | |
| |||
83 | 85 | | |
84 | 86 | | |
85 | 87 | | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
86 | 112 | | |
87 | 113 | | |
88 | 114 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
| 47 | + | |
| 48 | + | |
47 | 49 | | |
48 | 50 | | |
49 | 51 | | |
| |||
185 | 187 | | |
186 | 188 | | |
187 | 189 | | |
| 190 | + | |
| 191 | + | |
188 | 192 | | |
189 | 193 | | |
190 | 194 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
| 17 | + | |
16 | 18 | | |
17 | 19 | | |
18 | 20 | | |
| |||
45 | 47 | | |
46 | 48 | | |
47 | 49 | | |
| 50 | + | |
48 | 51 | | |
49 | 52 | | |
50 | 53 | | |
| |||
318 | 321 | | |
319 | 322 | | |
320 | 323 | | |
| 324 | + | |
| 325 | + | |
321 | 326 | | |
322 | 327 | | |
323 | 328 | | |
| |||
393 | 398 | | |
394 | 399 | | |
395 | 400 | | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
396 | 413 | | |
397 | 414 | | |
398 | 415 | | |
| |||
582 | 599 | | |
583 | 600 | | |
584 | 601 | | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
585 | 605 | | |
586 | 606 | | |
587 | 607 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
| 66 | + | |
66 | 67 | | |
67 | 68 | | |
68 | 69 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
106 | 106 | | |
107 | 107 | | |
108 | 108 | | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
109 | 163 | | |
110 | 164 | | |
111 | 165 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
127 | 127 | | |
128 | 128 | | |
129 | 129 | | |
| 130 | + | |
| 131 | + | |
130 | 132 | | |
131 | 133 | | |
132 | 134 | | |
| |||
135 | 137 | | |
136 | 138 | | |
137 | 139 | | |
138 | | - | |
| 140 | + | |
139 | 141 | | |
140 | 142 | | |
141 | 143 | | |
| |||
153 | 155 | | |
154 | 156 | | |
155 | 157 | | |
| 158 | + | |
| 159 | + | |
156 | 160 | | |
157 | 161 | | |
158 | 162 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
126 | 126 | | |
127 | 127 | | |
128 | 128 | | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
129 | 159 | | |
130 | 160 | | |
131 | 161 | | |
| |||
433 | 463 | | |
434 | 464 | | |
435 | 465 | | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
436 | 474 | | |
437 | 475 | | |
438 | 476 | | |
| |||
673 | 711 | | |
674 | 712 | | |
675 | 713 | | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
676 | 718 | | |
677 | 719 | | |
678 | 720 | | |
| |||
782 | 824 | | |
783 | 825 | | |
784 | 826 | | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
785 | 832 | | |
786 | 833 | | |
787 | 834 | | |
| |||
0 commit comments