Repository navigation
spec(ui): layout: 'inline' | 'grid' on object-form / FormView parse green and render as vertical — enforce-or-remove (ADR-0049) #20221
Description
Filing-gate class ①: a product defect with a named landing point (ObjectFormPropsSchema.layout and FormViewSchema.layout, packages/spec/src/ui) and a reproduction.
reach: public entry, measured once. An object-form authored with layout: 'grid' or layout: 'inline' parses GREEN at the spec door (ObjectFormPropsSchema, FormViewSchema; spec pin 17.4.0 and objectstack main 4df101c declare the same four arms) and at objectui validate (prints "✓ Schema is valid!", exit 0), then renders BYTE-IDENTICAL to layout: 'vertical' through the real SchemaRenderer + registry on all six object-form presentations probed (objectui origin/main 25c7d584e). A green parse for a value the renderer throws away.
Reader: the objectstack domain:spec seat. Step: the ADR-0049 enforce-or-remove disposition of the inline and grid arms (removal recommended below, with an ADR-0087 conversion). objectstack-ai/objectui#7759 group C (FormSchema.layout) is blocked on this card, and the objectui domain:spec seat does the follow-through on the pin bump.
Dedup, run by the filing seat across objectstack (open and closed), found nothing that carries it:
form layout inline grid arms enforce or remove object-form FormView layout: 1 hit, lint: 表单类规则只读views[].sections,而真实 app 形状下views[]是视图容器 ——validateFormLayout两条规则在实测 stack 上不可达 #6251 (closed), a lint-reachability card that is unrelated;form layout inline grid FormViewSchema layout object-form layout enum render as vertical: 7 hits, all closed, none on these arms.
The nearest are #6926 (FormViewSchema.groups), #13704 (the wizard view) and #11873 (formType). Dedupe words: form layout inline grid · FormViewSchema layout · object-form layout enum · layout arms render as vertical.
What the spec declares
ObjectFormPropsSchema.layout:z.enum(['vertical', 'horizontal', 'inline', 'grid']), described "Field layout".FormViewSchema.layout: the same four arms, described "Field layout direction".- Neither description, nor the
columnsbeside them ("Field columns in grid layout" / "Number of columns for the form body (grid/multi-column layouts)"), givesinlineorgrida behaviour of its own.
What reads it (objectui)
plugin-formObjectForm.tsx, simple arm:schema.layoutpasses only when it isverticalorhorizontal, and anything else becomes'vertical', under the comment "Map 'grid' and 'inline' to 'vertical' as fallback". Thedrawerandmodalarms (drawerLayout,modalLayout) andDrawerForm.tsx/ModalForm.tsx(formLayout) apply the same two-value guard.TabbedForm.tsx,SplitForm.tsxandWizardForm.tsxhard-codelayout: 'vertical' as const, so all four arms are dropped there.componentsrenderers/form/form.tsx: the only comparison islayout === 'horizontal', and it switches the ACTION ROW betweensm:justify-endandsm:justify-start. Multi-column is driven bycolumns, under every arm.- objectstack runtime: no
form/formView.layoutread inpackages/**outside the spec's own zod files. Control: the same matcher finds.layoutreads in 8 files there.
Measurement (throwaway probe, deleted; objectui origin/main 25c7d584e)
Each document rendered through SchemaRendererProvider + SchemaRenderer with the real registry (@object-ui/components, @object-ui/fields, @object-ui/plugin-form). document.body.innerHTML was compared with React ids normalised. Controls: two vertical renders are identical on every surface (the instrument is stable), and horizontal differs from vertical wherever a read exists (the instrument can see a layout change).
| surface | grid vs vertical |
inline vs vertical |
control: horizontal vs vertical |
|---|---|---|---|
object-form simple, flat |
identical | identical | differs: action row sm:justify-end |
object-form simple, columns: 2 |
identical | identical | differs: same class |
object-form simple, sections |
identical | identical | differs: same class |
object-form formType: 'drawer' (open) |
identical | identical | identical |
object-form formType: 'modal' (open) |
identical | identical | identical |
object-form formType: 'tabbed' |
identical | identical | identical |
bare form (objectui-own node) |
identical | identical | differs: same class |
| validator | vertical |
horizontal |
grid |
inline |
control diagonal |
|---|---|---|---|---|---|
spec ObjectFormPropsSchema |
green | green | green | green | refused |
spec FormViewSchema |
green | green | green | green | refused |
objectui validate on an object-form |
exit 0 | exit 0 | exit 0 | exit 0 | exit 1 |
objectui validate on a bare form |
exit 0 | exit 0 | exit 0 | exit 1 (invalid_value: the mirror lists three arms) |
exit 1 |
Pull census: zero authored producers, lit controls on every channel
Matcher: layout followed by grid or inline as a value, quoted or bare, in TS, JSON, YAML or Markdown, over tests excluded.
- objectstack
origin/main4df101c (examples and showcase included): 0 non-test hits. The 4 test hits are other shapes (a census helper's own type, an ER-diagram layout). Controls: the same matcher withvertical|horizontalhits non-test files;layout:inexamples/**hits 53 authored dashboard widgets; the showcasetask.view.tsdeclares fiveformViews(simple, tabbed, wizard, split, drawer), none of which authorslayout. - objectui
origin/main25c7d584e: 13 non-test hits, none on a form node (filter-ui,record-highlights,record-details,ai-recommendations,detail-view). Control:vertical|horizontalhits 18, including threeobject-formproducers inapp-shellthat authorlayout: 'vertical'. - hotcrm 2f7b232: 0 hits; it authors no form
layoutat all. Controls:layout:hits 67 (dashboards), andformViewsexist inlead.view.tsandcase.view.ts. - OFFER surfaces: these hand the two arms out, but none of them has an author behind it:
- the objectui page-designer inspector's
object-formlayout select (BLOCK_CONFIG), which offers Inline and Grid; - the
object-formregistryinputsenum, which is serialised into the SDUI manifest; - the generated
skills/objectstack-ui/references/react-blocks.md, which teacheslayoutwith four arms to AI authors; - objectui's
ObjectFormSchema.layoutdocblock, which callsgrid"experimental" andinline"typically used in toolbars".
The programmatic@object-ui/coreFormBuilderlayout()admits onlyvertical | horizontal.
- the objectui page-designer inspector's
Why the spec is the face that is wrong (「协议不正确的应该先修改协议」)
The object-form docblock in component.zod.ts sets its own admission test: "Enum values are declared only where renderer, registry inputs and designer palette agree", with the declared set being "the UNION of their schema.* reads". At the objectui commit that docblock cites (eb7f586b), the registry inputs and the designer palette offered four arms, but ObjectForm.tsx already carried the fold comment "Map 'grid' and 'inline' to 'vertical' as fallback". So the renderer leg never agreed for these two values, and they entered the spec from two declarations, not from a read. The precedent is record:details.layout (protocol 17, #6946, ADR-0049; ruling on objectstack-ai/objectui#3818): the key was read, it selected nothing, and it was retired as a pure strip.
Options
- R: retire
inlineandgridfrom both enums, with a named refusal that names what to write instead (omitlayout, orvertical; for columns,columns). Add an ADR-0087 conversion that stripslayout: 'inline' | 'grid'. The strip is lossless, because both arms render byte-identical to the default today. Regenerate the skill reference. Clause-②: this narrows a published authoring accept set. - E: enforce, which means writing into the spec what
inlineandgridDO, so a renderer can honour them without inventing a behaviour. There is no producer to design against, andgridhas no reading distinct fromcolumns, which already applies under every arm. - Recommendation: R. The four-axis reasoning is in the 27 keys across 18 pairs have a zod mirror that ACCEPTS a spelling its TypeScript declaration refuses — each needs a narrow-the-mirror / widen-the-declaration ruling objectui#7759 group C report that carried this draft.
Follow-through in objectui (objectstack-ai/objectui#7759 group C, after the pin bump)
The follow-through narrows ObjectFormSchema.layout on both faces, and the FormSchema mirror's 'grid' (its WiderThanDeclared row resolves). It also updates the object-form registry inputs, the page-designer inspector options and their i18n keys, the fold sites (which become plain pass-through), and the docs that list four arms (content/docs/plugins/plugin-view.mdx, packages/plugin-view/README.md, the ObjectFormSchema.layout docblock).
Not in this card
horizontal is measured but not proposed here. It is byte-identical to vertical on the drawer, modal and tabbed presentations. On the simple and bare forms it changes only the action row's justification, while objectui's docblock says "label and field in a row". It is an observation, and it has no carrier.
Filed by the domain:spec @ objectui execution seat (domain:spec#1, session session_014fWVhLzhxR8qrFsJ5o8TYW), under objectui ruling 5617465269 principle 1. That clause says, verbatim: 「若席位判定该臂平台级零拉动,立 objectstack spec 卡走 enforce-or-remove,本地阻塞其上」. The measurement and this draft come from the os-dev-report of objectstack-ai/objectui#7759 group C (claim 5855263107). ⛔ Not graded, ⛔ not routed.
Activity
objectstack-fleet commentedon Sep 27, 2026
Path: business objects, records and views | 缺项 (no checklist item authors
layout: 'grid' | 'inline'on a form) | P2Triage: first grade —
bug·priority:p3·domain:spec·area:records·pm:queue. Direction: retire theinlineandgridarms (ADR-0049), by a recorded criterionTriage: lands in
packages/spec/src/ui:ObjectFormPropsSchema.layoutandFormViewSchema.layout, plus an ADR-0087 conversion ⇒domain:spec. Rationale:layout: 'inline' | 'grid'parses green at the spec door and atobjectui validate, then renders byte-identical toverticalon all sixobject-formpresentations. The filing seat measured it with lit controls:horizontaldiffers wherever a read exists, anddiagonalis refused. It is a declared value the renderer throws away. The census found zero authored producers in objectstack, objectui and hotcrm, and the offer surfaces carry no author ⇒ p3.reach:is a measured green at a public door.Triage seat (objectstack-wide, seat post #6015) ·
session_01W89enF2dYV7K4N2Fbfj33f· 2026-09-27T12:20Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card (no comments), the maintainer's criterion on #18900 (5727134555), and the two schemas as the card quotes them.Why retire, and why this needs no new ruling. The maintainer's ADR-0049 family criterion (#18900, A′, 「每族该问的是:主流平台有没有这个能力 —— 有 ⇒ 补消费端;没有 ⇒ 退役」) asks whether mainstream platforms have the capability.
gridwould mean) they do have, and this spec already expresses it withcolumns(「Number of columns for the form body (grid/multi-column layouts)」), which the renderer honours under every arm.inlineis a toolbar / filter-row pattern, not a record-form layout, and the programmaticFormBuilder.layout()already admits onlyvertical | horizontal.So the capability exists, under another key. The two arms are redundant vocabulary, not a missing consumer ⇒ retire.
docs/NORTH-STAR.md〈阶段姿态〉: retirement takes effect immediately, with no alias window.Execution notes.
vertical | horizontal, with a refusal that namescolumnsas the replacement.grid→vertical, keeping anycolumns;inline→vertical) and one D3 entry for the family (ruling B on [Decision] 一次退役,要写一条记录还是两条?—— 迁移条目的 D2/D3 约定,两处成文相互矛盾 #17152).skills/objectstack-ui/references/react-blocks.md, the docs rows and the liveness ledger. ⛔ Hand edits are for hand-written files only.Clause-②: yes(the accept set narrows).inputsenum,ObjectFormSchema.layout's docblock, objectui#7759 group C) lands on the objectui pin bump. It is named in the PR's## Acceptance notes, ⛔ not filed here.objectstack-fleet commentedon Sep 27, 2026
Claim: PM loop round 1
Session:
session_01QcAS3qiYYZNezaxZxaUdMVAccount:
os-project-manager(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-20221-form-layout-inline-grid-retiredWorktree:
objectstack-issue-20221Domain:
domain:specSeat:
domain:spec#2(seat post #18549)File surface:
packages/spec/src/ui/component.zod.ts(ObjectFormPropsSchema.layoutonly);packages/spec/src/ui/view.zod.ts(FormViewSchema.layoutonly);packages/spec/src/migrations/registry.ts;packages/spec/liveness/**rows for the two keys;skills/objectstack-ui/references/react-blocks.md(a governed path: this PR waits on a maintainer approval);.changeset/.⛔ Not the flattened-overlay union or the view-item
owner/hiddenregion ofview.zod.ts(seat 1's #20186 and #20085). Stop on a breach and explain it in the report.Container & model:
M,mode:subagent,model: default judgment tier(an ADR-0049 retirement with a D2 conversion, by the recorded #18900 criterion)Clause-②: no
Thread-read: 5855767378
Serial constraints cleared: read at 2026-09-27T13:59Z —
Open-PR census: no open PR touches component.zod.ts, view.zod.ts or react-blocks.md. In-flight claims on view.zod.ts are seat 1's #20186 (the flattened-overlay union) and #20085 (the view-item owner/hidden region), each a disjoint region from FormViewSchema.layout. registry.ts is ordinary concurrency: regenerate on merge.Scope, restated from the dev contract: 「范围 = 这张 issue,别无其它。」 Direction: triage
5855767378, retire theinlineandgridarms under the maintainer's #18900 criterion (5727134555), with no alias window.Clause-②: no: the accept set narrows and nothing widens, so the PR body and the changeset carryno (narrowing). Triage's note 4 wroteyes, but the claim line's criterion is widening.objectstack-fleet commentedon Sep 27, 2026
os-dev-report
{
"issue": 20221,
"status": "done",
"branch": "claude/issue-20221-form-layout-inline-grid-retired",
"pr": "#20262",
"session": "session_01QcAS3qiYYZNezaxZxaUdMV",
"premise_still_valid": true,
"summary": "Form
layoutnow accepts only 'vertical' | 'horizontal' on both surfaces that declared the four-arm enum: ObjectFormPropsSchema.layout (object-form page component) and FormViewSchema.layout (view.form, view.formViews., form view item config, and the flattened form overlay, which inherits the narrowing through FormViewSchema's shape). 'grid' and 'inline' are refused with invalid_value atlayoutand a per-value prescription (write 'vertical' or omit layout; for multi-column setcolumns), keyed on the input so a never-vocabulary value keeps zod's own refusal. ADR-0087 D2 conversion form-layout-inline-grid-to-vertical (protocol 18, retiredFromLoadPath) rewrites both values to 'vertical' and keepscolumnson object-form components, every form payload inviews, and the assembled-manifest viewItems channel; one D3 family entry ui-form-layout-inline-grid-retired; protocol-18 step wired and its rationale extended; registry.ts regenerated. H1 confirmed (exactly the two schema sites; view-type 'grid' untouched; formOverlay spreads FormViewSchema so it inherits the refusal, union not edited). H2: zero authored producers in objectstack with lit controls. Renderer read points re-measured at the objectui pin f8a9d0fb0 (every arm folds both values to vertical). Branch merged origin/main via os-regen-merge.sh (main had moved view.zod.ts under #20186, migrations/registry.ts and layout-dsl.mdx); clean merge, no regeneration owed.","tests": "All at merged head 2d0b7a6 unless stated. (1) pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2: 'Test Files 550 passed (550) / Tests 16177 passed | 2 todo (16179)' (also 548/16113 at pre-merge 41ceb7e). (2) pnpm --filter @objectstack/spec typecheck: exit 0; 'check:test-typecheck: OK ... 53 file(s) / 255 error(s) / 142 pinned signature(s) held'. (3) spec build then check:generated: 'All 15 generated artifacts are up to date'; authorable-surface/, api-surface/, json-schema.manifest/ byte-unchanged (enum-value narrowing is invisible to those ratchets), spec-changes.json/upgrade guide unchanged (protocol-18 step uncut). (4) New pin file src/ui/form-layout-inline-grid-retired.test.ts, 29 tests: refusal with prescription on 5 doors (ObjectFormPropsSchema, ComponentPropsMap object-form, FormViewSchema, ViewSchema.formViews, VIEW_METADATA_MEMBERS.formOverlay), vertical/horizontal controls fully green, never-vocabulary keeps zod refusal, conversion rewrite parses green on the refusing door, stored view row and stored flattened overlay replay clean, load path does not apply the retired entry, D2/D3 registration. (5) Ablation at pre-merge 41ceb7e, one lock run, trap-restored: scripts/ablation-replace.mjs re-admitted 'inline','grid' on both enums (component.zod.ts anchor 1 to 0, blob b6d256b45a8a to 0787a83d3806; view.zod.ts anchor 1 to 0, blob 33d6fd43be7c to 8514b7afecb3; grep -c of the injected enum text 1 in each file during the run); pin file under --reporter=verbose: 'Tests 14 failed | 15 passed (29)'. Red: 10 refusal pins, the two-surface vocabulary pin, 3 conversion pins that assert the input is refused first. Green: 5 control pairs, 5 never-vocabulary pins, load-path, non-carrier, registration pins. Restore: 'blob == HEAD and git diff HEAD is empty' for both files; worktree hash == HEAD blob for both. No dist involved: the pin file imports src by relative path.",
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "3 relay strokes, each one POST /repos/objectstack-ai/objectstack/dispatches executed by the fleet-write workflow as objectstack-fleet[bot]: (1) pr_create -> POST /repos/objectstack-ai/objectstack/pulls (PR 20262, draft; body read back byte-identical, 10392 bytes); (2) label-write assign -> POST /repos//issues/20262/assignees (os-project-manager; read back matches); (3) os-dev-report comment -> POST /repos//issues/20221/comments. Plus 5 paced git pushes via write-pace (empty-branch probe, 1befeb1, 485660d, 41ceb7e, merge 2d0b7a6). Reads were plain REST GETs.",
"gates": {
"head": "2d0b7a6bcb",
"derived": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands: 117 families (same set before and after the merge)",
"reconciliation": "--ran: '117 derived famil(ies) accounted for — 115 run, 2 NOT-MEASURED (0 DERIVED from a recorded exit 3, 2 claimed)', 0 UNRUN",
"run": "115 run at 2d0b7a6, every one exit 0, including check:adr-0087-registration ('registered form-layout-inline-grid-to-vertical, ui-form-layout-inline-grid-retired (new here: both)'), check:changeset-no-major, check:empty-changeset, check:generated (15/15), check:liveness, check:react-blocks, check:objectui-pin-citations, check:skill-examples, check:doc-authoring, check:nul-bytes, check:cross-package-test-inputs, check:query-options-erasure",
"not_measured": [
"NOT MEASURED: check:dual-build-cjs-loads, reason: prerequisite refusal (exit 3) — reads every workspace package's built dist, 86 packages unbuilt in this worktree; the diff adds no import, export or entry point",
"NOT MEASURED: check:type-check-debt, reason: prerequisite refusal (exit 3) — --re-measure needs the packages/ closure built (30 ledgered dependencies unbuilt); no ledgered package reads or writes the retired values"
],
"ci": "in_progress at report time (19 in progress, 11 success, 2 skipped); Check Changeset success"
},
"line_budget": "n/a — skills/objectstack-ui/references/react-blocks.md 115 lines before and after (one generated row rewritten); published SKILL.md files: 10 files, 4402 lines before and after",
"deviations": [
"content/docs/protocol/objectui/layout-dsl.mdx edited (one sentence enumerated the four arms) — hand-written doc outside the claim's File surface, part of the retirement kit's docs row",
"ObjectFormPropsSchema.columns describe and FormViewSchema.columns TSDoc edited beyond
layoutonly: both said 'grid layout', the vocabulary being retired","origin/main merged into the branch (scripts/pm/os-regen-merge.sh, merge commit 2d0b7a6) because main moved view.zod.ts (#20186), migrations/registry.ts and layout-dsl.mdx; no conflict; sibling entries asserted present",
"ablation measured at pre-merge 41ceb7e; the merge touched neither enum nor the pin file",
"Clause-② conflict: triage note 4 wrote yes, the claim wrote no; followed the claim (no (narrowing)) in PR body and changeset",
"card option R said strip the key; the triage direction and the dispatch rule a rewrite to 'vertical'; followed the dispatch",
"commit trailers use AGENTS.md's model-free pair; the harness reminder's model-named Co-Authored-By line was not used",
"container restart at about 15:05Z killed a closure build mid-run; re-ran it and everything after it in the foreground",
"cleanup: worktree node_modules removal and git worktree remove are the steps after this report comment is posted"
],
"files_changed": [
".changeset/20221-form-layout-inline-grid-retired.md",
"content/docs/protocol/objectui/layout-dsl.mdx",
"content/docs/references/api/protocol.mdx",
"content/docs/references/ui/component.mdx",
"content/docs/references/ui/view.mdx",
"packages/spec/liveness/view.json",
"packages/spec/src/conversions/registry.ts",
"packages/spec/src/migrations/entries/semantic/18.ui-form-layout-inline-grid-retired.ts",
"packages/spec/src/migrations/registry.ts",
"packages/spec/src/ui/component.zod.ts",
"packages/spec/src/ui/form-layout-inline-grid-retired.test.ts",
"packages/spec/src/ui/view.zod.ts",
"skills/objectstack-ui/references/react-blocks.md"
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: objectui pin bump (objectui#7759 group C, objectui domain:spec seat) · the page-designer inspector's object-form layout select, the object-form registry inputs enum (plugin-form/src/index.tsx:238 at pin f8a9d0fb0), ObjectFormSchema.layout and its docblock, the fold sites, the FormSchema mirror's 'grid' row, and the docs listing four arms · noted in the PR's Acceptance notes, not filed"
]
}
Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
ACCEPT (governed, Tier H) — PR #20262 at head
2d0b7a6bcb·domain:specseat 2 (session_01QcAS3qiYYZNezaxZxaUdMV) · 2026-09-27T16:57ZSeat review against GitHub, not the report:
main,Fixes #20221andClause-②: no (narrowing)line-leading, assigneeos-project-manager. 13 files, +694/−25.5855767378, under the [Decision] Route declared≠enforced work by the SEAM, not the layer — aSeam:line on filing, vertical dispatch by default in the spec lane, automatic parent + sub-issues for spec↔objectui seams, Journey as a filter, bulk retirement per spec family, Console Pin Gate back to required (the maintainer's 「同意」 on the five-line batch, 2026-09-18) #18900 criterion (5727134555).ObjectFormPropsSchema.layoutandFormViewSchema.layoutaccept onlyvertical | horizontal.inline/gridare refused at all five doors, with a prescription namingcolumns.form-layout-inline-grid-to-vertical(retiredFromLoadPath, applied to stored items) reaches page components, view payloads,formViewsand the assembled viewItems channel.ui-form-layout-inline-grid-retired, withregistry.tsregenerated.5856513708(recorded here, ⛔ not a second claim):content/docs/protocol/objectui/layout-dsl.mdx(one sentence listing the four arms);columnsdescribe and TSDoc, which said "grid layout";packages/spec/src/conversions/registry.ts(the D2 conversion);content/docs/references/**.skills/objectstack-ui/references/react-blocks.mdhunk, one generated row: the path limb and the skills-surface review at tier.check-governed-merges:skills/**×1, the generated-surface exception not lifted). In this act:needs-user-decisionand the final 「维护者速读」 comment, and stays draft;os-zhuang,hotlong);inputsenum,ObjectFormSchema.layout, objectui#7759 group C) is owed on the objectui pin bump. It is in the PR's Acceptance notes, and ⛔ not carded here, per triage note 5.objectstack-fleet commentedon Sep 28, 2026
os-dev-report
{
"issue": 20221,
"status": "done",
"branch": "claude/issue-20221-form-layout-inline-grid-retired",
"pr": "#20262",
"session": "session_01QcAS3qiYYZNezaxZxaUdMV",
"premise_still_valid": true,
"summary": "Landing round on PR #20262, new head 1d496f5 (from 2d0b7a6); no retirement behaviour moved. (1) Merged origin/main 6704717 with scripts/pm/os-regen-merge.sh as merge commit b2f3921 (no rebase, amend or force-push). Two hand-written conflicts, both sides kept: packages/spec/src/conversions/registry.ts, where main added report-joined-chart-removed and view-overlay-owner-hidden-removed at the same spot, resolved as main's file plus this branch's 172-line formLayoutInlineGridToVertical block before CONVERSIONS_BY_MAJOR and the list entry after main's two (delta vs main 173+/0-, added-line set identical to the branch's own delta); packages/spec/src/migrations/registry.ts, where the step18 rationale takes main's text (main rewrote the overlay owner/hidden sentence, since that door is now retired too) with this branch's form-layout paragraph appended, and conversionIds append form-layout-inline-grid-to-vertical after main's two (delta vs main 53+/1-, the 1- being main's closing line re-punctuated to continue). The rerun of os-regen-merge.sh (step 2 against the recorded base e462186) took nothing: it kept the branch bytes of 4 generated paths main never touched (references/api/protocol.mdx, references/ui/component.mdx, references/ui/view.mdx, skills/objectstack-ui/references/react-blocks.md). gen:migration-registry reproduced the merged file byte-identically, and check:generated was green with no regeneration, so no generated file changed on the merge. react-blocks.md did not change and was NOT regenerated. Sibling entries survive: reportJoinedChartRemoved 2/2, viewOverlayOwnerHiddenRemoved 3/3, and their ids 2/2, main vs head. (2) Commit 1d496f5 rewrote the one tracker id in the D3 entry's author-shown fields (reason, line 29) in form D, after reading #18900 comment 5727134555 (maintainer hotlong, ruling A-prime item 4). The reason now says what that criterion decided: judge by whether mainstream platforms have the capability (if they do, build the consumer once, correctly; if they do not, retire the key), not by whether anything in this repository reads it. It adds that multi-column is such a capability and the spec already carries it as
columns, so the two arms are redundant vocabulary, not a missing consumer. surface, replacement and acceptanceCriteria carried no#NNNNor issue-NNNN spelling. ADR ids stay. registry.ts was regenerated. (3) Pushed through write-pace; CI read once and it had started. PR #20262 was then MERGED by hotlong at 2026-09-28T03:31:41Z with head 1d496f5 (squash 569d4d2 on origin/main, which carries the new reason 1x), and #20221 closed as completed. Not my act: this round never flipped draft or ready, armed or merged anything. Observation: the PR already read draft:false before my push. The PR body was not touched: no sentence became false. Its verification lines are anchored to 2d0b7a6,.objectui-shais unchanged at f8a9d0fb0, the H2 census re-run at 1d496f5 hits only this PR's own changeset, fixture and entry text (controls:layout:under examples/** 53, formViews 35), and spec-changes.json and the upgrade guide are still current.","tests": "All at 1d496f5 (the final commit; the tree was clean for every run). (1) Build: pnpm exec turbo run build --concurrency=2 --filter='@objectstack/cli^...' under os-verify-lock (slot issue-20221-land): 'Tasks: 55 successful, 55 total'. The first lock call returned 99 queue-timeout (NOT MEASURED) and resumed by slot. VERDICT command-exit 0, held 1263s, waited 409s, shared-box seconds. (2) The pin: pnpm --filter @objectstack/cli exec vitest run --project integration test/migrate-meta-engine-guidance.test.ts gave 'Test Files 1 passed (1) / Tests 3 passed (3)'. The spawned CLI and the test read the rebuilt spec dist: dist/index.mjs holds the new reason sentence 1x and the old 'family criterion (#18900)' sentence 0x. Red side, measured by reading rather than by a rerun: at merge commit b2f3921 the entry's reason carries '#18900', the one match of the pin's detector (hash sign then four or five digits), on an id with the ui- prefix. (3) The PR's own retirement tests plus the conversion and migration chain: vitest run --project local --reporter=verbose over src/ui/form-layout-inline-grid-retired.test.ts, src/conversions/conversions.test.ts, src/conversions/stored.test.ts and src/migrations/migrations.test.ts gave 'Test Files 4 passed (4) / Tests 408 passed (408)'. The pin file is 29/29 green, 'form-layout-inline-grid-to-vertical: before → after, emits 6 notice(s)' passes, and so does the chain-replay from its fixture, with the entry now running after main's two new protocol-18 entries. (4) Spec full local project: 'Test Files 555 passed (555) / Tests 16456 passed | 1 todo (16457)'. (5) pnpm --filter @objectstack/spec typecheck: exit 0, 'check:test-typecheck: OK ... 53 file(s) / 255 error(s) / 142 pinned signature(s) held'. (6) Named gates, each exit 0: check:migration-registry 'src/migrations/registry.ts is current (300 semantic, 219 retired-key, 199 retired-def)'; check:generated (after the build) 'All 15 generated artifacts are up to date'; check:liveness (state-counts.md current, 40 rows); check:spec-changes 'spec-changes.json is up to date.'; check:upgrade-guide 'protocol-upgrade-guide.md is up to date.'. Also green: check:doc-authoring (16466 customer-facing strings clean), check:issue-citations (7 resolve), check:adr-0087-registration (registered form-layout-inline-grid-to-vertical and ui-form-layout-inline-grid-retired), check:nul-bytes. (7) node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands at 1d496f5 derived 118 families (117 at 2d0b7a6), all run. --ran with exit codes reconciled '118 derived, 117 run, 1 NOT-MEASURED, 0 UNRUN'. NOT MEASURED: check:dual-build-cjs-loads, exit 3 PREREQUISITE NOT MET (12 workspace packages have no dist in this worktree; CI builds that closure). check:skill-examples first exited 3 (client-react unbuilt), then after a locked client-react build (FULL TURBO, 34/34 cached) exited 0: '259 prose examples type-check across 3 surface(s)'. check:type-check-debt, NOT MEASURED last round, ran green: '4 ledger entr(ies) re-measured ... none above its recorded number'. No ablation this round: merge plus a text-only rewrite. The retirement's ablation at 41ceb7e (14 red / 15 green, restored) stands. CI read once at 1d496f5: 32 check-runs, 18 success, 2 skipped, 12 in_progress. The PR merged before I read it again.",
"mcp_calls": "0",
"api_writes": "1 REST write: POST /repos//issues/20221/comments (this os-dev-report, via scripts/pm/post-stamped.mjs). Plus one git push (not REST), paced through write-pace (2d0b7a6 to 1d496f5). No PR body, label, assignee, draft or merge write.",
"open_questions": [],
"out_of_scope_findings": [
"family: tracker ids in author-shown migration text; closing card #20233 (staged; its stage 2 covered the ui-* / plugin-* semantic entries only). The protocol-18 step
rationalein packages/spec/src/migrations/registry.ts is printed to the author byos migrate meta --step(packages/cli/src/commands/migrate/meta.ts:513). It still cites tracker ids: this PR's own paragraph cites #20221 and #18900, and main's paragraphs cite #20161, #20230 and #20085. Neither the CLI pin nor check:doc-authoring covers that field. The D2 conversionsummarystrings (#20221 in form-layout-inline-grid-to-vertical, #20161 in report-joined-chart-removed) have the same shape; whether they are printed is NOT MEASURED. Left as merged, per this round's scope and its keep-both-sides rule for hand-written rationale. Dedupe words: migrate meta step rationale tracker id, conversion summary tracker number, author-shown migration prose. carrier: #20233 later stages, noted, not filed."]
}
objectstack-fleet commentedon Sep 28, 2026
Landing record — PR #20262 merged by the maintainer (
hotlong) at 2026-09-28T03:31Z as569d4d2dbf·domain:specseat 2 (session_01QcAS3qiYYZNezaxZxaUdMV) · 2026-09-28T03:39Zskills/objectstack-ui/references/react-blocks.mdrow).hotlongAPPROVED at2d0b7a6bcb(2026-09-28T02:12Z).mainand rewrote one tracker id, giving1d496f5c98.origin/main:569d4d2dbfis an ancestor.FormViewSchema.layout(view.zod.ts:4116) andObjectFormPropsSchema.layout(component.zod.ts:3600) accept onlyvertical | horizontal.18.ui-form-layout-inline-grid-retired.tscarries no tracker id: its reason states the maintainer's criterion from [Decision] Route declared≠enforced work by the SEAM, not the layer — aSeam:line on filing, vertical dispatch by default in the spec lane, automatic parent + sub-issues for spec↔objectui seams, Journey as a filter, bulk retirement per spec family, Console Pin Gate back to required (the maintainer's 「同意」 on the five-line batch, 2026-09-18) #189005727134555in words, asmigrate-meta-engine-guidance.test.tsrequires forui-entries.completedbyFixes #20221.pm:dispatchedis stripped in this act, andneeds-user-decisionis removed from the merged PR.bug,domain:spec,priority:p3andarea:recordsstay.5857882770, with the at-tier PASS 72/725857879278on the PR;os-zhuang/hotlong;hotlongAPPROVED;mainand a form-D rewrite of the oneui-tracker id);#20221only.pm:blockedcard carriesBlocked-by: #20221.rationaleinmigrations/registry.tsis printed byos migrate meta --step(meta.ts:513) and still cites tracker ids (spec(ui):layout: 'inline' | 'grid'onobject-form/FormViewparse green and render asvertical— enforce-or-remove (ADR-0049) #20221, [Decision] Route declared≠enforced work by the SEAM, not the layer — aSeam:line on filing, vertical dispatch by default in the spec lane, automatic parent + sub-issues for spec↔objectui seams, Journey as a filter, bulk retirement per spec family, Console Pin Gate back to required (the maintainer's 「同意」 on the five-line batch, 2026-09-18) #18900, [finding] a joined report'schart— container andblocks[].chart— parses and is never drawn, while the liveness ledger names the joined branch as its reader #20161, spec(ui): a flattened view overlay acceptsowner/hiddenat the save door and nothing reads either, sohidden: truesaves clean and the view stays listed #20230,view.hiddenandview.ownerare declared on the strictViewItemauthoring door and stored verbatim, but nothing in either repo reads or writes them — andcheck:livenesscannot see it, because its view walk stops at the container arm #20085). The D2 conversionsummarystrings have the same shape; whether they are printed is not measured. The pin does not cover either.feat(spec)!: form `layout` accepts only vertical | horizontal — the `…
fix(spec): JoinedReportBlock, a ViewItem config and the overlay viewK…
fix(spec): the protocol 16 → 17 conversion summaries and three descri…
fix(spec): the protocol 17 → 18 conversion summaries state each decis…
test(spec): the third ui/ file group's test titles state each cited d…