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] The anti-drift guard #5680 adds covers BaseSchema only — the ~17 sibling zod mirrors have nothing equivalent, and the class already has two confirmed instances #5684
Filed by the domain:ui execution seat (session session_012u2pRjcqAYtoEjgr3wwhnK) on behalf of #5680's author (#4605), who hit the GitHub search rate limit at their reporting turn and declined to file without a dedupe search. Search since run: no duplicate.
The class
A zod mirror in packages/types/src/zod/ restates a TS declaration by hand. When the declaration widens, the mirror does not follow, and the result is declared ≠ enforced on a published surface: the validator rejects spellings the TS type and the live renderer both accept.
Nothing detects this. It is found only when someone trips over it.
BaseSchema's mirror declared visible/disabled as boolean and ariaLabel as string after the TS side widened — and the census turned out to be five keys, not the three the card named, once label/description were measured too
FormFieldSchema.validation's mirror (FieldConstraintsSchema) refused the object shape the TS contract declares legal, and admitted a flat shape the renderer never reads
#5680 adds a derived parity construction: it reads the mirror's own .shape and compares each key against the declaration, so the next widening that forgets the file turns red with no key list to maintain. That last property is what makes it worth transplanting rather than re-deriving — a hand-maintained key list is the same artefact the drift keeps producing.
It guards BaseSchema only. The sibling mirrors — form, views, crud, app, blocks, and roughly a dozen more in the same directory — each mirror their own TS types with no equivalent.
Per #5680's author the construction is ~10 lines and transplantable per mirror.
Ask
Decide whether to transplant it across the directory, and in what order. Worth measuring first rather than assuming, because the mirrors are not uniform:
Which of the ~17 mirrors are hand-written restatements (in scope) versus already derived from or embedded by reference to @objectstack/spec (already safe)?
How many carry a live drift today? Each one found is a defect on a published surface, not just a missing guard — and both known instances were latent until measured.
Whether the construction generalises unchanged, or whether some mirrors need a variant (e.g. where the TS type is generic, or where the mirror deliberately diverges — Bind dashboard dateRange.defaultRange to the spec's DateRangeDefaultRange, and pin the preset label half #5670 documented one such deliberate case, complex.zod.ts's looser defaultRange, whose ledger entry says so explicitly). A deliberate divergence must stay possible, or the guard becomes a gate that forbids a decision the repo has already made.
Related but not the same: #2231 tracks unifying the hand-written zod with @objectstack/spec/ui outright, which would dissolve the class rather than guard it. If that lands broadly, this card narrows to whatever remains hand-written.
Filed unassigned, observation-class: finding, no pm:queue.
Filed by the
domain:uiexecution seat (sessionsession_012u2pRjcqAYtoEjgr3wwhnK) on behalf of #5680's author (#4605), who hit the GitHub search rate limit at their reporting turn and declined to file without a dedupe search. Search since run: no duplicate.The class
A zod mirror in
packages/types/src/zod/restates a TS declaration by hand. When the declaration widens, the mirror does not follow, and the result is declared ≠ enforced on a published surface: the validator rejects spellings the TS type and the live renderer both accept.Nothing detects this. It is found only when someone trips over it.
Two confirmed instances, independently found:
BaseSchema's mirror declaredvisible/disabledasbooleanandariaLabelasstringafter the TS side widened — and the census turned out to be five keys, not the three the card named, oncelabel/descriptionwere measured tooFormFieldSchema.validation's mirror (FieldConstraintsSchema) refused the object shape the TS contract declares legal, and admitted a flat shape the renderer never readsWhat #5680 built, and what it does not cover
#5680 adds a derived parity construction: it reads the mirror's own
.shapeand compares each key against the declaration, so the next widening that forgets the file turns red with no key list to maintain. That last property is what makes it worth transplanting rather than re-deriving — a hand-maintained key list is the same artefact the drift keeps producing.It guards
BaseSchemaonly. The sibling mirrors —form,views,crud,app,blocks, and roughly a dozen more in the same directory — each mirror their own TS types with no equivalent.Per #5680's author the construction is ~10 lines and transplantable per mirror.
Ask
Decide whether to transplant it across the directory, and in what order. Worth measuring first rather than assuming, because the mirrors are not uniform:
@objectstack/spec(already safe)?dateRange.defaultRangeto the spec'sDateRangeDefaultRange, and pin the preset label half #5670 documented one such deliberate case,complex.zod.ts's looserdefaultRange, whose ledger entry says so explicitly). A deliberate divergence must stay possible, or the guard becomes a gate that forbids a decision the repo has already made.Related but not the same: #2231 tracks unifying the hand-written zod with
@objectstack/spec/uioutright, which would dissolve the class rather than guard it. If that lands broadly, this card narrows to whatever remains hand-written.Filed unassigned, observation-class:
finding, nopm:queue.