Repository navigation
spec: structured form button/defaults config on FormViewSchema + enroll view in the liveness ledger #2998
Description
Follow-up to the viewschema property-liveness audit (docs/audits/2026-06-viewschema-property-liveness.md, the framework#1890 line) and its objectui-side resolution (objectstack-ai/objectui#1763 → #2545, PR objectstack-ai/objectui#2552).
The objectui work reconciled the renderer-side key drift (bulkActions/batchActions, groups→sections) and classified every ObjectFormSchema key as [spec-aligned] / [ObjectUI extension] / [runtime-only]. That surfaced a residue that can only be closed here, in the spec.
Problem
ObjectView/ObjectForm in objectui read a family of form keys that do not exist in FormViewSchema (packages/spec/src/ui/view.zod.ts, FormViewSchema L832-929): showSubmit, submitText, showCancel, cancelText, showReset, initialValues (plus renderer-only className, fields, customFields). This repo's own audit already diagnosed it verbatim (docs/audits/2026-06-viewschema-property-liveness.md:25). These are renderer-invented surface with no spec backing — serializable form config that belongs in the protocol but isn't in it.
FormViewSchema is strip-mode (not .strict() — unlike the leaf FormFieldSchema/FormSectionSchema which are .strict() per ADR-0089 D3a, view.zod.ts L773/L810). So today these keys are silently stripped by a spec .parse() — the "parsed → silently inert" shape ADR-0078 prohibits.
Proposal — two tracks
Track A: add structured buttons + defaults to FormViewSchema (additive, non-breaking)
A new optional top-level key alongside submitBehavior (view.zod.ts L912-925). Sketch:
buttons: z.object({
submit: z.object({ show: z.boolean().optional(), label: I18nLabelSchema.optional() }).optional(),
cancel: z.object({ show: z.boolean().optional(), label: I18nLabelSchema.optional() }).optional(),
reset: z.object({ show: z.boolean().optional(), label: I18nLabelSchema.optional() }).optional(),
}).optional().describe('Form action-button visibility & labels'),
defaults: z.record(z.string(), z.unknown()).optional()
.describe('Initial field values for create-mode forms'), // absorbs objectui `initialValues`Because the container is strip-mode, this is purely additive (minor/patch changeset, no tombstone). The wizard nextText/prevText pair and showStepIndicator (already in spec) can be folded into buttons or left as-is — open question.
FormViewSchema — it's the protocol contract objectui renders. Adding an authorable buttons block that no code reads is itself parsed-but-inert, i.e. the exact thing ADR-0078 forbids. So Track A must land in one of two shapes:
- (preferred) co-landed with the objectui renderer wiring — objectui's
ObjectFormreadsschema.buttons.*/schema.defaultsinstead of its inventedshowSubmit/submitText/initialValues. Cross-repo, two worktrees. - (fallback) marked
[EXPERIMENTAL — not enforced]in the.describe()until the objectui side lands, per ADR-0078's escape hatch.
Note submitBehavior itself is currently classified dead (no renderer consumer) in the audit (L20) — worth wiring alongside, or the new block sits next to already-inert surface.
Track B: enroll view in the liveness ledger (separate, larger)
view is a governed-eligible metadata type (view: ViewSchema in BUILTIN_METADATA_TYPE_SCHEMAS, packages/spec/src/kernel/metadata-type-schemas.ts:78) — it round-trips through /api/v1/meta/types/:type and Studio admin forms. It is simply not yet in the GOVERNED rollout (packages/spec/scripts/liveness/check-liveness.mts:56; README lists it as rollout-pending, liveness/README.md:208). That rollout gap is the systemic reason the #1763/#2545 class of drift survived undetected.
Enrolling it: add 'view' to GOVERNED, --dump view, and author packages/spec/liveness/view.json classifying every top-level prop of ViewSchema (list/form/listViews/formViews/protection; the gate drills one level via children, so FormViewSchema's props — including a new buttons — classify under form.children.*). The existing audit already pre-computed much of the live/dead split and would seed view.json. This is a standalone effort, not a same-PR adjunct to Track A — flagging so it's scoped as its own PR.
Suggested sequencing
- objectui PR (independent, ready now): bump
@objectstack/specpin to ^15 + fix the/uizod-schema value-export erasure (tracked in ObjectFormSchema ↔ @objectstack/spec FormViewSchema alignment (ObjectView form adapter) objectui#2545). - Track A as a cross-repo pair: spec
buttons/defaults+ objectuiObjectFormwiring, so neither side is inert. - Track B as a dedicated liveness-enrollment PR seeded by the 2026-06 audit.
Grounding ADRs: 0078-no-silently-inert-metadata, 0049-no-unenforced-security-properties, 0050-form-layout-vs-presentation, 0089-unify-visibility-predicate-naming. Refs objectstack-ai/objectui#2545, objectstack-ai/objectui#1763.
Activity
feat(spec): structured buttons + defaults config on FormViewSchema (#…
feat(spec,cli): enroll `view` in the liveness ledger (#2998 Track B) (#…
os-zhuang commentedon Jul 16, 2026
Both tracks are merged — closing.
buttons+defaultsonFormViewSchema, landed in the ADR-0078 fallback shape ([EXPERIMENTAL — NOT ENFORCED]markers) pending the objectuiObjectFormwiring tracked in ObjectFormSchema ↔ @objectstack/spec FormViewSchema alignment (ObjectView form adapter) objectui#2545.viewin the liveness ledger (#2998 Track B) #3046 (merged,2973f7f):viewenrolled in the livenessGOVERNEDset with a fully classifiedliveness/view.json(83 props: 76 live / 2 experimental / 5 dead), CLI compile-lint coverage for view containers, and README rollout updates. Seeding re-verified every key against objectui HEAD — four audit-era DEAD findings were superseded by current reads (submitBehavior,sharing.lockedBy, list-pathViewDataproviders, and the ADR-0021list.chartdataset shape, whose renderer migration has since shipped).Residual (tracked elsewhere): the objectui-side
schema.buttons/schema.defaultsread (objectstack-ai/objectui#2545) — flipping those two ledger entriesexperimental → live+ dropping the spec markers rides on that PR; and the level-2 dead residue noted in the ledger (userActions.buttons,addRecord.mode/formView,tabs[].order) sits on the ADR-0049 enforce-or-remove worklist.Generated by Claude Code
feat(form): consume spec-aligned FormView buttons/defaults in ObjectF…
test(spec): the last ui/ file group's test titles state each cited de…