Skip to content

console forms: a type: 'wizard' form view drops layout: 'horizontal' (WizardForm hard-codes vertical) and the form page draws it as stacked sections #27224

Closed

Description

@objectstack-fleet

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:

  1. ui/packages/plugin-form/src/WizardForm.tsx builds its form node with a hard-coded layout: 'vertical', the shape TabbedForm had before fix(ui): a form view's layout horizontal puts labels beside fields, and the form page draws tabbed views as tabs #27222. So a wizard form view's authored layout: 'horizontal' is dropped on every step.
  2. ui/apps/console/src/components/FormPage.tsx reads type only for tabbed after fix(ui): a form view's layout horizontal puts labels beside fields, and the form page draws tabbed views as tabs #27222. A type: 'wizard' form view opened on the form page draws every section stacked on one page, with no steps.

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.


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    on Oct 28, 2026
  2. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Triage: p3 · domain:ui · area:records · 2026-10-28T12:02Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    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.

    PM mechanism assumptions (measure; disproof welcome)

    On origin/main 9c121400 (read-only check):

    • 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)

    1. 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.
    2. 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).
    3. Tests beside the fix(ui): a form view's layout horizontal puts labels beside fields, and the form page draws tabbed views as tabs #27222 FormPage tests: steps drawn, next/back, submit carries all steps, a refused field opens its step. CONTROL: a simple view 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-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.

  4. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    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/main 9c121400; 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.

  5. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    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": []
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:uipriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions