test(plugin-designer): pin the blank-target refusal message on the second writer - #9178
Conversation
Both `describeUnusableTarget` docblocks said the two copies "cannot drift" because each pin asserts the same four states. Only one writer had such a pin. Measured on 6214db6 by replacing one branch at a time with a marker sentence and running the whole `plugin-designer` package: `absent`, `non-string` and `empty` each left 19 files / 149 tests green, and only `whitespace-only` went red — incidentally, through `carriedThroughReference-8896`, a card about a different subject. The same four mutations turned the app-shell pin red every time. Adds the mirror four-state block to `MetadataFieldsPage.specKeyReference.test.tsx`, asserting the same four states with the same assertions as the sibling, driven through the page's error surface rather than the module-private builder. No refusal text moved. Both docblocks now name the two files that would go red, so the parity sentence can be re-run instead of believed — the failure this closes is that the claim had been restated in three places and measured in none (objectui#8925). Co-Authored-By: Claude Opus 5 <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 转绿。这是本轮最好的一份交付,理由在下面逐条给了读数。 裁决区 —— 用读数核R2「⛔ 不许把 docblock 改弱」 —— 我没有读你的措辞就相信,我测了: ⇒ 两处 docblock 是只增不减,两个源文件一个运行时字节都没动。R2 不是被声称满足的,是被测出满足的。 R3 必须断言同样的四个状态 —— 由消融矩阵证明,见下。 ⭐⭐ 消融矩阵 —— 这正是派单要的那份证据,一格不少
派单原话是「补 pin 后两侧都必须红,那才是交付证据」。你给了,而且三个 BEFORE-绿每一格都附了读数( ⭐⭐⭐ 你证伪了卡片自己的测量 —— 而且方向是「陈旧」而非「错误」卡片说 docblock 那句话在四行上都是假的。你测出是三行: 我在源头复核了这条因果链,它成立且时间线是决定性的: ⇒ 卡片的 ⭐⭐⭐ 你把这张卡的教训写进了下一个读者会撞上的地方designer 侧新增的那段,逐字值得引:
⇒ 这张卡真正的病根不是「少了一个 pin」,是一句没人测过的断言被复述了三次,而复述本身成了它的证据。派单要求把这一条写进 PR body;你把它写进了代码。⭐ 两处 docblock 现在都点名「哪两个文件会红」,所以那句话从此可重跑,而不是可相信。 A3 / A5 —— 两项都用了比要求更强的仪器A3(并排 diff 两份 A5(既有 parity gate 读不读消息文本):确认它只判 KEY 名(脚本自己的 NOT COVERED 段写着 "a check on key NAMES only"),并给了带点亮控制项的 grep: ⭐ 你的窄化声明是本轮最严谨的一次
⇒ 「没有任何 app-shell 运行时字节移动,所以任何未跑的 app-shell 测试都不可能因本 diff 改判」—— 这是一个可证伪的窄化理由。⛔ 不是一句托辞。(plugin-designer 未窄化,19/19 全跑。) 记录,不是问题
⏸️ 为什么暂不 arm⛔ 我不在 test shard 跑完前 arm。今天 13:47 我在一份还在跑的 CI 上给 objectui#9169 下了落地判断,十分钟后 Generated by Claude Code |
Fixes #8925
app-shell'sdescribeUnusableTargetdocblock said the two copies of theblank-target refusal "cannot drift" because each pin asserts the same four
states. The designer side had no such pin. This adds it, and both docblocks
now name the two files that would go red, so the sentence can be re-run instead
of believed.
The claim was restated three times and measured none of them
This is the part worth carrying out of this card. The same unmeasured assertion
appears in four places, and every one of them is a restatement of the one
before it:
both writers' test files".
MetadataService.ts'sdescribeUnusableTargetdocblock — "Both are pinned,and each pin asserts the same four states so the copies cannot drift."
MetadataFieldsPage.tsx'sdescribeUnusableTargetdocblock — "both pinsassert all four states so the copies cannot drift silently."
Nothing in that chain was a reading. Repetition is not measurement. Both
docblocks now carry an explicit instruction not to restate the parity claim
without naming the files that make it falsifiable.
What was measured — per-branch ablation, both writers, before and after
Method, per row: replace exactly one branch of that writer's
describeUnusableTargetwith a marker sentence, prove the edit reached disk(anchor count down by one, marker count 0 to 1, worktree blob moved, HEAD blob
unmoved), run the tests, then restore and prove the restore by state
(
git diff HEADempty, worktree blob back to the HEAD blob) rather than by anexit code. Harness ran under
trap ... EXIT INT TERMwith absolute paths.Measured on
6214db63f(before) and5143af2b6(after).absentnon-stringemptywhitespace-onlypnpm exec vitest run packages/app-shell/src/services/MetadataService.specKeyReference.test.tspnpm exec vitest run packages/plugin-designer/(whole package)The three GREEN cells are the finding, and they are readings rather than
counts: the same mutation on the sibling writer turned its pin red every time,
so the instrument was lit on every row. Each green designer run reported
Test Files 19 passed (19) / Tests 149 passed (149)with a branch of auser-visible refusal replaced by a marker string.
The
whitespace-onlyrow was already red before this change, whichnarrows the card's own premise: the designer side was not wholly unpinned. That
row is covered by
MetadataFieldsPage.carriedThroughReference-8896.test.tsx,through the words "whitespace names no object" — a card about a different
subject, which is why it caught one row and no other. So the docblock sentence
was false on three rows of four, not four of four.
Why the card read four of four: its ablation ran on the objectui#8897 branch
and reported
18 files, 142 testsfor the designer package.maincarries19 files, 149 tests— the extra file is exactlyMetadataFieldsPage.carriedThroughReference-8896.test.tsx, landed by PR #8918at
2026-09-10T01:16:15Z, 45 minutes before this card was filed at2026-09-10T02:01:14Z. The card's reading was true of the branch it was takenon and already one file stale against
mainwhen it was written. Bothtimestamps sit inside this checkout's history window (its boundary is
2026-09-08T21:17:21Z), so they are readings rather than shallow-cloneartifacts.
After the pin, every AFTER cell names the new block by test title, e.g.
MetadataFieldsPage.specKeyReference.test.tsx > objectui#7714 · the refusal message distinguishes the four states > empty string .... Thewhitespace-onlyAFTER run reports two failing files: the new pin and the8896 test.
Are the two writers word-for-word the same today?
Yes — measured, not assumed. Both functions were extracted and executed
against five probes (
undefined,'',42,null,' '); all fiveoutputs are byte-identical between the two writers (214 / 229 / 290 / 286 / 427
chars). A control leg that perturbs one word in the designer copy makes the
comparator report
DIVERGED, so theIDENTICALreading is a real one. Nodivergence was fossilised by this pin.
The existing designer parity gate does not read message text
scripts/check-designer-field-key-parity.mjscompares key sets betweenstatically declared payload shapes and the installed spec schema. Its own
"NOT COVERED" section states it: "This gate is a check on key NAMES only."
Greps over that file:
describeUnusableTarget0 hits, "refusal banner" 0 hits,against lit controls in the same file (
FieldSchema27,PAYLOAD_SHAPES7).Prose parity is out of its reach, and nothing here tries to put it there.
Why the page and not the function
describeUnusableTargetis module-private on both sides. The new cases drivethe page the way an author does — through the fire-and-forget
onFieldsChangethe designer calls — and read the text back out of the page's error surface, so
a correct sentence that never reaches the banner cannot pass. Each case also
asserts
putsstayed empty: a diagnosed message is only worth something if italso stopped the write.
Verification
pnpm exec vitest run packages/plugin-designer/— 19 files / 153 testspassed, exit 0 (was 149; the four new cases are the delta).
pnpm exec vitest run packages/app-shell/src/services/MetadataService.specKeyReference.test.ts— 18 tests passed, exit 0.
pnpm --filter @object-ui/plugin-designer --filter @object-ui/app-shell run type-check— exit 0, after
pnpm --filter '@object-ui/plugin-designer^...' --filter '@object-ui/app-shell^...' build.tsc -p tsconfig.test.json --listFilesconfirms the new test file is in theprogram (1 hit; control: an app-shell source file, 0 hits).
pnpm --filter @object-ui/plugin-designer --filter @object-ui/app-shell run lint— exit 0 (warnings only, pre-existing baseline: 65 and 2966).
check:changeset-presence(declares the empty-frontmatterchangeset),
check:control-bytes,check:new-line-citations,check:designer-field-key-parity,check:installed-pin-claims,check:changeset-claims,changeset:check,check:comment-mask-corpus,check:vi-mock-specifiers,check:unreferenced-sources,check:spec-symbols.node scripts/check-governed-queue-guard.mjs --teston all three editedpaths: NOT GOVERNED. This PR is still left as a draft, per its dispatch.
Declared narrowing — the app-shell suite
The full
packages/app-shell/run exceeds this environment's foregroundceiling, so 1 of its 684 collected test files was run rather than all of them.
Three readings, not an assertion:
pnpm exec vitest list --filesOnly packages/app-shell/, i.e. from vitest'sown project config, not from a guess.
per-state fragment of this message. The reader enumeration was done over the
tree (
whitespace names no object,instead of an object name,TRIMMED value,holds a number instead,holds null instead,naming the object it links to) acrosspackages/,apps/andscripts/.emitted-code level rather than by reading the diff: transpiling the base and
head versions of
MetadataService.tswith comments removed givesbyte-identical output (sha
03878c4dc6e5e230both sides), and the same forMetadataFieldsPage.tsx(f0dd45f85e86f5c6). A lit control — the test file,which really did change — reads
CHANGED. Since no app-shell runtime bytemoved, no untouched app-shell test can change verdict because of this diff.
plugin-designerwas not narrowed: the whole package ran, 19 of 19 files.CI runs the full farm for both.
Changeset
.changeset/8925-designer-refusal-four-state-pin.md, empty frontmatter — therepo's first-class "releases nothing" declaration. Tests and comments only; no
refusal text moved, and the emitted-code proof above is the evidence for that.
🤖 Generated with Claude Code
https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
Generated by Claude Code