[FIX] Rectify: Fix #4399 — Envelope Truncation and Tool Registry Loss - #4401
Merged
Trecek merged 6 commits intoJul 28, 2026
Merged
Conversation
…registry loss - Test 1A: Verify finalize_recipe_delivery() returns ORDINARY_INLINE for exempt surfaces whose ordinary-rendered payload fits within the 195KB exemption ceiling. Boundary cases: payload exceeding ceiling → ENVELOPE; non-exempt surface (get_recipe) → ENVELOPE. - Test 1B: Strengthen test_rendered_open_kitchen_payload_under_budget to assert the rendered output contains recipe content (not just byte-size compliance) for recipes whose ordinary-rendered payload fits within the ceiling. - Test 1C: After close_kitchen() hides pre-revealed kitchen tools, verify that a subsequent open_kitchen() with _skip_notify=True (Claude Code backend) restores all kitchen-tagged tools to visible. These tests fail before the fix in Steps 2A/2B and pass after.
When an exempt surface's ordinary-rendered payload fits within the registered exemption ceiling, upgrade ENVELOPE back to ORDINARY_INLINE so the full recipe body survives. Placed after ALL ENVELOPE-producing branches (initial resolve, response-budget downgrade, receipt-store- missing downgrade, reservation-failure downgrade) so the override catches every path. Without this, exempt surfaces like open_kitchen on Claude Code (protected_recipe_delivery_capable=False) get ENVELOPE for any payload exceeding the 46.5K ordinary_limit, producing a degenerate formatter output with no recipe body. Co-Authored-By: Claude <noreply@anthropic.com>
When the backend can't process tool/list_changed notifications (Claude Code), the _skip_notify branch in open_kitchen only logged a debug message without re-enabling kitchen and plan-review tags. After close_kitchen() appends global mcp.disable() for these tags, the skipped re-enable left tools invisible. Replace the log-only branch with explicit global mcp.enable() calls for both tags, using FastMCP's last-match-wins semantics to override the prior disables. _redisable_subsets() runs immediately after to re-apply config-disabled subsets as session-scoped disables, so dual-tagged tools don't leak. Co-Authored-By: Claude <noreply@anthropic.com>
- _recipe_delivery.py: scope ENVELOPE→ORDINARY override to backends without recipe_delivery_budget (Claude Code) only; Codex keeps bounded envelope semantics. Inject success=True into candidate_payload once so ORDINARY_INLINE always carries the wire-shape success indicator. - test_subpackage_isolation.py: bump tools_kitchen.py line-limit exemption to 1700 for the +2-line global-enables refresh; extend reason with #4399 note. - test_schema_version_convention.py: shift legacy JSON write line 1453→1460 to match the post-fix tools_kitchen.py file shape. - test_tools_kitchen_visibility.py: patch _get_ctx inside close_kitchen call so _close_kitchen_handler operates on the mocked ctx (it reads from _get_ctx, not the ctx parameter). - test_tools_recipe_pull.py: clamp oversized payload to ceiling - 1000 to keep the test below the open_kitchen exemption ceiling irrespective of downstream ceiling changes. Co-Authored-By: Claude <noreply@anthropic.com>
…rojection - tests/arch/test_subpackage_isolation.py: bump _LINE_LIMIT_EXEMPTIONS for server/_recipe_delivery.py from 1100 to 1150. The #4399 ENVELOPE→ORDINARY override (scope check + byte budget + decision.replace; ~18 net lines) and success=True injection on candidate_payload (~8 net lines) brought the file to 1129 lines. Add a per-line rationale citing both #4399 code paths. - tests/infra/test_pretty_output_recipe.py: the test_rendered_open_kitchen_payload_under_budget content gate (#4399 Test 1B) compared the raw YAML against the rendered output, but _fmt_recipe_body applies compact_recipe_display (strips top-level name/description/summary/recipe_version and step descriptions, halves structural indentation) AND additionally strips the YAML ingredients: block when ingredients_table is present. Mirror both projections inline before substring-matching: 1. Inline a localized _strip_yaml_ingredients_block (production helper uses bare relative imports, not directly importable from the test harness which uses autoskillit.* imports). 2. Strip ingredients when ingredients_table is present, then run compact_recipe_display, then substring-check the rendered output. This catches both #4399's original failure mode (formatter receives envelope → no recipe body) and the order-of-operations correctness of the formatter chain. Co-Authored-By: Claude <noreply@anthropic.com>
…hook instead of re-defining it locally
Trecek
deleted the
claude-code-full-open-kitchen-returns-no-recipe-body-and-clo/4399
branch
July 28, 2026 02:13
This was referenced Jul 28, 2026
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Issue #4399 has two independent root causes:
Envelope truncation:
finalize_recipe_delivery()delegates toresolve_recipe_delivery_decision(), which returns ENVELOPE mode for recipes exceeding ~46.5K tokens on Claude Code backends (protected_recipe_delivery_capable=False). The 195KB response exemption registered foropen_kitchenis only consulted AFTER the delivery decision, at a different layer (enforce_response_budget), and only prevents a different downgrade (ORDINARY_INLINE→ENVELOPE for non-exempt oversized responses). The PostToolUse formatter_fmt_open_kitchenonly reads ORDINARY_INLINE-shape keys (content,summary,diagram) and has zero knowledge of ENVELOPE-shape keys (step_flow_skeleton,recipe_pull), producing a degenerate## open_kitchen ✓ v{version}with no recipe body.Tool registry loss:
close_kitchen()appends permanent globalmcp.disable(tags={"kitchen"})andmcp.disable(tags={"plan-review"})to FastMCP's append-onlyProvider._transformslist. The nextopen_kitchen()has_skip_notify=Truefor Claude Code backends (supports_tool_list_changed=False), so it skipsctx.enable_components()— the session-scoped enable that would override the global disable. The 41 kitchen-tagged tools permanently vanish.Both defects share a pattern — lifecycle assumptions that hold on first use but are violated by subsequent state changes. The fix restores alignment between exemption scope and delivery decision, and tightens the visibility lifecycle so close/reopen cycles preserve the kitchen-tagged tool registry.
Requirements
open_kitchen(name="remediation")on Claude Code returns the recipe body — step flow, ingredients table and orchestration rules — not## open_kitchen ✓ v.close_kitchen()followed byopen_kitchen()leaves all 41 kitchen-tagged tools callable;run_skillin particular resolves and executes.close_kitchen→open_kitchenlifecycle. The existing suite passes while the registry is empty, which is why this shipped.Closes #4399
Implementation Plan
Plan file:
/home/talon/projects/autoskillit-runs/fix-4362-20260727-154546-650120/.autoskillit/temp/rectify/rectify_fix_4362_envelope_truncation_and_registry_loss_2026-07-27_170000.md🤖 Generated with Claude Code via AutoSkillit