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 }}
Repository navigation
[finding] shadcn: sheet/sidebar carry load-bearing local edits that are documented in prose but NOT declared patches — a --force sync drops them, and one of them does it silently #6027
Fell out of #4996 (PR pending), which vendored byte-verbatim registry fixtures for all four patched families and asserted the declared anchors against them. Out of scope there: that card owns anchor verification, not the question of which edits are declared in the first place.
Fact
With the vendored fixtures in place, applyLocalPatches(name, upstreamBytes) reproduces the shipped primitive byte for byte for dialog and slider, and does not for sheet and sidebar. The residue is local edits that live only as prose in packages/components/shadcn-components.json (localEdits), with nothing in scripts/shadcn-local-patches.mjs re-applying them:
drops it → styling/test-locator behaviour changes, no gate
shadcn-components.json already says the quiet part out loud for sheet: the i18n patch is declared and re-applied automatically, "so unlike the hideOverlay edit above it does not depend on anyone remembering it."
Why it matters
This is the exact hazard scripts/shadcn-local-patches.mjs opens with — --force bypasses the localOnlyLines refusal by design and takes the edit with it, and a type-check cannot see the loss. The module's answer is "declare the edit". Two of these four are caught downstream by other gates; the Tailwind v4 one is not caught by anything, and it is explicitly documented as load-bearing (the v3 spelling compiles to a bare custom-property name on Tailwind 4.x — invalid CSS).
Not asserting a severity
Nothing is broken today: no --force sync has run, and #4996's fixtures now pin the declared anchors. This is about which edits are declared, which is a judgement call about cost (declaring the Tailwind migration as a patch family means anchoring ~20 class strings) versus the silence of the failure mode. Options worth weighing include declaring only the load-bearing subset, or an offline assertion that the shipped file contains no v3 [--var] spellings at all — the latter is much cheaper and catches the same regression from the other side.
Filed unassigned, finding only, no pm:queue — observation class, for PM triage.
PM claim + dispatch order — R13, with the cost ruling made
Claimed by the domain:devx @ objectui execution seat (#5748), round R13, PM session session_019b5UBNMtTzKbVtZZGvFuxe. Branch: claude/issue-6027-shadcn-undeclared-local-edits.
The ruling: take the cheap offline assertion, ⛔ do NOT declare the Tailwind migration as a patch family
The card deliberately withholds a recommendation because this is a cost judgement. Ruled:
Build the offline assertion that the shipped sidebar.tsx contains no v3 [--var] spellings. That is the option the card itself names as "much cheaper and catches the same regression from the other side", and it targets the only one of the four that no gate catches.
⛔ Do not declare the Tailwind v4 migration as a patch family. Anchoring ~20 class strings buys the same protection at many times the cost, and every anchor is a future maintenance surface that can drift out of agreement with upstream — the exact failure class #4996 just finished measuring.
Rationale, so it can be argued with: three of the four undeclared edits already fail loudly through existing gates — hideOverlay breaks type-check, the eslint-disable breaks Lint, and the data-collapsible qualifiers change test-locator behaviour. Those need recording, not fixing. The Tailwind one is the outlier precisely because it "compiles, renders, and emits invalid CSS browsers discard" — silence is what makes it worth a gate, and a one-sided spelling assertion closes exactly that silence.
Scope
The offline assertion, run by an existing suite — ⛔ no new workflow.
⚠️ Assert on the shippedpackages/components/src/.../sidebar.tsx, not on a fixture. The regression this catches is "a --force sync overwrote the shipped file", so a fixture-based check would pass while the defect landed.
Non-vacuity, executable. The assertion must be shown going red: reintroduce a v3 [--sidebar-width] spelling into the shipped file, prove the mutation reached disk by grepping the literal, watch it fail naming the spelling and the file, then restore with git checkout HEAD -- <path> (⛔ not the bare form) and prove restoration with an empty git diff HEAD.
Guard against the inverse: the v4 (--var) spellings that are supposed to be there must not trip it. Assert a representative one stays green, or the check is a trap for the next person doing the migration correctly.
⚠️ Establish that your pattern actually distinguishes the two spellings rather than matching any --var occurrence — show the count of v4 spellings it tolerates alongside the v3 spellings it refuses.
Record the other three edits and the gate that catches each, in the assertion's own docblock, so a reader knows why only one is guarded here. That record is half the deliverable.
⛔ Never git stash — shared stack, other agents are using it now.
{
"issue": 6027,
"status": "done",
"branch": "claude/issue-6027-shadcn-undeclared-local-edits",
"pr": "https://github.com/objectstack-ai/objectui/pull/6091",
"premise_still_valid": true,
"summary": "Built the ruled offline assertion: a new describe block in the existing scripts/__tests__/shadcn-local-patches.test.ts suite (no new workflow) asserting the SHIPPED packages/components/src/ui/sidebar.tsx carries no Tailwind v3 `[--var]` arbitrary values. Read off disk via fs.readFileSync, not from a vendored fixture, because the regression guarded is 'a --force sync overwrote the shipped file'. Did NOT declare the Tailwind migration as a patch family, per the ruling. The block also guards the inverse (correct v4 `(--var)` spellings must stay green), pins the tolerated/refused counts so a sync that deletes the utilities outright cannot pass as 'no v3 spellings found', and its docblock records the other three undeclared edits with the gate catching each. One correction to the card's table surfaced while writing that record: `hideOverlay` has ZERO consumers in the tree (the DesignDrawer that ROADMAP.md credits no longer exists), so as an optional prop its loss type-checks clean and the type-check gate the card credits is vacuous today — recorded in the docblock, filed as #6090, not fixed here.",
"tests": "All from the repo ROOT, never package-scoped (this repo's vitest-invocation-guard refuses package-scoped runs). Exit codes captured by redirecting to a file BEFORE any pipe, never via `tail`.\n\n(1) Union at the FINAL commit `0eadb67ab` (sha read from that same run, HEAD unchanged before and after): `NODE_OPTIONS=--max-old-space-size=4096 npx vitest run scripts/__tests__/` -> VITEST_EXIT=0, 'Test Files 65 passed (65) / Tests 1793 passed (1793)'. Includes scripts-type-check.test.ts (type-checks the changed file) and check-changeset-fixed.test.ts.\n\n(2) Targeted: `npx vitest run scripts/__tests__/shadcn-local-patches.test.ts` -> EXIT=0, 'Test Files 1 passed (1) / Tests 43 passed (43)'.\n\n(3) `npx eslint scripts/__tests__/shadcn-local-patches.test.ts` -> ESLINT_EXIT=0, no output.\n\n(4) Control-byte scan `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` over the changed file -> exit 1 (clean).\n\nNON-VACUITY / ABLATION. No rebuild leg applies and that is a property of the resolution path, not an omission: the assertion reads the .tsx SOURCE directly through fs.readFileSync(path.join(uiDir,'sidebar.tsx')) — there is no package `exports` resolution and no dist/ that could serve stale bytes, so the mutated file IS what the test reads. The mutation script carried `trap '<git checkout HEAD -- $TARGET>' EXIT INT TERM` so a foreground-cap SIGTERM could not leave the tree mutated.\n\nMutation confirmed ON DISK by grepping the literal, NOT by the editor's exit code (perl -i returns 0 on zero matches): injected literal 'w-[--sidebar-width]' occurrence count = 1, with the line echoed back — `199: \"flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground\",` — and the REMOVED literal 'w-(--sidebar-width) flex-col' count = 0. Both anchors checked, in both directions.\n\nObserved direction (predicted before running: turn red): 'Tests 3 failed | 40 passed (43)'. The guard failed naming BOTH the spelling and the file: 'AssertionError: packages/components/src/ui/sidebar.tsx contains Tailwind v3 bare-custom-property arbitrary value(s) ... Offenders: packages/components/src/ui/sidebar.tsx:199: [--sidebar-width]'. The inverse guard failed independently ('expected 6 to be greater than or equal to 7') as did the count pin ('v4Shorthand: 6' vs 7) — i.e. the same one-character mutation is caught three ways. The literal-based 'distinguishes' test stayed green, correctly, since it does not read the file.\n\nRESTORE leg: `git checkout HEAD -- packages/components/src/ui/sidebar.tsx`, proven by an EMPTY `git diff HEAD -- <path>` and counts back to 7 v4 shorthand / 0 v3; `git status --short` then showed only my own test file modified.\n\nSPELLING DISCRIMINATION (the counts asked for, measured on the shipped file): 7 v4 shorthand `-(--var)` TOLERATED; 6 `var(--x)` calls inside arbitrary values TOLERATED (e.g. `w-[calc(var(--sidebar-width-icon)_+_1rem)]`, `shadow-[0_0_0_1px_hsl(var(--sidebar-border))]`); 0 v3 `[--var]` REFUSED. The pattern requires `[` followed IMMEDIATELY by `--` and closed by `]`, so it matches neither a var() call inside brackets nor the arbitrary-PROPERTY form `[--sidebar-width:16rem]` (still valid on v4 — the trailing `:` breaks the match, deliberately). Proven on literals independently of the file, so it keeps holding after the file changes.\n\nSCOPE CHECK: scanned all of packages/components/src/ui/*.tsx plus app-shell/AppSidebar.tsx and cli/app-generator.ts for v3 spellings — zero found, so the 925051db6 migration holds tree-wide and there is no live defect beyond the guarded file.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #6090: sheet.tsx's undeclared `hideOverlay` prop has zero consumers (the DesignDrawer ROADMAP.md credits no longer exists), so as an optional prop its loss type-checks clean — the type-check gate #6027's table credits with catching it is vacuous today, making the count of unguarded undeclared edits two rather than one. Filed unassigned, `finding` label, no `pm:queue`; searched for duplicates first (none)."
]
}
Deviations / notes for PM:
Changeset, not skip-changeset. This is a tests-only PR. Per the dev contract, objectui has noskip-changeset label mechanism (that is objectstack's), so the declaration is an empty-frontmatter changeset — .changeset/6027-sidebar-tailwind-v4-spelling-guard.md — matching the convention already used by 4850-misplaced-eslint-disable.md and designer-field-key-parity-gate.md. No label was created or applied in this repo.
Declared narrowing: I did not run the repo-wide pnpm lint / full vitest run. I ran the whole scripts/__tests__/ project (the suite this change lives in, 1793 tests) plus targeted eslint. CI runs the full farm regardless; per the dev contract the report is delivered at draft-PR time and CI convergence is yours.
(Supersedes the previous comment: the HTML-comment os-dev-report marker did not survive GitHub's body sanitizer, so this copy leads with the literal text instead. Content is otherwise the same report.)
{
"issue": 6027,
"status": "done",
"branch": "claude/issue-6027-shadcn-undeclared-local-edits",
"pr": "https://github.com/objectstack-ai/objectui/pull/6091",
"premise_still_valid": true,
"summary": "Built the ruled offline assertion: a new describe block in the existing scripts/__tests__/shadcn-local-patches.test.ts suite (no new workflow) asserting the SHIPPED packages/components/src/ui/sidebar.tsx carries no Tailwind v3 [--var] arbitrary values. Read off disk via fs.readFileSync, not from a vendored fixture, because the regression guarded is 'a --force sync overwrote the shipped file'. Did NOT declare the Tailwind migration as a patch family, per the ruling. The block also guards the inverse (correct v4 (--var) spellings must stay green), pins the tolerated/refused counts so a sync that deletes the utilities outright cannot pass as 'no v3 spellings found', and its docblock records the other three undeclared edits with the gate catching each. One correction to the card's table surfaced while writing that record: hideOverlay has ZERO consumers in the tree (the DesignDrawer that ROADMAP.md credits no longer exists), so as an optional prop its loss type-checks clean and the type-check gate the card credits is vacuous today — recorded in the docblock, filed as #6090, not fixed here.",
"tests": "All from the repo ROOT, never package-scoped (this repo's vitest-invocation-guard refuses package-scoped runs). Exit codes captured by redirecting to a file BEFORE any pipe, never via tail.\n\n(1) Union at the FINAL commit 0eadb67ab (sha read from that same run; HEAD identical before and after): NODE_OPTIONS=--max-old-space-size=4096 npx vitest run scripts/__tests__/ -> VITEST_EXIT=0, 'Test Files 65 passed (65) / Tests 1793 passed (1793)'. Includes scripts-type-check.test.ts (type-checks the changed file) and check-changeset-fixed.test.ts.\n\n(2) Targeted: npx vitest run scripts/__tests__/shadcn-local-patches.test.ts -> EXIT=0, 'Test Files 1 passed (1) / Tests 43 passed (43)'.\n\n(3) npx eslint scripts/__tests__/shadcn-local-patches.test.ts -> ESLINT_EXIT=0, no output.\n\n(4) Control-byte scan grep -naP over the changed file -> exit 1 (clean).\n\nNON-VACUITY / ABLATION. No rebuild leg applies, and that is a property of the resolution path rather than an omission: the assertion reads the .tsx SOURCE directly through fs.readFileSync(path.join(uiDir,'sidebar.tsx')) — no package exports resolution, no dist/ that could serve stale bytes, so the mutated file IS what the test reads. The mutation script carried a trap on EXIT INT TERM restoring the file, so a foreground-cap SIGTERM could not leave the tree mutated.\n\nMutation confirmed ON DISK by grepping the literal, NOT by the editor's exit code (perl -i returns 0 on zero matches): injected literal 'w-[--sidebar-width]' occurrence count = 1, with the line echoed back — line 199, \"flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground\" — and the REMOVED literal 'w-(--sidebar-width) flex-col' count = 0. Both anchors checked, in both directions.\n\nObserved direction (predicted before running: turn red): 'Tests 3 failed | 40 passed (43)'. The guard failed naming BOTH the spelling and the file: 'AssertionError: packages/components/src/ui/sidebar.tsx contains Tailwind v3 bare-custom-property arbitrary value(s) ... Offenders: packages/components/src/ui/sidebar.tsx:199: [--sidebar-width]'. The inverse guard failed independently ('expected 6 to be greater than or equal to 7') as did the count pin ('v4Shorthand: 6' vs 7) — the same one-character mutation is caught three ways. The literal-based 'distinguishes' test stayed green, correctly, since it does not read the file.\n\nRESTORE leg: git checkout HEAD -- packages/components/src/ui/sidebar.tsx, proven by an EMPTY git diff HEAD for that path and counts back to 7 v4 shorthand / 0 v3; git status --short then showed only my own test file modified.\n\nSPELLING DISCRIMINATION (the counts asked for, measured on the shipped file): 7 v4 shorthand -(--var) TOLERATED; 6 var(--x) calls inside arbitrary values TOLERATED (e.g. w-[calc(var(--sidebar-width-icon)_+_1rem)], shadow-[0_0_0_1px_hsl(var(--sidebar-border))]); 0 v3 [--var] REFUSED. The pattern requires [ followed IMMEDIATELY by -- and closed by ], so it matches neither a var() call inside brackets nor the arbitrary-PROPERTY form [--sidebar-width:16rem] (still valid on v4 — the trailing colon breaks the match, deliberately). Proven on literals independently of the file, so it keeps holding after the file changes.\n\nSCOPE CHECK: scanned all of packages/components/src/ui/*.tsx plus app-shell/AppSidebar.tsx and cli/app-generator.ts for v3 spellings — zero found, so the 925051db6 migration holds tree-wide and there is no live defect beyond the guarded file.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #6090: sheet.tsx's undeclared hideOverlay prop has zero consumers (the DesignDrawer ROADMAP.md credits no longer exists), so as an optional prop its loss type-checks clean — the type-check gate #6027's table credits with catching it is vacuous today, making the count of unguarded undeclared edits two rather than one. Filed unassigned, finding label, no pm:queue; searched for duplicates first (none)."
]
}
Deviations / notes for PM:
Changeset, not skip-changeset. Tests-only PR. Per the dev contract, objectui has noskip-changeset label mechanism (that is objectstack's), so the declaration is an empty-frontmatter changeset — .changeset/6027-sidebar-tailwind-v4-spelling-guard.md — matching the convention already used by 4850-misplaced-eslint-disable.md and designer-field-key-parity-gate.md. No label was created or applied in this repo.
Declared narrowing: I did not run the repo-wide pnpm lint / full vitest run. I ran the whole scripts/__tests__/ project (the suite this change lives in, 1793 tests) plus targeted eslint on the changed file. CI runs the full farm regardless; per the dev contract the report is delivered at draft-PR time and CI convergence is yours.
Activity
yinlianghui-tw commentedon Aug 24, 2026
PM claim + dispatch order — R13, with the cost ruling made
Claimed by the
domain:devx@ objectui execution seat (#5748), round R13, PM sessionsession_019b5UBNMtTzKbVtZZGvFuxe. Branch:claude/issue-6027-shadcn-undeclared-local-edits.The ruling: take the cheap offline assertion, ⛔ do NOT declare the Tailwind migration as a patch family
The card deliberately withholds a recommendation because this is a cost judgement. Ruled:
Build the offline assertion that the shipped
sidebar.tsxcontains no v3[--var]spellings. That is the option the card itself names as "much cheaper and catches the same regression from the other side", and it targets the only one of the four that no gate catches.⛔ Do not declare the Tailwind v4 migration as a patch family. Anchoring ~20 class strings buys the same protection at many times the cost, and every anchor is a future maintenance surface that can drift out of agreement with upstream — the exact failure class #4996 just finished measuring.
Rationale, so it can be argued with: three of the four undeclared edits already fail loudly through existing gates —
hideOverlaybreaks type-check, theeslint-disablebreaks Lint, and thedata-collapsiblequalifiers change test-locator behaviour. Those need recording, not fixing. The Tailwind one is the outlier precisely because it "compiles, renders, and emits invalid CSS browsers discard" — silence is what makes it worth a gate, and a one-sided spelling assertion closes exactly that silence.Scope
packages/components/src/.../sidebar.tsx, not on a fixture. The regression this catches is "a--forcesync overwrote the shipped file", so a fixture-based check would pass while the defect landed.scripts/shadcn-local-patches.mjs's patch declarations,shadcn-components.json'slocalEditsprose beyond what accuracy requires, or any vendored registry fixture ([finding] shadcn 声明式补丁:sheet / sidebar 两族的锚点从未被「作用到 upstream」验证过 —— #4976 的同一盲区,目前休眠 #4996 / PR test(shadcn): verify every declared patch anchor against real registry bytes #6030 owns those and it merged today — rebase on currentmain).shadcnsync of any kind.Verification
[--sidebar-width]spelling into the shipped file, prove the mutation reached disk by grepping the literal, watch it fail naming the spelling and the file, then restore withgit checkout HEAD -- <path>(⛔ not the bare form) and prove restoration with an emptygit diff HEAD.(--var)spellings that are supposed to be there must not trip it. Assert a representative one stays green, or the check is a trap for the next person doing the migration correctly.--varoccurrence — show the count of v4 spellings it tolerates alongside the v3 spellings it refuses.⛔ Never
git stash— shared stack, other agents are using it now.Refs: #4996 / PR #6030 ·
925051db6(the Tailwind v4 migration commit).Generated by Claude Code
yinlianghui-tw commentedon Aug 24, 2026
{ "issue": 6027, "status": "done", "branch": "claude/issue-6027-shadcn-undeclared-local-edits", "pr": "https://github.com/objectstack-ai/objectui/pull/6091", "premise_still_valid": true, "summary": "Built the ruled offline assertion: a new describe block in the existing scripts/__tests__/shadcn-local-patches.test.ts suite (no new workflow) asserting the SHIPPED packages/components/src/ui/sidebar.tsx carries no Tailwind v3 `[--var]` arbitrary values. Read off disk via fs.readFileSync, not from a vendored fixture, because the regression guarded is 'a --force sync overwrote the shipped file'. Did NOT declare the Tailwind migration as a patch family, per the ruling. The block also guards the inverse (correct v4 `(--var)` spellings must stay green), pins the tolerated/refused counts so a sync that deletes the utilities outright cannot pass as 'no v3 spellings found', and its docblock records the other three undeclared edits with the gate catching each. One correction to the card's table surfaced while writing that record: `hideOverlay` has ZERO consumers in the tree (the DesignDrawer that ROADMAP.md credits no longer exists), so as an optional prop its loss type-checks clean and the type-check gate the card credits is vacuous today — recorded in the docblock, filed as #6090, not fixed here.", "tests": "All from the repo ROOT, never package-scoped (this repo's vitest-invocation-guard refuses package-scoped runs). Exit codes captured by redirecting to a file BEFORE any pipe, never via `tail`.\n\n(1) Union at the FINAL commit `0eadb67ab` (sha read from that same run, HEAD unchanged before and after): `NODE_OPTIONS=--max-old-space-size=4096 npx vitest run scripts/__tests__/` -> VITEST_EXIT=0, 'Test Files 65 passed (65) / Tests 1793 passed (1793)'. Includes scripts-type-check.test.ts (type-checks the changed file) and check-changeset-fixed.test.ts.\n\n(2) Targeted: `npx vitest run scripts/__tests__/shadcn-local-patches.test.ts` -> EXIT=0, 'Test Files 1 passed (1) / Tests 43 passed (43)'.\n\n(3) `npx eslint scripts/__tests__/shadcn-local-patches.test.ts` -> ESLINT_EXIT=0, no output.\n\n(4) Control-byte scan `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` over the changed file -> exit 1 (clean).\n\nNON-VACUITY / ABLATION. No rebuild leg applies and that is a property of the resolution path, not an omission: the assertion reads the .tsx SOURCE directly through fs.readFileSync(path.join(uiDir,'sidebar.tsx')) — there is no package `exports` resolution and no dist/ that could serve stale bytes, so the mutated file IS what the test reads. The mutation script carried `trap '<git checkout HEAD -- $TARGET>' EXIT INT TERM` so a foreground-cap SIGTERM could not leave the tree mutated.\n\nMutation confirmed ON DISK by grepping the literal, NOT by the editor's exit code (perl -i returns 0 on zero matches): injected literal 'w-[--sidebar-width]' occurrence count = 1, with the line echoed back — `199: \"flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground\",` — and the REMOVED literal 'w-(--sidebar-width) flex-col' count = 0. Both anchors checked, in both directions.\n\nObserved direction (predicted before running: turn red): 'Tests 3 failed | 40 passed (43)'. The guard failed naming BOTH the spelling and the file: 'AssertionError: packages/components/src/ui/sidebar.tsx contains Tailwind v3 bare-custom-property arbitrary value(s) ... Offenders: packages/components/src/ui/sidebar.tsx:199: [--sidebar-width]'. The inverse guard failed independently ('expected 6 to be greater than or equal to 7') as did the count pin ('v4Shorthand: 6' vs 7) — i.e. the same one-character mutation is caught three ways. The literal-based 'distinguishes' test stayed green, correctly, since it does not read the file.\n\nRESTORE leg: `git checkout HEAD -- packages/components/src/ui/sidebar.tsx`, proven by an EMPTY `git diff HEAD -- <path>` and counts back to 7 v4 shorthand / 0 v3; `git status --short` then showed only my own test file modified.\n\nSPELLING DISCRIMINATION (the counts asked for, measured on the shipped file): 7 v4 shorthand `-(--var)` TOLERATED; 6 `var(--x)` calls inside arbitrary values TOLERATED (e.g. `w-[calc(var(--sidebar-width-icon)_+_1rem)]`, `shadow-[0_0_0_1px_hsl(var(--sidebar-border))]`); 0 v3 `[--var]` REFUSED. The pattern requires `[` followed IMMEDIATELY by `--` and closed by `]`, so it matches neither a var() call inside brackets nor the arbitrary-PROPERTY form `[--sidebar-width:16rem]` (still valid on v4 — the trailing `:` breaks the match, deliberately). Proven on literals independently of the file, so it keeps holding after the file changes.\n\nSCOPE CHECK: scanned all of packages/components/src/ui/*.tsx plus app-shell/AppSidebar.tsx and cli/app-generator.ts for v3 spellings — zero found, so the 925051db6 migration holds tree-wide and there is no live defect beyond the guarded file.", "open_questions": [], "out_of_scope_findings": [ "filed as #6090: sheet.tsx's undeclared `hideOverlay` prop has zero consumers (the DesignDrawer ROADMAP.md credits no longer exists), so as an optional prop its loss type-checks clean — the type-check gate #6027's table credits with catching it is vacuous today, making the count of unguarded undeclared edits two rather than one. Filed unassigned, `finding` label, no `pm:queue`; searched for duplicates first (none)." ] }Deviations / notes for PM:
skip-changeset. This is a tests-only PR. Per the dev contract, objectui has noskip-changesetlabel mechanism (that is objectstack's), so the declaration is an empty-frontmatter changeset —.changeset/6027-sidebar-tailwind-v4-spelling-guard.md— matching the convention already used by4850-misplaced-eslint-disable.mdanddesigner-field-key-parity-gate.md. No label was created or applied in this repo.pnpm lint/ fullvitest run. I ran the wholescripts/__tests__/project (the suite this change lives in, 1793 tests) plus targeted eslint. CI runs the full farm regardless; per the dev contract the report is delivered at draft-PR time and CI convergence is yours.main7c96c9420, which already contains PR test(shadcn): verify every declared patch anchor against real registry bytes #6030 ([finding] shadcn 声明式补丁:sheet / sidebar 两族的锚点从未被「作用到 upstream」验证过 —— #4976 的同一盲区,目前休眠 #4996's fixtures), as instructed. Worktree removed cleanly without--force.Generated by Claude Code
Generated by Claude Code
yinlianghui-tw commentedon Aug 24, 2026
os-dev-report
(Supersedes the previous comment: the HTML-comment
os-dev-reportmarker did not survive GitHub's body sanitizer, so this copy leads with the literal text instead. Content is otherwise the same report.){ "issue": 6027, "status": "done", "branch": "claude/issue-6027-shadcn-undeclared-local-edits", "pr": "https://github.com/objectstack-ai/objectui/pull/6091", "premise_still_valid": true, "summary": "Built the ruled offline assertion: a new describe block in the existing scripts/__tests__/shadcn-local-patches.test.ts suite (no new workflow) asserting the SHIPPED packages/components/src/ui/sidebar.tsx carries no Tailwind v3 [--var] arbitrary values. Read off disk via fs.readFileSync, not from a vendored fixture, because the regression guarded is 'a --force sync overwrote the shipped file'. Did NOT declare the Tailwind migration as a patch family, per the ruling. The block also guards the inverse (correct v4 (--var) spellings must stay green), pins the tolerated/refused counts so a sync that deletes the utilities outright cannot pass as 'no v3 spellings found', and its docblock records the other three undeclared edits with the gate catching each. One correction to the card's table surfaced while writing that record: hideOverlay has ZERO consumers in the tree (the DesignDrawer that ROADMAP.md credits no longer exists), so as an optional prop its loss type-checks clean and the type-check gate the card credits is vacuous today — recorded in the docblock, filed as #6090, not fixed here.", "tests": "All from the repo ROOT, never package-scoped (this repo's vitest-invocation-guard refuses package-scoped runs). Exit codes captured by redirecting to a file BEFORE any pipe, never via tail.\n\n(1) Union at the FINAL commit 0eadb67ab (sha read from that same run; HEAD identical before and after): NODE_OPTIONS=--max-old-space-size=4096 npx vitest run scripts/__tests__/ -> VITEST_EXIT=0, 'Test Files 65 passed (65) / Tests 1793 passed (1793)'. Includes scripts-type-check.test.ts (type-checks the changed file) and check-changeset-fixed.test.ts.\n\n(2) Targeted: npx vitest run scripts/__tests__/shadcn-local-patches.test.ts -> EXIT=0, 'Test Files 1 passed (1) / Tests 43 passed (43)'.\n\n(3) npx eslint scripts/__tests__/shadcn-local-patches.test.ts -> ESLINT_EXIT=0, no output.\n\n(4) Control-byte scan grep -naP over the changed file -> exit 1 (clean).\n\nNON-VACUITY / ABLATION. No rebuild leg applies, and that is a property of the resolution path rather than an omission: the assertion reads the .tsx SOURCE directly through fs.readFileSync(path.join(uiDir,'sidebar.tsx')) — no package exports resolution, no dist/ that could serve stale bytes, so the mutated file IS what the test reads. The mutation script carried a trap on EXIT INT TERM restoring the file, so a foreground-cap SIGTERM could not leave the tree mutated.\n\nMutation confirmed ON DISK by grepping the literal, NOT by the editor's exit code (perl -i returns 0 on zero matches): injected literal 'w-[--sidebar-width]' occurrence count = 1, with the line echoed back — line 199, \"flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground\" — and the REMOVED literal 'w-(--sidebar-width) flex-col' count = 0. Both anchors checked, in both directions.\n\nObserved direction (predicted before running: turn red): 'Tests 3 failed | 40 passed (43)'. The guard failed naming BOTH the spelling and the file: 'AssertionError: packages/components/src/ui/sidebar.tsx contains Tailwind v3 bare-custom-property arbitrary value(s) ... Offenders: packages/components/src/ui/sidebar.tsx:199: [--sidebar-width]'. The inverse guard failed independently ('expected 6 to be greater than or equal to 7') as did the count pin ('v4Shorthand: 6' vs 7) — the same one-character mutation is caught three ways. The literal-based 'distinguishes' test stayed green, correctly, since it does not read the file.\n\nRESTORE leg: git checkout HEAD -- packages/components/src/ui/sidebar.tsx, proven by an EMPTY git diff HEAD for that path and counts back to 7 v4 shorthand / 0 v3; git status --short then showed only my own test file modified.\n\nSPELLING DISCRIMINATION (the counts asked for, measured on the shipped file): 7 v4 shorthand -(--var) TOLERATED; 6 var(--x) calls inside arbitrary values TOLERATED (e.g. w-[calc(var(--sidebar-width-icon)_+_1rem)], shadow-[0_0_0_1px_hsl(var(--sidebar-border))]); 0 v3 [--var] REFUSED. The pattern requires [ followed IMMEDIATELY by -- and closed by ], so it matches neither a var() call inside brackets nor the arbitrary-PROPERTY form [--sidebar-width:16rem] (still valid on v4 — the trailing colon breaks the match, deliberately). Proven on literals independently of the file, so it keeps holding after the file changes.\n\nSCOPE CHECK: scanned all of packages/components/src/ui/*.tsx plus app-shell/AppSidebar.tsx and cli/app-generator.ts for v3 spellings — zero found, so the 925051db6 migration holds tree-wide and there is no live defect beyond the guarded file.", "open_questions": [], "out_of_scope_findings": [ "filed as #6090: sheet.tsx's undeclared hideOverlay prop has zero consumers (the DesignDrawer ROADMAP.md credits no longer exists), so as an optional prop its loss type-checks clean — the type-check gate #6027's table credits with catching it is vacuous today, making the count of unguarded undeclared edits two rather than one. Filed unassigned, finding label, no pm:queue; searched for duplicates first (none)." ] }Deviations / notes for PM:
skip-changeset. Tests-only PR. Per the dev contract, objectui has noskip-changesetlabel mechanism (that is objectstack's), so the declaration is an empty-frontmatter changeset —.changeset/6027-sidebar-tailwind-v4-spelling-guard.md— matching the convention already used by4850-misplaced-eslint-disable.mdanddesigner-field-key-parity-gate.md. No label was created or applied in this repo.pnpm lint/ fullvitest run. I ran the wholescripts/__tests__/project (the suite this change lives in, 1793 tests) plus targeted eslint on the changed file. CI runs the full farm regardless; per the dev contract the report is delivered at draft-PR time and CI convergence is yours.main7c96c9420, which already contains PR test(shadcn): verify every declared patch anchor against real registry bytes #6030 ([finding] shadcn 声明式补丁:sheet / sidebar 两族的锚点从未被「作用到 upstream」验证过 —— #4976 的同一盲区,目前休眠 #4996's fixtures), as instructed. Worktree removed cleanly without--force.Generated by Claude Code
Generated by Claude Code