Summary
change(operation="detect") can exceed the MCP facade's ~59-second deadline in a multi-repo workspace even with output.summary_only=true. Narrowing the same call with options.repo makes it complete in 10–34 seconds.
This is not an index-health failure. The measured workspace reported health_score=100, clean path liveness, and a ready daemon.
Environment
- Gortex:
v0.63.8+2a33f3d
- Host: Windows 11
- MCP client: Codex
0.150.1
- Workspace: 4 near-identical linked worktrees/repositories
- Daemon snapshot: 10,004 files, 535,510 nodes, 2,808,466 edges
- Connected MCP sessions at snapshot: 22
- Daemon state:
ready; memory 291 MiB; 111 goroutines
No source, repository content, or raw pprof output is attached.
Reproduction
Broad workspace call:
{
"operation": "detect",
"source": {"scope": "all"},
"output": {"summary_only": true}
}
Observed after approximately 59 seconds:
tool "change" exceeded its 58.997s deadline and was abandoned
Repo-scoped calls against the same daemon and dirty tree:
{
"operation": "detect",
"source": {"scope": "all"},
"options": {"repo": "domain-manage"},
"output": {"summary_only": true}
}
Completed in 10.309 seconds.
Changing only the scope to unstaged completed in 34.037 seconds. The variance is consistent with concurrent/background contention, but both repo-scoped calls stayed within the MCP deadline.
Per-repo controls:
- clean linked worktree: 0.48 seconds;
- dirty documentation worktree: 10.67 seconds;
- another clean linked worktree: 0.46 seconds.
analyze(kind="retrieval_log", options:{repo:"domain-manage"}) reported for the latest 5,000 queries:
change n=423 p50=238.4ms p95=143003.7ms
The p95 is over twice the MCP deadline.
Index/daemon controls
workspace(operation="index") completed in milliseconds and reported:
health_score: 100
path_liveness.clean: true
orphan_files: 0
resolution_regressions: 0
edges_ok: true
gortex daemon status reported the daemon as ready. A graph-wide analyze health and workspace graph call also exceeded the same deadline, which points to broad graph/store work or contention rather than a git diff failure specific to change.detect.
All detect scopes reported that they read tracked git diff only. Untracked .codex-worktrees directories were therefore not the cause.
summary_only observation
Successful responses with summary_only=true still contained the complete changed_symbols, changed_files, and file_changes payloads. The option removed per-depth row lists but did not avoid:
- Git diff collection;
- range-to-symbol lowering;
- reach traversal;
- materialization/serialization of changed symbols.
Therefore summary_only is useful for response shaping but does not currently provide a count-only fast path.
Retry behavior
The timeout response says work may continue in the background. Retrying the same broad request immediately three times can therefore stack expensive work and amplify contention. A later repo-scoped request succeeds, so restarting/reindexing a healthy daemon is not an appropriate first response.
Requested improvements
-
Current-repo default or hook scoping
- Default
change.detect to the current repo when a session cwd resolves unambiguously, or make the Codex post-edit hook pass the explicit repo selector.
- Preserve an explicit workspace-wide option for callers that need it.
-
True count-only output
- Add an output mode that avoids materializing
changed_symbols and per-symbol reach rows when only counts/risk/file names are required.
- Alternatively clarify that
summary_only is response-only and add a separate compute-short-circuit field.
-
Deadline cancellation
- Propagate the MCP context cancellation through Git diff/lowering/reach work so an abandoned request does not continue consuming graph/store capacity.
-
Phase timing diagnostics
- Return or locally log bounded timings such as
git_diff_ms, lower_ms, reach_ms, serialize_ms, selected repo count, seed-symbol count, and whether the reach index fell back to a live graph walk.
Current workaround
After the first timeout, do not retry the broad call immediately. Check index health, then retry once with both an explicit options.repo and the narrowest required source.scope. If that still times out, use already-known symbol IDs for targeted impact/tests/guards/contracts and record the detect timeout rather than claiming full-workspace absence.
Summary
change(operation="detect")can exceed the MCP facade's ~59-second deadline in a multi-repo workspace even withoutput.summary_only=true. Narrowing the same call withoptions.repomakes it complete in 10–34 seconds.This is not an index-health failure. The measured workspace reported
health_score=100, clean path liveness, and a ready daemon.Environment
v0.63.8+2a33f3d0.150.1ready; memory 291 MiB; 111 goroutinesNo source, repository content, or raw pprof output is attached.
Reproduction
Broad workspace call:
{ "operation": "detect", "source": {"scope": "all"}, "output": {"summary_only": true} }Observed after approximately 59 seconds:
Repo-scoped calls against the same daemon and dirty tree:
{ "operation": "detect", "source": {"scope": "all"}, "options": {"repo": "domain-manage"}, "output": {"summary_only": true} }Completed in 10.309 seconds.
Changing only the scope to
unstagedcompleted in 34.037 seconds. The variance is consistent with concurrent/background contention, but both repo-scoped calls stayed within the MCP deadline.Per-repo controls:
analyze(kind="retrieval_log", options:{repo:"domain-manage"})reported for the latest 5,000 queries:The p95 is over twice the MCP deadline.
Index/daemon controls
workspace(operation="index")completed in milliseconds and reported:gortex daemon statusreported the daemon as ready. A graph-wideanalyze healthandworkspace graphcall also exceeded the same deadline, which points to broad graph/store work or contention rather than agit difffailure specific tochange.detect.All detect scopes reported that they read tracked
git diffonly. Untracked.codex-worktreesdirectories were therefore not the cause.summary_onlyobservationSuccessful responses with
summary_only=truestill contained the completechanged_symbols,changed_files, andfile_changespayloads. The option removed per-depth row lists but did not avoid:Therefore
summary_onlyis useful for response shaping but does not currently provide a count-only fast path.Retry behavior
The timeout response says work may continue in the background. Retrying the same broad request immediately three times can therefore stack expensive work and amplify contention. A later repo-scoped request succeeds, so restarting/reindexing a healthy daemon is not an appropriate first response.
Requested improvements
Current-repo default or hook scoping
change.detectto the current repo when a session cwd resolves unambiguously, or make the Codex post-edit hook pass the explicit repo selector.True count-only output
changed_symbolsand per-symbol reach rows when only counts/risk/file names are required.summary_onlyis response-only and add a separate compute-short-circuit field.Deadline cancellation
Phase timing diagnostics
git_diff_ms,lower_ms,reach_ms,serialize_ms, selected repo count, seed-symbol count, and whether the reach index fell back to a live graph walk.Current workaround
After the first timeout, do not retry the broad call immediately. Check index health, then retry once with both an explicit
options.repoand the narrowest requiredsource.scope. If that still times out, use already-known symbol IDs for targeted impact/tests/guards/contracts and record the detect timeout rather than claiming full-workspace absence.