Skip to content

fix(app-shell,types): the page designer writes element:repeater's query into dataSource, and the repeater arm takes the spec gate's object waiver (objectui#12056) - #12064

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-12056-repeater-datasource
Oct 9, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-12056-repeater-datasource

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #12056
Clause-②: yes

What changes

The page-block inspector's element:repeater row (@object-ui/app-shell, patch). The row of PAGE_BLOCK_FIELDS now matches the element:number row from objectui#11880. The Object picker writes dataSource.object and the Limit box writes dataSource.limit, both through at: 'dataSource'. The Title field and Fields pickers read their object with objectFrom: 'dataSource'. The labels keep their existing keys, and no language-pack key is added.

The repeater's zod arm (@object-ui/types, minor). ElementRepeaterBlockSchema now applies the spec props gate's object waiver, the same way ElementNumberBlockSchema does:

  • the bag is ComponentPropsMap['element:repeater'] with object alone made optional (.partial({ object: true }));
  • a node refinement on the shared dataSourceSuppliesObject puts the requirement back (ELEMENT_REPEATER_OBJECT_REQUIRED, at properties.object) wherever no dataSource.object names the object.

The arm's dataSource description no longer says that properties.object stays required. Without this half, objectui validate refused the node the moved designer writes (properties: {} beside dataSource.object), which is the stop reported on the card. The seat's claim amendment on the card is the scope for this half.

Docs. In content/docs/guide/data-source.md, the repeater paragraph now states the waiver and that the Studio designer writes the binding, and its JSON example drops properties.object. The waiver sentence in packages/types/src/zod/README.md now names both arms.

Governing text. objectstack ruling 6051498447 (objectstack-ai/objectstack#11509, sub-question 2): "objectui first (the repeater takes dataSource, record_picker and number lose their ?? fallbacks, the designer writes the node-level dataSource, the docs follow), and the spec retirement ships with the pin bump that carries it."

Clause-② yes (widening). Two faces widen to the spec gate's rule:

  • StrictAnyComponentSchema (objectui validate / objectui check) and safeValidateSchema now accept a repeater whose bag omits object beside a non-empty dataSource.object;
  • the inferred ElementRepeaterBlockSchema type, and PublicBlockNodeOf for element:repeater, type properties.object as optional.

No export is added or removed. The changesets are @object-ui/types minor and @object-ui/app-shell patch. No changeset declares major.

Measured at the public door

The CLI was built from this branch (d59d2c8b2; the only later commit is a README sentence). I ran node packages/cli/dist/cli.js validate FILE:

node before (round 1, 47454d363, arm unchanged) after
repeater, properties: {} + dataSource: { object: contact } (what the designer writes) exit 1, properties → object, invalid_type exit 0
repeater, properties: {}, no binding not run exit 1, properties → object, custom
repeater, properties: { object: contact } exit 0 exit 0
element:number control, binding only exit 0 exit 0 (unchanged)
element:number, properties: { aggregate: count }, no binding not run exit 1, properties → object, custom

Tests

All of these ran through os-verify-lock on db0ed76e1 (git rev-parse --short HEAD) unless a different commit is named.

  • pnpm exec vitest run --maxWorkers=2 packages/types/ …app-shell previews/__tests__/ …inspectors/PageBlockInspector …inspectors/__tests__/page-block-path.test.ts plus the four CLI strict-face tests, objectname-binding-required-11605 and data-list.elementDataSource-11880: Test Files 400 passed (400) · Tests 10478 passed (10478), VERDICT command-exit 0. The full app-shell suite was not run, by the dispatch's instruction; CI runs it.
  • pnpm --filter @object-ui/types type-check and pnpm --filter @object-ui/app-shell type-check: VERDICT command-exit 0 on d59d2c8b2, after turbo run build --filter=@object-ui/cli... --filter=@object-ui/app-shell^....
  • pnpm check:doc-snippets: "784 of 784 block(s) judged, 0 failed", exit 0, after the gate's own --build-filter build. Also exit 0: check:doc-examples, check:doc-fences, check:doc-example-ids, check:doc-example-readers, check:doc-types, check:readme-exports, check:control-bytes, check:test-path-roots, check:changeset-claims, check:pending-changeset-literals, check:new-line-citations (0 new), the three check:vi-mock-* gates, check:designer-field-key-parity, check:spec-symbols, check-changeset-presence and check-changeset-no-major.
  • ESLint over the touched sources: 0 errors. The one warning is a pre-existing as any on a line of the objectui#11880 pin this PR does not change.

New and changed pins:

  • PageBlockInspector.repeaterDataSource-12056.test.tsx drives the real inspector. Object and Limit land under dataSource, a display control still writes properties, both pickers list the bound object's fields, and stored flat keys stay visible under Advanced.
  • In element-repeater-data-source-11880.test.ts, the old "a binding does not stand in" row is inverted:
    • a binding that names the object stands in;
    • a node with neither is refused with ELEMENT_REPEATER_OBJECT_REQUIRED;
    • a wrong-typed object stays the row's refusal;
    • the differential models the gate's waiver;
    • the bag is pinned member by member against the row;
    • a typed PublicBlockNodeOf literal without properties.object compiles.
  • held-public-block-arms-10872, public-block-arms-10872, public-block-responsive-styles-10872 and zod-mirror-parity change only where they asserted or described the old requiredness or bag identity.
  • The objectui#11880 inspector pin drops its control row that asserted the repeater's flat write.

Reverse verification (ablation, predictions written first)

All legs ran through ablation-replace.mjs in WRAP mode, with the anchor count and blob change checked on disk. Every restore was proven: blob equal to HEAD, and git diff HEAD empty.

  • Inspector row, leg A (old four-field row put back), on e4206e077: predicted 4 failed / 5 passed; observed Tests 4 failed, 5 passed (9).
  • Inspector row, leg C (only the Fields picker pointed at objectFrom: 'page'): predicted 1 failed / 8 passed; observed 1 failed, 8 passed (9). The red came at the DOM roster assertion, so the picker pin's DOM half is not vacuous.
  • Arm, leg W1 (bag half removed: the full row, no .partial), on d59d2c8b2: predicted 7 failed, by name; observed Tests 7 failed, 254 passed (261), the same 7.
  • Arm, leg W2 (node refinement dropped): predicted 5 failed, by name; observed 5 failed, 256 passed (261), the same 5.
  • Arm, leg W1 under tsc -p tsconfig.test.json: predicted one error, at the typed binding-only literal; observed exactly one, TS2741 Property 'object' is missing.

Acceptance notes

  • Not migrated: a repeater already stored with flat properties.object / properties.limit. The curated controls show the binding's values, and a stored flat key stays editable under Advanced. The v18 retirement ([Direction · v18] Retire the flat object/filter data-binding family — dataSource as the single data-binding door (re-anchor of the deleted #6590 tracker) objectstack#11509) owns that.
  • Out of this PR's surface: the repeater renderer's registration in packages/components still declares its object input required: true, while the arm now waives it beside a binding (element:number's registration declares no required on object). This is noted, not filed. Carrier: whoever next touches that registration, or the v18 pin bump that retires the flat key.
  • objectstack's own gate waives object beside dataSource.object for every component type (suppliedByDataSource, read on objectstack main). This arm now agrees with it.
  • The branch merged objectui main at 1f1c4b526 (objectui#6349 batch 6) before the arm change.

Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 (dispatched dev under the domain:ui seat 3 claim on objectui#12056).


Generated by Claude Code

claude added 5 commits October 9, 2026 12:27
…ery into dataSource (objectui#12056)

The repeater row of PAGE_BLOCK_FIELDS moves as the element:number row did
in objectui#11880: the Object picker and the Limit box are homed in the
node-level dataSource binding (at: 'dataSource'), and the Title field and
Fields pickers read their object from dataSource. They wrote
properties.object / properties.limit, the flat keys objectstack v18
retires (objectstack#11509).

New pin PageBlockInspector.repeaterDataSource-12056.test.tsx drives the
real inspector; the objectui#11880 suite drops its control that pinned the
repeater's old flat write.

Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8
Co-authored-by: Claude <noreply@anthropic.com>
Replaces an `as any` with the patch shape the inspector hands the editor,
so the new pin adds no no-explicit-any warning.

Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8
Co-authored-by: Claude <noreply@anthropic.com>
…de dataSource.object (objectui#12056)

ElementRepeaterBlockSchema now mirrors the spec props gate's waiver the
way ElementNumberBlockSchema does: the bag is ComponentPropsMap's
element:repeater row with `object` alone made optional, and a node
refinement on the shared dataSourceSuppliesObject restores the
requirement (ELEMENT_REPEATER_OBJECT_REQUIRED) wherever no binding names
the object. Without it, objectui validate refused the node the page
designer writes once its Object picker homes in dataSource.

The repeater arm pins move with it (the old "binding does not stand in"
row inverts; the bag is pinned member by member against the row), the
mirror ledger prose follows, and data-source.md's paragraph and example
stop teaching properties.object for a bound repeater.

Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 289 chunks) 3164.3 KB 3204.6 KB
Main entry chunk (gzip) 72.7 KB 350 KB
Entry file index-CSmW5ege.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 19.52KB 7.19KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 587.83KB 141.44KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 268.74KB 68.04KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.50KB 11.82KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.43KB 15.54KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 201.52KB 47.99KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 233.53KB 49.80KB
plugin-detail (index.js) 248.57KB 65.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.11KB 45.88KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 249.62KB 69.16KB
plugin-kanban (index.js) 52.77KB 16.56KB
plugin-list (index.js) 120.51KB 30.42KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.06KB 11.80KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 92.12KB 23.31KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: db0ed76e14610ec8eda870db6365097d68fe1d8d
Local-runs: none

Inputs: card #12056 (body, 5 comments: triage 6080546440, claim 6080758221, report 6081483536, amendment 6081544199, report 6082088385), PR #12064 (body, 13-file list, net diff against main), and the 41 check-runs on the head. Gate verdicts: every run success; Test (coverage), Test (coverage shard) and dependabot are conditional skipped, not failures. No run failed. Not read: the dispatch order, the seat's own conclusions, anything outside pr-12064/.

① Derived judgments

A. Designer write surface (@object-ui/app-shell, block-config.ts, the 'element:repeater' row of BLOCK_CONFIG). The Object picker gains at: 'dataSource' and writes dataSource.object; the Limit box gains at: 'dataSource' and writes dataSource.limit; titleField and fields move from objectFrom: 'self', objectProp: 'object' to objectFrom: 'dataSource'; emptyText and divided keep writing properties. Judged right: it is the card's direction and ruling 6051498447 sub-question 2 ("the designer writes the node-level dataSource"), and it is byte-for-byte the 'element:number' row's shape at head (object at: 'dataSource', field objectFrom: 'dataSource'). BlockPropField is (...kinds) & { at?: 'dataSource' }, so the number kind carries at, which answers the claim's first stop clause; the new pin's Limit row lands dataSource.limit: 25 as a number, and Test shards plus Type Check are green on the head. BLOCK_CONFIG is not re-exported from packages/app-shell/src/index.ts, so no package-entry member changes; the row keeps its six existing label keys and packages/i18n/** is untouched, so the claim's "a new key is a stop" never fires. The card and claim call the table PAGE_BLOCK_FIELDS; the file exports it as BLOCK_CONFIG. A naming slip in the card text, not in the code; noted only.

B. Accept-set widening, both zod faces (@object-ui/types, ElementRepeaterBlockSchema). The bag becomes stripImportedDefaults(SpecElementRepeaterPropsSchema).partial({ object: true }) (ElementRepeaterPropsBag) and the arm gains .superRefine(elementRepeaterObjectIsSupplied, ...) with when returning true on the shared dataSourceSuppliesObject predicate. Result: a repeater whose bag omits object beside a dataSource.object that is a non-empty string is now accepted by StrictAnyComponentSchema (objectui validate / objectui check) and by safeValidateSchema; before, both refused it at properties.object (invalid_type). Judged right: it mirrors the spec props gate's type-blind object waiver that the module docblock on main already documents for element:number (suppliedByDataSource), and the construction is the element:number arm's, helper for helper: same .partial({ object: true }), same predicate, same always-true when, flatPropRefusals over the partial bag (it keys on row.shape, so the refusals are unchanged), and propsBag(type, row, description) is exactly the number arm's inline .optional().describe(...). Without this half the moved designer writes a node its own validator refuses, which the round-1 report measured at the door (exit 1, properties → object).

C. Refusal retained, with a new issue shape. A bag without object and no binding naming one (no dataSource, dataSource.object empty or not a string, or a string dataSource) is still refused at properties.object, but as a custom issue with params.code: 'ELEMENT_REPEATER_OBJECT_REQUIRED' where the row used to report invalid_type. A wrong-typed object beside a binding is still the row's own invalid_type. Judged right and named: it is ELEMENT_NUMBER_OBJECT_REQUIRED's shape, and the changeset describes the new code; it does not spell out that the prior code was invalid_type, which a consumer matching issue codes at that path would notice. A disclosure nit, not a gap in the accept-set.

D. Type face. ElementRepeaterPropsBag's object is optional, so the arm's z.input and with it PublicBlockNodeOf for 'element:repeater' (derived by ClosedArmInput from the arm's shape) type properties.object as optional; the diff's typed binding-only literal compiles under tsc -p tsconfig.test.json (and Type Check is green). Judged right and disclosed in the changeset. Named consequence: a reader that typed properties.object as string off that node type now sees it possibly undefined; the element:number arm carried the same consequence in objectui#10872 batch 2, and nothing in-repo breaks. Under this repo's Clause-② rule this is the widening it is declared as; see ②.

E. Test realignments. The objectui#11880 inspector pin loses only its "repeater still writes properties.object" control row (it asserted the retired write); held-public-block-arms-10872 and public-block-arms-10872 exclude the repeater from "the bag IS the row" because the bag is now a clone by construction, and the member-by-member pin moves to element-repeater-data-source-11880.test.ts (keys equal, catchall never, every member but object identical by reference, object the row's own wrapped in optional); the differential now mirrors the gate's waiver; zod-mirror-parity and public-block-responsive-styles change only their descriptions. Judged right: each edit tracks a changed assertion, none weakens a pin that still holds.

F. Docs. content/docs/guide/data-source.md now says the gate waives properties.object beside dataSource.object, the validator applies the same waiver, a bag with neither is refused, and the designer writes object and limit into dataSource; its example drops "object": "task" from the bag. packages/types/src/zod/README.md names both arms for the waiver. Judged right (the ruling's "the docs follow"); Doc Snippet Type Check and Build Docs are green.

G. Not changed, correctly. The repeater renderer and its packages/components registration; every other BLOCK_CONFIG row and zod arm; packages/i18n/**; packages/components/src/ui/**; no read-side migration of flat properties.object / properties.limit (the v18 retirement owns it, and stored flat keys stay editable under Advanced per the new pin). No export is added or removed in either package (README Export Check green).

② Semver level

Two changesets: .changeset/12056-repeater-datasource.md (@object-ui/app-shell: patch) and .changeset/12056-repeater-object-waiver.md (@object-ui/types: minor). They match what the diff publishes: app-shell changes what an internal field table writes with no entry-member change (patch); types widens the accept-set of both zod faces and loosens one member of an exported arm's input type (minor, Clause-② widening), with no export added or removed and no major. Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Claim Re-read and Changeset Overwrite Report are all success; the dev's check-changeset-no-major reads clean.

Clause-②: yes
The PR body carries Clause-②: yes at line start on its second line. Triage 6080546440 and claim 6080758221 said no; amendment 6081544199 replaced it with yes on the dev's measured stop, and the PR matches the amendment, which is the governing text for this head.

③ Boundary flags

  • Round-1 open_questions[0] (arm refuses the designer-written node; options A/B/C): answered by amendment 6081544199, option A, in this PR. The diff implements A in full (arm, tests, docs, two changesets). Closed.
  • Round-1 out_of_scope_findings (the lint/types seam; the data-source.md paragraph): both absorbed by A. Closed.
  • Claim stop clauses (at: 'dataSource' on a number field; a parity gate refusing the moved keys): at is declared on every BlockPropField kind, and check:designer-field-key-parity plus every CI gate is green. Not triggered.
  • Round-2 open_questions: none.
  • Round-2 out_of_scope_findings (the element:repeater registration in packages/components/src/renderers/basic/data-list.tsx:243 still declares its object input required: true while the node face now waives it beside a binding; "noted, not filed", reach not measured): premise confirmed on the file, which this PR does not touch. The amendment keeps the renderer out of scope ("Still out: the repeater renderer"), so it does not block this head. Escalated to the seat: decide whether any registry-driven surface reads inputs[].required (a Studio required-marker, a registry lint, objectui check outside the zod face) and so whether this needs a card before chore(objectui): bump the console pin past the fix for objectstack-ai/objectui#12056 (the designer's repeater row writes dataSource). #11509's v18 retirement must ship with it objectstack#22476 carries the pin, or stays with the named carrier.
  • Round-2 deviations: (1) prose-only edits outside the amendment's list (the module docblock, the dataSourceSuppliesObject docblock, the record:line_items parenthetical, the zod README, the bag-identity row of public-block-arms-10872) — each follows a sentence the change made false; within "the docs follow". Accepted. (2) A parallel elementRepeaterObjectIsSupplied rather than a factory shared with the number arm, so that arm stays byte-unchanged — scope-preserving; the duplication is two short functions. Accepted. (3) Merge commit without trailers (squash-merged), (4) AGENTS.md trailer pair, (5) worktree and namespaced refs removed after the PR — process, not contract. Accepted.
  • Round-1 deviations (no PR at the stop; the 11880 control row removed; cli built for the door probe): each resolved or carried into the diff as judged in ①E. Closed.
  • Acceptance notes in the PR body (no migration of stored flat keys; the registration mismatch; the branch merged main 1f1c4b526): consistent with the diff and the card. The net diff against main holds 13 files, the PR's own file list, so the merge carried nothing extra.

Implemented-by: claude/issue-12056-repeater-datasource
Reviewed-by: session_01CGZy1BGCjdN5cXqL9cnvB8

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 9, 2026 14:04
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 9, 2026 14:04
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 9, 2026
Merged via the queue into main with commit 2a48bd4 Oct 9, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-12056-repeater-datasource branch October 9, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation package: app-shell package: types tests

Projects

None yet

2 participants