Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

ObjectFormSchema ↔ @objectstack/spec FormViewSchema alignment (ObjectView form adapter) #2545

Closed

Description

@os-zhuang

Split out from #1763 (viewschema key-drift audit, refs objectstack-ai/objectstack#1890).

Closed — everything actionable inside objectui is merged; the rest is tracked in dedicated follow-ups.

Outcome

Phase Status
1 — spec-bridge form passthrough + groups→sections normalization ✅ merged (#2552)
2 — key taxonomy ([spec-aligned]/[ObjectUI extension]/[runtime-only]) + docs ✅ merged (#2552)
4 — remove the dead FormRenderer/FieldFactory dual render path ✅ merged (#2560)
3 — framework spec proposal (buttons/defaults on FormViewSchema, enroll view in the liveness ledger) 📤 objectstack-ai/objectstack#2998
3 — objectui-side (/ui re-export value-erasure decision + spec ^15 pin bump) 📋 #2561 (maintainer decision)

What changed vs. the original report

Remaining work lives in #2561 (objectui) and objectstack-ai/objectstack#2998 (spec). Nothing left to do under this issue.

Activity

  1. added a commit that references this issue on Jul 16, 2026
  2. os-zhuang commented on Jul 16, 2026

    @os-zhuang
    ContributorAuthor

    Phase 1 + 2 landed via #2552 (merged as ddcc7d6):

    • ✅ Form bridge no longer drops spec FormViewSchema keys (full passthrough + mapField/mapSection completeness), locked in by the FormViewSpecConformance round-trip test — every spec key must land on the node or be explicitly listed in IGNORED_SPEC_KEYS with a reason.
    • ✅ groups → sections normalized at both consumers (bridge + ObjectForm); ObjectFormSchema.groups is now @deprecated; groups-only metadata renders grouped instead of silently flat.
    • ✅ ObjectView.buildFormSchema forwards form.sections.
    • ✅ Key taxonomy ([spec-aligned] / [ObjectUI extension] / [runtime-only]) documented in the type header and in schema-reference.md ("Spec alignment & extension keys").

    Remaining (tracked here):

    • Phase 3 — framework-side spec proposal: structured buttons config (absorbing the showSubmit/submitText family) and defaults/initialValues; bump objectui's @objectstack/spec pin to ^15.0.0 and restore spec installation in CI so tsc catches type drift.
    • Phase 4 — audit packages/react/components/form/FormRenderer.tsx usage and converge the dual render path onto bridge→ObjectForm (candidate for a separate issue when picked up).

    Generated by Claude Code

  3. os-zhuang commented on Jul 16, 2026

    @os-zhuang
    ContributorAuthor

    Re-evaluation of the remaining backlog (Phases 3–4)

    Evidence: full diff of the published @objectstack/spec 14.6.0 vs 15.0.0, plus two repo-wide audits (dependency state, FormRenderer usage).

    Phase 3 — item-by-item verdicts

    ❌ "Restore spec installation in CI so tsc catches drift" — withdrawn, was based on a wrong premise. @objectstack/spec@^14.6.0 is declared in 14 package.json files, fully pinned in pnpm-lock.yaml (:3993, importer at :1077), and both CI jobs run pnpm install --frozen-lockfile — CI's tsc has always resolved the spec and catches drift. My earlier claim came from inspecting a checkout that had simply never been pnpm installed. Correction noted.

    ✅ Spec pin bump ^14.6 → ^15.0 — low risk, worth doing. The /ui export-name surface is identical across both versions; exports subpaths unchanged → zero type-level breakage. v15's only major change is ADR-0089 D3a: FormFieldSchema / FormSectionSchema / PageComponentSchema are now .strict() (unknown keys = loud parse error instead of silent strip). When bumping, run example/test metadata through spec validation to catch fixtures carrying undeclared field/section-level keys.

    ✅ buttons / defaults spec proposal — still needed, now more urgent. In v15, FormViewSchema's top level remains strip-mode, so the showSubmit/submitText family is still silently stripped by spec validation — exactly the "silently inert" shape ADR-0078 forbids. With field/section level now strict, the top level is the last remaining seam for undeclared keys; structuring these into a spec buttons config (and defaults for initialValues) closes it.

    New findings from this evaluation

    1. Latent bug — /ui zod schema values are erased at re-export. All ~20 re-export blocks for @objectstack/spec/ui in packages/types/src/index.ts use export type { …Schema } — export type erases values, so importing e.g. DndConfigSchema from @object-ui/types yields undefined at runtime. Only index.ts:948-956 (defineStack etc.) is a real value export. Fix: convert to value exports (the dependency is installed, so this is safe) or drop the schema-name re-exports.
    2. Governance gap (framework side). The spec package ships a liveness ledger with a CI ratchet (liveness/*.json, enforce-or-remove per ADR-0049) — but there is no view.json: view/form properties are entirely outside liveness governance. That's the systemic root cause that let the viewschema key drift: bulkActions/batchActions + ObjectView form-adapter keys not in FormViewSchema #1763 / ObjectFormSchema ↔ @objectstack/spec FormViewSchema alignment (ObjectView form adapter) #2545 class of drift survive. The framework proposal should include bringing views into the ledger.

    Phase 4 — verdict upgraded: delete, don't converge

    packages/react/src/components/form/FormRenderer.tsx (262 lines) is exported-but-dead public API: zero runtime consumers — the only import in the whole repo is its own test file (27 tests); FieldFactory is captive to it. Functionally it's a degraded duplicate (raw HTML/Tailwind, hard-coded Submit button, no submitBehavior/aria/groups).

    Deletion scope: FormRenderer.tsx + FieldFactory.tsx + their tests; two re-exports (components/form/index.ts:9-10, react/src/index.ts:12); docs cleanup (content/docs/core/form-renderer.mdx, schema-renderer.mdx:165 cross-link, components/form/README.md, types/examples/form-renderer-example.ts, three skills guides, two stale app-shell README mentions). Zero consumers → hard delete + CHANGELOG breaking note; no migration needed.

    Side finding (separate issue candidate): SpecBridge itself is never instantiated in production — the live form path is app-shell RecordFormPage → ObjectForm directly, and plugin-form registers the bare 'form' type on ComponentRegistry (spec JSON reaches ObjectForm via SchemaRenderer without the bridge). The bridge is currently test-only plumbing; whether to wire it into the runtime path or lean on registry-direct rendering is an architecture question worth its own issue — not mixed into this one.

    Suggested implementation split

    • PR A (objectui): spec pin → ^15.0.0 (14 package.json + lockfile) + fix the /ui schema value-export erasure + validate fixtures under v15 strict mode.
    • PR B (objectui): delete FormRenderer/FieldFactory + docs cleanup.
    • framework: proposal issue — structured buttons + defaults on FormViewSchema, and add view to the liveness ledger.

    Generated by Claude Code

  4. os-zhuang commented on Jul 16, 2026

    @os-zhuang
    ContributorAuthor

    Phase 3 framework proposal filed: objectstack-ai/objectstack#2998 — structured buttons/defaults on FormViewSchema + enrolling view in the liveness ledger.

    Two corrections vs. my earlier comment, after auditing the actual framework source:

    • FormViewSchema is strip-mode, not .strict() — only the leaf FormFieldSchema/FormSectionSchema went strict in v15 (ADR-0089 D3a). So adding buttons/defaults is a clean additive change, but the container won't reject today's invented keys — it silently strips them.
    • ADR-0078 tension: the framework has no runtime consumer of FormViewSchema (it's purely objectui's protocol contract). Adding an authorable buttons block that nothing reads is itself "parsed-but-inert" — the thing ADR-0078 forbids. So Track A must co-land with the objectui ObjectForm wiring (cross-repo) or be marked [EXPERIMENTAL — not enforced]. Captured in the framework issue.

    view-liveness enrollment (Track B) is confirmed applicable — view is a governed-eligible metadata type (view: ViewSchema in BUILTIN_METADATA_TYPE_SCHEMAS) that's simply not yet in the GOVERNED rollout list — but it's a standalone effort classifying the whole ViewSchema surface, not a same-PR adjunct.


    Generated by Claude Code

  5. added a commit that references this issue on Jul 16, 2026
  6. os-zhuang commented on Jul 16, 2026

    @os-zhuang
    ContributorAuthor

    Phase 4 landed via #2560 (merged as f94905d): FormRenderer / FieldFactory and their @object-ui/react exports removed, docs + stale app-shell/skills guidance cleaned up. The dual form render path is gone — ObjectForm (plugin-form) is now the single path.

    Status of #2545

    Phase Status
    1 — bridge passthrough + groups→sections ✅ #2552
    2 — key taxonomy + docs ✅ #2552
    3 — framework spec proposal 📤 filed: objectstack-ai/objectstack#2998 (buttons/defaults + enroll view in liveness ledger)
    4 — remove FormRenderer dual path ✅ #2560

    Still open (needs a decision, not yet actioned)

    The objectui-side of Phase 3 — bump @objectstack/spec pin to ^15 and the /ui zod-schema re-export question — is deliberately not done yet because it involves a design call, not a mechanical fix:

    • packages/types/src/index.ts re-exports the whole @objectstack/spec/ui surface with export type { …Schema }, so the zod values are erased — importing a …Schema from @object-ui/types yields undefined at runtime. "Fixing" it by converting to value exports would give @object-ui/types a real runtime dependency on @objectstack/spec (bundle-size / eager-load impact). The alternative — dropping the schema-name re-exports entirely (keep them type-only) — may be the intended design. This is a judgment call for a maintainer rather than something to change unilaterally.
    • The spec ^15 pin bump is low-risk on the type surface (identical /ui exports) but should ride together with validating fixtures under v15's new field/section .strict() mode.

    Leaving these for maintainer direction.


    Generated by Claude Code

  7. os-zhuang commented on Jul 16, 2026

    @os-zhuang
    ContributorAuthor

    The objectui-side Phase 3 decision point is now tracked in #2561 (/ui re-export shape + spec ^15 pin bump) — kept out of a mechanical PR because option (a) vs (b) for the value-erased schema re-exports is a maintainer call. That leaves #2545 with only the cross-repo framework work outstanding (framework#2998); the objectui phases (1, 2, 4) are all merged.


    Generated by Claude Code

  8. added a commit that references this issue on Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions