Skip to content

[FIX] Pre-Prune Validity Computation Breaks Codex open_kitchen#4068

Merged
Trecek merged 13 commits into
developfrom
pre-prune-validity-computation-breaks-codex-open-kitchen-and/4059
Jun 11, 2026
Merged

[FIX] Pre-Prune Validity Computation Breaks Codex open_kitchen#4068
Trecek merged 13 commits into
developfrom
pre-prune-validity-computation-breaks-codex-open-kitchen-and/4059

Conversation

@Trecek

@Trecek Trecek commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

The recipe loading pipeline in load_and_validate() computes validity from semantic rule findings that were generated against the pre-prune recipe. When the Codex backend is used, the backend-incompatible-skill rule fires ERROR-severity findings against steps that have skip_when_false guards — steps that would be pruned immediately after. The stale pre-prune findings poison compute_recipe_validity(), marking the recipe as valid=False. The error response function then reads the empty errors[] list (structural errors only) and falls back to "unknown structural error", hiding the true cause entirely.

Part A fixes the core pipeline ordering bug in load_and_validate() and surfaces semantic findings in the validation error response. Part B will cover adding pruning support to validate_from_path and its full caller chain — implement as a separate task.

Requirements

open_kitchen(name="implementation") fails on the first orchestrator message of every Codex-backend session in v0.10.752 with:

Recipe 'implementation' failed structural validation: unknown structural error (stage=recipe_validation, errors=[], suggestions contain only the usual warnings)

The fail-closed guard added by PR #3995 (61802dce4) gates on result["valid"], but load_and_validate computes valid from semantic-rule findings evaluated on the unpruned recipe. With the Codex capability override (backend_supports_git_write="false"), the backend-incompatible-skill rule emits 13 ERROR-severity findings — 9 on steps that are pruned immediately afterward (skip_when_false: inputs.backend_supports_git_write) and 4 on inputs.open_pr-route-guarded steps that the conformance test explicitly exempts via _ROUTE_GUARDED_COMPAT_EXCEPTIONS but compute_recipe_validity does not. So valid=False even though the served (pruned) content is structurally sound.

Root cause chain (all empirically confirmed on installed v0.10.752)

  1. CodexBackend declares git_metadata_writable=False (execution/backends/codex.py:521); _backend_capability_overrides yields {"backend_supports_git_write": "false"}, merged last/unoverridable (tools_kitchen.py:560-561 via _promote_capability_keys).
  2. Sequencing defect (the root): in recipe/_api.py (load_and_validate): run_semantic_rules runs at line 397 on the unpruned recipe → _prune_skipped_steps at line 408 → compute_recipe_validity at line 457 consumes the pre-prune findings.
  3. backend-incompatible-skill (rules/rules_backend_compat.py:27-68) has no awareness of skip_when_false guards or route-guard exemptions → 13 ERROR findings for codex.
  4. compute_recipe_validity (registry.py:245-254) folds any ERROR-severity semantic/contract finding into validvalid=False, content non-empty, errors=[].
  5. Rectify: Surface LoadRecipeResult Errors and Fail-Closed on Invalid Content #3995's guard trips on not result.get("valid", False); the envelope renders only the structural errors list → misleading "unknown structural error".

Scope of fix

  1. Compute validity from the post-prune recipe. Move semantic-rule evaluation (or at minimum the validity-relevant findings derivation) after _prune_skipped_steps. The ValidationContext must be rebuilt with the pruned recipe — not reused from the pre-prune build.
  2. Single source of truth for route-guard exemptions. Promote _ROUTE_GUARDED_COMPAT_EXCEPTIONS (currently test-only) into a production constant consumed by both the validity computation path and the conformance test.
  3. Fleet parity. fleet/_api.py dispatch validation must pass the backend capability overrides so fleet's pruning matches what would actually be served.
  4. Regression tests through the real producer. Bundled-recipe × backend validity assertions through the real load_and_validate with real capability overrides.

Related work

Closes #4059

Implementation Plan

Plan file: /home/talon/projects/autoskillit-runs/remediation-20260611-063523-547628/.autoskillit/temp/rectify/rectify_pre_prune_validity_2026-06-11_063523_part_a.md

🤖 Generated with Claude Code via AutoSkillit

Token Usage Summary

Step Model count uncached output cache_read peak_ctx turns cache_write time
run_bem* sonnet 1 45.1k 48.1k 677.2k 130.5k 35 112.5k 17m 18s
dispatch:bf3ba4d0-338f-46e2-b744-1cc5af0cb74f* sonnet 1 66 6.1k 324.1k 48.4k 17 29.4k 18m 48s
plan* opus[1m] 4 3.7k 74.9k 5.8M 108.8k 180 327.3k 42m 59s
verify* sonnet 4 1.9k 41.2k 2.3M 84.4k 131 185.0k 18m 49s
implement* MiniMax-M3 4 8.1M 40.0k 532.7k 64.8k 269 0 29m 34s
audit_impl* sonnet 3 1.1k 21.1k 620.0k 45.8k 45 89.0k 10m 59s
prepare_pr* MiniMax-M3 3 460.3k 6.1k 85.0k 44.1k 38 0 3m 8s
compose_pr* MiniMax-M3 3 564.7k 4.0k 0 0 36 0 2m 22s
review_pr* sonnet 5 656 88.3k 3.8M 82.8k 189 267.0k 23m 6s
resolve_review* sonnet 2 394 25.5k 2.7M 74.7k 109 100.9k 12m 36s
dispatch:a43619bc-64d1-412e-9f66-144a95109fd1* sonnet 1 330 10.2k 3.0M 83.5k 86 137.5k 1h 5m
dispatch:76e5c3a5-b134-41b9-a511-2d686a4069c8* sonnet 1 362 14.6k 3.4M 88.0k 93 63.6k 38m 32s
dispatch:d889090d-ee20-4510-810a-e993a5d388d7* sonnet 1 322 12.7k 3.0M 85.2k 116 60.8k 55m 25s
Total 9.2M 392.8k 26.3M 130.5k 1.4M 5h 39m

* Step used a non-Anthropic provider; caching behavior may differ.

Token Efficiency

Step LoC Changed cache_read/LoC cache_write/LoC output/LoC
run_bem 0
dispatch:bf3ba4d0-338f-46e2-b744-1cc5af0cb74f 0
plan 0
verify 0
implement 423 1259.3 0.0 94.6
audit_impl 0
prepare_pr 0
compose_pr 0
review_pr 0
resolve_review 6 453949.7 16820.8 4254.0
dispatch:a43619bc-64d1-412e-9f66-144a95109fd1 0
dispatch:76e5c3a5-b134-41b9-a511-2d686a4069c8 554 6178.5 114.8 26.3
dispatch:d889090d-ee20-4510-810a-e993a5d388d7 39 76890.5 1559.9 326.8
Total 1022 25734.1 1343.4 384.3

Model Usage Breakdown

Model steps uncached output cache_read cache_write time
sonnet 9 50.3k 267.8k 19.8M 1.0M 4h 21m
opus[1m] 1 3.7k 74.9k 5.8M 327.3k 42m 59s
MiniMax-M3 3 9.1M 50.1k 617.7k 0 35m 5s

@Trecek Trecek force-pushed the pre-prune-validity-computation-breaks-codex-open-kitchen-and/4059 branch from 61f65d0 to b9324e7 Compare June 11, 2026 16:13
Trecek and others added 11 commits June 11, 2026 09:19
Pre-prune semantic findings (notably backend-incompatible-skill for
guarded steps) poisoned compute_recipe_validity, causing load_and_validate
to return valid=False for codex-pruned recipes. open_kitchen then fell
through to the "unknown structural error" envelope because
_recipe_validation_error_response only reads result.errors, not
error-severity suggestions.

Move the prune block before the semantic rules block in load_and_validate
so pruned steps are never seen by semantic rules — self-enforcing against
future rules that produce step-specific findings. Also surface
error-severity suggestions in the validation error response with .get()
fallbacks so the actual rule and message are visible instead of the
generic "unknown structural error" fallback.

Add an AST structural test (test_semantic_rules_run_after_pruning) to
prevent future regression of the ordering invariant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
_prune_skipped_steps clears skip_when_false on truthy-resolved steps but
left optional=True. When semantic rules run after pruning, this causes
false optional-without-skip-when findings. Also fix: pipeline ordering
test path (doubled autoskillit), ruff violations, schema version
allowlist line shifts, parametrize ids lambda on empty dict, and
open_kitchen test wrapper signature.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… test overrides

Pruning changes the step graph reachability, causing dead-output findings
for captures that were live pre-prune. Compare pre-prune vs post-prune
dead-output sets and suppress findings introduced by pruning. Also add
open_pr=false override to codex tests since merge-conflict steps are
guarded by open_pr, not backend_supports_git_write.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace dead-output-only filter with general approach: run semantic rules
on the pre-prune recipe as baseline, then intersect with post-prune
findings. Only findings present in BOTH survive — pruning-induced false
positives (dead-output, capture-inversion-detection) are suppressed.
Also fix open_kitchen test: set project_dir, pass open_pr=false via
resolve_ingredient_defaults.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The pre-prune baseline adds a first run_semantic_rules call before
_prune_skipped_steps. The AST ordering test must find the last
(post-prune) call. Also fix backend name: canonical is 'claude-code'
(hyphen), not 'claude_code' (underscore).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ng support

After Part A fixed load_and_validate(), validate_from_path() still ran semantic
rules on the raw unpruned recipe, producing false-positive backend-incompatible
errors for Codex sessions. This change threads ingredient_overrides through the
full caller chain — Protocol, DefaultRecipeRepository, InMemoryRecipeRepository,
and the validate_recipe MCP tool — and calls _prune_skipped_steps() before
semantic rule execution when overrides are provided.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…date arch catalog

validate_from_path now snapshots pre-prune semantic findings before
pruning, then filters post-prune findings to exclude pruning-induced
false positives (dead-output, capture-inversion) — mirroring the
load_and_validate pipeline.

Also adds test_pipeline_ordering.py to the resolve-review Architectural
Constraint Catalog.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Trecek Trecek force-pushed the pre-prune-validity-computation-breaks-codex-open-kitchen-and/4059 branch from b9324e7 to f337bca Compare June 11, 2026 16:20
Trecek and others added 2 commits June 11, 2026 09:32
…yright allowlist

_recipe_validation_error_response now prefers structural errors when present,
falling back to semantic findings only when errors list is empty. Uses
'unknown-rule' as the .get() default for missing rule keys. Updates pyright
suppression allowlist for recipe/_api.py line shift (275→276).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant