Skip to content

docs(mcp): re-anchor the dead tracker citations in packages/mcp/src to the commits that decided them - #20713

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20594-mcp-dead-citations
Sep 29, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20594-mcp-dead-citations

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #20594
Clause-②: no

What changed

This is stage 7 of the domain:cli lane of the dead-citation sweep: packages/mcp/src. Every comment site there that cited a tracker number answering 404 now cites, in ruling C+D's form C (comment 5749154545 on #19123), the commit in this repository's history that decided what the line describes, and keeps saying in its own words what that commit decided. PR #20533 is the method, and stages 1 to 6 of this card (PR #20624, PR #20632, PR #20656, PR #20673, PR #20689, PR #20703) are the precedents. The card stays open for the lane's remaining packages, so this PR says Part of.

That is 17 sites on 17 lines in 9 files, covering 9 numbers, rewritten to 9 distinct commits:

  • the census's 10 sites, in mcp-server-runtime.ts (5), plugin.ts (3) and stdio-data-bridge.ts (2), 7 numbers;
  • 7 test-file comment sites in 6 test files (the census defers *.test.ts; stages 1 to 6 took test comments too).

One more line changed: __tests__/plugin-execution-context.test.ts:7, the second half of the :6 sentence ("this face was not in that card's inventory" now reads "not in that commit's inventory", since the card it pointed back to is now named as a commit).

Only comments changed: 18 lines out, 18 in, and every touched file keeps its line count, so no line citation into these files moves. No citation number is added: over the 18 line pairs, added-minus-removed numbers is empty, and no PR number stands newly on any line. No ADR or ruling-record file in docs/adr/ or scripts/adr-anchors/ records any of these 9 decisions (a grep for the 9 numbers there reads 0 hits, with a control number from the same tree reading 2), so every anchor is a commit.

No changeset, and skip-changeset: none of the rewritten comments reaches dist (measured below: base and head emit six byte-identical files, and a code-mutation control changes four of them). That is stage 5's case (PR #20689), not stage 6's.

Census: packages/mcp, before and after

Instrument. The gate's own node scripts/check-issue-citations.mjs --census --json, read-only and unchanged, run under with-fleet.sh --read for the token. The count is its allocated-but-absent findings under packages/mcp/. Both runs enumerated the whole board.

reading tree board whole-repo allocated-but-absent packages/mcp sites lines numbers files
before base e4e5222b7b, run 2026-09-29T19:45:33Z to 19:50:37Z enumerated, 186 pages, frontier #20708, 18,535 numbers 1,222 10 10 7 3
after 459ff81088, run 19:58:39Z to 20:02:47Z enumerated, 186 pages, frontier #20709, 18,536 numbers 1,212 0 0 0 0

The whole-repo drop of 10 is exactly these sites: a site-by-site diff of the two JSON outputs has 10 findings gone, all under packages/mcp/src, and none added. The other three tallies (resolves 32,968, resolves-as-pull-request 1,984, cross-repo-unjudged 994) are equal in both runs. packages/mcp/src is byte-identical at 459ff81088 and at the head.

Supplementary scan (test files included). The gate's exported extractCitations and classifyCitation over all 43 .ts files under src/, with the board from the gate's own probeBoard: 365 citations and 21 dead before (src comments 10, test comments 7, src strings 0, test strings 4), 348 and 4 after (0, 0, 0, 4). Its before list of src comment sites is identical to the census's. The 4 left are test titles, the form-D stage (see Acceptance notes).

Per-site table

git blame at the base ties each line to the commit that wrote it, and each anchor was read in its message, changeset or diff, not only its subject. Where the pull request that landed an anchor still answers, its body's first line names the dead number, which is noted.

number sites (base line) anchor: what it decided
#13318 mcp-server-runtime.ts:272 3ec8646f1: the bridged tools' readOnlyHint / destructiveHint come from what the definition declares, and a tool that declares nothing is served neither hint (omit-when-unsourced). The line blames to c39369d12, the openWorldHint sibling, whose changeset calls this the repair "that preceded it". The PR that landed 3ec8646f1 answers 404 too.
#6724 mcp-server-runtime.ts:625; mcp-server-runtime.metadata-outage.test.ts:289 4f3d2322e: corrects diagnoseEmptyRead's falsified claim that MetadataFacade.getObject differs from get('object', n), in the TSDoc and in the outage test's restatement of it. Both lines blame to it; PR #6948, which landed it, names #6724.
#6745 mcp-server-runtime.ts:636 7a5ef0008: adds metadata-service-getobject-equivalence.test.ts, pinning getObject(n) equal to get('object', n) across all three implementations. The line's "PR #6839 for #6745" named this commit's PR (answers 200), which stays beside the sha as a convenience link. The spec lane gave the number this anchor.
#6723 mcp-server-runtime.ts:637, :652; mcp-server-runtime.metadata-outage.test.ts:293 8ad609c69: declares on IMetadataService.getObject that it answers the same as get('object', name). #6723 was the pull request that landed as this commit (its subject carries the number); #6505, the issue beside it on :637, answers 200 and stays. The spec lane gave the number this anchor.
#17114 plugin.ts:8, :67; stdio-tenancy-posture-api-key-matrix.test.ts:569 4af758d47: the last two admission doors, this one included, classify the tenancy rejection through the shared classifyAdmissionTenancyPosture. All three lines blame to it; PR #17683 names #17114, and stage 1 gave the number this anchor.
#6216 plugin.ts:126; __tests__/plugin-execution-context.test.ts:6 f586f1a89: one ExecutionContext assembler for the dispatcher, REST and share-link sites. Both lines blame to 502dc6fe7, which converged this stdio face afterwards and names that convergence as its precedent. Its file list touches no packages/mcp file, which is what :7 ("not in that commit's inventory") says. Stages 1 and 2 and the spec lane gave the number this anchor.
#8422 stdio-data-bridge.ts:85, :394; stdio-data-bridge.not-found.test.ts:4 4810dd628: the stdio bridge's by-id write seams throw the shared recordNotFoundError envelope instead of a bare Error. All three lines blame to it; PR #8507 names #8422.
#17568 mcp-record-id-key-mistake-refusal.test.ts:4 9c9e6d08f: pins that a missing-recordId refusal also names the id the caller sent (test-only). The line blames to it; PR #17650 names #17568.
#13486 mcp-tool-bridge-safety-annotations.test.ts:423 6193e576d: pins the bridge's two hand-copied safety name sets in the direction the old pin could not see (the docblock's heading is that commit's subject). The line blames to it; PR #13888 names #13486.

Anchor checks. Every cited sha matches exactly one object (git rev-parse --disambiguate, count 1 for each of the 9), is a commit, has one parent, and is an ancestor of main (merge-base --is-ancestor against 5757463712, exit 0 for all 9). The checkout is not shallow. The control leg 979ad9575 (2026-08-08, the parent of the oldest anchor 8ad609c69 of 2026-08-08) exits 0, and the negative control, this branch's own 459ff81088, exits 1. Four anchors reuse the landed stages' (f586f1a89, 4af758d47, 7a5ef0008, 8ad609c69), so each number carries one anchor across the tree; five are new (3ec8646f1, 4f3d2322e, 4810dd628, 9c9e6d08f, 6193e576d).

Numbers. All 9 dropped numbers answer 404 by REST (re-probed 2026-09-29T19:54Z). The numbers kept on changed lines (#6839, a pull request; #6505, #15348, #16013, #4435, #5138, #7867) answer 200. Four slash-joined groups stand in packages/mcp/src, whose later halves the citation grammar does not read (#4435/#5138/#7867 twice, #5138/#5581, #7728/#7823); every half answers 200, so none is dead.

Mechanical guard: no code token moves

H2 holds on both readings: the parser leaf-token diff is empty, and the emitted dist is byte-identical.

Token guard. It compares the TypeScript parser's leaf tokens (TypeScript 6.0.3, JSDoc nodes excluded) of the 9 touched files at base e4e5222b7b and at 459ff81088. Controls mutate the head text in memory only.

  • Real run: 21,192 base tokens, 0 differing (exit 0).
  • Comment-insertion control: 0 differing (exit 0).
  • Code-insertion control: all 9 files differ at token 0 (exit 1).
  • String control (the first character of the 'vitest' import specifier in plugin-execution-context.test.ts flipped): exactly 1 differing StringLiteral, at token 15 of that file (exit 1).

Emitted dist. pnpm --filter @objectstack/mcp build at the head, then at base (the base tree of packages/mcp/src restored in place under a trap-armed restore; an on-disk probe read #13318 1 and commit 3ec8646f1 0 before that build; afterwards every touched blob equals its HEAD blob and git diff HEAD is empty), with the same dependency builds:

  • all six files (index.cjs, index.cjs.map, index.d.cts, index.d.ts, index.js, index.js.map) are byte-identical by sha256. The built files do carry docblocks (14 in index.js, 78 in index.d.ts); none of the rewritten ones is on an emitted declaration.
  • Code-mutation control (scripts/ablation-replace.mjs, anchor: the sync leg's typed ctx.getService call on 'tenancy' in plugin.ts, hit 1 to 0, its argument renamed to a marker; blob restored to HEAD 0a1aaa7955, git diff HEAD empty): scripts/ablation-dist-preflight.mjs found the marker in index.cjs and index.js, and index.cjs, index.js and both .map files differ from the head build. dist was then rebuilt, its six sha256 values equal the first head build, and the preflight in --absent mode reads the marker absent from all 6 files with a clean tree.

A raw scan of the 9 changed files for control bytes finds none (a positive probe on a scratch file matched).

Changeset

None, and skip-changeset. @objectstack/mcp's files[] is dist, README.md and CHANGELOG.md, and the build above emits byte-identical dist at base and head, so this diff publishes nothing from any released package. Stage 5 (PR #20689) measured the same and shipped the same; stage 6 (PR #20703) measured the opposite and carried a patch.

Gates (head 7a0f15de62)

This host has no flock, so os-verify-lock.sh ran in its declared unlocked mode. Its disclosure, verbatim, from each run at this head and from the four dist builds (at 459ff81088, packages/mcp/src byte-identical to this head):

os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 25s · declare it in the PR body · pnpm --workspace-concurrency=2 --filter '@objectstack/mcp...' build
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 116s (1m56s) · declare it in the PR body · pnpm turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=2
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 10s · declare it in the PR body · pnpm --filter @objectstack/mcp exec vitest run --maxWorkers=2
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 4s · declare it in the PR body · pnpm --filter @objectstack/mcp typecheck
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 3s · declare it in the PR body · pnpm --filter @objectstack/mcp build
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 3s · declare it in the PR body · pnpm --filter @objectstack/mcp build
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 3s · declare it in the PR body · pnpm --filter @objectstack/mcp build
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 2s · declare it in the PR body · pnpm --filter @objectstack/mcp build
  • Build: @objectstack/mcp with its closure (9 of 81 workspace projects), then the whole workspace, turbo run build --filter='./packages/*' --filter='./packages/*/*', 71 of 71 tasks, after the merge. The tree was clean after both.
  • Tests: vitest run: 32 files, 344 tests passed (every *.test.ts under src/), at the head and before the merge.
  • Typecheck: pnpm --filter @objectstack/mcp typecheck exits 0. tsc --listFiles: tsconfig.json compiles the 11 non-test src files, tsconfig.test.json all 43 including the 32 test files. check:test-typecheck: 6 files, 53 errors, 8 pinned signatures, held.
  • Lint: the repo-wide pnpm lint (eslint . --no-inline-config) exits 0 at this head (2026-09-29T20:18:54Z to 20:19:23Z), and at 459ff81088 before the merge.
  • Citation judging: after merging origin/main (9b384f63ae), node scripts/check-issue-citations.mjs --base 9b384f63ae judges 5 citations on the changed lines of 3 files (the kept numbers #15348, #16013, #4435, #6505, and #6839 as a pull request) and exits 0: every one resolves. Against origin/main after it moved to 5757463712, the same 5 citations, exit 0.
  • Derived gates: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derived 53 families. All 53 exit 0, and --ran with the exit-coded record reads "53 derived, 53 run, 0 NOT-MEASURED, 0 UNRUN" (a derived zero). Among them: check:issue-citations, check:doc-authoring (808 pinned sites, no growth), check:nul-bytes (9,331 files, no raw control bytes), check:published-files, check:type-check-debt.
  • Artifact rosters: 36 of the 39 non-self-test roster rows exit 0, including the three the derivation marks as keeping their roster under one of this diff's paths (check:authz-resolver, check:error-code-casing, check:filter-alias-parity). The other three need a pull request's context; they are run against this PR once it exists and reported on the card. The 18 self-test-only rows grade their checkers' fixtures and cannot judge this diff.

Hypotheses (measured first)

  • H0 holds. At base e4e5222b7b the filtered census answers 10 sites on 10 lines, 7 numbers, in 3 files, as on the seat's 0be898499f. The whole-repo count is 1,222.
  • H1 holds. After the rewrite, the filtered census answers 0 for packages/mcp. No site was left for an open PR (the file lists of all 8 open PRs were read at 20:08:05Z: only the Version Packages PR chore: version packages #20639 touches packages/mcp, in CHANGELOG.md and package.json) or for an unfound anchor.
  • H2 holds, on both readings. The comment-stripped (parser-token) diff of all 9 touched files is empty with its controls firing, and the emitted dist is byte-identical at base and head with a code control that changes it.

Acceptance notes

  • Test titles, the form-D stage. 4 dead numbers remain in test string literals in packages/mcp/src (describe titles, no assertion text): #17568 twice in mcp-record-id-key-mistake-refusal.test.ts (:151, :315), #8422 in stdio-data-bridge.not-found.test.ts:99, #17114 in stdio-tenancy-posture-api-key-matrix.test.ts:592. They stay on the card for its form-D stage; no string moved here.
  • Outside src/**, a later stage of the card: packages/mcp/vitest.config.ts:18 cites #8651 (404). packages/mcp/test-typecheck-debt.json:2 cites #13470 (404) inside its _comment field, which the file itself says is generated by scripts/check-test-typecheck.mts, so a fix there is at that producer, in the scripts/** lane, not a hand edit. The other citations in packages/mcp outside src/** (CHANGELOG.md excluded) answer 200.
  • Card-word residue, cited nowhere. A few docblocks still say "this card" or "the card" a paragraph away from the rewritten line (for example stdio-data-bridge.not-found.test.ts:19, mcp-record-id-key-mistake-refusal.test.ts:19, stdio-tenancy-posture-api-key-matrix.test.ts:580, :584). They cite no number, so they were left, as the landed stages left theirs; only the one same-sentence companion (plugin-execution-context.test.ts:7) was changed.
  • The moving origin/main. The branch merged origin/main once (7a0f15de62, merging 9b384f63ae: service-storage, platform-objects and plugin-audit, nothing in packages/mcp). A later fetch advanced the shared ref to 5757463712, one commit in platform-objects translations. There was no second merge; CI judges the merge ref.

Deviations

  • One companion line (plugin-execution-context.test.ts:7) beyond the 17 sites, the second half of the :6 sentence.
  • Commit trailers are AGENTS.md's model-free pair (Claude-Session plus Co-authored-by: Claude), and the pre-push trailer check passed on every push. The harness's attribution reminder asked for a model-named trailer and a different PR footer, and AGENTS.md overrides it. The merge commit carries git's default message.

Generated by Claude Code

hotlong and others added 2 commits September 30, 2026 03:58
…o the commits that decided them

Stage 7 of the domain:cli lane's dead-citation sweep, in ruling C+D's
form C: every comment site in packages/mcp/src that cited a tracker
number answering 404 now cites the commit in this repository's history
that decided what the line describes.

Comments only: 18 lines out, 18 in, every file keeps its line count.

Claude-Session: https://claude.ai/code/session_local_1d2a197c-c20e-4e90-9be8-413d4d432289
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 29, 2026
@github-actions github-actions Bot added the tests label Sep 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 2 changed file(s) yielded no anchor (packages/mcp/src/mcp-server-runtime.ts, packages/mcp/src/plugin.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/mcp/src/mcp-server-runtime.ts, packages/mcp/src/plugin.ts) — pages documenting those are invisible to this run
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 12 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 57574637129bebb4a6868c465be7e1b80eed1954 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 6bd09d745fe859e9995e40db8aba65bb850e97b6 — the merge of head 7a0f15de6274d74343e27c4b6a70b89c0b35a1cd into base 57574637129bebb4a6868c465be7e1b80eed1954, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 6bd09d745fe859e9995e40db8aba65bb850e97b6 && git checkout 6bd09d745fe859e9995e40db8aba65bb850e97b6
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 57574637129bebb4a6868c465be7e1b80eed1954 7a0f15de6274d74343e27c4b6a70b89c0b35a1cd && git checkout -B drift-repro 57574637129bebb4a6868c465be7e1b80eed1954 && git merge --no-ff 7a0f15de6274d74343e27c4b6a70b89c0b35a1cd

node scripts/docs-audit/affected-docs.mjs --json 57574637129bebb4a6868c465be7e1b80eed1954

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 29, 2026 20:44
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit dfc8547 Sep 29, 2026
39 of 40 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20594-mcp-dead-citations branch September 29, 2026 21:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant