Skip to content

refactor(mcp): serve admin sync through the project owner - #2343

Merged
ScriptedAlchemy merged 3 commits into
masterfrom
fleet/mcp-compat-deletion
Sep 27, 2026
Merged

ScriptedAlchemy merged 3 commits into
masterfrom
fleet/mcp-compat-deletion

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

tracedecay_admin_sync is now an operation the project's graph-tool owner serves, with a typed request and result. tracedecay init and tracedecay sync read that typed result through the daemon invocation path; they no longer send an MCP tools/call and pull status out of its text. This is the first of the four internal admin tools still on the compat path (admin_sync, admin_project, hook_runtime, admin_cli). The compat layer itself is deleted in a later PR.

Internal owner operations (tracedecay-tool-catalog)

  • ApplicationSurfaceOperation::AdminSync is a graph-tool operation, and it is listed in the new INTERNAL_OPERATIONS. Internal operations are served by name for first-party CLI commands and host hooks. They never appear in tools/list, the format-capable list (and so the generated Hermes plugin), or the HTTP mount: the MCP definition projection filters them out.
  • EffectClass::SchedulesWork is a new owner-side-effect class for "queue work on a daemon scheduler and answer with its admission". The operation's entry is { SchedulesWork, LONG_RUNNING_CEILING_MILLIS, RunEach }. The ten-minute ceiling is the one it already dispatched under (it was in LONG_RUNNING_DISPATCH_TOOLS); the budget is unchanged. Its catalog capability is not cancellable, and it has no cancelled terminal.
  • The now-unused generated MCP_TOOL_NAMES constant is deleted.

Typed contract (owner_effect_surface.rs)

AdminSyncSurfaceRequestV1 {} (deny_unknown_fields) and AdminSyncResultV1 { reconcile_scope, status: queued | not_applicable, project_root } serialize to the JSON the tool already emitted. Before this change, any argument was silently ignored. Now it is refused.

Deleted

  • dispatch_info_tools
  • the whole McpToolDispatchGroup::Info group and its binding row. Its other names (port_status, port_order, todos) were already owner operations, so the rows were dead.
  • the INTERNAL_DAEMON_TOOL_NAMES and long-running-ceiling entries for admin sync
  • the stringly admin_sync_status text parser in the CLI
  • the fake-socket brokered_init_* tests, which spoke the old tools/call wire. Replacements:
    • The real reconcile request is covered by the core_cli_suite init journeys against a real daemon (tool_daemon_test, cli_non_interactive_test, which assert daemon code-index reconciliation requested).
    • The fix(mcp): render parked refusals and graph-tool errors as typed fields #2340 parked-refusal rendering is now a_parked_admin_sync_refusal_prints_as_fields. It converts the owner's refusal through the new ApplicationRefusal::into_error, which keeps the typed detail. The code across the owner boundary is the canonical application.code-index.parked, the same code every other owner read uses for a park (see code_index_park_test), rather than the pre-owner reason code.

Fail-before / pass-after (production MCP tools/call, harness.call_tool)

New test: mcp_handler_test::info_health_request_test::admin_sync_answers_the_scheduler_admission_and_refuses_arguments. It asserts the literal result {"reconcile_scope":"authoritative_project","status":"queued","project_root":<root>}, paired with the literal refusal invalid arguments for tracedecay_admin_sync: unknown field project_root, there are no fields.

On origin/master source with only this test file added:

panicked at .../info_health_request_test.rs:89:5:
tracedecay_admin_sync must refuse: {"_meta":{"duration_us":57},"content":[{"text":"{\"project_root\":\"/tmp/.tmpV0ES5S/project\",\"reconcile_scope\":\"authoritative_project\",\"status\":\"queued\"}","type":"text"}]}
test result: FAILED. 0 passed; 1 failed

With this change: ok.

Runtime journey

Setup: debug tracedecay built from this branch, isolated HOME/profile, and one daemon under systemd-run --user --scope -p MemoryMax=6G -p MemorySwapMax=1G, stopped afterwards.

$ tracedecay init
initialized .../corpus/.; daemon code-index reconciliation requested
(exit 0)
$ tracedecay sync --verbose
{
  "reconcile_scope": "authoritative_project",
  "status": "queued",
  "project_root": ".../corpus"
}
code-index reconciliation queued via daemon for .../corpus

tracedecay serve --path corpus:
tools/list: 230 tools
  tracedecay_admin_sync advertised: False
tracedecay_admin_sync {"format": "json"}: isError=None 35ms
     {"project_root":".../corpus","reconcile_scope":"authoritative_project","status":"queued"}
tracedecay_admin_sync {"project_root": "/elsewhere"}: isError=True 3ms
     Problem: application.surface.invalid_request, Message: invalid arguments for tracedecay_admin_sync: unknown field `project_root` ...
     structuredContent.problem="application.surface.invalid_request"

Checks (local, on f3f7e04; merged with the 1.0.0-beta.58 version bump)

  • Lib tests: tool-catalog 6, contracts 420, mcp-catalog 29, tracedecay-mcp 387, daemon-protocol 65, daemon-service 322 (the adoption_observation census includes the new capability.application.primitive.admin-sync), api 53.
  • mcp_suite filtered to info_health_request_test branch_search_test multi_mcp_coordination_test schema_test protocol_test admin_test: 53 passed.
  • Root lib mcp:: daemon::: 736 passed, 1 failed. The failure is runtime_identity::concurrent_same_identity_worktrees_keep_exact_server_and_scheduler_bindings. It expects a JSON-RPC error for a tracedecay_files refusal but gets the isError result that fix(mcp): render parked refusals and graph-tool errors as typed fields #2340 introduced. That test does not reach admin sync.
  • daemon/transport/runtime acceptance (git_watch, indexing_lifecycle, sealed_generation, graph_rebuild_status, grafeo_restart, code_index_park): 10 passed, 2 failed. Every admin-sync caller passes. The two failures do not call admin sync:
  • CLI: bins 340. core_cli_suite: 145 passed, 1 failed. The failure is tool_diagnostics_reads_the_typescript_producer_publication, where the host TypeScript diagnostics producer is unavailable.
  • cargo clippy -D warnings over tracedecay, mcp, contracts, daemon-protocol, api, mcp-catalog, tool-catalog, cli, and daemon-service, --all-targets, with and without tracedecay/test-transport,test-helpers: clean.
  • cargo fmt --all -- --check: clean.
  • pnpm run contracts:generate, then contracts:check: up to date. The dashboard EffectClass and the SDK are regenerated.

@changeset-bot

changeset-bot Bot commented Sep 27, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: cf6249e

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 b836d91 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-27T16:28:49.033350Z cf6249e 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.

1 participant