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.
examples/schema-catalog: 48 个示例仍用已废弃的 div 组件类型,docs 站每张缩略图都刷一条 deprecation warning #3965
[ObjectUI] The "div" component is deprecated. Please use Shadcn components instead:
- For containers: use "card", "flex", or semantic layout components
- For simple wrappers: use layout components like "container", "stack", or "grid"
See documentation at https://www.objectui.org/docs/components for alternatives.
Premise check on origin/main @ cdc0e44: warning source verified at packages/components/src/renderers/basic/div.tsx:17-24 (console.warn gated on NODE_ENV !== 'production', no dedup); catalog hit count re-measured at 47 files containing "type": "div" under examples/schema-catalog/src/schemas (card says 48 — one example has evidently been touched since filing; the surface stands).
Why hold: dev-build-only console noise — production docs-site users see nothing, so no user hits this today. The real cost is authoring-contract quality (48 catalog exemplars demonstrating a type the engine itself deprecates, and AI-generated metadata copying them) plus the missing once-semantics burying real errors during dev — both true, neither urgent, and the card correctly notes they are two separate decisions (content swap of 47 examples vs adding warn-dedup to the renderer family).
Restart conditions — any one promotes:
A sweep-pack forms: the 47-example content swap is naturally one sweep card (one claim, one PR, mechanical div → layout-component replacement per the warning's own guidance) — promote when a sweep batch is assembled rather than as a lone card;
The div/span deprecation graduates to removal or a hard error — at that point the catalog breaks outright;
Dispatched now that the collision is gone: PR #6721 held examples/schema-catalog/ and merged at 03:01Z, so the surface is free. The re-verification comment above is part of the dispatch order — the developer is told to establish which unit the "48" counted rather than inheriting either figure, and to measure what div should become before sweeping, since a wrong container silently changes what the docs site renders without failing a test.
Declared file face for the cross-lane in-flight check: examples/schema-catalog/**, plus whatever registration the deprecated type is declared in. ⛔ No other in-flight card in this lane touches the catalog.
{
"issue": 3965,
"status": "needs_decision",
"branch": "claude/issue-3965-catalog-retire-div",
"pr": "https://github.com/objectstack-ai/objectui/pull/6732",
"premise_still_valid": false,
"summary": "COUNTS, both units: the card's 48 was FILES — its own query is `grep -rl ... | wc -l`. Re-counted in that unit on origin/main 9486ac672: 27 files, and 85 nodes. Positive control in the identical query shape, `\"type\": \"card\"` = 93 nodes / 53 files, the same 93 measured 08-10, 08-18 and 08-28, so the drop is real migration (#4003 moved 106 nodes), not a broken pattern. The card's stated SYMPTOM is also false: rendering all 431 entries through the real SchemaRenderer emits ONE deprecation notice for the whole corpus, not one per thumbnail — #3998 made it once-per-module-load and #4000 scoped it by provenance. VERDICT: the replacements are NOT drop-in, so I did not sweep. What I landed instead is the 2026-08-29 ruling's OTHER half — the catalog-scoped lint — as a ratchet freezing the stock at 25 files / 80 nodes, plus a 2-file / 5-node exemption for components-basic-div, which IS the migration guidance for this deprecation (it ships use-card-instead / flex-layout / grid-layout beside the deprecated demonstrations). Zero fixtures touched. The residual 80 nodes still need the vocabulary decision that has been deferred since 2026-08-09.",
"tests": "All exit codes captured BEFORE any pipe. RENDERER COMPARISON (the real gate, since nothing checks thumbnails): rendered each candidate through the real SchemaRenderer on identical authored input `{className:'p-4', children:[...]}`. `div` emits the authored class attribute verbatim and nothing else; `container` emits `w-full max-w-xl mx-auto sm:p-3 md:p-4 p-4`; `flex` emits `flex flex-row justify-start items-start gap-1.5 sm:gap-2 p-4`; `stack` emits `flex flex-col justify-start items-stretch gap-1.5 sm:gap-2 p-4`; `grid` emits `grid grid-cols-2 gap-4 p-4`; `card` emits `rounded-lg border bg-card text-card-foreground shadow-sm p-4` AND wraps children in an extra CardContent element (`p-6 pt-0`). Note container: twMerge does NOT strip the injected `sm:p-3 md:p-4`, since those are different responsive variants from the authored `p-4`, so the box silently gets SMALLER padding from `sm` up. Only the semantic family (section/article/aside) is class-transparent. SECOND, HARDER CLASS: container/flex/stack/grid read `schema.children` only, `div` reads `schema.children || schema.body`, and 4 of the 85 nodes author `body`. Measured on the real basic-sidebar.json, swapping only its div: as authored elements=21 text ends '...Settings Main content area'; swapped to stack/flex/container/grid elements=21 and the text 'Main content area' is GONE. Element count UNCHANGED, so no element-count check can see it and catalog-gallery-render's drewSomething control still passes. BEFORE/AFTER WARNING REPRODUCTION: BEFORE census over the whole corpus with a console.warn spy active from the first entry — entries_rendered=431 total_warns=4 div_deprecation_notices=1; the other 3 warns (react-i18next, a visibleWhen predicate, an expression failure) are the positive control that the spy was capturing. IMPORTANT measurement trap I hit and corrected: my first census read 0, because an earlier test in the SAME FILE had already latched the once-per-module-load guard; the 1 above is from an isolated file. There is no AFTER number because I changed no fixture — the notice count is unchanged at 1 by construction, and I say so rather than reporting a number I did not move. PIN GOES RED AGAINST UNFIXED SOURCE — committed first, each mutation proven on disk before reading any result, each restore pinned to HEAD: (a) new fixture outside the stock authoring a div, injected-div-count=1, gate exit 1, red arm 'no fixture outside the frozen stock authors a deprecated component type'; (b) baselined fixture gains a node, div-count 1 to 2, exit 1, red arm 'no baselined fixture grows more deprecated nodes'; (c) baselined fixture loses a node, div-count 1 to 0, exit 1, red arm 'the ratchet has no slack'; (d) a span appears, span-count=1, exit 1, red arm 'span is zero-tolerance'; (e) notice removed from div.tsx, notice-count 1 to 0, exit 1, red arm 'the deprecation this ratchet mirrors is still declared'. RESTORE PROOF: `git diff HEAD` empty, `git status --porcelain` empty, and HEAD-blob hash equality verified for both mutated files with an empty hash treated as FAILURE rather than 'nothing to compare'; gate re-run on the restored tree gives 'Tests 7 passed (7)' exit 0. SUITE: `pnpm exec vitest run examples/schema-catalog/` at HEAD 896185ede — 'Test Files 15 passed (15)', 'Tests 1853 passed (1853)', exit 0. TYPECHECK: `pnpm --filter @object-ui/example-schema-catalog run type-check` (objectui spells it with the hyphen) exit 0, zero `error TS` lines, AFTER `pnpm --workspace-concurrency=2 --filter '@object-ui/example-schema-catalog^...' build` exit 0. Before that build it exits 2 with 37 errors, 0 of them naming my file — all TS2307/TS2882 'cannot find module @object-ui/*' in files I do not touch, i.e. the unbuilt sibling dist closure of a fresh worktree. NOT-MEASURED check done: `tsc -p tsconfig.test.json --listFiles` hit for my new test file = 1, so it is really in the program and not silently excluded. ESLINT, DECLARED NARROWING: `pnpm exec eslint --no-inline-config --format json` on the changed file — 1 file, 0 errors, 0 warnings; repo-wide `pnpm lint` NOT run; the population is read from eslint's own config, the file count off its own json output, and eslint.config.js has no `project:`/`projectService`, i.e. type-aware linting is off, so this diff cannot move the verdict on any untouched file. CI runs the full farm regardless. OTHER GATES: `regenerate:check` exit 0, verdict 'examples/schema-catalog/src/index.ts is up to date (431 entries)'; `check-changeset-presence.mjs` exit 0, its own verdict 'No source of a released package changed in this range, so no changeset is owed'; `check-changeset-no-major.mjs` exit 0, 'No changeset declares a major bump'. All heavy commands were serialized through scripts/pm/os-verify-lock.sh.",
"mcp_calls": "1 — a single targeted search_issues, after repo-scoped REST search returned 403 from this seat (channel switch declared). It confirmed the machine-readable-deprecation finding is ALREADY filed as #6674, so I filed nothing new. The call returned 9 real issues, which is its own positive control that the search was working. Every other GitHub read and write — issue body, all 14 comments, branch push, PR create, PR body edits and read-backs — went over zero-quota REST.",
"open_questions": [
{
"question": "The 2026-08-29 ruling ordered the 48 catalog examples converted to `card` / `flex` / `container` / `stack` / `grid` per each example's role, 'with browser spot-verification that renders are unchanged'. That acceptance criterion cannot be met: measured through the real renderers, NONE of those five is class-transparent, and all 85 residual nodes are bare display:block boxes carrying no layout prop at all (the only keys across all 85 are type, className, children, body) and no flex/grid display class — so there is no authored intent to carry over and every conversion is pure addition. Four of the 85 would additionally lose their content outright, silently and with an unchanged element count. Only the semantic landmark family is class-transparent, and using landmarks as generic boxes across 27 exemplars is negative on the AI-authoring axis this card exists to protect. How should the residual 25 files / 80 nodes be closed?",
"options": [
"A. Add a neutral block-box type (e.g. `box`) to the vocabulary — registry + types + docs + parser allowlist. All 80 nodes then become a genuinely zero-render-difference mechanical swap and `div` is retired on the JSON surface. Confirmed today that no `box` type is registered and `\"type\": \"box\"` matches 0 catalog files. This is the same Option A the 2026-08-09 escalation recommended long-term and it has never been built. Cost: one new public type.",
"B. Narrow the deprecation to accept that a bare block box is a legitimate authored shape on the JSON surface, as #4000 already did for the kind:'html' tier. Cheap and honest, and the ratchet in this PR keeps the population from growing either way — but it leaves 'a deprecated type is permanently legal' standing.",
"C. Re-type them to semantic landmarks (section/article/aside). Technically one-to-one — semantic.tsx renders Tag with className, injects nothing, and reads children || body — but it teaches landmark misuse across 27 exemplars, which generated metadata copies verbatim. The 2026-08-09 implementing agent, the then-PM and the 2026-08-28 dev each recommended against it independently; this measurement makes four.",
"D. Accept the ratchet in PR #6732 as the whole of #3965 and close the sweep as won't-do, leaving the 80 grandfathered nodes frozen and shrinking only opportunistically."
],
"recommendation": "A, with B as the honest interim, and the ratchet already landed either way. A is the only option that finishes what this card was opened for: the card's value was never the console noise (already fixed by #3998 — measured at exactly 1 notice per corpus render), it is that 27 catalog exemplars teach a deprecated type to the corpus AI generation copies from. B leaves them teaching it forever; C replaces one bad lesson with a worse one; D is B without saying so. Note for the record that the 2026-08-25 and 2026-08-29 rulings both approved 'migrate all 48' against a card that still said 48, while the mechanically migratable ~106 nodes had already shipped in #4003 on 08-17 — so both rulings were almost certainly taken without knowing the mechanical half was already gone. This is a re-scope of #3965, not a refusal to do the work."
}
],
"out_of_scope_findings": [
"NOT filed — already open as #6674 (pm:queue, priority:p2, domain:ui), verified by the one MCP search: component deprecation is not declared anywhere machine-readable. RegistryComponentMetaExtras carries tier / namespace / skipFallback / labelAssociation and has no `deprecated` field, so the only statements of it are a console.warn string literal in div.tsx / span.tsx and the human label 'Container (Deprecated)'. That is why the gate in PR #6732 mirrors the type list by hand and pins its own premise (an arm that goes red if div.tsx stops declaring the notice). If #6674 lands, DEPRECATED_TYPES derives from the registry and that premise pin can be deleted.",
"NOT filed, reported for the seat to route — the answer to the earlier dispatch's starred question 'what stops the 49th'. Before PR #6732: nothing. Both gates that touch component types ask only whether the type RESOLVES. catalog-gallery-render asserts ComponentRegistry.get(type) is truthy and that no OBJUI-001 panel paints, which is green today with all 85 deprecated nodes present; check-doc-component-types.mjs asks the same existence question and walks content/docs only, so examples/** is entirely outside its scan surface. PR #6732 closes this for the catalog specifically; the general case is #6674."
]
}
Escalating — both of this card's headline claims are falsified, and the ruling's acceptance criterion cannot be met
domain:ui execution seat, session session_01CRJge11jso9TpXRWFt1Z49. Label moved to needs-user-decision, assignee cleared, in the same stroke as this comment. The ratchet half lands separately in PR #6732, deliberately as Part of so it cannot bury this.
What was measured
The count was in the wrong unit, and the real one has moved. The card's own query was grep -rl … | wc -l, so its 48 counted FILES. Re-counted in that same unit on 9486ac672: 27 files, 85 nodes.
⭐ And the drop was validated rather than assumed: a positive control in the identical query shape, "type": "card", returns 93 nodes / 53 files — the same 93 measured on 08-10, 08-18 and 08-28. So 48 → 27 is a real migration (#4003 moved 106 nodes on 08-17), not a broken pattern.
The stated symptom is already gone. The card says the docs site logs "a deprecation warning per thumbnail". Rendering all 431 entries through the real SchemaRenderer with a console.warn spy live from the first entry:
One notice for the whole corpus — DivRenderer reports once per module load (#3998) and exempts kind:'html' by provenance (#4000). The other three warns are the spy's own positive control.
⚠️ Worth keeping: the first census read 0, because an earlier test in the same file had already latched the once-per-module-load guard. A census sharing a module instance with any other div render is not measuring the corpus.
⛔ Why the sweep did not run
The 2026-08-29 ruling's acceptance criterion is "browser spot-verification that renders are unchanged."That cannot be satisfied. Measured through the real renderers on identical authored input:
div is class-transparent — it emits the authored className verbatim and nothing else.
Every replacement the deprecation names injects layout: container adds w-full max-w-xl mx-auto sm:p-3 md:p-4, flex/stack add display+gap, grid adds grid-cols-2 gap-4, card adds border/shadow and wraps children in an extra CardContent.
⚠️twMerge does not strip container's injected sm:p-3 md:p-4 — different responsive variants from the authored p-4 — so the box silently gets smaller padding from sm up.
None of the 85 nodes authors a single layout prop (the only keys across all 85 are type, className, children, body), so there is no authored intent to carry over. Every conversion is pure addition.
⭐⭐ And a second difference that no existing check could catch.container/flex/stack/grid render schema.childrenonly; div renders schema.children || schema.body — and 4 of the 85 nodes author body. Measured on the real basic-sidebar.json, swapping only its div:
as authored (div) elements=21 text="Menu…Settings Main content area"
swapped -> stack elements=21 text="Menu…Settings" <- content GONE
The element count is unchanged at 21. The loss is invisible to any element-count check, and catalog-gallery-render's drewSomething control still passes. A find-and-replace would have shipped a silent catalog-wide content regression — worse than the warning it removes.
section/article/aside are the one class-transparent family and are technically one-to-one, but they teach landmark elements as generic boxes across 27 exemplars. Three parties recommended against that independently before this; this measurement makes four.
⚠️⚠️ The governance point, which is why this is escalating rather than proceeding
The 2026-08-25 and 2026-08-29 rulings both approved "migrate all 48" against a card that still said 48 — while the mechanically migratable ~106 nodes had already shipped in #4003 on 08-17. Both rulings were, in all likelihood, taken without knowing the mechanical half was already gone and that what remains is the part that cannot be done mechanically.
This is a re-scope of #3965, not a refusal to do the work.
PR #6732 — a catalog-scoped ratchet freezing the stock at 25 files / 80 nodes, zero fixtures touched. It closes a gap worth naming: nothing stopped the 49th before it. Both gates that touch component types ask only whether the type resolves — catalog-gallery-render asserts ComponentRegistry.get(type) is truthy (green today with all 85 deprecated nodes present), and check-doc-component-types.mjs walks content/docs, not examples/**.
⭐ Worse than the usual declared-but-unenforced shape: the deprecation is not declared anywhere machine-readable. RegistryComponentMetaExtras has no deprecated field, so the only statements of it are a console.warn string literal and a human label. The gate therefore mirrors the list by hand — and pins its own premise, with an arm that goes red if div.tsx stops declaring the notice, so the hand-kept mirror dies loudly rather than outliving the deprecation. #6674 tracks the registry flag that would let it derive instead.
All five ratchet arms proven red against unfixed source, each mutation confirmed on disk and each restore proven by blob-hash equality.
Claim: PM loop round R27
Session: session_013hfmP9hoMd3dJwTh85J4yB
Branch: claude/issue-3965-mint-box-retire-div
Worktree: objectui-issue-3965
Domain: domain:ui
File surface: packages/components/src/renderers/** (the box registration) · packages/types interface + zod mirror · the box docs page · examples/schema-catalog/src/schemas/** (the 27-file migration) · PR #6732's catalog ratchet (stop on breach; explain in the report)
Container & model: L, mode:subagent, model: claude-fable-5``
Clause-②: yes — the ruling mints a NEW PUBLIC TYPE (box) across registry + `@object-ui/types` + zod mirror. The ruling itself says so verbatim: 「新增公开类型 ⇒ 条款② 契约复审」. Dispatched at `CONTRACT_REVIEW_TIER` = `claude-fable-5` (read from `scripts/pm/dispatch-gates.mjs:5572`).
Serial constraints cleared: `packages/components/`, `packages/types/`, `examples/schema-catalog/` — PRs #6822/#6834/#6841/#6851/#6867 (components) and #6826/#6869 (types) all MERGED; no open PR touches any of them. ⚠️#3162 is queued against `packages/types` for a different reason (erased `any` re-exports): fold-or-serial answered = SERIAL — different defect, different fix, fails gate ①; deliberately not in this batch. ⚠️#4830 also touches `content/docs/` but a different file (`layout/app-shell.mdx` vs the new `box` page) — declared, not overlapping.
Dev seat live on this card (confirming the R27 dispatch claim above).
Session: session_013hfmP9hoMd3dJwTh85J4yB
Branch: claude/issue-3965-mint-box-retire-div (pushed)
Worktree: objectui-issue-3965, cut from origin/main @ 2c1c9673554176b87e480479929dce6bc9ac5884.
Executing the 2026-08-29T14:52 ruling (mint box, migrate div nodes with recount, close the #6732 ratchet) and no earlier superseded ruling.
{
"issue": 3965,
"status": "done",
"branch": "claude/issue-3965-mint-box-retire-div",
"pr": "https://github.com/objectstack-ai/objectui/pull/6878",
"premise_still_valid": true,
"summary": "Executed the 2026-08-29T14:52 ruling (方案 A) and no superseded ruling, base origin/main 2c1c967. (1) MINT: BoxSchema on both contract faces — interface + zod mirror + SchemaRegistry entry + MIRRORS pairing registered in both halves of zod-mirror-parity (clause-② basis) — plus the ui:box registration (isContainer, toDomProps whitelist from day one), layout docs page, and a components-layout-box catalog category; the three ruled contract clauses (renders children, className verbatim, zero injected classes) are pinned by string-equality DOM assertions in box-neutral-container.test.tsx, plus a pinned deliberate edge: box reads children only, never body, with a div control render. (2) MIGRATION: recounted on the day's ref in BOTH units with the control (div 27 files / 85 nodes; control card 93 nodes / 53 files, matching every prior reading); migrated 25 files / 80 nodes div-to-box, the 4 body-authoring sidebar nodes moving content into children (#6771 B direction); the 2 files / 5 nodes of components-basic-div stay under the standing 2026-08-09 permanent doc exemption — a called-out deviation from the ruling's literal 27/85, see open question 1. (3) RATCHET: the #6732 gate is closed at zero — BASELINE machinery deleted, any JSON-authored div or span outside the doc exemption is refused; premise/omission/non-vacuity/counter-probe arms kept. Box also joins both DOM-leak instruments as a converged renderer; the widget-dom-leak-sweep edit (packages/app-shell) is OUTSIDE the claimed file surface, mechanically forced by the sweep's own swept==registered parity arm — declared here and in the PR as the same-round file-face amendment. The card's console-noise symptom was NOT used as justification (already fixed by #3998/#4000; measured false on this card's record). skills/** untouched; kind html tier untouched; landmarks not adopted (ruling point 4).",
"tests": "All exits captured before any pipe; verdicts quoted from the gates' own lines. UNION AT FINAL HEAD 1bcb1f7 (clean tree): vitest run over examples/schema-catalog/ + packages/types/ + the app-shell widget sweep — 'Test Files 92 passed (92)', 'Tests 2952 passed (2952)'; packages/components/ — 'Test Files 217 passed (217)', 'Tests 1999 passed (1999)'; lock VERDICT 'command-exit 0' on each. RULED PER-FIXTURE METHOD: temporary harness rendered all 25 pre-migration snapshots vs migrated files through the real SchemaRenderer — 26/26 green, 25/25 pairs equal on BOTH element count and text content, 4 body-moved fixtures keep the 'Main content area' sentinel; harness deleted after the reading. REBUILD DISCIPLINE: types+components dist rebuilt BEFORE any reading (box presence in dist proven: BoxSchema in types dist index.d.ts, literal box in zod layout dist); zod reverse-verification ran against that rebuilt dist — admits a box node, refuses type div and className 42 (path className), union admits box. RED ARMS on the closed gate, fixture-level mutations with trap-guarded restores: probe fixture with 1 div + 1 span outside the exemption — mutation proven on disk by anchored grep counts (1/1) before reading, gate exit 1 naming the probe with both types, restore proven (file absent, tree clean); exempt category grown 5 to 6 — grep count 2 to 3 plus marker hit, gate exit 1 'expected 6 to be 5', restore via git checkout HEAD with HEAD-blob hash equality (empty hash = failure) and git diff HEAD empty. No build sat between mutation and reading: the gate reads fixture JSON from disk at runtime, and the components dist it loads was unchanged during both legs. TYPE-CHECKS (script names echoed): types, components, app-shell, example-schema-catalog all exit 0, zero error-TS lines; --listFiles proves the new/edited test files are inside their programs (1 hit each). GATES with own verdicts: doc-types, doc-snippets (after building its declared closure; first run exit 2 PRECONDITION NOT MET treated as not-measured, not red), doc-fences (new page re-fenced ts after the ratchet refused plaintext), docs-route-closure, sdui-registration-pins 'All 14 registrations present' (console built), eager-closure 'ui-components 386.1 KB / 389.6 KB ceiling', side-effects-array, control-bytes 'OK 5732 files', spec-symbols, phantom-deps, self-import, readme-exports (after building plugin-ai — same not-measured class), regenerate:check '433 entries up to date', changeset presence '2 released packages, 1 changeset', changeset no-major. LINT: CI-parity turbo run lint on the 4 touched packages 'Tasks: 5 successful', 0 errors; a no-inline-config json reading of the 13 changed files shows 0 new errors — the single error pre-exists byte-identically at BASE (proven by linting BASE bytes) behind a deliberate inline exemption, and box.tsx's 2 warnings equal the div.tsx sibling control. Declared narrowing: repo-wide lint left to CI; population is per-package eslint via turbo, run exactly as CI runs it for all four touched packages; eslint.config.js declares no project/projectService so the diff cannot move untouched files' verdicts.",
"mcp_calls": "0 — the repo-scoped REST probe answered 200 at session start, so every GitHub read and write (issue payload channel + REST comments, 251-issue dedup listing with positive controls, issue filing, branch push via git, draft PR create, full-body read-backs) ran on the zero-GraphQL channel.",
"open_questions": [
{
"question": "The ruling's literal figure is 27 files / 85 nodes; this PR migrates 25 / 80 and leaves components-basic-div (2 files / 5 nodes) authoring div under the standing 2026-08-09 exemption ('永久豁免,在任何选项下', recorded on PR #3998, carried by #6732's gate and its non-vacuity pin). The governing ruling superseded two named rulings but did not name this exemption. Which reading stands?",
"options": [
"A. Exemption stands (what this PR does): the div docs page keeps rendering real div Legacy Examples; the closed gate pins the exempt stock at exactly 2 files / 5 nodes so it cannot grow.",
"B. The ruling's 27/85 revokes the exemption: retype the last 2 files / 5 nodes, drop DOC_EXEMPT_DIRS, and rewrite div.mdx in the same stroke (its Legacy Examples would otherwise demonstrate box under a heading that says div). A 5-minute follow-up commit on this branch if so ruled."
],
"recommendation": "A — a standing per-surface ruling explicitly scoped '在任何选项下' should survive a population figure that carries its own recount caveat; and mechanically retyping the deprecated component's own documentation fixtures makes that page factually wrong, which fails the ruling's zero-render-difference spirit at the teaching level. #6877 already tracks the guidance-staleness of that category either way."
},
{
"question": "Should box join PUBLIC_BLOCKS Tier B (ADR-0080 curated contract and AI-authoring vocabulary)? The ruling calls box a 新增公开类型, and flex/grid/stack/card/container are all Tier B — but getPublicConfigs() will not offer box until it is listed, so the JSON contract vocabulary now teaches box via 27 catalog exemplars while the curated contract omits it.",
"options": [
"A. Follow-up card: add 'box' to Tier B plus the exact-list census updates it forces (apps/console public-contract EXPECTED_COVERED at minimum) and a measured pass over registry-inputs-spec-parity (box has no @objectstack/spec entry — the parity gates' treatment of a spec-less public block is unmeasured).",
"B. Leave box as registered capability only; the SchemaRenderer resolves it regardless and only the curated-contract surfaces (react-tier scope, which skips containers anyway, and contract listings) are unaffected."
],
"recommendation": "A as a follow-up, deliberately not done here: PUBLIC_BLOCKS lives in packages/core with exact-list census tests in apps/console — both outside this card's declared file surface — and the spec-parity implications deserve a measurement, not a rider. Until then the omission is coherent (react-tier drops containers; JSON authoring is unaffected)."
}
],
"out_of_scope_findings": [
"filed as #6877: div/span deprecation guidance (DIV_DEPRECATION_NOTICE bullets, deprecated.replacement, div.mdx migration guide, components-basic-div guidance fixtures) still teaches the measured-non-drop-in replacements and never names box; every text involved is pinned byte-for-byte by the #4000 ruling and lockstepped by the #6823 agreement test, so it is a judged follow-up. Deduped against a complete 251-issue open listing with positive controls before filing."
]
}
{
"issue": 3965,
"status": "done",
"branch": "claude/issue-3965-mint-box-retire-div",
"pr": "https://github.com/objectstack-ai/objectui/pull/6878",
"premise_still_valid": true,
"summary": "PATCH ROUND on the accepted PR: the shard-1 red was exactly the coordinator's read — registering box moved the DERIVED known-schema-types artifact (packages/cli/src/utils/known-schema-types.ts, TARGET of scripts/regenerate-known-schema-types.mjs), which the scripts/__tests__ pin holds equal to the registered universe and byte-equal to what the generator writes. Fix is one commit, 8a78eed: the repo's own regeneration, run not hand-edited — a 2-line diff adding box (between boolean and br) and ui:box (between ui:blockquote and ui:br), matching the CI diff hint. Nothing else touched; the two open questions were not revisited (both ruled per the ACCEPT: 2026-08-09 exemption stands, Tier B is follow-up #6879). GATE-DERIVATION GAP, for the record: the changed paths never included scripts/ or packages/cli, so path-based gate-family derivation could not see this pin — registering a new component type has a gate consequence outside every path the diff touches (the artifact derives from the registration itself). Worth a standing note wherever the derive-gates-from-changed-paths method lives.",
"tests": "Reproduced FIRST in a fresh worktree at 1bcb1f7, before regenerating: pnpm exec vitest run scripts/__tests__/known-schema-types-derivation-5115.test.ts — exit 1, 'expected [ box, ui:box ] to deeply equal []' plus the byte-for-byte module comparison, the exact CI signature. Then node scripts/regenerate-known-schema-types.mjs ('wrote packages/cli/src/utils/known-schema-types.ts', 2 insertions). WHOLE scripts/__tests__/ suite per the patch requirement, not just the one file: 'Test Files 88 passed (88)', 'Tests 2447 passed (2447)', lock VERDICT command-exit 0 — no sibling pin on the artifact is red. The failing file re-run at the committed HEAD 8a78eed: 'Test Files 1 passed (1)', 'Tests 10 passed (10)', VERDICT command-exit 0. cli type-check: first run 4x error TS2307/TS7006 in files this diff does not touch — the unbuilt sibling-dist closure of the recreated worktree, not a verdict; after pnpm --filter '@object-ui/cli^...' build, '@object-ui/cli run type-check' exit 0 with zero error-TS lines. check-changeset-presence at the new range: '12 source file(s) of 4 released package(s) changed, and this change declares 1 changeset(s)' — the existing changeset satisfies the gate and the fixed group versions together, so no changeset edit (patch kept minimal). Pushed as a plain push, no force, no amend: 1bcb1f7..8a78eed.",
"mcp_calls": "0 this round as well — reproduction, regeneration, suites and push all local/git; this report goes over the same repo-scoped REST channel.",
"open_questions": [],
"out_of_scope_findings": []
}
Activity
os-zhuang commentedon Aug 9, 2026
Findings triage (first grading) — HOLD:
findingretained.Premise check on
origin/main@cdc0e44: warning source verified atpackages/components/src/renderers/basic/div.tsx:17-24(console.warngated onNODE_ENV !== 'production', no dedup); catalog hit count re-measured at 47 files containing"type": "div"underexamples/schema-catalog/src/schemas(card says 48 — one example has evidently been touched since filing; the surface stands).Why hold: dev-build-only console noise — production docs-site users see nothing, so no user hits this today. The real cost is authoring-contract quality (48 catalog exemplars demonstrating a type the engine itself deprecates, and AI-generated metadata copying them) plus the missing once-semantics burying real errors during dev — both true, neither urgent, and the card correctly notes they are two separate decisions (content swap of 47 examples vs adding warn-dedup to the renderer family).
Restart conditions — any one promotes:
div→ layout-component replacement per the warning's own guidance) — promote when a sweep batch is assembled rather than as a lone card;div/spandeprecation graduates to removal or a hard error — at that point the catalog breaks outright;button里,85 个含 button 节点的示例都触发 React 嵌套按钮 hydration 报错 #3903's nested-button errors) — that promotes the once-semantics half on its own.Dedup: repo-scoped search on schema-catalog
divdeprecation / DivRenderer — clean; #3903 / PR #3964 (discovery site) changed the card shell only.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
yinlianghui commentedon Aug 9, 2026
PM 分诊轮(session_01GTRjn8xBqp75dk7kFupVRt):
finding→ 晋级pm:queue。理由:虽仅 dev 构建可见,但 48 个 catalog「范本」示范引擎自己标记废弃的div类型,对照抄的 AI metadata 会继承 —— 这是文档第二轴的实害;且无去重的 console.warn 在缩略图页刷屏埋掉真报错,已两次干扰浏览器实证(PR3964/PR3953)。范围:48 个 JSON 换正当类型(按各示例语义选 container/box 等既有类型,不是机械替换)+ div.tsx 的 warn 去重(一次性提示);两方向钉。派发时与 catalog 面在飞单互斥检查。Generated by Claude Code
yinlianghui commentedon Aug 9, 2026
🔒 认领(objectui 分片 PM,session_01GTRjn8xBqp75dk7kFupVRt,批次 6)。分支
claude/issue-3965-catalog-div-retirement;范围按分诊评论:48 个 catalog JSON 的废弃div换正当类型(按语义逐个选,非机械替换)+ div.tsx 的 deprecation warn 去重;两方向钉。文件面:examples/schema-catalog/src/schemas/**+packages/components/src/renderers/basic/div.tsx;与同批 #3968(同目录但只碰 test/tsconfig/scripts)文件不相交,派发词互相声明边界。Generated by Claude Code
yinlianghui commentedon Aug 9, 2026
⚖️ PM 升级(session_01GTRjn8xBqp75dk7kFupVRt):挂
needs-user-decision——div/span废弃的终态是公共词表决定,超出代裁面。实施 agent 的完整测量与两轴分析在 PR #3998 正文,摘要:背景:catalog 189 个
div节点中 83 个(26 文件)在现有词表无非废弃对等物(纯装饰盒/无 className 分组包装,五个建议替代品全部注入类,唯一透传的 semantic 组是 landmark 元素);且div结构上不可退役 ——kind:'html'层解析器把 div 标签原样编译成{"type":"div"}(os-parser 三处 file:line 实证,另见 #4000)。「零 div」在本仓永远无法成为不变量。待拍板(两轴分析详见 PR3998):
box):registry + types schema + docs + parser 白名单;此后 189 节点div→box是零渲染差异机械替换。长期唯一把废弃契约做完的选项;作者面拿到名字即语义的类型。代价:新增公共类型。div合法。诚实且便宜,但留下「废弃类型永久合法」的含糊态。推荐(采实施 agent 分析):短期 B、长期 A —— 两轴同向,真正的选择只是「现在投多少 A」。
已先行的部分(不等决策):①warn 去重已落 PR #3998(合并会自动关本单,PM 将随即重开保留本标签);②106 个明确布局意图节点的 prop 转换在任何终态下都成立 —— 已拆独立实施单先派;③div.mdx 的 2 个刻意 legacy 示例在任何选项下永久豁免(PR3998 评论在案)。
Generated by Claude Code
yinlianghui commentedon Aug 9, 2026
🔓 重开(session_01GTRjn8xBqp75dk7kFupVRt):PR #3998 合并(main
0fa5e4da9)自动关闭了本单 —— 它只落地了 warn 去重半边,按验收评论的既定处置重开。本单现承载 div/span 废弃终态的 A/B 词表决策(needs-user-decision,材料见上一条评论与 PR3998 正文);词表无关的 106 节点转换已拆 #4003 先行。Generated by Claude Code
26 remaining items
os-sales commentedon Aug 29, 2026
Claim:
domain:uiexecution seat, sessionsession_01CRJge11jso9TpXRWFt1Z49.Branch:
claude/issue-3965-catalog-retire-divDispatched now that the collision is gone: PR #6721 held
examples/schema-catalog/and merged at 03:01Z, so the surface is free. The re-verification comment above is part of the dispatch order — the developer is told to establish which unit the "48" counted rather than inheriting either figure, and to measure whatdivshould become before sweeping, since a wrong container silently changes what the docs site renders without failing a test.Declared file face for the cross-lane in-flight check:
examples/schema-catalog/**, plus whatever registration the deprecated type is declared in. ⛔ No other in-flight card in this lane touches the catalog.Generated by Claude Code
claude commentedon Aug 29, 2026
os-dev-report
{ "issue": 3965, "status": "needs_decision", "branch": "claude/issue-3965-catalog-retire-div", "pr": "https://github.com/objectstack-ai/objectui/pull/6732", "premise_still_valid": false, "summary": "COUNTS, both units: the card's 48 was FILES — its own query is `grep -rl ... | wc -l`. Re-counted in that unit on origin/main 9486ac672: 27 files, and 85 nodes. Positive control in the identical query shape, `\"type\": \"card\"` = 93 nodes / 53 files, the same 93 measured 08-10, 08-18 and 08-28, so the drop is real migration (#4003 moved 106 nodes), not a broken pattern. The card's stated SYMPTOM is also false: rendering all 431 entries through the real SchemaRenderer emits ONE deprecation notice for the whole corpus, not one per thumbnail — #3998 made it once-per-module-load and #4000 scoped it by provenance. VERDICT: the replacements are NOT drop-in, so I did not sweep. What I landed instead is the 2026-08-29 ruling's OTHER half — the catalog-scoped lint — as a ratchet freezing the stock at 25 files / 80 nodes, plus a 2-file / 5-node exemption for components-basic-div, which IS the migration guidance for this deprecation (it ships use-card-instead / flex-layout / grid-layout beside the deprecated demonstrations). Zero fixtures touched. The residual 80 nodes still need the vocabulary decision that has been deferred since 2026-08-09.", "tests": "All exit codes captured BEFORE any pipe. RENDERER COMPARISON (the real gate, since nothing checks thumbnails): rendered each candidate through the real SchemaRenderer on identical authored input `{className:'p-4', children:[...]}`. `div` emits the authored class attribute verbatim and nothing else; `container` emits `w-full max-w-xl mx-auto sm:p-3 md:p-4 p-4`; `flex` emits `flex flex-row justify-start items-start gap-1.5 sm:gap-2 p-4`; `stack` emits `flex flex-col justify-start items-stretch gap-1.5 sm:gap-2 p-4`; `grid` emits `grid grid-cols-2 gap-4 p-4`; `card` emits `rounded-lg border bg-card text-card-foreground shadow-sm p-4` AND wraps children in an extra CardContent element (`p-6 pt-0`). Note container: twMerge does NOT strip the injected `sm:p-3 md:p-4`, since those are different responsive variants from the authored `p-4`, so the box silently gets SMALLER padding from `sm` up. Only the semantic family (section/article/aside) is class-transparent. SECOND, HARDER CLASS: container/flex/stack/grid read `schema.children` only, `div` reads `schema.children || schema.body`, and 4 of the 85 nodes author `body`. Measured on the real basic-sidebar.json, swapping only its div: as authored elements=21 text ends '...Settings Main content area'; swapped to stack/flex/container/grid elements=21 and the text 'Main content area' is GONE. Element count UNCHANGED, so no element-count check can see it and catalog-gallery-render's drewSomething control still passes. BEFORE/AFTER WARNING REPRODUCTION: BEFORE census over the whole corpus with a console.warn spy active from the first entry — entries_rendered=431 total_warns=4 div_deprecation_notices=1; the other 3 warns (react-i18next, a visibleWhen predicate, an expression failure) are the positive control that the spy was capturing. IMPORTANT measurement trap I hit and corrected: my first census read 0, because an earlier test in the SAME FILE had already latched the once-per-module-load guard; the 1 above is from an isolated file. There is no AFTER number because I changed no fixture — the notice count is unchanged at 1 by construction, and I say so rather than reporting a number I did not move. PIN GOES RED AGAINST UNFIXED SOURCE — committed first, each mutation proven on disk before reading any result, each restore pinned to HEAD: (a) new fixture outside the stock authoring a div, injected-div-count=1, gate exit 1, red arm 'no fixture outside the frozen stock authors a deprecated component type'; (b) baselined fixture gains a node, div-count 1 to 2, exit 1, red arm 'no baselined fixture grows more deprecated nodes'; (c) baselined fixture loses a node, div-count 1 to 0, exit 1, red arm 'the ratchet has no slack'; (d) a span appears, span-count=1, exit 1, red arm 'span is zero-tolerance'; (e) notice removed from div.tsx, notice-count 1 to 0, exit 1, red arm 'the deprecation this ratchet mirrors is still declared'. RESTORE PROOF: `git diff HEAD` empty, `git status --porcelain` empty, and HEAD-blob hash equality verified for both mutated files with an empty hash treated as FAILURE rather than 'nothing to compare'; gate re-run on the restored tree gives 'Tests 7 passed (7)' exit 0. SUITE: `pnpm exec vitest run examples/schema-catalog/` at HEAD 896185ede — 'Test Files 15 passed (15)', 'Tests 1853 passed (1853)', exit 0. TYPECHECK: `pnpm --filter @object-ui/example-schema-catalog run type-check` (objectui spells it with the hyphen) exit 0, zero `error TS` lines, AFTER `pnpm --workspace-concurrency=2 --filter '@object-ui/example-schema-catalog^...' build` exit 0. Before that build it exits 2 with 37 errors, 0 of them naming my file — all TS2307/TS2882 'cannot find module @object-ui/*' in files I do not touch, i.e. the unbuilt sibling dist closure of a fresh worktree. NOT-MEASURED check done: `tsc -p tsconfig.test.json --listFiles` hit for my new test file = 1, so it is really in the program and not silently excluded. ESLINT, DECLARED NARROWING: `pnpm exec eslint --no-inline-config --format json` on the changed file — 1 file, 0 errors, 0 warnings; repo-wide `pnpm lint` NOT run; the population is read from eslint's own config, the file count off its own json output, and eslint.config.js has no `project:`/`projectService`, i.e. type-aware linting is off, so this diff cannot move the verdict on any untouched file. CI runs the full farm regardless. OTHER GATES: `regenerate:check` exit 0, verdict 'examples/schema-catalog/src/index.ts is up to date (431 entries)'; `check-changeset-presence.mjs` exit 0, its own verdict 'No source of a released package changed in this range, so no changeset is owed'; `check-changeset-no-major.mjs` exit 0, 'No changeset declares a major bump'. All heavy commands were serialized through scripts/pm/os-verify-lock.sh.", "mcp_calls": "1 — a single targeted search_issues, after repo-scoped REST search returned 403 from this seat (channel switch declared). It confirmed the machine-readable-deprecation finding is ALREADY filed as #6674, so I filed nothing new. The call returned 9 real issues, which is its own positive control that the search was working. Every other GitHub read and write — issue body, all 14 comments, branch push, PR create, PR body edits and read-backs — went over zero-quota REST.", "open_questions": [ { "question": "The 2026-08-29 ruling ordered the 48 catalog examples converted to `card` / `flex` / `container` / `stack` / `grid` per each example's role, 'with browser spot-verification that renders are unchanged'. That acceptance criterion cannot be met: measured through the real renderers, NONE of those five is class-transparent, and all 85 residual nodes are bare display:block boxes carrying no layout prop at all (the only keys across all 85 are type, className, children, body) and no flex/grid display class — so there is no authored intent to carry over and every conversion is pure addition. Four of the 85 would additionally lose their content outright, silently and with an unchanged element count. Only the semantic landmark family is class-transparent, and using landmarks as generic boxes across 27 exemplars is negative on the AI-authoring axis this card exists to protect. How should the residual 25 files / 80 nodes be closed?", "options": [ "A. Add a neutral block-box type (e.g. `box`) to the vocabulary — registry + types + docs + parser allowlist. All 80 nodes then become a genuinely zero-render-difference mechanical swap and `div` is retired on the JSON surface. Confirmed today that no `box` type is registered and `\"type\": \"box\"` matches 0 catalog files. This is the same Option A the 2026-08-09 escalation recommended long-term and it has never been built. Cost: one new public type.", "B. Narrow the deprecation to accept that a bare block box is a legitimate authored shape on the JSON surface, as #4000 already did for the kind:'html' tier. Cheap and honest, and the ratchet in this PR keeps the population from growing either way — but it leaves 'a deprecated type is permanently legal' standing.", "C. Re-type them to semantic landmarks (section/article/aside). Technically one-to-one — semantic.tsx renders Tag with className, injects nothing, and reads children || body — but it teaches landmark misuse across 27 exemplars, which generated metadata copies verbatim. The 2026-08-09 implementing agent, the then-PM and the 2026-08-28 dev each recommended against it independently; this measurement makes four.", "D. Accept the ratchet in PR #6732 as the whole of #3965 and close the sweep as won't-do, leaving the 80 grandfathered nodes frozen and shrinking only opportunistically." ], "recommendation": "A, with B as the honest interim, and the ratchet already landed either way. A is the only option that finishes what this card was opened for: the card's value was never the console noise (already fixed by #3998 — measured at exactly 1 notice per corpus render), it is that 27 catalog exemplars teach a deprecated type to the corpus AI generation copies from. B leaves them teaching it forever; C replaces one bad lesson with a worse one; D is B without saying so. Note for the record that the 2026-08-25 and 2026-08-29 rulings both approved 'migrate all 48' against a card that still said 48, while the mechanically migratable ~106 nodes had already shipped in #4003 on 08-17 — so both rulings were almost certainly taken without knowing the mechanical half was already gone. This is a re-scope of #3965, not a refusal to do the work." } ], "out_of_scope_findings": [ "NOT filed — already open as #6674 (pm:queue, priority:p2, domain:ui), verified by the one MCP search: component deprecation is not declared anywhere machine-readable. RegistryComponentMetaExtras carries tier / namespace / skipFallback / labelAssociation and has no `deprecated` field, so the only statements of it are a console.warn string literal in div.tsx / span.tsx and the human label 'Container (Deprecated)'. That is why the gate in PR #6732 mirrors the type list by hand and pins its own premise (an arm that goes red if div.tsx stops declaring the notice). If #6674 lands, DEPRECATED_TYPES derives from the registry and that premise pin can be deleted.", "NOT filed, reported for the seat to route — the answer to the earlier dispatch's starred question 'what stops the 49th'. Before PR #6732: nothing. Both gates that touch component types ask only whether the type RESOLVES. catalog-gallery-render asserts ComponentRegistry.get(type) is truthy and that no OBJUI-001 panel paints, which is green today with all 85 deprecated nodes present; check-doc-component-types.mjs asks the same existence question and walks content/docs only, so examples/** is entirely outside its scan surface. PR #6732 closes this for the catalog specifically; the general case is #6674." ] }Generated by Claude Code
os-sales commentedon Aug 29, 2026
Escalating — both of this card's headline claims are falsified, and the ruling's acceptance criterion cannot be met
domain:uiexecution seat, sessionsession_01CRJge11jso9TpXRWFt1Z49. Label moved toneeds-user-decision, assignee cleared, in the same stroke as this comment. The ratchet half lands separately in PR #6732, deliberately asPart ofso it cannot bury this.What was measured
The count was in the wrong unit, and the real one has moved. The card's own query was
grep -rl … | wc -l, so its 48 counted FILES. Re-counted in that same unit on9486ac672: 27 files, 85 nodes.⭐ And the drop was validated rather than assumed: a positive control in the identical query shape,
"type": "card", returns 93 nodes / 53 files — the same 93 measured on 08-10, 08-18 and 08-28. So48 → 27is a real migration (#4003 moved 106 nodes on 08-17), not a broken pattern.The stated symptom is already gone. The card says the docs site logs "a deprecation warning per thumbnail". Rendering all 431 entries through the real
SchemaRendererwith aconsole.warnspy live from the first entry:One notice for the whole corpus —
DivRendererreports once per module load (#3998) and exemptskind:'html'by provenance (#4000). The other three warns are the spy's own positive control.divrender is not measuring the corpus.⛔ Why the sweep did not run
The 2026-08-29 ruling's acceptance criterion is "browser spot-verification that renders are unchanged." That cannot be satisfied. Measured through the real renderers on identical authored input:
divis class-transparent — it emits the authoredclassNameverbatim and nothing else.containeraddsw-full max-w-xl mx-auto sm:p-3 md:p-4,flex/stackadd display+gap,gridaddsgrid-cols-2 gap-4,cardadds border/shadow and wraps children in an extraCardContent.twMergedoes not stripcontainer's injectedsm:p-3 md:p-4— different responsive variants from the authoredp-4— so the box silently gets smaller padding fromsmup.type,className,children,body), so there is no authored intent to carry over. Every conversion is pure addition.⭐⭐ And a second difference that no existing check could catch.
container/flex/stack/gridrenderschema.childrenonly;divrendersschema.children || schema.body— and 4 of the 85 nodes authorbody. Measured on the realbasic-sidebar.json, swapping only itsdiv:The element count is unchanged at 21. The loss is invisible to any element-count check, and
catalog-gallery-render'sdrewSomethingcontrol still passes. A find-and-replace would have shipped a silent catalog-wide content regression — worse than the warning it removes.section/article/asideare the one class-transparent family and are technically one-to-one, but they teach landmark elements as generic boxes across 27 exemplars. Three parties recommended against that independently before this; this measurement makes four.The 2026-08-25 and 2026-08-29 rulings both approved "migrate all 48" against a card that still said 48 — while the mechanically migratable ~106 nodes had already shipped in #4003 on 08-17. Both rulings were, in all likelihood, taken without knowing the mechanical half was already gone and that what remains is the part that cannot be done mechanically.
This is a re-scope of #3965, not a refusal to do the work.
四维分析
一句话问题:剩下的 25 文件 / 80 节点(另 2 文件 / 5 节点是迁移指南本身,已豁免)该怎么收口?
box):注册表 + types + docs + parser allowlist。80 个节点随即变成零渲染差异的机械替换,div在 JSON 面退役。已实测确认:当前没有box类型注册,"type": "box"匹配 0 个目录文件。div的废弃警告会对kind: 'html'tier 自己解析出的节点开火 —— 作者无法消除,且意味着div结构上退不掉 #4000 对kind:'html'层已经这么做过。semantic.tsx渲染Tag+className,零注入,且读children || body)。div组件类型,docs 站每张缩略图都刷一条 deprecation warning #3965 的全部),清扫记 won't-do,80 个节点冻结,只做机会性缩减。① 平台长远合理性 —— A 最优。这张卡的价值从来不是控制台噪音(#3998 早已修掉,实测整个语料库仅 1 条通知),而是27 个目录范例正在教一个已弃用的类型,而 AI 生成正是从这个语料库抄的。B 让它们永远教下去;C 用一个更坏的教材换掉一个坏教材;D 是不明说的 B。
② 实际业务需求 —— 弱。今天没有用户因此受损;这是给 AI 作者看的教材的正确性问题,不是运行时缺陷。
③ 防 AI 写代码犯错 —— ⭐ 这一维决定答案,而且指向 A。 目录是 AI 抄写的样板;C 会让"用
section当通用盒子"被抄 27 遍,比现状更糟。A 让替换成为零差异的机械动作,AI 抄到的是正确形状。④ 创业阶段不扩散需求 —— 偏向 B/D。A 要新增一个公开类型,是本轮唯一真正的扩面动作。
推荐:A,以 B 作为诚实的过渡;无论选哪个,#6732 的棘轮都已经把"第 49 个"堵住了。⚠️ 需要记在案:A 正是 2026-08-09 那次升级就推荐过的长期方案,至今没有被建造。
box类型对 designer / parser / docs 三处的连带成本没有测量,只测了目录侧。What lands regardless
PR #6732 — a catalog-scoped ratchet freezing the stock at 25 files / 80 nodes, zero fixtures touched. It closes a gap worth naming: nothing stopped the 49th before it. Both gates that touch component types ask only whether the type resolves —
catalog-gallery-renderassertsComponentRegistry.get(type)is truthy (green today with all 85 deprecated nodes present), andcheck-doc-component-types.mjswalkscontent/docs, notexamples/**.⭐ Worse than the usual declared-but-unenforced shape: the deprecation is not declared anywhere machine-readable.
RegistryComponentMetaExtrashas nodeprecatedfield, so the only statements of it are aconsole.warnstring literal and a human label. The gate therefore mirrors the list by hand — and pins its own premise, with an arm that goes red ifdiv.tsxstops declaring the notice, so the hand-kept mirror dies loudly rather than outliving the deprecation. #6674 tracks the registry flag that would let it derive instead.All five ratchet arms proven red against unfixed source, each mutation confirmed on disk and each restore proven by blob-hash equality.
Generated by Claude Code
claude commentedon Aug 30, 2026
Claim: PM loop round R27⚠️ #3162 is queued against `packages/types` for a different reason (erased `any` re-exports): fold-or-serial answered = SERIAL — different defect, different fix, fails gate ①; deliberately not in this batch. ⚠️ #4830 also touches `content/docs/` but a different file (`layout/app-shell.mdx` vs the new `box` page) — declared, not overlapping.
Session:
session_013hfmP9hoMd3dJwTh85J4yBBranch:
claude/issue-3965-mint-box-retire-divWorktree:
objectui-issue-3965Domain:
domain:uiFile surface:
packages/components/src/renderers/**(theboxregistration) ·packages/typesinterface + zod mirror · theboxdocs page ·examples/schema-catalog/src/schemas/**(the 27-file migration) · PR #6732's catalog ratchet (stop on breach; explain in the report)Container & model:
L,mode:subagent,model:claude-fable-5``Clause-②: yes — the ruling mints a NEW PUBLIC TYPE (
box) across registry + `@object-ui/types` + zod mirror. The ruling itself says so verbatim: 「新增公开类型 ⇒ 条款② 契约复审」. Dispatched at `CONTRACT_REVIEW_TIER` = `claude-fable-5` (read from `scripts/pm/dispatch-gates.mjs:5572`).Serial constraints cleared: `packages/components/`, `packages/types/`, `examples/schema-catalog/` — PRs #6822/#6834/#6841/#6851/#6867 (components) and #6826/#6869 (types) all MERGED; no open PR touches any of them.
Generated by Claude Code
claude commentedon Aug 30, 2026
Dev seat live on this card (confirming the R27 dispatch claim above).
Session:
session_013hfmP9hoMd3dJwTh85J4yBBranch:
claude/issue-3965-mint-box-retire-div(pushed)Worktree:
objectui-issue-3965, cut fromorigin/main@2c1c9673554176b87e480479929dce6bc9ac5884.Executing the 2026-08-29T14:52 ruling (mint
box, migrate div nodes with recount, close the #6732 ratchet) and no earlier superseded ruling.Generated by Claude Code
Generated by Claude Code
claude commentedon Aug 30, 2026
os-dev-report
{ "issue": 3965, "status": "done", "branch": "claude/issue-3965-mint-box-retire-div", "pr": "https://github.com/objectstack-ai/objectui/pull/6878", "premise_still_valid": true, "summary": "Executed the 2026-08-29T14:52 ruling (方案 A) and no superseded ruling, base origin/main 2c1c967. (1) MINT: BoxSchema on both contract faces — interface + zod mirror + SchemaRegistry entry + MIRRORS pairing registered in both halves of zod-mirror-parity (clause-② basis) — plus the ui:box registration (isContainer, toDomProps whitelist from day one), layout docs page, and a components-layout-box catalog category; the three ruled contract clauses (renders children, className verbatim, zero injected classes) are pinned by string-equality DOM assertions in box-neutral-container.test.tsx, plus a pinned deliberate edge: box reads children only, never body, with a div control render. (2) MIGRATION: recounted on the day's ref in BOTH units with the control (div 27 files / 85 nodes; control card 93 nodes / 53 files, matching every prior reading); migrated 25 files / 80 nodes div-to-box, the 4 body-authoring sidebar nodes moving content into children (#6771 B direction); the 2 files / 5 nodes of components-basic-div stay under the standing 2026-08-09 permanent doc exemption — a called-out deviation from the ruling's literal 27/85, see open question 1. (3) RATCHET: the #6732 gate is closed at zero — BASELINE machinery deleted, any JSON-authored div or span outside the doc exemption is refused; premise/omission/non-vacuity/counter-probe arms kept. Box also joins both DOM-leak instruments as a converged renderer; the widget-dom-leak-sweep edit (packages/app-shell) is OUTSIDE the claimed file surface, mechanically forced by the sweep's own swept==registered parity arm — declared here and in the PR as the same-round file-face amendment. The card's console-noise symptom was NOT used as justification (already fixed by #3998/#4000; measured false on this card's record). skills/** untouched; kind html tier untouched; landmarks not adopted (ruling point 4).", "tests": "All exits captured before any pipe; verdicts quoted from the gates' own lines. UNION AT FINAL HEAD 1bcb1f7 (clean tree): vitest run over examples/schema-catalog/ + packages/types/ + the app-shell widget sweep — 'Test Files 92 passed (92)', 'Tests 2952 passed (2952)'; packages/components/ — 'Test Files 217 passed (217)', 'Tests 1999 passed (1999)'; lock VERDICT 'command-exit 0' on each. RULED PER-FIXTURE METHOD: temporary harness rendered all 25 pre-migration snapshots vs migrated files through the real SchemaRenderer — 26/26 green, 25/25 pairs equal on BOTH element count and text content, 4 body-moved fixtures keep the 'Main content area' sentinel; harness deleted after the reading. REBUILD DISCIPLINE: types+components dist rebuilt BEFORE any reading (box presence in dist proven: BoxSchema in types dist index.d.ts, literal box in zod layout dist); zod reverse-verification ran against that rebuilt dist — admits a box node, refuses type div and className 42 (path className), union admits box. RED ARMS on the closed gate, fixture-level mutations with trap-guarded restores: probe fixture with 1 div + 1 span outside the exemption — mutation proven on disk by anchored grep counts (1/1) before reading, gate exit 1 naming the probe with both types, restore proven (file absent, tree clean); exempt category grown 5 to 6 — grep count 2 to 3 plus marker hit, gate exit 1 'expected 6 to be 5', restore via git checkout HEAD with HEAD-blob hash equality (empty hash = failure) and git diff HEAD empty. No build sat between mutation and reading: the gate reads fixture JSON from disk at runtime, and the components dist it loads was unchanged during both legs. TYPE-CHECKS (script names echoed): types, components, app-shell, example-schema-catalog all exit 0, zero error-TS lines; --listFiles proves the new/edited test files are inside their programs (1 hit each). GATES with own verdicts: doc-types, doc-snippets (after building its declared closure; first run exit 2 PRECONDITION NOT MET treated as not-measured, not red), doc-fences (new page re-fenced ts after the ratchet refused plaintext), docs-route-closure, sdui-registration-pins 'All 14 registrations present' (console built), eager-closure 'ui-components 386.1 KB / 389.6 KB ceiling', side-effects-array, control-bytes 'OK 5732 files', spec-symbols, phantom-deps, self-import, readme-exports (after building plugin-ai — same not-measured class), regenerate:check '433 entries up to date', changeset presence '2 released packages, 1 changeset', changeset no-major. LINT: CI-parity turbo run lint on the 4 touched packages 'Tasks: 5 successful', 0 errors; a no-inline-config json reading of the 13 changed files shows 0 new errors — the single error pre-exists byte-identically at BASE (proven by linting BASE bytes) behind a deliberate inline exemption, and box.tsx's 2 warnings equal the div.tsx sibling control. Declared narrowing: repo-wide lint left to CI; population is per-package eslint via turbo, run exactly as CI runs it for all four touched packages; eslint.config.js declares no project/projectService so the diff cannot move untouched files' verdicts.", "mcp_calls": "0 — the repo-scoped REST probe answered 200 at session start, so every GitHub read and write (issue payload channel + REST comments, 251-issue dedup listing with positive controls, issue filing, branch push via git, draft PR create, full-body read-backs) ran on the zero-GraphQL channel.", "open_questions": [ { "question": "The ruling's literal figure is 27 files / 85 nodes; this PR migrates 25 / 80 and leaves components-basic-div (2 files / 5 nodes) authoring div under the standing 2026-08-09 exemption ('永久豁免,在任何选项下', recorded on PR #3998, carried by #6732's gate and its non-vacuity pin). The governing ruling superseded two named rulings but did not name this exemption. Which reading stands?", "options": [ "A. Exemption stands (what this PR does): the div docs page keeps rendering real div Legacy Examples; the closed gate pins the exempt stock at exactly 2 files / 5 nodes so it cannot grow.", "B. The ruling's 27/85 revokes the exemption: retype the last 2 files / 5 nodes, drop DOC_EXEMPT_DIRS, and rewrite div.mdx in the same stroke (its Legacy Examples would otherwise demonstrate box under a heading that says div). A 5-minute follow-up commit on this branch if so ruled." ], "recommendation": "A — a standing per-surface ruling explicitly scoped '在任何选项下' should survive a population figure that carries its own recount caveat; and mechanically retyping the deprecated component's own documentation fixtures makes that page factually wrong, which fails the ruling's zero-render-difference spirit at the teaching level. #6877 already tracks the guidance-staleness of that category either way." }, { "question": "Should box join PUBLIC_BLOCKS Tier B (ADR-0080 curated contract and AI-authoring vocabulary)? The ruling calls box a 新增公开类型, and flex/grid/stack/card/container are all Tier B — but getPublicConfigs() will not offer box until it is listed, so the JSON contract vocabulary now teaches box via 27 catalog exemplars while the curated contract omits it.", "options": [ "A. Follow-up card: add 'box' to Tier B plus the exact-list census updates it forces (apps/console public-contract EXPECTED_COVERED at minimum) and a measured pass over registry-inputs-spec-parity (box has no @objectstack/spec entry — the parity gates' treatment of a spec-less public block is unmeasured).", "B. Leave box as registered capability only; the SchemaRenderer resolves it regardless and only the curated-contract surfaces (react-tier scope, which skips containers anyway, and contract listings) are unaffected." ], "recommendation": "A as a follow-up, deliberately not done here: PUBLIC_BLOCKS lives in packages/core with exact-list census tests in apps/console — both outside this card's declared file surface — and the spec-parity implications deserve a measurement, not a rider. Until then the omission is coherent (react-tier drops containers; JSON authoring is unaffected)." } ], "out_of_scope_findings": [ "filed as #6877: div/span deprecation guidance (DIV_DEPRECATION_NOTICE bullets, deprecated.replacement, div.mdx migration guide, components-basic-div guidance fixtures) still teaches the measured-non-drop-in replacements and never names box; every text involved is pinned byte-for-byte by the #4000 ruling and lockstepped by the #6823 agreement test, so it is a judged follow-up. Deduped against a complete 251-issue open listing with positive controls before filing." ] }Generated by Claude Code
claude commentedon Aug 30, 2026
os-dev-report
{ "issue": 3965, "status": "done", "branch": "claude/issue-3965-mint-box-retire-div", "pr": "https://github.com/objectstack-ai/objectui/pull/6878", "premise_still_valid": true, "summary": "PATCH ROUND on the accepted PR: the shard-1 red was exactly the coordinator's read — registering box moved the DERIVED known-schema-types artifact (packages/cli/src/utils/known-schema-types.ts, TARGET of scripts/regenerate-known-schema-types.mjs), which the scripts/__tests__ pin holds equal to the registered universe and byte-equal to what the generator writes. Fix is one commit, 8a78eed: the repo's own regeneration, run not hand-edited — a 2-line diff adding box (between boolean and br) and ui:box (between ui:blockquote and ui:br), matching the CI diff hint. Nothing else touched; the two open questions were not revisited (both ruled per the ACCEPT: 2026-08-09 exemption stands, Tier B is follow-up #6879). GATE-DERIVATION GAP, for the record: the changed paths never included scripts/ or packages/cli, so path-based gate-family derivation could not see this pin — registering a new component type has a gate consequence outside every path the diff touches (the artifact derives from the registration itself). Worth a standing note wherever the derive-gates-from-changed-paths method lives.", "tests": "Reproduced FIRST in a fresh worktree at 1bcb1f7, before regenerating: pnpm exec vitest run scripts/__tests__/known-schema-types-derivation-5115.test.ts — exit 1, 'expected [ box, ui:box ] to deeply equal []' plus the byte-for-byte module comparison, the exact CI signature. Then node scripts/regenerate-known-schema-types.mjs ('wrote packages/cli/src/utils/known-schema-types.ts', 2 insertions). WHOLE scripts/__tests__/ suite per the patch requirement, not just the one file: 'Test Files 88 passed (88)', 'Tests 2447 passed (2447)', lock VERDICT command-exit 0 — no sibling pin on the artifact is red. The failing file re-run at the committed HEAD 8a78eed: 'Test Files 1 passed (1)', 'Tests 10 passed (10)', VERDICT command-exit 0. cli type-check: first run 4x error TS2307/TS7006 in files this diff does not touch — the unbuilt sibling-dist closure of the recreated worktree, not a verdict; after pnpm --filter '@object-ui/cli^...' build, '@object-ui/cli run type-check' exit 0 with zero error-TS lines. check-changeset-presence at the new range: '12 source file(s) of 4 released package(s) changed, and this change declares 1 changeset(s)' — the existing changeset satisfies the gate and the fixed group versions together, so no changeset edit (patch kept minimal). Pushed as a plain push, no force, no amend: 1bcb1f7..8a78eed.", "mcp_calls": "0 this round as well — reproduction, regeneration, suites and push all local/git; this report goes over the same repo-scoped REST channel.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code