Skip to content

fix(mcp): stdio bridge throws the shared RECORD_NOT_FOUND envelope - #8507

Merged
os-zhuang merged 3 commits into
mainfrom
claude/issue-8422-stdio-shared-not-found-envelope
Aug 14, 2026
Merged

os-zhuang merged 3 commits into
mainfrom
claude/issue-8422-stdio-shared-not-found-envelope

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #8422

What changed

packages/mcp/src/stdio-data-bridge.ts minted its own local recordNotFound(object, id) — a bare Error with neither code nor status — thrown from the update() and remove() by-id write seams on a missing id. The HTTP bridge's callData path throws the repo's ONE not-found envelope, recordNotFoundError (code: 'RECORD_NOT_FOUND', status: 404, packages/core/src/utils/record-not-found.ts, #4435/#5138/#7867), for the identical miss. Same operation, two transports, two envelopes.

The local mint is deleted; both seams now throw recordNotFoundError(object, id).

Line numbers were stale (#8439 / #8266 touched this file since the card was filed) — located both call sites by symbol (recordNotFound(object, id) calls inside update()/remove()), not by the card's cited :337/:347.

Import path: import { recordNotFoundError } from '@objectstack/core', not @objectstack/metadata-protocol's re-export. @objectstack/mcp's package.json already declares a direct @objectstack/core dependency, and packages/mcp/src/plugin.ts already imports named exports from it — @objectstack/core is also the lower of the two packages that carry the factory (packages/objectql/src/engine.ts's own #7867 comment explains why: @objectstack/objectql cannot import @objectstack/metadata-protocol at all, ADR-0076 D2). No new dependency needed either way.

Preserved, not touched: the local factory's comment explaining why it throws rather than returns (registerObjectTools turns a throw into a tool error) — that reasoning is correct and now sits at the update() call site (the first of the two seams), with remove() pointing back at it. Only which error object is thrown changed.

Adjacent deliverable — folded in, not filed as a follow-up

scripts/check-engine-double-contract.mjs's consumer-seam invariant (#8194) reported the two stdio seams as refusal: 'local' rather than reddening, because the gate's own header names this as a deliberate, temporary state pending this exact card, with a noted SHARED_ONLY-shaped one-line tightening. Since the seam list was already both-directions complete and the tightening really is mechanical, I folded it into this PR rather than filing a follow-up:

  • File surface: scripts/check-engine-double-contract.mjs — the seam filter changed from seams.filter((x) => !x.refusal) (only "no refusal at all" failed) to seams.filter((x) => x.refusal !== 'shared') (a local mint now fails too), plus an updated header comment (## WHICH not-found envelope (#8194, tightened to SHARED_ONLY by #8422)) and an adjusted REFUSES message that distinguishes "refuses through a locally minted error" from "does not refuse anywhere before it".
  • Self-test (--self-test) still green — none of its synthetic fixtures assert the pre-tightening filter behavior, only the refusal classification (shared/local/null), which is unchanged.
  • Real-tree run: all four consumer seams now report [shared] (was 2 [shared] + 2 [local]); gate is green.

Reverse verification

Prediction (recorded before running): reverting only stdio-data-bridge.ts to its pre-fix state, while keeping the tightened gate and the new test file, goes red in both places — the new unit tests (asserting code/status) fail, and check-engine-double-contract.mjs reddens because the two stdio seams revert to refusal: 'local', which the tightened SHARED_ONLY filter now rejects. Ordinary direction (more red), no inversion expected.

Measured, via git checkout HEAD~1 -- packages/mcp/src/stdio-data-bridge.ts (fix already committed, so this is a real restore point) against the tightened gate + new tests, then restored via git checkout claude/issue-8422-stdio-shared-not-found-envelope -- packages/mcp/src/stdio-data-bridge.ts:

  • Tests: 3/3 new tests failed — expected undefined to be 'RECORD_NOT_FOUND'.
  • Gate: 2 REFUSES errors — stdio-data-bridge.ts:351 and :361, both "refuses through a locally minted error rather than the shared envelope".

Matches the prediction. Restored cleanly (git diff HEAD -- packages/mcp/src/stdio-data-bridge.ts empty); both re-verified green afterward.

Tests

  • New: packages/mcp/src/stdio-data-bridge.not-found.test.ts — covers both by-id write seams (update() and remove()) against a missing id, asserting err.code === 'RECORD_NOT_FOUND' and err.status === 404 specifically (not just that something threw), plus a same-shape-on-both-seams check.
  • pnpm --filter @objectstack/mcp test → 16 test files, 173 tests passed.
  • pnpm --filter @objectstack/mcp typecheck → clean.
  • pnpm --filter @objectstack/mcp build → clean (also built the dependency closure first: pnpm --filter '@objectstack/mcp^...' build).
  • node scripts/check-engine-double-contract.mjs --self-test && node scripts/check-engine-double-contract.mjs → self-test OK, real run OK (197 pinned, 133 DEBT, 2 exempt; all 4 consumer seams [shared]).
  • node scripts/check-error-code-casing.mjs → OK.
  • node scripts/check-nul-bytes.mjs → OK.
  • node scripts/check-cross-package-test-inputs.mjs → OK (new test file's package-scoped read is already covered).
  • pnpm run check:query-options-erasure → OK (test surface unaffected — the new file has no query-options sites).
  • pnpm run check:type-check-coverage → OK, ledger unchanged (13 packages / 436 frozen errors in DEBT, unchanged; new test file typechecks cleanly, no debt raised).
  • pnpm exec eslint on the three changed files → clean.
  • node scripts/pm/dispatch-gates.mjs <changed paths> → surfaced check:cross-package-test-inputs, check:engine-double-contract (both already run above) and, as convention-triggered by adding a test file, check:query-options-erasure + check:type-check-coverage (both run above, both green — no addition beyond the prompt's named families was otherwise needed).

Changeset

.changeset/mcp-stdio-record-not-found-envelope.md — @objectstack/mcp, patch (behavior fix, no exported symbol or authorable metadata moves).

Not in scope

Every other divergence between the two MCP transports noted in the file's own docblock (callData's protocol-service preference, ingress readonly strip, existence probes, expand/select) — deliberate, filed, and untouched here. Also not addressed: #8328 (mcp-server-runtime.ts, needs-user-decision) — out of this card's file surface.

Refs #8194, #8083, #8266, #7867, #5138, #4435.


Generated by Claude Code

claude added 2 commits August 13, 2026 16:18
The stdio MCP bridge's update()/remove() by-id write seams minted their own
bare Error on a missing id. The HTTP bridge's callData path already throws
recordNotFoundError (code RECORD_NOT_FOUND, status 404) for the identical
miss, so the two transports answered the same operation with two different
envelopes.

Also tightens check-engine-double-contract.mjs's consumer-seam invariant
from "refuses at all" to SHARED_ONLY, now that all four seams reach the
shared envelope.
@vercel

vercel Bot commented Aug 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 13, 2026 7:40pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/mcp.

11 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/actions-as-tools.mdx (via @objectstack/mcp)
  • content/docs/ai/agents.mdx (via @objectstack/mcp)
  • content/docs/ai/connect-mcp.mdx (via @objectstack/mcp)
  • content/docs/ai/index.mdx (via @objectstack/mcp)
  • content/docs/ai/natural-language-queries.mdx (via @objectstack/mcp)
  • content/docs/api/index.mdx (via @objectstack/mcp)
  • content/docs/deployment/environment-variables.mdx (via @objectstack/mcp)
  • content/docs/permissions/authorization.mdx (via @objectstack/mcp)
  • content/docs/permissions/system-context.mdx (via packages/mcp)
  • content/docs/plugins/packages.mdx (via @objectstack/mcp)
  • content/docs/protocol/knowledge.mdx (via @objectstack/mcp)

⛔ 1 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx (via @objectstack/mcp)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Aug 13, 2026
The third test's `.catch((e) => e)` idiom inferred the settled value as
`unknown` (TResult from an `any`-typed catch parameter widens to `unknown`
here), which is a second, unrelated way to fail tsc from the same file that
already had this idiom's cousin (`await res.json()`) in the package's
53-error TEST_DEBT baseline. Replaced it with the same explicit-cast helper
the other two tests already used, extracted once as `catchError`.

pnpm check:type-check-debt (the real ratchet command, not
check:type-check-coverage) on a full built closure: OK, 33 ledger entries
re-measured, none above their recorded number — @objectstack/mcp back to
its recorded 53.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P7vaLs7bhBPi9m3JyzkhDj

Copy link
Copy Markdown
Contributor Author

CI follow-up on the two red checks flagged.

TypeScript Type Check (check:type-check-debt) — was real, now fixed. My new stdio-data-bridge.not-found.test.ts added exactly 5 raw tsc errors to @objectstack/mcp's TEST_DEBT baseline (recorded 53, measured 58). All 5 were TS2571 ("object is of type 'unknown'") on updateErr/removeErr in the third test's .catch((e) => e) idiom — TypeScript infers the settled TResult from an any-typed catch parameter as unknown here, a second, unrelated instance of the same category of idiom already sitting in this package's 53-error baseline (await res.json()). Fixed at the source: replaced the untyped .catch((e) => e) with the same explicit-cast pattern the other two tests already used (.then(() => null, (e: unknown) => e) cast to Error & { code?: string; status?: number }), extracted once as a shared catchError helper. No ledger entry touched.

Verified with the actual command, not the one I ran the first time: pnpm check:type-check-debt (check-type-check-coverage.mjs --self-test && --re-measure) on a full pnpm build closure → OK — 33 ledger entr(ies) re-measured ... none above its recorded number, @objectstack/mcp back to exactly 53. Re-ran pnpm --filter @objectstack/mcp test (173/173) and check-engine-double-contract.mjs (self-test + real run, all 4 seams [shared]) — both still green.

ESLint — confirmed a flake, not mine. Polled the new SHA's check-runs after push: ESLint completed success at 19:45Z (TypeScript Type Check followed at 19:48Z), no check-regen-pending.mjs git-plumbing error this time. One occurrence, as predicted — leaving check-regen-pending.mjs untouched.

Pushed as f051980d0. Full check-run sweep on that SHA: every completed job is success (ESLint, TypeScript Type Check, Test Core ×3 + aggregate, Build Core, Check Changeset, ADR maintainer approval, Temporal Conformance, Dogfood Verify CLI, Dogfood Regression Gate 1/3 + 3/3, the two duplicate/scope guard checks, etc.); Dogfood Regression Gate (2/3) was still in_progress at last check, unrelated to either flagged job. Still draft, as instructed.


Generated by Claude Code


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 14, 2026 05:35
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 14, 2026
Merged via the queue into main with commit 4810dd6 Aug 14, 2026
27 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-8422-stdio-shared-not-found-envelope branch August 14, 2026 05:53
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…o the commits that decided them (objectstack-ai#20713)

Part of objectstack-ai#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
objectstack-ai#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 objectstack-ai#20533 is the method, and stages 1 to 6 of this card (PR
objectstack-ai#20624, PR objectstack-ai#20632, PR objectstack-ai#20656, PR objectstack-ai#20673, PR objectstack-ai#20689, PR objectstack-ai#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 objectstack-ai#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 objectstack-ai#20708, 18,535 numbers | 1,222 | **10**
| 10 | 7 | 3 |
| after | `459ff81088`, run 19:58:39Z to 20:02:47Z | enumerated, 186
pages, frontier objectstack-ai#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 |
|---|---|---|
| `objectstack-ai#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. |
| `objectstack-ai#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 objectstack-ai#6948, which landed it,
names objectstack-ai#6724. |
| `objectstack-ai#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 objectstack-ai#6839 for objectstack-ai#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. |
| `objectstack-ai#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)`. `objectstack-ai#6723` was the pull request that landed as this
commit (its subject carries the number); `objectstack-ai#6505`, the issue beside it on
`:637`, answers 200 and stays. The spec lane gave the number this
anchor. |
| `objectstack-ai#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 objectstack-ai#17683 names objectstack-ai#17114, and stage 1 gave the
number this anchor. |
| `objectstack-ai#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. |
| `objectstack-ai#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
objectstack-ai#8507 names objectstack-ai#8422. |
| `objectstack-ai#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 objectstack-ai#17650 names
objectstack-ai#17568. |
| `objectstack-ai#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 objectstack-ai#13888 names objectstack-ai#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 (`objectstack-ai#6839`, a pull
request; `objectstack-ai#6505`, `objectstack-ai#15348`, `objectstack-ai#16013`, `objectstack-ai#4435`, `objectstack-ai#5138`, `objectstack-ai#7867`) answer
200. Four slash-joined groups stand in `packages/mcp/src`, whose later
halves the citation grammar does not read (`objectstack-ai#4435/objectstack-ai#5138/objectstack-ai#7867` twice,
`objectstack-ai#5138/objectstack-ai#5581`, `objectstack-ai#7728/objectstack-ai#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 `objectstack-ai#13318` 1 and `commit
3ec8646` 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 objectstack-ai#20689) measured the same and
shipped the same; stage 6 (PR objectstack-ai#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):

```text
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 9b384f6` judges 5
citations on the changed lines of 3 files (the kept numbers `objectstack-ai#15348`,
`objectstack-ai#16013`, `objectstack-ai#4435`, `objectstack-ai#6505`, and `objectstack-ai#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 objectstack-ai#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): `objectstack-ai#17568` twice in `mcp-record-id-key-mistake-refusal.test.ts`
(`:151`, `:315`), `objectstack-ai#8422` in `stdio-data-bridge.not-found.test.ts:99`,
`objectstack-ai#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 `objectstack-ai#8651` (404).
`packages/mcp/test-typecheck-debt.json:2` cites `objectstack-ai#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](https://claude.ai/code/session_local_1d2a197c-c20e-4e90-9be8-413d4d432289)_

Co-authored-by: Jack Zhuang <50353452+hotlong@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants