Repository navigation
Conversation
explain_change_impact and detect_changes waited up to 3 s on every pending reindex receipt in the repository and then failed with "refused a stale graph". The edit that triggered the wait was usually in a file unrelated to the answer, and an id that did not resolve widened the wait to every receipt, so concurrent agents got refusals for most impact calls and each one held a dispatch slot for the full wait. Both handlers now wait at most 250 ms, and only for the edits that can change the answer: the queried symbols' own files, files with edges into them, files whose current bytes name them as a whole identifier (a call the graph has not read yet), and for detect_changes the changed files of the diff, which is now mapped files-first so the scope is known before the wait. An unresolved id contributes the file and name its spelling carries instead of widening the scope. Whatever in scope is still pending or failed labels the answer (complete:false, graph_freshness, freshness_scope, pending_files, pending_file_count, and recovery_hint when failed); the walk always runs. An earlier wait_deadline ends the wait sooner, including one that passed between admission and the handler reading it. TOON and plain text answers lead with a "relations: <state> (N files)" line, and GCX keeps GCX1 as line 1 with the label as header meta and the line as a comment on line 2. Every answer reports barrier_wait_ms. Only require_fresh refuses, once its wait_deadline passes with the scope still lagging. The label lives in relation_freshness.go so the relation-reading mutators can share it.
…lations The Stop briefing and the Codex PostToolUse briefing call detect_changes with only the 5 s transport bound, and fold every failure into an empty string. While edits were pending the change tool refused, the hook could not decode the error, and the agent was told nothing about its change set. detect_changes now gets 300 ms through callServerToolWithin, which tells a reply that did not arrive in time apart from an unreachable bridge. The call carries a wait_deadline 150 ms out, so the daemon labels the edits it is still publishing instead of waiting 250 ms for them on top of the diff and the walk, which would land the labelled answer past the budget. An empty reply after that deadline, which is how the daemon refuses a deadline that passed before it took the call up, counts as a stall. A stall, or an answer labelled pending or failed (graph_freshness on a primary checkout, graph_status on a worktree), yields a short briefing with the relations line and the changed files instead of silence or diagnostics over lagging relations. A stall says only that detect_changes did not answer, and stays silent for a session that recorded no writes, so a slow daemon does not put a warning into every read-only turn. renderDeadCodeHits never reports a symbol listed on a "pending_unknown " line of analyze dead_code as dead.
…allers detect_changes scoped its wait and label to the diff's changed files, but its risk and blast radius come from walking the changed symbols' callers. With scope staged or compare the working tree is not in the diff, so a pending edit in a caller file left the walk reading the old relations without a label. After the symbol join it now also waits for, and labels, the relation scope of the changed symbols: their files, the files with edges into them and the files that name them. That wait ends at the same deadline as the changed files' wait, so the answer still waits at most once.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Status: work in progress. The handler, hook and helper changes are done and pass their focused tests. Two pieces wait for #1169 and #1159, which change the same file: deleting the old freshness barrier, and the
docs/mcp.mdparagraph.explain_change_impactanddetect_changeswaited up to 3 s on every pending reindex receipt in the repository. Then they failed with "refused a stale graph". This caused three problems:Changes
Both handlers wait at most 250 ms, and only for edits that can change the answer. That means:
detect_changes, the diff's changed files. The diff is now mapped files-first, so the scope is known before the wait.An unresolved id adds the file and name its spelling carries, instead of widening the scope.
Lagging edits label the answer instead of refusing it. Anything in scope that is still pending or failed adds these fields:
complete:false,graph_freshness,freshness_scope,pending_files(up to 16),pending_file_count, andrecovery_hintwhen failed. The walk always runs, and every answer reportsbarrier_wait_ms.Text formats show the label first.
relations: <state> (N files)line.GCX1as line 1, carries the label as header meta, and adds the relations line as a comment on line 2.Deadlines and refusals. An earlier
wait_deadlineends the wait sooner, including a deadline that passed between admission and the handler reading it. Onlyrequire_freshstill refuses, once its deadline passes with the scope still lagging.detect_changesalso covers the changed symbols' callers. After the symbol join, it waits for and labels the changed symbols' relation scope: their files, files with edges into them, and files that name them. This wait shares the deadline of the changed-files wait. Before this, with scopestagedorcompare, the working tree is not in the diff, so a pending edit in a caller file was walked without a label.Hooks. The Stop briefing and the PostToolUse briefing now call
detect_changesthroughcallServerToolWithinwith a 300 ms budget.wait_deadline150 ms out, so the daemon labels pending edits instead of waiting past the budget.renderDeadCodeHitsnever reports as dead a symbol listed on apending_unknownline ofanalyze dead_code.The label lives in
relation_freshness.go, so the relation-reading mutators in the follow-up PR can share it.Status
Done: the three commits on this branch. Production code no longer calls the old barrier.
Not done. These wait for #1169 and #1159, which change
edit_serialization.go:awaitMutationFreshnessForRepos,awaitMutationFreshness,mutationReposForSymbolIDs,mutationSafetyWaitanddefaultMutationSafetyWait. Until then, their tests keep them in use, so lint stays clean.docs/mcp.mdparagraph on change-tool labels.No test on this branch is red by design.
Tests
TestChangeImpactLabelsLaggingReceiptsInScope: pending is labelled, published is clean, failed carries a recovery hint.TestDetectChangesLabelsPendingChangedFile, includingwait_deadlineat +50 ms and -1 s, and a pending caller under staged scope.TestChangeToolsIgnorePendingReceiptOutsideScope,TestChangeToolsScopeIncludesFilesNamingTheSymbol,TestChangeToolsUnresolvedIDDoesNotWidenBarrierandTestChangeToolsRequireFreshWaitsUntilWaitDeadline.TestChangeAnswersStartWithRelationsLine.TestPostTaskBriefingPrintsPendingRelationsandTestPostTaskBriefingBoundedWhenDetectStalls.TestRunPostTask_DeadCodeFiltersToChanged.-race, these pass:TestChangeTools*,TestChangeAnswers*,TestChangeImpact*,TestDetectChanges*, mutation freshness, checkout mutation receipt, overlay and zero-edge caveat;Diff.go vetandgofmtare clean. golangci-lint over mcp, hooks and analysis reports 0 issues.Untested: the full package suites for mcp, hooks and analysis. CI runs them.
Refs #1018