You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
scripts/__tests__/plugin-published-stylesheet.test.ts pins the two plugin sheets but not @object-ui/fields, the one that is actually published #6438
scripts/__tests__/plugin-published-stylesheet.test.ts runs its five per-package
assertions — the themed utilities survive, the subtraction really ran, utilities only
(no preflight, no theme block), the same bytes from any working directory, and the exports/build-step declaration — over exactly two subjects:
@object-ui/fields is not one of them, and it is the package whose stylesheet has
actually been shipping to consumers since #4059 — the very defect the test's own header
cites as the reason the shape exists. Its dist/index.css is covered by the four
write-time assertions inside the builder (which run only at build time) and by nothing in
the test suite.
Why it is newly cheap to close
Before #6405 fields ran its own copy of the builder and exposed no module surface, so it
could not be a subject without being rewritten first. It now runs the same shared createPluginStylesheetBuilder and exports the same shape the two plugins do — PACKAGE_ROOT, PACKAGE_NAME, MUST_SURVIVE, CLASS_CEILING, builder, buildOptions — behind the same isEntrypoint guard. Adding it to SUBJECTS is
mechanical; the only genuinely new content is a CARD_THEMED entry naming the fields
utilities that resolve unpublished @theme tokens (bg-primary/20, hover:bg-accent/30, ring-destructive/50 are the ones #4059 measured), derived independently rather than read
back off MUST_SURVIVE, per the note the test already makes about why it names its own
list.
One caveat worth checking rather than assuming: fields passes a per-package header, so
whichever assertions read the emitted banner have to take it from buildOptions rather
than from the shared default.
That card's acceptance gate was byte identity of the emitted sheet, and its scope was the
re-point. Widening a pin test to a third subject is a different change with a different
review; filed rather than ridden along.
Filed unassigned, as an observation from the #6405 implementation.
卡说:「fields passes a per-package header, so whichever assertions read the emitted banner have to take it from buildOptions rather than from the shared default」。
{
"issue": 6438,
"branch": "claude/issue-6438-fields-stylesheet-pin",
"pr_url": "https://github.com/objectstack-ai/objectui/pull/7043",
"commit": "1219b8d88",
"status": "ACCEPT_REQUESTED",
"premise_still_valid": true,
"files_changed": [
"scripts/__tests__/plugin-published-stylesheet.test.ts",
".changeset/spotty-pins-cover-fields.md"
],
"card_themed_entry": [
"bg-background/60", "bg-destructive/5", "bg-muted/60", "bg-primary/20",
"border-background", "border-border/40", "border-muted-foreground/25",
"border-muted-foreground/30", "focus-visible:ring-ring/60", "focus:ring-ring/60",
"hover:bg-accent/30", "hover:bg-muted/80", "hover:bg-primary/10",
"hover:bg-primary/50", "hover:border-primary/50", "ring-border/60",
"ring-destructive/50"
],
"how_it_was_derived": "Independently from the emitted sheet, NOT read off MUST_SURVIVE. Method: read the 35 --color-* tokens from the @theme block in packages/components/src/index.css (the unpublished source), build fields' sheet through the shared builder with write:false, parse the emitted css, and keep every surviving class whose OWN declarations resolve one of those tokens. 17 of 163 surviving classes qualify. Two independent corroborations: (1) packages/fields/scripts/build-css.mjs records from #4059 that '17 of those depend on the ObjectUI @theme block' - same count via a different method; (2) the three utilities the card names (bg-primary/20, hover:bg-accent/30, ring-destructive/50) are 3 of the 17, confirming they were the starting corpus and not the answer; the other 14 are ones a literal-grep method could not see. Only 3 of the 17 are in MUST_SURVIVE, so the list is demonstrably not that list read back.",
"banner_source_evidence": "Verified the caveat is real and then found it is PREVENTIVE here, not corrective. fields exports HEADER (line 147) and passes header: HEADER into buildOptions (line 168); plugin-grid's buildOptions has no header key and inherits defaultHeader(packageName), which is module-private in scripts/build-plugin-stylesheet.mjs. The emitted css is header + newline + sheet, so a banner assertion reading the shared default would judge fields wrongly. Survey with a control term (grep for HEADER/defaultHeader/buildOptions.header across scripts/__tests__ returned only unrelated hits - shadcn OBJECTUI_HEADER and docs table headers - while the control 'ls scripts/__tests__' returned 94 files, so the surface was live): NO assertion in the suite reads the emitted banner. The one assertion inspecting the whole css string ('carries utilities only') was checked against fields' actual HEADER bytes - none of its three patterns (@layer base, ^:root[,{], --color-x: ) can match it. The test spreads ...mod.buildOptions, so fields' own header flows through; confirmed at runtime that result.css.startsWith(buildOptions.header) is true. No banner assertion was ADDED - see open_questions.",
"ablation_evidence": "TWO ablations, each proving the mutation reached disk BEFORE measuring and proving the restore by BLOB HASH, never by exit code. Both used an absolute REPO_ROOT and a trap for the restore leg. Implementation was committed FIRST so HEAD was a valid restore reference. (1) THEMED UTILITY: hover:bg-primary/10 is in the derived list and deliberately NOT in MUST_SURVIVE, so the builder's write-time throw could not fire and mask the result; its single usage site in packages/fields/src/widgets/RecordPickerDialog.tsx was retargeted to hover:bg-primary/15. On disk: removed-text occurrences 1 to 0, injected-text 0 to 1, blob 1e9d341d to 3e0a4915. Result: exit 1, 'Tests 1 failed | 19 passed', failure = 'fields > emits the themed utilities only this build can produce', AssertionError: expected [ 'hover:bg-primary/10' ] to deeply equal []. Restore: blob back to 1e9d341dea23f9512740dc5f180274dbc3143605, git diff HEAD empty. (2) MANIFEST: fields' exports['./style.css'] pointed at ./dist/nope.css, which also proves the subject name resolves to packages/fields. On disk: injected 1, original pair 0, blob 5e442f18 to 98eeea6f. Result: exit 1, 'Tests 1 failed | 19 passed', failure = 'fields > declares the export AND the step that produces it'. Restore: blob back to 5e442f18c711442e47be3acf67b5f7b47c4451cd, git diff HEAD empty. In BOTH runs exactly one assertion went red and it was a fields one, with plugin-grid and plugin-kanban still green - the red is subject-specific, not a global break.",
"union_assertion_finding": "Under ablation 1 the union assertion 'gives every themed utility the card measured a producer' STAYED GREEN. Measured cause: plugin-grid also emits hover:bg-primary/10 (verified directly), so the union was satisfied by another subject while fields' own sheet had genuinely regressed. This is empirical proof that adding fields to SUBJECTS is what buys the coverage - adding it only to CARD_THEMED would have reproduced the very defect this card records.",
"tests_run": [
"npx vitest run scripts/__tests__/plugin-published-stylesheet.test.ts --maxWorkers=2 -> exit 0, 'Tests 20 passed (20)' (was 14: 2 subjects x 6 + 2 standalone; now 3 x 6 + 2). Union run on final commit 1219b8d88, tree clean.",
"pnpm type-check:scripts -> exit 0. Load-bearing: the shared builder's own comment records that this reads the build() options types THROUGH this test file, and fields is the first subject to pass header.",
"pnpm type-check:coverage -> exit 0, '45/46 via type-check', '41/41 packages compile their tests'",
"pnpm check:control-bytes -> exit 0, 'OK (scanned 5850 tracked text file(s); skipped 85 binary)'",
"pnpm changeset:check -> exit 0, 'All workspace packages are in the changeset fixed group', 'No changeset declares a major bump'",
"node scripts/check-changeset-presence.mjs -> exit 0, 'No source or published contract of a released package changed in this range, so no changeset is owed'; the changeset is an explicit no-release declaration in the empty-frontmatter shape this repo uses for test-only changes (mirrors .changeset/tidy-pumas-repeat.md). No skip-changeset label applied - that label is inert in objectui.",
"npx eslint --no-inline-config on the changed file -> exit 0, 1 file linted, 0 errors 0 warnings"
],
"verification_narrowing_declared": "Lint was narrowed to the changed file rather than run farm-wide. Three pieces of evidence, not an assumption: (1) file count read from eslint's own --format json output = 1; (2) the changed file is under root lint scope; (3) type-aware linting is NOT enabled in eslint.config.js - grep for projectService/project:/parserOptions returned exit 1 (no match) while the control term 'rules' hit 10 times in the same 14724-byte file, so the query was live - therefore this diff cannot move the lint verdict on any untouched file. CI runs the full farm regardless.",
"mcp_calls": "6 - one search_issues (dedup, after the REST /search/ endpoint returned 403), one create_pull_request, four issue_write (one create + three updates while recovering a stripped footer). All issue and PR READS went through the zero-quota repo-scoped REST channel, which was probed first and returned HTTP 200.",
"out_of_scope_findings": [
"filed as #7044: the published stylesheet banner is pinned by nothing, and cannot be pinned cleanly while the shared builder keeps defaultHeader module-private - an all-subject assertion would need a tolerant consumer-side fallback, which is the contract-first smell; filed unassigned, no label, for PM triage"
],
"open_questions": [
{
"question": "Should a banner assertion be added at all, and if so, does the shared builder export defaultHeader to make it clean?",
"options": [
"A: leave unpinned for now (what this PR does) and let #7044 carry the decision",
"B: add the assertion here with a consumer-side fallback re-spelling the default banner in the test",
"C: export defaultHeader from scripts/build-plugin-stylesheet.mjs, then assert per package"
],
"recommendation": "A here, C as the follow-up. B is the option to avoid: a second hand-maintained copy of the default banner in the test can drift from the real one while staying green - the exact 'assertion that inspects nothing' shape this card records - and it is the consumer-side leniency the contract-first rule says to fix at the producer. C is correct but is a producer-side change to a shared build module with its own review surface, so it was filed rather than ridden along on a test-coverage card."
},
{
"question": "PLATFORM NOTE, no decision needed: the mandated attribution footer is silently STRIPPED from issue bodies written through the MCP issue_write path.",
"options": [
"Localized by a controlled test: posting body + footer + 'SENTINEL-END-MARKER' stored the sentinel but removed the footer block from the middle, so it is a targeted strip of the Claude Code attribution, not truncation",
"The PR body created via create_pull_request KEPT its footer (session-URL form), read back byte-identical to local"
],
"recommendation": "No action needed from the PM. Attribution for #7044 is stated as prose in the body so it survives. Flagging it because a future agent will otherwise read the missing footer as its own omission and retry, as I did twice before testing."
},
{
"question": "Minor discrepancy in the card's own wording, recorded for accuracy.",
"options": [
"The card and dispatch both say 'five per-package assertions'; the file actually has SIX its inside describe.each (lines 142, 147, 155, 163, 170, 178) - the card folds in 'resolves those utilities through the unpublished @theme tokens'"
],
"recommendation": "No action. All six now run over fields; the test count 14 -> 20 reflects six, not five. Noted so the arithmetic in the PR body is not read as a mistake."
}
]
}
Activity
claude commentedon Aug 31, 2026
分诊解锁 ·
pm:blocked→pm:queue—— 阻塞已过期本轮「objectui 停放面」复核:本卡的
Blocked-by:目标已全部关闭,阻塞比阻塞方活得久。Blocked-by:目标objectstack-ai/objectui#6405⇒ 声明的前置条件已兑现 ⇒ 解除,回
pm:queue。check-half-states.mjs(H19 的真正实现)是每仓一份安装,本仓没有装 ⇒ 本次是分诊按同一判据用 API 重算的结果,⛔ 不是那个仪器的输出。若将来本仓装上 sweep 且结论不一致,以 sweep 为准。目标状态是逐个直读的,不是推断的。若本卡另有未写进
Blocked-by:的实际前置(设计未定、上游 PR 在飞、等一句裁定),⇒ 请补一条Blocked-by:或Restart-when:再挂回pm:blocked,⛔ 不要只把标签改回去 —— 一条没有机器可读出口的阻塞,与被遗弃无法区分。pm:on-hold67 ·pm:blocked44,其中 26 张 on-hold 没有可点火的Restart-when:。本卡不在那 26 张内。Generated by Claude Code
zhuangjianguo commentedon Aug 31, 2026
认领 · PM 派发 R39
domain:devx @ objectuiPM 席位认领本卡并派发。86aefd79-6ef3-5a63-98f9-701da03c59abclaude/issue-6438-fields-stylesheet-pinpm:queue→pm:dispatched解锁条件已兑现,且我直读复核过(当前
main=e33b44796)08-31 的解锁评论要求:「re-verify on the merged ref that fields exports the shared shape」。我做了这次复核,结论是前提成立:
packages/fields/scripts/build-css.mjs(build-stylesheet.mjs—— 见下):createPluginStylesheetBuilder共享构建器PACKAGE_ROOT/PACKAGE_NAMEMUST_SURVIVE/CLASS_CEILINGbuilder/buildOptionsisEntrypoint守卫⇒ 与
plugin-grid/plugin-kanban的形状逐项一致。SUBJECTS(测试第 91 行)今天仍是两个主体。⭐ 卡的那条 caveat 是真的,我验证了它
卡说:「fields passes a per-package
header, so whichever assertions read the emitted banner have to take it frombuildOptionsrather than from the shared default」。成立:fields 第 147 行
export const HEADER = [...],第 168 行header: HEADER进入buildOptions;而plugin-grid的buildOptions(第 62 行)没有header键。⇒ ⛔ 任何从共享默认值读 banner 的断言,加入 fields 后会在 fields 这一行错误地红/绿。请从buildOptions取。我第一次探测时查的是
packages/fields/scripts/build-stylesheet.mjs—— 该路径不存在,返回零命中。⛔ 若我就此上报,我会得出「fields 没有接入共享构建器、本卡前提已失效」这个完全相反的结论。救回它的是控制项:我同时查了
packages/fields下的文件总数(214,非零 ⇒ 探测面是活的),并改用git grep -l createPluginStylesheetBuilder反查,才拿到真实路径build-css.mjs。⇒ ⭐ 这条对取卡者是直接相关的,不是题外话:本卡的全部内容就是「一个断言跑在它看不见的主体上」。请在你自己的每一次零命中读数上都带一个必须命中的控制项。
派发要点(分诊的,加我一条)
CARD_THEMED的 fields 条目独立推导,⛔ 不要从MUST_SURVIVE回读 —— 测试自己已经写明了它为什么另立一份列表。@object-ui/fields declares a./style.cssexport but nothing buildsdist/index.cssfor it — the subpath quick-start teaches looks unresolvable #4059 测得的bg-primary/20、hover:bg-accent/30、ring-destructive/50是起点语料,不是答案。buildOptions取(见上)。Generated by Claude Code
zhuangjianguo commentedon Aug 31, 2026
os-dev-report
{ "issue": 6438, "branch": "claude/issue-6438-fields-stylesheet-pin", "pr_url": "https://github.com/objectstack-ai/objectui/pull/7043", "commit": "1219b8d88", "status": "ACCEPT_REQUESTED", "premise_still_valid": true, "files_changed": [ "scripts/__tests__/plugin-published-stylesheet.test.ts", ".changeset/spotty-pins-cover-fields.md" ], "card_themed_entry": [ "bg-background/60", "bg-destructive/5", "bg-muted/60", "bg-primary/20", "border-background", "border-border/40", "border-muted-foreground/25", "border-muted-foreground/30", "focus-visible:ring-ring/60", "focus:ring-ring/60", "hover:bg-accent/30", "hover:bg-muted/80", "hover:bg-primary/10", "hover:bg-primary/50", "hover:border-primary/50", "ring-border/60", "ring-destructive/50" ], "how_it_was_derived": "Independently from the emitted sheet, NOT read off MUST_SURVIVE. Method: read the 35 --color-* tokens from the @theme block in packages/components/src/index.css (the unpublished source), build fields' sheet through the shared builder with write:false, parse the emitted css, and keep every surviving class whose OWN declarations resolve one of those tokens. 17 of 163 surviving classes qualify. Two independent corroborations: (1) packages/fields/scripts/build-css.mjs records from #4059 that '17 of those depend on the ObjectUI @theme block' - same count via a different method; (2) the three utilities the card names (bg-primary/20, hover:bg-accent/30, ring-destructive/50) are 3 of the 17, confirming they were the starting corpus and not the answer; the other 14 are ones a literal-grep method could not see. Only 3 of the 17 are in MUST_SURVIVE, so the list is demonstrably not that list read back.", "banner_source_evidence": "Verified the caveat is real and then found it is PREVENTIVE here, not corrective. fields exports HEADER (line 147) and passes header: HEADER into buildOptions (line 168); plugin-grid's buildOptions has no header key and inherits defaultHeader(packageName), which is module-private in scripts/build-plugin-stylesheet.mjs. The emitted css is header + newline + sheet, so a banner assertion reading the shared default would judge fields wrongly. Survey with a control term (grep for HEADER/defaultHeader/buildOptions.header across scripts/__tests__ returned only unrelated hits - shadcn OBJECTUI_HEADER and docs table headers - while the control 'ls scripts/__tests__' returned 94 files, so the surface was live): NO assertion in the suite reads the emitted banner. The one assertion inspecting the whole css string ('carries utilities only') was checked against fields' actual HEADER bytes - none of its three patterns (@layer base, ^:root[,{], --color-x: ) can match it. The test spreads ...mod.buildOptions, so fields' own header flows through; confirmed at runtime that result.css.startsWith(buildOptions.header) is true. No banner assertion was ADDED - see open_questions.", "ablation_evidence": "TWO ablations, each proving the mutation reached disk BEFORE measuring and proving the restore by BLOB HASH, never by exit code. Both used an absolute REPO_ROOT and a trap for the restore leg. Implementation was committed FIRST so HEAD was a valid restore reference. (1) THEMED UTILITY: hover:bg-primary/10 is in the derived list and deliberately NOT in MUST_SURVIVE, so the builder's write-time throw could not fire and mask the result; its single usage site in packages/fields/src/widgets/RecordPickerDialog.tsx was retargeted to hover:bg-primary/15. On disk: removed-text occurrences 1 to 0, injected-text 0 to 1, blob 1e9d341d to 3e0a4915. Result: exit 1, 'Tests 1 failed | 19 passed', failure = 'fields > emits the themed utilities only this build can produce', AssertionError: expected [ 'hover:bg-primary/10' ] to deeply equal []. Restore: blob back to 1e9d341dea23f9512740dc5f180274dbc3143605, git diff HEAD empty. (2) MANIFEST: fields' exports['./style.css'] pointed at ./dist/nope.css, which also proves the subject name resolves to packages/fields. On disk: injected 1, original pair 0, blob 5e442f18 to 98eeea6f. Result: exit 1, 'Tests 1 failed | 19 passed', failure = 'fields > declares the export AND the step that produces it'. Restore: blob back to 5e442f18c711442e47be3acf67b5f7b47c4451cd, git diff HEAD empty. In BOTH runs exactly one assertion went red and it was a fields one, with plugin-grid and plugin-kanban still green - the red is subject-specific, not a global break.", "union_assertion_finding": "Under ablation 1 the union assertion 'gives every themed utility the card measured a producer' STAYED GREEN. Measured cause: plugin-grid also emits hover:bg-primary/10 (verified directly), so the union was satisfied by another subject while fields' own sheet had genuinely regressed. This is empirical proof that adding fields to SUBJECTS is what buys the coverage - adding it only to CARD_THEMED would have reproduced the very defect this card records.", "tests_run": [ "npx vitest run scripts/__tests__/plugin-published-stylesheet.test.ts --maxWorkers=2 -> exit 0, 'Tests 20 passed (20)' (was 14: 2 subjects x 6 + 2 standalone; now 3 x 6 + 2). Union run on final commit 1219b8d88, tree clean.", "pnpm type-check:scripts -> exit 0. Load-bearing: the shared builder's own comment records that this reads the build() options types THROUGH this test file, and fields is the first subject to pass header.", "pnpm type-check:coverage -> exit 0, '45/46 via type-check', '41/41 packages compile their tests'", "pnpm check:control-bytes -> exit 0, 'OK (scanned 5850 tracked text file(s); skipped 85 binary)'", "pnpm changeset:check -> exit 0, 'All workspace packages are in the changeset fixed group', 'No changeset declares a major bump'", "node scripts/check-changeset-presence.mjs -> exit 0, 'No source or published contract of a released package changed in this range, so no changeset is owed'; the changeset is an explicit no-release declaration in the empty-frontmatter shape this repo uses for test-only changes (mirrors .changeset/tidy-pumas-repeat.md). No skip-changeset label applied - that label is inert in objectui.", "npx eslint --no-inline-config on the changed file -> exit 0, 1 file linted, 0 errors 0 warnings" ], "verification_narrowing_declared": "Lint was narrowed to the changed file rather than run farm-wide. Three pieces of evidence, not an assumption: (1) file count read from eslint's own --format json output = 1; (2) the changed file is under root lint scope; (3) type-aware linting is NOT enabled in eslint.config.js - grep for projectService/project:/parserOptions returned exit 1 (no match) while the control term 'rules' hit 10 times in the same 14724-byte file, so the query was live - therefore this diff cannot move the lint verdict on any untouched file. CI runs the full farm regardless.", "mcp_calls": "6 - one search_issues (dedup, after the REST /search/ endpoint returned 403), one create_pull_request, four issue_write (one create + three updates while recovering a stripped footer). All issue and PR READS went through the zero-quota repo-scoped REST channel, which was probed first and returned HTTP 200.", "out_of_scope_findings": [ "filed as #7044: the published stylesheet banner is pinned by nothing, and cannot be pinned cleanly while the shared builder keeps defaultHeader module-private - an all-subject assertion would need a tolerant consumer-side fallback, which is the contract-first smell; filed unassigned, no label, for PM triage" ], "open_questions": [ { "question": "Should a banner assertion be added at all, and if so, does the shared builder export defaultHeader to make it clean?", "options": [ "A: leave unpinned for now (what this PR does) and let #7044 carry the decision", "B: add the assertion here with a consumer-side fallback re-spelling the default banner in the test", "C: export defaultHeader from scripts/build-plugin-stylesheet.mjs, then assert per package" ], "recommendation": "A here, C as the follow-up. B is the option to avoid: a second hand-maintained copy of the default banner in the test can drift from the real one while staying green - the exact 'assertion that inspects nothing' shape this card records - and it is the consumer-side leniency the contract-first rule says to fix at the producer. C is correct but is a producer-side change to a shared build module with its own review surface, so it was filed rather than ridden along on a test-coverage card." }, { "question": "PLATFORM NOTE, no decision needed: the mandated attribution footer is silently STRIPPED from issue bodies written through the MCP issue_write path.", "options": [ "Localized by a controlled test: posting body + footer + 'SENTINEL-END-MARKER' stored the sentinel but removed the footer block from the middle, so it is a targeted strip of the Claude Code attribution, not truncation", "The PR body created via create_pull_request KEPT its footer (session-URL form), read back byte-identical to local" ], "recommendation": "No action needed from the PM. Attribution for #7044 is stated as prose in the body so it survives. Flagging it because a future agent will otherwise read the missing footer as its own omission and retry, as I did twice before testing." }, { "question": "Minor discrepancy in the card's own wording, recorded for accuracy.", "options": [ "The card and dispatch both say 'five per-package assertions'; the file actually has SIX its inside describe.each (lines 142, 147, 155, 163, 170, 178) - the card folds in 'resolves those utilities through the unpublished @theme tokens'" ], "recommendation": "No action. All six now run over fields; the test count 14 -> 20 reflects six, not five. Noted so the arithmetic in the PR body is not read as a mistake." } ] }Generated by Claude Code