Skip to content

Rectify: open_kitchen Registry Loss — Client-Cache Coherence - #4407

Merged
Trecek merged 5 commits into
developfrom
open-kitchen-registry-loss-residual/4399
Jul 28, 2026
Merged

Rectify: open_kitchen Registry Loss — Client-Cache Coherence#4407
Trecek merged 5 commits into
developfrom
open-kitchen-registry-loss-residual/4399

Conversation

@Trecek

@Trecek Trecek commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

After close_kitchen()open_kitchen(), all 41 kitchen-tagged MCP tools vanish from the client's tool list. The server-side state is correct — the global mcp.enable() calls added in PR #4401 work and the server reports tools as enabled — but the client is never notified to re-query tools/list, so it keeps serving a stale cache from the close transition.

The architectural weakness is a notification asymmetry: close_kitchen unconditionally sends ToolListChangedNotification (via ctx.reset_visibility()), but open_kitchen deliberately suppresses notification when _skip_notify=True (all production backends). The _skip_notify flag correctly prevents notification at boot (no client connected yet) but is incorrectly reused mid-session (client IS connected and synced to the disabled state).

The fix is to send an explicit ToolListChangedNotification after the global re-enables in open_kitchen, and to structurally prevent this class of bug from recurring via an architectural guard and a session-level round-trip regression test.

Implementation Plan

Plan file: /home/talon/projects/autoskillit-runs/remediation-20260728-095428-498581/.autoskillit/temp/rectify/rectify_open_kitchen_registry_loss_residual_2026-07-28_101500.md

Closes #4399

🤖 Generated with Claude Code via AutoSkillit

Trecek and others added 5 commits July 28, 2026 11:24
After `close_kitchen()` → `open_kitchen()`, the server-side state was
correct (global `mcp.enable()` calls work), but the connected MCP client
kept serving a stale post-close tool cache because no
`ToolListChangedNotification` was sent after the global re-enables.

The `_skip_notify` flag correctly prevents notification at boot (no
client connected yet) but is incorrectly reused mid-session (client IS
connected and synced to the disabled state). `close_kitchen`
unconditionally sends the notification via `ctx.reset_visibility()`,
creating an asymmetry: tools vanish but never return.

Fix: send an explicit `ctx.send_notification(ToolListChangedNotification())`
after the global re-enables in `open_kitchen`'s `_use_global_enable`
branch (formerly `_skip_notify`). The variable rename clarifies that
the branch selects an enable mechanism (global vs session-scoped), not
notification suppression. A scope-placement invariant comment is added
at the `if not _skip_handler:` guard to prevent future maintainers from
breaking the gate_infrastructure_ready transition.

Arch guard: `_SKIP_GUARD_NAMES` extended with `_use_global_enable` so
the rename doesn't break `test_enable_components_calls_have_notification_fallback`.
A companion check (`test_guard_branches_send_notification`) verifies that
every guarded `if`-branch body contains a `send_notification` call,
scoped to the branch body (not the whole function) so an unrelated
notification elsewhere doesn't vacuously satisfy the check.

Line-limit exemption for tools_kitchen.py bumped 1800 → 1830 with
rationale citing #4399.

Tests:
- Test A: `test_close_open_roundtrip_restores_tools_via_session` drives
  the real `close_kitchen`/`open_kitchen` tools through an in-process
  `fastmcp.Client` session with `supports_tool_list_changed=False`,
  asserting the post-open `client.list_tools()` returns kitchen tools.
- Test B: `test_open_kitchen_exempt_surface_renders_real_content`
  passes a well-formed payload through `_format_response` and asserts
  the `--- STEP FLOW ---` marker (proving substantive content rendering,
  not a degenerate stub).

Co-Authored-By: Claude <noreply@anthropic.com>
…te allowlist (#4399)

Two follow-ups to the open_kitchen client-cache coherence fix:

1. tests/arch/test_cascade_map_guard.py's file-level drift guard flagged
   server/test_tools_kitchen_envelope.py as missing from
   LAYER_CASCADE_CONSERVATIVE['hooks']: the new
   test_open_kitchen_exempt_surface_renders_real_content test imports
   autoskillit.hooks.formatters.pretty_output_hook directly to drive
   the _fmt_open_kitchen contract. Add it as a file-level entry with a
   comment referencing #4399 criterion 4.

2. tests/infra/test_schema_version_convention.py's allowlist
   ratchet flagged stale line numbers: the same fix shifted
   json.dumps+atomic_write sites in tools_kitchen.py from
   290/309/343/1575 to 291/310/344/1602 (one-line import added,
   followed by the net-new try/except notification block).
)

Plan Change 3 called for updating the T-VISIBILITY-1b test description to
reference the renamed _use_global_enable variable. The rename was applied to
production code and the other visibility tests but this docstring was missed.

Co-Authored-By: Claude <noreply@anthropic.com>

@Trecek Trecek left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AutoSkillit review passed. No blocking issues found.

(Verdict: approved. Self-authored PR — GitHub does not permit a self-APPROVE review, so this is posted as a COMMENT review instead.)

@Trecek
Trecek added this pull request to the merge queue Jul 28, 2026
Merged via the queue into develop with commit 9e56f23 Jul 28, 2026
3 checks passed
@Trecek
Trecek deleted the open-kitchen-registry-loss-residual/4399 branch July 28, 2026 19:54
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