Repository navigation
fix(components): element:number reads its object from dataSource.object, as the spec binding declares (objectui#10909) - #10944
Conversation
…ct (objectui#10909)
ElementNumberRenderer read its object only from `properties.object`, so a
spec-valid `{ dataSource: { object }, properties: { aggregate } }` metric
issued no query and painted the empty dash. It now resolves the binding
through `useElementDataSource`, the way `element:record_picker` does:
`dataSource.object ?? properties.object`, one value for the fetch guard,
`aggregate` / `find` and the `useDataInvalidation` key. A named `view`
scopes the aggregate (an unresolvable one reports and aggregates nothing),
a binding filter wins over `properties.filter`, and `sort` / `limit` are
not read. The registration goes through `elementDataSourceBlock`, so it
declares `dataSource`, and `object` is no longer required.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…d add its changeset (objectui#10909) Declaring `dataSource` on element:number adds an object-armed input to a covered block, so the registry-inputs parity census requires a member pin for it. The pin is the renderer file that asserts which binding members the metric reads and which it does not. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
… for the strict test project (objectui#10909) `tsc -p tsconfig.test.json` refused `adapter.aggregate` as possibly undefined on the optional-member fake. The aggregating fake and the find-only fake are now two factories. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…ctui#10909) The data-source guide's table of blocks that consume `dataSource` gains the `element:number` row (object, a view's filter, filter; no sort, no limit), and says how the two `element:*` rows read the binding: directly, with a binding member winning over the flat `properties` one. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
|
changeset-claim-re-read
|
✅ Console Performance Budget
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
Size Limits
|
Contract reviewServed-tier: ① Derived judgmentsObject resolution: correct and complete.
Filter precedence. Ruling: B (AND-combine). This is not a FAIL ground on its own; adopt it in the fix round that is owed anyway.
Registration and manifest.
Pins and ablation.
Census: true against source.
Docs and changeset.
Compatibility with PR #10908.
② Semver levelClause-②: yes (widening). The
③ Boundary flagsLocal-runs: none
Implemented-by: VERDICT: FAIL Required fix round (text only):
Recommended in the same round, the seat's call:
|
…he binding's filter (objectui#10909, fix round 1) Seat ruling B on the open question: with a `dataSource` binding present, `properties.filter` is AND-combined with the binding's filter and its view's, lowered and merged the way `ElementDataSourceGate` does (`toFilterNodeSafely` + `mergeFilterNodes`). Neither is dropped; a refused merge takes the configuration-error panel and aggregates nothing. The properties form with no binding is unchanged. Pins: the precedence row flips to assert both filters reach the aggregate; new rows for a view filter beside properties.filter, a refused merge, and the unresolved-view guard beside a flat object. The registered `filter` description, the guide paragraph and the changeset say the same. The changeset becomes `minor` with `Clause-②: yes (widening)`, and its two sentences the review found false are replaced. The MEMBER_PINS prose says what the injected declaration does and does not carry. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
✅ Console Performance Budget
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
Size Limits
|
Contract reviewServed-tier: ① Derived judgmentsThis is a delta review of The merge is the gate's own merge.
Wire shape: measured by reading, and accepted everywhere
Refused-merge panel. A refused merge renders
Texts.
Pins and ablations, checked against the test source. There are 18 rows.
② Semver level
③ Boundary flagsLocal-runs: none
Implemented-by: VERDICT: PASS |
… bare apiMethods-card number (objectui#10803, batch 7) (objectstack-ai#10962) Part of objectstack-ai#10803 Clause-②: no Dispatched implementation of the `domain:ui` seat objectstack-ai#1 claim (comment `5867587761`) on objectui#10803, batch 7, session `https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk`. Citations only: no sentence's claim moves, and every edited pending changeset's frontmatter is byte-identical. The only runtime text that moves is two console warnings, one in `@object-ui/app-shell` and one in `@object-ui/plugin-detail`, which lose their dead pointer and nothing else (amendment `5860244997`, Q1 = A; `patch` changeset). No test pins any changed text: the literal-anchor sweep below finds no specific anchor, so no test file is edited. This batch carries release `5866922219`'s two lists: - the **30 `objectstack#N` citations that answer 404**, in the card's two classes (pending changeset prose and non-test `packages/*/src`); - the **28 bare `objectstack-ai#3391` lines in 10 files** that mean objectstack's apiMethods whitelist card, which batch 6 fixed at 2 other sites. ## Why `Part of`, not a closing line Both lists read **0** after this batch (**Census**). The brief's rule was a closing line if the card's lists all read 0. They do, but reading the sentences found **10 more lines in the same two classes that cite a dead objectstack number written bare**, three numbers in all (**Acceptance notes** 1). Triage item 3 puts a dead number found later in these classes on this card, so the card is not finished. Whether it carries them as a batch 8 or closes is the seat's call. ## Premise, re-measured on `origin/main` `3b469c8ea` (the branch point) - **Every distinct `objectstack#N` in the two in-scope classes.** 372 numbers (the one objectstack issue URL in these classes names 6227, which is among them). Each was read once with REST `GET /repos/objectstack-ai/objectstack/issues/N`: - 341 answer 200; - 1 answers 301: objectstack#14026, transferred to objectui#10102, which batch 6 re-qualified; - **30 answer 404**, exactly the 30 batch 6 listed. - **The 30, read again.** A second `issues/N` read of each answers 404 (30 of 30), and `pulls/N` answers 404 for all 30. Lit controls in the same run: objectstack#3391, objectstack-ai#3720 and objectstack-ai#3546 answer 200 as issues, and `pulls/13267` answers 200. - **objectstack-ai#3391 and objectstack-ai#3546, both repositories.** - objectui#3391 is the record-header api-action placeholder card, unrelated. - objectstack#3391 is the apiMethods whitelist contract card: "UI 操作按钮与 apiMethods 白名单一致性契约落地". Its body names the effective operation set, `/me/permissions`, the 405 import refusal and export derived from list, which is what each of the 28 sentences says. - objectui#3546 is the missing-i18n-keys card, unrelated. - objectstack#3546 is "detail/form 面的 edit/delete 按钮接入服务端 effective 操作集", the inline-edit gate the two paired lines describe. - **objectstack history.** Read from a full, not shallow, treeless clone of objectstack `main` (`git rev-parse --is-shallow-repository`: false). - Every objectstack sha this PR cites is an ancestor of objectstack `main` (`git merge-base --is-ancestor`, exit 0): the 24 this PR adds to the tree, the 3 its edited sentences already cited (`c459da6bc`, `89448a52b`, `9bd4344e4`), and the 7 this body names besides. - `git rev-parse --short=9` returns the same 9 characters for each. - Control legs in the same clone: the head of the open PR objectstack#20421 (`a22b90fc0`) answers exit 1; the known ancestor `51789064` answers exit 0. - **The one objectui sha.** `7a197e7c5` is an ancestor of the branch point, exit 0. Control legs: the head of PR objectui#10945 answers exit 1, and `5f789538d` answers exit 0. This checkout is not shallow. - **A cross-check, not the method.** objectstack's own sweep of dead tracker citations in its tree (objectstack#19123's landing `66e266c93`, its stages `21ab41041`, `5cf58eb16` and `0d7ed5a37`, and `f415bcf18`) anchored eight of these numbers in its own files. For each of the eight (objectstack-ai#5970, objectstack-ai#6483, objectstack-ai#9934, objectstack-ai#10485, objectstack-ai#11330, #11846, #12868 and #17147) it chose the same commit this PR cites. - Every edited changeset is pending: it is present in `.changeset/` on `main`. ## Census (the enumeration pin for this batch) The 30 numbers (REF = a commit or tree): ``` git grep -nE 'objectstack#(5970|5976|6038|6124|6281|6331|6450|6483|6515|9933|9934|10354|10485|10695|11330|11507|11513|11658|11703|11753|11846|12009|12868|13117|13670|16126|17147|17762|17987|18012)([^0-9]|$)' REF -- '.changeset/*.md' 'packages/*/src/**' ':!**/__tests__/**' ':!**/*.test.*' ':!**/*.spec.*' ':!**/test/**' ':!**/tests/**' | wc -l ``` The bare `objectstack-ai#3391`, with the same pathspec: ``` git grep -nE '(^|[^0-9A-Za-z_#/])objectstack-ai#3391([^0-9]|$)' REF -- '.changeset/*.md' 'packages/*/src/**' ':!**/__tests__/**' ':!**/*.test.*' ':!**/*.spec.*' ':!**/test/**' ':!**/tests/**' | wc -l ``` | REF | the 30 | bare `objectstack-ai#3391` | |:--|:--|:--| | `3b469c8ea` (branch point) | **82**: 26 changeset lines in 24 files, 56 src lines in 39 files | **28** in 10 files | | `c292a6400` (this head) | **0** | **0** | | this head merged with `main` `5c94589f0` (`git merge-tree --write-tree`, clean, tree `bba27eece`) | **0** | **0** | | `5c94589f0` (`main` alone) | 82 | 28 | - Lit controls on the same instruments at this head: live objectstack#10856 reads 4 lines (4 at the branch point); `objectstack#3391` reads 32 lines, against 3 at the branch point (28 re-qualified here, and this PR's sweep changeset names it once). - **Out of scope, as it stands** (whole tree at this head, unfiltered): - the 30 numbers: 84 test lines in 47 files, 1 scripts line, 2 lines in 2 `apps/console` files, 2 package READMEs (`auth`, `react`), 11 lines of published `CHANGELOG.md` history in 8 files; 0 `.github`, 0 governed, 0 `content/docs`; - bare `objectstack-ai#3391`: 12 test lines in 9 files and 23 `CHANGELOG.md` lines in 8 files. ## Citation form - **An objectstack commit** is written the way batch 6 wrote its stand-ins: objectstack and the 9-character backticked sha of the commit on objectstack `main` that landed the change the sentence rests on. - **A dead card beside its own dead pull request** collapses to that pull request's squash commit (objectstack-ai#5970 with PR objectstack-ai#6450, objectstack-ai#10485 with PR objectstack-ai#10695). - **A dead number beside the live landing it already names** is dropped: #11846 beside PR objectstack#12718, #16126 beside PR objectstack#16920, #12868 beside objectstack `c459da6bc`, and #12009 beside objectstack `89448a52b` (**Special cases** 3). - **A ruling the dead card carried** is cited by its date, with the commit that executed or recorded it, as batches 3 to 6 did for objectui rulings. - **One sentence cites this repository's commit**, `7a197e7c5`, because the change it names landed here (objectstack-ai#6331). - **Runtime text carries no sha.** In the two console warnings the dead pointer is dropped (**Special cases** 8). - **The bare `objectstack-ai#3391`** becomes `objectstack#3391`, and on the two lines that write `objectstack-ai#3391/objectstack-ai#3546`, `objectstack#3391/objectstack#3546`. ## Mapping, the 30 numbers Lines / files are the branch-point census for that number (a line naming two of them counts under both). | dead number | resolution | what that commit carries | lines / files | |:--|:--|:--|:--| | objectstack-ai#5970 | objectstack `97e7e3caa` | "unify ActionSchema.visible/disabled on one condition shape (objectstack-ai#6450)", body "(objectstack-ai#5970)": `visible` gains the boolean arm | 2 / 1 | | objectstack-ai#6450 (PR) | the same `97e7e3caa`, its squash | as above; the card / PR pair collapses | 2 / 1 | | objectstack-ai#5976 (PR) | objectstack `795b6e1aa`, its squash | "5 值子集改名 `HttpMethodSubset`" | 1 / 1 | | objectstack-ai#6038 | objectstack `7618ee814` | "key a container's default `list` `_views` name by the runtime identity": leg 2 of 3 of the objectstack#5164 ruling, the `packages/lint` half | 1 / 1 | | objectstack-ai#6124 (PR) | objectstack `b3c1f3cd5`, its squash | "key `_views` translations by the runtime view identity"; "The extractor now ASKS the composer for the key" | 1 / 1 | | objectstack-ai#6281 (PR) | objectstack `85ec26d28`, its squash, 2026-08-07 | "SDUI props — enforce or remove (objectstack-ai#5775) (objectstack-ai#6281)": the shared `PageContainerProps`, whose single key is `children`, for `page:section` / `page:footer` / `page:sidebar`, which were `EmptyProps` | 1 / 1 | | objectstack-ai#6331 | objectui `7a197e7c5` | this repository's "SchemaForm reads the canonical `visibleWhen`, reviving every metadata-form predicate (objectstack#6331)" | 1 / 1 | | objectstack-ai#6483 | objectstack `ee58392e1` | "ADR-0005 白名单强制 … (objectstack-ai#6483)"; its diff carries the sentence the comment quotes, "Runtime-created sets … ride `allowRuntimeCreate` (still `true`) and keep working" | 1 / 1 | | objectstack-ai#6515 (PR) | objectstack `2fdb36eb9`, its squash | "SpecifierSchema gains a closed `valueDomain` enum": "`bcp47_locale` is deliberately not in the vocabulary", because `localization.locale`'s options ARE the shipped catalogs | 1 / 1 | | objectstack-ai#9933 | objectstack `d5552ca13` | "admit columnState as an explicitly runtime-only view-overlay key" (subject ending "(objectstack-ai#9996)"; "(objectstack-ai#9933)" is on the message's first body line), on the overlay faces including `viewItemWireFields` | 3 / 3 | | objectstack-ai#9934 | objectstack `79c46da90` | "producer-side user-facing marking for hook refusal messages — userMessage channel (objectstack-ai#9934)": `ApiErrorSchema.userMessage`, the contract half of the objectui#5210 split | 10 / 9 | | objectstack-ai#10354 (PR) | objectstack `9e04c3e35`, its squash | "let the publish door state the package it is promoting"; its changeset and code comment carry the key-presence / `no_draft` warning `ResourceEditPage.tsx` points at | 4 / 3 | | objectstack-ai#10485 | objectstack `35ad101bc` | "retire the `themes` carrier key and ThemeSchema (objectstack-ai#10485, ADR-0049) (objectstack-ai#10695)": "Ruled B (退役授权面, 2026-08-21)", "delete ui/theme.zod.ts whole" | 15 / 13 | | objectstack-ai#10695 (PR) | the same `35ad101bc`, its squash | as above; the card / PR pair collapses | 6 / 6 | | objectstack-ai#11330 | objectstack `a9ee98992` | "manifest.runtime trust-tier text states publish-gate-only enforcement truthfully", the trust-tier half (**Special cases** 2) | 1 / 1 | | objectstack-ai#11507 | objectstack `88b9d749a` | "declare sys_activity.type as an open, author-extensible vocabulary": "Maintainer ruling 2026-08-24, direction 4" | 13 / 9 | | objectstack-ai#11513 | objectstack `e170b0ae5` | "lock package-declared permission sets at the save door; clone to customize", quoting the 2026-08-24 ruling 「同意 第一步(创业阶段,Salesforce 式)」 | 3 / 3 | | objectstack-ai#11658 | objectstack `1a6a19c31` | "open RecordActivityProps.types to author-contributed activity kinds"; its message names objectstack-ai#11658 as the card it settles, and it executes the 2026-08-24 ruling | 1 / 1 | | objectstack-ai#11703 | objectstack `5cb62d88b` | "make clone_permission_set carry all five copied facets"; its message names objectstack-ai#11703 as the card it settles: the silent-grant-loss shape | 1 / 1 | | objectstack-ai#11753 | "the 2026-08-25 ruling whose spec half is objectstack `0e4e51b0a`" | `ActionParamSchema.carryOver`, whose changeset reads "(objectstack-ai#11753 ruling, spec half; #11992)" and "The maintainer's 2026-08-25 ruling on objectstack-ai#11753" | 2 / 2 | | #11846 | objectstack `0c2334f6c`; dropped beside PR objectstack#12718 | "retire preview mode — the RuntimeMode 'preview' value and the whole PreviewModeConfig block (#12718)" | 3 / 3 | | #12009 | dropped beside objectstack `89448a52b` | the card of the `AUTH_SSO_PROVIDER_SCHEMA` removal, whose landing the line already cites (**Special cases** 3) | 1 / 1 | | #12868 | dropped beside objectstack `c459da6bc` | the line already cites the commit that executed the ruling; objectstack's own `f415bcf18` anchors #12868 to the same `c459da6bc` | 1 / 1 | | #13117 (PR) | objectstack `225e7690f`, its squash | "Readiness read for the Phase-2 members … global:search and global:notifications both have shipped platform data sources, so per the ruling both STAY declared" | 1 / 1 | | #13670 | "maintainer ruling 2026-08-31, option 2, recorded in objectstack `8c6a7fc0b`" | "The #13670 ruling settled the question: text's intended evaluation channel is `content` alone" | 1 / 1 | | #16126 | dropped beside PR objectstack#16920 | PR objectstack#16920 (200) names #16126 in its body as the card it settles; merged 2026-09-08 as `859ded3ec` | 2 / 2 | | #17147 | objectstack `aaacf1d5c` | "the install-time granted permission set is REGISTERED at load and refuses nothing — say so, and pin the measurement (#17147)", the measurement on `9bd4344e4` | 2 / 2 | | #17762 | objectstack `4342c9923` | "guard three data lookups against Object.prototype fall-through"; its message names #17762 as a card it settles, `classifyFilterToken` among the three lookups | 1 / 1 | | #17987 | objectstack `e233db9db` | "declare element-level `navigation` on object-kanban / object-calendar …"; its message names #17987 as the card it settles, and its Downstream note: objectui#8652 waits on it, unlock criterion a released, installable `@objectstack/spec` (**Special cases** 1) | 2 / 2 | | #18012 | objectstack `176b03582` | "`$between` requires two non-blank endpoints (#18012)": "Ruling executed: decision batch objectstack-ai#146 item 5, **letter A**" | 3 / 3 | The 28 `objectstack-ai#3391` lines, all now `objectstack#3391`: `ObjectDataPage.tsx` (3) and `ObjectView.tsx` (2) in app-shell; `managedBy.ts` (5); `MePermissionsProvider.tsx` (2), `PermissionContext.ts`, `PermissionProvider.tsx`; `fieldWriteGate.ts`; `ImportWizard.tsx` (6), `ObjectGrid.tsx` (4); `ListView.tsx` (3). Each was read: every one names the server's effective API operation set, `/me/permissions` `apiOperations`, or the 405 import refusal. ## Special cases (the judgement calls) 1. **#17987, two sentences.** - `ObjectTree.tsx`: "blocked on objectstack#17987, whose unlock criterion is a released `@objectstack/spec` carrying the declaration being installable here" becomes "blocked on objectstack `e233db9db`, whose unlock criterion …". That commit's Downstream note states the same criterion. - `ObjectCalendar.tsx`: "that card is `pm:blocked` on objectstack#17987" becomes "that card waits on objectstack `e233db9db`". The label word is not kept, because objectui#8652's label reads `pm:on-hold` today (measured); "waits on" is the phrase `e233db9db`'s own note uses for that card. 2. **objectstack-ai#11330.** "it is objectstack#11330's half of the same panel" becomes "it is the trust-tier half of the same panel, which objectstack `a9ee98992` settled separately". `aaacf1d5c`'s message calls objectstack-ai#11330 "the sibling half of this very sentence", ruled the same way on 2026-08-30, and `a9ee98992` (2026-08-30) is that half's landing. 3. **#12009 collapses into the sha beside it.** objectui#6910's body and ruling comment `5534414562` name "objectstack#12009 / PR #13413" together as the one `AUTH_SSO_PROVIDER_SCHEMA` precedent, a card and its pull request. Batch 6 replaced PR #13413 with its squash `89448a52b`, so the card goes the way of batch 3's objectstack-ai#5401 / objectstack-ai#5505 pair. 4. **objectstack-ai#11753, two sites.** The card carried the ruling, and `0e4e51b0a` is its spec half. Both sites keep "ruling" as the antecedent that `ActionParamDialog.tsx`'s next paragraph ("The ruling's point …") reads. 5. **objectstack-ai#10354 in `ResourceEditPage.tsx`.** "since objectstack#10354 `doPublish` states" gains a comma, "since objectstack `9e04c3e35`, `doPublish` states", so two adjacent code spans do not read as one. 6. **objectstack-ai#11507 in the 8137 changeset.** "objectstack#11658 executing the maintainer's 2026-08-24 ruling on objectstack#11507" becomes "objectstack `1a6a19c31` executing the maintainer's 2026-08-24 ruling": the executing commit is named, and the ruling is cited by its date. 7. **Line breaks moved** where the stand-in is longer or shorter: `ActionRunner.ts` (two sites), `ActionParamDialog.tsx`, `theme.ts`, `theme.zod.ts` (two sites), `index.zod.ts` and the metadata-admin `i18n.ts` comment, where "ruling on" became "ruling of 2026-08-24,". 8. **The runtime strings.** Only the listed text moves. | file | member | before | after | |:--|:--|:--|:--| | `app-shell/src/layout/activityItemType.ts` | the `console.warn` in `warnUnmappedActivityType` | "… `sys_activity.type` is author-extensible (objectstack#11507, ruled 2026-08-24) and is not validated on write …" | "… `sys_activity.type` is author-extensible (ruled 2026-08-24) and is not validated on write …" | | `plugin-detail/src/renderers/recordActivityFeed.ts` | the `warnOnce` message in `warnUnknownActivityType` | "… `sys_activity.type` is author-extensible (objectstack#11507, ruled 2026-08-24) and is not validated on write …" | "… `sys_activity.type` is author-extensible (ruled 2026-08-24) and is not validated on write …" | No test, doc or changeset quotes either message with the pointer: the census reads 0 in `.changeset/`, and the anchor sweep finds no test literal that drops. 9. **`objectstack-ai#3391/objectstack-ai#3546`.** On the two lines that pair them (`managedBy.ts`, `ObjectGrid.tsx`), both halves are qualified, as batch 6 qualified both halves of "#13337/#13086". The other bare `objectstack-ai#3546` lines are not in this batch's lists and are left (**Acceptance notes** 2). ## The literal-anchor sweep (both test-pin classes, ruling `5861900779`) - **Instrument.** Every string, template and regex literal in all 4073 tracked test and script files (106544 distinct literals), read with the TypeScript scanner. - **Candidate filter.** A literal is a candidate if it matches the diff's removed lines with two lines of context, raw or comment-flattened: 1983. - **Test.** Does its occurrence count DROP between `3b469c8ea` and `c292a6400` in any of the 74 changed files, raw or comment-flattened? 136 do. - **Every one is generic:** digits, punctuation, single words ("object", "blocked", "locked"), character classes, and two regexes that read no changed file: `/objectui#\d+|objectstack#\d+/` in `registry-inputs-spec-parity`, which asserts over its own ledger's reasons, and the older spelling of the three submitRedirect tests' ruling matcher, quoted in their own doc comments (the live `CITES_ITS_RULING` asserts over their own refusal text). None is a changed phrase, a dead number or a changed warning. ## Held **By the serial rule: nothing.** Open PRs were mapped at branch time (9 open) and again after the push, before this PR opened (11 open). The second mapping came after the push, not before it; the same three files were shared both times. Three open PRs share a file with this PR: - _Both PRs below have merged since this PR opened (objectui#10945 as `06a96e948`, objectui#10908 as `b45d463a9`). The trial merge with today's `main` is clean, and both censuses read 0 on it (contract review `5870922323`), so nothing is owed. The two rows are kept as the record at the time._ - **objectui#10945, `RecordDetailView.tsx`.** The blob at its merge-base equals the branch point's. Its hunks are the imports and one block far below; this PR's one changed line in that file is far from both. - **objectui#10908, `types/src/zod/index.zod.ts`.** Its one insertion is in the export list, far below this PR's two changed comment lines. - **objectui#10278, `plugin-grid/src/ObjectGrid.tsx`.** The file drifted between its merge-base and the branch point, so this PR's four changed lines were mapped onto its merge-base by a line alignment: the nearest of its hunks is more than 150 lines from any of them. Trial merges with this head (`git merge-tree --write-tree`): - clean for objectui#10952, objectstack-ai#10950, objectstack-ai#10949, objectstack-ai#10947, objectstack-ai#10945, objectstack-ai#10944, objectstack-ai#10930, objectstack-ai#10908 and objectstack-ai#10777; - objectui#10278 conflicts in `ObjectGrid.tsx`, `plugin-grid/README.md` and `content/docs/plugins/plugin-grid.mdx`, and conflicts in the same three files against `main` alone; - objectui#5400 (Version Packages) regenerates and is not a hold. `.changeset/9954-read-rate-banner.md` is held by this seat's objectui#10913 dispatch (PR objectui#10949) and is untouched here. It carries none of this batch's numbers. ## Changesets - `.changeset/10803-dead-citation-sweep-seventh-batch.md`, EMPTY frontmatter. It covers the comment-only edits in 17 released packages; no published behaviour changes through them. It points at the second file for the runtime text. - `.changeset/10803-seventh-batch-runtime-strings.md`, `'@object-ui/app-shell': patch` and `'@object-ui/plugin-detail': patch`: the two warnings lose their pointer. What renders, and when and how often each warning fires, are unchanged. ## Proof of prose-only (C4), against `3b469c8ea` - **Source.** Each of the 48 touched `.ts` / `.tsx` files was parsed at `3b469c8ea` and at this head with TypeScript 6.0.3's `createSourceFile`, and re-printed by `createPrinter({ removeComments: true })`. - 46 of 48 prints are identical. - `activityItemType.ts` and `recordActivityFeed.ts` are equal once the one listed substitution each (**Special cases** 8) is applied to the base print, each matched once. - 0 parse diagnostics. - Lit controls on the same instrument: editing a string literal moves the print; re-spacing a comment does not. - **Changesets.** The frontmatter block of every one of the 24 edited changesets is byte-identical at `3b469c8ea` and this head (24 of 24, md5). The overwrite gate below agrees. - **Scope of the diff:** 74 files, +157 / −115: 24 edited and 2 new changesets, and 48 non-test source files in 17 released packages. No test file. ## Gates, on this head `c292a6400` Each line is the gate's own verdict and exit code, captured by redirect-then-`$?`. - `node scripts/check-changeset-presence.mjs`, exit 0: "48 source file(s) of 17 released package(s) changed, and this change declares 2 changeset(s): .changeset/10803-dead-citation-sweep-seventh-batch.md, .changeset/10803-seventh-batch-runtime-strings.md." - `pnpm changeset:check`, exit 0: "All workspace packages are in the changeset fixed group." / "No changeset declares a `major` bump." - `node scripts/check-changeset-overwrite.mjs` (report-only), exit 0: "2 changeset(s) added, 24 modified, 0 deleted". `declared at base` equals `declares now` for each of the 24. - `pnpm check:changeset-claims` (report-only), exit 0: - "Every one of those 1 address(es) either names the tree it was read from, or points at a line this change does not move"; - "Every package declared across those 22 body(ies) is either not negated …"; - the standing notice "87 pending changeset(s) describe a file this change touches". Read against the diff: a pending changeset quoting a replaced pointer would itself carry a dead number and sit in the census, which reads 0. - `pnpm check:control-bytes`, exit 0: "check-control-bytes: OK (scanned 9229 tracked text file(s); skipped 85 binary)." A `grep -P` control-byte self-scan of the 74 files finds none. - `pnpm check:new-line-citations`, exit 0: "VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0". - `pnpm check:pending-changeset-literals`, exit 0: "No test source names a pending changeset." - Also run over the touched comments: - `pnpm check:spec-symbols`, exit 0: "spec member citations: 1421 sources + 184 documentation pages; nothing cites a key its spec symbol does not declare."; - `pnpm check:installed-pin-claims`, exit 0 ("OK"); - `pnpm check:comment-mask-corpus`, exit 0 (1 disagreeing file, within the ceiling objectui#7882 holds open); - `node scripts/check-hand-rolled-comment-mask.mjs`, exit 0 ("OK every carrier is a DEBT entry, and every DEBT entry still carries one."); - `pnpm check:handler-key-reads`, exit 0 ("every judged read is a declared member of it"). - The governed-surface predicate over the 74 paths, exit 0: "NOT GOVERNED — 74 path(s) checked against 5 governed surface(s); none matched." Lit control `AGENTS.md`: exit 3. **Tests and type-check**, through the shared verify lock, on `c292a6400`. Each is `VERDICT command-exit 0`. - `scripts/__tests__/`, the whole directory, whose whole-tree scanners read the touched files and changesets: `Test Files 177 passed | 2 skipped (179)`, `Tests 5332 passed | 2 skipped (5334)`. The two skipped files are the network-escape fixtures that run only as a child. - `packages/types/`, `core/`, `react/`, `i18n/`, `providers/`, `permissions/` and `data-objectstack/`, whole packages, in one run: `Test Files 704 passed (704)`, `Tests 13170 passed | 13 skipped (13183)`. - The eight touched plugin packages (calendar, designer, detail, form, grid, kanban, list, tree): `Test Files 787 passed | 1 skipped (788)`, `Tests 7521 passed | 27 skipped (7548)`. - `packages/components/`: `Test Files 324 passed | 1 skipped (325)`, `Tests 3148 passed | 24 skipped (3172)`. - `packages/app-shell/`: `Test Files 854 passed | 1 skipped (855)`, `Tests 8789 passed | 9 skipped (8798)`. - Type-check: `turbo run build` of the 28-package dependency closure (`Tasks: 28 successful, 28 total`), then `pnpm --workspace-concurrency=2` with the 17 package filters `run type-check`: 17 script echoes, 17 `Done`. A first attempt before the build exited 2 on an unbuilt dependency (`Cannot find module '@object-ui/types'`) and measured nothing. - No red leg: the sweep found no anchor to move, so there is no pin whose old copy should fail. CI on `c292a6400`: 43 check-runs, 40 success, 3 skipped, 0 failed; `Spec Main Shape Gate` success. ## Acceptance notes 1. **Dead objectstack numbers written bare: 10 more lines in the same two classes.** A bare number resolves to this repository, where each of these is a live, unrelated card, so no `objectstack#` census sees them. - **Measurement.** The bare-number instrument of PRs objectui#10875 / objectstack-ai#10892 / objectstack-ai#10914 reads 965 distinct numbers at this head. The 916 between 100 and 25000 were each read once as objectstack issues: 877 answer 200 and 39 answer 404. Reading the sentences of those 39, three mean an objectstack card or pull request (below); the other 36 cite objectui cards or objectui pull requests. - **objectstack-ai#9934**, 7 lines in 7 files: `.changeset/7980-agent-key-envelope-read.md`, and in app-shell `index.ts`, `apiErrorEnvelope.ts` (2), `PackageFormDialog.tsx`, `StudioDesignSurface.tsx` and `packages-io.ts`. All mean the `userMessage` channel, objectstack `79c46da90`. - **"PR objectstack-ai#6281"**, 2 lines in `containers.tsx`, beside objectstack#5775. The landing is objectstack `85ec26d28`. - **"objectstack PR objectstack-ai#8452"**, 1 line in `useRecordCrudVerdicts.ts`. The landing is objectstack `27358d517` ("add batch recordIds to security/explain (objectstack-ai#8326) (objectstack-ai#8452)"). - None of them sits in a sentence this PR edits, so they are outside this batch's lists and left. Carrier: this card, triage item 3. 2. **The rest of the bare `objectstack-ai#3546` population.** Five more comment lines write objectstack#3546 as a bare `objectstack-ai#3546` (`RecordDetailView.tsx` 2, `RelatedRecordActionsBridge.tsx`, `record-details.tsx`, `fieldWriteGate.ts`), and one writes it as `objectui#3546` (`plugin-detail`'s `index.tsx`). Each names the server's effective API operation set on a detail or form surface. It is live-but-wrong, not a 404, like the `objectstack-ai#3391` class this batch closed. Carrier: none. 3. **A stale label in a comment.** The `ObjectCalendar.tsx` sentence said objectui#8652 is `pm:blocked`; that card reads `pm:on-hold` today. **Special cases** 1 says how the repaired sentence avoids the label. 4. **Filenames are not citations.** `.changeset/17147-plugin-disclosure-not-enforced.md` carries one of the 30 numbers in its name; it stays, as in PRs objectui#10707, objectstack-ai#10797, objectstack-ai#10854, objectstack-ai#10869, objectstack-ai#10875, objectstack-ai#10892 and objectstack-ai#10914. --- _Generated by [Claude Code](https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk)_ _Tests block completed by the `domain:ui` seat objectstack-ai#1 from the dev report `5870507620` (the suites that finished after this PR opened), and three figures corrected after contract review `5870922323` (the objectstack-ai#9933 and objectstack-ai#6515 quotes, and the Held rows); no code claim moved._ Co-authored-by: Claude <noreply@anthropic.com>
Fixes #10909
Clause-②: yes (widening)
Implemented on branch
claude/issue-10909-element-number-datasourceby theos-devrun dispatched from thedomain:uiseat 2 sessionsession_014mXUNuFomfj24w7s1pZzhN(claim comment 5866532141 on the card).What changed
ElementNumberRenderer(packages/components/src/renderers/basic/elements.tsx) now reads the spec's per-element binding (PageComponentSchema.dataSource) throughuseElementDataSource, the way its element twinelement:record_pickerdoes.dataSource.object ?? properties.object, and that one value is the fetch guard, theadapter.aggregate/adapter.findtarget and theuseDataInvalidationkey.dataSource.objectwins when both are set.properties.object.properties.filteris AND-combined with the binding's filter and with its view's. The lowering and merge are the onesElementDataSourceGateuses (toFilterNodeSafely+mergeFilterNodes), so neither filter is dropped. A merge the converter refuses shows the configuration-error panel and aggregates nothing. With no binding,properties.filterreaches the adapter exactly as authored.element:numberis registered throughelementDataSourceBlock(...)(from@object-ui/core), soRegistry.registerinjects the one shareddataSourcedeclaration (ELEMENT_DATA_SOURCE_INPUT,binding: 'object').objectis no longerrequired: true.objectinput's description states its precedence.filterinput's description is exactly: "Criteria the aggregate is scoped by. When a node-leveldataSourcebinding also supplies a filter (its own, or the saved view itsviewnames), the two are AND-combined: neither is dropped."packages/components/src/renderers/basic/__tests__/elementNumber.dataSourceBinding-10909.test.tsx(18 rows). It is registered as theelement:number.dataSourcemember pin inapps/console/src/__tests__/registry-inputs-spec-parity.test.ts, which the parity census requires once the block declares an object-armed input.content/docs/guide/data-source.mdgains theelement:numberrow in the per-element binding table. A paragraph says how the twoelement:*rows read the binding, and that they differ onfilter:record_pickersubstitutes,element:numberAND-combines..changeset/10909-element-number-datasource.md,'@object-ui/components': minor, withClause-②: yes (widening).H1: reproduced on
main(733fd5a)The probe used the real
SchemaRenderer, the real registry, and anAdapterCtxadapter withaggregate/findspies. The probe file was temporary and not committed.The same probe on this branch:
ElementDataSourceSchemamembers: which ones this renderer readsobjectdataSource.object ?? properties.object. There is no object at all while a named view is unresolved or unresolvable. A binding naming no object (empty or not a string) supplies nothing, and the flat object stands.viewuseElementDataSource: the saved view'sfilterscopes the aggregate. While the view resolves, the shared loading panel shows and nothing is read. If it cannot be resolved, the shared error panel (element-number-datasource-error) shows and nothing is aggregated.filterproperties.filterAND (view filter AND binding filter), lowered and merged asElementDataSourceGatedoes. Neither is dropped; a refused merge reports and aggregates nothing. Context tokens are handled by the existinguseResolvedFilteron the merged value. There is no record-context binding (objectui#7297 stays on hold), and the filter-shape note (objectui#8945) is untouched.sortaggregate()orfind(), and the whole options bag is asserted.limitpagination.pageSizereachesaggregate()or thefind()fallback.columns/viewType(view-only composed keys)No member is declared per member. The only declaration is the injected
dataSourceinput (type: 'object',binding: 'object'), whose description lists the binding's five members generically. Which of them this block reads is pinned at the read site.The registration change: what
elementDataSourceBlockchanges, exactlyelementDataSourceBlockis a marker. It adds the renderer to aWeakSetand returns it unchanged, so it adds no gate, no loading state and no fetch. The declaration is emitted bywithElementDataSourceInputinsideRegistry.register. Measured on the live registry:object,aggregate!,field,filter,format,prefix,suffix,dataSource(!= required). On base,object!carried the required mark and there was nodataSource;validateTreeon{ type: 'element:number', dataSource: { object: 'contact' }, aggregate: 'count' }returns no diagnostic. On base it returnedis missing required prop "object"andhas no prop "dataSource".The render-path changes come from
useElementDataSourceand from the filter merge, and they reach only a node that carries a binding:viewreads saved views and shows the shared loading panel while it resolves;view, or a filter refused in either merge, shows the shared error panel;dataSourceresolvesabsentsynchronously, so thepropertiesform issues no new read, shows no new panel, and sends its filter unlowered. This is pinned: the control row assertsgetObjectSchemais never called.Pins: red before, green on head
File:
elementNumber.dataSourceBinding-10909.test.tsx, 18 rows. The file was byte-identical in all three runs, and each base run used a temporary detached worktree that was removed afterwards.Tests 18 passed (18)Tests 16 failed | 2 passed (18)— the 2 green rows are theproperties-form control and the empty-objectbinding rowTests 6 failed | 12 passed (18)The 6 rows red on the previous head:
Rows:
element:textstill reportshas no prop "dataSource").Ablations at 37bc45f, through
objectstack/scripts/ablation-replace.mjsin wrap mode with its trap-armed restore. Each was run over the pin file plus the three pre-existingelement:numberfiles (elementNumberFilterMembers-8071,elementReaders.invalidationRefetch-10623,element-number.contractEnvelope-6726), 33 rows in all. Each restore printedok restored: blob == HEAD (3ab0d9a745a4) and git diff HEAD is empty.(composed?.object ?? props.object)becomes(/* ablation-10909 */ props.object); blob3ab0d9a745a4to875d8dfa0e4a11 failed | 22 passed (33): rows 1, 2, 4, 6–10, 12, 15 and 16, the dataSource-form rows that reach the resolution linemergeFilterNodes(own.node, bound.node)becomes(/* ablation-10909-and */ bound.node ?? own.node), the rule A semantics; blob3ab0d9a745a4to2a9bf06af87c2 failed | 31 passed (33): exactly the two AND rows, 9 and 10unresolved || filterRefusal ? undefined :becomes/* ablation-10909-guard */; blob3ab0d9a745a4toc249c85fa79b2 failed | 31 passed (33): exactly row 11 (refused merge) and row 14 (unresolved view beside a flat object)Census: element renderers and the
dataSourcebindingEvery
element:*registration is in@object-ui/components, and no other package registers one. The census probe rendered each registered element withdataSource: { object: 'contact' }and no flat object:dataSource?dataSourceelement:numberobject)aggregate(contact)element:record_pickerobject)find(contact)element:repeaterobject, required)has no prop "dataSource"and the missingobjectelement:metadata_viewerhas no prop "dataSource"element:text,element:divider,element:image,element:button,element:text_input,element:definition-listhas no prop "dataSource"element:formandelement:filterhaveComponentPropsMaprows but no objectui renderer.element:repeateris not a spec block: it has noPageComponentTypemember and noComponentPropsMaprow. Its declaration and its runtime agree, and the html tier reports the key, so this is an observation, not a finding.Consumer sweep
The file set is the
git grephits forelement:number,ElementNumberRenderer,elementDataSourceBlock,withElementDataSourceInputandELEMENT_DATA_SOURCE_INPUTin tests, plus:dataSourcedeclaration suites;element-data-source-input-injectionrender probe;record_pickerdata-source and malformed-filter pins;Fix round 1 adds the gate-family filter-merge tests this change reuses:
ElementDataSourceGate.malformedFilter-10789,ElementDataSourceGate.test, corefilter-source-merge.test, andObjectMetric.elementDataSource.test.At 37bc45f, 34 files, run under the shared verify lock:
Test Files 34 passed (34)·Tests 983 passed (983).The full
@object-ui/componentssuite ran at 75f027e (before this round) in three directory runs: 326 collected files, all green. This round changes onlyelements.tsxand its pin file inside that package. Both are covered above by the directories' ownelement:numberfiles and by the sweep.Gates
At 37bc45f. Each verdict is the gate's own line.
pnpm --filter @object-ui/components type-check(tsc --noEmit && tsc -p tsconfig.test.json), afterpnpm --workspace-concurrency=2 --filter '@object-ui/components^...' build(8 of 47 projects): exit 0.pnpm check:element-data-source-declaration:OK — 14 gate-consuming file(s) checked, 3 definition file(s) excluded; all reach the seam and take it from core.node scripts/check-changeset-presence.mjs:3 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s): .changeset/10909-element-number-datasource.md.node scripts/check-changeset-no-major.mjs:No changeset declares a major bump.The frontmatter isminor.pnpm check:changeset-claims: report-only. The same three pending changesets name touched files (6726-find-envelope-records-arms,6783-readprops-degenerate-config-bag,8067-component-input-member-kind); each paragraph is still true.pnpm check:control-bytes:OK.pnpm check:new-line-citations:VERDICT new-cross-file-line-citations: 0 new citation(s).node scripts/check-doc-links.mjs:Links are valid across 17 scan roots.--no-inline-config --format jsonover the three changed source and test files):ESLint.isPathIgnoredanswersfalsefor all three.elements.tsxhas 24 warnings, the same per-rule split as base; the other two files have 0.parserOptions.projectorprojectService), and the one custom rule reads no file from disk, so untouched files cannot move.check:handler-key-reads,check:designer-field-key-parity,check:component-surface-parity,check:unreferenced-sources,check:test-path-roots,check:vi-mock-specifiers,check:registry-bare-names,check:i18n-keys,check:doc-types,check:doc-fences,check:doc-example-idsandcheck:spec-symbolswere green at 75f027e.apps/consoletype-check. Reason: its program needs every plugin's built declarations, and that closure build is beyond the foreground cap. The CIType Checkjob owns it. The console edit is oneMEMBER_PINSentry with two string fields.check:sdui-registration-pins. Reason: PREREQUISITE, no console bundle (exit 2). Theperformance-budgetworkflow runs it.check:doc-snippetsandcheck:doc-examples. Reason: PREREQUISITE, unbuilt packages (exit 2). The guide edit adds no fenced snippet.Serial
packages/components/src/renderers/basic/, the parity suite orcontent/docs/guide/data-source.md. Positive control: PR objectui#10908'spackages/types/files match the same grep.packages/coreis unchanged; this change only imports its existingtoFilterNodeSafelyandmergeFilterNodes.git merge-tree --write-treeagainst a freshorigin/main5c94589, fetched into a private ref: exit 0, tree4c51202bf5. Main moved 20 commits since the base, and none of them touches the renderer, the parity suite, the guide,useElementDataSource,useResolvedFilter, the gate, the core data-source module,filter-converter,Registry.tsorvalidate.ts.git merge-tree --write-treeagainst PR objectui#10908's head fe8f6a6: exit 0, tree4bb57ac81c.Fix round 1
This responds to the contract review FAIL at 48d755d, on text and one seat ruling. Object resolution, the unchanged properties form, the registration, the census and the pins held.
toFilterNodeSafely+mergeFilterNodes.properties.filter; a refused merge taking the error panel with no aggregate call.filterdescription is the ruling's text verbatim. The guide paragraph and the changeset bullet say AND-combined.record_pickeris untouched.minor;Clause-②: yes (widening)text verbatim;objectui validatewill once objectui#10908 arms the type";Clause-②: yes (widening).dataSource: { object: 'contact', view: 'no_such_view' }besideproperties.object: 'contact'asserts no aggregate call and the error panel. The guard ablation reddens exactly that row and the refused-merge row.MEMBER_PINSprose: rewritten for rule B. It no longer says the declaration carries "nothing about members"; it says the injected description lists the five members generically and the pin carries which of them this block reads.Acceptance notes
{ dataSource: { object } }, and PR objectui#10908's arm (which acceptsproperties.filterbeside a binding filter or view) now matches a renderer that honours both.element:numbermanifest entry declaresdataSource, andobjectis no longerrequired. The html tier, and the objectstack CLI's JSX gate that reads the manifest, now also accept a node that names neitherobjectnor a binding, which paints the empty dash. That case is still reported on the JSON door by the spec lint gate (advisory), and after PR objectui#10908 by its arm when a bag is present. A runtime "no object named" notice, or a one-of check at the html tier, would be a follow-up card. Precedent: objectui#10508 made the same trade on calendar and ganttobjectName.aggregate()/find(), asElementDataSourceGatedoes forobject-metric. Without a binding,properties.filteris sent as authored, as before.element:numberstays properties-only and offers nodataSourceeditor.record_pickershares the wording. This is a review nit, and the text is unchanged here.Generated by Claude Code