feat(spec): object-grid declares description, emptyState (the list view's own shape) and keyboardNavigation - #20882
Conversation
…Navigation The list view's inline empty-state shape is extracted into the exported EmptyStateSchema and taken by reference on both doors; its action/button guidance is reworded to read true on the object-grid door as well. Claude-Session: https://claude.ai/code/session_018fxqvRJW12TaHC7DUQ89Y6 Co-authored-by: Claude <noreply@anthropic.com>
…igation The three members parse byte-identical, the row still refuses an unknown key and a wrong value in each member (code and path), and both the grid's and the list view's emptyState hold EmptyStateSchema itself. Claude-Session: https://claude.ai/code/session_018fxqvRJW12TaHC7DUQ89Y6 Co-authored-by: Claude <noreply@anthropic.com>
The reference docs gate refuses a documented schema without its type alias. The schema has no default or transform, so it is pinned isomorphic (ADR-0122) rather than given an EmptyStateParsed synonym. Claude-Session: https://claude.ai/code/session_018fxqvRJW12TaHC7DUQ89Y6 Co-authored-by: Claude <noreply@anthropic.com>
Regenerated by the build and `check:generated --fix`: authorable-surface, json-schema manifest, api-surface, export-origins, declaration-map and the reference docs. Claude-Session: https://claude.ai/code/session_018fxqvRJW12TaHC7DUQ89Y6 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fxqvRJW12TaHC7DUQ89Y6 Co-authored-by: Claude <noreply@anthropic.com>
…ject-grid-desc-emptystate-keynav
authorable-surface/ui.json and the reference docs were merged by the os-regen driver without a text merge; regenerated from the merged tree so both sides' entries are present (PageHeaderProps.breadcrumb [RETIRED] from main, the object-grid and EmptyState rows from this branch). Claude-Session: https://claude.ai/code/session_018fxqvRJW12TaHC7DUQ89Y6 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check5 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 137 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 f94385ab56cd01d1f71ff7a9f34bc38ea5ed9a4e && git checkout f94385ab56cd01d1f71ff7a9f34bc38ea5ed9a4e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9905e61ca2fddb43d266c23cdb12ced5019c1a74 98dad84e210101e764efd40d32446187ffed7c92 && git checkout -B drift-repro 9905e61ca2fddb43d266c23cdb12ced5019c1a74 && git merge --no-ff 98dad84e210101e764efd40d32446187ffed7c92
node scripts/docs-audit/affected-docs.mjs --json 9905e61ca2fddb43d266c23cdb12ced5019c1a74 |
Contract reviewServed-tier: Inputs, and nothing else: card #20694 (body and all 7 comments: triage ① Derived judgmentsEvery accept-set and public-surface change the diff implies, each named right or wrong.
No governed-surface path in the file list; head repo equals base repo; 387 changed lines. ② Semver level
Clause-②: yes (widening) — ③ Boundary flagsEvery dev flag and every
Nothing escalated. Implemented-by: VERDICT: PASS |
Fixes #20694
Clause-②: yes
ComponentPropsMap['object-grid'](ObjectGridPropsSchema) gains the three members named in triage's direction (5897622232). This is a widening. Nothing that parsed before is refused, and the row still refuses every key it does not declare.What changes
description:I18nLabelSchema.optional(). At the.objectui-shapindb11afd4967c,plugin-grid/src/ObjectGrid.tsx:5677resolves it withresolveInlineI18nLabel(schema.description, displayLocale)and draws it above the rows at:5784,:6303and:6342. The card calls the functionresolveI18nLabel; at the pin it is namedresolveInlineI18nLabel.emptyState: the list view's own empty-state shape, taken by reference. It used to be an inlinestrictObjectatListViewShapeSchema.emptyState(view.zod.ts). It is now the exportedEmptyStateSchema(author typeEmptyState), andlist-viewandobject-gridboth hold that one schema. At the pin it is read atObjectGrid.tsx:6225and drawn throughDataEmptyStateat:6233.keyboardNavigation:z.boolean().optional(). Its describe is the one triage specified: arrow-key cell navigation on the WAI-ARIA grid pattern, on by default wheneditableis set, and a read-only grid keeps its Tab behaviour unless this istrue. It is prefixed[EXPERIMENTAL — not enforced]because nothing reads it at the pin. Under objectuipackages/andapps/, excluding tests, CHANGELOGs, READMEs andpackages/types(which holds its type and zod twin), it has 0 hits; the controlschema.editablehas 3 hits inObjectGrid.tsx.The extraction, and the one text change
emptyStatevalues on each of the four list-view doors:ListViewSchema,ObjectListViewSchema,ViewSchema.listand the flattened overlay arm ofViewMetadataSchema. That is 112 cells, compared between BASE72f8c38201and head.successand parseddataare identical in 112 of 112 cells.messagefield stripped at every depth).action,buttonand mixed inputs on each of the four doors. In those cells theaction/buttonguidance is reworded so that it also reads true onobject-grid, which has noaddRecordblock. It still points ataddRecord(view-strictness-batch18.test.tsstill passes), and it follows theVIEW_ROW_BOUND_GUIDANCEprecedent for text that two doors share.Liveness record for
keyboardNavigationThe liveness ledger has no row for any
ComponentPropsMapkey. The page ledger leavespage/regionsundrilled (it is listed inundrilled-containers.baseline.json), andPageComponent.propertiesis an open record, so the walk never reachesObjectGridPropsSchema; a row inpage.jsonwould be an orphan. The record is therefore the describe marker[EXPERIMENTAL — not enforced], which is the ledger's own spelling for a declared key that is not enforced (MARKER_REincheck-liveness.mts). The member docblock says to drop the marker in the change that lands the reader.check:livenessis green.Files outside the claimed surface
packages/spec/src/type-alias-convention.pin.test.ts:gen:docsrefuses to document a schema that has no type alias, soEmptyStateis exported. ADR-0122 then requires an isomorphism pin (Iso_ui_view__EmptyStateSchema), which moves the pin count from 779 to 780. The schema's tree has no default and no transform, so a synonymEmptyStateParsedwould be wrong.Evidence (head
98dad84e21, which includes a merge oforigin/mainat07356a6ab0throughos-regen-merge.sh)object-gridcorpus: 3 of 36 cases parsed at BASE and 14 of 36 parse at head. The newly accepted cases are exactly the valid values of the three members, and no case is newly refused. Bothexamples/app-showcaseobject-gridnodes (command-centercc_queue_gand the my-work queue) parse before and after.component-object-grid-declared-keys.pin.test.tspasses 18 of 18. §1 checks that the three members parse byte-identically. §2 checks that an unknown top-level key is refused withunrecognized_keysat the row, and that a wrong value in each member is refused (code and path). §3 checks that the grid and the list view holdEmptyStateSchemaby identity, and that the grid answers exactly what that schema answers.scripts/ablation-replace.mjs. Each restore was proven: the blob equals HEAD andgit diff HEADis empty.keyboardNavigationmember: 4 tests fail and 14 pass.EmptyStateSchemawith a copiedz.strictObject: only the identity pin fails, and 17 pass.pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2: 581 files, 17154 passed, 1 todo.pnpm --filter @objectstack/spec typecheck: exit 0. This includescheck:test-typecheck, and the debt ledger is unchanged.@objectstack/lint. Its closure was built upstream withturbo run build --filter='@objectstack/lint...'(lint plus its dependencies).typecheck: exit 0.validateComponentPropson anobject-gridnode:emptyStatproducescomponent-props-unknown-keyatproperties.emptyStat.emptyState.headingproduces the same rule atproperties.emptyState.heading. That path exists only through the new declaration, which shows the rule read the rebuilt dist.pnpm --filter @objectstack/spec check:generated: all 15 artifacts are up to date.dispatch-gates.mjs --commands, 108 families, run at head): 104 exited 0. Four exited 3 with PREREQUISITE NOT MET, so they are NOT MEASURED:check:skill-examples: the client SDK closure is not built.check:dual-build-cjs-loads,check:lean-entry-closureandcheck:type-check-debt: each needs the whole-repo package build that CI does.--ran: all 108 accounted for, 104 run, 4 NOT MEASURED.Acceptance notes
emptyState.titleor.message. At the pin,ObjectGridpasses both toDataEmptyStateunresolved (:6241-6242), unlikedescription. React throws on an object child, measured on react 19.2.8: "Objects are not valid as a React child".ListViewguards withtypeof === 'string'(plugin-list/src/ListView.tsx:5112and:5114), so a map there silently falls back to the default text. No producer inexamples/**orplatform-objectswrites a map there; all of them use strings. So this is not filed here. Carrier: the objectui follow-up the seat files at ACCEPT, which lists the three keys inGRID_QUERY_INPUTS. Its fix is one resolver,resolveInlineI18nLabel, applied at both renderers. Meanwhile the changeset tells authors to write strings onobject-grid.origin/mainmoved to9905e61ca2after the merge; fix(metadata-protocol,spec)!: a saved view stores the parsed value of every key its body carried, and a ViewItem record's top-level options is refused by name (stage iv of #20051) #20868 editsview.zod.tsandview.mdx. A merge-tree probe from a bare clone with no merge driver shows a clean merge, and the hunks in both files are disjoint from this branch's. CI validates the merge ref.object-gridfollow-up in objectui, per triage: after the release is installable, listdescription,emptyStateandkeyboardNavigationinGRID_QUERY_INPUTS(the reverse direction of the parity gate).Generated by Claude Code