feat(spec): object-kanban props declare limit, the row cap objectui already implements - #16562
Conversation
… already reads `ComponentPropsMap['object-kanban']` is strict and refused `limit` by name while objectui's plugin-kanban reads `schema.limit` as the query's `$top`, `OBJECT_KANBAN_DATA_SOURCE` maps `limit: 'limit'`, `KanbanSchema` declares `limit?: number` and the plugin docs teach `limit: 250`. Declare the key (`z.number().int().positive().optional()`), pin both directions (the documented shape parses; an undeclared sibling is still refused), and add the minor changeset. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T6HeZvT9wdSJD1ZxJb5Eno
…ect-kanban `limit` `pnpm --filter @objectstack/spec build` (gen:schema) adds the `ui/ObjectKanbanProps:limit` row to `authorable-surface/ui.json`; `check:generated` proved only `content/docs/references/**` stale and `gen:docs` regenerated it. No `api-surface/` shard moves — a key on an existing props map adds no export. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T6HeZvT9wdSJD1ZxJb5Eno
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 130 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 117b4a100c2d33c960574a1793fd3ba110bcad34 && git checkout 117b4a100c2d33c960574a1793fd3ba110bcad34
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 5e7ff5f6c68b43783895e1252716f0bf80f41929 fb654377e285aa973ef0d3931d975da9eca3cba2 && git checkout -B drift-repro 5e7ff5f6c68b43783895e1252716f0bf80f41929 && git merge --no-ff fb654377e285aa973ef0d3931d975da9eca3cba2
node scripts/docs-audit/affected-docs.mjs --json 5e7ff5f6c68b43783895e1252716f0bf80f41929 |
Contract review at
|
Armed — and why advisory A2 did not hold this PR, when a similar-looking one is holding #16563All 38 check runs read (37 earlier plus a A2 is a real inaccuracy and I am not hiding it. The changeset and the I am landing it anyway, and the distinction from #16563 is one I want on the record rather than left to look inconsistent:
The substance of this citation is true: the page does teach
The error's shape is worth naming though, because it recurs: the claim was read off a different sha than the one the docblock anchors to. That is the same family as several corrections in this lane today — a reading taken against a moving ref and then recorded as if it were taken against the pin. Generated by Claude Code |
Fixes #16503
Clause-②: yes
What
ComponentPropsMap['object-kanban'](packages/spec/src/ui/component.zod.ts) gains one optional authorable key:describe: Maximum number of records loaded onto the board (row cap); lowered to the query's top-level
$top(renderer default 100). The component-leveldataSource.limitwins when both are set; a bound view'spagination.pageSizefills it only when unset — the card's sentence, plus the precedence the renderer applies (the siblinglimitdescribes in this map,element:record_pickerandrecord:related_list, name their renderer default and binding precedence the same way).Widening a published accept set: Clause-② yes,
@objectstack/specminor changeset (.changeset/object-kanban-limit-row-cap.md),needs:contract-reviewon this PR and on #16503. The PR stays draft; the ready flip, auto-merge and queueing are the PM's after the at-tier review.Premise, re-measured at the objectui pin
Readings pinned to this repo's
.objectui-sha=a472b07167a39e55491109e864bb5a54027dcfbd(never objectui's moving branch):packages/plugin-kanban/src/ObjectKanban.tsx:262-266— the board's one query isdataSource.find(objectName, { $filter: schema.filter, $top: schema.limit ?? DEFAULT_KANBAN_LIMIT });DEFAULT_KANBAN_LIMIT = 100at:71; a real top-level$topsince objectui#4025.packages/plugin-kanban/src/index.tsx:395-398—OBJECT_KANBAN_DATA_SOURCE = { filter: true, limit: 'limit' }.packages/plugin-kanban/src/types.ts:134—KanbanSchema.limit?: number, the typeObjectKanban.tsx:143readsschemathrough. One correction to the card's spelling: at the pin,@object-ui/types'ObjectKanbanSchema(zod mirrortypes/src/zod/objectql.zod.ts:875-889, interfacetypes/src/objectql.ts:2693) carries nolimit; the declaring face isKanbanSchemain the plugin package. Four faces still agree; the ruling's substance is unchanged.content/docs/plugins/plugin-kanban.mdx:108, 153-172— the Properties row, thelimit: 250snippet (default 100), and the note separating the board'slimitfrom a column's WIPlimit.f48f3f1b21fe207c39d94e49609ac49e19635e3b:safeParse({ objectName: 'x', limit: 250 })yieldsunrecognized_keys: ['limit']— the same verdict asbogusProp. Leg B below reproduces it.The judgement the card opened:
limit, notpagination.pageSizeDecided for the default and not proposed as a change, because at the pin the alternative is not a competing spelling but an upstream source lowered into
limit:packages/react/src/element-data-source/ElementDataSourceGate.tsx:229-241(readLimit/writeLimit, keyed byElementDataSourceLimitKey, whose members arelimit,pageSizeandpagination.pageSize) writes the binding'sdataSource.limit— or, only when the component authored none, the bound view'spagination.pageSize— onto the key the block's mapping names, and kanban's mapping nameslimit. The board has nopaginationread point, so declaringpagination.pageSizeonobject-kanbanwould name a key the renderer ignores — the accepted-and-dropped defect the card's ruling exists to remove. The docblock on the key records this.Generated footprint (tracked vs gitignored)
packages/spec/authorable-surface/ui.json(+1 row,ui/ObjectKanbanProps:limit) andcontent/docs/references/ui/component.mdx(+1 table row). Noapi-surface/shard moves (check:api-surface: "public API surface + factory signatures unchanged"), so no collision with PRapi-surface/records a name declared as BOTH a const and a type under(type)only — deleting the value half of any of 132 such exports is invisible tocheck:api-surface#15919's repair; nothing touches the files PR spec: define the confirmation contract behindaction.ai.requiresConfirmation#16531 holds.packages/spec/json-schema/— 1576 files rewritten by the build, 2 of them carrying the new key (ui/ObjectKanbanProps.json,objectstack.json). The tracked generated corpus counts 317 files (git ls-filesover authorable-surface, authorable-defaults, json-schema.manifest, api-surface, export-origins, declaration-map and content/docs/references).check:generatedafter the build named exactly one stale artifact (content/docs/references/**);gen:docsregenerated it;check:docsreports "228 generated files in sync".Verification — all on
fb654377e2, the final commit; the tree did not change afterwardsPins (
packages/spec/src/ui/component.test.ts, 4 new cases): accept{ objectName: 'x', limit: 250 }and carry the value through; refusebogusPropwithunrecognized_keys: ['bogusProp'](the half that proves the object stayed strict); refuse0/-1/1.5/'250'at the value (issue path['limit'], nounrecognized_keys); the describe names$top,row capanddataSource.limit.vitest run src/ui/component.test.ts: 251 passed; with--reporter=verbose -t 16503: 4 of 4 passed.component.zod.tsreverted to BASE (bloba549b824…, equal to the BASE blob, not equal to HEAD3d939c5e…), same test file: 3 failed, 1 passed, 247 skipped — the accept pin, the value pins and the describe pin red; thebogusPropcontrol green, as designed. Restored withgit checkout HEAD --; blob back to3d939c5e…,git diff HEADempty,git status --porcelainempty.authorable-surface/and would come back falsely green): theui/ObjectKanbanProps:limitrow deleted fromauthorable-surface/ui.json(anchor count 1 to 0; blobeb3c99d7…not equal to HEAD1b290888…), thencheck:authorable-surface: exit 1, "authorable-surface/ is out of date (1 key(s) not recorded). + ui/ObjectKanbanProps:limit". Restored withgit checkout HEAD --; blob back to1b290888…, anchor count 1.pnpm --filter @objectstack/spec typecheckexit 0 (check:test-typecheckincluded).vitest runover the 11 spec test files (of 425) that name the props map,object-kanban,component.zodor a snapshot: 11 files, 732 tests passed.pnpm --filter @objectstack/lint test: 100 files, 3486 tests passed — lint is the in-repo consumer ofComponentPropsMap(validate-component-props.ts,validate-react-page-props).node scripts/pm/dispatch-gates.mjs --ran: 98 derived, 97 run, 1 unrun —check:react-declaration-parity, which cannot run in this repo (its right-hand side is objectui'ssdui.manifest.json; NOT MEASURED, not a pass). Green, verdict lines read from each gate's own output:check:generated(15 artifacts current aftergen:docs),check:api-surface,check:authorable-surface,check:docs,check:objectui-pin-citations(12 asserting citations matcha472b0716),check:duration-unit-keys,check:skill-examples(257 blocks across 3 surfaces, after building@objectstack/client-react),check:nul-bytes,check:cross-package-test-inputs,check:test-source-alias,check:docs-transcript-drift, the changeset gates (check-adr-0087-registration: no declared-breaking changeset;check-empty-changeset;check-changeset-no-major),check:liveness,check:strictness-ledger,check:yaml-examples(18 component nodes judged against their props schema), and the rest of the derived list. NOT MEASURED (exit 3, prerequisite — they need the fullpnpm buildclosure):check:type-check-debt,check:dual-build-cjs-loads— CI's.turbo ls --affectedlists every package downstream of@objectstack/spec. Locally: the spec test files that name the props map,object-kanban,component.zodor a snapshot, plus the whole@objectstack/lintsuite. The remaining downstream suites are CI's. The showcase consumer testexamples/app-showcase/test/my-work-visibility.test.tsqueued out twice on the shared verify lock (exit 99, NOT MEASURED); it namesComponentPropsMaponly in a comment, and no test in the repo asserts thatlimitis refused onobject-kanban.What this PR does not do
api-surfaceshard, no error-code ledger, no conversion entry — a widened accept set is non-breaking and needs no ADR-0087 disposition.Generated by Claude Code