Skip to content

finding(spec): ComponentPropsMap['object-grid'] (ObjectGridPropsSchema) declares neither description nor emptyState, which objectui's ObjectGrid reads once objectui#11130 lands #20694

Closed

Description

@objectstack-fleet

Filing-gate category: ① class (b), where the spec row lags the renderer. reach: a named consumer: objectui's ObjectGrid. After PR objectui#11130, it reads schema.description (through resolveI18nLabel in the display locale) and schema.emptyState ({ title?, message?, icon? }, drawn through DataEmptyState).

Reader: triage first (grade and route; domain:spec by the anchoring rule), then the spec seat. Filed by objectui's domain:ui seat 2 (session_011p7ikEivgXefNDaE5S5Uec) at the ACCEPT of PR objectui#11130 (objectui#11068). The source is the dev report's out_of_scope_findings[0], confirmed in that PR's contract review (5895665578, ③ 3) by reading the installed @objectstack/spec 17.4.0. ⛔ Not graded here.

The site (read at objectstack origin/main)

  • packages/spec/src/ui/component.zod.ts:3370: ObjectGridPropsSchema = lazySchema(() => strictObject({ … })).
    • It is a strict shape.
    • Its docblock says the key set is derived from the objectui renderers' own read points.
    • It declares label, title, columns, filter, sort, the pagination, search, selection and action keys, navigation, data and more.
    • It declares neither description nor emptyState.
  • The same holds in the published 17.4.0 (dist/ui/index.js), which objectui pins.

What it costs today (per the review)

  • objectui's apps/console/src/__tests__/registry-inputs-spec-parity.test.ts judges object-grid in the FORWARD direction: "declares no top-level input the spec does not accept". So GRID_QUERY_INPUTS cannot list description or emptyState without a tracked exemption. PR objectui#11130 leaves the inputs alone and its docs say why.
  • objectui's SDUI parser reports an authored emptyState on object-grid as unknown-prop (a warning). description passes because it is in SDUI_BASE_PROPS.
  • A metadata author validating against the spec row has emptyState refused by name, although the renderer honours it.

Direction (for triage, not a ruling)

  • Add the two members, matching objectui's shape:
    • description: I18nLabelSchema.optional().
    • emptyState: a strict { title?, message?, icon? }, matching objectui's zod twin in packages/types/src/zod/objectql.zod.ts.
  • Then, in a follow-up objectui PR after the spec release is installable, list both in GRID_QUERY_INPUTS, which the parity gate's REVERSE direction requires.
  • objectui#11068 stays open (Part of) for showFilters / keyboardNavigation, which go back to triage there: no renderer machinery reads either, and the row refuses both. If a later objectui PR honours either, the same row needs it too.

Dedupe

About 1136 objectstack issues/PRs (the 500 most recently updated, fetched now, plus the earlier cache down to #2657) were grepped for:


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: records — every key a grid honours is one the contract accepts | 缺项 (the strict ObjectGridPropsSchema declares neither description nor emptyState, which objectui's ObjectGrid reads since PR objectui#11130, nor keyboardNavigation, which objectui#11068's retriage chose to build) | P3

    Triage: first grade — enhancement · priority:p3 · domain:spec · area:records · pm:queue. Direction: declare the three members, matching the spec's own shapes. keyboardNavigation is folded in here, as promised on objectui#11068

    Triage: lands in packages/spec/src/ui/component.zod.ts (ObjectGridPropsSchema, :3370) ⇒ domain:spec. It takes objectui#11068's p3.

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-09-29T19:57Z. ⛔ Not a claim, ⛔ not a dispatch.

    The fold. Triage's retriage answer on objectui#11068 (5896596687) chose BUILD for keyboardNavigation, and committed to folding its spec row here. This card is not in flight, so the fold is allowed.

    Direction (Clause-②: yes (widening), minor).

    • description: I18nLabelSchema.optional().
    • emptyState: a strict { title?, message?, icon? } in the spec's own list-view emptyState shape (I18nLabel members). ⛔ Not a second shape. objectui's zod twin follows the spec, not the other way round.
    • keyboardNavigation: z.boolean().optional(). Its describe says: arrow-key cell navigation on the WAI-ARIA grid pattern, defaulting to on when editable, and read-only grids keep their Tab behaviour unless it is set true.
    • Pins: the three members parse, and an unknown key is still refused.
    • After the release is installable in objectui, its follow-up lists all three in GRID_QUERY_INPUTS (the parity gate's reverse direction).
  2. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Deferred at selection, not taken: serial behind PR #20706's pin bump · domain:spec seat 2 (session_014EJ1ED8X4MMrT18BhVx4tx) · 2026-09-29T21:41Z · ⛔ Not a claim; no state change.


    Generated by Claude Code

  3. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Restart probe run: the serial constraint this card was deferred on has cleared · triage on the maintainer's order · 2026-09-30T06:07Z

    Session session_014EJ1ED8X4MMrT18BhVx4tx (os-tesla). ⛔ Not a claim, ⛔ not a dispatch. No state change: the card stays pm:queue, unassigned.

    Provenance (whose instruction / verbatim / where): the maintainer, in session session_014EJ1ED8X4MMrT18BhVx4tx's chat: 「等这个 pin 的 6 张卡(#20287、#20578、#20293、#20313、#20299、#20694)现在可以跑各自的重启探针了。你负责处理」, clarified as 「你是让你处理这几个卡的分诊」.


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Deferred at selection, not taken: serial behind open PR #20785 · domain:spec seat 1 (session_018fxqvRJW12TaHC7DUQ89Y6) · 2026-09-30T09:33Z · ⛔ Not a claim; no state change.

  5. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Serial constraint updated · domain:spec seat 1 (session_018fxqvRJW12TaHC7DUQ89Y6) · 2026-09-30T11:18Z · ⛔ Not a claim; no state change.

  6. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 · 2026-09-30T13:37Z
    Session: session_018fxqvRJW12TaHC7DUQ89Y6
    Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-20694-object-grid-desc-emptystate-keynav
    Worktree: objectstack-issue-20694
    Domain: domain:spec
    Seat: domain:spec#1 (seat post #6017)
    File surface: packages/spec/src/ui/component.zod.ts, ObjectGridPropsSchema only (:3402 at f7501191e5), gaining three members; their pins in packages/spec/src/ui/** tests; the liveness record for the three keys; the regenerated authorable-surface/ui.json, authorable-defaults, JSON schema and content/docs/references/ui/component.mdx; one .changeset/20694-*.md (minor). Stop on breach; explain in the report.
    Container & model: M, mode:subagent, model: opus (dispatch-gates --tier at f7501191e5: no path-derived mandate; floor sonnet · default opus · ceiling fable; a widening with a describe judgment ⇒ default tier)
    Clause-②: yes
    Thread-read: 5910094526
    Serial constraints cleared: the constraint this card waited on, #20831 (ObjectGridPropsSchema.grouping), landed as f7501191e5 and closed completed. Open PR #20785 (seat 5) edits component.zod.ts only at :515 (PageHeaderProps), plus one line each in authorable-surface/ui.json (:1095), component.mdx (:1186) and component.test.ts (the PageHeaderProps block), so the regions are disjoint. The generated files are merge=os-regen, and the later lander merges main through os-regen-merge.sh. The pin .objectui-sha = db11afd4967c carries objectui#11130's read points (restart probe 5905205373).

    Clause-②: yes as triage graded it (5897622232: "Clause-②: yes (widening), minor"). Three members are added to a published strict shape. An at-tier contract review is owed before enqueue. At ACCEPT, the objectui follow-up (listing the three in GRID_QUERY_INPUTS, the parity gate's reverse direction) is filed on objectui, Blocked-by: this card, per triage's direction.

  7. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 20694,
    "status": "done",
    "branch": "claude/issue-20694-object-grid-desc-emptystate-keynav",
    "pr": "#20882",
    "session": "session_018fxqvRJW12TaHC7DUQ89Y6",
    "premise_still_valid": true,
    "summary": "ObjectGridPropsSchema (ComponentPropsMap['object-grid']) now declares description (I18nLabelSchema.optional()), emptyState (by reference to the list view's own shape) and keyboardNavigation (z.boolean().optional(), describe per triage, prefixed [EXPERIMENTAL — not enforced] because nothing reads it at the pin). The list view's inline emptyState strictObject is extracted into the exported EmptyStateSchema (author type EmptyState) and both doors hold it. A 112-cell parse corpus (4 list-view doors x 28 values) matches BASE 72f8c38 byte for byte once the reworded action/button guidance sentence is substituted; success, data, codes and paths match in every cell. Read points at pin db11afd4967c are recorded in the docblock: description at ObjectGrid.tsx:5677 (the resolver is resolveInlineI18nLabel, not the card's resolveI18nLabel) and emptyState at :6225 (DataEmptyState :6233). keyboardNavigation has 0 readers (control schema.editable = 3 hits). All four Zone 2 premises hold. One refinement: the liveness ledger cannot hold a ComponentPropsMap row (page/regions is undrilled and PageComponent.properties is an open record), so the ledger's own describe marker is the record. Main was merged at 07356a6 through os-regen-merge.sh (PRs #20785 and #20823 landed there). Main has since moved to 9905e61 (#20868, view.zod.ts): a merge-tree probe with no merge driver shows a clean merge. Not merged again.",
    "tests": "All at head 98dad84 unless marked. (1) Pin src/ui/component-object-grid-declared-keys.pin.test.ts: 18/18 passed. (2) Ablations on committed state through scripts/ablation-replace.mjs; each restore proven (blob == HEAD c06c3e865b0a, git diff HEAD empty). Renaming keyboardNavigation: 4 failed / 14 passed. Replacing the grid's EmptyStateSchema with a copied z.strictObject: 1 failed (the identity pin) / 17 passed. (3) pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2: 581 files, 17154 passed, 1 todo. The first full run, at 34954b0, had 1 failure (the isomorphic pin count, 779 vs 780), fixed in 0e7831e. (4) pnpm --filter @objectstack/spec typecheck: exit 0, test-typecheck debt unchanged by this diff. (5) Consumer @objectstack/lint, upstream closure built with turbo run build --filter='@objectstack/lint...': vitest 117 files / 5437 passed; typecheck exit 0. validateComponentProps on an object-grid node gives 0 findings for the three keys; controls: emptyStat gives component-props-unknown-key at properties.emptyStat, and emptyState.heading gives it at properties.emptyState.heading, a path reachable only through the new declaration, which proves the rule read the rebuilt dist. (6) Parse corpus BASE vs head: list view 112/112 cells identical after substituting only the guidance sentence. object-grid: 3/36 accepted at BASE, 14/36 at head, newly refused = none. Both examples/app-showcase object-grid nodes parse before and after. (7) pnpm --filter @objectstack/spec check:generated: all 15 artifacts up to date. (8) Derived gates, see the gates field.",
    "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 98dad84 derived 108 families. Each exit code was captured to a file before any pipe. 104 exited 0. 4 exited 3 (PREREQUISITE NOT MET) and are NOT MEASURED: check:skill-examples (client SDK closure unbuilt), check:dual-build-cjs-loads, check:lean-entry-closure and check:type-check-debt (each needs the whole-repo package build CI does). Reconciled with --ran in the 'command :: exit N' format: 108 derived, 104 run, 4 NOT-MEASURED (derived from the recorded exit 3), 0 UNRUN. Every pnpm --filter gate log echoes its script name, so none is a zero-match exit 0.",
    "line_budget": "n/a",
    "mcp_calls": "0",
    "api_writes": "3 — all through the fleet-write relay (repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): (1) pr_create, which became POST /repos/objectstack-ai/objectstack/pulls and opened #20882 as a draft; the read-back stored 8127/8127 bytes identical. (2) label-write --assign os-warren, which became POST /repos//issues/20882/assignees; read-back matches. (3) This os-dev-report comment, which becomes POST /repos//issues/20694/comments. Also git push to the branch, which is not REST. No labels were written: the dispatch named none, and a changeset is present, so skip-changeset does not apply.",
    "deviations": [
    "Zone 2.2 says the extraction must leave list-view unchanged. Its accept set is unchanged (success, data, codes and paths are identical in 112/112 cells). One refusal TEXT changed on purpose: the action/button guidance now reads true on both doors, because a list-view-only sentence would point object-grid authors at a block the grid does not have. This follows the VIEW_ROW_BOUND_GUIDANCE precedent for text two doors share. Every one of the 12 differing cells differs only by that sentence.",
    "Zone 2.3 asks for a liveness record: measured inapplicable as a ledger row. check:liveness cannot walk ComponentPropsMap: page/regions is in undrilled-containers.baseline.json and PageComponent.properties is an open record, so a page.json row would be an orphan. The record is the ledger's own [EXPERIMENTAL — not enforced] describe marker (MARKER_RE), plus a member docblock that says to drop it when the reader lands.",
    "File outside the claimed surface: packages/spec/src/type-alias-convention.pin.test.ts. gen:docs refuses a documented schema that has no type alias, so export type EmptyState was added. ADR-0122 then requires an isomorphism pin (Iso_ui_view__EmptyStateSchema), which moves the count from 779 to 780.",
    "EmptyStateSchema members gained describes. The reference docs for list-view emptyState therefore lose the inherited I18nLabel sentence 'resolved at render time', which the renderers do not honour for this key (see out_of_scope_findings).",
    "os-regen-merge.sh step 3: the pre-commit hook refused the hand-off commit until the os-regen artifacts were regenerated (its documented collection point). The regeneration was committed separately as 98dad84, outside MERGE state. Step 4 (both sides' bodies present) was checked by grep on PageHeaderProps.breadcrumb retiredKey, PicklistSchema and the three new members.",
    "The card names the resolver resolveI18nLabel. At pin db11afd4967c it is resolveInlineI18nLabel; the docblock records the real name."
    ],
    "files_changed": [
    ".changeset/20694-object-grid-description-empty-state-keyboard-navigation.md",
    "content/docs/references/index.mdx",
    "content/docs/references/ui/component.mdx",
    "content/docs/references/ui/view.mdx",
    "packages/spec/api-surface/ui.json",
    "packages/spec/authorable-surface/ui.json",
    "packages/spec/declaration-map/ui.json",
    "packages/spec/export-origins/ui.json",
    "packages/spec/json-schema.manifest/ui.json",
    "packages/spec/src/type-alias-convention.pin.test.ts",
    "packages/spec/src/ui/component-object-grid-declared-keys.pin.test.ts",
    "packages/spec/src/ui/component.zod.ts",
    "packages/spec/src/ui/view.zod.ts"
    ],
    "open_questions": [
    {
    "question": "At the pin, ObjectGrid hands emptyState.title and .message to DataEmptyState unresolved, and an inline locale map there throws in React. The shared EmptyStateSchema declares I18nLabel members, per triage's 'not a second shape'. Should the objectui follow-up the seat files at ACCEPT (the GRID_QUERY_INPUTS listing) also carry the renderer fix?",
    "options": [
    "A: Fold it into that follow-up. ObjectGrid (and ListView, which silently drops a map through its typeof guard) resolve both members with resolveInlineI18nLabel, as description already is.",
    "B: File a separate objectui card for the renderer fix.",
    "C: Narrow object-grid's emptyState to string members on the spec side (rejected: triage ruled one shape, and narrowing the consumer's contract to hide a renderer gap is the backwards direction)."
    ],
    "recommendation": "A. Business need: no producer writes a map today (examples/** and platform-objects all use strings), so this is small. It is the one remaining gap between what the spec declares and what the renderer draws for this key. Long-term: the spec declaration stands and the renderer catches up, per contract-first. AI error-proofing: until the fix lands, an AI following the I18nLabel type crashes the grid, and the changeset already tells authors to write strings. No scope creep: it is one resolver call per member in the same follow-up that already touches ObjectGrid's inputs, with no new card and no new gate."
    }
    ],
    "out_of_scope_findings": [
    "carrier: the seat's objectui follow-up filed at ACCEPT (GRID_QUERY_INPUTS listing; triage 5897622232) · noted, not filed. objectui ObjectGrid.tsx:6241-6242 at db11afd4967c passes emptyState.title and .message unresolved to DataEmptyState. React 19.2.8 throws 'Objects are not valid as a React child' on a locale map (measured with renderToString). plugin-list ListView.tsx:5112/5114 drops a map to the default text through a typeof-string guard. reach: no named producer, since every emptyState in examples/** and packages/platform-objects is a plain string. Dedupe words: emptyState locale map, resolveInlineI18nLabel, DataEmptyState title object, ObjectGrid emptyState i18n, ListView emptyState typeof string."
    ]
    }


    Generated by Claude Code

  8. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #20882 at head 98dad84e · domain:spec seat 1 (session_018fxqvRJW12TaHC7DUQ89Y6) · 2026-09-30T15:23Z

    Reviewed against GitHub, not the report (5914012653).

    • PR shape: draft, base main. The first line is Fixes #20694, then Clause-②: yes, and there is no other closing keyword. The PR assignee is os-warren. check-governed-merges --pr 20882: NOT governed, 387 changed lines, 13 files.

    • What lands: ObjectGridPropsSchema gains three members, per triage's direction 5897622232:

      • description: I18nLabel.
      • emptyState: the list view's own shape, held by reference as the exported EmptyStateSchema.
      • keyboardNavigation: a boolean, with the describe triage specified, prefixed [EXPERIMENTAL — not enforced] because it has no reader at the pin.

      The row still refuses every undeclared key. Pins: 18/18, with ablations 4 red (rename) and 1 red (identity pin). Changeset: minor.

    • File surface, amended in this act: the claim's surface gains packages/spec/src/type-alias-convention.pin.test.ts (the ADR-0122 isomorphism pin that the new EmptyState type alias requires, 779 → 780) and view.zod.ts (where the list view's inline shape is extracted). Both are forced by "not a second shape", and the reviewer judged them right.

    • Deviations accepted:

      • The list view's accept set is unchanged: success, data, codes and paths are identical in 112/112 corpus cells. One refusal sentence (action/button) is reworded to read true on both doors.
      • The liveness record is the ledger's own describe marker, because ComponentPropsMap is not walkable as a row.
    • Contract review: at-tier record 5914228301 on the PR, Head-sha = the current head, PASS, Local-runs: none, identity pair present.

    • The dev's open question (renderer gap for a locale map in emptyState.title / .message): answered A by the review. The spec keeps one shape; the renderers catch up in the objectui follow-up.

    • objectui follow-up, filed in this act: plugin-grid + plugin-list + types: follow the object-grid description / emptyState / keyboardNavigation rows (objectstack#20694) once released — list them in GRID_QUERY_INPUTS, type emptyState text as I18nLabel, resolve maps at both renderers objectui#11227, Blocked-by: this card, unlocking on the installable release. It carries three items:

      • GRID_QUERY_INPUTS gains description and emptyState, with keyboardNavigation sequenced with its reader (objectui#11068);
      • the ObjectGridSchema.emptyState interface and its zod twin widen title / message to I18nLabel;
      • resolveInlineI18nLabel is applied at ObjectGrid.tsx:6241-6242 and ListView.tsx:5112/:5114.
    • Out of scope: none left unfiled. The renderer gap has no named producer today, and the changeset tells authors to write strings on object-grid until objectui#11227 lands.

    • Not yet landed: at this stamp, the completed runs are all success or skipped, and Test Core (1/6) and (5/6) are still running. When every run is success or a rostered skip, the seat sends pr_ready + automerge_enable and follows the PR to MERGED.

  9. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · PR #20882 MERGED through the merge queue as f5c7b2c753 (single parent) · domain:spec seat 1 (session_018fxqvRJW12TaHC7DUQ89Y6) · 2026-09-30T15:56Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:specenhancementNew feature or requestpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions