Uh oh!
There was an error while loading. Please reload this page.
fix(spec): the shard reader names the file, entry and anchor for a non-string entry (#6751) - #7075
Conversation
…n-string entry (#6751) `aggregateCategoryShards` cast each shard's array to `string[]` and handed the entries straight to `categoryOfDefKey`, whose parameter is declared `string`. A hand-edited non-string entry therefore died inside the helper on a bare `key.indexOf is not a function`. All three call sites in `build-schemas.ts` print `error.message` and nothing else, so that text was the whole diagnostic: no shard file among 14, no entry, no issue anchor — while the reader's other three defect classes all name the file and carry one. The check lands at the reader, not in `categoryOfDefKey`: the helper's contract already says `string`, and only its caller knows the file name. The `as string[]` cast is replaced by a real `typeof` check that narrows, so the entry type is now verified where untyped JSON enters rather than asserted. Gate behaviour is unchanged — same exit 1, same verdicts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AZgRyPVwi1jLb1mNNuUQ9o
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckNo hand-written docs reference the 0 changed package(s). ✅ |
os-project-manager
commented
Aug 9, 2026
座位验收( 复核(在报告到达前已独立做完,与报告零出入):
本单最值钱的产出是反向验证挖出的第三类,单据没写:数组条目根本不抛 TypeError—— 预测失手也记得干净:首轮 26/26 绿的预测被自己的新夹具打红,抓的是新代码的冠词缺陷( 范围守住了:姊妹读取器( 摘草稿入队。 Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#6751
分片读取器四类缺陷里,只有「条目不是字符串」这一类不说话。本 PR 把它补齐到与另外三类同一水平:报文件名 + 报条目 + 带 issue 锚点 + 带处方。门禁行为一格未动。
落点选择:B(读取处),不是 A(
categoryOfDefKey内)单子给了两个候选,选 B ——
aggregateCategoryShards的遍历处,理由是契约方向:categoryOfDefKey(key: string)的签名已经声明了string。真正的违约方是调用者:它把磁盘上的无类型 JSON 数组as string[]一转就喂了进去。在 helper 里加typeof容错,等于让一个声明只收string的函数去迁就它自己声明不接受的类型 —— 修的是消费者,不是生产者。shard.name。A 最多补到「不是裸报错」,补不出文件名 —— 而 14 个分片里到底是哪一个,恰恰是作者唯一真正需要的信息。declares category …/has no "keys" array/carries "…")全部由这一层产出,同族同形。顺带的结构收益:
as string[]这个断言消失了,换成一次真实的typeof窄化 —— 条目类型现在是在无类型 JSON 入场处被验证,而不是被断言。锚点沿用
#5837(分片重做),与相邻三句一致:锚点指向「这个文件为什么长这样」的设计决策,不指向写下这行的 PR。实测:同一破坏,修前 / 修后
往
packages/spec/authorable-surface/ui.json的keys[0]注入数字12345,跑pnpm --filter @objectstack/spec check:authorable-surface:修前(
origin/main@2c7e62d5f),EXIT=1:修后,
EXIT=1(退出码未变):干净树上门禁照常
EXIT=0,且该次运行把三个分片产物全读了一遍(1598 schemas / 1311 defaults / manifest),验证均无变化 —— 三个消费者都走的是被改动的这段循环。反向验证发现:数组条目根本不抛 TypeError,而是报错报错了
把新加的分支删掉重跑新夹具,预测「3 红 / 23 绿」,实测
Tests 3 failed | 23 passed (26),与预测一致。三条红各自暴露的真实旧行为:12345key.indexOf is not a functionnullCannot read properties of null (reading 'indexOf')["ui/View"]cannot shard "ui/View": … has no category segment (#5837)第三行是本轮最值得记的一笔,单子里没有:数组条目压根不会抛 TypeError。
['ui/View'].indexOf('/')是一次完全合法的Array.prototype.indexOf调用,返回-1,于是slash <= 0成立,代码一路落进「没有 category 段」那条消息 —— 报出一个文件里并不存在的 key,和一个不是真正病因的病因。一个自信的错误诊断比一个裸报错更贵,这也是类型检查必须排在路由之前的原因。该事实已写进夹具注释。夹具:为什么不能用
toThrow()这三条用例断言的是消息内容,逐项 pin 文件名 / 条目 / 锚点,不是
expect(...).toThrow()—— 修前的读取器照样抛(只是抛裸TypeError),所以 throw-only 断言在它本该 pin 的缺陷上是永远绿的。这里消息即契约(#5240),按此口径写。null与object单独一条:typeof null === 'object',把「你写了null」说成「object」会把作者引去找花括号。诚实记一笔预测失手:首轮我预测 26/26 全绿,实测
1 failed—— 夹具抓出的是我自己新代码的语法缺陷(a object,冠词没跟类型名走),不是读取器的。已修为按首字母定冠词(object/undefined都取an)。验证
pnpm --filter @objectstack/spec test→Test Files 354 passed (354)/Tests 9238 passed (9238)pnpm --filter @objectstack/spec typecheck→ 全绿,含check:scripts-typecheck(strict 程序,真正读到本次改动的那个)npx eslint两个改动文件 →EXIT=0;node scripts/check-nul-bytes.mjs→ OKcheck:authorable-surface:干净树EXIT=0,注入非字符串EXIT=1changeset
无 ——
packages/spec的files数组为dist / json-schema / liveness / prompts / llms.txt / README.md / src/**/*.zod.ts / CHANGELOG.md / api-surface / spec-changes.json,不含scripts/(本轮自行复验)。本 PR 只动packages/spec/scripts/**,发布物零变化,故打skip-changeset标签。🤖 Generated with Claude Code
Generated by Claude Code