Skip to content

feat(spec): FormSection.pane — explicit split-pane placement (objectui#2153 follow-up) - #4160

Merged
os-zhuang merged 2 commits into
mainfrom
feat/form-section-pane
Jul 30, 2026
Merged

feat(spec): FormSection.pane — explicit split-pane placement (objectui#2153 follow-up)#4160
os-zhuang merged 2 commits into
mainfrom
feat/form-section-pane

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Why

A type: 'split' form view has no way to say which pane a section renders in — the ObjectUI renderer hardcodes first section left, everything else right. Evaluated against two criteria (platform long-term soundness, AI-authoring error avoidance):

  • The positional rule is invisible in the metadata: nothing in the JSON records the assignment, so an agent asked to add or reorder sections silently moves them across the divider. Implicit positional semantics are the class of rule AI authors reliably get wrong.
  • An author cannot place two sections in the left pane at all.
  • The 'split' enum comment claimed "Master-Detail split" — but master-detail already has two homes (subforms on the form, related lists on record pages). A third would be redundant vocabulary (another AI-error source: three dialects for one intent). Split's only non-redundant meaning is the side-by-side section layout, so the comment now says that.

What

FormSectionSchema.pane: 'primary' | 'secondary' (optional):

  • Explicit, per-section — survives reordering; an agent editing the view sees and preserves placement.
  • Omitted → the legacy rule (first section primary, rest secondary) — keyless metadata keeps its exact layout, zero migration.
  • Split-only, fail-loud: a FormViewSchema refinement rejects pane on any other form type at parse — covering the legacy groups alias and the defaulted type: 'simple'. "Accepted but ignored" is the one failure mode this key must never have. Probed zod 4.4.3 before relying on it: .extend() keeps refinements, so the flattened runtime-overlay variant in ViewMetadataSchema (view.zod.ts:1491) enforces it too.
  • Strict two-value enum — pane: 'left' is a parse error, not free text; orientation-neutral names survive splitDirection: 'vertical' and map 1:1 onto ObjectUI's already-shipped fieldPanes keys (primary/secondary, objectui#3012).

Also: the showcase task split view declared a single section — which renders as a plain, unsplit form, i.e. the demo never demonstrated the feature — and now has two sections with explicit panes. authorable-surface.json regenerated (exactly one new entry: ui/FormSection:pane).

Verification

  • 5 new cases in view.test.ts: accepts panes on split (omitted stays undefined), rejects typo values, rejects pane on non-split with a pointed message at sections.1.pane, same for the groups alias, and defaulted-type counts as non-split.
  • @objectstack/spec: 273 files / 7124 tests green; build green (incl. the Studio JSON-schema derivation path).
  • @objectstack/example-showcase: 60/60 green after building its dep graph — the updated config round-trips the full Zod parse (gap-fill imports objectstack.config.js).

Follow-up (ObjectUI)

SplitForm maps sections → FormSchema.fieldPanes by the hardcoded slice today; the follow-up PR reads section.pane with the same positional default. No compile-time dependency on this spec version (the key arrives as plain JSON), but this PR merges first per cross-repo convention.

🤖 Generated with Claude Code

…tui#2153 follow-up)

A `type: 'split'` form view had no way to say which pane a section renders in:
the renderer hardcoded "first section left, everything else right". That
positional rule is invisible in the metadata, so reordering sections silently
moved them across the divider, and an author (human or AI) could not place two
sections on the left at all.

FormSectionSchema gains an optional `pane: 'primary' | 'secondary'`:

- explicit and PER SECTION, so placement survives reordering and an agent
  editing the view can see — and must preserve — where each section lives;
- omitted → the legacy positional rule (first section primary, others
  secondary), so keyless metadata keeps its exact layout;
- split-only, enforced loudly: a FormViewSchema refinement rejects `pane` on
  any other form type at parse (legacy `groups` alias and defaulted
  `type: 'simple'` included). "Accepted but ignored" is the failure mode this
  key must never have — a silent no-op reads as working, especially to an AI
  author. Verified that zod 4 keeps refinements through `.extend()`, so the
  flattened runtime-overlay variant in ViewMetadataSchema enforces it too;
- strict two-value enum — a typo ('left') is a parse error, not free text.

The 'split' enum comment claimed "Master-Detail split"; master-detail already
has two homes (`subforms` on the form, related lists on record pages), so the
comment now states split's non-redundant meaning: side-by-side resizable panes
with sections placed via `section.pane`.

The showcase task form's `split` view declared a single section — which renders
as a plain, unsplit form — and now demonstrates the feature: two sections with
explicit panes. `authorable-surface.json` regenerated (one new entry).

Renderer support ships in ObjectUI (SplitForm → FormSchema.fieldPanes, whose
pane keys are already named primary/secondary — a 1:1 mapping).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 30, 2026 1:08pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests protocol:ui tooling size/m labels Jul 30, 2026
check:docs gates generated reference docs against the spec; the new key needs
its generated row committed alongside the schema change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant