Skip to content

[finding] The SAME drift class one level up: FormViewSpec and FormSectionSpec are each hand-declared twice (console + app-shell), and #5542 only converged the leaf #5596

Description

@os-sales

Found while implementing #5542. Filed unassigned as an observation, for PM triage — the
card's declared surface was FormFieldSpec only, and this is deliberately not widened
into it.

Mechanism

#5040 and #5542 are about one contract having several independent hand-written
descriptions. #5542 converged the leaf of that contract — the field spec. The two
containers above it have exactly the same shape of duplication, and nobody has
counted them.

Both files describe the same artifact, @objectstack/spec's FormView, and both say so:

sitedeclarationhow it says it is the spec's FormView
packages/app-shell/src/views/metadata-admin/SchemaForm.tsx:629FormViewSpecdocstring: "Lightweight shape of the spec FormView we consume"
apps/console/src/components/FormPage.tsx:89FormViewSpecheader comment chain, and the identical type union
packages/app-shell/src/views/metadata-admin/SchemaForm.tsx:660FormSectionSpecelement type is FormFieldSpec
apps/console/src/components/FormPage.tsx:271FormSectionSpecelement type is FormFieldSpec

Line numbers verified on the merged ref cad512fe1, before #5542's branch.

The tell that these are one contract and not two coincidences: both FormViewSpec
declarations spell a character-identical six-member union, in the same order —

type?: 'simple' | 'tabbed' | 'wizard' | 'split' | 'drawer' | 'modal';

— and both name the element type of FormSectionSpec.fieldsFormFieldSpec. That is
the same evidence #5542 used to rule the field spec one contract rather than two layers.

They have already drifted, which is the point

Unlike the field spec — where the console's copy was a clean 9-of-26 subset with no
disagreements — these two differ in both directions, so neither is a subset of the
other:

  • FormSectionSpec: app-shell has description, visibleWhen, visibleOn; console has
    none of those. Console's columns admits the string arm
    (1 | 2 | 3 | 4 | '1' | '2' | '3' | '4'), app-shell's admits numbers only. Same key,
    two different answers about what an author may write.
  • FormViewSpec: console has label, groups, sharing, submitBehavior; app-shell
    has none of those and stops at type plus sections.

So this is not a hypothetical future drift, the way the field spec's was at filing time
— it is drift that has already happened and that nothing can see, because each
description is only ever checked against itself. That is #5040's mechanism, unresolved,
one level up from where #5542 closed it.

Why file it rather than fold it into #5542

#5542's scope fence was explicit and correct: the field spec, its pin, and one
changeset, with the converged form-spec.ts a read-only reuse target. Converging the
containers is a materially bigger call than converging the leaf, because the two
containers genuinely serve different surfaces — sharing and submitBehavior are
public-form concerns the metadata-admin designer has no use for, and columns' string
arm is a tolerance for hand-authored layouts. So unlike the field spec, "same contract,
import it" is not obviously the answer here; "narrower layer, rename and pin the
subset" may well be, and for FormViewSpec the honest answer might be a third thing —
that both should read the spec's own FormViewSchema type rather than either hand copy.

That is a triage decision with an architecture behind it, not a mechanical convergence,
which is why this is a finding and not a queued card.

Severity note

No user-reachable failure. Recording it because a reader of #5040 and #5542 would
reasonably conclude the class is now closed for this contract, and it is not — the leaf
is pinned, the containers are not. Severity is triage's call.

Boundary

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions