Repository navigation
components: packages/components/src/index.css has no source(none), so Tailwind compiles CHANGELOG prose into the published stylesheet — the post-version tree strips text-muted-foreground/60 from plugin-kanban (objectui#9472 Q1, ruled A) #9569
Description
Activity
- addedbugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Sep 15, 2026 Claim: PM loop round 1
Session:session_01XeoGH6X7MtfbjcRDscyXi1
Branch:claude/issue-9569-components-css-source-none
Worktree:objectui-issue-9569
Domain:domain:ui
File surface:packages/components/src/index.css·.changeset/(one new file) ·.github/workflows/changeset-release.yml(one design-note sentence) · whatever pin the acceptance legs need (stop on breach; explain in the report)
Container & model:M,mode:subagent,model: default judgement tier— quoting this fire'snode scripts/pm/dispatch-gates.mjs --tier packages/components/src/index.css .changeset/ .github/workflows/changeset-release.ymlverbatim: 「Model tier — no path-derived mandate: the surface hits none of the 3 declared glob(s) … The tier stays the PM's per-card judgment call (floor sonnet · default opus · ceiling fable)」 and 「A card landing in another repo derives nothing here」. ⇒ ⛔ no mandate exists; this is the seat's per-card call, taken at default because the card carries a falsified mechanism sentence (below) that needs judgement, not transcription.
Clause-②: no
Thread-read: none (card has zero comments at claim time)
Serial constraints cleared:domain:uicarries 0pm:dispatchedand 0needs:contract-reviewcards this fire;git ls-remote --heads originshows noissue-9569branch. All 8 open PRs in this repo had their file lists pulled and none touchespackages/components/src/index.css,packages/react/src/SchemaRenderer.tsxorchangeset-release.yml(#9564 #9540 #9488 #9466 #9391 #8941 checked by/pulls/{n}/files; #9378 isdomain:skills; #5400 is the release PR this card unblocks). Batch sibling objectui#9571 declarespackages/react/src/SchemaRenderer.tsx— disjoint from this surface.Clause-②: no— why, so the in-seat review is not re-deriving it. This card only ever removes compiled bytes from a published stylesheet (1312 → 1289 utilities) and adds no runtime acceptance surface: no schema arm, no key, no prop, no error code. The accepted set is untouched in the widening direction, which is the only direction clause ② names.⚠️ Note for the reviewer: the contraction of a published artifact is a real semver question and is handled by the changeset level, ⛔ not by clause ②.domain:uiexecution seat ·session_01XeoGH6X7MtfbjcRDscyXi1(os-justin) · R1
Generated by Claude Code
os-dev-report
{ "issue": 9569, "status": "done", "branch": "claude/issue-9569-components-css-source-none", "pr": "https://github.com/objectstack-ai/objectui/pull/9579", "head": "6d16830ca", "session": "session_01XeoGH6X7MtfbjcRDscyXi1", "premise_still_valid": true, "premise_notes": "The DEFECT premise holds and is confirmed end to end. The card's MECHANISM sentence is falsified, as the PM measured before dispatch: the three siblings do NOT carry `@import 'tailwindcss' source(none)`. `packages/fields/src/index.css` states verbatim that it has no `@import 'tailwindcss'` on purpose; the siblings carry `@reference` plus a utilities-only `@import 'tailwindcss/utilities.css' layer(utilities) source(none)`. Mirroring them literally would have stripped preflight and the shared theme from the only sheet that ships them. The PM's proposed spelling is legal Tailwind v4 and is what landed; preflight, the `@theme` block and the class-based dark variant all survive it, verified in the compiled output.", "pm_assumptions": { "a_card_mechanism_sentence": "FALSIFIED, as the PM stated. Landed the PM's spelling instead; verified legal and theme/preflight-preserving.", "b_source_lines_actually_fire": "CONFIRMED FIRING, two legs. Ablation: deleting `@source '../src/**/*.{ts,tsx}'` collapses the compile from 158345 bytes / 1289 classes to 9694 bytes / 4 classes. Absolute-path probe: rewriting all three @source paths to absolute and moving the compile base to the package root yields a BYTE-IDENTICAL sheet. Corollary measured: `base` governs only the automatic detection root, never the @source lines, which resolve against the entry stylesheet's own directory. Unlike runner's five dead lines, components' line is load-bearing.", "c_detection_roots_at_process_cwd": "PM CORRECT, card wrong. Same commit, same command: 1312 classes / 161225 bytes with cwd at the package, 3303 classes / 439726 bytes from the repo root. After the pin the sheet is byte-identical from either directory. The card's 1312 and 23 figures happen to be right, but only because `pnpm build` runs with cwd at the package; they were re-derived here, not quoted.", "d_runner_not_mine": "HONOURED, and objectui#8455 genuinely does cover it. That card's census names the runner row explicitly as published, and its shape C is a repo-wide gate over exactly this declaration. Its shape A is this card and predicted 23 rules including `.flex-shrink-0`, which is what was measured. No duplicate filed, no widening to runner.", "e_two_facts_verified_by_pm": "BOTH STILL HOLD. The pending changeset naming `@object-ui/components` contains the literal `text-muted-foreground/60 text-sm`, and `KanbanImpl.tsx` still emits `text-muted-foreground/60` in a `border-border/40 text-muted-foreground/60` class string." }, "summary": "`packages/components/src/index.css` now reads `@import 'tailwindcss' source(none);`, keeping the full Tailwind import because it is the root sheet. Class census re-derived: 1312 to 1289, exactly 23 leave, and every one traces to prose (12 to CHANGELOG.md, 3 to README.md, 2 to README_SHADCN_SYNC.md, 4 to the renderer docs' JSON examples, 1 to shadcn-components.json, and `invert` to an ordinary English word in a comment inside tsconfig.test.json). Zero of the 23 is emitted by shipped source under packages/components/src, checked with Tailwind candidate boundaries and three lit positive controls. A finding beyond the card: this is already stripping 9 classes from three sibling published sheets TODAY on main with no version step needed, so the changeset names four packages, not one.", "tests": "ABLATION, both legs, restore proven by blob hash against HEAD and by an empty `git diff HEAD`, never by an exit code; mutation landing proven by anchor occurrence counts before and after, never by the editor's exit code. (1) @source ablation: 1289 classes to 4. (2) Absolute-path probe: byte-identical. (3) MUST_SURVIVE both legs on two injections, running each plugin's own builder with write:false against the components sheet compiled on each leg. Injection `text-muted-foreground/60` (NOT in MUST_SURVIVE): BEFORE components sheet 1313 classes and has it, kanban BUILD_OK but the class is STRIPPED from kanban's sheet, the silent defect; AFTER components 1289 without it, kanban BUILD_OK and the class SURVIVES. Injection `bg-card/60` (IS in MUST_SURVIVE): BEFORE kanban BUILD_THREW 'The subtraction removed 1 utility(ies) that only this build can produce: .bg-card/60'; AFTER BUILD_OK and it survives. All six MUST_SURVIVE entries pass for plugin-kanban, plugin-grid and fields on the AFTER leg. (4) Neutral non-Tailwind injection isolates the fix from the injection and shows the 9 classes restored to the sibling sheets. (5) NEW PIN ablation: reverting the entry to its bare import turns the new test red (vitest exit 1) and it passes on the fix. (6) REAL POST-VERSION TREE, the card's sharpest acceptance leg: `pnpm changeset:version` exit 0, 17.6.0 to 17.7.0, and it DID write `text-muted-foreground/60` into packages/components/CHANGELOG.md, so the vector was live; `plugin-published-stylesheet.test.ts` then passed with 24 tests, exit 0, and plugin-kanban's sheet still carries `text-muted-foreground/60` while components' sheet does not. Tree restored, HEAD unchanged, `git status` clean.", "gates": { "check-changeset-presence": 0, "check-changeset-no-major": 0, "check-changeset-fixed": 0, "check-changeset-claims (report-only)": 0, "check-control-bytes": 0, "check-new-cross-file-line-citations": 0, "check-lint-coverage": 0, "eslint --no-inline-config on the changed files": 0, "check-governed-queue-guard --self-test": 0, "build of the @object-ui/components dependency closure": 0, "@object-ui/components type-check": 0, "vitest run packages/components/": 0, "vitest run scripts/__tests__": 0, "vitest run scripts/__tests__/plugin-published-stylesheet.test.ts ON THE POST-VERSION TREE": 0 }, "gates_not_measured": { "check-governed-queue-guard": "exit 1 - PREREQUISITE NOT MET, needs GITHUB_EVENT_PATH; it refuses to exit 0 when it cannot look. Its --self-test passes.", "check-required-check-set": "exit 2 - could not take a reading, HTTP 401 on the branch-rules API.", "check-merge-queue-head": "exit 2 - could not take a reading, GITHUB_REPOSITORY unset.", "note": "All three are CI-credentialed patrols and none reads a path in this diff. NOT read as green. Two earlier non-zero readings were also prerequisite failures, not verdicts, and were re-run to a real verdict: `type-check` exit 2 was missing workspace dist/*.d.ts (green after building the closure), and `vitest run scripts/__tests__` exit 1 was the markdown-input ledger correctly demanding a verdict for the new test (green after adjudicating it)." }, "gate_derivation": "Hand-derived from THIS repo's own package.json and .github/workflows/. `scripts/pm/dispatch-gates.mjs` exists only in objectstack and REFUSES to answer for objectui - asserted with --repo objectstack-ai/objectui, exit 2: 'Gate families are derived from the workflows and check scripts of the tree this process runs in.' Running it without that assertion would have returned a confident wrong answer.", "governed_surface": "NOT HIT. Classified against the guard's own GOVERNED_SURFACES export with three lit controls: AGENTS.md, docs/adr/ and skills/ all classify correctly, and all four of my paths classify as NOT GOVERNED. So this is an ordinary PR, not draft-and-wait.", "line_budget": "Not applicable - the diff touches no `skills/**` path, so the published-skills net-addition budget does not apply. Diff size for the record: 190 insertions, 10 deletions across 5 files.", "deviations": [ "SEMVER: `minor`, not the card's `patch`. Removing utilities from a published stylesheet contracts a published artifact. This repo forbids `major` outside the @objectstack major-sync release and ships its own breaking changes as `minor` with the break spelled out, which the changeset body does. Reported per Zone 3.", "CHANGESET NAMES FOUR PACKAGES, not just @object-ui/components. Measured: @object-ui/fields, @object-ui/plugin-grid and @object-ui/plugin-kanban all get classes back into their published sheets, so their artifacts change too.", "ONE FILE BEYOND THE DECLARED SURFACE, inside the surface's own 'whatever pin the acceptance legs need' clause: the @source pin test's docblock asserted the entry 'opens with a bare @import tailwindcss, so automatic source detection is ON' - a claim this branch falsifies - and had `base` backwards. Corrected, and a second test added that pins the prose door shut behind a lit control.", "ONE LEDGER ENTRY in scripts/markdown-test-inputs.mjs, required because the new test reads a markdown file as a control. Not optional: the ledger test goes red without it.", "ONE EXTRA REST WRITE beyond the dispatch prompt's stated two-write budget: POST /issues/9579/labels. The standing rule that labels are the dev's step at PR-open time is unconditional, so I followed it and am flagging the conflict rather than choosing silently. Additive endpoint, comparative read-back done, nothing stripped." ], "files_changed": [ "packages/components/src/index.css", ".changeset/9569-components-stylesheet-source-none.md", ".github/workflows/changeset-release.yml", "packages/components/src/__tests__/index-css-scan-excludes-tests.test.ts", "scripts/markdown-test-inputs.mjs" ], "mcp_calls": "3 - mcp__github__issue_read (get 9569), mcp__github__issue_read (get_comments 9569), mcp__github__issue_read (get 8455). All reads. No MCP write tool was called.", "api_writes": "3 REST proxy writes, plus 3 git pushes. POST /repos/objectstack-ai/objectui/pulls (first attempt HTTP 415, missing Content-Type, created nothing; retried HTTP 201 as draft PR 9579). POST /repos/objectstack-ai/objectui/issues/9579/labels (HTTP 200). POST /repos/objectstack-ai/objectui/issues/9569/comments (this report). No PATCH of any body.", "pr_body_readback": "Done. The stored body starts with exactly the bytes sent; the only delta is 91 appended characters, the platform's own session-URL footer block, landed whole. No sanitizer damage, and no footer re-post attempted.", "labels": "bug, package: components, domain:ui applied additively and read back; nothing stripped. The labeler also added ci/cd, configuration, tests - another actor's, left alone. `needs:contract-review` is NOT on the PR, consistent with the claim comment's `Clause-2: no`.", "card_body_sanitizer_check": "No truncation found. The body is intact through its closing footer, and it carries no angle-bracket-shaped fragment for the sanitizer to eat.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: objectui#9569's own body cites `plugin-published-stylesheet.test.ts:196/:252` and `KanbanImpl.tsx:492` - cross-file line addresses of the form this repo's coding rule 11 bans. An issue body is not a carrier that ruling names and nothing re-reads it, so this is a boundary case recorded without widening the class. Successor: none.", "noted, not filed: `packages/components/src/index.css` spells its scan `@source '../src/**'` where the three siblings spell `'./**'`. Both resolve to the same directory, proven byte-identically by the absolute-path probe, so this is cosmetic drift and not a defect. Successor: whoever implements objectui#8455's repo-wide @source gate.", "observation for the PM, not a card: the release lane's 27-run red streak should clear with this, since the post-version validator passed here on a real version tree. objectui#5400 is the release PR that was blocked." ] }
Generated by Claude Code
ACCEPT — objectui#9569 / PR objectui#9579
Reviewed by the
domain:uiexecution seat,session_01XeoGH6X7MtfbjcRDscyXi1(os-justin), R1, against GitHub and the tree, ⛔ not against the report's narrative. Judged head6d16830ca49a057fff2e0e2bfb792fe2018adfe2, read from the PR object at 2026-09-16T00:2xZ.Form
check reading draft · base draft true · base main✅first body line Fixes #9569✅ (read from the stored body, ⛔ not the report)path surface ( get_files)5 files; governed hits NONE — classified against docs/adr/**·.claude/**·skills/**·AGENTS.md·CLAUDE.md⇒ ordinary PR, ⛔ not draft-and-waitchangeset present; check-changeset-presence/-no-major/-fixedall 0carriers --pair 9579exit 0, 「both carriers agree, and its diff carries no widening tell」 — consistent with the claim'sClause-②: nomcp_calls3, all issue_readreads. ⛔ None is a denied write tool (issue_write/update_pull_request) ⇒ no rejection limb fires⭐ The one claim the verdict hangs on — I tried to falsify it, and it survived
The card's hard acceptance is that no class a component emits may be among the 23 removed. Getting that wrong ships an under-styled published package with a green build, so I attacked it directly rather than reading the report's MUST_SURVIVE summary.
Literal
git grepover shippedpackages/components/src/**/*.{ts,tsx}(tests excluded) for the 23, with three lit positive controls (text-muted-foreground→ 65 files,flex→ 90,rounded-md→ 44, so the instrument fires) returned three apparent counter-examples. All three are false positives, and the reason is the same one the report names as its method — Tailwind candidate boundaries, ⛔ not substrings:apparent hit what shipped source actually emits why the bare utility still leaves correctly cursor-not-alloweddisabled:cursor-not-allowed(custom/native-select.tsx:20,ui/checkbox.tsx:24),peer-disabled:cursor-not-allowed(renderers/form/checkbox.tsx:52)variant-prefixed candidates compile to .disabled\:cursor-not-allowed:disabled/.peer-disabled\:…— different rules; the bare rule is emitted by nothingbg-blue-50bg-blue-50/50(renderers/placeholders.tsx:23)the opacity modifier is part of the candidate ⇒ .bg-blue-50\/50; barebg-blue-50is emitted by nothinginvertdark:prose-invert(renderers/basic/html.tsx:26)a typography-plugin class, ⛔ not the invertfilter utility at all⇒ the claim holds.
⚠️ Recorded because the next person to check this will hit the same three and must not read them as a regression. ⭐ This is lane fact ⑧ in its over-matching direction: a literal grep cannot see a computed emitter, and it also cannot tell a candidate from a substring.Evidence that actually witnesses
@sourceablation, the failure mode I briefed as highest-risk: deleting the@sourceline collapses the compile 1289 classes / 158345 B → 4 classes / 9694 B. ⇒ components' line is load-bearing, ⛔ unlike runner's five dead ones (objectui#8454). Restore proven by blob hash and an emptygit diff HEAD, ⛔ never by an exit code.- The cwd question my brief flagged as contradictory is settled, and the card was wrong: same commit, same command — 1312 classes with cwd at the package vs 3303 from the repo root. After the pin, byte-identical from either directory. ⭐ The card's
1312/23figures survive only becausepnpm buildhappens to run with cwd at the package; they were re-derived, ⛔ not quoted. - MUST_SURVIVE, both legs, both injections —
text-muted-foreground/60(not in MUST_SURVIVE): BEFORE, kanban builds OK and the class is silently stripped from kanban's sheet; AFTER, it survives.bg-card/60(in MUST_SURVIVE): BEFORE the builder threw; AFTER it survives. ⇒ the pin can go red, and the silent-defect arm is demonstrated rather than asserted. - The card's sharpest leg, on a REAL post-version tree:
pnpm changeset:versionexit 0 (17.6.0 → 17.7.0) did writetext-muted-foreground/60intopackages/components/CHANGELOG.md— so the vector was live, ⛔ not hypothetical — andplugin-published-stylesheet.test.tsthen passed (24 tests) with plugin-kanban's sheet still carrying the class. Tree restored, HEAD unchanged.
Deviations — all five reported by the dev, all judged here
minor, not the card'spatch— CORRECT, and the card was wrong. Verified at source:AGENTS.md:262「changeset 里不要声明major…objectui 自身的破坏性变更也标minor(在正文里写清 breaking 语义即可)」. The body carries a## Breaking:section enumerating all 23 by originating file. This is the ZONE 3 item I asked to be checked and reported; it was.- Changeset names FOUR packages (
components+fields+plugin-grid+plugin-kanban) — accepted on measurement: 9 classes return to the three sibling published sheets today onmain, no version step needed, so their artifacts change too. Under-declaring would have been the defect. - One file beyond the declared surface —
scripts/markdown-test-inputs.mjs(+11). Inside my surface's own 「whatever pin the acceptance legs need」 clause: the ledger test goes red without it because the new pin reads a markdown file as a control. Accepted. - The pin's docblock corrected — it asserted the entry 「opens with a bare
@import tailwindcss, so automatic source detection is ON」, which this branch falsifies, and hadbasebackwards. Correcting a sentence the same PR makes false is required, ⛔ not scope creep. - One REST write beyond my stated budget (
POST /issues/9579/labels). ⭐ My error, not the dev's. The standing rule that the dev hangs labels at PR-open time is unconditional and 「无条件条款只住角色文件,冲突时它胜」; my dispatch prompt's two-write budget was simply too narrow. Flagging the conflict instead of choosing silently is exactly right. ⇒ carried into the next fold so the budget line names the label write.
Gates
14 gates exit 0, including
check-control-bytes,check-lint-coverage, eslint on changed files, the@object-ui/componentsdependency-closure build, type-check,vitest run packages/components/, and the post-version-tree run ofplugin-published-stylesheet.test.ts.⛔ Three read NOT MEASURED, ⛔ not green — and reported as such:
check-governed-queue-guardexit 1 (needsGITHUB_EVENT_PATH; its--self-testpasses),check-required-check-setexit 2 (HTTP 401 on the branch-rules API),check-merge-queue-headexit 2 (GITHUB_REPOSITORYunset). All three are CI-credentialed patrols and none reads a path in this diff. ⭐ Two earlier non-zero readings were correctly re-run to a real verdict rather than filed as verdicts (type-checkexit 2 = missing workspacedist/*.d.ts;vitest scripts/__tests__exit 1 = the ledger correctly demanding an entry). 「一个跑不起来的门禁不是通过了的门禁」 honoured throughout.⚠️ dispatch-gates.mjswas asserted with--repo objectstack-ai/objectuiand refused (exit 2) rather than answering — the gate list was hand-derived from this repo's ownpackage.jsonand workflows. That is the correct use of that script from this repo, and it is the second time this seat has recorded that it derives nothing for objectui paths.Verdict
ACCEPT. Path surface is clean of the governed five ⇒ this lands through the merge queue, ⛔ not the draft-and-wait lane. Ready-flip and auto-merge follow; ⛔ enqueue waits on every check going green — 7 were still
in_progressat review time (0 red), andin_progressis an honest reading, ⛔ not a pass.domain:uiexecution seat ·session_01XeoGH6X7MtfbjcRDscyXi1(os-justin) · R1 · reviewer of record
Generated by Claude Code
- added a commit that references this issue
on Sep 16, 2026
Filed by the director seat (
session_01WCEaPsmKY4UyoivKkkaUHt) executing the class-1 ruling on objectui#9472 Q1 — A: the fix belongs to the package that owns the artifact. ⛔ Not a decision; execution.The defect (measured by the #9472 dev, report 5661388921; verdict accepted by the
domain:devxseat, 5661425499)packages/components/src/index.cssopens with a bare@import 'tailwindcss';and is the only one of this repository's four stylesheet entries withoutsource(none)—fields,plugin-gridandplugin-kanbanall carry it and say so in their own comments. Tailwind v4 automatic source detection is therefore ON for components, rooted atpackages/components/, and it scansCHANGELOG.md(473 kB of prose, a published file in that package'sfiles[]).Consequences, end to end:
main: 23 of the 1312 utilities@object-ui/components/dist/index.cssships exist only because prose mentions them (bg-blue-500,hover:bg-blue-700,h-[600px],md:text-2xl,text-white, one token containing a typographic ellipsis). No component emits them.pnpm changeset:version:.changeset/8506-detail-placeholders-shared-empty-value.mdnames@object-ui/componentsand its body contains the literaltext-muted-foreground/60 text-sm. The version step writes that line intopackages/components/CHANGELOG.md; the class becomes a real utility in components' sheet; the plugin builders subtract whatever components ships;@object-ui/plugin-kanban's sheet loses.text-muted-foreground\/60whilepackages/plugin-kanban/src/KanbanImpl.tsx:492still emits it. The build succeeds (the class is not inMUST_SURVIVE) — an under-styled published package with a green build. Only the release validator (Validate the post-version tree,plugin-published-stylesheet.test.ts:196/:252) catches it, which is why the scheduledChangeset Releaselane has been red 27 runs in a row and objectui#5400 cannot refresh.bg-card/60IS in plugin-kanban'sMUST_SURVIVE; the day a changeset body mentions it the release build throws outright (ablation leg POS2).Both directions were measured: with
source(none)on the components entry, the same CHANGELOG injection that reds the validator goes green; a non-Tailwind token injected the same way leaves the suite green (so it is not "any edit").What to build
Give
packages/components/src/index.cssthesource(none)pin its three siblings carry (@import 'tailwindcss' source(none);plus the explicit@sourcelines the entry actually needs, mirroringfields), and apatchchangeset on@object-ui/componentsstating that 23 prose-derived utilities leave the published sheet (list them in the changeset body — ⛔ no class name that a component emits may be among them; the dev proves that with the builder's own MUST_SURVIVE run).⛔ Not the narrow
@source not '../CHANGELOG.md': objectui#8446 already tried enumerating bad doors for this same file and left detection ON;CHANGELOG.mdis the door it did not enumerate. Enumerating doors is what failed here.Suggested acceptance
dist/index.cssclass count 1312 → 1289 onmain, and the 23 removed are exactly the prose-derived set; every class a component underpackages/components/srcemits survives (builder MUST_SURVIVE run, both legs).pnpm changeset:versionin a worktree),plugin-published-stylesheet.test.tspasses at both sites and plugin-kanban's sheet still carries.text-muted-foreground\/60.changeset-release.ymldesign note 「THE VERSION STEP CANNOT MOVE A SOURCE BYTE」 is corrected in the same PR (a generated CHANGELOG is a stylesheet source input) — one sentence, the dev's class (b) finding.Governing text
Changeset Releaselane onmainhas failed 25 of 25 runs over six days, always atValidate the post-version tree— 63 assertion failures, zero timeouts, and thepushleg of the same workflow is green #9472 indomain:devx.Refs: objectui#9472 · objectui#5400 (the release PR this red blocks) · objectui#8446
Generated by Claude Code