From f62153bbc929e39f88e728f2806a3644c2149664 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 18 Sep 2026 23:39:17 +0000 Subject: [PATCH 01/10] fix(pr-1617): share the exact-arguments dispatch The review asked to add a no-`format`-injection variant of `handle_real_server_tool_call_raw` to `support.rs`, use it from `derives_test.rs`, and revert `CaptureTransport.incoming` to private. The local dispatch in `call_derives` only differed from the existing helper by skipping the default `format` injection, which its explicit `format: "markdown"` and bare-argument error cases depend on. Co-Authored-By: Claude Fable 5.1 --- .../mcp_handler_test/derives_test.rs | 23 ++++--------------- crates/tracedecay/tests/mcp_suite/support.rs | 14 ++++++++++- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/crates/tracedecay/tests/mcp_suite/mcp_handler_test/derives_test.rs b/crates/tracedecay/tests/mcp_suite/mcp_handler_test/derives_test.rs index e740733a4c..de51115ef9 100644 --- a/crates/tracedecay/tests/mcp_suite/mcp_handler_test/derives_test.rs +++ b/crates/tracedecay/tests/mcp_suite/mcp_handler_test/derives_test.rs @@ -6,8 +6,8 @@ //! the only runtime identity; every other field is a literal of the fixture. use crate::support::{ - CaptureTransport, ProductionCompositionFixture, production_composition_fixture_with_sources, - warm_code_index_search, + ProductionCompositionFixture, handle_real_server_tool_call_raw_exact, + production_composition_fixture_with_sources, warm_code_index_search, }; use serde_json::{Value, json}; use std::fs; @@ -301,23 +301,8 @@ async fn call_derives( .harness .server(&fixture.project_root) .expect("derives fixture server"); - let request = json!({ - "jsonrpc": "2.0", - "id": 1, - "method": "tools/call", - "params": { - "name": "tracedecay_derives", - "arguments": arguments, - } - }); - let mut transport = CaptureTransport { - incoming: Some(request.to_string()), - output: String::new(), - }; - Box::pin(server.run_connection(&mut transport)) - .await - .expect("real MCP server tool call"); - let response: Value = serde_json::from_str(transport.output.trim()).expect("JSON-RPC response"); + let response = + handle_real_server_tool_call_raw_exact(&server, "tracedecay_derives", arguments).await; if !response["error"].is_null() { return Err(response["error"].clone()); } diff --git a/crates/tracedecay/tests/mcp_suite/support.rs b/crates/tracedecay/tests/mcp_suite/support.rs index 78dc7dc2dc..93b26dac54 100644 --- a/crates/tracedecay/tests/mcp_suite/support.rs +++ b/crates/tracedecay/tests/mcp_suite/support.rs @@ -87,7 +87,7 @@ const SOURCE_EDIT_TOOL_NAMES: &[&str] = &[ #[cfg(feature = "test-transport")] #[derive(Default)] pub(crate) struct CaptureTransport { - pub(crate) incoming: Option, + incoming: Option, pub(crate) output: String, } @@ -211,6 +211,18 @@ pub(crate) async fn handle_real_server_tool_call_raw( .entry("format".to_string()) .or_insert_with(|| json!("json")); } + handle_real_server_tool_call_raw_exact(server, tool_name, arguments).await +} + +/// Same dispatch as [`handle_real_server_tool_call_raw`] without the default +/// `format` injection, for tests that assert the server's own default and its +/// argument-validation errors on the exact arguments a host would send. +#[cfg(feature = "test-transport")] +pub(crate) async fn handle_real_server_tool_call_raw_exact( + server: &McpServer, + tool_name: &str, + arguments: Value, +) -> Value { let request = json!({ "jsonrpc": "2.0", "id": 1, From 8176e7b278a89291ec13a0a61f2390d1d44bc8f5 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 18 Sep 2026 23:44:02 +0000 Subject: [PATCH 02/10] fix(pr-1707): sort the project_context_test module declaration The review found the two added lines landed between `dependency_hint_test` and `edit_test` instead of their alphabetical slot; the mod list is kept sorted (rustfmt `reorder_modules`). Co-Authored-By: Claude Fable 5.1 --- crates/tracedecay/tests/mcp_suite/mcp_handler_test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/tracedecay/tests/mcp_suite/mcp_handler_test.rs b/crates/tracedecay/tests/mcp_suite/mcp_handler_test.rs index 3aa73a7072..0cf78924be 100644 --- a/crates/tracedecay/tests/mcp_suite/mcp_handler_test.rs +++ b/crates/tracedecay/tests/mcp_suite/mcp_handler_test.rs @@ -20,8 +20,6 @@ mod dependency_depth_test; mod dependency_hint_test; mod derives_test; #[cfg(feature = "test-transport")] -mod project_context_test; -#[cfg(feature = "test-transport")] mod edit_test; #[cfg(feature = "test-transport")] mod fact_store_update_behavior_test; @@ -42,6 +40,8 @@ mod memory_feedback_test; #[cfg(feature = "test-transport")] mod move_symbol_test; #[cfg(feature = "test-transport")] +mod project_context_test; +#[cfg(feature = "test-transport")] mod rename_symbol_test; mod retrieve_truncation_test; mod schema_test; From 74613878d9e35f47b51ac2e050fe67404dd93e03 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 18 Sep 2026 23:45:09 +0000 Subject: [PATCH 03/10] fix(pr-1726): shut the grep fixture harness down at the end of the test The review found `tracedecay_grep_reports_literal_matches_and_typed_failures` ended without `fixture.harness.shutdown().await;`, unlike its siblings in the suite, leaving the production composition to be torn down implicitly. Co-Authored-By: Claude Fable 5.1 --- .../tests/mcp_suite/mcp_handler_test/grep_behavior_test.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/tracedecay/tests/mcp_suite/mcp_handler_test/grep_behavior_test.rs b/crates/tracedecay/tests/mcp_suite/mcp_handler_test/grep_behavior_test.rs index 7d8bd11cf2..4010bc1e8c 100644 --- a/crates/tracedecay/tests/mcp_suite/mcp_handler_test/grep_behavior_test.rs +++ b/crates/tracedecay/tests/mcp_suite/mcp_handler_test/grep_behavior_test.rs @@ -504,6 +504,9 @@ _Scanned {FILES_SCANNED} files._ "tool execution failed: config error: invalid path_glob '[': error parsing glob '[': unclosed character class; missing ']'" ) ); + + drop(server); + fixture.harness.shutdown().await; } fn json_text(response: &Value) -> Value { From 6c0be4c3d62976212c782d70ad14bec8e7546a16 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 18 Sep 2026 23:46:48 +0000 Subject: [PATCH 04/10] fix(pr-1740): resolve git through git_program and sort the mod line The review required `git_repository()` to use `common::git_program()` instead of a bare `Command::new("git")`, matching the rest of the suite so the fixture does not depend on an ambient `git` on PATH. The merge also placed `mod project_list_test;` out of its alphabetical slot. Co-Authored-By: Claude Fable 5.1 --- crates/tracedecay/tests/mcp_suite/mcp_handler_test.rs | 4 ++-- .../tests/mcp_suite/mcp_handler_test/project_list_test.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/tracedecay/tests/mcp_suite/mcp_handler_test.rs b/crates/tracedecay/tests/mcp_suite/mcp_handler_test.rs index 353fe7fea3..82ace14e95 100644 --- a/crates/tracedecay/tests/mcp_suite/mcp_handler_test.rs +++ b/crates/tracedecay/tests/mcp_suite/mcp_handler_test.rs @@ -36,8 +36,6 @@ mod hermes_skill_bridge_test; mod inheritance_depth_test; mod lcm_test; #[cfg(feature = "test-transport")] -mod project_list_test; -#[cfg(feature = "test-transport")] mod memory_contradiction_contract_test; #[cfg(feature = "test-transport")] mod memory_fact_assertions; @@ -48,6 +46,8 @@ mod move_symbol_test; #[cfg(feature = "test-transport")] mod project_context_test; #[cfg(feature = "test-transport")] +mod project_list_test; +#[cfg(feature = "test-transport")] mod project_search_behavior_test; #[cfg(feature = "test-transport")] mod rename_symbol_test; diff --git a/crates/tracedecay/tests/mcp_suite/mcp_handler_test/project_list_test.rs b/crates/tracedecay/tests/mcp_suite/mcp_handler_test/project_list_test.rs index 71e7dc8291..ef25ce74b6 100644 --- a/crates/tracedecay/tests/mcp_suite/mcp_handler_test/project_list_test.rs +++ b/crates/tracedecay/tests/mcp_suite/mcp_handler_test/project_list_test.rs @@ -581,7 +581,7 @@ fn directory(path: &Path) -> PathBuf { fn git_repository(path: &Path) -> PathBuf { let root = directory(path); - let status = Command::new("git") + let status = Command::new(crate::common::git_program()) .args(["init", "--quiet"]) .current_dir(&root) .status() From cfe4983a442579ba0fda9b6e6f38ca246044336e Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 18 Sep 2026 23:47:07 +0000 Subject: [PATCH 05/10] fix(pr-1747): drop the committed .audit scratch files The review required deleting `.audit/ast-grep-figure.summary.txt` and `.audit/ast-grep-rewrite.tsv`; they are one-off scratch output from the authoring session, not part of the test. Co-Authored-By: Claude Fable 5.1 --- .audit/ast-grep-figure.summary.txt | 3 --- .audit/ast-grep-rewrite.tsv | 10 ---------- 2 files changed, 13 deletions(-) delete mode 100644 .audit/ast-grep-figure.summary.txt delete mode 100644 .audit/ast-grep-rewrite.tsv diff --git a/.audit/ast-grep-figure.summary.txt b/.audit/ast-grep-figure.summary.txt deleted file mode 100644 index f81e809019..0000000000 --- a/.audit/ast-grep-figure.summary.txt +++ /dev/null @@ -1,3 +0,0 @@ -command: REQUIRE_EXACT_TEST_COUNT=3 scripts/require-exact-test.sh cargo test -p tracedecay --test mcp_suite --features test-transport ast_grep_rewrite_behavior_test -- --test-threads=1 -test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 350 filtered out; finished in 4.63s -EXIT:0 diff --git a/.audit/ast-grep-rewrite.tsv b/.audit/ast-grep-rewrite.tsv deleted file mode 100644 index f644b3d2ec..0000000000 --- a/.audit/ast-grep-rewrite.tsv +++ /dev/null @@ -1,10 +0,0 @@ -ts phase decision why evidence result -2026-09-18T14:06:38Z frame Done means three MCP behavior tests pass, the diff stays test-only, and the PR stays a draft. A reviewer who steps away needs a check they can fail, not a summary. https://github.com/ScriptedAlchemy/tracedecay/pull/1747 predicate set -2026-09-18T14:06:38Z design Audit the existing tests, then rerun them. Skip a second design pass. The rewrite proof already exists. Another design would add files without changing the check. crates/tracedecay/tests/mcp_suite/mcp_handler_test/ast_grep_rewrite_behavior_test.rs one sequential pass -2026-09-18T14:06:38Z audit The retry test now asserts the first apply touches src/checkout.rs before it asserts the retry touches nothing. An empty list by itself would still pass if the tool never reported files. crates/tracedecay/tests/mcp_suite/mcp_handler_test/ast_grep_rewrite_behavior_test.rs change written, not yet measured -2026-09-18T14:08:06Z measure Reran the three behavior tests through scripts/require-exact-test.sh with a required count of 3. A zero-match filter still exits 0. The script fails that case. /tmp/ast-grep-figure.log tests green, 3 passed, 0 failed -2026-09-18T14:14:24Z comments Deleted the module banner at the top of the behavior test. It restated the file name and was not a contract. crates/tracedecay/tests/mcp_suite/mcp_handler_test/ast_grep_rewrite_behavior_test.rs one comment deleted -2026-09-18T14:14:24Z evidence Pointed the green run at a saved summary instead of a temp log. A reviewer cannot open /tmp after this session ends. .audit/ast-grep-figure.summary.txt summary saved -2026-09-18T14:14:24Z draft Left PR 1747 open and not a draft. The PR tool refused a conversion back to draft. The original request asked for a draft and no merge. Merge did not happen. Draft status cannot be restored from here. https://github.com/ScriptedAlchemy/tracedecay/pull/1747 open -2026-09-18T14:14:24Z ci Did not change Clippy or repository-gate failures. Those jobs fail on files this branch does not touch, and the transport tests are still queued. Fixing unrelated files would leave the one-tool scope. https://github.com/ScriptedAlchemy/tracedecay/actions/runs/35353886523 open -2026-09-18T14:14:40Z measure Did not rerun after deleting the module banner. The banner is not an assertion. The counted run already included the touch-list check. .audit/ast-grep-figure.summary.txt not rerun From 17cdf9ad28f7c6f0758956d3f6c81b985eacc129 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 18 Sep 2026 23:48:09 +0000 Subject: [PATCH 06/10] fix(pr-1758): hold the isolation guard for the whole test body The review found `IsolatedEnv::acquire()` was bound inside `open_port_order_project()`, so the guard dropped as soon as the fixture was returned and the tests only passed under `--test-threads=1`. Acquire it in each test body instead, matching `affected_tests_behavior_test.rs`. Co-Authored-By: Claude Fable 5.1 --- .../tests/mcp_suite/mcp_handler_test/port_order_test.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/tracedecay/tests/mcp_suite/mcp_handler_test/port_order_test.rs b/crates/tracedecay/tests/mcp_suite/mcp_handler_test/port_order_test.rs index 475d41e7e0..e237dfb02c 100644 --- a/crates/tracedecay/tests/mcp_suite/mcp_handler_test/port_order_test.rs +++ b/crates/tracedecay/tests/mcp_suite/mcp_handler_test/port_order_test.rs @@ -70,7 +70,6 @@ const CYCLE_NOTE: &str = "Mutual dependency. Port together, starting at `entry_p const BREAK_RATIONALE: &str = "Highest in-cycle in-degree. Refactoring its callers is the most effective way to fragment this SCC."; async fn open_port_order_project() -> ProductionCompositionFixture { - let (_isolated_env, _) = crate::common::IsolatedEnv::acquire().await; let fixture = production_composition_fixture_with_sources(|project| { fs::create_dir_all(project.join("order")).unwrap(); fs::create_dir_all(project.join("cycle")).unwrap(); @@ -164,6 +163,7 @@ fn ordered_chain() -> Value { #[tokio::test] async fn port_order_ports_leaves_first_and_reports_one_scc() { + let (_isolated_env, _) = crate::common::IsolatedEnv::acquire().await; let fixture = open_port_order_project().await; // `mid` calls `leaf`; `top` calls `mid`. Leaves share level 0 in source order. @@ -301,6 +301,7 @@ async fn port_order_ports_leaves_first_and_reports_one_scc() { #[tokio::test] async fn port_order_rejects_unknown_kinds_and_missing_source_dir() { + let (_isolated_env, _) = crate::common::IsolatedEnv::acquire().await; let fixture = open_port_order_project().await; let unknown_kind = tool_error( From 08acb2e1397774b8df6acfc9c1b2fde912c37703 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Fri, 18 Sep 2026 23:56:22 +0000 Subject: [PATCH 07/10] fix(pr-1708): use Duration::from_mins for the feedback-owner deadline `cargo clippy --workspace --all-targets --locked -- -D warnings` (the CI Clippy job) fails on `Duration::from_secs(60)` with `clippy::duration_suboptimal_units`. `Duration::from_mins` is the form the rest of the workspace already uses for minute-scale deadlines. Co-Authored-By: Claude Fable 5.1 --- crates/tracedecay/src/daemon/tests/feedback_impact.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/tracedecay/src/daemon/tests/feedback_impact.rs b/crates/tracedecay/src/daemon/tests/feedback_impact.rs index 7b2add49e5..cb94e1ad67 100644 --- a/crates/tracedecay/src/daemon/tests/feedback_impact.rs +++ b/crates/tracedecay/src/daemon/tests/feedback_impact.rs @@ -241,7 +241,7 @@ async fn wait_for_feedback_owner( harness: &ProductionProjectCompositionHarnessV1, project: &Path, ) -> JsonRpcResponse { - let deadline = Instant::now() + Duration::from_secs(60); + let deadline = Instant::now() + Duration::from_mins(1); loop { let response = harness .call_tool( From 80dab83216da5e2f34ddd87a88ef4606b7e155d6 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sat, 19 Sep 2026 00:00:16 +0000 Subject: [PATCH 08/10] fix(pr-1633): warm diagnose fixture through the shared support helper The review required replacing the file-local `wait_for_graph` status poll with `crate::support::warm_code_index_search(&server, "target")`, matching 1622/1628. The fixture exposes a server through `fixture.harness.server(&fixture.project_root)`, so the swap is direct and the helper also waits on the search generation, not only graph serving. Co-Authored-By: Claude Fable 5.1 --- .../mcp_handler_test/diagnose_test.rs | 63 +++---------------- 1 file changed, 10 insertions(+), 53 deletions(-) diff --git a/crates/tracedecay/tests/mcp_suite/mcp_handler_test/diagnose_test.rs b/crates/tracedecay/tests/mcp_suite/mcp_handler_test/diagnose_test.rs index 4cb0fef00a..e889989312 100644 --- a/crates/tracedecay/tests/mcp_suite/mcp_handler_test/diagnose_test.rs +++ b/crates/tracedecay/tests/mcp_suite/mcp_handler_test/diagnose_test.rs @@ -9,12 +9,14 @@ #![cfg(feature = "test-transport")] use std::fs; -use std::time::Duration; use serde_json::{Value, json}; use tracedecay_mcp::JsonRpcResponse; -use crate::support::{ProductionCompositionFixture, production_composition_fixture_with_sources}; +use crate::support::{ + ProductionCompositionFixture, production_composition_fixture_with_sources, + warm_code_index_search, +}; const SOURCE: &str = "pub fn target() {}\npub fn caller() { target(); }\n"; @@ -347,60 +349,15 @@ async fn open_indexed_project() -> ProductionCompositionFixture { fs::write(project.join("src/lib.rs"), SOURCE).unwrap(); }) .await; - wait_for_graph(&fixture).await; + let server = fixture + .harness + .server(&fixture.project_root) + .expect("diagnose fixture server"); + warm_code_index_search(&server, "target").await; + drop(server); fixture } -async fn wait_for_graph(fixture: &ProductionCompositionFixture) { - tokio::time::timeout(Duration::from_secs(20), async { - loop { - let response = fixture - .harness - .call_tool( - &fixture.project_root, - "tracedecay_status", - json!({ - "format": "json", - "include_branch_diagnostics": false, - "include_storage_health": false, - "include_session_ingest": false, - "include_staleness": false, - }), - ) - .await - .expect("status while the graph is publishing"); - assert!( - response.error.is_none(), - "status failed: {:?}", - response.error - ); - let status = json_text(&response); - let freshness = &status["code_index_freshness"]; - let serving = &freshness["worktree"]["code_graph_serving"]; - match ( - freshness["status"].as_str(), - serving["state"].as_str(), - serving["reason"].as_str(), - freshness["worktree"]["staleness_state"].as_str(), - ) { - (Some("current"), Some("ready"), _, _) => break, - (Some("warming"), _, _, _) - | (Some("stale"), Some("ready"), _, Some("verifying")) - | (_, Some("pending"), _, _) - | (_, Some("unavailable"), Some("generation_unavailable"), _) => { - tokio::time::sleep(Duration::from_millis(50)).await; - } - (_, Some("refused"), _, _) | (_, _, Some("activation_disabled"), _) => { - panic!("graph readiness was refused: {status}"); - } - actual => panic!("graph readiness became {actual:?}: {status}"), - } - } - }) - .await - .expect("graph did not become current within the publication budget"); -} - async fn exact_symbol_id(fixture: &ProductionCompositionFixture, name: &str) -> String { let response = fixture .harness From 0fa50fc3b08a5b68bd1938ab1417c344980196ad Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sat, 19 Sep 2026 00:11:01 +0000 Subject: [PATCH 09/10] Revert "fix(daemon): mount the published branch worktree's query authority" This reverts commit 51402cdf8d4e2e085330ccb1f7f0a59fac875c20. --- crates/tracedecay/src/daemon/branch_add.rs | 89 -------------------- crates/tracedecay/src/daemon/branch_admin.rs | 21 ----- 2 files changed, 110 deletions(-) diff --git a/crates/tracedecay/src/daemon/branch_add.rs b/crates/tracedecay/src/daemon/branch_add.rs index 959bb92702..4e2d3793f7 100644 --- a/crates/tracedecay/src/daemon/branch_add.rs +++ b/crates/tracedecay/src/daemon/branch_add.rs @@ -153,8 +153,6 @@ async fn activate_and_track_manual_branch( let graph = Arc::clone(graph); let schedulers = schedulers.clone(); let branch = branch.to_owned(); - let published_schedulers = schedulers.clone(); - let published_sessions = administration.mounted_session_runtime_registry().await; administration .admit_manual_branch_publication(|cancellation, admitted| async move { @@ -217,15 +215,6 @@ async fn activate_and_track_manual_branch( tracked } .await; - if matches!(&result, Ok(outcome) if *outcome != BranchAddOutcome::Deferred) { - mount_published_branch_query_authority( - published_sessions.as_ref(), - &published_schedulers, - &data_root, - &branch, - ) - .await; - } match &result { Ok(outcome) => log_daemon_event( "manual_branch_publication", @@ -248,84 +237,6 @@ async fn activate_and_track_manual_branch( .await } -/// Mounts the checked-in core query authority on the branch worktree this -/// publication sealed, from the project's own durable cursor-key authority. -/// -/// An explicitly published branch worktree is never a project-open route, so -/// nothing else mounts its query authority: an exact branch read could only -/// borrow one already mounted on a peer checkout of the same repository. That -/// peer's own mount is deferred until it seats a text generation, so a read -/// taken right after this publication sealed its provenance failed closed with -/// a non-retryable `authority_unavailable`. Mounting here makes the generation -/// this journey publishes queryable as soon as its provenance commits. -/// -/// Best effort by design: the branch generation is already committed, so a -/// missing session mount or cursor key must not retract it. The exact branch -/// read falls back to borrowing a peer authority when this could not run. -#[cfg(unix)] -#[hotpath::measure(label = "daemon.branch_add.query_authority", future = true)] -async fn mount_published_branch_query_authority( - sessions: Option<&Arc>, - schedulers: &CodeIndexSchedulerRegistryV1, - data_root: &Path, - branch: &str, -) { - let Some(sessions) = sessions else { - return; - }; - let Some(source) = - tracedecay_runtime_core::branch_meta::load_branch_meta(data_root).and_then(|meta| { - meta.branches - .get(branch) - .and_then(|entry| entry.graph_source.clone()) - }) - else { - return; - }; - let worktree_root = std::path::PathBuf::from(&source.worktree_root); - let Ok(project_id) = tracedecay_domain::ProjectId::new(source.project_id.clone()) else { - return; - }; - let Ok(scope) = - tracedecay_code_index_runtime::resolved_scope_for_project(&worktree_root, &project_id) - else { - return; - }; - let Some(session_db) = sessions.mounted_project_sessions(&project_id).await else { - return; - }; - let cursor_keys = match session_db.load_session_cursor_key_provider_result().await { - Ok(cursor_keys) => cursor_keys, - Err(error) => { - tracing::debug!( - event = "branch_query_authority_mount", - outcome = "unavailable", - branch = %branch, - reason = %error, - "durable query cursor key is unavailable for the published branch" - ); - return; - } - }; - if let Err(error) = - tracedecay_code_index_runtime::code_index_scheduler::query_runtime::mount_core_query_authority_on_project_open( - schedulers, - &worktree_root, - &scope, - &cursor_keys, - ) - .await - { - tracing::debug!( - event = "branch_query_authority_mount", - outcome = "unavailable", - branch = %branch, - reason = %error, - "published branch query authority is unavailable; exact reads fall back to a peer" - ); - } -} - #[cfg(unix)] #[hotpath::measure(label = "daemon.branch_add.owner", future = true)] pub(super) async fn activate_and_track_manual_branch_owned( diff --git a/crates/tracedecay/src/daemon/branch_admin.rs b/crates/tracedecay/src/daemon/branch_admin.rs index 31813e667e..f65a2c3924 100644 --- a/crates/tracedecay/src/daemon/branch_admin.rs +++ b/crates/tracedecay/src/daemon/branch_admin.rs @@ -944,27 +944,6 @@ impl StoreAdministration { registry.mounted_session_databases().await } - /// The mounted session-runtime registry for this profile, when one is - /// installed. Branch publication reads the project's durable cursor-key - /// authority through it so an explicitly published branch can mount its - /// own query authority instead of borrowing a peer worktree's. - #[hotpath::measure(label = "daemon.branch_admin.session_runtime_registry", future = true)] - pub(super) async fn mounted_session_runtime_registry( - &self, - ) -> Option> { - let profile_root = self - .profile_identity() - .and_then(|identity| authority::canonical_identity_path(identity.profile_root())) - .ok()?; - let registry = { - let registries = self.session_runtime_registries.lock().await; - registries - .get(&profile_root) - .map(|entry| Arc::clone(&entry.registry)) - }?; - registry.get().cloned() - } - #[hotpath::measure(label = "daemon.branch_admin.mounted_project_servers", future = true)] pub(super) async fn mounted_project_servers(&self) -> Vec> { let Ok(profile_root) = self From 1b175519ce9e8a942f6bc1dce05109d2fcf645fc Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Sat, 19 Sep 2026 00:58:36 +0000 Subject: [PATCH 10/10] test(mcp): make the project_search injection probe one token The registry search ORs whitespace-separated tokens through `LIKE`. The injection probe `search-alpha' OR '1'='1` therefore also searched for the substring `OR`, which matched both fixture roots whenever their shared temp directory drew a name containing it (`.tmpXoRyz`, ~2% of runs; CI run 35408468113 TRY 1). Drop the whitespace so the probe stays a single token that breaks the quote but can match no fixture field. Co-Authored-By: Claude Fable 5.1 --- .../mcp_handler_test/project_search_behavior_test.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/tracedecay/tests/mcp_suite/mcp_handler_test/project_search_behavior_test.rs b/crates/tracedecay/tests/mcp_suite/mcp_handler_test/project_search_behavior_test.rs index 215e9cac69..6abd724350 100644 --- a/crates/tracedecay/tests/mcp_suite/mcp_handler_test/project_search_behavior_test.rs +++ b/crates/tracedecay/tests/mcp_suite/mcp_handler_test/project_search_behavior_test.rs @@ -499,14 +499,18 @@ async fn project_search_bounds_pages_and_does_not_expand_wildcards() { "No projects matching \"no-such-project-token\" found." ); + // The search ORs whitespace-separated tokens, so a spaced `OR` would be a + // legitimate two-letter substring token that can match a random temp + // path (`.tmpXoRyz`). Keep the quote breakout, drop the whitespace, so + // the query is one token that no fixture field contains. let injected = search_json( server, - json!({"query": "search-alpha' OR '1'='1", "format": "json"}), + json!({"query": "search-alpha'OR'1'='1", "format": "json"}), ) .await; assert_eq!(project_ids(&injected), Vec::::new()); assert_eq!(injected["status"], "ok"); - assert_eq!(injected["query"], "search-alpha' OR '1'='1"); + assert_eq!(injected["query"], "search-alpha'OR'1'='1"); } #[tokio::test]