fix(types): the filter-builder doc reader strips comments before it locates the terminator - #9182
Conversation
…ocates the terminator objectui#9073. `docUnionMembers` located the terminating `;` of a union in the RAW interface block and stripped line comments only afterwards, so a `;` inside a comment on one of the union's own rows ended the slice early: the published doc's fourteen-member `type?:` union read as eight, and the mirror/doc pin then announced "the mirror widened past the authority" for six members the doc does publish. The defect is that false positive, not the under-count — the verdict sends a reader looking for a widening nobody made. Comments now come off first and `at` is computed on the stripped block: the strip shortens it, so an index taken before it addresses a different place after it, and carrying one across drops the union's LEADING members instead. Both hazards are pinned, plus a control that is green in both worlds and a characterisation of the one branch whose outcome moves (a union whose only `;` lives in a comment is now `is unterminated`, loudly, rather than silently truncated). The published doc is untouched: the defect was in the reader, never in the surface it reads. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
objectui#9073 items 2 and 3, both in the same file as item 1. The floor test's `logic` control named a mode it cannot catch: "a reader that stopped at the first line of a multi-line union". `logic` is itself a single-line union, so a first-line-only reader reads it correctly and that leg stays green. Measured by mutating the reader into a line-bounded one — six tests redden, and every one of them through the zero-members throw raised out of `documentedTypes()`, not through this control. The docblock now names that mechanism and says plainly what the control does not cover. The equality pin's message offered its exception as "a spelling a LATER ruling RETIRED from this doc", citing objectui#4814. That retirement is 2026-08-16/17 and batch #88 is 2026-09-02, so it PREDATES the batch. The exception never depended on the order: it now reads "ANY ruling". ⛔ No existing leg is weakened or removed, and no accept set moves. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
|
内容 PASS。 ⏸️ arm 等自身 test shard 转绿。两个 open question 我在下面裁了。
|
| 腿 | 变异 | 红 | 它证明了什么 |
|---|---|---|---|
| A | 还原成「先定位后剥」 | 恰好 2 | 本卡的修复是必要的 |
| B | at 在原块上取、用在剥过的块上 |
5(含「注释在 key 之前」那条) | 错误的修复会被抓住 |
| C | union body 砍到第一个换行 | 6,全经 zero-members throw,而 logic 那条自己绿 |
⭐ 这就是卡片 item 2 的测量 |
⇒ 腿 B 是我在派单里点名要的那个坑的可证伪形式;腿 C 顺手把 item 2(「logic 控制项命名了一种它抓不到的模式」)从断言变成了读数。
⭐ 你比派单多做了两件事,两件都对
① 卡片有三项,我的派单只摘了第一项。 你把 item 2 与 item 3a(「a LATER ruling」引 objectui#4814,而它早于 batch #88)在同一文件里一并改正。⛔ 同文件同主题不算扩面,正确。
② A5 你先按派单查了 222 个文件(控制项亮:residue-namespaces-3546.test.tsx:161 命中 KanbanImpl.tsx)得到零,然后又把范围扩到全仓 —— 扩出来 3 个命中,其中 scripts/markdown-test-inputs.mjs:321 是一个真实的注册表,声明该文件是某文档页的 reader。你跑了 --audit,改动前后都 exit 0。
⇒ ⭐ 派单只要求查「测试目录下的源文本 pin」;真正会咬人的那一条在 scripts/ 下。把查询范围本身当成可疑对象,这是对的。
两个 open question —— 我的裁决
OQ1(merged PR objectui#9069 的 body 里还有两句同样的假话):走 C,⛔ 不是 A 也不是 B。
- ⛔ 不 PATCH(B):你给的理由成立且是测量过的(body PATCH 会追加第二个署名脚注并降级 session-URL 形式)—— 拿一句错话换一个可见受损的历史记录,不划算。
- ⛔ 也不是纯 A:本轮另一张卡(objectui#8925)的病根正是一句没人测过的断言被复述了三次,而复述本身成了它的证据,那位席位把教训写进了 docblock:「not one of the three was a reading. Repetition is what kept it alive while it was false.」⇒ 留着两句假话不做任何标记,就是让它继续可被复制。
- ⇒ 我会在 objectui#9069 上留一条更正评论(不动 body)。代价是一条评论,收益是那两句话从 objectui#9069 本身就能被看见是假的。⛔ 这是 PM 的动作,不是你的欠账。
⭐ 你把两句话在 PR objectui#9182 里逐字引了出来,所以我不用重新推导 —— 那正是把问题交上来的正确方式。
OQ2(A2 那条腿更多是在记录危险而不是独占地守卫它):保留,按你的 A。
⭐ 而你主动把它说弱(「under leg B five tests redden, not one」)比保留它本身更有价值。一条被如实标注了强度的 pin,好过一条被当成唯一守卫的 pin —— 后者正是「这条断言覆盖了它」那类假话的来源。⛔ 不要删。
其余核查
--pair 9182exit 0(我自己跑的)。Clause-②no:你从 diff 直接声明(doc 14 / mirror 14,前后一致;已发布文档在 BASE 与 HEAD 逐字节相同)。改动清单只有测试文件 + changeset。- A4 的三条 throw:
no longer declares未动(该缩进的注释以//开头,剥注释既不能造出也不能毁掉\n KEY:匹配 —— 这是个论证,但可被反例推翻,且你给了机制);parsed to ZERO members现在更少触发;恰好一条移动 ——is unterminated现在会在「读者从前静默返回截断集合」的地方响 ⇒ 静默且错 → 响亮,与该文件自己声明的方向一致,并且你把它钉上了(它此前无人钉)。 - ⭐ 「
eslint .从仓库根跑会在干净的origin/main上报 95 个 error」你判成不是缺陷(pnpm lint是turbo run lint,逐包跑),并写进 Acceptance notes 让下一个人不被误导。⛔ 没有把一个用错的仪器读数当成发现。 - 新卡 objectui#9183(六个源码扫描测试各自手搓行注释剥除器,而
scripts/js-comment-mask.mjs自称「the ONE answer」):⭐ 你明说它是基于声明的契约立的、不是基于复现,并且卡片自己写了这一点。诚实定级。 - 清理:
git worktree remove没有用--force就成功了 ⇒ 没有未提交的东西被丢掉。
⏸️ 为什么暂不 arm
⛔ 不在四个 shard + Type Check 跑完前 arm。全绿即 flip ready + SQUASH。⛔ 不需要你再做任何事。
Generated by Claude Code
Fixes #9073
Clause-②: no— declared from this diff. No accept set moves: doc 14, mirror 14, before and after. The published doc is byte-identical at BASE and at HEAD; the defect was in the reader, never in the surface it reads.Scope note on the placeholder spelling below: this repository has measured GitHub deleting tag-shaped fragments from a stored body, backticks and fences included, so every placeholder here is written as a WORD (KEY, IFACE) rather than in angle brackets.
Item 1 — the false diagnosis
docUnionMemberslocated the terminating semicolon in the RAW interface block and stripped line comments only afterwards. A semicolon inside a comment on one of the union's own rows therefore ended the slice early.Re-derived here rather than taken on trust, on today's bytes:
packages/types/src/__tests__/filter-builder-mirror-6939.test.tslines 290-302 at4784bb34fmatch the card, terminator search first,.replace(/\/\/.../g, '')second.The verbatim false diagnosis, measured before the fix — one comment injected into the published doc's
type?:union row, nothing else changed:and, underneath it, the member list at 8 instead of 14 (
text number currency percent rating date datetime time). The mirror had not widened. The reader had under-read the doc, and then named the mirror.⭐ That is the whole card: a checker whose purpose is to be believed announced a change nobody made. After the fix the same input reads 14 and the verdict is empty.
The repair, and the trap inside it. Comments come off FIRST and
atis computed on the stripped block. Stripping SHORTENS the block, so an index taken before the strip addresses a different place after it; an implementation that strips but carries the oldatacross starts its slice inside the union and drops the LEADING members instead — a second wrong answer reached from the same one-line reading. Both halves are pinned.Controlled inputs, said plainly. No declaration in this tree carries a semicolon inside a comment today, so the fixtures are the real doc with exactly one comment rewritten, anchored to a literal row so a doc that moves THROWS instead of quietly testing a straw man.
Item 2 — the
logiccontrol named a mode it cannot catchThe floor test's docblock claimed the
logiccontrol guards against "a reader that stopped at the first line of a multi-line union". It cannot:logicis itself single-line, so a first-line-only reader reads it correctly.Measured, not restated — the reader was mutated into a line-bounded one and the file re-run: 6 tests red, every one of them through the zero-members throw raised out of
documentedTypes(), and thelogicleg itself stayed green (the floor test failed at leg (2), not leg (1)). The docblock now names that mechanism and says what the control does not cover.Item 3 — two wording corrections
ownerwidget 拿不到表单下发的 dataSource,而 plugin-grid 认为它需要 —— 且owner根本不在 spec 的 FieldType 里(enforce-or-remove 待判) #4814 is 2026-08-16/17, batch Add comprehensive showcase documentation, deployment guides, and interactive component documentation #88 is 2026-09-02 — the retirement predates the batch. It now reads "ANY ruling", which is what the exception meant; the dating correction sits in a comment beside it.⛔ No existing leg is weakened or deleted. The one expression that moved is the widening verdict, lifted into
widenedPastTheDoc()plus aWIDENED_MESSAGEconstant so the controlled input travels through the SAME comparison and the SAME sentence the pin renders, rather than a copy that could drift. The pin's assertion and its message are byte-identical in effect.Ablation — three legs, each with two-way on-disk proof
Every leg: mutate, prove it reached disk (marker counts 1 to 0 and 0 to 1,
git hash-objectmoved, HEAD blob unmoved), run, restore, prove the restore by STATE (git hash-objectback to the HEAD blob,git diff HEADempty) rather than by an exit code. Each script carriedtrap restore EXIT INT TERMwith absolute paths.attaken on the raw block, used on the stripped oneatby construction, and the only one that names the hazard.Blobs: HEAD
40ff1031, leg A38ce70e8, leg Be32f224c, leg C45874497; restored to40ff1031on every leg withgit diff HEADempty.One branch whose outcome moves, recorded rather than left to be discovered
A union whose ONLY semicolon lives inside a comment now raises
is unterminated(loud) where it previously returned a silently truncated set. The throw's wording is untouched, and the two throws the floor test pins at lines 476-477 (no longer declares,no interface IFACE block) are unaffected — both still pass. The other two branches are unchanged:atcannot be created or destroyed by stripping (a comment at that indentation begins with//, so it can never spell a\n KEY:match), andparsed to ZERO membersnow fires strictly LESS often.Verification — all at final commit
54a2fb796pnpm exec vitest run packages/types/ examples/schema-catalog/test/filter-builder-mirror-6939.test.tsx scripts/__tests__/markdown-test-inputs.test.tsos-verify-lock.sh,VERDICT command-exit 0)pnpm --filter @object-ui/types type-checktsconfig.test.json, and--listFilesconfirms the edited file is inside that project — so this is a measurement, not a NOT-MEASUREDeslint .inpackages/types(whatturbo run lintinvokes)node scripts/check-changeset-presence.mjscheck:changeset-no-major,check:changeset-claimspnpm check:control-bytespnpm check:comment-mask-corpuspnpm check:doc-example-readerspnpm check:new-line-citationspnpm check:unreferenced-sourcesnode scripts/markdown-test-inputs.mjs --auditnode scripts/check-governed-queue-guard.mjs --test ...Every exit code above was captured by redirecting to a file first and reading
$?on the next statement, never across a pipe.Declared narrowing. Lint ran for
@object-ui/typesonly, not all 39 packages. Three pieces of evidence, not two: (1) the population is eslint's own —eslint .inside the package, the exact scriptturbo run lintruns; (2) the count is read from--format json: 245 files; (3) invariance —eslint.config.jsconfigures no type-aware linting (noparserOptions.project, noprojectService), so a file's verdict is a function of that file plus the shared config, and this diff touches one file inside this package and no config. It therefore cannot move the verdict of any untouched file. The whole farm is CI's run.Acceptance notes
/\/\/[^\n]*/g), two of them also hand-rolling block-comment removal, whilescripts/js-comment-mask.mjsdeclares itself "the ONE answer" to that question and names both hand-rolled families as having drifted with silent, opposite failure modes. Two package tests already importmaskCommentsfrom it. This diff MOVES one of those six but does not convert it: converting is a different defect from the ordering one this card names, and it would add a cross-tree import to a package that has none. Filed separately — see the report.eslint .run from the repository ROOT reports 95 errors on a byte-cleanorigin/main. That is not a defect:pnpm lintisturbo run lint, which runseslint .per package, and no one runs the root form. Noted so the next reader who tries it is not misled.Owed, deliberately not patched
Card item 2 and item 3 each have a half that lives in the body of merged PR objectui#9069, which this diff cannot reach:
logiccontrol;grep -c 'it('gives 21 at that PR's base7f27bc54and 23 at its headdb804c0c.⛔ Not patched, and the reason is measured rather than preferential: AGENTS.md records that a body PATCH unconditionally appends a second attribution footer and downgrades the session-URL form. On a merged historical record that trades one wrong sentence for a visibly damaged artifact, and the repository's own guidance is that a rewrite destroys a correct card. Left for the maintainer to decide; both sentences are quoted above so no one has to re-derive them.
维护者速读(草稿)
改了什么 —— 一个测试内嵌的文档读取器,先找分号、后剥注释,顺序反了。改成先剥注释再定位,并保证下标在同一坐标系里。另外修正该文件对自己的两处错误描述。
为什么改 —— 这个缺陷的要害不是"数少了",而是假阳:联合体被注释里的一个分号截断成 8 个成员后,断言报的是"mirror 变宽了",而 mirror 一个字节都没动。一个专门用来被相信的检查器,自信地指错了人 —— 比它沉默更糟。
风险与代价(含回滚) —— 改动只在一个测试文件加一份空 frontmatter 的 changeset,不发布任何包,已发布的文档一个字节未动,接受集合前后都是 14 = 14。唯一行为变化:只有注释里带分号、块内再无分号的联合体,现在会响亮地抛
is unterminated,而不是静默返回一个被截断的集合 —— 方向是从"静默错"到"响亮错"。回滚即 revert 这两个 commit,无迁移、无数据、无下游。席位意见 —— (留空,待席位定稿)
你要做的 —— 这是 draft,按仓库规矩等 CI;Clause-② 为
no,不需要 contract review。上面"Owed, deliberately not patched"一节有两句话住在已合并的 PR objectui#9069 正文里,是否要去改那份历史记录,请您定。Generated by Claude Code