You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[finding] A THIRD hand-written FormViewSchema mirror survives in packages/react's spec bridge, and it has drifted on three keys #5596 just measured #5652
Found while implementing #5596. Filed unassigned as an observation, for PM triage — #5596's
declared surface was the app-shell / apps/console pair only, and this is deliberately not
widened into it.
Mechanism
#5040 / #5542 / #5596 are one contract described several times, each description only ever
checked against itself. #5542 converged the leaf (FormFieldSpec); #5596 converged the two
containers (FormSectionSpec / FormViewSpec) across packages/app-shell and apps/console. A third hand-written description of the same FormViewSchema contract
survives in a different package and was outside both scopes:
packages/react/src/spec-bridge/bridges/form-view.ts declares its own FormViewSpec
(line 54), FormSection (line 34) and FormField (line 12), and says so in its own docstring:
The subset of @objectstack/spec FormViewSchema the bridge consumes. Every serializable
spec key is either mapped onto the object-form node or listed here with an explicit
reason for being ignored — the bridge must never silently drop spec configuration (#2545).
That is the mirror shape scripts/check-spec-symbol-derivation.mjs exists to catch: a mirror
nothing checks is one spec release from being a fork.
It has already drifted — on the exact keys #5596 measured
This is not a hypothetical. Three of its keys disagree with what the contract accepts, and in
each case #5596 measured the contract's answer while converging the other two copies:
key
bridge declares
the contract admits
FormSection.columns (l.42) and FormViewSpec.columns (l.57)
number
the string arm too — FormSectionSchema.columns unions z.enum(['1','2','3','4']) with the four numeric literals
FormField.dependsOn (l.28)
string[]
string | string[] — the bare-string arm is the one that makes field-selector work (#5040)
string | { dialect?, source } — the object arm is what evalFieldPredicate takes (ADR-0089)
columns is the same numbers-only narrowing #5596 found in app-shell's copy and removed as
"rejecting metadata the platform accepts, not a deliberate narrowing". The bridge still has it.
#5596's fence was the app-shell ↔ console pair, and its ruling was explicitly conditional
on the shape staying derived from spec symbols. This bridge is a different call with its own
architecture behind it, not a mechanical repeat:
The bridge consumes rather than authors: it maps spec keys onto an object-form node. So
"narrower layer, pin the subset" may well be the honest answer — but the three drifted keys
above are not narrowings, they are stale.
That is a triage decision, which is why this is a finding and not a queued card.
Severity note
No user-reachable failure proven from this repo's tests. The reachable consequence is the #5040 shape: a spec-authored form that spells columns: '3', dependsOn: 'objectName', or an
object-form visibleWhen is legal metadata the bridge's type refuses to describe. Severity is
triage's call.
Found while implementing #5596. Filed unassigned as an observation, for PM triage — #5596's
declared surface was the
app-shell/apps/consolepair only, and this is deliberately notwidened into it.
Mechanism
#5040 / #5542 / #5596 are one contract described several times, each description only ever
checked against itself. #5542 converged the leaf (
FormFieldSpec); #5596 converged the twocontainers (
FormSectionSpec/FormViewSpec) acrosspackages/app-shellandapps/console. A third hand-written description of the sameFormViewSchemacontractsurvives in a different package and was outside both scopes:
packages/react/src/spec-bridge/bridges/form-view.tsdeclares its ownFormViewSpec(line 54),
FormSection(line 34) andFormField(line 12), and says so in its own docstring:That is the mirror shape
scripts/check-spec-symbol-derivation.mjsexists to catch: a mirrornothing checks is one spec release from being a fork.
It has already drifted — on the exact keys #5596 measured
This is not a hypothetical. Three of its keys disagree with what the contract accepts, and in
each case #5596 measured the contract's answer while converging the other two copies:
FormSection.columns(l.42) andFormViewSpec.columns(l.57)numberFormSectionSchema.columnsunionsz.enum(['1','2','3','4'])with the four numeric literalsFormField.dependsOn(l.28)string[]string | string[]— the bare-string arm is the one that makesfield-selectorwork (#5040)FormField.visibleWhen(l.30),FormSection.visibleWhen(l.44)stringstring | { dialect?, source }— the object arm is whatevalFieldPredicatetakes (ADR-0089)columnsis the same numbers-only narrowing #5596 found in app-shell's copy and removed as"rejecting metadata the platform accepts, not a deliberate narrowing". The bridge still has it.
Why file it rather than fold it into #5596
#5596's fence was the
app-shell↔consolepair, and its ruling was explicitly conditionalon the shape staying derived from spec symbols. This bridge is a different call with its own
architecture behind it, not a mechanical repeat:
block records deliberate NON-declarations with issue numbers —
defaultSortandariaareomitted on purpose ([finding]
form-view.ts's spec bridge still readsspec.aria, a keyFormViewSchemaretired — a dormant read that can never fire #3901 / [finding]form-view.tsspec bridge also readsspec.defaultSort— the same dormant-read shape as #3901, on the sibling key #3901's scope explicitly excluded #3974), because re-adding either "is the first half of re-addinga read that can never fire". A blanket
Omit<FormView, …>derivation would erase thatretirement ledger, so [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's exact pattern is not obviously right here.
object-formnode. So"narrower layer, pin the subset" may well be the honest answer — but the three drifted keys
above are not narrowings, they are stale.
That is a triage decision, which is why this is a
findingand not a queued card.Severity note
No user-reachable failure proven from this repo's tests. The reachable consequence is the
#5040 shape: a spec-authored form that spells
columns: '3',dependsOn: 'objectName', or anobject-form
visibleWhenis legal metadata the bridge's type refuses to describe. Severity istriage's call.
Boundary
duplicate-declaration phrasings). [finding]
form-view.ts's spec bridge still readsspec.aria, a keyFormViewSchemaretired — a dormant read that can never fire #3901, [finding]form-view.tsspec bridge also readsspec.defaultSort— the same dormant-read shape as #3901, on the sibling key #3901's scope explicitly excluded #3974, ObjectFormSchema ↔ @objectstack/spec FormViewSchema alignment (ObjectView form adapter) #2545, SpecBridge hands ObjectGrid a bareexportOptionsarray, so a spec-authored view's declared formats are silently ignored #4585 all touch this file but areclosed and are about dormant reads and alignment, not about it being a third hand-written
declaration of the container contract. Nothing open covers this.