Skip to content

spec: ComponentPropsMap['object-kanban'] declares limit (row cap) — four objectui faces implement and teach it, the strict props map refuses it by name (spec half of objectui#8172) #16503

Description

@os-zhuang

Filed by the director seat under the objectui#8172 ruling (decision batch #68, 2026-09-07, option A — the contract declares the capability that is already implemented, documented and in use). objectui#8172 is pm:blocked on this card.

Measured (objectui#8172, @objectstack/spec 17.2.0)

  • objectui plugin-kanban reads schema.limit as the top-level $top (deliberately wired by objectui#4025), OBJECT_KANBAN_DATA_SOURCE maps limit: 'limit', ObjectKanbanSchema (@object-ui/types) declares limit?: number, and content/docs/plugins/plugin-kanban.mdx teaches it with a typed snippet (limit: 250) plus a Properties-table row.
  • ComponentPropsMap['object-kanban'] is a strict object (objectName, groupBy, columns, filter, data, cardTitle, titleField, cardFields, swimlaneField, grouping, quickAdd, coverImageField, conditionalFormatting); safeParse({ objectName: 'x', limit: 250 }) → unrecognized_keys: ['limit'], same verdict as the bogusProp control.

⇒ An author following the published docs writes a node the platform's save gate refuses. Declaring limit in objectui's registration alone is wrong (it would publish surface the save gate cannot store and break check:react-blocks-declaration-parity).

Scope

  • Add limit: z.number().int().positive().optional().describe('Maximum number of records loaded onto the board (row cap); lowered to the query's top-level $top') to object-kanban's props in the spec.
  • If the spec seat judges that the cap should instead derive from the bound named view's pagination.pageSize, propose that on this card before implementing — the ruling is that the four faces and the contract must agree, not which spelling; but the default is to declare limit, since it ships and is taught.
  • Widening a published accept set ⇒ Clause-② yes: landing PR carries needs:contract-review; changeset (minor); api-surface / authorable-surface baselines regenerated.

Acceptance

  • safeParse({ objectName: 'x', limit: 250 }) succeeds; bogusProp still refused
  • baselines regenerated; changeset present
  • objectui#8172 notified (registration then publishes the key)

Refs objectui#8172, objectui#4025, objectstack#15449.

Activity

  1. self-assigned this
    on Sep 7, 2026
  2. huangyiirene commented on Sep 7, 2026

    @huangyiirene
    Collaborator

    Claim:

    • session: session_01T6HeZvT9wdSJD1ZxJb5Eno (PM dispatch, domain:spec execution seat)
    • branch: claude/issue-16503-object-kanban-limit-prop
    • dispatched: 2026-09-07T08:40Z

    The dev inherits this claim and the assignee. ⛔ It posts no second Claim:, and never writes the assignee field.


    Why this card is being claimed now, after I passed it over

    I read this card at ~07:45Z and deliberately did not claim it, on one stated ground only: it is Clause-②: yes, CONTRACT_REVIEW_TIER is claude-fable-5-1, that tier had been exhausted, and I would not stack a second PR that could not clear review while a cheaper landable card existed.

    That constraint is lifted. Maintainer directive, 2026-09-07, verbatim: 「你有fable,可以优先派fable」 — fable is available and is now the preferred tier. Concurrency was also raised 3 → 5 in the same instruction.

    ⇒ ⛔ Nothing about this card changed; my constraint did. Recording it so the gap between filing (05:11Z) and claim is not read as a judgement on the card's merit.

    Serial constraint — checked, and one adjacency worth naming

    packages/spec/src/ui/** (the props map) is named by no in-flight claim.

    ⚠️ Two in-flight PRs regenerate generated corpus, so know where they sit before you regenerate:


    Generated by Claude Code

  3. huangyiirene commented on Sep 7, 2026

    @huangyiirene
    Collaborator

    os-dev-report

    {
      "issue": 16503,
      "status": "done",
      "branch": "claude/issue-16503-object-kanban-limit-prop",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/16562",
      "premise_still_valid": true,
      "summary": "Declared `limit: z.number().int().positive().optional()` on `ComponentPropsMap['object-kanban']` (packages/spec/src/ui/component.zod.ts) with the card's describe plus the renderer's precedence sentence; four pin tests (accept `{ objectName: 'x', limit: 250 }` carrying the value through, refuse `bogusProp` with `unrecognized_keys: ['bogusProp']`, refuse 0 / -1 / 1.5 / '250' at the VALUE, describe names `$top`); minor `@objectstack/spec` changeset; regenerated `authorable-surface/ui.json` (+ `ui/ObjectKanbanProps:limit`) and `content/docs/references/ui/component.mdx` (+1 row) — no `api-surface` shard moved, so no collision with PR #15919's repair or PR #16531's files. The card's open judgement (`pagination.pageSize` as the carrier) was decided for the default `limit` and NOT proposed as a change: at the pin, `ElementDataSourceGate.tsx:229-241` lowers a bound view's `pagination.pageSize` INTO the key the block's mapping names, and kanban's mapping names `limit`; the board has no `pagination` read point, so that spelling would be accepted-and-dropped. Premise re-measured at `.objectui-sha` a472b07167a39e55491109e864bb5a54027dcfbd: ObjectKanban.tsx:264 `$top: schema.limit ?? DEFAULT_KANBAN_LIMIT` (100), index.tsx:395-398 `limit: 'limit'`, plugin-kanban.mdx:108,153-172 — one spelling correction to the card: the declaring type at the pin is `KanbanSchema` (plugin-kanban/src/types.ts:134), not `@object-ui/types`' `ObjectKanbanSchema`, which carries no `limit` there; four faces still agree, ruling substance unchanged. Draft PR #16562 carries `Fixes #16503`, a standalone `Clause-②: yes` line and `needs:contract-review` on both carriers (read back — PR: documentation, size/m, tests, tooling, needs:contract-review, protocol:ui; card: enhancement, priority:p2, pm:dispatched, domain:spec, needs:contract-review); objectui#8172 notified by comment 5568725356, not edited. Assignee never written, no second Claim; worktree removed after the PR opened; PR left draft — ready flip, auto-merge and queueing are the PM's after the at-tier review.",
      "tests": "All on fb654377e2 (final commit; tree unchanged after). `vitest run src/ui/component.test.ts`: 'Test Files 1 passed (1) / Tests 251 passed (251)'; `--reporter=verbose -t 16503`: 4 of 4 pass. Reverse verification (committed first): component.zod.ts reverted to BASE f48f3f1b21 (blob a549b824 equals the BASE blob, differs from HEAD 3d939c5e), same file: 'Tests 3 failed | 1 passed | 247 skipped' — accept, value and describe pins red, bogusProp control green; restored via `git checkout HEAD --`, blob back to 3d939c5e, `git diff HEAD` empty. Ablation of the checked-in baseline (no rebuild, since a spec build regenerates authorable-surface/): `ui/ObjectKanbanProps:limit` row removed from authorable-surface/ui.json (anchor count 1 to 0, blob eb3c99d7 differs from HEAD 1b290888), `check:authorable-surface` exit 1: 'authorable-surface/ is out of date (1 key(s) not recorded). + ui/ObjectKanbanProps:limit'; restored, blob back to 1b290888, anchor 1; trap-protected script, final `git status --porcelain` empty. Build under os-verify-lock (slot issue-16503): `pnpm --filter @objectstack/spec build && check:generated` gave 'VERDICT command-exit 1' naming exactly one stale artifact (content/docs/references/**); `gen:docs` 'Generated 228 files'; `check:docs` '228 generated files in sync'; `check:api-surface` 'public API surface + factory signatures unchanged'; `check:authorable-surface` green. Footprint: tracked 2 files moved (of a 317-file tracked generated corpus); gitignored packages/spec/json-schema/ 1576 files rewritten, 2 carrying the key. `pnpm --filter @objectstack/spec typecheck` exit 0 (check:test-typecheck OK). Narrowed spec tests under the lock — the 11 of 425 spec test files naming ComponentPropsMap / object-kanban / component.zod / snapshots: 'Test Files 11 passed (11) / Tests 732 passed (732)'. Consumer: `pnpm --filter @objectstack/lint test` 'Test Files 100 passed (100) / Tests 3486 passed (3486)'. Gates: `dispatch-gates.mjs --ran` reconciliation '98 derived, 97 run, 1 UNRUN' — the unrun is check:react-declaration-parity (needs objectui's sdui.manifest.json; NOT MEASURED by construction). Green verdict lines: check:objectui-pin-citations '12 asserting objectui pin citation(s) match .objectui-sha (a472b0716)'; check:duration-unit-keys; check:skill-examples '257 prose examples type-check across 3 surface(s)' (after building the client-react closure); check:nul-bytes 'OK (scanned 8144 text file(s))'; check:cross-package-test-inputs; check:test-source-alias; check:docs-transcript-drift; check-adr-0087-registration 'adds no declared-breaking changeset'; check-empty-changeset; check-changeset-no-major; check:liveness; check:strictness-ledger; check:yaml-examples; and every other derived family including all --self-test variants, exit 0. NOT MEASURED (exit 3, prerequisite: full `pnpm build` closure): check:type-check-debt, check:dual-build-cjs-loads — CI's. NOT MEASURED (lock queue-timeout exit 99, twice, 9 min each): examples/app-showcase/test/my-work-visibility.test.ts — it names ComponentPropsMap only in a comment and no repo test asserts `limit` refused on object-kanban; declared narrowing, CI runs it. CI on the PR: in_progress at report time.",
      "mcp_calls": "12 — issue_read get_labels #16503; create_pull_request; issue_write labels #16503 (union); issue_read get_labels on the PR number (errored: GraphQL cannot resolve a PR as an Issue); issue_read get_labels #16503 read-back; pull_request_read get; add_issue_comment objectui#8172; issue_read get on the PR (labels); issue_write labels PR (union); issue_read get PR read-back; add_issue_comment (this report); issue_read get_comments read-back. REST was 403 this session (session gate closed); card and comments were read from git and the public issue-page payload; no search_issues needed — nothing out of scope to file.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  4. removed their assignment
    on Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions