fix(spec): offer the view form's pagination to every view type - #19841
Conversation
`pagination.pageSize` is the one row bound a view carries, and for a kanban, gallery or timeline view it is the only one. The Studio view form offered `pagination` only inside the grid-only `table_options` section, so an author of any other view type could not reach it. `pagination` moves to its own section with no `visibleWhen`; the grid-only fields stay in `table_options`. A pin derives the type list from the list-view `type` enum and asserts both halves. Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr Co-authored-by: Claude <noreply@anthropic.com>
…w pagination section Output of `pnpm i18n:extract`, unedited: the view form's new `pagination` section adds its label and description to the four metadata-form bundles, with the three translated locales filled from the source and their provenance recorded in the source-hash tables. Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr Co-authored-by: Claude <noreply@anthropic.com>
Hand-written zh-CN / ja-JP / es-ES leaves for the new section's label and description (the label reuses each locale's existing `pagination` field label). A re-run of `pnpm i18n:extract` then dropped the three source-hash entries by itself, since those leaves are no longer copies of the source. Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr Co-authored-by: Claude <noreply@anthropic.com>
…agination section Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr Co-authored-by: Claude <noreply@anthropic.com>
The view form's new `pagination` section adds one `.label` leaf, and it is authored in zh-CN, ja-JP and es-ES rather than left an extractor fill, so the per-locale count of translated labels moves by exactly one. Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 2 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
Contract reviewServed-tier: Card #19814, round 1. Base Tier, measured by the SEAT from the reviewer's transcript (⛔ not its self-report): ① Derived judgmentsBlocking — B1: a shipped changeset sentence is false on this head.
Everything else measured holds:
② Semver level
③ Boundary flags
CI at this head: 35 names — 32 success, 3 skipped (Console Pin Gate, Build Docs, Packed-tarball smoke (opt-in)), 0 in progress / queued / cancelled / failure. All seven required contexts are Non-blocking, recorded and ⛔ not a round:
Implemented-by: VERDICT: FAIL Generated by Claude Code |
…he per-kind limit retirement The changeset, the form comment and the pin's docblock said `pagination.pageSize` is a view's only row bound. The kanban, gallery and timeline configs still declare a per-kind `limit` until its retirement lands, so all three now say only that `pagination.pageSize` is the row bound every view type carries. Wording only; no form entry, test logic, bundle or translation moves. Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Card #19814, round 2. Base Tier, measured by the SEAT from the reviewer's transcript (⛔ not its self-report): ① Derived judgmentsB1 is resolved.
Nothing else moved.
② Semver level
③ Boundary flags
CI at this head, read after the seat's 11:46Z body edit: 35 names — 30 success, 5 skipped ( Non-blocking, recorded and ⛔ not a round:
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #19814
Clause-②: no
What changes
pagination.pageSizeis the row bound every view type carries, and maintainer ruling D on #19228 makes it a view's one row bound. The per-kindkanban.limit,gallery.limitandtimeline.limitthat the ruling retires are still declared onmainuntil PR #19809 lands. The Studio view form (packages/spec/src/ui/view.form.ts) offeredpaginationonly insidetable_options, whose condition isdata.type == 'grid' || data.type == nulland whose description isGrid-only display options.. An author editing any other view type could not see or set that bound in the form.paginationmoves out oftable_optionsinto its own section,pagination(labelPagination, collapsible, collapsed), placed right aftertable_options. The new section has novisibleWhen.table_optionskeepsresizable,compactToolbar,rowHeightandselectionunder its unchanged grid condition.table_options' condition would show the four grid-only fields to every view type. Movingpaginationintocolumns_filterswould put a field that is neither a column nor a filter under the labelColumns & filters. The file already gives each concern its own section. The new description claims only what the schema guarantees:Page size and page-size options — every view type accepts them, not only grids.view.zod.ts,component.zod.tsand every per-kindlimitare untouched; they belong to PR fix(spec): one row bound per view — retire the unpublished per-kind viewlimit#19809.Premise, measured on
origin/mainc1dfa5241btypeenum is on the list-view shape (ListViewSchema.shape.type). The containerViewSchemahas notypekey, and itslistmember resolves to the same nine values: grid, kanban, gallery, calendar, timeline, gantt, map, chart, tree.paginationis a member of the single list-view shape, so every kind has it. Per kind,ListViewSchema.safeParse({ type, columns: ['name'], pagination: { pageSize: 50 } })parsed 9/9 and kept{ pageSize: 50 }. As a control,pagination: { pageSize: 50, zzBogus: 1 }was refused 9/9 withunrecognized_keysat['pagination'], which shows the block is validated and not stripped.view.form.tshas no per-kindlimitentry. Nothing PR fix(spec): one row bound per view — retire the unpublished per-kind viewlimit#19809 retires is in the form, so this change does not depend on a shape that PR has not landed.Tests
packages/spec/src/ui/view-form-pagination.test.ts, 58 cases:ListViewSchema'stypeenum. A floor names the four types ruling D covers, so an empty derivation cannot pass.typeyet,paginationis offered in exactly one visible section. The form offers it exactly once.paginationblock and keeps it.gridand for an unset type, and hidden from each of the eight non-grid kinds.data.type == '...'anddata.type == null. Any other term throws and names the predicate, so the pin fails loudly on a predicate it cannot read.Firing control (a one-time run, not a kept test). With the fix committed,
packages/spec/src/ui/view.form.tswas restored to its base blobbea0c5ab92withgit restore --sourceset to the base commit, and the pin was run. Result:Tests 8 failed | 50 passed (58). The eight failures are exactly the "is visible for type" cases for kanban, gallery, calendar, timeline, gantt, map, chart and tree. The file was then restored fromHEADunder a trap; its blob was re-checked equal to HEAD's (bf6c3a7f93) andgit statuswas clean.Generated artifacts
One repo generator reads
view.form.ts:pnpm i18n:extract. It reaches the form throughMETADATA_FORM_REGISTRYand writespackages/platform-objects/src/apps/translations/*.metadata-forms.generated.ts. A search of the tree for the form's section text finds it only in the source and inen.metadata-forms.generated.ts. TheviewFormexport keeps its name, andcheck:api-surfacereports the surface unchanged.17bb7c1603is the generator output, unedited. It adds the new section's label and description to all four bundles, fills the three translated locales from the source, and adds twometadataForms.view.sections.pagination.*rows per locale to the source-hash tables.72185706b2writes the zh-CN, ja-JP and es-ES translations. These leaf values are the only edits these files allow. Each label reuses the locale's existing label for thepaginationfield. A re-run of the extractor then dropped the source-hash rows by itself, andpnpm check:i18nreports the bundles in sync at head.2bfeee4a23:object-lifecycle-panel-echo-decisions.test.tspins the per-locale count of translated.labelleaves across the metadata-form catalog. The count moves from 583 to 584, which is the one section label this PR translates in each locale.Changeset
@objectstack/specshipsdistin itsfiles[].viewFormis exported from./uiand reaches./systemthroughMETADATA_FORM_REGISTRY. After a build, the new description string is in 6 dist files (ui/index.{js,mjs},system/index.{js,mjs},browser/system/index.{js,mjs}). The positive controlGrid-only display optionsis in the same 6 files.@objectstack/platform-objectsalso shipsdistin itsfiles[]. After its rebuild the new string is in 6 dist files (index,pluginandmetadata-translations/index, each.jsand.mjs); the control is in the same 6..changeset/19814-view-form-pagination-all-kinds.mdbumps both aspatch. AGENTS.md Post-Task step 3: a fix in a released package takes a patch changeset, andskip-changesetis only for a diff that publishes nothing.Clause-②: no, because no accept set moves.Local verification
0395fd696d(re-run after round 2; first measured at2bfeee4a23, same result):node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsderived 84 commands. All 84 were run on2bfeee4a23and all exited 0.--ran, fed the recorded exit codes, reports: "84 derived famil(ies) accounted for — 84 run, 0 NOT-MEASURED (a DERIVED zero — all 84 recorded an exit code and none of them is 3)". On the first pass,check:i18n,check:type-check-debtandcheck:dual-build-cjs-loadsexited 3 (build prerequisite not met). They were measured afterturbo run build --filter='./packages/*' --filter='./packages/*/*'.@objectstack/spec, run atfe2ae4cf09(the spec package has no byte change since):vitest run --project local: 522 files, 15405 passed, 1 todo.typecheck: exit 0. The test layer holds 53 files / 255 errors in the ledger, and the new test is inside thetsconfig.test.jsonprogram.@objectstack/platform-objectsat2bfeee4a23: vitest 54 files, 883 passed.typecheckexit 0.pnpm lint, which CI owns.eslint --no-inline-config --format jsonover the 7 touched.tsfiles returned 7 file results, 0 errors, 0 warnings, and none of the files was ignored. The repo's oneeslint.config.mjsenables no type-aware linting (its note at:326-328), so this diff cannot change the lint result of any file it does not touch.check-issue-citations, shard attestations, test completeness), the 11 declared-wide families, andpnpm lint.Acceptance notes
87af769e9,plugin-list/src/ListView.tsxreadsschema.pagination?.pageSizeinto the$topof every list fetch (:1307-1312,:2356). For non-grid views it shows a rows-per-page selector whenpageSizeOptionsis set (:4866). The one exception is a gantt view with anapidata provider, which does its own fetch (:1950-1955). This PR does not change the plugin-view route's hard-coded$top: 100, which is recorded in review record5791323483on PR fix(spec): one row bound per view — retire the unpublished per-kind viewlimit#19809.mapandtreeblocks that the list-view shape declares. That falls under the top-level "declared but not offered" class, whichmetadata-form-declared-rows.pin.test.tscounts as its own census, not under this card.Generated by Claude Code
Generated by Claude Code