Skip to content

fix(mcp): render parked refusals and graph-tool errors as typed fields - #2340

Merged
ScriptedAlchemy merged 2 commits into
masterfrom
fleet/small-bundle-4-b
Sep 27, 2026
Merged

ScriptedAlchemy merged 2 commits into
masterfrom
fleet/small-bundle-4-b

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Fixes #2280

What was wrong

  • Parked refusals (fix(mcp): code-index corruption refusals format park facts into a route detail #2280). Since 21552e3 the admin-sync (tracedecay sync / tracedecay init) and background freshness-probe refusals carried the park as typed Parked detail inside the route error, but every consumer still flattened it. The CLI printed Display: one sentence, The code index for this worktree is parked; remedy: …; cause: …, and the 512-byte bound on that sentence cut the cause off. The daemon log did the same (error=project route error (…): …). The MCP JSON-RPC problem data had the detail but no kind. Branch publication still raised the same park as a format!("{reason}; {remediation}") string in two places.
  • Graph-tool handler errors. The owner mapped a handler error with no kind of its own (database, io, json, …) to unavailable/authority, which claims a missing authority. It is an internal failure.

Change

  • CodeIndexConvergenceParkedV1::publication_authority_corrupt_error (contracts) is now the one constructor for this refusal. Admin sync, the freshness probe, and both branch-publication sites use it, and the server-local copy is deleted.
  • MCP: project_route_problem takes the problem kind for a typed detail from ApplicationProblem::from_detail, so a parked refusal is {kind: unavailable, code, reason_code, retryable: false, detail: {kind: parked, cause, remedy, retries_on_wake}}.
  • CLI: the process boundary prints a route refusal with typed detail as its reason code plus one label: value line per detail field (ApplicationProblemDetailV1::labelled_fields). Nothing is truncated.
  • Daemon log: the startup catch-up and read-refresh refusals log reason_code, cause, remedy, retries_on_wake as tracing fields.
  • Graph-tool owner: every handler error now has a kind. Argument errors are invalid_request, route refusals are unavailable under their reason code, typed details/lock deadlines keep their detail, and any other handler failure is execution_failed (graph_tool.failed, retry: never, legal_actions: [contact_administrator]), the contract's internal-failure kind. The test(mcp): pin owner refusals as typed isError problems #2319 isError problem-record shape is unchanged. The four refactor(mcp): answer info and runtime reads through their owners #2335 pins for a broken registry table now assert execution_failed.

Fail before / pass after

With the behavioral pieces reverted (tests kept), these fail with the literals below and pass on this branch:

  • mcp::tools::handlers::dispatch_tests::admin_sync_reports_terminal_publication_corruption_without_queueing: wire data lacked "kind": "unavailable", "code": ….
  • tool_errors::tests::project_route_parked_detail_stays_structured_on_the_wire: same.
  • commands::index::init_bootstrap_tests::brokered_init_prints_a_parked_refusal_as_fields: fixture daemon answers with the real tool_error_response. Left was project route error (code_index_publication_authority_corrupt): The code index for this worktree is parked; remedy: …; cause: … xxxx (cut); right is project route error (…)\nParked cause: <full 600+ byte cause>\nParked remedy: run \tracedecay daemon restart`\nRetries on wake: false`.
  • mcp::server::background_refresh_writer_tests::parked_freshness_probe_logs_cause_and_remedy_as_fields: left error=project route error (…): … parked; remedy: …; cause: … xxx (cut); right reason_code="code_index_publication_authority_corrupt" cause="<full>" remedy="run \tracedecay daemon restart`" retries_on_wake=false`.
  • code_index_scheduler::tests::branch_publication_tests::mid_wait_branch_publication_surfaces_terminal_publication_park: typed detail was None.
  • mcp::tools::handlers::application_surface::tests::every_graph_tool_handler_error_renders_as_a_kinded_problem: io error kind was unavailable, expected execution_failed.

Runtime proof (debug CLI, isolated HOME, one daemon under systemd-run --scope -p MemoryMax=6G)

Corpus: a scratch git repo. Its code-index publication was corrupted (garbage generation manifests) and the scope store made read-only, so the automatic reset fails and the worktree parks terminally.

Master binary, tracedecay sync:

Error: project route error (code_index_publication_authority_corrupt): The code index for this worktree is parked; remedy: the derived code-index publication is corrupt and could not be deleted; fix the named filesystem fault on the project's code-index-v1 scope store, then run `tracedecay daemon restart` to rebuild it from source; cause: code-index production owner failed: the publication authority is corrupt and requires an index reset: parent generation manifest identity is corrupt; reset failed: code-index production owner failed: the publication authority is unavailable:

(the cause is cut before code-generation retention storage failure: Permission denied (os error 13))

Branch binary, same profile, tracedecay sync (exit 1):

Error: project route error (code_index_publication_authority_corrupt)
Parked cause: code-index production owner failed: the publication authority is corrupt and requires an index reset: active code-generation byte size does not match its durable entry; reset failed: code-index production owner failed: the publication authority is unavailable: code-generation retention storage failure: Permission denied (os error 13)
Parked remedy: the derived code-index publication is corrupt and could not be deleted; fix the named filesystem fault on the project's code-index-v1 scope store, then run `tracedecay daemon restart` to rebuild it from source
Retries on wake: false

Daemon JSON-RPC error data: {"code": "code_index_publication_authority_corrupt", "detail": {"cause": "…Permission denied (os error 13)", "kind": "parked", "remedy": "…", "retries_on_wake": false}, "kind": "unavailable", "reason_code": "code_index_publication_authority_corrupt", "retryable": false, "tool": "tracedecay_admin_sync"}
Daemon log after a read triggered the freshness probe:

WARN tracedecay::mcp::server::lifecycle: background read reconciliation was not admitted reason_code="code_index_publication_authority_corrupt" cause="…Permission denied (os error 13)" remedy="the derived code-index publication is corrupt …" retries_on_wake=false

Graph tools already rendered the whole record on master (tracedecay tool tracedecay_rank --args '{"direction":"bogus"}' --json: isError: true, problem.kind: invalid_request); that is unchanged.

Suites

  • Focused (filters parked publication admin_sync graph_tool brokered_init project_route freshness_probe, after merging current master): tracedecay lib 38 passed; tracedecay-cli bin 5 passed; tracedecay-code-index-runtime lib 87 passed; tracedecay-mcp lib 9 passed.
  • Full mcp_suite (rebased tip, CLI built first, run outside any memory scope): test result: ok. 580 passed; 0 failed. Master itself now has 580 tests: since this lane started, master removed 6 and added 1, and this PR adds or removes none. Two earlier runs under heavy shared load each hit unrelated readiness timeouts (code index did not publish … after 20000 ms; composition gate … waiting=88, and one diff_context truncation test that passes alone). The clean run above is the evidence.
  • cargo clippy -p tracedecay-contracts -p tracedecay-code-index-runtime -p tracedecay-mcp -p tracedecay -p tracedecay-cli --all-targets -- -D warnings: clean with and without tracedecay/test-transport,tracedecay/test-helpers,tracedecay-cli/test-transport.
  • cargo fmt --all -- --check: clean. dashboard contracts:check: contracts up to date.
  • cargo check --workspace --all-targets --target x86_64-pc-windows-gnu --features tracedecay/test-transport,tracedecay/test-helpers,tracedecay-cli/test-transport: exit 0. No cfg(windows) code touched.

Left as is

tracedecay status already reports the park as code_index_freshness.parked.{reason, remediation} fields in --json. Its human warning line still joins them in one sentence, and this PR does not change it.

@changeset-bot

changeset-bot Bot commented Sep 27, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 7a392cd

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@ScriptedAlchemy
ScriptedAlchemy merged commit d0d4d3a into master Sep 27, 2026
1 check passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-27T15:25:23.894588Z 7a392cd PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

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.

fix(mcp): code-index corruption refusals format park facts into a route detail

1 participant