Repository navigation
console record forms: a section whose authored columns does not divide the form's one grid draws the wrong number of fields per row (HotCRM Leads → New: a two-column section draws one field per row) #12548
Description
Derived in-flight sub-issue of #12521. It carries the half that dev report 6197105887 names Q2 and out_of_scope_findings[0]: authored sections with mixed columns densities render wrong. It inherits domain:ui and priority:p1 from #12521, by the dispatch seat's ruling (domain:ui seat 1, session session_01JZnppgHRp4sREcouhkQYDB). #12521 keeps the other half, the dialog's default size, which waits on a maintainer decision.
Parent: #12521. Nothing blocks this card. It does not wait on #12521's decision, because an authored layout renders as written whatever size the dialog is.
Reader who acts: the domain:ui lane's next dispatch, in @object-ui/plugin-form, size M–L. It fits a cloud session.
Dedupe (MCP search_issues, scope objectstack-ai/objectui, all states, read 2026-10-15T16:40Z): "form section columns mixed density one field per row col-span rounding authored sections" → 6 results, all read.
- fix(form): 分组表单右侧永远空着 —— section 的多列 grid 套在整个表单外面而非字段上 #2128 (closed 2026-07-01) moved a section's multi-column grid from around the whole form onto the fields. This card is that family's residue for mixed densities.
- ModalForm 显式多 section 表单:每个 section 独立 <form>,提交时丢弃第 2+ 个 section 的值 #2153 (closed) is ModalForm multi-section submit; Data pillar Form tab: a compact Layout / Preview control with Add section and Add field at the right, field cards with a type tag and a selection ring, and a dashed "Add a section" footer #12451 is Studio's form tab.
- None is this card.
What happens
HotCRM's crm_lead form view authors six sections with columns 2 / 3 / 2 / 2 / 1 / 2 (hotcrm src/sales/views/lead.view.ts :245–:380; last_name has span: 'full'). On Leads → New at 1440:
- the Contact Information section (
columns: 2) draws ONE field per row, at 841 px, and leaves the right third of the form empty; last_namespans 1270 px;- the Lead Classification section (
columns: 3) draws three per row, at 413 px.
These are #12521's readings 4 and figure 1. The dev re-measured them with a real-ModalForm probe on main 521817ce9d.
Mechanism (measured by the #12521 dev, 6197105887)
- A sectioned form draws ONE field grid, sized by its widest section. Here that is
grid gap-4 grid-cols-1 @md:grid-cols-2 @2xl:grid-cols-3. - Each field's span is
Math.round(grid / sectionColumns)(packages/plugin-form/src/autoLayout.ts,resolveColSpan):- a
columns: 2section's fields get@md:col-span-2, two of three cells, so one field per row; span: 'full'gets@md:col-span-2 @2xl:col-span-3;- a
columns: 3section's fields get no span, so one cell each.
- a
- By the same arithmetic, which was NOT probed, a
columns: 3section on a four-wide grid would draw four per row. - Shared call sites:
applyAutoColSpan(body, formColumns, sectionColumns)inModalForm,ObjectForm,SplitForm,TabbedFormandWizardForm.
Governing text
@objectstack/spec FormSectionSchema.columns (packages/spec/src/ui/view.zod.ts:3783): 1 | 2 | 3 | 4, default 1. That is a per-section column count. The renderer draws a different count when sections disagree, so this is a declared-but-not-honoured defect, fixed in the implementation.
What to build
- Every authored section renders its own
columnscount per row, from the breakpoint where multi-column starts. This holds whatever the other sections declare, on all five sectioned hosts. - One shared rule serves the five hosts, not five copies. Two examples, the dev's measurement to choose:
- a 12-track grid where 1 / 2 / 3 / 4 per row are spans 12 / 6 / 4 / 3;
- per-section field containers inside the one form.
span: 'full'takes the row. An authoredcolSpankeeps winning. A single-density form renders unchanged; pin it at class level.- A form with no section
columnsis outside this card. That is console record form: a create/edit dialog with no authored layout opens at 95vw with one column of unequal field widths — a bounded dialog with a two-column field grid and one width per field kind #12521's half (PR fix(plugin-form): a record dialog with no authored layout opens bounded with a two-column grid (objectui#12521) #12546).
Acceptance
- Per host, tests that a 2 / 3 / 1 mixed-density form renders 2 / 3 / 1 fields per row, with
span: 'full'taking the row.- CONTROL: a single-density form's grid and span classes are byte-identical before and after.
- Ablation: the old rounding reds the mixed pins.
- Browser reading by the landing seat at 1440 on HotCRM Leads → New:
- Contact Information reads two fields per row, of one width;
- Lead Classification reads three per row;
- nothing in the form's right third is empty except a row's natural remainder.
pnpm testfrom the repo root is green on the affected files. One changeset for@object-ui/plugin-form, patch or minor, never major.
Out of scope
- The dialog's default size (console record form: a create/edit dialog with no authored layout opens at 95vw with one column of unequal field widths — a bounded dialog with a two-column field grid and one width per field kind #12521's decision).
- Field order, and the HotCRM form's own authoring.
Attribution: filed by the domain:ui seat 1 in session session_01JZnppgHRp4sREcouhkQYDB (account sunhaolin), from the #12521 dev report above.
Activity
objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 1 (
priority:p1; derived in-flight sub-issue of objectui#12521, filed by this seat)Session:
session_01JZnppgHRp4sREcouhkQYDBAccount:
sunhaolinBranch:
claude/issue-12548-section-columnsWorktree:
objectui-issue-12548(cloud session; the session's own checkout)Domain:
domain:uiSeat:
domain:ui#1File surface:
packages/plugin-form/src/autoLayout.ts:resolveColSpan,applyAutoColSpanand the form grid's column classes (CONTAINER_GRID_COLS), notinferModalSize.applyAutoColSpancall sites and grid wiring inModalForm.tsx,ObjectForm.tsx,SplitForm.tsx,TabbedForm.tsxandWizardForm.tsx.packages/components' form renderer span ladder, only if the span classes are drawn there..changeset/12548-*.md.Outside the surface: the dialog's size (
inferModalSize,modalSize,AppContent.tsx), which is objectui#12521's open decision;@objectstack/spec; HotCRM's form; and any type, prop or export (stop on breach; explain in the report).Container & model:
M–L,mode:cloud(a cloud session, on the maintainer's 「后续 objectui 复杂任务可以用云卡片。」 recorded at seat-post comment6193043281),model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuinames no path-derived mandate)Clause-②: no
Responsibility:
@object-ui/plugin-form'sautoLayout, which rounds a section's span on the form's single widest grid.columns. Measured on HotCRM Leads → New at 1440.Thread-read: none
Serial constraints cleared: read at objectui
mainb51242899b.pm:dispatchedclaim in objectui namesplugin-formor the form renderer (scanned in this act).ModalForm.tsxandautoLayout.ts, the default size and the no-columns grid). Same files, different regions. It is held on objectui#12521's decision (6197229681), so this card likely lands first, and fix(plugin-form): a record dialog with no authored layout opens bounded with a two-column grid (objectui#12521) #12546 mergesmainin its rework round.packages/plugin-form/src/affordanceGrantMap-12082.test.tsxkeys on call-site paths, and the dispatch names it.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Dispatched as a cloud session:
session_01DN3fpUznSTqNMkk9nXd5BG, titled 「⚡ domain:ui #12548 form sections render their own columns」. Its outcome branch isclaude/issue-12548-section-columns, and the seat'screate_sessionmade it at 2026-10-15T16:43Z. The claim6197286546is its identity.A cloud container cannot write to GitHub (seat-post comment
6194979941). So the session pushes the branch and ends with its PR title, PR body andos-dev-reportas its final message. The seat then opens the draft PR, posts the report verbatim, and subscribes to the PR. The seat archives the session when the PR merges or closes.Anyone taking this card over reads the session's liveness from GitHub output first: the branch, then the draft PR and the
os-dev-report.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
os-dev-report
Posted by the
domain:uiseat 1 (session_01JZnppgHRp4sREcouhkQYDB) on behalf of the cloud dev sessionsession_01DN3fpUznSTqNMkk9nXd5BG. As the dispatch planned (6197327191), that session pushedclaude/issue-12548-section-columns@88bfef068fand returned its report as its last message, because a cloud container cannot write to GitHub. The JSON below is that message, verbatim. Its"pr": nullis the session's own value: the draft PR it asks for is #12556, opened by the seat with the dev's own title and body text.{ "issue": 12548, "status": "done", "branch": "claude/issue-12548-section-columns", "pr": null, "session": "session_01DN3fpUznSTqNMkk9nXd5BG", "premise_still_valid": true, "summary": "A1 was confirmed on main 9c2785b: a 2/3/1 form draws one field per row in the 2-column section on every host, and a 3-column section beside a 4-column one draws four per row (the card had not probed this). A2 holds with one refinement. A 12-track grid alone loses the intermediate step ladder, so the components form renderer (A3: the span classes are drawn there) gained a literal twelfths table that keeps each count's ladder (2 per row at @md, 3 at @2xl, 4 at @4xl). Per-section grids were rejected because they would need a new section-divider key, a type change, in a single-form renderer. One shared rule, sectionedGridLayout in autoLayout.ts, now serves all five hosts. A form whose sections are all 1 or the grid width stays byte-identical; otherwise the grid is 12 tracks from @md and spans count twelfths. Primary checkout note: it was detached to add the worktree and has been put back on the branch at 88bfef0.", "tests": "At HEAD 88bfef0: the new mixedSectionColumns-12548.test.tsx (25) and form-twelve-track-span-12548.test.tsx (7) pass. Before the fix, on main: 6 failed / 8 passed (mixed pins red, CONTROLs green). The whole plugin-form suite: 184 files, 2269 passed, 1 skipped, EXIT 0. The union of host-rendering, census and components-form suites: 208 package files, 3067 passed, 17 skipped, EXIT 0; plus 17 apps/examples/scripts files, 1465 passed, EXIT 0. Ablation 1, sectionedGridLayout forced to the old path (marker count 1): EXIT 1, 12 red / 13 green, all 8 CONTROLs green; restore blob 2635fef1 equals the HEAD blob and git diff HEAD is empty. Ablation 2, the renderer's 12-track detection disabled: EXIT 1, 16 red / 16 green; restore verified by hash. No dist build was needed: vitest aliases both packages to src. Real-layout Chromium reading (console Tailwind build), HotCRM lead shape at 1368px: Contact 1 per row at 875px before, 2 per row at 652px after; Classification 3 per row at 429px both ways.", "gates": "Local exit codes were captured before any pipe; all were 0: build plugin-form^... (closure includes components); type-check plugin-form; type-check components; tsc over both test files; check:control-bytes; check:new-line-citations (VERDICT 0 new); check-changeset-presence; check-changeset-no-major; check:changeset-claims; check:pending-changeset-literals; check:test-path-roots; check:vi-mock-specifiers; check:vi-mock-inherit; check:vi-mock-override-shape; check:unreferenced-sources; eslint on the 9 changed files (0 errors, 185 pre-existing warnings). NOT MEASURED: check:readme-exports (exit 1 with 'population COLLAPSED' because unbuilt package dists left it nothing to judge, not a red gate); scripts/check-nul-bytes.mjs (MODULE_NOT_FOUND, the script does not exist in objectui; check:control-bytes covers it); performance-budget (a full console build, CI-owned). No PR check-runs exist yet because no PR is open.", "mcp_calls": "4: mcp__github__issue_read x2 (get, get_comments on #12548), mcp__github__list_pull_requests x1 (head branch check, empty), mcp__claude-code-remote__add_repo x1 (objectstack, read). No write tool was used.", "api_writes": "0 REST writes. git push x2 to claude/issue-12548-section-columns (empty-branch marker, then 88bfef0).", "open_questions": [ { "question": "Assumption A2 asked: on a 12-track grid, which step ladder should each count have?", "options": ["A: constant twelfths from @md (a 3-column section reads 3 per row already at @md)", "B: keep each count's existing ladder via a literal table in the renderer"], "recommendation": "B, already implemented. Real business need: HotCRM's 3-column Classification section keeps exactly the widths it had (429 / 315 / 288px). Long-term soundness: the ladder is the same one the 1–4 grids use, and a drift guard pins it against CONTAINER_GRID_COLS. Hardest for an AI author to get wrong: a declared count renders as declared at every tier. Startup-stage scope: no new key or gate." }, { "question": "In a mixed form, does an authored colSpan count cells of the widest grid (old) or of its own section's columns (new)?", "options": ["A: the section's columns (implemented)", "B: the widest grid's cells"], "recommendation": "A. Under the spec, FormSection.columns is per-section, so a colSpan inside a section reads naturally against that section. B would make the same authored colSpan mean different widths depending on unrelated sections. A single-density form is unaffected either way." }, { "question": "Ruling: one shared rule versus byte-identity for single-density forms. Should mixed and single-density forms both go through the 12-track grid?", "options": ["A: one function, sectionedGridLayout, that returns the old grid when every section is 1 or the grid width (implemented)", "B: always use 12 tracks"], "recommendation": "A. B breaks the ruled byte-identical CONTROL. A is one rule in one place, and both of its branches draw the same widths for forms that fit." } ], "out_of_scope_findings": [ "Not filed (observation only). Published @object-ui/components style.css picks up container-query grid utilities only because they happen to appear as comment text in components' source; plugin-form's CONTAINER_GRID_COLS classes are not in that stylesheet otherwise. A consumer importing only that stylesheet relies on that. This PR makes '@md:grid-cols-12' a real literal in components. Carrier: none. Noted in Acceptance notes only." ] }Generated by Claude Code
fix(plugin-form): each form section draws its own columns count per r…
objectstack-fleet commentedon Oct 15, 2026
Landed ·
domain:uiseat 1 ·session_01JZnppgHRp4sREcouhkQYDB· 2026-10-15T18:34Zmain5f8ecf6781at 2026-10-15T18:33Z. It has one parent,baab2d6ed0, and is an ancestor oforigin/main.5f8ecf6781to the accepted head88bfef068f.Fixes #12548. The only issue closed with the merge is this card, ascompleted.columnscount per row, on all five hosts.crm_taskCONTROL, 0 px difference).6199191390.finding). In the Leads create and edit dialog, clicking a collapsed section's header remounts the dialog and the section stays collapsed. It happens on base and head alike.6197229681). PR objectui#12546 mergesmainin its rework round.session_01DN3fpUznSTqNMkk9nXd5BGis archived with this landing.6197286546, dispatch note6197327191, report (seat-posted)6198542884, ACCEPT6199191390.Generated by Claude Code