feat(client, rest): bind both getHistory exits to HistoryMetaItemResponse; ledger row names the schema - #16694
Conversation
…onse; ledger row names the schema Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
The `note` field ships to authors and operators through generated surfaces, where an issue id resolves to nothing. The anchors move to an adjacent source comment, which only a reader who can resolve them sees. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
…story-door-schema-rebind
📓 Docs Drift CheckThis PR changes 2 package(s): 9 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 22 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 3ac8bd89f96104a667be73cc7c084bcfb60c8b90 && git checkout 3ac8bd89f96104a667be73cc7c084bcfb60c8b90
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 001a83b0486391847d45f6866896c53ad8714569 cd9f93413eb577817fdfa9d0ad8cb7867dcd079b && git checkout -B drift-repro 001a83b0486391847d45f6866896c53ad8714569 && git merge --no-ff cd9f93413eb577817fdfa9d0ad8cb7867dcd079b
node scripts/docs-audit/affected-docs.mjs --json 001a83b0486391847d45f6866896c53ad8714569
|
|
契约复核 PASS —
|
Docs-drift owed item: all 10 rows answered — nothing falsified, nothing pushedVerdict: Case A. No page in the corpus is falsified by this diff. No commit, no push, no docs edit. The branch head stays Tree auditedThe bot computed on Auditing in the Why the bot could not answer itTwo distinct reasons, one per half of its list:
SearchedCorpus: Positive control (live, run before the negatives): One hit, on the exact page the bot flagged. The instrument reaches the corpus and would have reported the term had it been elsewhere — so the zeroes below are the corpus's zeroes, not the instrument's. Second control: Terms swept (each run corpus-wide,
The emitter-side hand read the bot said it structurally cannot doThe bot's own limitation — "a page that states a rule by its inputs shares no identifier with the emitter that implements the rule" — is real, and the sweep did surface a hand-written page outside its 9:
Not falsified — it is corroborated. Three reasons: (a) the verb union it spells out matches No edit made: nothing here is this diff's own consequence, and touching it would be widening. The 9 rows1. Sentence read (line 78, inside the
This is not the method this diff touches. It is the kernel service contract, and the page says so in its own Callout: Source: and Decisively, the two
Different names, different members, different files, different layers. The page names no field of the history door's body — a corpus-wide scan of it for 2. Sentence read (line 253, under A cross-link naming the route's path only. No body, no field, no 3–8. The six
9. Sentence read (lines 170–172, under
"Both routes" are Release-owned page audit (read-only — not edited, and not proposed for edit)
The anchor that put it on the list is line 190:
Two checks, both pass. First, the sentence's pointer to One negative worth recordingThe generated (not hand-written) That is the widened GatesNone run, and none owed: Case A means no file changed, so there is no tree to gate and no empty commit was made. Head is unchanged at Generated by Claude Code |
…ot a form The edit-side sentence predicted one shape (session-URL survives, the platform appends a bare block) that was measured on a raw REST PATCH and is false on the MCP wrapper: an edit sent with no footer reads back with none (n=4). The create-side sentence is folded into the same clause because the readings on create disagree by channel too, so a rule that names only the edit action would still invite the generalisation that produced a duplicated footer on #16694. The sentence now states the two axes the ledger states — CHANNEL and ACTION — keeps the imperative half, and points at .claude/skills/pm-dispatch/references/platform-readings.md, which is where the per-cell readings belong. The os-dev.md twin is corrected in the same register, in place. Both files keep their line counts (AGENTS.md 1067/1068, os-dev.md 403/403); no reading is restated in either file. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P58euzUXCVJNwmhuPC9DXY
…ot a form (objectstack-ai#16771) The edit-side sentence predicted one shape (session-URL survives, the platform appends a bare block) that was measured on a raw REST PATCH and is false on the MCP wrapper: an edit sent with no footer reads back with none (n=4). The create-side sentence is folded into the same clause because the readings on create disagree by channel too, so a rule that names only the edit action would still invite the generalisation that produced a duplicated footer on objectstack-ai#16694. The sentence now states the two axes the ledger states — CHANNEL and ACTION — keeps the imperative half, and points at .claude/skills/pm-dispatch/references/platform-readings.md, which is where the per-cell readings belong. The os-dev.md twin is corrected in the same register, in place. Both files keep their line counts (AGENTS.md 1067/1068, os-dev.md 403/403); no reading is restated in either file. Claude-Session: https://claude.ai/code/session_01P58euzUXCVJNwmhuPC9DXY Co-authored-by: Claude <noreply@anthropic.com>
…nd a row says whether it came from prose (objectstack-ai#18515) Part of objectstack-ai#16696. The docs-drift bot's contract is that a row is *reportable*: "Each row says which anchor put it there, **so a wrong row is reportable rather than merely annoying**." objectstack-ai#16696 read all nine hand-written rows on PR objectstack-ai#16694 by hand and reported three shapes. Triage (comment `5578416342`) ruled a **different disposition for each**, and this PR keeps them apart. --- ## Finding 1 — the diagnosis first: this is MATCH WIDENING, not mis-attribution Triage's hard precondition: 「⛔ 修之前先定位是"匹配放宽"还是"归因记错"——这两者的修法相反」. **It is the match widening.** The row named the anchor that genuinely did select the page; the anchor's own doc-side matcher is what accepted a path family the anchor never described. The evidence that decides it, run through the tool's own `routePatternFor` against the real page: ``` ANCHOR TAIL : /environments/:environmentId PATTERN : \/environments\/(?::[A-Za-z_$][\w$]*|\{[A-Za-z_$][\w$]*\}|[A-Za-z0-9_%-]+) …then a NEGATIVE LOOKAHEAD on the class [\w-]. Spelled out, not written: this body's sanitizer eats the bang out of a lookahead opener — measured on the first write of this very PR, where the line came back one byte short. PAGE : content/docs/protocol/kernel/metadata-service.mdx regex .test : true MATCH :204 matched="/environments/:id" line="artifact route (`/pub/v1/environments/:id/artifact[?commit=ID]`) serves" MATCH :213 matched="/environments/env_42" line="path: 'https://cloud.example.com/pub/v1/environments/env_42/artifact?commit=cmt_1a2b'," ``` Reading taken 2026-09-16T17:13Z, tree `8cf527f8e`. Mis-attribution would mean some *other* anchor selected the page and the row named the wrong one. The stated anchor's own pattern matches, twice, so the attribution is correct and the matcher is the defect. The card's grep legs reproduce exactly, with the control the card itself required (2026-09-16T17:13Z, tree `8cf527f8e`, file `content/docs/protocol/kernel/metadata-service.mdx`): | leg | value | |:--|:--| | POSITIVE CONTROL `metadata` | **19** — the instrument fires | | SUBJECT `environments/:environmentId` | **0** — a real zero | | NEAR MISS `environments/:id` | **1**, at `:204` | | DARK CONTROL | **0** | ### The fix, and why it is two arms The two hits fail for two different reasons, and the card names both — "a different path family **and** a different parameter name". 1. **A parameter written as a parameter must name the same parameter** (either spelling: `:environmentId` and `{environmentId}` are one parameter, `:id` is another). Kills `:204`. 2. **A concrete example value filling the tail's LAST segment must end the documented path.** Every earlier parameter is still bounded by the segments to its right; the last one has nothing behind it, so a value there degrades the pattern to a prefix test on `/environments/`. Kills `:213`. ### ⛔ What was measured and rejected Requiring **every** match to sit at the end of the documented path — the way the same tail is matched against a ledger row, `route.endsWith(tail)` — kills both bad hits and reads **−36.3%**. It also deletes `api/environment-routing.mdx`, the most on-target page of that run, whose every occurrence is `/api/v1/environments/:environmentId/...` with a segment after it, plus `publish-and-preview.mdx`, `single-project-mode.mdx` and `http-protocol.mdx`. A route *prefix* written with the route's own parameter named IS a page documenting that route. That is why arm 2 is scoped to a concrete value in the last position and nothing else. ### Corpus measurement behind the narrowing Denominator stated: the **228** distinct route tails declared in this repo's **28** route-ledger files, against all **195** hand-written docs (`scripts/docs-audit/handwritten-docs.json` — the same denominator the tool's own recall figures use). Taken on tree `fef76a4aa`, 2026-09-16T17:45Z. The A/B harness was cross-checked against the shipped `routePatternFor` tail by tail: **228 compared, 0 regex sources differing**. | variant | tail×page rows | delta | tails matching NO page | |:--|--:|--:|--:| | before | 571 | — | 119 | | arm 1 only | 566 | −0.9% | 119 | | **shipped (arm 1 + arm 2)** | **524** | **−8.2%** | **119** | | rejected end-of-path variant | 364 | −36.3% | 120 | **Zero tails go from matching some page to matching none.** Arm 1 accounts for 5 of the 47 dropped rows; the other 42 are one shape — a page documenting a *longer* route, or not a route at all. `/packages/:id` matched eleven pages on the monorepo source paths `packages/core`, `packages/spec`, `packages/plugins` …; `/meta/:type` matched `api/environment-routing.mdx` on the prose *"data/meta/AI/automation"*. --- ## Finding 2 — the reading first, then the argument⚠️ Neither triage nor the PM had taken this reading. It is the card's own command, run before anything was built on it. Reading taken 2026-09-16T17:14Z against PR objectstack-ai#16694's diff `001a83b048...cd9f934`: ``` $ git diff 001a83b...cd9f934 -- packages/client/src/index.ts | grep -n 'environments/:environmentId' 73:+ * `registerForBase` replay against `/environments/:environmentId` — so ``` **Exactly one line, added, and it is English prose inside a JSDoc block:** ``` + /** + * The durable change-log for a metadata item, scoped to this + * environment. Reaches the SAME handler as the unscoped twin — one + * `registerForBase` replay against `/environments/:environmentId` — so + * the body is byte-identical and the declaration must be too. ``` Controls for that reading: the same diff for that file is **102 lines** with **41** `+`-prefixed lines, so the instrument reads a non-empty diff; a dark control (`zzz_no_such_token_zzz`) over the same diff returns **0**. Across the whole diff the anchor occurs **3** times — once in `.changeset/history-door-schema-rebind.md`, once in `packages/client/src/index.ts`, once in `packages/client/src/return-type-precision.test.ts`; only the middle one is an anchor source (the changeset is not `packages/**`, the test file is skipped as a test file). ⇒ the card's claim holds. **Count correction, stated rather than repeated:** the card and PR objectstack-ai#16694's own audit comment both say *six* rows carried this anchor. Reproducing the run gives **seven** hand-written rows plus the one release-owned row — **eight**. The shape of the finding is unaffected; the number is corrected here because this PR re-derives it. ### The option taken: MARK the row, ⛔ do not exclude comments Triage: 「要么把注释排除出锚源,要么在行里标注锚来自注释……⛔ 不要两个都做成硬排除:JSDoc 里新增一条真实路由的文档,有时**正是**该页需要更新的信号」. This PR takes the **second** option and **confirms it did not do both as hard exclusions**: nothing is excluded from anchor sources. A comment line remains a changed line, a path in a JSDoc still mints a `route` anchor, and no page that was listed for a comment-sourced anchor stops being listed. The comment mask is read **only to word the provenance clause** — `anchors[].from` is publication, never discrimination, exactly as the objectstack-ai#12824 ruling set it. Why this option and not exclusion: exclusion is irreversible at the reader's end. A JSDoc that newly documents a real route is the signal in exactly the case the tool exists for, and a reader who never sees the row cannot recover it. A marked row costs a glance and keeps the recall. Rows on PR objectstack-ai#16694 before → after: ``` before: /environments/:environmentId (route, a path literal in meta) after : /environments/:environmentId (route, a path literal in a comment in meta) ``` That distinction also turned out to be load-bearing for the *second* route anchor on that PR: `/meta/:type/:name/history` likewise enters only through a `//` comment — the line is `+ // [objectstack-ai#13523] The change-log body of GET /meta/:type/:name/history, the one`. Both route anchors on that run are comment-sourced, and now both say so. --- ## Finding 3 — left alone `IMetadataService.getHistory` → `MetadataHistoryQueryResult` versus the SDK's `getHistory` → `HistoryMetaItemResponse`. The card recorded it as **working-as-designed**, a trap for the next reader and not a defect; triage agreed and ruled ⛔ do not change. **Nothing in this PR touches it.** Its row is byte-identical before and after: ``` content/docs/kernel/contracts/metadata-service.mdx via getHistory (sdk, the bare tail of client method meta.getHistory, bound to GET /api/v1/meta/:type/:name/history) ``` Triage noted the row should say "锚为裸方法名" — it already does (`the bare tail of client method …`, objectstack-ai#12824), so there was nothing to add. --- ## Two-direction acceptance ⛔ Triage's fence: 「一个"把弱命中一律丢掉"的实现会让本卡三条全绿,同时把工具的价值删掉」, and the card's own first ⛔: *"Not that the bot should be quieter."* Both directions are exercised. ### Direction 1 — the wrong row is gone `node scripts/docs-audit/affected-docs.mjs 001a83b --json`, run in a detached worktree at PR objectstack-ai#16694's head `cd9f93413e` (whose `content/docs` tree is the object the bot computed on). **10 rows → 9.** The single drop: ``` DROPPED content/docs/protocol/kernel/metadata-service.mdx ['/environments/:environmentId (route, a path literal in meta)'] ``` Every other row is kept; seven are re-claused with the comment marker; the `getHistory` row is untouched. The run before the change reproduces the bot's published comment on PR objectstack-ai#16694 exactly — the same 9 hand-written rows and the same 1 release-owned row, with identical `via` clauses. ### Direction 2 — a genuinely falsified row is still listed, with a strong-hit anchor PR objectstack-ai#16689 is the sibling sweep the card cites, where the hand read found rows that **were** genuinely falsified and fixed (its own answer comment: "2 falsified and fixed, 1 not falsified"). Same command, base `c8e5ac645f`, head `947426a7b8`: **7 rows → 7 rows, every `via` clause byte-identical.** The page that PR objectstack-ai#16689 recorded as FALSIFIED and then fixed, `content/docs/getting-started/your-first-project.mdx`, is still listed, and its anchors carry no comment marker — a strong hit: ``` KEPT getting-started/your-first-project.mdx ['os create (command, read off packages/cli/src/commands/create.ts)', 'os init (command, read off packages/cli/src/commands/init.ts)'] ``` ### And in the self-test A new battery, `ROUTE-ANCHOR PRECISION, IN BOTH DIRECTIONS (objectstack-ai#16696)`, 20 cases, every narrowing case paired with a KEEP case from the same run — including the positive control that `/data/:object` still matches a page writing `POST /api/v1/data/accounts`, and that a comment-only anchor is **still an anchor** and merely gains a clause. **Reverse verification**, from the committed state, each leg proved on disk by blob hash and restored with `git checkout HEAD -- PATH`: | ablation | self-test exit | what failed | |:--|--:|:--| | revert the whole narrowing (restore the old widened parameter arm) | **1** | 6 assertions, all of them the narrowing cases; ⛔ every KEEP case stayed green | | blind the comment mask (`masked = lines`) | **1** | 4 assertions, all of them the provenance clauses | | restored | **0** | 605 cases pass; `git diff HEAD` empty, blob `6010e3c0` = HEAD blob | --- ## ⛔ No occurrence rate is asserted The card: 「One PR is not a population」. Triage: 「⛔ 修卡的人也不例外」. This PR measures **one** PR's rows on objectstack-ai#16694 and **one** on objectstack-ai#16689, and makes no claim about how often either shape occurs. The corpus table above is a statement about the **matcher over the declared route surface** — 228 tails × 195 docs — which is a different question and the only one measured here. ## Not touched `content/docs/releases/` — untouched. The `releases/implementation-status.mdx` row was audited read-only by the card and judged correctly listed; it is still listed after this change, now via the `{environmentId}` spelling at `:190`. ## Changeset — `skip-changeset`, measured The sole criterion is whether anything **published** moves, read off each package's actual `files[]`, ⛔ not off the path name: - **70** non-private `package.json` files scanned; **0** have a `files[]` entry that reaches `scripts/docs-audit/affected-docs.mjs` or `scripts/docs-audit/README.md`. - **Positive control** that the scan reads real entries, and the exact shape that made this reflex wrong on card objectstack-ai#18169: **147** non-`dist` `files[]` entries exist across those packages, and they are dominated by `README.md` / `CHANGELOG.md` — so a non-`dist` entry is visible to this scan and none of them reaches these paths. - **0** published packages name a `scripts` entry in `files[]` at all. - The root `package.json` is `private: true`. - Symbol leg: `routePatternFor` occurs in exactly one file in the tree — the changed one. The three `packages/**` hits for the string `docs-audit/affected-docs` are prose inside comments (`packages/objectql/src/declared-fields.ts:183`, `packages/spec/scripts/build-schemas.ts:1259`) and a JSON `description` field. ## Gates Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` from this worktree — **33** commands. Every one recorded in the report. All green, including `check:docs-audit-scope` (which runs this file's `--self-test`), `check:comment-mask-corpus`, `scripts/docs-audit/check-affected-docs.mjs` and `scripts/docs-audit/check-drift-comment.mjs`. ## Acceptance notes Noted here rather than filed — none is a reproducible defect, a declared-contract violation, or a metadata-authoring trap: - The card and PR objectstack-ai#16694's audit comment both say *six* rows carried the `/environments/:environmentId` anchor; reproducing the run gives seven hand-written plus one release-owned. An arithmetic slip in a narrative, corrected above, not a defect in code. Carrier: this PR body. - `/packages/:id` matching monorepo source paths such as `packages/core` on eleven pages is the same widening shape as finding 1, and this change removes it as a side effect. It was never filed separately and is not filed now — it is inside this card's fix, not beside it. --- _Generated by [Claude Code](https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk)_ --- _Generated by [Claude Code](https://claude.ai/code)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #13523
Clause-②: no
Clause-② — ruled
yesby the dispatching PM (#6024), on the condition its ownClaim:setThe claim (
5574952927) declared Clause-② conditionally:yesif the hand-written inline shape andz.infer<typeof HistoryMetaItemResponseSchema>differ,noif they are byte-equivalent. The delivered changeset measures them as differing, so the condition resolvesyes:HistoryMetaItemResponseactorstringnullfor every system-initiated write (boot sync, migration, scheduled job), and the schema declares it "never a sentinel string"opstringcreate/update/delete/rename/publish/revert)version,previousName,ref.versionref.orgorg?: string(optional)_unwrapwith no type argument ⇒Promise<unknown>⇒ the published return type narrows. Same shape as #14313, and the maintainer's #12104 ruling (
5472614711) puts this class atClause-②: yesexplicitly. The changeset is graded@objectstack/client: minor/@objectstack/rest: patchand carriesBREAKING (types)in its own body.The two consumers bound
packages/rest/src/rest-route-ledger.ts, theGET /api/v1/meta/:type/:name/historyentry now carriesresponseSchema: 'HistoryMetaItemResponseSchema', joining its already-boundaudit/rollback/diffsiblings. Data only, in a package-internal module: no route, handler or emitted byte moves. This row was the one member of the metadata family left unfilled, because the declaration (MetadataProtocoldeclares nohistoryMetaItemmember, so the REST history door's request literal is compiled against nothing — the last undeclared read door of the audit/history pair #12005, PR feat(spec): declare MetadataProtocol.historyMetaItem and de-cast the REST history door #13521) landed after the row was written.getHistoryexits inpackages/client/src/index.ts— the unscopedObjectStackClient.meta.getHistoryand the scopedScopedEnvironmentClient.meta.getHistory, each now annotatedPromise<HistoryMetaItemResponse>with the unwrap call given the matching type argument.The two
getHistoryoccurrences: measured, not assumedTriage flagged binding only one as "this card's most likely error" and asked for the relationship to be measured. They are two exits of ONE door, not two doors.
Located by symbol (⛔ never by the stale line numbers — the file moved under #15445, #14526 and #16676):
packages/client/src/index.tsholds exactly twogetHistorydefinitions, one onObjectStackClient.metaand one onScopedEnvironmentClient.meta.The producer side proves they are one mount. In
packages/rest/src/rest-server.ts,registerMetadataEndpoints(bp)is called from inside theregisterForBase(bp)closure, andregisterForBaseis invoked twice — once withbasePathand once withscopedBase(${basePath}/environments/:environmentId). Inside it,isScopedis derived from that same string (basePath.includes('/environments/:environmentId')) andregisterPerItemRouteregisters one handler body for the/:type/:name/historypath under both bases; the only difference is whetherenvironmentIdis read fromreq.params. ⇒ one handler, replayed against two prefixes, answering a byte-identical body.Both exits are bound. Their declared states before this change were nevertheless different — the unscoped carried a hand-written inline object type, the scoped carried nothing (
Promise<unknown>) — so binding one and leaving the other would have relocated the divergence rather than removed it.return-type-precision.test.tspinsUnscopedHistory ≡ HistoryMetaItemResponse,ScopedHistory ≡ HistoryMetaItemResponseandUnscopedHistory ≡ ScopedHistory; that third assertion is the one that refuses a half-fix, and the ablation below shows it is red for either half alone.Gates — measured at head
cd9f93413eb577817fdfa9d0ad8cb7867dcd079bEvery reading below is against that commit, working tree clean (
git status --porcelainempty,git diff HEADempty). Each exit code was captured before any pipe; no verdict is read from a;-sequenced batch.origin/mainmoved to001a83b048during this run. It was not re-merged: its one new commit (#16650) touchespackages/drivers/*,packages/services/*,packages/types/*and adds two new test files underpackages/rest/src/— it overlaps none of this PR's five files, so no merge was needed and the head above is the head that was measured.Measured green
dispatch-gates --commandsderived family — 58 commands, all run individuallydispatch-gates --ran ran.listreconciliation✓ 58 derived famil(ies) accounted for — 58 run, 0 NOT-MEASURED, 0 UNRUNpnpm --filter @objectstack/client typechecktsc --noEmitclean;check:test-typecheckOK, 0 files / 0 errors intest-typecheck-debt.jsonpnpm --filter @objectstack/rest typecheck@objectstack/clientunit tier (pnpm --filter @objectstack/client test)pnpm exec eslint . --no-inline-config(whole repo)The
ran.listwas built from the byte-for-byte command strings--commandsprinted (verifieddiff-identical to the harvested list), never from log file names.NOT MEASURED — with each gate's own stated prerequisite
dispatch-gates --ran's0 NOT-MEASUREDis the runner's own accounting of the derived set, not verification of everything CI runs. The honest split:check-changeset-no-majorLEVEL AXIS — exit 0 overall, but the gate itself prints: "LEVEL AXIS: NOT MEASURED — no clause-② declaration was readable for this PR… nopull_requestpayload was available to read a declaration from." Prerequisite: a livepull_requestpayload, which exists only once this PR does. CI measures it on this PR.check-shard-attestation.mjs(×3,${{ matrix.shard }}) andcheck-test-completeness.mjs(×2,$RUNNER_TEMP/*.log). Prerequisite: a CI run; there is no local invocation. Named by the tool as⊘ NOT MEASUREDand deliberately outside the 58.check:init-service-contract,check:live-db-isolation,check:meta-type-normalized,check:optional-error-sink,check:resume-authority-declared,check:runner-env-posture,check:settings-bind-window,check:startup-registry-verdict,check:verify-stand-in,check:wildcard-fallthrough.lint.ymlcarries no paths filter, so CI runs them on every PR; no path derivation can say whether they bite. Their absence from the derived total is not a clearance.silent— a fact about a tracked-file baseline, not about these paths (4 of them keep that roster in a directory one of these paths is in). Not clearance in either direction.pr-labels.mjs, the publish smoke script) — outside--commandsby construction, run by CI.Zero gates hit
exit 3, zero were killed by the OS, zero fell to UNRUN.Ablation — the new pin is proved able to fail
return-type-precision.test.tsis the guard for this whole card, so it was ablated rather than trusted. Nothing was committed: the committed tree was mutated in place by exact-anchor replacement that refuses on anchor count ≠ 1, the mutation was proved on disk by occurrence counts andgit hash-object, and restoration was proved by blob hash and an emptygit diff HEAD— never by an exit code. Atrap … EXIT INT TERMwith absolute paths held the restore.Pristine blob of
packages/client/src/index.ts:7fa9e129ca1aa66c8bb099a243351e9b73389606.tsc --noEmitcheck:test-typecheckviteston the pin fileunwrapResponse(res)9e7640b5ee…Promise<HistoryMetaItemResponse>2→1; oldactor: string;0→1;unwrapResponse<…>1→0src/return-type-precision.test.ts_unwrap(res)12e1014d8f…Promise<HistoryMetaItemResponse>2→1;_unwrap<…>1→0src/return-type-precision.test.ts615a6ac70b…Promise<HistoryMetaItemResponse>2→0; both unwrap type args goneTS6133 'HistoryMetaItemResponse' is declared but its value is never read(a side effect of the full revert, not the pin)src/return-type-precision.test.ts7fa9e129ca…Restoration after every row: blob hash matched pristine and
git diff HEADwas empty. Final state:git status --porcelain0 lines, HEAD stillcd9f93413e,git hash-object packages/client/src/index.ts=7fa9e129ca….Two readings worth stating plainly:
vitestis green in every row, and that is correct, not a hole. The pin is type-level on purpose:expectTypeOfand@ts-expect-errorare erased at runtime, so the value the stubbed transport returns is identical either way. The teeth are incheck:test-typecheck(which compiles the test layer underpackages/client/tsconfig.test.json), and the pin file's own docblock says so. A runtime-only reading of this pin would have reported a false green three times.Serial scan —
packages/client/src/index.tsis clearRe-scanned at claim time as the card demands, ⛔ not taken from any comment. All 17 open PRs enumerated and paged to the last page (
list_pull_requests state=open perPage=100; page 1 returned 17, page 2 returned empty — the paging failure that cost this card two days is exactly what that second call exists to rule out), then each scanned per ref:git fetch --depth=80 origin refs/pull/N/head,git merge-base FETCH_HEAD origin/main,git diff --name-only <merge-base> FETCH_HEAD.Result: every one of the 17 refs fetched and resolved a real merge base within depth; none holds
packages/client/src/index.ts. The only open PR touchingpackages/client/at all is the changeset-release bot (#15334), and onlyCHANGELOG.md/package.json— same-package, which is exempt in this lane; same-file is the hard-serial rule and nothing holds it. The four cards historically queued on this file (#14313, #14314, #15451, #14879) have no open PR: #14879's carrier PR #16676 merged at 19:32Z, and none of the 17 open heads is any of the others.Method validated rather than assumed: the git per-ref file list for the largest open PR (#16628, 37 files) is byte-identical to GitHub's own
get_fileslist for it.Scope
⛔ Out of scope, preserved from the card: the dead
environmentIdwire-spread on the same door belongs to #12005, not this card. Untouched here.No wire byte moves.
HistoryMetaItemResponseSchemais a describe-only transcription of whathistoryMetaItemalready returned, and the SDK's runtime path is unchanged — only what the compiler knows about it.Generated by Claude Code
Generated by Claude Code