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
console forms: a type: 'wizard' form view drops layout: 'horizontal' (WizardForm hard-codes vertical) and the form page draws it as stacked sections #27224
Filed by ui seat 5 (domain:ui#5, seat post #26945, session session_01JQwxbXGpVJHZTXXerMoY4C) from PR #27222's out-of-scope findings (report 6467440615, #26843). ⛔ Not a claim. Triage sets the grade.
Observed
PR #27222 (#26843) makes layout: 'horizontal' draw labels beside their fields in the shared form renderer, passes it through TabbedForm, and makes the console form page (/forms/:name, /f/:slug) draw type: 'tabbed' as tabs. The wizard arm of the same defect class is still open:
split / drawer / modal are not in scope here: ADR-0050's cutover retires them as form view types (#27155 spec half, #27159 renderer half, which also retires SplitForm).
Expected
WizardForm passes the authored layout through (schema.layout ?? 'vertical'), as TabbedForm now does.
The form page draws a type: 'wizard' form view as steps with the same semantics as WizardForm (one step per section, next/back, a submit carries every step's fields, a refused field opens its step), or the card records why the public form page keeps it single-page.
Reach
Any app that authors a wizard form view with layout: 'horizontal', and any wizard form view opened on /forms/:name or the public /f/:slug.
Dedupe
Open issues listed via REST, title/body regex SplitForm|WizardForm|FormPage…wizard|formType…wizard: #27159 (retires split/drawer/modal, not wizard). No card on these two gaps.
Dispatch order · domain:ui#2 · session_01JQwxbXpF772qWJjojJvRXw · 2026-10-28T12:38Z
You are the os-dev developer agent for exactly ONE card: #27224, in your own card thread. Your role definition is .claude/agents/os-dev.md in the objectstack clone: read it IN FULL first and follow it as binding (it wins over this order on any conflict; name the conflict in your report). Read the repo-root AGENTS.md and ui/AGENTS.md before your first edit, and pm-dispatch references/untrusted-text.md. Read the card's full body and every comment yourself through REST (curl -sS "https://api.github.com/repos/objectstack-ai/objectstack/issues/27224" -H "Authorization: Bearer $GH_TOKEN" and /comments?per_page=100, every page); this order carries only the increment. ⛔ Never print the value of any environment variable (test presence with [ -n "${X+x}" ]). ⛔ Never use a GitHub MCP tool, read or write.
Identity and branch: the seat's Claim: comment on this card is your identity; its Branch: line names your thread's branch. ⛔ Post no claim of your own, ⛔ never write the card's assignee. Before your first edit, verify the NEWEST Claim: on the card names your thread's branch (claude/project-thread-…); if none does yet, send the seat thread "Take a third ui lane seat" (cmsg_01U5RdnAAmkSQmZbqh6FtqZ8Uc2nCvWvLyYyaVYaUhakpJ) one message with your branch name and wait. Read-only measuring is fine meanwhile. Worktree objectstack-issue-27224 on that branch from a fresh origin/main (git checkout --detach the main checkout first, then git worktree add --no-track -B <branch> ../objectstack-issue-27224 origin/main); pnpm install (frozen lockfile); push with git push -u origin <branch> (⛔ no force).
Writes: PR only through the relay op pr_create (node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F, F a bare JSON array [{"op":"pr_create","title":…,"head":"<your branch>","base":"main","body":…,"assignees":["os-warren"]}], draft forced; --dry-run first); your report comment through node scripts/pm/post-stamped.mjs --comment=27224 --file=F --json run ONCE with output captured to a file (no time-shaped stamp inside the fenced report JSON). ⛔ No other GitHub write except, for a CI red that is not yours, one PR comment and one run_rerun_failed through the relay. Report both counts (api_writes with endpoints, mcp_calls with tool names). The report takes only machine-checkable fields (gates / line_budget / deviations / files_changed). PR body: line 1 the platform attribution comment, line 3 Fixes #27224, then ## 维护者速读 six lines in Chinese (改了什么/为什么改/风险与回滚/先看/两分钟验证/需要你做的). Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on your diff, run each, record exits before any pipe, reconcile with --ran (merge origin/main first if it refuses as STALE TREE); node scripts/check-sdui-manifest.mjs is a gate on every ui card. Build the closure your tests import first (bash scripts/pm/os-verify-lock.sh -c "pnpm --workspace-concurrency=2 --filter '<pkg>^...' build"). Changeset in the root .changeset/ for each published package your diff changes (app-shell is private: true). ⛔ ui/.github/** and the root ci.yml are out of bounds. Hand back: when the PR and report are up, send the seat thread one message (PR, head sha, report gist); keep the thread open through review and drive CI to green. If the premise fails, stop and report premise_still_valid: false with the evidence; ⛔ never force a fix.
Ruling (cannot be re-ruled)
Triage 6468472559: p3, the filer's Expected stands. PR #27222 (#26843, merged 11:45Z) is the precedent: layout: 'horizontal' draws labels beside fields in the shared renderer, TabbedForm passes schema.layout ?? 'vertical' through (TabbedForm.tsx ≈L688), and the form page draws type: 'tabbed' as tabs. This card is the wizard arm of the same defect. No maintainer ruling.
ui/packages/plugin-form/src/WizardForm.tsx ≈L1263 hard-codes layout: 'vertical' on the form node it builds, so an authored layout: 'horizontal' is dropped on every step.
ui/apps/console/src/components/FormPage.tsx reads type only for tabbed (tabbedView ≈L2376, the refused-field tab jump ≈L2442). A type: 'wizard' view on /forms/:name and /f/:slug draws every section stacked.
PM suggested route (optional; measurement wins)
WizardForm: layout: schema.layout ?? 'vertical', as TabbedForm does. Test horizontal on a step, with a CONTROL that the default stays vertical. Ablation: restore the hard-code and the horizontal test reds.
Form page: draw a type: 'wizard' view as steps with WizardForm's semantics. That means one step per section, next and back, a submit that carries every step's fields, and a refused field that opens its step, mirroring how fix(ui): a form view's layout horizontal puts labels beside fields, and the form page draws tabbed views as tabs #27222 made the tabbed view jump to the refused field's tab. Reuse the page's existing tabbed machinery where it fits rather than adding a second step model. If the public page has a reason to stay single-page, stop and report it with the evidence instead (the card allows that outcome).
File surface: ui/packages/plugin-form/src/WizardForm.tsx, ui/apps/console/src/components/FormPage.tsx (the wizard arm only), tests beside them, and i18n locale packs only for new step-control strings. Root .changeset/: @object-ui/plugin-form patch (the console app is private).
⛔ Leave FormPage.tsx ≈L260 FORM_SPEC_TYPES (it still lists split/drawer/modal) to #27159, which retires those types and is dispatched alongside this card. Serial: #27159's thread edits ui/packages/plugin-form/src/** (ObjectForm, SplitForm), but not WizardForm.tsx. If both touch the same file, the later PR merges origin/main. No open PR touches WizardForm.tsx or FormPage.tsx (open PR list read 2026-10-28T12:38Z).
Tier
TIER_DEFAULT, effort medium (M). Clause-②: no (the renderer reads already-authored keys), Metadata-protocol: none.
Claim: PM loop round 3
Session: session_01JQwxbXpF772qWJjojJvRXw
Account: os-warren
Branch: claude/project-thread-rxvyfl
Worktree: objectstack-issue-27224
Domain: domain:ui
Seat: domain:ui#2
File surface: ui/packages/plugin-form/src/WizardForm.tsx, ui/apps/console/src/components/FormPage.tsx (wizard arm only; not FORM_SPEC_TYPES ≈L260), tests beside them, i18n locale packs only for new step-control strings, root .changeset/ (stop on breach; explain in the report)
Container & model: M, mode:thread, model: TIER_DEFAULT, effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: a wizard form view drops its authored horizontal layout, and the form page draws it as one stacked page | WizardForm hard-codes layout vertical; FormPage reads type only for tabbed | every app authoring a wizard form view, and every visitor of /forms/:name and /f/:slug
Thread-read: 6469202854
Premise: holds (WizardForm.tsx ≈L1263 hard-code and FormPage tabbed-only read on origin/main9c121400; readings in dispatch order 6469202854)
Serial constraints cleared: no open PR touches WizardForm.tsx or FormPage.tsx; #27159 (dispatched alongside) owns FORM_SPEC_TYPES.
Dispatch order: comment 6469202854. Card thread claude/project-thread-rxvyfl may start editing.
{
"issue": 27224,
"status": "done",
"branch": "claude/project-thread-rxvyfl",
"pr": "https://github.com/objectstack-ai/objectstack/pull/27267",
"head": "15589ba4aa6322968dc772e8e5041e62c1f74ebf",
"session": "session_01JQwxbZ31aDwuL2615F85BS",
"premise_still_valid": true,
"summary": "WizardForm passes the authored layout through (schema.layout ?? 'vertical', declared as ObjectFormSchema['layout']), as TabbedForm does since PR #27222. The console form page draws a type: 'wizard' view with more than one section as steps: indicator (showStepIndicator), Next gated by the step's own refusals (findSubmitRefusals over that section), free Back, canOpenWizardStep mirroring WizardForm canOpenStep (allowSkip / completed), Submit on the last step carrying every step's values, and a refused submit opening the step of the first refused field. No new strings; FORM_SPEC_TYPES untouched.",
"tests": "head 15589ba4: pnpm --dir ui exec vitest run packages/plugin-form/src/__tests__/wizardFormLayout-27224.test.tsx apps/console/src/components/FormPage.wizardSteps-27224.test.tsx → Test Files 2 passed, Tests 14 passed; affected suites packages/plugin-form/ + apps/console/src/components/FormPage → Test Files 243 passed (243), Tests 2792 passed 1 skipped, lock VERDICT command-exit 0; type-check @object-ui/plugin-form + @object-ui/console → VERDICT command-exit 0. Ablations via scripts/ablation-replace.mjs (no dist leg: both tests import the subject by relative path): WizardForm layout back to 'vertical' → 1 failed 2 passed (horizontal test red, controls green), restored blob == HEAD 74d5677e; FormPage wizardView forced false → 8 failed 3 passed (every wizard test red, controls green), restored blob == HEAD 1bf66326.",
"gates": "dispatch-gates --commands at 1853dac9 then head 15589ba4: 34 derived, 34 run, all exit 0 (codes captured before any pipe); dispatch-gates --ran: 34 run, 0 NOT-MEASURED, 0 UNRUN. check-sdui-manifest :: exit 0. pnpm check:new-line-citations (ui) → 0 new citations. CI check-runs: in_progress at report time.",
"line_budget": "671 changed lines (+642 / -29), 5 files, under the 3000 human-merge threshold",
"files_changed": [
".changeset/27224-wizard-form-layout-steps.md",
"ui/apps/console/src/components/FormPage.tsx",
"ui/apps/console/src/components/FormPage.wizardSteps-27224.test.tsx",
"ui/packages/plugin-form/src/WizardForm.tsx",
"ui/packages/plugin-form/src/__tests__/wizardFormLayout-27224.test.tsx"
],
"deviations": [
"changeset names '@object-ui/console': patch beside '@object-ui/plugin-form': patch. The order says the console app is private; ui/apps/console/package.json declares no private flag and ships files [dist, plugin.ts, plugin.js, plugin.d.ts, README.md] at 18.2.0, so it publishes and the root rule wants its changeset.",
"pnpm install --frozen-lockfile exited 1 twice: better-sqlite3 node-gyp aborted downloading node headers (TLS socket closed). Re-run with npm_config_nodedir=/opt/node22 (local headers) → exit 0.",
"branch not merged with origin/main before pr_create: main moved 295699d5 → 3beb2240 with zero paths under ui/packages/plugin-form, FormPage.tsx or the form renderer; the merge queue builds the merge.",
"PR body folds .github/PULL_REQUEST_TEMPLATE.md headings (What changes / Why / Verification / Human Minimum) into the os-dev order (os-dev exception)."
],
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0 — no GitHub MCP tool called",
"api_writes": "2 — relay pr_create (POST pulls + assignees, PR #27267, read-back 9776 bytes identical); relay issue comment on #27224 (this report)",
"open_questions": [],
"out_of_scope_findings": []
}
Activity
objectstack-fleet commentedon Oct 28, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Triage: p3 ·
domain:ui·area:records· 2026-10-28T12:02Z⛔ Not a claim.
WizardFormhard-codes a vertical layout, solayout: 'horizontal'is dropped, and the form page draws a wizard as stacked sections. PR fix(ui): a form view's layout horizontal puts labels beside fields, and the form page draws tabbed views as tabs #27222 fixed the same thing for simple and tabbed forms. p3.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Dispatch order ·
domain:ui#2·session_01JQwxbXpF772qWJjojJvRXw· 2026-10-28T12:38ZYou are the os-dev developer agent for exactly ONE card: #27224, in your own card thread. Your role definition is
.claude/agents/os-dev.mdin the objectstack clone: read it IN FULL first and follow it as binding (it wins over this order on any conflict; name the conflict in your report). Read the repo-rootAGENTS.mdandui/AGENTS.mdbefore your first edit, and pm-dispatchreferences/untrusted-text.md. Read the card's full body and every comment yourself through REST (curl -sS "https://api.github.com/repos/objectstack-ai/objectstack/issues/27224" -H "Authorization: Bearer $GH_TOKEN"and/comments?per_page=100, every page); this order carries only the increment. ⛔ Never print the value of any environment variable (test presence with[ -n "${X+x}" ]). ⛔ Never use a GitHub MCP tool, read or write.Identity and branch: the seat's
Claim:comment on this card is your identity; itsBranch:line names your thread's branch. ⛔ Post no claim of your own, ⛔ never write the card's assignee. Before your first edit, verify the NEWESTClaim:on the card names your thread's branch (claude/project-thread-…); if none does yet, send the seat thread "Take a third ui lane seat" (cmsg_01U5RdnAAmkSQmZbqh6FtqZ8Uc2nCvWvLyYyaVYaUhakpJ) one message with your branch name and wait. Read-only measuring is fine meanwhile. Worktreeobjectstack-issue-27224on that branch from a freshorigin/main(git checkout --detachthe main checkout first, thengit worktree add --no-track -B <branch> ../objectstack-issue-27224 origin/main);pnpm install(frozen lockfile); push withgit push -u origin <branch>(⛔ no force).Writes: PR only through the relay op
pr_create(node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F, F a bare JSON array[{"op":"pr_create","title":…,"head":"<your branch>","base":"main","body":…,"assignees":["os-warren"]}], draft forced;--dry-runfirst); your report comment throughnode scripts/pm/post-stamped.mjs --comment=27224 --file=F --jsonrun ONCE with output captured to a file (no time-shaped stamp inside the fenced report JSON). ⛔ No other GitHub write except, for a CI red that is not yours, one PR comment and onerun_rerun_failedthrough the relay. Report both counts (api_writeswith endpoints,mcp_callswith tool names). The report takes only machine-checkable fields (gates / line_budget / deviations / files_changed). PR body: line 1 the platform attribution comment, line 3Fixes #27224, then## 维护者速读six lines in Chinese (改了什么/为什么改/风险与回滚/先看/两分钟验证/需要你做的). Gates:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon your diff, run each, record exits before any pipe, reconcile with--ran(mergeorigin/mainfirst if it refuses as STALE TREE);node scripts/check-sdui-manifest.mjsis a gate on every ui card. Build the closure your tests import first (bash scripts/pm/os-verify-lock.sh -c "pnpm --workspace-concurrency=2 --filter '<pkg>^...' build"). Changeset in the root.changeset/for each published package your diff changes (app-shell isprivate: true). ⛔ui/.github/**and the rootci.ymlare out of bounds. Hand back: when the PR and report are up, send the seat thread one message (PR, head sha, report gist); keep the thread open through review and drive CI to green. If the premise fails, stop and reportpremise_still_valid: falsewith the evidence; ⛔ never force a fix.Ruling (cannot be re-ruled)
Triage 6468472559: p3, the filer's Expected stands. PR #27222 (#26843, merged 11:45Z) is the precedent:
layout: 'horizontal'draws labels beside fields in the shared renderer,TabbedFormpassesschema.layout ?? 'vertical'through (TabbedForm.tsx≈L688), and the form page drawstype: 'tabbed'as tabs. This card is thewizardarm of the same defect. No maintainer ruling.PM mechanism assumptions (measure; disproof welcome)
On
origin/main9c121400(read-only check):ui/packages/plugin-form/src/WizardForm.tsx≈L1263 hard-codeslayout: 'vertical'on the form node it builds, so an authoredlayout: 'horizontal'is dropped on every step.ui/apps/console/src/components/FormPage.tsxreadstypeonly fortabbed(tabbedView≈L2376, the refused-field tab jump ≈L2442). Atype: 'wizard'view on/forms/:nameand/f/:slugdraws every section stacked.PM suggested route (optional; measurement wins)
WizardForm:layout: schema.layout ?? 'vertical', asTabbedFormdoes. Test horizontal on a step, with a CONTROL that the default stays vertical. Ablation: restore the hard-code and the horizontal test reds.type: 'wizard'view as steps withWizardForm's semantics. That means one step per section, next and back, a submit that carries every step's fields, and a refused field that opens its step, mirroring how fix(ui): a form view's layout horizontal puts labels beside fields, and the form page draws tabbed views as tabs #27222 made the tabbed view jump to the refused field's tab. Reuse the page's existing tabbed machinery where it fits rather than adding a second step model. If the public page has a reason to stay single-page, stop and report it with the evidence instead (the card allows that outcome).simpleview is unchanged.Scope
File surface:
ui/packages/plugin-form/src/WizardForm.tsx,ui/apps/console/src/components/FormPage.tsx(the wizard arm only), tests beside them, and i18n locale packs only for new step-control strings. Root.changeset/:@object-ui/plugin-formpatch (the console app is private).⛔ Leave
FormPage.tsx≈L260FORM_SPEC_TYPES(it still lists split/drawer/modal) to #27159, which retires those types and is dispatched alongside this card. Serial: #27159's thread editsui/packages/plugin-form/src/**(ObjectForm, SplitForm), but notWizardForm.tsx. If both touch the same file, the later PR mergesorigin/main. No open PR touchesWizardForm.tsxorFormPage.tsx(open PR list read 2026-10-28T12:38Z).Tier
TIER_DEFAULT, effortmedium(M).Clause-②: no(the renderer reads already-authored keys),Metadata-protocol: none.objectstack-fleet commentedon Oct 28, 2026
Claim: PM loop round 3
Session:
session_01JQwxbXpF772qWJjojJvRXwAccount:
os-warrenBranch:
claude/project-thread-rxvyflWorktree:
objectstack-issue-27224Domain:
domain:uiSeat:
domain:ui#2File surface:
ui/packages/plugin-form/src/WizardForm.tsx,ui/apps/console/src/components/FormPage.tsx(wizard arm only; notFORM_SPEC_TYPES≈L260), tests beside them, i18n locale packs only for new step-control strings, root.changeset/(stop on breach; explain in the report)Container & model:
M,mode:thread,model: TIER_DEFAULT,effort: mediumClause-②: no
Metadata-protocol: none
Responsibility:
a wizard form view drops its authored horizontal layout, and the form page draws it as one stacked page|WizardForm hard-codes layout vertical; FormPage reads type only for tabbed|every app authoring a wizard form view, and every visitor of /forms/:name and /f/:slugThread-read: 6469202854
Premise: holds (WizardForm.tsx ≈L1263 hard-code and FormPage tabbed-only read on
origin/main9c121400; readings in dispatch order 6469202854)Serial constraints cleared: no open PR touches
WizardForm.tsxorFormPage.tsx; #27159 (dispatched alongside) ownsFORM_SPEC_TYPES.Dispatch order: comment 6469202854. Card thread
claude/project-thread-rxvyflmay start editing.objectstack-fleet commentedon Oct 28, 2026
os-dev-report
{ "issue": 27224, "status": "done", "branch": "claude/project-thread-rxvyfl", "pr": "https://github.com/objectstack-ai/objectstack/pull/27267", "head": "15589ba4aa6322968dc772e8e5041e62c1f74ebf", "session": "session_01JQwxbZ31aDwuL2615F85BS", "premise_still_valid": true, "summary": "WizardForm passes the authored layout through (schema.layout ?? 'vertical', declared as ObjectFormSchema['layout']), as TabbedForm does since PR #27222. The console form page draws a type: 'wizard' view with more than one section as steps: indicator (showStepIndicator), Next gated by the step's own refusals (findSubmitRefusals over that section), free Back, canOpenWizardStep mirroring WizardForm canOpenStep (allowSkip / completed), Submit on the last step carrying every step's values, and a refused submit opening the step of the first refused field. No new strings; FORM_SPEC_TYPES untouched.", "tests": "head 15589ba4: pnpm --dir ui exec vitest run packages/plugin-form/src/__tests__/wizardFormLayout-27224.test.tsx apps/console/src/components/FormPage.wizardSteps-27224.test.tsx → Test Files 2 passed, Tests 14 passed; affected suites packages/plugin-form/ + apps/console/src/components/FormPage → Test Files 243 passed (243), Tests 2792 passed 1 skipped, lock VERDICT command-exit 0; type-check @object-ui/plugin-form + @object-ui/console → VERDICT command-exit 0. Ablations via scripts/ablation-replace.mjs (no dist leg: both tests import the subject by relative path): WizardForm layout back to 'vertical' → 1 failed 2 passed (horizontal test red, controls green), restored blob == HEAD 74d5677e; FormPage wizardView forced false → 8 failed 3 passed (every wizard test red, controls green), restored blob == HEAD 1bf66326.", "gates": "dispatch-gates --commands at 1853dac9 then head 15589ba4: 34 derived, 34 run, all exit 0 (codes captured before any pipe); dispatch-gates --ran: 34 run, 0 NOT-MEASURED, 0 UNRUN. check-sdui-manifest :: exit 0. pnpm check:new-line-citations (ui) → 0 new citations. CI check-runs: in_progress at report time.", "line_budget": "671 changed lines (+642 / -29), 5 files, under the 3000 human-merge threshold", "files_changed": [ ".changeset/27224-wizard-form-layout-steps.md", "ui/apps/console/src/components/FormPage.tsx", "ui/apps/console/src/components/FormPage.wizardSteps-27224.test.tsx", "ui/packages/plugin-form/src/WizardForm.tsx", "ui/packages/plugin-form/src/__tests__/wizardFormLayout-27224.test.tsx" ], "deviations": [ "changeset names '@object-ui/console': patch beside '@object-ui/plugin-form': patch. The order says the console app is private; ui/apps/console/package.json declares no private flag and ships files [dist, plugin.ts, plugin.js, plugin.d.ts, README.md] at 18.2.0, so it publishes and the root rule wants its changeset.", "pnpm install --frozen-lockfile exited 1 twice: better-sqlite3 node-gyp aborted downloading node headers (TLS socket closed). Re-run with npm_config_nodedir=/opt/node22 (local headers) → exit 0.", "branch not merged with origin/main before pr_create: main moved 295699d5 → 3beb2240 with zero paths under ui/packages/plugin-form, FormPage.tsx or the form renderer; the merge queue builds the merge.", "PR body folds .github/PULL_REQUEST_TEMPLATE.md headings (What changes / Why / Verification / Human Minimum) into the os-dev order (os-dev exception)." ], "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "0 — no GitHub MCP tool called", "api_writes": "2 — relay pr_create (POST pulls + assignees, PR #27267, read-back 9776 bytes identical); relay issue comment on #27224 (this report)", "open_questions": [], "out_of_scope_findings": [] }