Uh oh!
There was an error while loading. Please reload this page.
feat(spec): narrow the per-option default key out of the form-view options vocabulary — the object-field face keeps it enforced - #13033
Conversation
…cabulary FormSelectOptionSchema (Omit-derivation of SelectOptionSchema minus 'default') replaces SelectOptionSchema at the FormFieldBaseSchema.options reference site; tombstone guidance for 'default' and the alias spellings isDefault/selected; protocol-18 conversion form-view-option-default-removed strips stored sources; pin tests both directions; changeset with ADR-0087 registration; regenerated artifacts (authorable-surface, json-schema.manifest, api-surface, export-origins, docs references, strictness-ledger counts).
📓 Docs Drift CheckThis PR changes 1 package(s): 26 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 5 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 657b6de263a286f1c9f8b056cbef67d87a632c25 && git checkout 657b6de263a286f1c9f8b056cbef67d87a632c25
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 0da7cd2c1114649755b7f9f45a7dbbe34985ac6e a258d1a7766cce41f93c001ffe0c0a0fea77cc4b && git checkout -B drift-repro 0da7cd2c1114649755b7f9f45a7dbbe34985ac6e && git merge --no-ff a258d1a7766cce41f93c001ffe0c0a0fea77cc4b
node scripts/docs-audit/affected-docs.mjs --json 0da7cd2c1114649755b7f9f45a7dbbe34985ac6e
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#12868
Executes the maintainer's 2026-08-28 ruling (disposition 甲, carried verbatim on the card; analysis anchor on the objectui side): the per-option
defaultkey is narrowed OUT of the form-view options usage ONLY. The enforced object-field face —applyFieldDefaults, its precedence pin, and the alias rows — is untouched.Hard precondition — census FIRST (part of the ruling)
Run at base
0da7cd2c1(current origin/main at branch time), BEFORE any narrowing was written:options: [...]array element checked for the keysdefault/isDefault/selected, with ancestor property chains for face classification — plus a structural scan over 412 parsed JSON files and a bracket-span scan over 1266 md/mdx files.fields.NAME.select().options), platform-objects/driver-sql/objectql/lint/spec test fixtures, including oneisDefaultalias hit in the objectql engine-select-option-default suite. The docs scan hit 32 object-fieldField.selectsamples. This is exactly the enforced-usage population the card requires the control to hit.*.form.tscorpus (all underpackages/spec/src), zero in the example apps' form views, zero inline FormView literals anywhere in the tree, zero in JSON, zero in docs.options:as an object). The select-option-shaped remainder was audited by hand: two spread sites (BUILTIN_MEMBERSHIP_ROLE_OPTIONSin sys-invitation / sys-member) both sit on the object-field face; none is form-view.Census = ZERO, so the narrowing proceeds per the ruling.
The narrowing
FormSelectOptionSchema(packages/spec/src/ui/view.zod.ts): an Omit-derivation at the shape level —SelectOptionSchema.shapeminus exactlydefault, property schemas reused by reference so the two faces cannot drift key-by-key. Built throughstrictObjectwith its own surface metadata:guidancetombstones fordefault(full prescription: delete the key; declare the pre-selected choice on the OBJECT definition — field-leveldefaultValue, ordefault: trueon that field's own options entry, with the enforced precedence stated; houseos migrate meta --from 17sentence) and for the alias spellingsisDefault/selected(guidance instead of a rename — an alias must name a key the shape accepts).default; the editability boundary guidance set rides along unchanged.FormFieldBaseSchema.optionsnow takes the narrowed schema. This is the single site where the FormView vocabulary consumedSelectOptionSchema(verified by grep overpackages/spec/src/ui); it is NOT indata/field.zod.ts, so the fence never fired.form-view-option-default-removed(D2,retiredFromLoadPath) strips the key from stored sources throughform/ namedformViews, sections/groups/top-level fields, recursing into nestedfields; fixture pins three notices across all three containers. D3: wired into the protocol-18 step's conversionIds + rationale. Changeset carries the FROM-TO mapping and the registration marker; shipped asminorper the launch-window convention for breaking changes (the sibling accept-set narrowings' precedent).defaultand with the pointer at the object definition; the object-field reference is unchanged.Fences honoured (verified, not assumed)
git diff BASE..HEAD -- packages/spec/src/data/is EMPTY —SelectOptionSchema(data/field.zod.ts) not edited, alias rows intact, and the on-hold Restart-touch trigger file untouched.applyFieldDefaultsand the precedence pin: no diff outsidepackages/spec+ generated artifacts + changeset (12-file diff, listed in the checks below).FieldSchemaselect field still acceptdefault: true).Evidence (exits captured pre-pipe; gate verdict lines quoted from logs)
All measurements at commit
a258d1a77(the branch head; the tree has not moved since — every run below cites this sha):pnpm --filter @objectstack/spec build—check-dts-emitted: 34/34 declared declaration file(s) present, verify-lockVERDICT command-exit 0.pnpm --filter @objectstack/spec typecheck— tsc + scripts +check:test-typecheck: OK(test layer compiles; debt ledger unchanged),VERDICT command-exit 0. The new test file is inside the compiled test layer (per-file debt gate would have named it otherwise).pnpm --filter @objectstack/spec test—Test Files 441 passed (441),Tests 11691 passed (11691),VERDICT command-exit 0. Includes the new pin file (14 tests) and the conversion fixture replay (whole-table replay, notice attribution, and the retired-from-load-path zero-notice contract).z.array(SelectOptionSchema)— prediction: the two DOOR pins (refusal through FormFieldSchema/FormViewSchema; nested-fields recursion) flip RED while the direct-schema and derivation pins stay green. Measured: mutation proven on disk (anchor counts 1 to 0 / 0 to 1, diff stat), run =2 failed | 12 passed— exactly the predicted split; restore proven byte-identical to the HEAD blob (git hash-object=HEAD:blob,ec68b449), post-restore re-run14 passed (14). No rebuild leg needed: spec's own tests importsrcrelatively, so no dist resolution is in the loop.@objectstack/metadata-protocoltests144 passed | 2 skipped,@objectstack/linttests81 passed (81), dogfoodexpression-conformance3 passed(the retirement-radius suite). No in-repo reader of a form-view optiondefaultexists (grep over all non-spec packages).node scripts/pm/dispatch-gates.mjs(no paths passed — the script took the change-set from merge-base itself, stderr header:gate list derived from the tree of 'objectstack-ai/objectstack' at commit a258d1a77). 35 derived rows / 35 unique commands — ALL PASS: the spec artifact family (check:generatedfully green incl.check:authorable-surface,check:api-surface,check:docs,check:strictness-ledger,check:spec-changes,check:upgrade-guide,check:migration-registry), the spec source audits (check:liveness,check:empty-state,check:variant-docs,check:skill-examplesafter building client-react — its first FAIL was PREREQUISITE-NOT-MET, not a red —check:exported-any,check:dual-source-exports), and the 24 repo-root gates incl.check:doc-authoring,check:adr-0087-registration,check:merge-driver,check:nul-bytes. Exits were captured before any pipe in every loop.ui/FormSelectOptionrows (shape minusdefault);data/SelectOption:defaultstays (object face); api-surface/export-origins gain the three new exports; strictness-ledger counts +1 strict site inui/; json-schema.manifest gains the def. No RETIRED_KEYS_BY_MAJOR entry: this route is a strict-shape narrowing via a new derived def — no tombstone key enters any walked shape, so gates (b)/(c) demand nothing and an entry naming the still-livedata/SelectOption:defaultwould itself be refused..defaultread in pinned objectui (190fbd01d, metadata-viewer state-machine panel) is typed against that file's own local interface and reads an OBJECT definition's field options — the untouched face. The ui-side derived type is already tombstoned there, so the pin gate has nothing to break.Downstream and process notes
needs:contract-reviewre-attaches to the card (it was deliberately removed while no diff existed). Left to the review chain together with enqueue — this PR stays draft.Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4
Generated by Claude Code
Generated by Claude Code