Repository navigation
feat(spec)!: addRecord says what renders — enabled required, position top, mode/formView retired - #24970
Conversation
… top, mode/formView retired (WIP) Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz Co-authored-by: Claude <noreply@anthropic.com>
…drecord-retire-mode-formview
📓 Docs Drift CheckThis PR changes 3 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 144 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 1fe2d6a94e12ed4f6b9c54c1de3080cfa8983af1 && git checkout 1fe2d6a94e12ed4f6b9c54c1de3080cfa8983af1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6440cf16334e1c8106dfdb31d87b6ca728d176c2 fc643e459f6327a3840106256a3eb3a6f575a800 && git checkout -B drift-repro 6440cf16334e1c8106dfdb31d87b6ca728d176c2 && git merge --no-ff fc643e459f6327a3840106256a3eb3a6f575a800
node scripts/docs-audit/affected-docs.mjs --json 6440cf16334e1c8106dfdb31d87b6ca728d176c2
|
…drecord-retire-mode-formview
…cord composite's four new rows Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Read against: card #24692 (body; ruling 6281834942 letter B′ items 1–6; seat-2 hold 6282077441; the 甲 note 6292027850; dispatch 6296388083; dev reports 6299365608 and 6303362402), the standing ruling #6015 comment 6292005854 (「甲」), the PR body, its 37-file list and the three-dot diff ① Derived judgmentsEvery accept-set and public-surface change, named against the ruling:
The
Also read, nothing wrong found: the ② Semver level
③ Boundary flagsGate verdicts on this head (the check-runs ARE the verdicts). 42 check-runs on Dev flag 1 — Dev flag 2 / the landing hazard — objectui
Out-of-scope / remaining items from the dev report, each answered or escalated:
Implemented-by: VERDICT: PASS |
…two spec-alignment tests (objectstack-ai#13312) Fixes objectstack-ai#13308 Clause-②: no ## What objectstack-ai/objectstack#24970 retired `addRecord.mode` and `addRecord.formView` (`retiredKey()` tombstones). Two `@object-ui/types` tests still authored both keys on typed values, which is what turns objectstack's hourly Spec Shape Gate red (four `TS2322`s). Test-only change: - `packages/types/src/__tests__/p2-spec-exports.test.ts`: the `AddRecordConfig` value keeps `enabled` and `position` only; the test name "should validate a full AddRecordConfig" now says what it checks ("with enabled and position"). - `packages/types/src/__tests__/p1-spec-alignment.test.ts`: the `ListViewSchema.addRecord` value keeps `enabled` and `position`; the `schema.addRecord?.mode` assertion is dropped. - `.changeset/13308-add-record-retired-keys.md`: empty frontmatter (releases nothing). `check-changeset-presence` asked for it, because files under `packages/types/src` count as that package's source. No source file changes. `.objectstack-sha` does not move. Outside tests, nothing in the tree authors or reads `addRecord.mode` / `addRecord.formView` (`git grep`). The only other mentions are prose: CHANGELOGs, `plugin-list` registration description text, a parity-ledger note string. ## Evidence (head `562f56cc00`) - At the pin (`06cab48`): `pnpm --filter @object-ui/types run type-check` exited 0. - Against a spec built from objectstack `main` `28c9a2cb66` (that main already contains `fde7456352`, `merge-base --is-ancestor` exit 0): I built and packed `@objectstack/spec` in a separate objectstack worktree, then put it into this worktree's install with `scripts/ci/spec-shape-gate.mjs inject`, the gate's own wiring (32 consumers re-pointed). `type-check` exited 0. - Control leg on the same injected install: I restored the two test files from `2794254099` (pre-fix main), and `tsc -p tsconfig.test.json` exited 2 with exactly the gate's four diagnostics, `p1-spec-alignment.test.ts(184,9)`, `(185,9)` and `p2-spec-exports.test.ts(143,9)`, `(144,9)`: `TS2322 Type 'string' is not assignable to type '{ '[REMOVED] Key retired: ...': never; }'`. The trap then restored both files with `git checkout HEAD --`. `git diff HEAD` came back empty, and both blob hashes match HEAD. - vitest (`--maxWorkers=1`): the two edited files passed 59/59. The three tests that name them (`page-node-type-contract`, `phase2-schemas`, `related-list-filter-rule-array-10199`) passed 51/51. I ran no whole-directory test locally; `packages/types` in full is declared to CI. - eslint `--no-inline-config` on the 2 changed `.ts` files gave 0 errors and 0 warnings (2 files per `--format json`). The repo config has no type-aware linting, so this diff cannot change any untouched file's result. - Pre-tsc battery: all exited 0. That covers phantom-deps, self-import, unreferenced-sources, doc-example-readers, handler-key-reads, side-effects-array, element-data-source-declaration, spec-symbols, action-forward-parity, designer-field-key-parity, icon-record-names, i18n-keys, i18n-drift, i18n-designer-parity, control-bytes, check-hand-rolled-comment-mask, check-changeset-fixed, changeset-presence, changeset-claims, changeset-no-major and pending-changeset-literals. - `check:spec-floors` exited 1 with `no-artifact` because the workspace was not built. That is NOT MEASURED, not a red. ## Acceptance notes - objectstack-ai/objectstack#25055's registry entry (objectstack PR #25091, open, not merged when this was written) lists these four diagnostics as `known`. Once this lands, that entry matches nothing and is deleted when objectstack-ai#13308 closes. --- _Generated by [Claude Code](https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Closes #24692
Clause-②: yes (narrowing:
mode/formViewretire through ADR-0087 drop conversions;enabledbecomes required)A list's
addRecordblock now says what the renderer draws.enabledis required with no default,positiondefaults totop, andmode/formVieware retired through thespec-property-retirementkit. It ships in 18.x under the standing ruling for breaking narrowings after GA (#6015, comment 6292005854, 「甲」), because ADR-0087 conversions cover every body that parsed under 18.0.The ruling this implements (comment 6281834942, letter B′, items 1–5 verbatim)
What changed
Schema (
packages/spec/src/ui/view.zod.ts,AddRecordConfigSchema). One def serves every door:ListViewSchema,ObjectListViewSchema(an object's or a container'slistViews),InterfacePageConfigSchema(a list page'sinterfaceConfig) and the pagelist-viewnode's props row.enabledisz.boolean()with no default. If the key is omitted, the parse refuses with a prescription that names the key and says how to fix it.positiondefaults totop. The valuestop/bottom/bothare unchanged. I checked the renderer at objectui origin/main and at the pinned sha616df6598b:ListView.tsx#resolveAddRecordPlacementmaps an absent position to top, andshowAddRecordneedsenabled === true.modeandformViewareretiredKey()tombstones. Authoring either key is atscerror and also a parse error that carries the prescription.ADR-0087. There are two D2 conversions, both retired from the load path with
retiredAfter: '18.0.0'.stack.views[]in all three persisted spellings,objects[].listViews.*,pages[].interfaceConfigand every pagelist-viewnode.add-record-mode-form-view-removedstrips both keys and emits one notice per key.add-record-enabled-defaultedwritesenabled: trueonto a block that omitsenabled. An explicit value is never touched.RETIRED_KEYS_BY_MAJOR[18]gainsui/AddRecordConfig:modeandui/AddRecordConfig:formView.add-record-mode-form-view-retiredandadd-record-enabled-required. Each has its own step-18 rationale fragment.DEFAULT_CHANGES_BY_MAJOR[18].Ruling item 3 under 「甲」: the
enabledhalf ships with a conversion, not as a warning. I measured whether any producer writesaddRecordwithoutenabled:71696121a6: the showcase's two blocks and the lint fixture all writeenabled: true;addRecord: { enabled: true };The measured population is zero, so the conversion is sound. It is lossless against the contract, because it writes the value the 18.0 parse already produced. It is not lossless in pixels, and the changeset says so: a stored block that omitted
enableddrew no button, and once converted it draws the one it declared. No 19.0 refusal card is needed.Forms. In
view.form.tsandpage.form.ts, theaddRecordcomposites now listenabledandpositionexplicitly. The new nested labels are translated in the three locales.Liveness ledger. The
liveness/view.jsonaddRecordrow records the removal. The sub-keys are one drill level below a recorded undrilled container, so no row is added or removed. The README residue line is updated.Producers and docs.
contact.view.tsandactive-projects.page.ts, and the lint fixtureshowcase-shape.fixtures.ts, dropmode/formView.content/docs/ui/create-vs-edit-form.mdxstates the convention (formViews.createwhen declared, elseform/formViews.default) and names no binding.content/docs/ui/views.mdxdropsaddRecord.formViewfrom where a named form is served and states thecreateconvention.@objectstack/lint:os explain list-view-field-unknownand one docblock no longer nameaddRecord.formView.lintViewRefsnever read it.content/docs/references/are regenerated.Tests.
packages/spec/src/ui/add-record-config-retirement.test.ts, in the repo project. It covers every door, the prescriptions, thetscchannel, the conversions over stored view, page and object rows, idempotence, load-path retirement, the ledger rows, the forms, and a tree-scoped absence pin over the five declared roots.retired-key-removal-version.pin.test.tsjoins both new conversions.view.test.ts/page.test.tspins onmodeand thebottomdefault are rewritten.Not in this PR
p1-spec-alignment.test.ts:189parity row (modedefaultinline) will go red when objectui's pin takes this spec release, and ruling item 4 says that row changes then. No objectui source writesmodeorformViewat the pin (git grepat616df6598b).content/docs/releases/is untouched.Acceptance notes
active-projects.page.tssetsuserActions.addRecordForm: falsenext toaddRecord: { enabled: true }. objectui'sListViewwithholds the Add button onaddRecordForm === false, so that page probably shows no Add button, even though its comment says it shows one. I only read this from the code and did not reproduce it in a browser, so it is not filed. Carrier: none.position: 'bottom'into built artifacts that carried anaddRecordblock. Those artifacts keepbottomexplicitly, so nothing moves on screen for them.Verification (final head
a05f141642, after merging origin/main)pnpm --filter @objectstack/spec buildandcheck:generated:✓ All 14 generated artifacts are up to date.pnpm --filter @objectstack/spec test:Test Files 703 passed (703),Tests 21369 passed | 1 todo.pnpm --filter @objectstack/spec test:repo:72 passed (72),1236 passed.pnpm --filter @objectstack/spec typecheck: exit 0.pnpm --filter @objectstack/lint typecheck: exit 0.pnpm --filter @objectstack/lint test:151 passed (151),7013 passed.&&chain under the verify lock, which reportedVERDICT command-exit 0.objectstack validateprinted✓ Validation passed, andtsc --noEmitexited 0.check-adr-0087-registration: registered, 4 ids new here.check:i18n: in sync.check:i18n-coverage: none new.check:cross-package-test-inputs,check:nul-bytesandcheck:doc-authoring: green.dispatch-gatesderives were run on the final heada05f141642, and every one exited 0.--ranreconciles:120 run, 0 NOT-MEASURED.formView: 'create'back intocontact.view.ts(ablation-replace: mutation landed, blob changed) turned the pin red,1 failed | 34 passed, namingexamples/app-showcase/src/ui/views/contact.view.ts. The restore was verified: the blob equals HEAD andgit diff HEADis empty.pnpm lint(repo-wide eslint) is NOT MEASURED locally and left to CI.Generated by Claude Code