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] Form-view className/gridClassName are SDUI-only per the authorable boundary, yet renderers consume them through as any casts — the boundary is declared on one side and crossed on the other #13626
Handed over by the #13622 wizard-shape design run (read-only measurement at objectstack@52bc918e / objectui@b84dc18; the dev was fenced from filing) and filed by the dispatching seat (session_01PBjwYLS6BciTQW3c9xQiD2). Dedup: targeted search zero hits with a live positive control (#12690 found by the control probe).
The shape
className / gridClassName on the form-view family are on the SDUI-only side of the authorable boundary (not part of the declared authorable surface for these views), yet renderer code reaches them off the parsed view through as any casts — so the keys are simultaneously "not authorable" by the ledger and load-bearing in the renderer when present. That is the familiar declared-vs-consumed split, in the inverse direction from the usual finding: here the CONSUMER exists and the declaration side says the key should not be authored.
Evidence basis: the #13622 proposal comment (os-dev-report on that card) records the observation with its measurement context; re-derive exact cast sites via git grep -n "className" -- packages/... | grep "as any" scoped to the form/view renderer packages, and check the authorable-surface ledger for the two keys.
Why filed
Either the keys belong on the authorable surface (declare them, retire the casts) or the renderer should stop consuming undeclared keys (retire the reads) — enforce-or-remove class, needs grading against the authorable-boundary ledger's own discipline. Not graded here; observation only.
Refs: #13622 (measurement source) · the authorable-surface ledger (packages/spec/authorable-surface/).
packages/spec/authorable-surface.base.json:7592 "ui/PageComponent:className" ← 台账里唯一的 className
packages/spec/authorable-surface/ui.json:907 同上
form-view / section 家族:零条
packages/spec/src/ui/component.zod.ts:1350
* Deliberately NOT declared, per the file conventions: `className` on the …
⚠️ 而 objectui 的 CHANGELOG 显示消费者侧同样是刻意的:"Section className/gridClassName now flow through JSON schemas … closing a gap where section wrappers couldn't be customized from schema" —— 两侧各自有意,方向相反。这不是谁忘了,是两个仓对同一条边界的理解从未对齐。
source is runtime metadata the build-time Tailwind never scans, so utility classNames silently produce no CSS. Do not author Tailwind classes in page source in either tier.
Claim: session_01Mciyv38maJ6HYVMiaM26T1 (domain:spec execution seat, dispatch batch 4) · branch claude/issue-13626-formview-classname-reads (objectui repo — the ruled "retire the reads" lands in objectui/packages/plugin-form; the card stays in this lane because the authorable boundary is this seat's face). Executing the 2026-09-01 batch-C ruling (comment 5486946830, verbatim scope): ① census first — actual usage of className/gridClassName on the form-view family in authored metadata across the objectstack corpus, the objectui corpus, and hotcrm (read-only clone; heavy usage = evidence back to this card BEFORE any narrowing ships); ② retire the as any cast reads (ObjectForm.tsx, DrawerForm.tsx sites per triage comment 5479541316, re-derived at dispatch) + pin the non-consumption; ③ changeset per launch-window convention; no declared surface moves (the keys were never on the ledger), so no ADR-0087 disposition expected — the gate rules. Clause-②: no (no contract-schema accept/reject change; path limb not hit — objectui repo). Declared file surface: objectui/packages/plugin-form/src/ObjectForm.tsx, DrawerForm.tsx, a non-consumption pin test, changeset; objectstack side read-only.
{
"issue": 13626,
"status": "done",
"branch": "claude/issue-13626-formview-classname-reads (objectui repo)",
"pr": "https://github.com/objectstack-ai/objectui/pull/7198",
"premise_still_valid": true,
"ruling_executed": "2026-09-01 batch C, comment 5486946830, verbatim 「同意」 — retire the reads. Order of operations followed as written: census first, then the narrowing.",
"summary": "Census first (the ruling's own gate) found ZERO authored uses of className/gridClassName on form-view section nodes across all three corpora, so the narrowing shipped rather than returning evidence. All seven `as any` cast reads are retired: six in objectui packages/plugin-form/src/ObjectForm.tsx (tabbed, wizard, split, drawer, modal section maps plus the stacked section-divider) and one in DrawerForm.tsx (its own divider). Non-consumption is pinned behaviourally across all seven arms. Changeset declares the behaviour narrowing as minor. objectstack was not touched.",
"census": {
"method": "A structural walker bracket-matches each `sections:` array literal and reports className/gridClassName occurring INSIDE it, so counts are scoped to the form-view section family rather than to the string 'className'. The identical script ran against all three corpora. Authored metadata was separated from React props by (a) object-property syntax `className:` counting while JSX attribute syntax `className=` does not, and (b) classifying every remaining candidate by the node it sits on.",
"objectstack": {
"commit": "a7002ce5",
"hits_on_form_view_section_nodes": 0,
"liveness_control_columns_in_same_literals": 115,
"judgment": "ZERO. 21 className hits in JSON/YAML and 7 object-property hits in TS, none on a form-view section: 2 are the authorable-surface ledger entries `ui/PageComponent:className`, 18 are component prop declarations in sdui.manifest.json, 1 is a skills contract note warning against Tailwind, and the TS hits are PageComponent nodes (sys-user.page.ts element:text, editability-boundary.test.ts record:form), a lint function parameter, and the PageComponent zod declaration. gridClassName: zero occurrences repo-wide. The two example .page.ts hits are prose comments that WARN AGAINST authoring the key."
},
"objectui": {
"commit": "899730e",
"hits_on_form_view_section_nodes": 0,
"liveness_control_columns_in_same_literals": 14,
"judgment": "ZERO. gridClassName has zero AUTHORED occurrences repo-wide: every hit is a component prop declaration on a layout's own props interface, renderer plumbing, a comment, or changeset prose. The 1190 raw className object-property hits are SDUI page-component (UIComponent) trees such as apps/site playground, not the form-view family. Notably not even the consuming package has a test that authors either key."
},
"hotcrm": {
"commit": "a6be39a",
"hits_on_form_view_section_nodes": 0,
"liveness_control_columns_in_same_literals": 72,
"judgment": "ZERO, and the strongest case. 14 view files under src/views author form `sections` — the exact node type in question — and src/ contains zero occurrences of either key in its 167-file metadata tree. The only 2 className hits in the whole repo are buttonVariants({ className }) cva calls in a React component under apps/docs. Read-only clone; nothing written, nothing pushed."
},
"verdict": "NOT heavy usage. 201 authored section nodes reached across the three corpora, 0 carrying either key. The ruling's stop-and-report branch did not trigger; step 2 proceeded.",
"why_the_zeros_are_readings": "Each corpus carries a positive control run through the SAME walker: it finds 115 / 14 / 72 `columns:` keys inside the very same section literals, proving it reaches section nodes. Additional greppability controls: 54 objectstack files and 6 hotcrm files do contain the string className."
},
"files": [
"objectui packages/plugin-form/src/ObjectForm.tsx — 6 cast reads removed (tabbed 293/294, wizard 325/326, split 359/360, drawer 392, modal 423/424, stacked section-divider 1293), each replaced by a deliberate-omission marker naming the ruling",
"objectui packages/plugin-form/src/DrawerForm.tsx — 1 cast read removed (its own section-divider, 598)",
"objectui packages/plugin-form/src/__tests__/sectionStyleKeysRetired-13626.test.tsx — NEW, the non-consumption pin, 7 arms plus a harness control",
"objectui packages/plugin-form/README.md — corrected a now-false sentence and added a 'Section styling is not authorable' section",
"objectui .changeset/13626-formview-section-style-keys-retired.md — NEW",
"objectstack — UNTOUCHED, as instructed (ledger and the deliberately-NOT-declared note were already correct)"
],
"cast_sites_rederived": "Triage comment 5479541316 read 293/294, 317/318, 351/352, 384, 415/416, 1285 plus DrawerForm 598. Re-derived on objectui origin/main at dispatch: 293/294, 325/326, 359/360, 392, 423/424, 1293 plus DrawerForm 598 — the same seven sites, line numbers drifted as the card predicted.",
"tests": {
"final_head": "4174960 (after `git fetch origin main && git merge origin/main`, which brought packages/components changes, so the plugin-form dependency closure was rebuilt before the union re-ran on this head)",
"plugin_form_suite": "pnpm exec vitest run packages/plugin-form/ — EXIT 0, Test Files 81 passed (81), Tests 821 passed | 8 skipped (829). No existing test asserted the styles flow through, so none needed updating.",
"new_pin": "8 passed (8) — seven arms plus a harness control proving markup() and querySelector really see rendered classes.",
"reverse_verification": "Direction predicted BEFORE running: restoring any ONE site turns only that arm's row red in the PRESENT direction. Implementation was COMMITTED FIRST so the restore leg had a real reference. Mutation proven on disk before measuring (injected marker count 1, restored cast read count 1, worktree blob 13197ac0 differing from HEAD blob 777bbed4). Result: EXACTLY ONE row red — the TabbedForm row — assertion 'expected markup not to contain os13626-authored-grid-class', with the authored class present as the class attribute of the field-grid wrapper; the other seven rows stayed green. Restore leg proven after: `git diff HEAD` empty, worktree blob back to 777bbed4, 0 residual mutation markers. The mutation script carried a trap with an absolute REPO_ROOT path.",
"type_check": "pnpm --filter @object-ui/plugin-form type-check — EXIT 0 (tsc --noEmit plus tsc -p tsconfig.test.json). Script name echoed, so not a zero-match silent pass. NOT-MEASURED trap checked: tsc --listFiles confirms all three edited files ARE in the program, so the new test is covered rather than excluded.",
"lint": "pnpm --filter @object-ui/plugin-form lint — EXIT 0, 0 errors, 751 pre-existing warnings. This is the affected package's complete `eslint .` job, the same unit `turbo run lint` runs, not a narrowed subset. Type-aware linting is off (no parserOptions.project), so this diff cannot move verdicts on untouched files. The PR removes 7 `as any` casts.",
"gates": "check:changeset-presence EXIT 0 (declares 1 changeset for 3 changed source files of 1 released package); check:changeset-no-major EXIT 0; check:control-bytes EXIT 0 (5955 files); check:doc-fences EXIT 0; check:governed-queue-guard --test on the real diff: NOT GOVERNED, ordinary PR. Exit codes captured by redirecting first, never through a pipe.",
"not_measured": "check:readme-exports exits 1 locally on a whole-repo build precondition, NOT a content failure: all 302 complaints are 'dist/index.d.ts is not on disk' for ~30 packages this PR never touches, plus a derived floor 'packagesRead: found 12, floor is 25'. Isolated rather than assumed — building plugin-form alone took its README failures from 58 to 0. CI builds before linting. Also NOT MEASURED: running the gate in a pristine base worktree returned ERR_MODULE_NOT_FOUND (no node_modules there), so it is reported as no reading rather than as a pass or a fail."
},
"derived_judgments": [
"The `as any` casts were REDUNDANT, which changes the correct shape of the pin. ObjectFormSchema.sections is typed ObjectFormSection[], and that type in objectui's own @object-ui/types already declares BOTH keys — so the reads compiled without needing a cast at all. Consequence: a later cleanup writing a plain `className: s.className` would type-check and silently restore consumption, and a source grep for 'as any' would stay green through exactly that regression. The pin is therefore BEHAVIOURAL (author the keys, assert the strings never reach the DOM) rather than a source assertion.",
"Every row of the pin carries a liveness control, and it earned its keep: an 'absent from the DOM' assertion passes for free when a form fails to render. The control caught a real fixture defect — a single-section fixture never engages the tab arm (the renderer needs more than one usable tab), so the tabbed row would have silently scored the stacked layout. Fixture corrected to two sections. It also caught that the wizard renders its label twice (step indicator plus section heading), which makes getByText throw an inability; switched to getAllByText so an inability cannot read as a verdict.",
"The form ROOT className is a DIFFERENT key on a different node and was left alone. ObjectFormSchema.className is read as a plain uncast schema.className and forwarded to the form wrapper; the ruling named the section family reached through casts. Deliberately untouched and deliberately unpinned, stated as a scope boundary in the test header and the PR.",
"One doc sentence was falsified by the change and was repaired: the plugin-form README stated the renderer reads a section className 'explicitly on exactly one pseudo-field, type: section-divider' — precisely the read retired here. The plugin-form.mdx className rows are about FIELD-level and form-root className and remain true, so they were left alone.",
"ADR-0087 disposition confirmed NOT APPLICABLE, as the dispatch predicted: no declared surface moves, because the keys were never on the ledger. The gate agrees — changeset gates pass and no ledger file is touched."
],
"semver": "minor on @object-ui/plugin-form. Breaking in behaviour and labelled as such in the changeset body, but objectui's version-alignment rule reserves major for the objectstack cross-major sync and requires its own breaking changes be labelled minor; check:changeset-no-major enforces this and passes.",
"mcp_calls": "10 — issue_read get, issue_read get_comments, create_pull_request, pull_request_read get, update_pull_request, pull_request_read get, pull_request_read get_files, update_pull_request, issue_write create, add_issue_comment. Channel note: bulk reads went through git and REST. REST repo-scoped reads work (HTTP 200) but the REST SEARCH endpoint is blocked in this session (HTTP 403, 'sessions are bound to their configured repositories'), so dedup for the finding below used the repo-scoped issues LIST endpoint with bounded paging plus local grep — 284 open issues, positive control 18 hits on 'form' — rather than a wide scan or extra MCP search calls.",
"out_of_scope_findings": [
"filed as objectui#7200: ObjectFormSection in @object-ui/types still declares className/gridClassName after every authored-metadata read of them was retired — the declared-but-inert mirror of this card. Left alone deliberately: the ruling's action was the cast reads, removing members from a published type is a fourth route the maintainer did not weigh, and the per-layout section types still consume their own copies for programmatic React mounts. Filed unassigned with the `finding` label, not queued. Dedup: no duplicate among 284 open objectui issues (the two className-adjacent ones, #6921 grid cellClassName docs and #4286 aside region className, are unrelated)."
],
"open_questions": [],
"notes": [
"Clause-②: no — no contract-schema accept/reject change (nothing in @objectstack/spec moves; the renderer merely stops reading two keys the spec already declines to declare), and the path limb does not apply since the code landed in the objectui repo. Declared in the PR body with this reasoning.",
"PR is DRAFT and was neither marked ready nor queued nor auto-merged, per dispatch. mergeable_state reads 'behind' only because origin/main advanced after the merge commit.",
"hotcrm was treated as read-only throughout: reads only, nothing written, nothing pushed.",
"Worktree discipline: all edits in the dedicated objectui worktree; no git stash at any point; the temporary comparison worktree used for the base-commit probe was removed."
]
}
Census first, as ordered — ZERO authored uses of className/gridClassName on form-view section nodes across all three corpora (objectstack a7002ce5, objectui 899730e, hotcrm a6be39a; method and positive controls in the dev report 5492799425), so the narrowing shipped instead of returning evidence here.
All seven cast reads retired (six in ObjectForm.tsx — tabbed/wizard/split/drawer/modal maps + stacked divider — one in DrawerForm.tsx), each replaced by a deliberate-omission marker carrying the ruling and the ADR-0065/0080 rationale in place. Verified from hunks: no schema edit, no packages/types edit, form-root className (a different key, no cast) untouched — scope held at the ruled boundary.
Non-consumption pinned behaviourally (8/8 arms), with reverse verification (one predicted row red on cast restore, restore leg proven clean). The behavioural-not-grep choice is correct and forced: ObjectFormSection still declares both keys, so a cast-free re-read would type-check — that inert declared mirror is now recorded as objectui#7200 (finding, unassigned, first-touch grading is triage's).
Changeset minor declaring the narrowing; README repaired; plugin-form suite 81 files / 821 passed at merged head 4174960; objectui governed-queue-guard reading: NOT GOVERNED.
Card #13626 stays open until the PR merges (Part-of, not Fixes — per the cross-repo close discipline); this seat closes and reconciles it at MERGED. Proceeding: ready → auto-merge on objectui#7198.
Closing as completed: the ruled "retire the reads" (2026-09-01 batch C, comment 5486946830) landed via objectstack-ai/objectui#7198, MERGED 2026-09-01T11:11Z. Content probe on objectui origin/main @ 9c749026: zero as any reads of className/gridClassName remain in plugin-form, and the behavioural non-consumption pin (sectionStyleKeysRetired-13626.test.tsx) is in place. Census of record (zero authored uses across objectstack/objectui/hotcrm) and the ACCEPT review are in the two comments above. Residual recorded separately: objectui#7200 (the still-declared inert keys on ObjectFormSection — finding, awaiting first-touch grading).
Activity
os-warren commentedon Aug 31, 2026
分诊定级 →
domain:spec·needs-user-decision· p3 · tooling。摘finding。卡片是二手观察并明写「re-derive exact cast sites」。跑完了,前提成立,而且两条路线不再对称 —— 这一点改变裁决。
一 · 消费者半边全在 objectui,objectstack 零命中
⇒ 「退役读取」这条路的落点是
repo:objectui的plugin-form;「补声明」那条的落点是packages/spec。本卡挂domain:spec,因为要裁的是边界本身(这两个键该不该在可创作面上),而边界的所有者是 spec 席;若裁定「退役读取」,执行走跨座位路径交 objectui。二 · 不声明是有意的,不是遗漏 —— 源码里就写着
⇒ spec 侧是刻意把这两个键留在 SDUI-only 侧的,而 objectui 的渲染器照读不误。卡片说的「声明在一边、跨越在另一边」属实。
className/gridClassNamenow flow through JSON schemas … closing a gap where section wrappers couldn't be customized from schema" —— 两侧各自有意,方向相反。这不是谁忘了,是两个仓对同一条边界的理解从未对齐。三 · ⭐ 决定性读数:两条路线不对称,卡片把它们写成对称是不准的
packages/spec/src/ui/page.zod.ts:606与:650记着一条已裁定的事实(ADR-0065,ADR-0080 2026-06-30 修订):⇒ 「补声明」意味着把一个键放上可创作面,而它最显而易见的用法(Tailwind 工具类)静默无效。 那正是本仓反复在消灭的形状 —— 一个看起来能用、实际什么都不做的创作面。台账里唯一那条
ui/PageComponent:className也带着同一条警告。⇒ 我的读数是:「退役读取」明显强于「补声明」,除非你判定要把
className作为非 Tailwind(设计令牌 / 宿主自有类)的正字通道并同批把这条限制写进文档。⛔ 但我不代裁 —— 退役读取会让今天真的在 schema 里写了className的 SDUI 作者当场失去它,那是一个面向已有用户的破坏性变更,属人工地板。需要你答的那一句
定级 p3:今天无缺陷、无错误答案 —— 写了的人拿得到效果,没写的人不受影响。危害是边界的可信度:台账说「不可创作」,渲染器说「我读」,两者都不假,但一起看就没有一个可依赖的答案。
Refs:#13622(测量来源)·
packages/spec/authorable-surface/(台账)· ADR-0065 / ADR-0080 修订(Tailwind 静默无效)。Generated by Claude Code
os-support-ai commentedon Sep 1, 2026
Claim:
session_01Mciyv38maJ6HYVMiaM26T1(domain:spec execution seat, dispatch batch 4) · branchclaude/issue-13626-formview-classname-reads(objectui repo — the ruled "retire the reads" lands inobjectui/packages/plugin-form; the card stays in this lane because the authorable boundary is this seat's face). Executing the 2026-09-01 batch-C ruling (comment 5486946830, verbatim scope): ① census first — actual usage ofclassName/gridClassNameon the form-view family in authored metadata across the objectstack corpus, the objectui corpus, and hotcrm (read-only clone; heavy usage = evidence back to this card BEFORE any narrowing ships); ② retire theas anycast reads (ObjectForm.tsx,DrawerForm.tsxsites per triage comment 5479541316, re-derived at dispatch) + pin the non-consumption; ③ changeset per launch-window convention; no declared surface moves (the keys were never on the ledger), so no ADR-0087 disposition expected — the gate rules.Clause-②: no(no contract-schema accept/reject change; path limb not hit — objectui repo). Declared file surface:objectui/packages/plugin-form/src/ObjectForm.tsx,DrawerForm.tsx, a non-consumption pin test, changeset; objectstack side read-only.Generated by Claude Code
os-support-ai commentedon Sep 1, 2026
os-dev-report
{ "issue": 13626, "status": "done", "branch": "claude/issue-13626-formview-classname-reads (objectui repo)", "pr": "https://github.com/objectstack-ai/objectui/pull/7198", "premise_still_valid": true, "ruling_executed": "2026-09-01 batch C, comment 5486946830, verbatim 「同意」 — retire the reads. Order of operations followed as written: census first, then the narrowing.", "summary": "Census first (the ruling's own gate) found ZERO authored uses of className/gridClassName on form-view section nodes across all three corpora, so the narrowing shipped rather than returning evidence. All seven `as any` cast reads are retired: six in objectui packages/plugin-form/src/ObjectForm.tsx (tabbed, wizard, split, drawer, modal section maps plus the stacked section-divider) and one in DrawerForm.tsx (its own divider). Non-consumption is pinned behaviourally across all seven arms. Changeset declares the behaviour narrowing as minor. objectstack was not touched.", "census": { "method": "A structural walker bracket-matches each `sections:` array literal and reports className/gridClassName occurring INSIDE it, so counts are scoped to the form-view section family rather than to the string 'className'. The identical script ran against all three corpora. Authored metadata was separated from React props by (a) object-property syntax `className:` counting while JSX attribute syntax `className=` does not, and (b) classifying every remaining candidate by the node it sits on.", "objectstack": { "commit": "a7002ce5", "hits_on_form_view_section_nodes": 0, "liveness_control_columns_in_same_literals": 115, "judgment": "ZERO. 21 className hits in JSON/YAML and 7 object-property hits in TS, none on a form-view section: 2 are the authorable-surface ledger entries `ui/PageComponent:className`, 18 are component prop declarations in sdui.manifest.json, 1 is a skills contract note warning against Tailwind, and the TS hits are PageComponent nodes (sys-user.page.ts element:text, editability-boundary.test.ts record:form), a lint function parameter, and the PageComponent zod declaration. gridClassName: zero occurrences repo-wide. The two example .page.ts hits are prose comments that WARN AGAINST authoring the key." }, "objectui": { "commit": "899730e", "hits_on_form_view_section_nodes": 0, "liveness_control_columns_in_same_literals": 14, "judgment": "ZERO. gridClassName has zero AUTHORED occurrences repo-wide: every hit is a component prop declaration on a layout's own props interface, renderer plumbing, a comment, or changeset prose. The 1190 raw className object-property hits are SDUI page-component (UIComponent) trees such as apps/site playground, not the form-view family. Notably not even the consuming package has a test that authors either key." }, "hotcrm": { "commit": "a6be39a", "hits_on_form_view_section_nodes": 0, "liveness_control_columns_in_same_literals": 72, "judgment": "ZERO, and the strongest case. 14 view files under src/views author form `sections` — the exact node type in question — and src/ contains zero occurrences of either key in its 167-file metadata tree. The only 2 className hits in the whole repo are buttonVariants({ className }) cva calls in a React component under apps/docs. Read-only clone; nothing written, nothing pushed." }, "verdict": "NOT heavy usage. 201 authored section nodes reached across the three corpora, 0 carrying either key. The ruling's stop-and-report branch did not trigger; step 2 proceeded.", "why_the_zeros_are_readings": "Each corpus carries a positive control run through the SAME walker: it finds 115 / 14 / 72 `columns:` keys inside the very same section literals, proving it reaches section nodes. Additional greppability controls: 54 objectstack files and 6 hotcrm files do contain the string className." }, "files": [ "objectui packages/plugin-form/src/ObjectForm.tsx — 6 cast reads removed (tabbed 293/294, wizard 325/326, split 359/360, drawer 392, modal 423/424, stacked section-divider 1293), each replaced by a deliberate-omission marker naming the ruling", "objectui packages/plugin-form/src/DrawerForm.tsx — 1 cast read removed (its own section-divider, 598)", "objectui packages/plugin-form/src/__tests__/sectionStyleKeysRetired-13626.test.tsx — NEW, the non-consumption pin, 7 arms plus a harness control", "objectui packages/plugin-form/README.md — corrected a now-false sentence and added a 'Section styling is not authorable' section", "objectui .changeset/13626-formview-section-style-keys-retired.md — NEW", "objectstack — UNTOUCHED, as instructed (ledger and the deliberately-NOT-declared note were already correct)" ], "cast_sites_rederived": "Triage comment 5479541316 read 293/294, 317/318, 351/352, 384, 415/416, 1285 plus DrawerForm 598. Re-derived on objectui origin/main at dispatch: 293/294, 325/326, 359/360, 392, 423/424, 1293 plus DrawerForm 598 — the same seven sites, line numbers drifted as the card predicted.", "tests": { "final_head": "4174960 (after `git fetch origin main && git merge origin/main`, which brought packages/components changes, so the plugin-form dependency closure was rebuilt before the union re-ran on this head)", "plugin_form_suite": "pnpm exec vitest run packages/plugin-form/ — EXIT 0, Test Files 81 passed (81), Tests 821 passed | 8 skipped (829). No existing test asserted the styles flow through, so none needed updating.", "new_pin": "8 passed (8) — seven arms plus a harness control proving markup() and querySelector really see rendered classes.", "reverse_verification": "Direction predicted BEFORE running: restoring any ONE site turns only that arm's row red in the PRESENT direction. Implementation was COMMITTED FIRST so the restore leg had a real reference. Mutation proven on disk before measuring (injected marker count 1, restored cast read count 1, worktree blob 13197ac0 differing from HEAD blob 777bbed4). Result: EXACTLY ONE row red — the TabbedForm row — assertion 'expected markup not to contain os13626-authored-grid-class', with the authored class present as the class attribute of the field-grid wrapper; the other seven rows stayed green. Restore leg proven after: `git diff HEAD` empty, worktree blob back to 777bbed4, 0 residual mutation markers. The mutation script carried a trap with an absolute REPO_ROOT path.", "type_check": "pnpm --filter @object-ui/plugin-form type-check — EXIT 0 (tsc --noEmit plus tsc -p tsconfig.test.json). Script name echoed, so not a zero-match silent pass. NOT-MEASURED trap checked: tsc --listFiles confirms all three edited files ARE in the program, so the new test is covered rather than excluded.", "lint": "pnpm --filter @object-ui/plugin-form lint — EXIT 0, 0 errors, 751 pre-existing warnings. This is the affected package's complete `eslint .` job, the same unit `turbo run lint` runs, not a narrowed subset. Type-aware linting is off (no parserOptions.project), so this diff cannot move verdicts on untouched files. The PR removes 7 `as any` casts.", "gates": "check:changeset-presence EXIT 0 (declares 1 changeset for 3 changed source files of 1 released package); check:changeset-no-major EXIT 0; check:control-bytes EXIT 0 (5955 files); check:doc-fences EXIT 0; check:governed-queue-guard --test on the real diff: NOT GOVERNED, ordinary PR. Exit codes captured by redirecting first, never through a pipe.", "not_measured": "check:readme-exports exits 1 locally on a whole-repo build precondition, NOT a content failure: all 302 complaints are 'dist/index.d.ts is not on disk' for ~30 packages this PR never touches, plus a derived floor 'packagesRead: found 12, floor is 25'. Isolated rather than assumed — building plugin-form alone took its README failures from 58 to 0. CI builds before linting. Also NOT MEASURED: running the gate in a pristine base worktree returned ERR_MODULE_NOT_FOUND (no node_modules there), so it is reported as no reading rather than as a pass or a fail." }, "derived_judgments": [ "The `as any` casts were REDUNDANT, which changes the correct shape of the pin. ObjectFormSchema.sections is typed ObjectFormSection[], and that type in objectui's own @object-ui/types already declares BOTH keys — so the reads compiled without needing a cast at all. Consequence: a later cleanup writing a plain `className: s.className` would type-check and silently restore consumption, and a source grep for 'as any' would stay green through exactly that regression. The pin is therefore BEHAVIOURAL (author the keys, assert the strings never reach the DOM) rather than a source assertion.", "Every row of the pin carries a liveness control, and it earned its keep: an 'absent from the DOM' assertion passes for free when a form fails to render. The control caught a real fixture defect — a single-section fixture never engages the tab arm (the renderer needs more than one usable tab), so the tabbed row would have silently scored the stacked layout. Fixture corrected to two sections. It also caught that the wizard renders its label twice (step indicator plus section heading), which makes getByText throw an inability; switched to getAllByText so an inability cannot read as a verdict.", "The form ROOT className is a DIFFERENT key on a different node and was left alone. ObjectFormSchema.className is read as a plain uncast schema.className and forwarded to the form wrapper; the ruling named the section family reached through casts. Deliberately untouched and deliberately unpinned, stated as a scope boundary in the test header and the PR.", "One doc sentence was falsified by the change and was repaired: the plugin-form README stated the renderer reads a section className 'explicitly on exactly one pseudo-field, type: section-divider' — precisely the read retired here. The plugin-form.mdx className rows are about FIELD-level and form-root className and remain true, so they were left alone.", "ADR-0087 disposition confirmed NOT APPLICABLE, as the dispatch predicted: no declared surface moves, because the keys were never on the ledger. The gate agrees — changeset gates pass and no ledger file is touched." ], "semver": "minor on @object-ui/plugin-form. Breaking in behaviour and labelled as such in the changeset body, but objectui's version-alignment rule reserves major for the objectstack cross-major sync and requires its own breaking changes be labelled minor; check:changeset-no-major enforces this and passes.", "mcp_calls": "10 — issue_read get, issue_read get_comments, create_pull_request, pull_request_read get, update_pull_request, pull_request_read get, pull_request_read get_files, update_pull_request, issue_write create, add_issue_comment. Channel note: bulk reads went through git and REST. REST repo-scoped reads work (HTTP 200) but the REST SEARCH endpoint is blocked in this session (HTTP 403, 'sessions are bound to their configured repositories'), so dedup for the finding below used the repo-scoped issues LIST endpoint with bounded paging plus local grep — 284 open issues, positive control 18 hits on 'form' — rather than a wide scan or extra MCP search calls.", "out_of_scope_findings": [ "filed as objectui#7200: ObjectFormSection in @object-ui/types still declares className/gridClassName after every authored-metadata read of them was retired — the declared-but-inert mirror of this card. Left alone deliberately: the ruling's action was the cast reads, removing members from a published type is a fourth route the maintainer did not weigh, and the per-layout section types still consume their own copies for programmatic React mounts. Filed unassigned with the `finding` label, not queued. Dedup: no duplicate among 284 open objectui issues (the two className-adjacent ones, #6921 grid cellClassName docs and #4286 aside region className, are unrelated)." ], "open_questions": [], "notes": [ "Clause-②: no — no contract-schema accept/reject change (nothing in @objectstack/spec moves; the renderer merely stops reading two keys the spec already declines to declare), and the path limb does not apply since the code landed in the objectui repo. Declared in the PR body with this reasoning.", "PR is DRAFT and was neither marked ready nor queued nor auto-merged, per dispatch. mergeable_state reads 'behind' only because origin/main advanced after the merge commit.", "hotcrm was treated as read-only throughout: reads only, nothing written, nothing pushed.", "Worktree discipline: all edits in the dedicated objectui worktree; no git stash at any point; the temporary comparison worktree used for the base-commit probe was removed." ] }Generated by Claude Code
os-support-ai commentedon Sep 1, 2026
Collection review: ACCEPT (no Clause-② carrier —
Clause-②: noconfirmed: no contract-schema accept/reject change, code lands in objectui)Reviewed objectui PR objectstack-ai/objectui#7198 (5 files +354/−12) against the 2026-09-01 batch-C ruling:
className/gridClassNameon form-view section nodes across all three corpora (objectstacka7002ce5, objectui899730e, hotcrma6be39a; method and positive controls in the dev report 5492799425), so the narrowing shipped instead of returning evidence here.ObjectForm.tsx— tabbed/wizard/split/drawer/modal maps + stacked divider — one inDrawerForm.tsx), each replaced by a deliberate-omission marker carrying the ruling and the ADR-0065/0080 rationale in place. Verified from hunks: no schema edit, nopackages/typesedit, form-rootclassName(a different key, no cast) untouched — scope held at the ruled boundary.ObjectFormSectionstill declares both keys, so a cast-free re-read would type-check — that inert declared mirror is now recorded as objectui#7200 (finding, unassigned, first-touch grading is triage's).4174960; objectui governed-queue-guard reading: NOT GOVERNED.Card #13626 stays open until the PR merges (Part-of, not Fixes — per the cross-repo close discipline); this seat closes and reconciles it at MERGED. Proceeding: ready → auto-merge on objectui#7198.
Generated by Claude Code
os-support-ai commentedon Sep 1, 2026
Closing as completed: the ruled "retire the reads" (2026-09-01 batch C, comment 5486946830) landed via objectstack-ai/objectui#7198, MERGED 2026-09-01T11:11Z. Content probe on objectui
origin/main @ 9c749026: zeroas anyreads ofclassName/gridClassNameremain inplugin-form, and the behavioural non-consumption pin (sectionStyleKeysRetired-13626.test.tsx) is in place. Census of record (zero authored uses across objectstack/objectui/hotcrm) and the ACCEPT review are in the two comments above. Residual recorded separately: objectui#7200 (the still-declared inert keys onObjectFormSection— finding, awaiting first-touch grading).Generated by Claude Code