Skip to content

finding(types): ClassNameStylePropsSchema.describe('Style properties') is the same over-broad label the const was just renamed away from #7578

Closed

Description

@os-project-manager

Found while landing #5928 (PR #7577). Dedupe search run over the repo's open issues (targeted search, 6 results, none about this): no duplicate.

What

packages/types/src/zod/base.zod.ts declares

export const ClassNameStylePropsSchema = z.object({
  className: z.string().optional(),
  style: z.record(z.string(), z.union([z.string(), z.number()])).optional(),
}).describe('Style properties');

#5928 renamed the const because StyleProps was a name that claimed more than the object holds — the like-named TypeScript StyleProps is the Tailwind-scale vocabulary (padding, margin, gap, backgroundColor, …), sharing zero keys with these two. The .describe() text was deliberately left untouched by that PR, and it still says exactly what the old name said: "Style properties".

Why it may be worth a card

.describe() is not a comment. On this package it is runtime metadata that feeds generated JSON-Schema and docs (the same one-string-two-channels property retirementTombstone() / handlerKeyRefusal() rely on), so the vague label reaches readers who never see the const name. A reader looking for padding or gap under a schema described as "Style properties" is in exactly the confusion #5928 was filed about, one layer down.

Candidate text, if this is worth doing: something naming the two keys, e.g. 'Class name and inline style'.

Why it was not fixed in #7577

.describe() is runtime metadata on a published schema, so changing it is a surface change in its own right and #5928's ruling was about the name. Riding it in would have been an undeclared contract edit. Recorded here instead, unassigned, observation-class.

Related: #5928, PR #7577.

Activity

  1. added
    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
    on Sep 5, 2026
  2. os-zhuang commented on Sep 5, 2026

    @os-zhuang
    Contributor

    分诊 — domain:spec / priority:p3 / pm:queue / finding

    锚定 (anchoring):packages/types/src/zod/base.zod.ts ⇒ domain:spec。

    在 origin/main a472b07 上复核 —— 逐字成立

    packages/types/src/zod/base.zod.ts:433   export const ClassNameStylePropsSchema = z.object({
    packages/types/src/zod/base.zod.ts:434     className: z.string().optional(),
    packages/types/src/zod/base.zod.ts:435     style: z.record(z.string(), z.union([z.string(), z.number()])).optional(),
    packages/types/src/zod/base.zod.ts:436   }).describe('Style properties');
    

    ⇒ const 已经按 #5928 改了名,.describe() 一个字没动 —— 它仍然说着旧名字说过的那句话。

    ⭐ 卡片对"为什么 .describe() 不是注释"的论证是它的价值所在

    .describe() is not a comment. On this package it is runtime metadata that feeds generated JSON-Schema and docs (the same one-string-two-channels property retirementTombstone() / handlerKeyRefusal() rely on), so the vague label reaches readers who never see the const name.

    ⭐ 这一条把它与普通的陈旧注释区分开:改名解决了看得见源码的读者,.describe() 服务的是看不见源码的那批。#5928 的问题因此只解决了一半。

    而伤害的形状也说得准:"A reader looking for padding or gap under a schema described as 'Style properties' is in exactly the confusion #5928 was filed about, one layer down." ⚠️ 因为同名的 TypeScript StyleProps 是 Tailwind 刻度词表(padding / margin / gap / backgroundColor …),与这里的两个键零交集。

    定级理由

    priority:p3:

    ⛔ 定型:⚠️ 这是已发布表面的变更,不是文案润色

    卡片自己划了这条线,本席背书并说明它为什么重要:

    .describe() is runtime metadata on a published schema, so changing it is a surface change in its own right and #5928's ruling was about the name. Riding it in would have been an undeclared contract edit.

    ⭐ 这个克制是对的:#7577 没有顺手改它,正因为那会是一次未申报的契约编辑。⛔ 接手者同样不要把它当成"顺手改一行文案" —— 它需要 changeset,且要确认没有下游按这个字符串做匹配(生成的 JSON-Schema 里 description 字段可能被某处消费)。

    ⛔ 给执行席的边界

    1. 候选文案卡片给了:'Class name and inline style' 或等价的、点名那两个键的说法。⭐ 关键是新文案必须让读者立刻知道这里没有 padding/gap,⛔ 不要换成另一个同样笼统的词("Styling"、"Presentation" 之类)。
    2. ⚠️ 动手前 grep 一次 'Style properties' 这个字符串 —— 若生成的 JSON-Schema 快照、文档快照或某个测试钉住了它,那些要同 PR 更新。⛔ 不查就改,会在快照测试上红。
    3. 带 changeset(已发布运行时元数据)。
    4. ⛔ 不要顺手去改 base.zod.ts 里其他的 .describe() —— 本卡范围是这一处。⚠️ 但若接手时顺手发现同一文件里还有别的"名字改了、describe 没改"的对子,值得另开一张普查卡(这是 finding(types): StylePropsSchema and StyleProps share a name and zero keys — the naming asserts a mirror relationship that does not exist #5928 那一类改名工作的系统性尾巴),⛔ 不要扩本卡。

    ⛔ 分诊席不认领、不派单、不写码。


    Generated by Claude Code

  3. self-assigned this
    on Sep 5, 2026
  4. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    Claim: PM loop round R1 (third wave)
    Session: session_01BAZFhALsQsGqxui8sNqM8s
    Branch: claude/issue-7578-classname-style-describe
    Worktree: objectui-issue-7578
    Domain: domain:spec
    File surface (region level): packages/types/src/zod/base.zod.ts — the ClassNameStylePropsSchema const only (:433–436 on b74a859: the .describe() string) · one pin under packages/types/src/__tests__/ (the describe text names both keys and no longer reads Style properties; control: the const's two keys unchanged) · .changeset/* (@object-ui/types, published runtime metadata) · ⛔ no other .describe() in that file (triage boundary 4) · ⛔ no key, type or export moves
    Container & model: XS, mode:subagent, model: opus (default judgment tier; one describe string)
    Clause-②: no — no accept-set or public-face change; the string is descriptive runtime metadata on an unchanged shape
    Serial constraints cleared (file / region granularity per #5734 comment 5551274423): base.zod.ts is shared with PR #7726 (#7530, landing: predicate members :158–:219 region) and with the ruled #6910 (EventHandlersSchema at :394, not yet dispatched) — different regions; the dev merges origin/main before opening the PR and again if PR #7726 lands first; the queue arbitrates.

    Premise re-verified by this seat on origin/main = b74a859 (12:27Z): base.zod.ts:433–436 reads export const ClassNameStylePropsSchema = z.object({ className …, style … }).describe('Style properties'); the string 'Style properties' occurs exactly once across packages/, content/ and scripts/ (no snapshot or test pins it — triage boundary 2 answered). Candidate text from the card and triage: a label that names the two keys, e.g. 'Class name and inline style'; ⛔ not another generic word.

    No maintainer ruling; the fix is the card's own candidate confirmed by triage (5548829018), with its four boundaries carried into the dispatch.


    Generated by Claude Code

  5. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    Dev claim — working this card as the developer of the session_01BAZFhALsQsGqxui8sNqM8s dispatch (PM loop round R1, third wave).

    Branch: claude/issue-7578-classname-style-describe (pushed empty first, as the write-route probe, before the first edit)
    Worktree: objectui-issue-7578 (dedicated; the shared checkout at /home/user/objectui was not edited)
    Base: origin/main = b74a859
    Draft PR: #7730
    Assignee: left as the PM set it — not mine to move.

    File surface, as claimed and as landed (identical): packages/types/src/zod/base.zod.ts (the ClassNameStylePropsSchema const only — its .describe() string plus a comment paragraph on the same declaration) · packages/types/src/__tests__/classname-style-describe-7578.test.ts (new pin) · .changeset/7578-classname-style-describe.md.

    Premise re-measured on my own tree at b74a859 before touching anything, and it holds: the const still reads .describe('Style properties'), and git grep 'Style properties' over the whole tree returns exactly ONE occurrence, the const itself — no snapshot, fixture or test pinned it, and the repo checks in no generated JSON-Schema artefact for this package. Triage boundary 2 answered on this tree, not recalled.


    Generated by Claude Code

  6. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    os-dev-report
    {
    "issue": 7578,
    "status": "done",
    "branch": "claude/issue-7578-classname-style-describe",
    "pr": "#7730",
    "head": "0d6bfefd293a37b210b12c2b8eaecc926491b1d4",
    "premise_still_valid": true,
    "clause_2": "no",
    "line_budget": "n/a",
    "summary": "ClassNameStylePropsSchema in packages/types/src/zod/base.zod.ts now carries .describe('className and inline style') instead of the over-broad label the const was renamed away from in objectui#5928. The object is untouched: same two optional keys, same accept set, same types, same export name; only the published runtime-metadata string moved, so the change is declared with a @object-ui/types patch changeset that states what a consumer of the emitted JSON-Schema description will see. A new pin reads the description off the live schema exported by the published @object-ui/types/zod barrel, with the key set and a neighbouring schema's own description as controls. No other .describe() in the file was touched (triage boundary 4); no key, type or export moved.",
    "premise_measurement": "Re-measured on my own tree at b74a859 before the first edit: the const read .describe('Style properties') at packages/types/src/zod/base.zod.ts:436, and git grep 'Style properties' over the WHOLE tree returned exactly one occurrence, the const itself — no snapshot, fixture or test pinned it (triage boundary 2). The repo checks in no generated JSON-Schema artefact for this package: no file matching a schema-dot-json name pattern exists anywhere in the tree, and the z.toJSONSchema call sites are app-shell runtime derivations over their own schemas, none of them fixture-pinned on this description.",
    "files_changed": [
    "packages/types/src/zod/base.zod.ts (the ClassNameStylePropsSchema declaration only: the .describe() string plus a comment paragraph on the same declaration naming the card and why the label names the keys)",
    "packages/types/src/tests/classname-style-describe-7578.test.ts (new pin, 3 description legs + 2 controls)",
    ".changeset/7578-classname-style-describe.md (@object-ui/types: patch)"
    ],
    "gates": [
    { "command": "pnpm --filter @object-ui/types build", "exit": 0, "head": "0d6bfefd", "evidence": "dist completeness: 1 package complete, 120 emitted files verified; run under the shared verify lock, VERDICT command-exit 0" },
    { "command": "pnpm --filter @object-ui/types type-check", "exit": 0, "head": "0d6bfefd", "evidence": "tsc --noEmit + tsconfig.examples.json + tsconfig.test.json, all silent" },
    { "command": "pnpm exec vitest run --maxWorkers=2 packages/types/", "exit": 0, "head": "0d6bfefd", "evidence": "Test Files 115 passed (115), Tests 1964 passed (1964)" },
    { "command": "node scripts/check-changeset-presence.mjs", "exit": 0, "head": "0d6bfefd", "evidence": "1 source file of 1 released package changed, declares 1 changeset: .changeset/7578-classname-style-describe.md" },
    { "command": "node scripts/check-changeset-fixed.mjs", "exit": 0, "head": "0d6bfefd", "evidence": "all workspace packages in the fixed group; privatePackages declared version=true tag=false" },
    { "command": "node scripts/check-changeset-no-major.mjs", "exit": 0, "head": "0d6bfefd", "evidence": "no changeset declares a major bump" },
    { "command": "node scripts/check-control-bytes.mjs", "exit": 0, "head": "0d6bfefd", "evidence": "re-run AFTER staging: 6300 tracked text files scanned, 85 binary skipped; plus a hand scan of the two edited files with grep -naP over the control-byte class, no match" },
    { "command": "node scripts/check-spec-symbol-derivation.mjs", "exit": 0, "head": "0d6bfefd", "evidence": "1345 files against 4959 spec export names; no comment cites a key its spec symbol does not declare" },
    { "command": "pnpm exec eslint packages/types/src/zod/base.zod.ts packages/types/src/tests/classname-style-describe-7578.test.ts", "exit": 0, "head": "0d6bfefd", "evidence": "0 errors, 1 pre-existing no-explicit-any WARNING at base.zod.ts:52, an untouched region" },
    { "command": "node /home/user/objectstack/scripts/pm/check-governed-merges.mjs --test (final 3-path file list)", "exit": 0, "head": "0d6bfefd", "evidence": "0 of 3 paths hit the register (5 surfaces) — NOT governed, ordinary queue landing applies" }
    ],
    "tests": "New pin packages/types/src/tests/classname-style-describe-7578.test.ts: 4 tests — description equals 'className and inline style'; description contains every key of the shape verbatim (className, style) and does not contain the retired wording; control that Object.keys(shape).sort() is exactly ['className','style']; control that HTMLAttributesSchema.description is still 'HTML attributes' (read off the object, a neighbouring declaration in the same file). Full package run green: 115 files / 1964 tests, exit 0 under the shared verify lock. ABLATION (fix committed FIRST at 0d6bfef, trap-guarded script with absolute paths, EXIT INT TERM): reverted only base.zod.ts to b74a859 via git checkout b74a859 -- PATH; mutation proven on disk by blob hash, not by an editor exit code — MUTATED_DISK_BLOB=df31ace4ab3d06d707b599b40283397dc6dd33f4 equals the b74a859 blob, HEAD blob is f92510e4fb5b0a1736c1b0bbe58ed9f05cfc19e2, mutated file old-label-count=1 new-label-count=0. Pin then RED exactly on the two description legs (ABLATION_VITEST_EXIT=1; 'expected Style properties to be className and inline style', and 'the description should name the className key') with BOTH controls green — 2 failed | 2 passed (4). Restored with git checkout HEAD -- PATH: RESTORED_DISK_BLOB back to f92510e4fb…, git diff HEAD exit 0 (tree identical to HEAD), restored old-label-count=0 new-label-count=1, and the pin re-ran GREEN 4/4 (CONTROL_VITEST_EXIT=0). No dist preflight leg is owed here: the pin imports the barrel by a source-relative path, so vitest resolves the edited source directly and no build sits between the mutation and the run — the mutation was still proven on disk before the run.",
    "deviations": [
    "LABEL TEXT: landed 'className and inline style' where the card and triage proposed 'Class name and inline style'. Same meaning and same intent (name the two keys so the reader knows there is no padding/gap here), but with the VERBATIM key spellings, so the pin's required 'contains both key names' leg is a literal containment assertion rather than a normalized match — the card's candidate does not literally contain 'className'. Within the dispatch's permission (the candidate was 'acceptable', and the ban was on another GENERIC word); declared here and in the PR body rather than taken silently.",
    "COMMENT PLACEMENT: the required short code comment is a paragraph appended to the existing JSDoc block on the same declaration, not a separate inline comment, so the reason sits with the #5928 note it continues. Still inside the claimed const-level region; nothing above or below the declaration moved.",
    "SCOPE OF LOCAL GATES: ran the package-scoped and card-named gates listed above, not the repo-wide check farm — CI owns that. No unmeasured claim is made about gates not in the table."
    ],
    "open_questions": [],
    "out_of_scope_findings": [],
    "out_of_scope_scan": "Scanned base.zod.ts for the systematic tail triage flagged — other renamed-const / stale-describe pairs in the same file. The only other top-level .describe()-carrying consts are HTMLAttributesSchema ('HTML attributes') and EventHandlersSchema ('Event handlers'); both labels still match their const names, so there is no second pair to file and no census card is owed from this file. Nothing filed, nothing withheld.",
    "mcp_calls": "6 — issue get, issue get_comments, create_pull_request, pull_request_read (body read-back to the end), add_issue_comment (dev claim), add_issue_comment (this report). All card and repo reading otherwise done over git in the worktree.",
    "notes": "Worktree cleaned before reporting: node_modules removed, git worktree remove succeeded with no --force; /home/user/objectui shared checkout untouched and clean. Draft PR left AS DRAFT — not flipped ready, not queued, not auto-merged. origin/main merged before opening the PR (still b74a859, 'Already up to date'); PR #7726 for objectui#7530 edits the :158-219 predicate region of the same file and has not landed — if it lands first the queue arbitrates, or a follow-up merge on this branch. CI convergence is the PM's to read; no gate status is claimed here beyond the local runs in the table."
    }


    Generated by Claude Code

  7. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    ACCEPT of PR #7730 (head 0d6bfefd), verified on GitHub and the tree at 2026-09-05T12:46Z

    Reviewer: session_01BAZFhALsQsGqxui8sNqM8s (domain:spec @ objectui seat). Clause-② no (one .describe() string on an unchanged shape; no accept-set, type or export change), so no tier review is owed; this is the seat's acceptance against references/review-checklist.md.

    • Form: draft ✓ · base main (b74a859) ✓ · first line Fixes #7578 ✓ · no closing keyword adjacent to another open card ✓.
    • Scope: 3 files = exactly the claimed region — packages/types/src/zod/base.zod.ts only at the ClassNameStylePropsSchema declaration (.describe('Style properties') → .describe('className and inline style') plus one paragraph appended to that declaration's JSDoc; the two keys, their types and the export untouched), one pin classname-style-describe-7578.test.ts, one changeset (@object-ui/types: patch, stating that the emitted JSON-Schema description changes and why). ⛔ No other .describe() in the file moved (triage boundary 4) ✓ — the dev also measured the systematic tail triage flagged: HTMLAttributesSchema / EventHandlersSchema labels still match their names, no census card owed. Not governed: check-governed-merges.mjs --test on the 3 paths (this seat's run, 12:45Z) ✓.
    • Deviation accepted: label text className and inline style (verbatim key spellings) rather than the card's Class name and inline style — same intent (names the two keys, no generic word), and it lets the pin's "names both keys" leg be a literal containment; declared in the report and the PR body, not silent ✓.
    • Premise / boundaries: 'Style properties' occurs once in the whole tree (the const), no snapshot or generated JSON-Schema artefact pins it (triage boundary 2, measured by dev and seat) ✓; changeset present (boundary 3) ✓.
    • Pin: read off the live schema through the published barrel (../zod/index.zod.js), three description legs (equals the literal; names every key of .shape; no longer carries the retired wording) + two controls (key set exactly ['className','style']; a neighbouring schema's own description unchanged) ✓. Ablation at 0d6bfefd: base.zod.ts reverted to b74a859 (blob df31ace4… = base), pin RED on the two description legs only, both controls green; restore proven by blob equality (f92510e4…) and git diff HEAD exit 0; 4/4 green after ✓.
    • Gates (dev, exit before any pipe, at 0d6bfefd): types build 0, type-check 0, packages/types/ vitest 115 files / 1964 tests, three changeset gates 0, control-bytes 0 (re-run after staging), spec-symbol derivation 0, eslint on the two files 0 errors ✓.
    • Farm at this reading (12:45Z): 22 of 32 terminal (19 success + 3 skipped); the four Test shards, Type Check, Lint, README Export Check, Bundle Analysis, Skill Example Check in progress — landing waits for them.
    • Serial: base.zod.ts shared with PR feat(types): declare the CEL envelope on visible / hidden / disabled as one shared ExpressionWire (objectui#7530, ruled A) #7726 ([Decision] The CEL envelope object is accepted on visible / hidden / disabled by the shared evaluator but declared on none of them — declare it on all three, or refuse it on all three #7530, predicate members :158–:219) at a different region; origin/main merged before opening (already up to date at b74a859); the queue arbitrates.

    Landing: ready + auto-merge (explicit SQUASH) once every check on 0d6bfefd is green; flip re-check armed. pm:dispatched comes off at the MERGED reading.


    Generated by Claude Code

  8. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    Landing note — PR #7730 enqueued (2026-09-05T13:01Z)

    Pre-landing three:

    1. Clause-② declared no at claim and confirmed at collection: the diff changes one .describe() string on ClassNameStylePropsSchema, no accept-set or public-surface change, so no contract-tier gate applies; ACCEPT on record at comment 5551911142 on this card.
    2. Carriers: neither this card nor PR fix(types): describe ClassNameStylePropsSchema by its two keys #7730 ever carried needs:contract-review (PR labels read back at 13:00Z: package: types, tests); nothing to clear.
    3. Check runs on 0d6bfefd: 29 completed: success + 3 skipped, none failed, none pending; mergeable_state: clean after the ready flip.

    Landing actions:

    • draft:false at 12:59Z.
    • Auto-merge enabled with mergeMethod: SQUASH explicitly at 13:00Z (tool echoes empty method/time — known reading, seat post §4).
    • Positive probe at 13:01Z: git ls-remote origin 'refs/heads/gh-readonly-queue/main/*' shows refs/heads/gh-readonly-queue/main/pr-7730-c354ce5db69b6cfb55d91d2f022b68a7b7cad3ba at 9587fc95, queued behind PR feat(types): declare the CEL envelope on visible / hidden / disabled as one shared ExpressionWire (objectui#7530, ruled A) #7726 (objectui#7530), which edits a different region of the same base.zod.ts; conflicts, if any, go to the queue per the maintainer's file-granularity ruling (seat post §1). main = a00db9ef.

    Next reading: MERGED by two readings (PR merged: true / this card's closed_by_pull_requests state MERGED, and origin/main containing the squash), then strip pm:dispatched here. Until then pm:dispatched stays on.


    Generated by Claude Code

  9. os-justin commented on Sep 5, 2026

    @os-justin
    Collaborator

    MERGED — two readings (2026-09-05T13:19Z, session session_01BAZFhALsQsGqxui8sNqM8s): (1) PR #7730 reads merged: true, merged_at 13:16:16Z, and this card's closed_by_pull_requests names PR #7730 in state MERGED (card closed 13:16:18Z, state_reason: completed); (2) origin/main contains the squash 9587fc95 ("fix(types): describe ClassNameStylePropsSchema by its two keys (#7578) (#7730)"), which is the main tip at 13:19Z. Landed head 0d6bfefd; ACCEPT on record at 5551911142 (Clause-② no).

    pm:dispatched stripped in the same round; domain:spec, priority:p3, finding and package: types stay (finding is triage's to drop — seat post §4).


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanefindingpackage: typespriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions