Skip to content

fix(mcp): carry the index-drift guardrail into the per-project instructions - #1851

Open
AllanStepps wants to merge 1 commit into
colbymchenry:mainfrom
AllanStepps:fix/no-root-index-drift-guardrail
Open

fix(mcp): carry the index-drift guardrail into the per-project instructions#1851
AllanStepps wants to merge 1 commit into
colbymchenry:mainfrom
AllanStepps:fix/no-root-index-drift-guardrail

Conversation

@AllanStepps

Copy link
Copy Markdown

The gap

SERVER_INSTRUCTIONS_NO_ROOT_INDEX omits the index-drift anti-pattern:

A file flagged "⚠ changed on disk after the last index sync" drifted from its index (most common on projects queried via projectPath, which have no live watcher).

That parenthetical names projectPath-reached projects as the audience most exposed to drift. A server whose root has no index serves nothing but projectPath queries — the variant says so itself ("pass its path as projectPath … for as many projects as you like in one session"). So the guidance is absent from the only audience that always meets the condition it describes.

An agent that receives the ⚠ changed on disk flag without ever having been told what it means can read a flagged response as a normal one. That makes this a correctness gap rather than an efficiency one: the flag exists precisely because codegraph declined to serve a possibly-mis-sliced body, and the agent is supposed to Read that file instead.

This deliberately does not re-send the playbook

__tests__/mcp-unindexed.test.ts pins that the per-project variant is not the full playbook, and the comment gives the reason ("keeps the common case tight"). I agree with that decision, so this PR does not touch it:

  • expect(instructions).not.toMatch(/## How to query/) still passes, unchanged.
  • The variant grows by 581 characters, not by the ~5.5 KB a full playbook would add.
  • SERVER_INSTRUCTIONS is byte-identical before and after — verified by extracting both constants pre- and post-change and comparing.

Only the one bullet whose own text points at this audience moves, into a shared INDEX_DRIFT_GUARDRAIL constant that both variants interpolate, so a later wording edit cannot reach one variant and miss the other.

Tests

Three assertions added to the existing mcp-unindexed suite: both variants carry the guardrail, and the per-project one still is not the full playbook.

npx vitest run __tests__/mcp-unindexed.test.ts
  Test Files  1 passed (1)
       Tests  11 passed (11)

npm run build (tsc included) passes. __tests__/installer-targets.test.ts shows 2 failures on my machine under the CLAUDE_CONFIG_DIR describe — I verified those are pre-existing: identical on a clean checkout with my changes stashed. Node 22.23.2 (the suite needs node:sqlite, so ≥22.5 as documented; on Node 20 the DB-backed tests fail before reaching any of this).

How I ran into it

A global codegraph install, then work across several repos — so the server starts outside any index and every project is reached by projectPath. src/mcp/tools.ts:1643 already records a prior case where this variant's prose was not enough on its own and "the reporter had to add an AGENTS.md note"; I had written the same kind of note before tracking it to this constant.

Happy to narrow or reword if you would rather keep the variant leaner still.

…ctions

SERVER_INSTRUCTIONS_NO_ROOT_INDEX omitted the "changed on disk after the
last index sync" anti-pattern. That bullet's own parenthetical names
projects queried via `projectPath` — "which have no live watcher" — as the
audience most exposed to drift, and a server whose root has no index serves
nothing but `projectPath` queries. The guidance was therefore missing from
the only audience that always hits the condition it describes.

An agent that meets the flag without having been told what it means can
read a flagged response as a normal one, so this is a correctness gap
rather than an efficiency one.

The bullet moves into one INDEX_DRIFT_GUARDRAIL constant that both variants
interpolate, so a later edit cannot reach one and miss the other.
SERVER_INSTRUCTIONS is byte-identical before and after; the per-project
variant grows by 581 characters and stays well short of the full playbook,
which the existing "not the full playbook" assertion still pins.
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