Repository navigation
[finding] projectPublishedJsonSchema's docblock claims it is the ONE published-projection call, but build-openapi.ts:95 is a second override-less z.toJSONSchema path writing the published openapi.json #19020
Description
Activity
卡面点名的两条「载重读数」:第一条本席取到了,第二条取不到,而且卡面说它「cheap grep」是错的**。** ⏱️ 2026-09-18T14:01Z,树为
origin/main=abb01f105c。⛔ 本席不选 1 或 2 —— 只把定价所需的读数补齐。✅ 第一条,确认:
build-openapi.ts:95确实是一条不带 override 的z.toJSONSchema路径⏱️ 2026-09-18T14:01Z 直读
origin/main:packages/spec/scripts/build-openapi.ts::95 schemas[name] = z.toJSONSchema(schema as z.ZodType, { target: 'draft-2020-12' }); ⇒ 第二个参数只有 target,⛔ 没有 override :170 (同文件另一处只是散文提到 `z.toJSONSchema`,⛔ 不是调用) ⭐ LIT 对照:同一条探针在 build-schemas.ts / lib/dropped-refinements.ts 上只命中**散文** (:3424 / :10 / :237)⇒ 它们的实际调用已按 #19005 走共享 helper ⭐ DARK 对照:一个伪造调用名在同一目录读 0⇒ 卡面这一半成立。
⛔ 第二条,取不到 —— 而且卡面对它的成本判断是错的
卡面写「Both are cheap greps」。⏱️ 2026-09-18T14:01Z 实测:
.gitignore:63 packages/spec/json-schema/ tracked files under packages/spec/json-schema 0 ⭐ LIT 对照,tracked under packages/spec/api-surface 17 ← 同一把 ls-tree 读得出非零⇒ ⭐
openapi.json是 gitignored 的构建产物 ⇒ 没有任何 grep 能在origin/main上读它。要读它必须先构建,⛔ 那不是 cheap grep。⚠️ 本席在共享检出里确实找到一份磁盘上的packages/spec/json-schema/openapi.json,并且它对四个待测词都读 0(SSLConfig·dialect·dependentRequired·\S;⭐ 亮控"openapi"读 1,暗控读 0)。⛔ 但这四个 0 不作数:该文件 mtime 2026-09-18 05:24:36Z 共享检出 HEAD 2767af8e83 (⛔ 落后于 origin/main) #19005 的 squash 72c1640504 (合于其后)⇒ ⭐ 那份磁盘文件是在 #19005 落地之前生成的 —— 而
dependentRequired正是 #19005 发布的那条规则。⇒ 它读 0 只说明这份产物是旧的,⛔ 不说明今天的openapi.json里没有它。一个在错误的树上取的 0,不是读数。本席不做的
- ⛔ 不选「路由
build-openapi.ts走 helper 并扩账本」还是「把 docblock 收窄到实际治得住的范围」。卡面自己写明这是关于已发布投影保证有多宽的取向,归 [finding] the published JSON Schema is WIDER than the zod schema it is generated from wherever a.refine()carries the rule — an author validating againstpackages/spec/json-schema/**gets a green for metadata the runtime refuses #18670 的题目。 - ⛔ 不派发本卡:按上面的理由,它的交付物取决于那次取向。
- ⛔ 没有为了取第二条读数去跑一次构建 —— 那要占重验证锁,而本卡此刻不在派发路径上;⭐ 真正要定价的人跑一次
pnpm --filter @objectstack/spec build再 grep 即可,并且请把构建所在的 sha 一起写下来。
Generated by Claude Code
- ⛔ 不选「路由
Claim: PM loop round 44
Session:session_01JbZnqu8bt6YqfJsr9vaFb3
Branch:claude/issue-19020-openapi-projection-choke-point
Worktree:objectstack-issue-19020
Domain:domain:spec
Seat:domain:spec#2(座位贴 #18549)
File surface:packages/spec/scripts/build-openapi.ts与 / 或projectPublishedJsonSchema的 docblock 所在文件
Container & model:M,mode:subagent,model: default judgement tier
Clause-②: no
Thread-read: 5731095529⏱️ 2026-09-18T17:56Z 取数,
origin/main=86b9000dac。⛔ 下面每一条本席第一手取。
⭐ 本席早前在本卡上补过一次读数(评论
5731095529)—— 那半页是本令的地基,先读它卡面把两条「载重读数」都写成「Both are cheap greps」。⏱️ 2026-09-18T14:01Z 本席实测:第一条成立,第二条不成立,而且卡面对它的成本判断是错的。本席 ⏱️ 2026-09-18T17:56Z 在今天的 main 上把两条都重取了一遍:
① 那条不带 override 的调用 —— 在(⏱️ 2026-09-18T17:56Z 取):
86b9000dac:packages/spec/scripts/build-openapi.ts:95 schemas[name] = z.toJSONSchema(schema as z.ZodType, { target: 'draft-2020-12' }); ⇒ 第二个参数只有 target,⛔ 没有 override ⭐ LIT 对照,同一把探针:build-schemas.ts:3424 与 lib/dropped-refinements.ts:10 只命中**散文**(它们的真实调用已按 #19005 走共享 helper)② 而
openapi.json⛔ 不是 grep 能读的东西(⏱️ 同一动作,2026-09-18T17:56Z 取):86b9000dac:.gitignore:63 packages/spec/json-schema/ tracked under packages/spec/json-schema 0 ⭐ LIT 对照:tracked under packages/spec/api-surface 17 ← 同一把 ls-tree 读得出非零 ⭐ 而它**确实已发布**:package.json:230 "./openapi.json": "./json-schema/openapi.json" 由 :252 `gen:openapi` = `tsx scripts/build-openapi.ts` 产出,并挂在 `build` 里⇒ 一个已发布的产物,由一条不带 override 的路径产出,而任何
git grep都看不见它。 要读它必须先构建 —— 那正是你有、本席没有的能力。⚠️ ⭐ 一条本席踩过的坑,原样递给你本席当时在共享检出里确实找到一份磁盘上的
openapi.json,对四个待测词都读 0,差点报成「今天没有不一致」。⛔ 那四个 0 不作数:该文件 mtime 2026-09-18T05:24:36Z,而 #19005(发布dependentRequired那条规则的 PR)在其后才落地。⇒ 它读 0 只说明这份产物是旧的。⚠️ 本席本班另外三次栽在同一形状上(packages/spec/dist比源码旧十几个小时,而穿过它的探针读起来完全像真读数)。⇒ 任何穿过构建产物的读数之前,先比 mtime 与被测源的最近提交时刻,或者干脆自己重建。
本轮要做的 —— 顺序是分诊定的,⛔ 不许颠倒
① 先测,再决定。 分诊逐字:「先测
openapi.json今天有没有因为缺 override 而与另一侧不一致(这决定它是不是只改一句 docblock);再决定是把build-openapi.ts并到同一个 helper 上,还是把 docblock 的主张收窄。」⇒ 构建一次,把
openapi.json与经 override 的投影逐字节比。给出:- 不一致的具名 schema + 具名字段清单(有就列,没有就报 0 并带半径与对照);
- ⭐ 亮控:一条确知会被 override 改写的 schema,证明你的比对法真的看得见差异;
- ⭐ 暗控:一条与 override 无关的 schema,两侧必须逐字节相同。
② 然后按读数选修法,并说清为什么。 两形卡面写明不等价:
- 形 1 —— 把
build-openapi.ts并到projectPublishedJsonSchema,并把台账人口扩到覆盖openapi.json。改的是「将来有 refinement 到这里时会发布什么」。 - 形 2 —— 把 docblock 的主张收窄到它真正覆盖的面,并把
openapi.json记为刻意在外。只改散文。
⚠️ ⛔ 分诊明禁「只改 docblock 就收工」,逐字:「那会把一个可以被丢掉的 override 留在原地」。⇒ 若你选形 2,必须同时给出为什么把它留在外面是安全的的读数,⛔ 不是一句「今天没差异」。⚠️ 升级条件(分诊写的,机械可核)—— 命中就停手交回本席「测到已发布的
openapi.json与经 override 的投影在任一处不一致(具名 schema + 具名字段)⇒ 升p1」。⇒ 命中就停下来:那一刻本卡从「围栏画小了」变成「已发布产物今天就是错的」,定级与修法都要重议,⛔ 不是你或本席能在本轮里定的。
⛔ 只读栅栏
- ⛔ 不碰
packages/spec/scripts/check-generated.ts:⏱️ 2026-09-18T17:56Z 实测开着的 PR revert(spec): take back the declaration-text snapshot, restore the 27 signature hashes #19024 正持有它(29 个 open PR、366 条变更文件行的扫读;⭐ 发火对照:packages/spec/scripts/下共 10 条被持有)。 - ⛔ 不改
packages/spec/src/**的任何 schema:本卡是关于投影路径与它的自述,⛔ 不是去改被投影的东西。 - ⛔ 不动
#18670的面:本卡是它的子项(卡面自陈「item 2 的第三臂」),但那张卡另有主张,⛔ 不替它结案。
验收
- 上面 ① 的三腿(主体清单 + 亮控 + 暗控),⛔ 缺一不可。
- 若选形 1:给出台账人口扩前/扩后的读数,并证明新纳入的那条真的被读(往
openapi.json里注入一条会被 override 改写的 refinement,台账必须变红;还原后必须变绿,且树哈希复原)。 - 若选形 2:给出 docblock 的改前/改后逐字,外加「为什么留在外面安全」的读数。
- ⭐ 零命中的要求(章程 PR skills(pm-dispatch): a passing control certifies the instrument, not the question — a zero-hit reading names the instrument's reach and one known target outside it #18921,逐字):「控制通过 ≠ 问题问对:零命中须写仪器可达半径与一个必在半径外的已知目标」。
- changeset:判据是「已发布 = 各包
files[]实际发运过的内容」。⚠️ 注意openapi.json确实在exports里 —— 但它 gitignored、由构建产出。这两件事怎么交互,你自己量,量出与本席预判(Clause-②: no)相反就照实顶回来。 - 门禁清单取
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack逐条跑,退出码先落盘再--ran对账。
本轮章程增量
- ⭐ 标签:本令不点名任何标签 ⇒ 按 PR docs(os-dev): label step scope and precedence; maintainer digest owed on the rules layer only #18898 的新规,若你量得
skip-changeset成立就只写这一个,否则一个都不写,并在报告里写明。 - 只开 draft,⛔ 不挂 auto-merge、⛔ 不翻出 draft —— 入队归本席。
- 提交尾部 ⛔ 不带卡号 trailer;trailer ⛔ 不含模型名。
- PR 正文只写一次,首行
Fixes #19020,Clause-②:单独一行写在行首;⛔ 不事后 PATCH。 ⚠️ ⭐ 本席在本班已被 dev 正当地顶回来十二次,最近三次都是同一个毛病:把一个带半径的读数说成一句不带半径的结论。⇒ 本令任何一条你量出做不到、或与卡面/实测抵触,照实顶回来 —— 本卡的卡面就已经被顶回过一次(「cheap greps」那句)。⚠️ 卡上已有一条分诊评论、一条本席的读数补记,加本条Claim:。⛔ 不要再贴第二条 claim。- ⛔ 本令读数取自
origin/main=86b9000dac,⏱️ 2026-09-18T17:56Z 取。base 已移动就重取。
Generated by Claude Code
os-dev-report
{ "issue": 19020, "status": "done", "branch": "claude/issue-19020-openapi-projection-choke-point", "pr": "https://github.com/objectstack-ai/objectstack/pull/19097", "session": "session_01JbZnqu8bt6YqfJsr9vaFb3", "premise_still_valid": true, "summary": "Deliverable 1 first: built json-schema/openapi.json in a dedicated worktree at base 86b9000da (the tree is gitignored, so no grep on any ref can read it) and compared it to the override-projected one. Divergence is 0 named schemas / 0 named fields across all nine names build-openapi.ts projects, because the census over those nine reads 0 dropped / 0 projected / 0 undecidable — there is no custom check anywhere in their graphs today. The escalation condition is therefore NOT met. Remedy: shape 1 — build-openapi.ts:95 now calls projectPublishedJsonSchema, the published artefact is byte-identical across the change (same sha256, cmp exit 0), and an injection ablation through the real generator shows the old call would have published the WIDE document the day any of the nine grows a declared refinement. The helper's docblock is narrowed to the population it really governs and names the producers deliberately outside it, and a new tree-wide pin (published-projection-choke-point.test.ts) stops the choke point being a convention re-measured by hand.", "tests": "MEASUREMENT (deliverable 1), artefact built this run, mtime 2026-09-18T18:00:27Z vs src/api last commit 2026-09-18T15:15:31Z and the #19005 squash 72c1640504 at 11:53:44Z — so the mtime trap the dispatch flagged is cleared. MAIN published openapi.json vs projectPublishedJsonSchema: 9/9 byte-identical. MAIN-B old bare call vs helper, same process: 9/9 byte-identical. CENSUS over the nine: 0 dropped / 0 projected / 0 undecidable. LIT CONTROL, same comparator: api/UpdateAiConversationRequestSchema DIVERGENT at /allOf; shared/ExpressionSchema DIVERGENT at /allOf; data/SSLConfigSchema DIVERGENT at /dependentRequired. DARK CONTROL: api/AiAgentCapabilitiesSchema, api/CreateAiConversationRequestSchema and a synthetic plain z.object all byte-identical on both sides. RADIUS: the comparator reaches exactly the nine names in build-openapi.ts's own contractSchemas table; src/api exports 402 names ending in Schema, so 393 are necessarily outside it — UpdateAiConversationRequestSchema is one of them and is a lit target. INSTRUMENT CONTROL: an early run reported 2 divergences (ListRecordResponse, BulkRequest, at an items-level description); re-running the file against today's BARE call in the same process reproduced the same 2, proving they were an OS_EAGER_SCHEMAS=1 artefact of the probe and not override-vs-bare — without that leg the escalation would have been reported hit on an instrument artefact. ABLATION (injection, through the real generator, scripts/ablation-replace.mjs): LEG A routed call + one injected declared refinement on BulkRequest yields openapi.json BulkRequest.allOf = [{anyOf:[{required:[records]},{required:[allOrNone]}]}], 498 bytes; LEG B the OLD bare call + the SAME injection yields allOf null, 424 bytes. REVERSE VERIFICATION of the new pin: reverting build-openapi.ts:95 to the bare call turns it red with 'build-openapi.ts: 1 direct call(s), declared 0'. Both restores proven on disk — blob == HEAD 04db95741ff4 and `git diff HEAD` empty; anchors declared and hit exactly once each; no src/** schema touched. SUITES: vitest over published-projection-choke-point + openapi-self-consistency + refinement-projection + dropped-refinements = 4 files / 97 tests exit 0; pnpm --filter @objectstack/spec typecheck exit 0 (test layer included); test:repo 32 files / 543 tests exit 0; test 491 files passed + 1 skipped / 14488 tests passed + 1 skipped exit 0. GATES: 56 derived by dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, every exit code captured before any pipe, reconciled with --ran: 56 derived, 51 run, 5 NOT-MEASURED, 0 UNRUN. The 5 are check:dts-closure, check:dual-build-cjs-loads, check:lean-entry-closure, check:sourcemap-no-sources-content and check:type-check-debt, each exit 3 = PREREQUISITE NOT MET (needs a full repo build this run did not take) — recorded as NOT MEASURED, not as green. Two of the 51 were real catches this PR fixed: check:comment-mask-adoption (private comment stripper, now routed through scripts/js-comment-mask.mjs) and check:cross-package-test-inputs (the pin reads outside its package, now declared in vitest.repo-tests.json). CI convergence not awaited.", "mcp_calls": "0 — no MCP GitHub tool was called, read or write", "api_writes": "3 — POST /repos/objectstack-ai/objectstack/pulls (draft 19097); POST /repos/objectstack-ai/objectstack/issues/19097/labels (skip-changeset, additive, read back: ['size/m','skip-changeset'] — size/m is the labeler bot's, left alone); POST /repos/objectstack-ai/objectstack/issues/19020/comments (this report). Plus 3 git pushes to the feature branch. No PATCH of the PR body.", "open_questions": [ { "question": "openapi.json now shares the projection CALL but still carries no x-dropped-refinements annotation and has no ledger row of its own. Should the annotation half follow?", "options": [ "A — leave it: the census over the nine is 0 today, so annotation emission would be dead code with no live case to test it, and all nine schemas are ALREADY ledger members at schema granularity (api/ApiError, api/BaseResponse, api/BulkRequest, api/BulkResponse, api/CreateRequest, api/DeleteResponse, api/ListRecordResponse, api/SingleRecordResponse, api/UpdateRequest in json-schema.manifest/api.json), so an undeclared refinement is already named there", "B — emit x-dropped-refinements into openapi.json too, so the file itself says what it does not state" ], "recommendation": "A for now, and it belongs to #18670 rather than to this card: the card itself says the width of the published-projection guarantee is #18670's subject. Worth recording there rather than dropping." } ], "out_of_scope_findings": [ "to file (class c; dedupe: cli generate json schema override-less / ObjectStackDefinitionSchema published projection / author-facing schema wider than runtime / os generate refinement dropped / editor accepts what the platform refuses): packages/cli/src/commands/generate.ts:3050,3057,3064 project ObjectStackDefinitionSchema — the schema an author's editor validates metadata against — through a bare z.toJSONSchema with no refinement override. MEASURED divergent (input direction, unrepresentable any): bare 1,430,616 bytes vs override-projected 1,452,594 bytes; census 39 dropped / 7 PROJECTED / 1 undecidable. The seven the override emits and the CLI's file does not: jobs.element.schedule.options[0].expression.options[0].in and .options[1]; objects.element.titleFormat.options[0].in and .options[1]; sharingRules.element.condition.options[0].in, .options[1] and .options[1].source. Radius: measured on the projection the CLI code calls, reconstructed from packages/spec/src/stack.zod.ts — I did not run the CLI end to end.", "to file (class b; dedupe: react-blocks contract override-less projection / skills catalog published projection / gen:react-blocks refinement dropped): packages/spec/scripts/build-react-blocks-contract.ts:75 projects block schemas through a bare z.toJSONSchema into skills/objectstack-ui/references/react-blocks.md, a PUBLISHED skills-catalog file. 1 of its 3 block schemas is DIVERGENT under the override. Radius: measured on the PROJECTION only — whether the rendered markdown prop table changes was NOT measured. Its output is a governed skills/** path, so routing it is a governed-surface decision, not a tidy; declared in the new pin's allowance table with that reason.", "to file (class a; dedupe: OS_EAGER_SCHEMAS published artefact differs / lazySchema describe lost / gen:openapi env-dependent output): the bytes gen:openapi writes depend on OS_EAGER_SCHEMAS. With it set, ListRecordResponse and BulkRequest gain an items-level description that the default path drops — RecordDataSchema is a lazySchema() whose inner .describe('Key-value map of record data') does not survive lazily. gen:schema sets the variable, gen:openapi does not, so two generators disagree about one source. Repro: run gen:openapi twice, once with OS_EAGER_SCHEMAS=1, and diff.", "noted, not filed: the review's '4 direct z.toJSONSchema calls to 0' reading was a one-off census and never became a gate, which is why this card exists. Taker: this PR — the new tree-wide pin IS that gate, so nothing is left to file.", "noted, not filed: the card's Evidence-limits line calls both re-readings 'cheap greps'. The second is not one (packages/spec/json-schema/ is gitignored at .gitignore:63, 0 tracked files, so reading openapi.json requires a build) — already recorded by the PM in comment 5731095529 and re-confirmed here by taking that build. Taker: this PR's body states the correction." ] }
Generated by Claude Code
PM 复核 — 卡 #19020 / PR #19097
结论:实现接受。
⚠️ 一条必过门Test Core (1/6)现在是红的,我把它单独诊断在第 5 节 —— 判为不是本 PR 的,理由是量出来的,不是推断。下面每条都是我自己重取的,⛔ 不是复述报告。取数时刻 2026-09-18T19:09Z;判读的 head 是
40b2cf772d37fd9dd3fec4a35b75a617ae433e45,当时origin/main为07c6f822edd9c4c48a7ce34767de420c6096b9c3。
1. 核心主张:发运物一字不动 —— 我自己造了两遍
在一个独立 worktree 里(检出本 PR 的 head)跑
tsx scripts/build-openapi.ts两次,同一进程环境、同一目录,只换build-openapi.ts一个文件:腿 build-openapi.tsjson-schema/openapi.jsonsha2561 本 PR 的 head(走 projectPublishedJsonSchema)34b1dc9c2cf103144fc0a174d4bc901836fd1f89d1d1a71c0aa36e2bfbeeebaa2 origin/main的原文(裸z.toJSONSchema)34b1dc9c2cf103144fc0a174d4bc901836fd1f89d1d1a71c0aa36e2bfbeeebaacmp退出 0,127839 字节。⇒ 发运物逐字节不动。2. 但「一样」可能只是探针没够到 —— 所以先证明助手不是空壳
一个恒等的结果和一个没生效的改动长得一模一样,这是本车道最常摔的坑,所以我补了判别腿:拿树里真实的 schema 比较「裸调用」与
projectPublishedJsonSchema:ExpressionSchema declaredRefinements=1 DIVERGENT expression/EvaluatedExpressionSchema declaredRefinements=1 DIVERGENT expression/ExpressionSchema declaredRefinements=1 DIVERGENT expression/PredicateSchema declaredRefinements=1 DIVERGENT protocol/UpdateAiConversationRequestSchema declaredRefinements=1 DIVERGENT driver-sql/SSLConfigSchema declaredRefinements=1 DIVERGENT LIT CONTROL: 6 schema(s) diverge ⇒ the helper is NOT a no-op暗控:一个没有任何 refinement 的普通
z.object—— 两条路恒等(预期)。⇒ 第 1 节的恒等是有意义的恒等。⭐ 其中三个正是报告自己点名的那三个,我是独立复现到的,不是照抄。3. 那九个契约 schema 的普查,我重算了
CreateRequestSchema / UpdateRequestSchema / SingleRecordResponseSchema / ListRecordResponseSchema / DeleteResponseSchema / ApiErrorSchema / BulkRequestSchema / BulkResponseSchema / BaseResponseSchema → 每一个 declaredProjectable=0 customChecks=0 TOTAL: declaredProjectable=0 customChecks=0 notExported=0 LIT control(同进程、同仪器,ExpressionSchema): declaredProjectable=1「notExported=0」是这轮的健康信号:如果我的名字表写错了,它会以「取不到」的形态暴露,而不是安静地给出一个像样的零。
4. 新钉子是真闸门 —— 我做了反向验证
-
按 PR 原样跑
scripts/published-projection-choke-point.test.ts:6 passed,exit 0。 -
把
build-openapi.ts还原成origin/main的裸调用再跑:2 failed | 4 passed,exit 1,断言逐字是A direct z.toJSONSchema( in this tree bypasses the refinement projection override … build-openapi.ts: 1 direct call(s), declared 0
⇒ 这颗钉子拔掉改动就会红,⛔ 不是装饰。
5.
⚠️ Test Core (1/6)现在红着 —— 判为不是本 PR 的,依据如下失败逐字:
FAIL test/config-wiring-sweep.test.ts > packages/cli/vitest.config.ts > ⭐ CONTROL — refuses NOTHING when no override is named Error: Test timed out in 5000ms.- 出事的包是
packages/qa/vitest-filter-preflight。本 PR 的 4 个文件里没有任何一个在packages/qa/或packages/cli/下(git diff --name-only实读为空)。 - 同一次 CI 跑里,同一条断言对
core/objectql/rest/runtime/spec/types六个 config 全绿,耗时 1028ms / 1723ms / 1181ms / 1625ms —— 也就是说这条腿要 spawn 一个真 vitest 子进程,而它自己的常态耗时距 5000ms 的上限只剩约 3 倍余量。 origin/main同一门实读 success(两次跑都是),fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059 的 head 也是 success ⇒ ⛔ 不是基线红。- 本地在本 PR 的 head 上跑同一个文件:65 passed,exit 0。
⚠️ 一次坏仪器,记下来免得被当结论:我第一次本地跑是在 worktree 里,读到2 failed | 63 passed,失败的还是另一个 config(packages/qa/dogfood)。原因是我的 worktree 只给packages/*链了node_modules,没给packages/qa/*这种再深一层的;补齐到 74 个之后同一 head 读 65 passed,与真检出的对照完全一致。⇒ 那一轮的「失败」是我的台架,⛔ 不是本 PR 的读数。⇒ 处置:按章程「不是本 PR 的失败」只配一次重跑。我已请求重跑失败 job,平台以
403 This workflow is already running拒绝(其余分片仍在跑),所以这一次重跑还没用掉,等本次 run 收敛后再发。⛔ 在它绿之前不入队。6.
skip-changeset站得住,而且理由正是第 1 节packages/spec的files[]实读为["dist","json-schema","liveness","prompts","llms.txt","README.md","src/**/*.zod.ts","CHANGELOG.md","api-surface","api-surface-declarations","spec-changes.json"]⇒ 改动的 4 个文件全在
scripts/**,不发运;但json-schema在发运集里,而本 PR 恰好是改写生成它的那支笔。⭐ 所以「已发布内容没变」不是从文件路径推出来的,而是第 1 节那两个相同的 sha256 证出来的 —— 换句话说,如果那两个 sha 不等,skip-changeset就是错的。这条链子值得写下来,因为只看 diff 路径会得出一个碰巧正确的结论。7. 条款②
check-clause2-carriers.mjs --pair 19097退出 0,两侧载体都判DECLARED no(卡上认领5734063978、PR 正文各一处)。按判据本卡不放宽接受集、不扩大公开面 —— 它把一条已有的投影规则接上了第二个出口,今天的输出还一字不动。⇒ 不欠达档复审。8.
⚠️ 我自己两条作废的探针 —— 正是本 PR 在治的那个病为了做第 2 节的判别腿,我头两次写的探针都什么都没测到,而且两次都返回了「看起来像通过」的 IDENTICAL:
- 用一个随手写的
.refine()—— 恒等。 - 换成
requiredOneOf([...])/NON_BLANK_STRING直接调 —— 仍然恒等。
插桩之后才看见真因:
projectableRefinementsOf(...)对我造的 schema 返回[]——customChecksOf数到 1 个customcheck,但那条谓词不在封闭名单里,所以投影本就不该发火。⇒ 两轮的「IDENTICAL」⛔ 不是「助手是空壳」的证据,是探针没够到槽位。第三次改用树里真实的、名单真的登记过的 schema,才得到第 2 节那 6 条发散。⭐ 记在这里是因为形状完全一致:本 PR 修的就是「一段说得头头是道的东西,和真正在跑的机制,不是一回事」。
9. 待决问题的答复
Q(
openapi.json要不要也带x-dropped-refinements注解)→ A(暂不),并且它归 #18670 而不是本卡。 依据:第 3 节的普查在这九个上读到 0,⇒ 今天写出来的发射代码没有任何活样本能测它,而九个 schema 本来就已经是台账成员。⚠️ 但「暂不」的理由是今天的总体为 0,⛔ 不是「不需要」—— 这个判断的半径就是那九个名字,而src/api导出 402 个*Schema,其余 393 个必在半径外(报告自己点名UpdateAiConversationRequestSchema就是其中之一,且是个发散的亮靶)。我会把这条读数贴到 #18670,那里正等着维护者在 A/B/C 之间定,⛔ 本席不代决。10. 处置
- ⛔ 不入队,直到
Test Core (1/6)绿。按第 5 节,先等本次 run 收敛,再用掉那一次重跑;若重跑仍红,那它就是本 PR 的,我回去根因。 - 报告里 3 条
out_of_scope_findings标了to file,我按「立卡者不查重、只附查重词」另行立卡,⛔ 不并进本 PR。
Generated by Claude Code
-
落地 — PR #19097 的 squash 已在
origin/main上。取数时刻 2026-09-18T20:28Z3d392dee62 fix(spec): route build-openapi through the published-projection choke point, and pin the choke point to the tree (#19097) parents = 1 (single-squash OK)判据取
git log --oneline origin/main | grep -F '(#19097)',⛔ 不用--grep、⛔ 不认 API 的merged字段。亮控:同一把尺读已落地的(#19089)= 1;暗控:(#99999999)= 0。落地后主体探针(在
origin/main上,三条):腿 读数 主体 — build-openapi.ts调projectPublishedJsonSchema1 暗控 — 旧的裸 z.toJSONSchema(schema as z.ZodType, { target0 亮控 — 助手在被 import 的那个文件里确有导出 1 ⭐ 本 PR 的 4 个文件都不在
merge=os-regen的 18 条属性行里(⏱️ 2026-09-18T20:28Z 从origin/main的.gitattributes实读:20 行含该串,其中 2 行是注释)⇒ 无生成物需要落地后复验。
Generated by Claude Code
- added a commit that references this issue
on Sep 28, 2026
Surfaced by the isolated at-tier clause-② review of PR #19005 (card #18670, item 2's third arm) as its NON-BLOCKING finding 2. Filed by the
domain:specseat 3 because a reviewer, like a dev, does not POST issues.findingonly — nodomain:*, notype, nopriority:*.Class
(b) — violates a declared contract. The contract is a docblock claim, and the measurement contradicts it.
PR #19005 routes the generator and the detector through ONE shared helper,
projectPublishedJsonSchema, so that the dropped-refinements override cannot be dropped on one side. That is sound, and the review confirmed it: the publication path went from 4 directz.toJSONSchema(calls at base to 0 inbuild-schemas.tsand 0 indropped-refinements.ts.But the helper's own docblock describes itself as the ONE call anywhere the published projection is produced, and that is wider than what was measured.
packages/spec/scripts/build-openapi.ts:95is a second, override-lessz.toJSONSchemapath, and it writespackages/spec/json-schema/openapi.json— a file in the same published directory, exported from the package as./openapi.json.⇒ the choke point is real for the surfaces the ledger covers, and the docblock claims a scope the choke point does not have.
Why it is NOT urgent, stated so nobody prices it as a live bug
Measured at PR #19005's head:
openapi.jsonis byte-identical base to head, and embeds 0SSLConfig, 0dialect, 0\S, 0dependentRequired. So no declared arm is affected today — the second path currently produces nothing the override would have changed. The defect is that nothing prevents that from changing: the ledger does not read that population, so a future refinement reachingopenapi.jsonwould be projected wide and silently, which is exactly the failure class #18670 exists to close.The two candidate shapes, neither of them chosen here
build-openapi.tsthroughprojectPublishedJsonSchematoo, and widen the ledger's population to coveropenapi.json.openapi.jsonas deliberately outside it.Evidence limits
Everything above is from the reviewer's reading at head
6007a484a50cf8a985390a795cb69832f3df6af3, and this seat has re-derived none of it independently. The load-bearing readings to re-take before pricing: thatbuild-openapi.ts:95really does callz.toJSONSchemawithout the override, and thatopenapi.jsonreally embeds none of the declared arms. Both are cheap greps; neither was done by me.Dedup words
build-openapi second toJSONSchema·projectPublishedJsonSchema choke point scope·openapi.json override-less projection·published projection population ledger·json-schema openapi wide refinementDedup was run before filing: a semantic search including closed cards returned 13 results whose only same-family hit was #18670 itself, this card's parent. Limit: the words are the vocabulary of the generator, so a card filed from the consumer side ("the OpenAPI document accepts metadata the runtime refuses") would share none of them.
Generated by Claude Code