Skip to content

test(http-conformance): setFallbackHandler 的四条契约保证跨适配器落锁 —— 参考适配器先补实现 (#6143) - #6851

Merged
os-project-manager merged 5 commits into
mainfrom
claude/issue-6143-fallback-handler-conformance
Aug 9, 2026
Merged

os-project-manager merged 5 commits into
mainfrom
claude/issue-6143-fallback-handler-conformance

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes #6143

摘要

IHttpServer.setFallbackHandler 的契约(packages/spec/src/contracts/http-server.ts,#5040 §1-C)写了四条可测的保证,而被这批语义点名负责跨适配器守卫的 @objectstack/http-conformance 一条也没断言。本 PR 按 issue 给定的、不可反转的顺序补齐:

  1. 先给 conformance 包自己的参考适配器 NodeHttpServer 实现 setFallbackHandler(packages/qa/http-conformance/src/adapter.ts)。node:http 没有现成的 not-found 钩子,所以在路由未命中分支接入:handler 存为字段、仅在 match() 对全部显式路由都未命中之后才被咨询,绝不是一条 ${prefix}/* 通配路由;未命中分支同时把 404/405 + Allow 的标准答复抽成 writeUnmatchedResponse(),fallback 不写响应时原样落回(405 + Allow 仍然优先);fallback 抛错答 500 Fallback handler failed(与主适配器同一措辞),不藏进 404。
  2. 再加共享用例 fallback-seam.conformance.test.ts,把四条保证逐条转写。describe.each 同时驱动 NodeHttpServer 与 HonoHttpServer(真实 socket,listen(0)),9 用例 × 2 适配器 = 18 条。没有任何 Hono-only 条件用例——那证明不了跨适配器一致,正是本卡片要防的那种绿。

观察类缺口,不是现网故障。 今天只有 HonoHttpServer 实现该成员;参考适配器 NodeHttpServer 此前不实现该成员——而该成员在契约上是 optional,所以那是合规状态,不是缺陷。本 PR 关闭的是潜伏缺口:#5111 之后该 seam 是声明式 apis: 端点的唯一进入通路,第二个适配器在这四条上分叉的那天,代价是「该适配器上声明式端点行为不可预期」。趁没有第二个实现者时补,是这份覆盖唯一便宜的时刻。

前提核查(在合并后的 origin/main 上重测)

分支闲置期间 origin/main 前进了 19 个提交,已合并(无冲突),此前一切测量作废、整条链在合并结果上重跑。

前提 实测
conformance 包内四条保证零断言 git grep -iE 'setFallbackHandler|fallback' origin/main -- packages/qa/http-conformance/ 唯一命中是 node-plugin.ts:18 头注释里一个无关的 "fallbacks" 字样 ✅
NodeHttpServer 不实现该成员 git grep setFallbackHandler origin/main -- .../http-conformance/src/adapter.ts 零命中 ✅
HonoHttpServer 实现该成员 .../plugin-hono-server/src/adapter.ts:569 ✅

四条保证 → 用例,以及每条怎么才会红

用例 保证 能失败的方式
G1-a 先装 fallback、再注册路由,路由不被遮蔽 1 通配路由实现:先注册的 /* 按 first-match-wins 抢走 /api/v1/thing → 红。这条是专门构造出来让 catch-all 必红、让真 not-found 钩子必绿的
G1-b 已被路由匹配的请求不进 fallback 1 惰性实现对它是平凡绿,反向核查中如实排除
G1-c 无人认领的路径进 fallback,请求身份完整 1 存而不接 → 红
G2-a req.body 可读:JSON body + query + method + path + headers + rawBody 2 不解析 body,或 params 崩在 undefined → 红
G2-b form body 按 content-type 解析,与路由 handler 同一份解析结果 2 另起一套请求构造、与路由路径漂移 → 红
G3-a 重复安装是替换:只有最后一个跑,且只跑一次 3 串链或叠钩子 → ran 数组与响应体双红
G4-a 不写响应时 404 逐字节保留,且 fallback 确实运行过 4 ran 断言把「压根没被咨询」与「拒答后落回」分开:惰性实现 404 一模一样但 ran=false → 红
G4-b 不写响应时 405 + Allow 保留(#5040 §7-1) 4 catch-all 把 DELETE 也吃掉答 500 → 红;丢 Allow → 红
G5-a 抛错的 fallback 答 500 同一措辞 (失败模式对齐) 藏进 404 或答适配器私有错误体 → 红

反向核查(预测先行,实测在后)

三个方向都只改 NodeHttpServer;预测写于任何反向运行之前(scratchpad 存档,续作沿用未改)。

方向 预测(node 侧) 实测(node 侧) 一致
(a) 实现还原为惰性(setter 存,未命中分支从不咨询) 8 红 / 1 绿 8 红:G1-a、G1-c、G2-a、G2-b、G3-a、G4-a、G4-b、G5-a ✅ 逐条一致
(b) 改为 /* 通配路由实现(契约禁止的那个形状) 5 红:G1-a、G3-a、G4-a、G4-b、G5-a 5 红:同上五条 ✅ 逐条一致
(c) 重复安装改为串链而非替换 1 红:G3-a 1 红:G3-a ✅ 逐条一致
  • 如实排除:G1-b 在方向 (a) 下两侧皆绿——一个从不运行的 fallback 当然"不会为已匹配的路由运行"。它对 (a) 无鉴别力,不计入战果;保留它是因为它对另一类分叉(fallback 抢跑已匹配路由)仍是守卫。
  • Hono 侧在三个方向下 9/9 全绿(反向变体只动 node 适配器),证明红是变体造成的、不是用例本身脆。
  • Hono 未做任何修改,18 条一次全过——即四条保证 Hono 侧无违反,没有需要"弯断言"的发现。

门禁(合并后的树,真实输出)

门禁 结果
pnpm lint exit=0
pnpm --filter @objectstack/http-conformance typecheck(tsc --noEmit) exit=0
@objectstack/http-conformance 全量用例 Test Files 3 passed (3) / Tests 64 passed (64)(其中新增 18)
turbo run build --filter='./packages/*' --filter='./packages/*/*'(lint.yml 的构建闭包) 70 successful, 70 total
lint.yml 的 check:* 全套(42 项) 42/42 exit=0
check:empty-changeset / check:adr-0087-registration exit=0(后者:本 PR 无 declared-breaking changeset)
TEST_DEBT 未上升 —— check:type-check-debt(--re-measure)重测 34 条账目:"none above its recorded number",scripts/check-type-check-coverage.mjs 账本本 PR 零改动,工作树干净

@objectstack/http-conformance 在 TEST_DEBT 账本里(记 4)。新用例文件在解除 **/*.test.ts 排除后贡献 0 个错误——重测报 3(TS2307 x2 + TS2304 x1,两条在 node_modules 的 .d.ts 里、一条是既有的 conformance.integration.test.ts 里 @objectstack/spec/contracts 未解析),比账本记的 4 少 1。那个 -1 不是本 PR 造成的:本 PR 只碰两个文件,adapter.ts 走的是包自己的 tsc --noEmit(绿),新用例文件零错误;该包自身源码在 main 上自 #4935 起未变。全仓共 12 条账目低于天花板(#6376 的既有状况),--lower 是 opt-in、本次未传,所以没有任何账本被改写。

⚠️ 过程记录:第一遍跑 check:type-check-debt 时我自己并发跑了一个 tsc 用同一份 tsconfig.debt-remeasure.json,把门禁的那次测量打成 tsc exited null ... refusing to record 0 → exit=1。那是我制造的并发假红,不是仓库状态;隔离重跑后 exit=0(上表即重跑结果)。

check:type-check-debt / check:i18n-coverage 没有完整构建闭包会拒绝测量,按 lint.yml 的做法先跑上面那条 turbo run build 再跑,不把它们的拒绝读成红。

Changeset 判断(诚实版)

.changeset/http-conformance-fallback-seam.md — @objectstack/http-conformance: minor,且只有这一个包。

  • 为什么带 changeset:AGENTS.md 的「feature work 要带 changeset,纯 bug fix 不用」。这不是纯测试 PR——参考适配器长出了一个此前没有的契约成员,是加法式的 API 面。
  • 为什么是 minor 而不是 patch:只加共享用例的话是 patch;NodeHttpServer 新增 setFallbackHandler 是新能力,minor。
  • 为什么只有这一个包:因为只有它变了。packages/spec 的契约未动(四条保证早已声明,本 PR 是断言它们,不是改它们);HonoHttpServer 也未动(它 9 条全过)。给任何已发布包挂个 bump 都是不实。
  • 为什么不是 skip-changeset:该包 private: true(不发布),但仓库确实给私有包做版本——pnpm changeset status 实测把 @objectstack/http-conformance 列进 "Packages to be bumped at minor",@objectstack/dogfood / @objectstack/downstream-contract 同样在版本流里。所以「本 PR 不声明任何 release」是假的,skip-changeset 会掩掉那条 CHANGELOG——而那条 CHANGELOG 正是未来第三个适配器作者知道「这四条现在是跨适配器强制的」的地方。

边界


🤖 Generated with Claude Code

https://claude.ai/code/session_017uFVNMmTxLpmfQYiuKM1Yx


Generated by Claude Code

@vercel

vercel Bot commented Aug 9, 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 9, 2026 12:35am

Request Review

@github-actions github-actions Bot added the size/m label Aug 9, 2026
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

No hand-written docs reference the 1 changed package(s). ✅

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Aug 9, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 9, 2026 00:35
@os-project-manager
os-project-manager added this pull request to the merge queue Aug 9, 2026
Merged via the queue into main with commit 12298c7 Aug 9, 2026
25 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-6143-fallback-handler-conformance branch August 9, 2026 00:53
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…commits that decided them (objectstack-ai#20723)

Part of objectstack-ai#20594
Clause-②: no

## What changed

This is stage 8 of the `domain:cli` lane of the dead-citation sweep:
`packages/qa`. `packages/qa` is a directory of five private workspace
packages, not one package, so the surface is `packages/qa/*/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. Each
line still says in its own words what that commit decided. PR objectstack-ai#20533 is
the method, and stages 1 to 7 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, PR objectstack-ai#20713) are the precedents.
The card stays open for the lane's remaining packages, so this PR says
`Part of`.

That is **12 sites on 12 lines in 5 files, covering 6 numbers**,
rewritten to **6 distinct commits**:
- the census's **8 sites**:
`downstream-contract/src/additional-domains.fixtures.ts` (2),
`http-conformance/src/adapter.ts` (4) and
`vitest-filter-preflight/src/index.ts` (2), 5 numbers;
- **4 test-file comment sites** in 2 test files under
`http-conformance/src/` (the census leaves `*.test.ts` out; stages 1 to
7 took test comments too).

Only comments changed: **12 lines out, 12 in**, and every touched file
keeps its line count, so no line citation into these files moves. **No
citation number is added.** Over the 12 line pairs, added-minus-removed
numbers is empty, and no PR number stands newly on any line. The two
numbers still on changed lines (`objectstack-ai#17978`, `objectstack-ai#14554`) were already on
them, and both answer 200.

**Two numbers have an ADR beside them, and both ADRs stay.**
- `objectstack-ai#6083`'s line already cited ADR-0122 phase 2. The ADR's own amendment
records phase 2, so the line now reads "ADR-0122 phase 2, commit
53068c1", the same pair spec stage 1 wrote in
`contracts/data-engine.ts`.
- `objectstack-ai#10485`'s line cites ADR-0049, the enforce-or-remove principle it was
retired under. No ADR records the theme retirement itself, so the commit
is the anchor, and the ADR stays beside it, as in the landed stages.

None of the other four numbers appears in `docs/adr/` or
`scripts/adr-anchors/`. The grep reads 0 hits for them. The control,
`objectstack-ai#5551`, reads 1 hit in ADR-0122.

**No changeset, and `skip-changeset`.** None of the three touched
packages publishes anything (see Changeset below).

## Census: `packages/qa`, 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/qa/`. Both runs enumerated the whole board.

| reading | tree | board | whole-repo `allocated-but-absent` |
`packages/qa` sites | lines | numbers | files |
|---|---|---|---|---|---|---|---|
| before | base `679f95ec5c`, run 2026-09-29T21:05:44Z to 21:09:44Z |
enumerated, 186 pages, frontier objectstack-ai#20718, 18,545 numbers | 1,185 | **8** |
8 | 5 | 3 |
| after | `30aae6a3e6`, run 21:18:38Z to 21:22:42Z | enumerated, 186
pages, frontier objectstack-ai#20720, 18,547 numbers | 1,177 | **0** | 0 | 0 | 0 |

The whole-repo drop of 8 is exactly these sites. A site-by-site diff of
the two JSON outputs has 8 findings gone, all under `packages/qa/*/src`,
and none added. The other three tallies are equal in both runs:
`resolves` 32,982, `resolves-as-pull-request` 1,984 and
`cross-repo-unjudged` 995. `packages/qa/*/src` is byte-identical at
`30aae6a3e6` and at the head; the two later commits are merges of
`origin/main` that touch nothing in `packages/qa`.

**Supplementary scan (test files and everything outside `src/`
included).** The gate's exported `extractCitations` and
`classifyCitation` ran over all 221 tracked files under `packages/qa`
(`CHANGELOG.md` excluded), with the board from the gate's own
`probeBoard`. The totals are 1,678 citations and 139 dead before, and
1,666 and 127 after.
- Under `src/`, before: src comments 40 / 8 dead, test comments 42 / 4,
src strings 1 / 0, test strings 9 / 0.
- Under `src/`, after: src comments 32 / 0, test comments 38 / 0,
strings unchanged. Nothing dead is left under any `src/`, strings
included, so there is no form-D residue in this stage's surface.
- Its before list of src comment sites is identical to the census's.
- The 127 left are all outside `src/**` (see Acceptance notes).

## Per-site table

`git blame` at the base ties each line to the commit that wrote it. 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, and that is noted.

| number | sites (base line) | anchor: what it decided |
|---|---|---|
| `objectstack-ai#6083` | `downstream-contract/src/additional-domains.fixtures.ts:11`
| `53068c130`, ADR-0122 phase 2: the bare type name becomes the AUTHOR
state (`z.input`) and the `XInput` synonyms retire. The same commit
moved these frozen fixtures' annotations onto the bare names without
touching a literal, which is what the paragraph says. The line blames to
it, and its subject carries the number. The PR that landed it answers
404, and spec stage 1 gave the number this anchor. |
| `objectstack-ai#10485` |
`downstream-contract/src/additional-domains.fixtures.ts:133` |
`35ad101bc`: retires the `themes` carrier key and `ThemeSchema` whole,
under ADR-0049, and drops `DcTheme` from these fixtures. The line blames
to it, and its subject carries the number. The spec, rest, runtime and
cli stages gave the number this anchor. |
| `objectstack-ai#6143` | `http-conformance/src/adapter.ts:32`, `:189`, `:257`,
`:346`; `http-conformance/src/fallback-seam.conformance.test.ts:4`,
`:27` | `12298c7d6`, which does two things: `NodeHttpServer` implements
the optional `setFallbackHandler` out of its own router, as a field
consulted in the route-miss branch, with the 405 answer extracted for
its second call site; and the cross-adapter suite asserts the contract's
four guarantees on both adapters. All six lines blame to it. PR objectstack-ai#6851,
which landed it, names objectstack-ai#6143 on its first line. |
| `objectstack-ai#6307` |
`http-conformance/src/query-multiplicity.conformance.test.ts:76`, `:296`
| `293476148`: refuses a repeated `?version=` on `GET` / `DELETE
/packages/:id`, and adds `package-routes-query-multiplicity.test.ts`.
Its changeset records the measured read: on `DELETE`, a repeated value
skipped the full-uninstall branch, and the call still reported success.
The lines blame to the later `68feaadd6` and `7cdbcbb30`, which cite
this earlier work by number. PR objectstack-ai#6895, which landed the anchor, names
objectstack-ai#6307 on its first line. The rest stage gave the number this anchor. |
| `objectstack-ai#17853` | `vitest-filter-preflight/src/index.ts:5` | `08f5f0e5a`: a
vitest file filter that selects nothing says so, even when the rest of
the run selects something. This is the first implementation, in
`packages/cli`. The line blames to `c667d8c80`, the shared port for all
eight project-declaring packages; its number is `objectstack-ai#17978`, which answers
200 and stays. PR objectstack-ai#17965, which landed the anchor, names objectstack-ai#17853 on its
first line. |
| `objectstack-ai#13504` | `vitest-filter-preflight/src/index.ts:273` | `44813ba57`:
splits `packages/cli`'s suite into the named `unit` and `integration`
tiers, decided on behaviour, with the partition pin. That is half of the
"tier walk" this sentence names, and its diff heads the new section with
this number. `objectstack-ai#14554`, the derived-population half, answers 200 and
stays. The only commit whose subject carries `objectstack-ai#13504` is `55519d503`,
the comment-only measurement half: PR objectstack-ai#13872 says it lands only that
half. So that commit is not the anchor for this sentence. The PR that
landed `44813ba57` answers 404. |

**Anchor checks.** Every cited sha matches exactly one object (`git
rev-parse --disambiguate`, count 1 for each of the 6). Each is a commit
with one parent, and each is an ancestor of `main` (`merge-base
--is-ancestor` against `cbaf04c1fd`, exit 0 for all 6). The checkout is
not shallow. The control leg `3cc8676e1` (2026-08-08, the parent of the
oldest anchor `53068c130` of 2026-08-08) exits 0, and the negative
control, this branch's own `06b8fbc2d3`, exits 1. Three anchors reuse
the landed stages' (`53068c130`, `35ad101bc`, `293476148`), so each
number carries one anchor across the tree. Three are new (`12298c7d6`,
`08f5f0e5a`, `44813ba57`).

**Numbers.** All 6 dropped numbers answer 404 by REST (probed
2026-09-29T21:16:49Z). The numbers kept on changed lines (`objectstack-ai#17978`,
`objectstack-ai#14554`) answer 200. No slash-joined citation group stands in
`packages/qa/*/src`.

## Mechanical guard: no code token moves

**H2 holds on the parser-token reading.** The emitted-`dist` reading
does not apply, because none of these packages has a build (see
Changeset below).

**Token guard.** It compares the TypeScript parser's leaf tokens
(TypeScript 6.0.3, `getChildren` walked to the leaves, JSDoc nodes
excluded) of the 5 touched files at base `679f95ec5c` and at
`30aae6a3e6`. Controls mutate the head text in memory only.
- Real run: 7,917 base tokens, 0 differing (exit 0).
- Comment-insertion control: 0 differing (exit 0).
- Code-insertion control: all 5 files differ at token 0 (exit 1).
- String control (the first character of the `'vitest'` import specifier
in `fallback-seam.conformance.test.ts` flipped): exactly 1 differing
`StringLiteral`, at token 11 of that file (exit 1).

Every one of the 24 changed lines is a `//` or `*` comment line. A raw
scan of the 5 changed files for control bytes finds none (a positive
probe on a scratch file matched).

## Changeset

**None, and `skip-changeset`.** There is no `@objectstack/qa` package.
The three touched packages are `@objectstack/downstream-contract`,
`@objectstack/http-conformance` and
`@objectstack/vitest-filter-preflight`. Each is `"private": true`, has
no `build` script, no `files[]` and no `dist/`. `.changeset/config.json`
versions private packages but never tags or publishes them. This diff
therefore publishes nothing from any released package, so there is no
`dist` to compare and no code-mutation control to run. The measurement
is the packages' own manifests.

## Gates (head `06b8fbc2d3`)

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 closure build at `527d5dca06` (the first merge; `packages/qa`
is byte-identical between the two):

```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 77s (1m17s) · declare it in the PR body · pnpm --workspace-concurrency=2 --filter '@objectstack/downstream-contract^...' --filter '@objectstack/http-conformance^...' --filter '@objectstack/vitest-filter-preflight^...' 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 11s · 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 2s · declare it in the PR body · pnpm --filter @objectstack/downstream-contract 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 3s · declare it in the PR body · pnpm --filter @objectstack/downstream-contract 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 8s · declare it in the PR body · pnpm --filter @objectstack/http-conformance 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/http-conformance 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 4s · declare it in the PR body · pnpm --filter @objectstack/vitest-filter-preflight 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 1s · declare it in the PR body · pnpm --filter @objectstack/vitest-filter-preflight typecheck
```

- **Build.** The three packages' dependency closure was built: 61 of 81
workspace projects, at `30aae6a3e6` and again at `527d5dca06`. Then the
whole workspace was built with `turbo run build --filter='./packages/*'
--filter='./packages/*/*'`: 71 of 71 tasks at this head, 66 of them
cache hits. The tree was clean after each build.
- **Tests (`vitest run`), at this head and at both earlier commits:**
  - `downstream-contract`: 3 files, 31 tests passed;
  - `http-conformance`: 8 files, 102 tests passed;
  - `vitest-filter-preflight`: 3 files, 111 tests passed.
  - These are every test file each package has.
- **Typecheck.** All three `typecheck` scripts exit 0;
`downstream-contract`'s is also one of CI's consumer-gate type-check
lanes. `http-conformance`'s `check:test-typecheck` holds: 3 files, 27
errors, 10 pinned signatures. `tsc --listFiles` shows every touched file
compiled:
  - `downstream-contract/tsconfig.json`: 11 files, 3 of them tests;
- `http-conformance/tsconfig.test.json`: 11 files, all 8 tests,
including both touched test files and `adapter.ts`;
  - `vitest-filter-preflight/tsconfig.json`: 6 files, 3 of them tests.
- **Lint.** The repo-wide `pnpm lint` (`eslint . --no-inline-config`)
exits 0 at this head (2026-09-29T21:48:59Z to 21:49:26Z), and at
`527d5dca06` before the second merge.
- **Citation judging.** `node scripts/check-issue-citations.mjs --base
origin/main`, with `origin/main` at `cbaf04c1fd` and merged, judges 2
citations on the changed lines of 3 files (the kept `objectstack-ai#17978` and
`objectstack-ai#14554`). Both resolve, and the run exits 0. The pinned merged base of
the first merge (`--base 1ab9892`, at `527d5dca06`) gives the same 2
citations and also exits 0.
- **Derived gates.** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` derived 53 families, identical at
`527d5dca06` and at this head. All 53 exit 0 at this head. `--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,342 text files, no raw control bytes);
- `check:published-files`, `check:type-check-coverage` and
`check:type-check-debt`.
- **Artifact rosters.** 36 of the 39 non-self-test roster rows exit 0.
These include 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 the results are 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 `679f95ec5c` the filtered census answers 8 sites
on 8 lines, 5 numbers, in 3 files, as on the seat's `0be898499f`. The
whole-repo count is 1,185.
- **H1 holds.** After the rewrite, the filtered census answers 0 for
`packages/qa`. No site was left for an open PR or for an unfound anchor.
The file lists of all 12 open PRs were read at 2026-09-29T21:13:53Z:
only the Version Packages PR objectstack-ai#20639 touches `packages/qa`, in
`CHANGELOG.md` and `package.json`.
- **H2 holds on the parser-token reading.** The comment-stripped
(parser-token) diff of all 5 touched files is empty, and its controls
fire. The `dist` reading is not available, because none of the packages
has a build.

## Acceptance notes

- **`packages/qa` outside `src/**`, a later stage of the card.** The
census surface is `packages/**/src/**`, and `packages/qa/dogfood` has no
`src/` at all. The supplementary scan counts 127 dead sites left in
`packages/qa` outside `src/**`:
- `dogfood/test/**` and `dogfood/vitest.config.ts`: 122 sites, 27
numbers, 28 files (94 comments, 28 strings);
- `downstream-contract/test/contract.test.ts:46` (`objectstack-ai#10485`, a comment);
- `vitest-filter-preflight/test/config-wiring-sweep.test.ts:6` and
`test/filter-preflight.test.ts:5` (`objectstack-ai#17853`, comments);
- `vitest-filter-preflight/package.json:6` (`objectstack-ai#17853`, in the package
`description`);
- `http-conformance/test-typecheck-debt.json:2` (`objectstack-ai#13470`, in the
generated `_comment`, whose producer is
`scripts/check-test-typecheck.mts`; that producer is objectstack-ai#20715's, and it is
never fixed by hand).

  None of them is in this stage's surface, and none moved.
- **ADR-0122's own status line and amendment heading** cite `objectstack-ai#6083`
(404). `docs/adr/**` is a governed surface and one of the gate's
deferred surfaces, so it is noted here, not touched.
- **Card-word residue, cited nowhere.**
`vitest-filter-preflight/src/index.ts:117` says "this card" about 150
lines from either rewritten line. It cites no number, so it was left, as
the landed stages left theirs.
- **The moving `origin/main`.** The branch merged `origin/main` twice.
The first merge (`527d5dca06`) took `1ab98926b0` (a spec retirement,
nothing in `packages/qa`). After it, the shared ref advanced to
`cbaf04c1fd`, the plugin-approvals re-anchor (PR objectstack-ai#20717). Against that
moved ref, `--base origin/main` then read the old plugin-approvals lines
as this branch's additions: it judged 31 citations and exited 2, because
the diff was two-dot. That run is not a measurement of this change. The
pinned base answered exit 0. The second merge (`06b8fbc2d3`) took
`cbaf04c1fd`, and every gate above was re-run on it. CI judges the merge
ref.

## Deviations

- **The dispatch's `packages/qa/src/**` and `@objectstack/qa`** do not
exist as spelled. The surface was read as `packages/qa/*/src/**`, the
census's own reading of `packages/**/src/**`. The changeset measurement
was taken per touched package.
- **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 two
merge commits carry 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