diff --git a/.changeset/23434-component-read-points-part-3.md b/.changeset/23434-component-read-points-part-3.md new file mode 100644 index 00000000000..44e5bc119af --- /dev/null +++ b/.changeset/23434-component-read-points-part-3.md @@ -0,0 +1,13 @@ +--- +'@objectstack/spec': patch +--- + +Component-member read points, part 3 of 3, plus the two held-back moves and the other citation spellings: `packages/spec/src` no longer ties a live read point to an objectui commit or to the console pin. + +Clause-②: no + +- **Component-member tests and the other group-C files.** `component.test.ts`, the `component-*.pin.test.ts` family, the record-block tests, `expression-bindable-text-keys.zod.ts`, `page.zod.ts` and `inline-grid-column-carriers.test.ts` now point at `liveness/page.json`, row `regions`. The evidence list there is the same one parts 1 and 2 carry. `content/docs/references/ui/expression-bindable-text-keys.mdx` is regenerated from the shortened comment. +- **The two held-back moves.** The `measures.format` record in `src/ui/dataset.zod.ts` moves into `liveness/dataset.json`, row `measures.format`. The `enable.apiMethods` record in `api-methods-batch-conformance.test.ts` moves into `liveness/object.json`, row `enable.apiMethods`. +- **Other spellings.** Live read points written as `objectui SHA … File.tsx:NNN`, or sha-less as "at the `.objectui-sha` pin", move into the note of their row in `field.json`, `action.json`, `app.json`, `dashboard.json`, `view.json` or `page.json`, each as `objectui @SHA FILE#SYMBOL`. +- **Unchanged on purpose.** Retirement history stays where it is: the ADR-0087 conversion entries, the v18 migration entries and the retirement pins. A citation with no matching ledger row also stays, and is listed on the pull request. +- No schema, type, export, default, accept or refuse change. No `.describe()` text changed. diff --git a/content/docs/references/ui/expression-bindable-text-keys.mdx b/content/docs/references/ui/expression-bindable-text-keys.mdx index 1cc31b33366..b5210c6ae19 100644 --- a/content/docs/references/ui/expression-bindable-text-keys.mdx +++ b/content/docs/references/ui/expression-bindable-text-keys.mdx @@ -51,16 +51,15 @@ renderer happens to read. ## Why these rows (measured, not inferred) Rows are a RECORD of what each component renderer already reads back from -the node's top level, measured at the `.objectui-sha` pin `82a9417` -(re-verified identical at objectui `origin/main` `6c68b13` on -2026-08-18) — declaring a key a renderer does not read back would recreate +the node's top level (read points recorded in `liveness/page.json`, row +`regions`) — declaring a key a renderer does not read back would recreate the evaluated-but-blank half of the objectui#4795 table: - | type | renderer read points (top level, within the closed set) | - |-------------|----------------------------------------------------------| - | `statistic` | `schema.label`, `schema.value`, `schema.description` (`data-display/statistic.tsx`) | - | `card` | `schema.title`, `schema.description` (`layout/card.tsx`) | - | `button` | `schema.label` (`form/button.tsx`) | + | type | renderer reads (top level, within the closed set) | + |-------------|---------------------------------------------------| + | `statistic` | `schema.label`, `schema.value`, `schema.description` | + | `card` | `schema.title`, `schema.description` | + | `button` | `schema.label` | These are the measured motivating cases from objectui#4795 (dashboard workhorses). Other registered renderers also read keys from this closed set @@ -69,7 +68,7 @@ at the top level (`alert`/`empty`/`dialog` `title`+`description`, `badge` declared yet: form-control `value` is interactive state rather than display text, and each row is an accept-surface widening that should arrive with its own measurement, not ride this one (startup scope discipline). `text` reads -back `schema.content || schema.value` (`basic/text.tsx`) — unlike the rows +back `schema.content || schema.value` (the same ledger entry) — unlike the rows above, its omission is not merely unmeasured: `value` there is a fallback spelling for the same slot `content` already evaluates (see `content` NOT a member, above), not a second read point, so a `text: ['value']` row would diff --git a/packages/spec/liveness/action.json b/packages/spec/liveness/action.json index a8189c08bbe..b9f265a2934 100644 --- a/packages/spec/liveness/action.json +++ b/packages/spec/liveness/action.json @@ -48,7 +48,7 @@ "verifiedAt": "2026-09-08", "evidenceScope": "in-repo", "evidence": "packages/runtime/src/action-execution.ts#isDeclarativeUpdateAction (the executor's own discriminator — a bare `action?.operation === DECLARATIVE_UPDATE_OPERATION`, deliberately with no `type` clause so data at rest whose `type` contradicts its `operation` is still routed by `operation`); packages/runtime/src/domains/actions.ts#handleActionsRequest (the REST `/actions` door: the branch runs ahead of the `type` switch and ahead of the trusted-mode plumbing, and hands off to `executeDeclarativeUpdateAction`); packages/runtime/src/action-execution.ts#invokeBusinessAction (the MCP `run_action` door takes the same branch into the same executor — one implementation, two doors); packages/runtime/src/action-execution.ts#isHeadlessInvokableAction (asked FIRST, so a declarative update is listed as invokable although it carries neither `target` nor `body`); packages/runtime/src/action-execution.ts#headlessActionTypeError (asked FIRST, so the key suppresses the `type` prescription that would otherwise be wrong for it); packages/runtime/src/action-execution.ts#summarizeAction (the MCP listing face projects the declared `operation` and forces `requiresRecord`); packages/spec/src/ui/action.zod.ts#refuseDeclarativeUpdateContradictions (authoring-time: every executor-binding key beside `operation: 'update'` is refused at its own path, and `patch` without it); packages/spec/src/stack.zod.ts#collectGlobalUpdateActionErrors (defineStack refuses a standalone `operation: 'update'` action with no `objectName`)", - "note": "The discriminator of the declarative single-record field write — one member, `'update'`; `'delete'`/`'custom'` are refused with the reason. FLIPPED `planned` -> `live` 2026-09-08 (#15080) on the runtime half #15079 (PR #15448, merged 2026-09-04): the key now decides dispatch at both server doors, and it is read BEFORE `type` at every one of them — contract point 1 of the executor contract PR #15077 pinned here, and a rule about ORDER, not merely a new branch. Declared by the #14092 maintainer ruling (2026-09-01): the row-level counterpart of a list view's `bulkActionDefs` `operation: 'update'`, spelled with the same words. The EXECUTOR CONTRACT is now honoured in tree rather than promised: the write is a single data-plane update of the CURRENT record executed AS THE CALLER under `wiring.ec` and never `buildActionExecutionContext` (the #14010 `runAs: 'user'` direction), so object/row/field permissions, hooks and validations fire as for a user edit and a caller who cannot read the row is refused (the class commit f19475c0a closed); `patch` merges UNDER the collected `params`; `undoable` captures the prior values of exactly the fields written; an action with no current record is a located 400, never a silent no-op. Pinned end-to-end in packages/runtime/src/action-declarative-update.test.ts, one describe per contract point plus a reverse pin that a handler-less `type: 'script'` action WITHOUT `operation` is unchanged. `evidenceScope: in-repo` is exact and deliberate: this verdict rests on the framework-side readers alone. The console half (objectui#7551) is a SECOND reader and the flip did not wait for it — objectui's `ActionRunner` still dispatches on `type` — so a console-side forward/undo pointer is a later widening of this row's scope, not a precondition of its verdict." + "note": "The discriminator of the declarative single-record field write — one member, `'update'`; `'delete'`/`'custom'` are refused with the reason. FLIPPED `planned` -> `live` 2026-09-08 (#15080) on the runtime half #15079 (PR #15448, merged 2026-09-04): the key now decides dispatch at both server doors, and it is read BEFORE `type` at every one of them — contract point 1 of the executor contract PR #15077 pinned here, and a rule about ORDER, not merely a new branch. Declared by the #14092 maintainer ruling (2026-09-01): the row-level counterpart of a list view's `bulkActionDefs` `operation: 'update'`, spelled with the same words. The EXECUTOR CONTRACT is now honoured in tree rather than promised: the write is a single data-plane update of the CURRENT record executed AS THE CALLER under `wiring.ec` and never `buildActionExecutionContext` (the #14010 `runAs: 'user'` direction), so object/row/field permissions, hooks and validations fire as for a user edit and a caller who cannot read the row is refused (the class commit f19475c0a closed); `patch` merges UNDER the collected `params`; `undoable` captures the prior values of exactly the fields written; an action with no current record is a located 400, never a silent no-op. Pinned end-to-end in packages/runtime/src/action-declarative-update.test.ts, one describe per contract point plus a reverse pin that a handler-less `type: 'script'` action WITHOUT `operation` is unchanged. `evidenceScope: in-repo` is exact and deliberate: this verdict rests on the framework-side readers alone. The console half (objectui#7551) is a SECOND reader and the flip did not wait for it — objectui's `ActionRunner` still dispatches on `type` — so a console-side forward/undo pointer is a later widening of this row's scope, not a precondition of its verdict. The console's runner reads `operation` before any executor (packages/spec/src/ui/action-declarative-update.ts; moved out of spec source, #23434): objectui @b9814b334 packages/core/src/actions/ActionRunner.ts#execute (isUpdateOperationAction) posts the write through its registered `script` dispatch and runs no executor of its own, whatever executor the action or row names; byte-identical at objectui main 524136e." }, "patch": { "status": "live", @@ -68,7 +68,7 @@ "status": "live", "verifiedAt": "2026-08-28", "evidence": "packages/runtime/src/action-execution.ts#dispatchFlowAction (type:'flow' server dispatch — automation.execute(action.target, …) runs inside it, after flowIsUnknown rejects an unrecognized flow name by that same value); packages/runtime/src/action-execution.ts#headlessActionTypeError (names the target the client-dispatched types go to instead)", - "note": "URL/script/flow/endpoint + ${param}/${ctx} interpolation. 2026-08-25: REPOINTED — the evidence cited packages/runtime/src/http-dispatcher.ts, which has 0 occurrences of `target`; the action consumer was extracted into domains/actions.ts + action-execution.ts and http-dispatcher.ts now only delegates. The file still existed and the citation carried no line, so nothing in the gate could see it. 2026-08-28: RE-ANCHORED (#12516) — the 08-25 repoint wrote line numbers (:725/:718/:472) and all three drifted IN RANGE within a day (the lines now hold the FlowActionRefusal brand block and disabledActionRefusal's docblock); the citation now anchors the consuming symbols, which move with the consumer." + "note": "URL/script/flow/endpoint + ${param}/${ctx} interpolation. 2026-08-25: REPOINTED — the evidence cited packages/runtime/src/http-dispatcher.ts, which has 0 occurrences of `target`; the action consumer was extracted into domains/actions.ts + action-execution.ts and http-dispatcher.ts now only delegates. The file still existed and the citation carried no line, so nothing in the gate could see it. 2026-08-28: RE-ANCHORED (#12516) — the 08-25 repoint wrote line numbers (:725/:718/:472) and all three drifted IN RANGE within a day (the lines now hold the FlowActionRefusal brand block and disabledActionRefusal's docblock); the citation now anchors the consuming symbols, which move with the consumer. The url-target scheme set's runner half (packages/spec/src/ui/navigation-target.ts and its pin action-url-target-scheme-set.test.ts; moved out of spec source, #23434): objectui @7578323 packages/core/src/actions/navigationTarget.ts#classifyNavigationTarget, which the action runner and the entitlement CTA both call; the runner's own pinned cases, mirrored by the spec test, are objectui @7578323 packages/core/src/actions/__tests__/navigationTarget.mailto-12191.test.ts." }, "body": { "status": "live", diff --git a/packages/spec/liveness/app.json b/packages/spec/liveness/app.json index 93e0bb8f9a9..d59304eafba 100644 --- a/packages/spec/liveness/app.json +++ b/packages/spec/liveness/app.json @@ -160,7 +160,7 @@ "status": "live", "verifiedAt": "2026-08-01", "evidence": "objectui @940ba24: packages/layout/src/NavigationRenderer.tsx:294-296, :418", - "note": "target precedence recordId → filters → viewName; also keys the view-label i18n lookup." + "note": "target precedence recordId → filters → viewName; also keys the view-label i18n lookup. With no `viewName` (packages/spec/src/ui/app.zod.ts, ObjectNavItemSchema; moved out of spec source, #23434): the entry links to the bare object route, where objectui @b9814b334 packages/app-shell/src/views/ObjectView.tsx#activeViewId opens `defaultViewId || views[0]`, the view objectui @b9814b334 packages/app-shell/src/views/ObjectView.tsx#buildViewTabs marks default, and the `all` tab that function adds only for an object with no list view (#21973); first read at objectui @0abd4f9f8769 (the same two symbols), re-read 2026-10-15." }, "recordId": { "status": "live", diff --git a/packages/spec/liveness/dashboard.json b/packages/spec/liveness/dashboard.json index a12e1ffb848..46f33a6218b 100644 --- a/packages/spec/liveness/dashboard.json +++ b/packages/spec/liveness/dashboard.json @@ -221,7 +221,7 @@ "status": "live", "verifiedAt": "2026-08-03", "evidence": "objectui @91757a7: packages/plugin-dashboard/src/DashboardRenderer.tsx:439 — spread into every component schema; objectui: packages/plugin-dashboard/src/DatasetWidget.tsx:190-191 — declared query keys lowered into the DatasetSelection", - "note": "the renderer-extras bag; its four DECLARED keys (dateGranularity / sortBy / sortOrder / limit, framework#3588) reach the analytics query, the rest pass through to the renderer." + "note": "the renderer-extras bag; its four DECLARED keys (dateGranularity / sortBy / sortOrder / limit, framework#3588) reach the analytics query, the rest pass through to the renderer. The #20960 census (packages/spec/src/ui/dashboard.zod.ts; moved out of spec source, #23434): on the dataset-bound path objectui main @5262f7dd packages/plugin-dashboard/src/DatasetWidget.tsx#DatasetWidget reads the declared query keys (`dateGranularity`, `sortBy`, `sortOrder`, `limit`) and the metric tile's `description` (#resolvedSubCaption) and nothing else of the bag; none of the keys this repo's examples wrote (`format`, `currency`, `color`, `suffix`, `showLegend`, `horizontal`, `showDataLabels`) is read there, only the legacy inline arm spreads the bag, and each was removed from its producer." }, "filterBindings": { "status": "live", diff --git a/packages/spec/liveness/dataset.json b/packages/spec/liveness/dataset.json index f48c30aecd0..a3f13ec585a 100644 --- a/packages/spec/liveness/dataset.json +++ b/packages/spec/liveness/dataset.json @@ -106,7 +106,7 @@ "format": { "status": "live", "evidence": "packages/services/service-analytics/src/dataset-compiler.ts#compileDataset (`if (typeof m.format === 'string') metric.format = m.format`); packages/services/service-analytics/src/analytics-service.ts#AnalyticsService (`queryDataset`: `if (f.format == null && m.format) f.format = m.format` — enriched onto the result field for the renderer)", - "note": "compiled into Cube metric + enriched onto result fields for the renderer. 2026-08-28: RE-ANCHORED (commit 9ee2dcfbd) and REPOINTED — `analytics-service.ts:480` had rotted onto a docblock sentence about driver selection. Re-closed by hand against c459da6bc; DATED for the first time (this file carried no `verifiedAt` anywhere, which the review accepting that re-anchoring sorts as OLDEST).", + "note": "compiled into Cube metric + enriched onto result fields for the renderer. 2026-08-28: RE-ANCHORED (commit 9ee2dcfbd) and REPOINTED — `analytics-service.ts:480` had rotted onto a docblock sentence about driver selection. Re-closed by hand against c459da6bc; DATED for the first time (this file carried no `verifiedAt` anywhere, which the review accepting that re-anchoring sorts as OLDEST). Renderer read points for the date-valued measure (packages/spec/src/ui/dataset.zod.ts; moved out of spec source, #23434), re-read at objectui @b9814b334 on 2026-10-14: objectui @b9814b334 packages/core/src/utils/dataset-format.ts#formatMeasure routes a non-numeric value through objectui @b9814b334 packages/core/src/utils/dataset-format.ts#formatMeasureDate, whose date-only arm threads `format` into the STYLE parameter of objectui @b9814b334 packages/core/src/utils/date-display.ts#formatDate (its `relative` branch falls back to the absolute form beyond ±7 days, stripping the style through objectui @b9814b334 packages/core/src/utils/date-display.ts#absoluteFallbackOptions), while its datetime arm answers `relative` with objectui @b9814b334 packages/core/src/utils/date-display.ts#formatRelativeDate, `short` with objectui @b9814b334 packages/core/src/utils/date-display.ts#formatDateTime (`style: 'compact'`) and everything else — a date pattern included — with the bare `formatDateTime(v, { locale })`. Until objectui#8352 a datetime value ignored `format`; the pattern-grammar gap stays documented rather than closed (objectui#7178 ruled A).", "verifiedAt": "2026-08-28" }, "currency": { diff --git a/packages/spec/liveness/field.json b/packages/spec/liveness/field.json index 9a99832bb76..1a13c967199 100644 --- a/packages/spec/liveness/field.json +++ b/packages/spec/liveness/field.json @@ -54,7 +54,7 @@ }, "options": { "status": "live", - "note": "select options {label,value,description,color,default} — renderers + validation. `description` joined 2026-08-31 (objectui#6153, inheriting the objectui#6140 ruling frame): objectui LookupField searches it on authored static options (LookupField.tsx:526) and recordToOption produces it for fetched options. `icon` is NOT an option key (withdrawn from the object.form options repeater under ADR-0049, #13671; pinned by packages/spec/src/data/field-rows-option-description.test.ts). Read points moved out of spec source (#23434), measured at objectui @d8ec8d6d4f011b11c8eb1e6dbd364ef206711391 and on objectui main with the same answer: objectui @d8ec8d6d4f01 packages/fields/src/index.tsx#SelectCellRenderer (its `renderOne` badge/dot branch reads `option?.label` and `option?.color` off a `SelectOptionMetadata[]`, never `option?.icon`); no field-option render path reads `icon`; the one `opt.icon` read in that tree is the config-panel `ConfigField` vocabulary (a `React.ReactNode` an authored option cannot reach)." + "note": "select options {label,value,description,color,default} — renderers + validation. `description` joined 2026-08-31 (objectui#6153, inheriting the objectui#6140 ruling frame): objectui LookupField searches it on authored static options (LookupField.tsx:526) and recordToOption produces it for fetched options. `icon` is NOT an option key (withdrawn from the object.form options repeater under ADR-0049, #13671; pinned by packages/spec/src/data/field-rows-option-description.test.ts). Read points moved out of spec source (#23434), measured at objectui @d8ec8d6d4f011b11c8eb1e6dbd364ef206711391 and on objectui main with the same answer: objectui @d8ec8d6d4f01 packages/fields/src/index.tsx#SelectCellRenderer (its `renderOne` badge/dot branch reads `option?.label` and `option?.color` off a `SelectOptionMetadata[]`, never `option?.icon`); no field-option render path reads `icon`; the one `opt.icon` read in that tree is the config-panel `ConfigField` vocabulary (a `React.ReactNode` an authored option cannot reach). No per-option `disabled` on the object-field pipeline (packages/spec/src/shared/editability-boundary.ts, the SelectOptionSchema prescription; moved out of spec source, #23434): measured on objectui main @aca27fa, packages/fields/src/widgets/SelectField.tsx#SingleSelectField calls the root `disabled` the single authority and packages/fields/src/widgets/RadioField.tsx#hostGroupProps reads only `props.disabled`; the per-option `disabled` that exists is the SDUI family's own vocabulary, objectui @aca27fa packages/components/src/renderers/form/select.tsx#SelectRenderer." }, "picklist": { "status": "live", @@ -292,7 +292,7 @@ }, "lookupColumns": { "status": "live", - "note": "objectui RecordPickerDialog — record-picker table columns; renderer auto-derives from the referenced object when omitted (reads lookupColumns || lookup_columns)." + "note": "objectui RecordPickerDialog — record-picker table columns; renderer auto-derives from the referenced object when omitted (reads lookupColumns || lookup_columns). The lookup keys' snake_case twins (the docblock above `displayField` in packages/spec/src/data/field.zod.ts, for all seven lookup keys; moved out of spec source, #23434): of the seven, only `lookupColumns`, `lookupPageSize` and `allowCreate` are read as `SNAKE ?? CAMEL`, at objectui @ed971e8 packages/fields/src/widgets/LookupField.tsx#lookupColumns, #lookupPageSize and #explicitAllowCreate (all inside LookupField); `displayField`, `descriptionField`, `lookupFilters` and `dependsOn` are read camelCase-only there; claim dated 2026-09-09." }, "lookupPageSize": { "status": "live", @@ -325,7 +325,7 @@ "verifiedAt": "2026-09-27", "evidenceScope": "cross-repo", "evidence": "objectui: packages/i18n/src/currency.ts:60 reads field.currencyConfig.defaultCurrency ONLY when currencyConfig.currencyMode is 'fixed' — the second step of resolveFieldCurrency's precedence chain; a 'dynamic' config, or one naming no mode, skips it for the tenant default localization.currency; objectui: packages/fields/src/widgets/CurrencyField.tsx:55 calls resolveFieldCurrency and applies the resolved code at :91 through formatAmount, which passes it to Intl.NumberFormat via formatDisplayNumber — measured objectui @25c7d584", - "note": "RE-CITED 2026-09-27 (#20126): since objectui#10461 the reader takes defaultCurrency only under currencyMode 'fixed', and the chain no longer cites the date/datetime-semantics ADR, which never names currency; line pointers moved with it (currency.ts :32 → :60, CurrencyField.tsx :45/:51 → :55/:91). Verdict unchanged: LIVE. RE-CITED 2026-08-10 (#7133/#7142): the old pointer 'packages/fields/src/index.tsx:469' now lands in formatRelativeDays, a date-phrase helper with no currency involvement — the resolver moved to @object-ui/i18n and packages/fields/src/currency.ts:14 only re-exports it, which is why the old line drifted onto unrelated code rather than disappearing. Verdict unchanged: LIVE, and now cited at both the reader that consumes the key and the widget that applies the resolved currency." + "note": "RE-CITED 2026-09-27 (#20126): since objectui#10461 the reader takes defaultCurrency only under currencyMode 'fixed', and the chain no longer cites the date/datetime-semantics ADR, which never names currency; line pointers moved with it (currency.ts :32 → :60, CurrencyField.tsx :45/:51 → :55/:91). Verdict unchanged: LIVE. RE-CITED 2026-08-10 (#7133/#7142): the old pointer 'packages/fields/src/index.tsx:469' now lands in formatRelativeDays, a date-phrase helper with no currency involvement — the resolver moved to @object-ui/i18n and packages/fields/src/currency.ts:14 only re-exports it, which is why the old line drifted onto unrelated code rather than disappearing. Verdict unchanged: LIVE, and now cited at both the reader that consumes the key and the widget that applies the resolved currency. The removed `currencyConfig.precision` (#19992; packages/spec/src/data/field.zod.ts; moved out of spec source, #23434): the widget's one width is the currency's ISO 4217 minor unit, objectui @b9814b334 packages/fields/src/widgets/CurrencyField.tsx#CurrencyField through objectui @b9814b334 packages/fields/src/widgets/number-format.ts#currencyDisplayWidth over objectui @b9814b334 packages/fields/src/currency.ts#currencyFractionDigits, and no objectui code reads `currencyConfig.precision`; re-read 2026-10-15, first cited sha-less (the source then named `currencyFractionDigits(currency)` as the widget's own line)." }, "dimensions": { "status": "live", @@ -456,7 +456,7 @@ "verifiedAt": "2026-09-20", "evidenceScope": "cross-repo", "evidence": "objectui: packages/app-shell/src/utils/deriveRelatedLists.ts#deriveRelatedLists:276 puts the authored value on the derived descriptor as `filter`, gated by the same file's #isDeclaredFilter predicate — the declared-vs-absent discrimination its relatedListTitle / relatedListColumns siblings do, with no alias accepted and no shape coerced; objectui: packages/app-shell/src/views/RecordDetailView.tsx#synthParts:2079 destructures that descriptor key as `declaredFilter` (:2041) and writes it onto the synthesized `record:related_list` node, the same `filter` slot the component-level input already lowers, so RelatedList AND-composes it with { [referenceField]: parentId } and the tab strip's count probe composes the same pair — measured objectui @53ded82bf7, which is this repo's `.objectui-sha` pin, so both pointers were read AT THE PIN this repo builds against rather than on objectui main", - "note": "[#8704] Contract-first spec half (maintainer ruling 2026-08-15, 「接受全部建议。」 item 3): the declarative default filter the auto-derived related list AND-composes with { [referenceField]: parentId }, badge-count parity included — both clauses normative in the key's describe()/JSDoc and pinned in field.test.ts. Reuses FilterConditionSchema, so the #8793 bare-preset schema door judges it at parse (pinned at this position) and the engine FILTER doors (#8296 formula refusal etc.) judge the composed query at run time. Seeded `planned` + authorWarn per enforce-or-mark, the app.navigation runAction (#4848) shape rather than the useGrouping one, and that seeding wrote its own flip condition: 「Flip to `live` (and drop authorWarn) with a deriveRelatedLists/RecordDetailView evidence pointer when objectui#4664 lands.」 **FLIPPED `planned` → `live` 2026-09-20 (#19187), and the condition was re-measured rather than relayed**: objectui#4664 landed as objectui d796c8dde (objectui PR #6946, 2026-08-31) and `git merge-base --is-ancestor d796c8dde 53ded82bf7` exits 0, so the consumer is inside the console this repo pins — the #10068 discipline (read both pointers at the pin, never at objectui main) one row over. What the flip drops is not bookkeeping: the authorHint it carried told an author 「the auto-derived related list does not apply this filter yet」 about a key the pinned console applies, and `packages/lint`'s ledger-driven advisory repeated that sentence at every compile, steering authors off a working key. The sentence is recorded rather than deleted because it was TRUE from #8704 until the pin absorbed d796c8dde — a `planned` row falsifies by its consumer landing, which is the one direction no citation check can see (the pointer never rots; there was no pointer)." + "note": "[#8704] Contract-first spec half (maintainer ruling 2026-08-15, 「接受全部建议。」 item 3): the declarative default filter the auto-derived related list AND-composes with { [referenceField]: parentId }, badge-count parity included — both clauses normative in the key's describe()/JSDoc and pinned in field.test.ts. Reuses FilterConditionSchema, so the #8793 bare-preset schema door judges it at parse (pinned at this position) and the engine FILTER doors (#8296 formula refusal etc.) judge the composed query at run time. Seeded `planned` + authorWarn per enforce-or-mark, the app.navigation runAction (#4848) shape rather than the useGrouping one, and that seeding wrote its own flip condition: 「Flip to `live` (and drop authorWarn) with a deriveRelatedLists/RecordDetailView evidence pointer when objectui#4664 lands.」 **FLIPPED `planned` → `live` 2026-09-20 (#19187), and the condition was re-measured rather than relayed**: objectui#4664 landed as objectui d796c8dde (objectui PR #6946, 2026-08-31) and `git merge-base --is-ancestor d796c8dde 53ded82bf7` exits 0, so the consumer is inside the console this repo pins — the #10068 discipline (read both pointers at the pin, never at objectui main) one row over. What the flip drops is not bookkeeping: the authorHint it carried told an author 「the auto-derived related list does not apply this filter yet」 about a key the pinned console applies, and `packages/lint`'s ledger-driven advisory repeated that sentence at every compile, steering authors off a working key. The sentence is recorded rather than deleted because it was TRUE from #8704 until the pin absorbed d796c8dde — a `planned` row falsifies by its consumer landing, which is the one direction no citation check can see (the pointer never rots; there was no pointer). The form hint's face today (packages/spec/src/data/field.form.ts; moved out of spec source, #23434): objectui @b9814b334 packages/app-shell/src/views/metadata-admin/SchemaForm.tsx#resolveFieldFace lands the `filter-condition` hint on `{ kind: 'raw-json', hint }`, past both structural fallbacks, because objectui @b9814b334 packages/app-shell/src/views/metadata-admin/widgets.tsx#WIDGETS registers no `filter-condition`; re-read 2026-10-15, first cited sha-less." }, "trackHistory": { "status": "live", diff --git a/packages/spec/liveness/object.json b/packages/spec/liveness/object.json index 2af77c3aa36..0c63f12041b 100644 --- a/packages/spec/liveness/object.json +++ b/packages/spec/liveness/object.json @@ -189,7 +189,7 @@ "apiMethods": { "status": "live", "evidence": "packages/spec/src/data/api-derivation.ts, packages/rest/src/rest-server.ts, packages/runtime/src/api-exposure.ts", - "note": "three-state whitelist over the six primitives (get/list/create/update/delete/bulk): undefined = unrestricted, [] = deny-all (fully closed), subset = the derived closure; a present-but-non-array value fails CLOSED (#3545). resolveEffectiveApiMethods (the single derivation table) is consumed by the REST gate (405 outside the effective set) and the runtime dispatcher gate. Since #3543 the enum IS the six primitives; the legacy 8 values (upsert/aggregate/history/search/restore/purge/import/export) are DERIVED effective operations (ApiOperation vocabulary) — a stored legacy value is stripped at parse (stripLegacyApiMethods, canonicalize-and-warn, permanent tolerance)." + "note": "three-state whitelist over the six primitives (get/list/create/update/delete/bulk): undefined = unrestricted, [] = deny-all (fully closed), subset = the derived closure; a present-but-non-array value fails CLOSED (#3545). resolveEffectiveApiMethods (the single derivation table) is consumed by the REST gate (405 outside the effective set) and the runtime dispatcher gate. Since #3543 the enum IS the six primitives; the legacy 8 values (upsert/aggregate/history/search/restore/purge/import/export) are DERIVED effective operations (ApiOperation vocabulary) — a stored legacy value is stripped at parse (stripLegacyApiMethods, canonicalize-and-warn, permanent tolerance). Read points for the `sys_api_key` single-record-write exemption pinned in packages/spec/src/data/api-methods-batch-conformance.test.ts (moved out of spec source, #23434), re-read at objectui @b9814b334 (2026-10-14): objectui @b9814b334 packages/plugin-grid/src/ObjectGrid.tsx#selectionMode — with neither `selection` nor `selectable` authored, multi-select is enabled only by objectui @b9814b334 packages/plugin-grid/src/ObjectGrid.tsx#hasBulkActions, and the one implicit bulk action, bulk-delete, is gated on the resolved delete affordance (objectui @b9814b334 packages/plugin-grid/src/ObjectGrid.tsx#rowDeleteVerdict); a list-item action named in `bulkActions` is promoted to `operation: 'custom'` + `actionDef` and dispatched through objectui @b9814b334 packages/plugin-grid/src/ObjectGrid.tsx#dispatchBulkAction; objectui @b9814b334 packages/plugin-grid/src/hooks/useBulkExecutor.ts#useBulkExecutor — a promoted custom def fans out per row through the action runner, and only an `update`/`delete` bulk def reaches the data-plane `bulk` primitive." }, "trackHistory": { "status": "live", diff --git a/packages/spec/liveness/page.json b/packages/spec/liveness/page.json index b960a0e5c9a..955126178f1 100644 --- a/packages/spec/liveness/page.json +++ b/packages/spec/liveness/page.json @@ -54,7 +54,7 @@ "verifiedAt": "2026-08-10", "evidenceScope": "cross-repo", "evidence": "objectui: packages/components/src/renderers/layout/page.tsx:290 branches on schema.regions, :197-204 resolves the named slots (header/sidebar/main/aside/footer) and appends the remainder below main at :211, and :157 flattens their components for the JSX-source path — measured objectui @11c1e71e", - "note": "region → component tree rendering (header/main/sidebar/footer). RE-CITED 2026-08-10 (#7133/#7142): same split-citation pruning as `template` — at objectui @11c1e71e the containers.tsx half names `regions` only in a comment (:798, 'page:section — thin wrapper used inside regions'). Verdict unchanged: LIVE. Component-level liveness stays unclassified (undrilled container, #4956); one component key carries a verdict OUTSIDE this file: `components[].responsive` is a retiredKey tombstone (#11027, ADR-0049 D2 — dead on a two-repo measurement; see the file _note)." + "note": "region → component tree rendering (header/main/sidebar/footer). RE-CITED 2026-08-10 (#7133/#7142): same split-citation pruning as `template` — at objectui @11c1e71e the containers.tsx half names `regions` only in a comment (:798, 'page:section — thin wrapper used inside regions'). Verdict unchanged: LIVE. Component-level liveness stays unclassified (undrilled container, #4956); one component key carries a verdict OUTSIDE this file: `components[].responsive` is a retiredKey tombstone (#11027, ADR-0049 D2 — dead on a two-repo measurement; see the file _note). COMPONENT-MEMBER READ POINTS (moved out of packages/spec/src, #23434; ruling 6165388570 letter C, Q1 A as the seat read it): an EVIDENCE LIST only — no verdict is asserted, so component-level liveness stays unclassified as above. Each entry names the ComponentPropsMap row and member it reads, then its read points in the spelling `objectui @SHA FILE#SYMBOL` (the SHA is the objectui tree the point was last read in; a pin hop moves nothing here, and a renamed or deleted symbol is the event that re-measures it). Inside one entry, a letter defined at its head abbreviates a repeated file: with `P = objectui @SHA FILE`, `P#a P#b` reads `objectui @SHA FILE#a`, `objectui @SHA FILE#b`. Entries, in source order: [1] page:header (H = objectui @b9814b334 packages/components/src/renderers/layout/containers.tsx#PageHeaderRenderer): title (`schema?.title ?? schema?.properties?.title`), the refused `icon` (resolved only per header action) and `breadcrumb` (an empty slot), `actions` (resolvedHeaderActions then headerActionPlan), recordChrome (`=== false` on either carrier, branching the header), showStar and showCopyId (the record chip), maxVisible and mobileMaxVisible (`?? 3` / `?? 1`), all in H; re-read 2026-10-15, first cited sha-less; [2] page:tabs (T = objectui @b9814b334 packages/components/src/renderers/layout/containers.tsx#PageTabsRenderer): tabStyle (`schema?.properties?.type || schema?.tabStyle || 'line'`), alwaysShowStrip (`schema?.alwaysShowStrip === true || schema?.properties?.alwaysShowStrip === true`), items[].value (`it.value`, falling back to `tab-${idx}`; the refused `key` is the designer's `page:tabs.items.itemFields` input in objectui @b9814b334 packages/app-shell/src/views/metadata-admin/previews/block-config.ts#BLOCK_CONFIG), all in T; the retired props key `type` (the reason for the rename): left unhoisted by objectui @b9814b334 packages/react/src/SchemaRenderer.tsx#HOIST_PROTECTED_KEYS, and skipped as a base prop by objectui @b9814b334 packages/sdui-parser/src/validate.ts#BASE_PROPS; re-read 2026-10-15, first cited sha-less; [3] page:tabs items[].icon (C = objectui @b9814b334 packages/components/src/renderers/layout/containers.tsx): C#triggerClass (`LazyIcon` inside the TabsTrigger) in C#PageTabsRenderer; published by its registration's `items` input; vocabulary objectui @b9814b334 packages/components/src/lib/lazy-icon.tsx#getLazyIcon; [4] page:tabs items[].visibleWhen (the tab-item evaluator: `data` is the row, bare fields spread flat, ambient roots after it): objectui @1fb1ced36243 packages/components/src/renderers/layout/containers.tsx#isItemVisible (its ExpressionEvaluator) in PageTabsRenderer; [5] record:details / record:highlights / record:related_list / record:quick_actions .requiredPermissions (RECORD_BLOCK_REQUIRED_PERMISSIONS_DESCRIPTION; R = objectui @b9814b334 packages/plugin-detail/src/renderers/, P = objectui @b9814b334 packages/permissions/src/): the gate `perms.hasCapabilities(required)` and the notice in R record-details.tsx#RecordDetailsBody, R record-highlights.tsx#highlightsAllowed (RecordHighlightsBody), R record-related-list.tsx#RecordRelatedListBody, R record-quick-actions.tsx#RecordQuickActionsBody; earlier withholding: no record R record-details.tsx#RecordDetailsBody, no object and the related read gate R record-related-list.tsx#RecordRelatedListBody; a gated highlights registers no fields R record-highlights.tsx#RecordHighlightsBody (useRegisterHighlightFields); ALL OF THEM and fail-open on unreported P MePermissionsProvider.tsx#hasCapabilities; fail-open P PermissionProvider.tsx#ALL_CAPABILITIES and P usePermissions.ts#NO_PROVIDER_PERMISSIONS; the binding wrapper (objectui#12294) objectui @b9814b334 packages/plugin-detail/src/renderers/recordBinding.tsx#recordBindingBlock; [6] record:details sections[].headerColor (closed enum, #12126): the six class literals of objectui @b9814b334 packages/plugin-detail/src/headerColor.ts#headerColorClass (objectui#6294, merged 2026-08-25); re-read 2026-10-15, first cited sha-less; [7] record:details sections[] hideEmpty, collapsible, showBorder and the #11661 trio (renderer defaults measured; the trio at objectui @190fbd01): objectui @190fbd01 packages/plugin-detail/src/renderers/record-details.tsx#RecordDetailsRenderer and objectui @190fbd01 packages/plugin-detail/src/DetailSection.tsx#DetailSection (`hideEmpty ?? true`; the undeclared `s.title ?? s.label` limb); [8] record:related_list.parentDataSource (the name): the sibling record blocks' binding read objectui @f0532d8a packages/plugin-detail/src/renderers/recordBinding.tsx#recordBindingBlock (`isElementDataSourceConfig(schema.dataSource)`), measured on objectui main; [9] record:details sections[].count (read since objectui c08c9332a, objectui#12192): objectui @b9814b334 packages/plugin-detail/src/renderers/record-details.tsx#countField (inside filteredSections; fills every `{{ count }}` hole); [10] record:related_list.parentDataSource (PLANNED): the objectui measurement is the file _note's (objectui @f0532d8a, the record context read in RecordRelatedListBody); no new evidence here; [11] record:activity guidance `items` / `loading` (host feed slot; R = objectui @89cad75d5570 packages/plugin-detail/src/renderers/): R record-activity.tsx#hostItems and R record-activity.tsx#loading in RecordActivityRenderer; record:chatter / record:discussion `feed` reads neither, R record-chatter.tsx#RecordChatterRenderer; [12] record:chatter.position (vocabulary bottom / right / left, #8762): objectui @665661ab0932 packages/plugin-detail/src/RecordChatterPanel.tsx#position (`right`/`left` dock vs `bottom` in-flow); the designer's CHATTER_INPUTS and the merge fallback in objectui @665661ab0932 packages/plugin-detail/src/renderers/record-chatter.tsx#RecordChatterRenderer; [13] record:reference_rail (RecordReferenceRailProps): objectui @b9814b334 packages/plugin-detail/src/renderers/record-reference-rail.tsx#RecordReferenceRailRenderer ( an entry `icon` is unread, `title` a raw React child); the synthesizer emitting `icon` objectui @b9814b334 packages/plugin-detail/src/synth/buildDefaultPageSchema.ts#buildDefaultPageSchema; re-read 2026-10-15, first cited sha-less; [14] record:alert, record:quick_actions, record:history: objectui @b9814b334 packages/plugin-detail/src/renderers/record-alert.tsx#RecordAlertRenderer, objectui @b9814b334 packages/plugin-detail/src/renderers/record-quick-actions.tsx#RecordQuickActionsRenderer, objectui @b9814b334 packages/plugin-detail/src/renderers/record-history.tsx#RecordHistoryRenderer and objectui @b9814b334 packages/plugin-detail/src/HistoryTimeline.tsx#HistoryTimeline (each renderer's ACTUAL reads, against its registration's declared inputs); re-read 2026-10-15, first cited sha-less; [15] record:line_items (RecordLineItemsProps; L = objectui @31971ff1e28f packages/plugin-form/src/LineItemsPanel.tsx): registered as `line_items` (namespace `record`) in objectui @31971ff1e28f packages/plugin-form/src/index.tsx with its mapping objectui @31971ff1e28f packages/plugin-form/src/index.tsx#RECORD_LINE_ITEMS_DATA_SOURCE; the fifteen `schema.KEY` reads in L#LineItemsPanel (childObject L#heldReloadRef; parentObject L#parentObject; parentId, recordId L#parentId; amountField, totalField L#parentPatch; filter L#scopeFilter; limit L#message; columns L#gridField (applyColumnPermissions)); the gate objectui @31971ff1e28f packages/react/src/element-data-source/ElementDataSourceGate.tsx#useElementDataSourceSchema; read set unchanged at objectui main d59f11c0d3dc; [16] record:line_items guidance (addLabel, sortField, onRowExpand not handed to the grid): objectui @9dfaca654311 packages/plugin-form/src/LineItemsPanel.tsx#gridField; [17] record:approvals (RecordApprovalsProps, EMPTY key set; A = objectui @47b1f0bb71 packages/app-shell/src/views/): registered as `approvals` (namespace `record`) in A record-approvals-renderer.tsx#RecordApprovalsRenderer; approvals A record-approvals-renderer.tsx#hostApprovals, threaded by A RecordDetailView.tsx#RecordDetailView and emitted by objectui @47b1f0bb71 packages/plugin-detail/src/synth/buildDefaultPageSchema.ts#buildDefaultPageSchema; currentUserId falls back in A RecordApprovalsPanel.tsx#isSubmitter; Studio page create seeds without options in A metadata-admin/anchors.ts#synth; [18] page:accordion items[].value (refused): objectui @b9814b334 packages/components/src/renderers/layout/containers.tsx#PageAccordionRenderer overwrites it with `panel-${idx}`; the designer input `page:accordion.items.itemFields` in objectui @b9814b334 packages/app-shell/src/views/metadata-admin/previews/block-config.ts#BLOCK_CONFIG (objectui#7973); re-read 2026-10-15, first cited sha-less; [19] page:accordion items[].icon (C = objectui @b9814b334 packages/components/src/renderers/layout/containers.tsx): C#commonChildren (`LazyIcon` in the AccordionTrigger) inside C#PageAccordionRenderer; published by its `accordion` registration's `items` input; vocabulary objectui @b9814b334 packages/components/src/lib/lazy-icon.tsx#getLazyIcon; [20] page:accordion.variant: objectui @b9814b334 packages/components/src/renderers/layout/containers.tsx#PageAccordionRenderer (`schema?.variant ?? schema?.properties?.variant ?? 'flush'`); re-read 2026-10-15, first cited sha-less; [21] element:text.variant retired `heading` / `subheading`: objectui @89cad75d55 packages/components/src/renderers/basic/elements.tsx#VARIANT_TAG; [22] element:button.icon (B = objectui @b9814b334 packages/components/src/renderers/form/button.tsx, R = objectui @b9814b334 packages/components/src/renderers/action/resolve-icon.ts): B#Icon (resolveIcon) and its render on either side of the label in B#ButtonRenderer; R#resolveIcon R#describeIconLookup R#toPascalCase R#iconNameMap R#recordIconName (the vocabulary: the generated objectui @b9814b334 packages/components/src/lib/lucide-record-icon-names.ts#LUCIDE_RECORD_ICON_NAME_TABLE); contrast objectui @b9814b334 packages/components/src/lib/lazy-icon.tsx#getLazyIcon; no `icon` in the registration's inputs inside B#ButtonRenderer (objectui#9910 added the `children` slot input); history: the read point moved files on the hop onto objectui 9602dc820, and the registration ranges were first wrong-since-written at objectui a472b0716; [23] action:* rows, the effective executor and the runner's reads (R = objectui @b9814b334 packages/core/src/actions/ActionRunner.ts, first read at objectui @1fb1ced36243 with every anchor at the same symbols; A = objectui @b9814b334 packages/components/src/renderers/action/): executor resolution R#execute (`action.type || action.actionType || action.name`); forwarded executor A action-button.tsx#forwarded, A action-icon.tsx#forwarded (`type: schema.actionType`), A action-group.tsx#paramsPayload, A action-menu.tsx#paramsPayload (`type: action.type`); the name leg documented in A action-button.tsx#forwarded; built-in executors and the `navigation` alias R#ActionRunner (`builtinExecutors`), url navigation executeUrl / executeNavigation / navigateTo in R#ActionRunner; no console host handler under `url` / `navigation`: objectui @b9814b334 packages/app-shell/src/console/ConsoleShell.tsx, packages/app-shell/src/hooks/useConsoleActionRuntime.tsx, packages/app-shell/src/console/AppContent.tsx, packages/app-shell/src/hooks/useObjectActions.ts; onSuccess R#handlePostExecution R#readOnSuccessNavigation; operation read before any executor R#execute (isUpdateOperationAction), forwarded A action-button.tsx#forwarded; patch merged only when a plain object (executeUpdateOperation in R#ActionRunner); unchanged at objectui main 866763bd and 72594f29; [24] section 4b, the action:* quartet and element:* lists (shared reads; A = objectui @b9814b334 packages/components/src/renderers/action/): publication objectui @b9814b334 packages/core/src/registry/public-blocks.ts#PUBLIC_BLOCKS; `autoTrigger` \"NOT persisted metadata\" in the header of A auto-trigger.ts#useAutoTriggerOnce; the Button primitive vocabulary objectui @b9814b334 packages/components/src/ui/button.tsx#buttonVariants; `objectName` forwarded by A action-button.tsx#forwarded and A action-icon.tsx#forwarded, and per member by A action-group.tsx#paramsPayload and A action-menu.tsx#paramsPayload; history: objectui#11168 slices 1-2, objectui#11212 (fail-closed `visible`), objectui#11182 (host `disabled` verdict), objectui#11344 (`outcomeMessages`), objectui#11638 (member `properties.params` read retired), objectui#11839 (disabled reason), objectui#11880 (repeater binding), objectui#12085 (repeater publishes only the binding); [25] action:button (ActionButtonPropsSchema; B = objectui @b9814b334 packages/components/src/renderers/action/action-button.tsx; the retired gate's quoted first line `type: schema.actionType,` holds at B#forwarded): name B#isVisible (diagnostic label) B#forwarded; label B#button (React child) B#isVisible B#forwarded; icon B#Icon (resolveIcon); actionType B#forwarded (`type`); variant, size B#variant and its `size` twin; visible B#isVisible B#ActionButtonRenderer (hasDeclaredVisibilityGate); disabled B#isDisabled B#button; params B#staticValues (withUndoBaseline) and objectui @b9814b334 packages/components/src/renderers/action/static-params.ts#readStaticParamValues; the forwarded set B#forwarded (outcomeMessages forwarded since objectui#11344, left unpublished in the registration's inputs inside B#ActionButtonRenderer); undo baseline B#withUndoBaseline; [26] action:icon (ActionIconPropsSchema; I = objectui @b9814b334 packages/components/src/renderers/action/action-icon.tsx; the retired gate's quoted first line `type: schema.actionType,` holds at I#forwarded): size not read I#size (fixed `'icon'`); the forward I#forwarded (no undoable / recordIdField; outcomeMessages since objectui#11344); label I#isVisible I#forwarded I#button (aria-label, initial letter, DisabledReasonTrigger heading) I#ActionIconRenderer (tooltip); visible I#isVisible I#ActionIconRenderer; disabled I#isDisabledPred I#button; variant I#variant; params I#staticValues (readStaticParamValues); [27] action:group / action:menu members (actionContainerMember; G = objectui @2e818d0b51ec packages/components/src/renderers/action/action-group.tsx, M = objectui @2e818d0b51ec packages/components/src/renderers/action/action-menu.tsx, P = objectui @2e818d0b51ec packages/components/src/renderers/action/static-params.ts): drawn G#InlineActionButton G#DropdownActionItem M#ActionMenuItem; visible G#useMemberVisible M#useMenuActionVisible; size (group only) G#btnSize; tags G#showSeparator; locations G#actions (actionRendersAt); forwarded G#staticValues M#staticValues (the forward block); object params forwarded on `api` and dropped, with a development-build warning only, for every other `type` P#readActionEntryParamValues (first cited at objectui @0abd4f9f8, same symbol); refused: properties.params P#readMemberStaticParamValues, className G#handleClick, autoTrigger M#ActionAutoTrigger (useAutoTriggerOnce), onClick G#handleExecute M#handleExecute; the array forward (the retired gate's quoted first line `const paramsPayload: ActionDef = Array.isArray(action.params)`) objectui @b9814b334 packages/components/src/renderers/action/action-group.tsx#paramsPayload and objectui @b9814b334 packages/components/src/renderers/action/action-menu.tsx#paramsPayload, other values objectui @b9814b334 packages/components/src/renderers/action/static-params.ts#readActionEntryParamValues; the fork measured at objectui @ab1879721595, files byte-identical at objectui main 2abec3a96; [28] action:group (ActionGroupPropsSchema; G = objectui @b9814b334 packages/components/src/renderers/action/action-group.tsx): actions G#declaredActions G#actions (actionRendersAt) G#useMemberVisible G#paramsPayload; display G#display; label, icon, variant, size G#TriggerIcon (the trigger; size raw to inline members) G#btnSize; visible G#isVisible; the host `disabled` verdict (objectui#11182) G#ActionGroupRenderer; group `name` unread (the comment above G#ActionGroupRenderer, objectui#11168); the registration's inputs inside G#ActionGroupRenderer; [29] action:menu (ActionMenuPropsSchema; M = objectui @b9814b334 packages/components/src/renderers/action/action-menu.tsx): actions M#ActionMenuRenderer M#ActionMenuItem M#ActionAutoTrigger M#paramsPayload; label, icon, variant, size, visible and the host `disabled` verdict (objectui#11182) M#ActionMenuRenderer; the registration's inputs inside M#ActionMenuRenderer (objectui#11168 slice 1); [30] element:definition-list (ElementDefinitionListPropsSchema; L = objectui @b9814b334 packages/components/src/renderers/basic/data-list.tsx; the retired gate's quoted first line `const props = readProps<{` holds at L#props): items, columns (`=== 2`), inline, term, description L#DefinitionListRenderer (an item's `description` shown through L#toText: a string or number as text, an object as JSON; first cited at objectui @89cad75d55); the registration's number enum and optional `items` (objectui#11168 slice 2) at its `definition-list` registration in L; the designer's number control objectui @b9814b334 packages/app-shell/src/views/metadata-admin/previews/block-config.ts#BLOCK_CONFIG; [31] element:repeater (ElementRepeaterPropsSchema; L = objectui @b9814b334 packages/components/src/renderers/basic/data-list.tsx; the quoted first line `const props = readProps<{` holds at L#props in L#RepeaterRenderer): object, filter, sort, limit, emptyText, divided, titleField, fields L#RepeaterRenderer; the binding L#dataBinding (useElementDataSource, objectui#11880, aaba8655a); resolved filter tokens L#filterScope; delivery objectui @b9814b334 packages/data-objectstack/src/index.ts#ObjectStackAdapter (translateFilterArray arm) and objectui @b9814b334 packages/data-objectstack/src/index.ts#serializeOrderBy; the registration wrapped by elementDataSourceBlock publishes no `object` input since objectui#12085 (ce991bd70); the registration's `object` required flag read at objectui @20c6d351a; [32] object-grid.aggregations[] (GridAggregationSchema): objectui @89cad75d55 packages/plugin-grid/src/ObjectGrid.tsx#groupHeaders (useGroupedData) and objectui @89cad75d55 packages/plugin-grid/src/ObjectGrid.tsx#serverGroupSource (useServerGroupHeaders); the grid's own function vocabulary objectui @89cad75d55 packages/plugin-grid/src/useGroupedData.ts#AggregationType (unchanged at objectui @b9814b334); [33] object-grid (ObjectGridPropsSchema, per key; E = objectui @eb7f586b packages/plugin-grid/src/ObjectGrid.tsx): columns, fields E#schemaFields; filter E#schemaFilterSource (toFilterNode); defaultFilters E#cols (legacy fallback); sort E#schemaSort; defaultSort E#cols; pagination, pageSize, showPagination E#lastFindParamsRef E#schemaPageSize E#paginationEnabled; searchableFields, showSearch E#trimmedSearch E#searchEnabled; rowHeight E#ObjectGrid (resolveRowHeightMode); grouping, aggregations E#grouping E#groupValueFormatter; rowColor E#getRowClassName; conditionalFormatting E#predicateFields E#rules; selection, selectable E#ObjectGrid; rowActions E#rowActionsList; batchActions, bulkActions E#declaredBulkActions; bulkActionDefs E#legacyBulkNames; navigation E#navigation; editable, singleClickEdit, resizable, resizableColumns, reorderableColumns E#dataTableSchema; frozenColumns E#effectiveFrozenColumns; showColumnTypeIcons E#fieldMeta; exportOptions, operations E#declared; label, title E#fileNameFor E#dataTableSchema; data, staticData E#getDataConfig; [#20694] description objectui @db11afd4967c packages/plugin-grid/src/ObjectGrid.tsx#resolvedDescription (drawn as `gridDescription`); emptyState objectui @db11afd4967c packages/plugin-grid/src/ObjectGrid.tsx#authoredEmptyState (DataEmptyState in renderedGridContent); keyboardNavigation 0 read points at objectui @db11afd4967c, 15 hit lines at objectui @ab1879721595 and at objectui @b9814b334 against 3 then 4 for the control `schema.editable` (the fourth objectui @b9814b334 packages/plugin-grid/src/ObjectGrid.tsx#carriesConcurrencyToken); [34] object-grid.description, object-grid.emptyState (B = objectui @b9814b334 packages/plugin-grid/src/ObjectGrid.tsx): description objectui @db11afd4967c packages/plugin-grid/src/ObjectGrid.tsx#resolvedDescription; emptyState title/message B#authoredEmptyTitle (resolveInlineI18nLabel, objectui#11227 carried by 6158e4c93; raw at objectui @89cad75d5570) drawn in B#renderedGridContent, link (objectui#12270, b434f5f47) B#renderedGridContent (EmptyStateLink); [35] object-grid.columns (ListViewSchema.shape.columns; A = objectui @ab1879721595 packages/plugin-grid/src/ObjectGrid.tsx): A#normalizeColumns (first-entry dispatch) A#cols A#fieldName (resolvesToDataColumn); the retired `colOverride?.options` read: A#groupValueFormatter A#rawOptions (objectui b0bf413, objectui#11544); the footer summary's off-contract column keys objectui @ab1879721595 packages/plugin-grid/src/useColumnSummary.ts#columnHints; [36] object-grid.fields (O = objectui @89cad75d55 packages/plugin-grid/src/ObjectGrid.tsx): O#schemaFields O#fieldsToShow O#generateColumns; the host hand-off projection O#getSelectFields; [37] object-grid.filter: objectui @53ded82b packages/plugin-grid/src/ObjectGrid.tsx#ObjectGrid (toFilterNode before `$filter`); [38] object-grid.sort (S = objectui @53ded82b packages/plugin-grid/src/ObjectGrid.tsx): S#schemaSort S#cols (the fetch path's string arm and array arm) S#declaredSort (parseSchemaSort, `TableSortItem[]`); the registration's array input objectui @53ded82b packages/plugin-grid/src/index.tsx (`{ name: 'sort', type: 'array' }`); [39] object-grid.defaultSort (REMOVED, tombstone; D = objectui @190fbd01d packages/plugin-grid/src/ObjectGrid.tsx): D#cols (`$orderby` fallback) and D#declaredSort (`[schema.defaultSort]`); [40] object-grid.pagination (D = objectui @d18322415 packages/plugin-grid/src/ObjectGrid.tsx): pageSize D#lastFindParamsRef D#schemaPageSize D#pageSize; pageSizeOptions D#dataTableSchema; presence D#paginationEnabled; the registry input objectui @d18322415 packages/plugin-grid/src/index.tsx (`pagination`, type object); [41] object-grid members read at objectui @89cad75d55 (O = objectui @89cad75d55 packages/plugin-grid/src/ObjectGrid.tsx): rowHeight O#resolveRowHeightMode; aggregations O#groupHeaders (useGroupedData) O#serverGroupSource (useServerGroupHeaders); conditionalFormatting O#getRowStyle (resolveConditionalFormatting); rowColor O#getRowClassName (useRowColor); selection and selectable O#ObjectGrid (the selection-mode branch) O#dataTableSchema (`selectable`), read by objectui @89cad75d55 packages/components/src/renderers/complex/data-table.tsx#resolveSelectionMode; rowActions O#rowActionsDeclared; bulkActions, batchActions O#declaredBulkActions (`batchActions ?? bulkActions`); bulkActionDefs O#legacyBulkNames (resolveBulkActions); [42] object-grid.navigation: objectui @89cad75d55 packages/plugin-grid/src/ObjectGrid.tsx#navigation (useNavigationOverlay); [43] object-grid.keyboardNavigation (built by objectui 154075ab1, objectui#11068; declared ahead of its reader, which read nothing at objectui @db11afd4967c): objectui @b9814b334 packages/plugin-grid/src/ObjectGrid.tsx#dataTableSchema (`schema.keyboardNavigation ?? inlineEditable`) with objectui @b9814b334 packages/plugin-grid/src/ObjectGrid.tsx#inlineEditable; objectui @b9814b334 packages/components/src/renderers/complex/data-table.tsx#DataTableRenderer (default false, `role=\"grid\"`, the roving tabIndex), objectui @b9814b334 packages/components/src/renderers/complex/data-table.tsx#navigationTarget and objectui @b9814b334 packages/components/src/renderers/complex/data-table.tsx#handleCellKeyDown; [44] object-grid.resizableColumns (REMOVED, tombstone): objectui @89cad75d55 packages/plugin-grid/src/ObjectGrid.tsx#dataTableSchema (`schema.resizable ?? schema.resizableColumns ?? true`); [45] object-grid.operations (GridOperationsSchema; O = objectui @89cad75d55 packages/plugin-grid/src/ObjectGrid.tsx): create O#dataTableSchema (showAddRow); update, delete O#wantDeleteAction; export O#handleExport O#dataTableSchema (exportable) O#exportEnabled; a declared block replaces the default O#explicitOperations; [46] object-grid.data: the registry input objectui @b9814b334 packages/plugin-grid/src/index.tsx#GRID_QUERY_INPUTS (`{ name: 'data', type: 'object' }`, held by gridDataInputContract.test.ts); re-read 2026-10-15, first cited sha-less; [47] object-metric.aggregate (ObjectMetricAggregateSchema; M = objectui @89cad75d55 packages/plugin-dashboard/src/ObjectMetricWidget.tsx): field, function M#computeOne; function also M#fieldUnitApplies (answersInFieldUnit) M#inferredFormat (min/max width); groupBy M#computeOne (objectAggregateSpecQuery; floored at `'_all'`) typed on M#ObjectMetricWidgetProps (`ChartAggregate['groupBy']`); objectui's unit-rule pin ObjectMetricWidget.countNotCurrency-10356.test.tsx mounts one tile per AggregationFunction member; [48] object-metric.trend (ObjectMetricTrendSchema; W = objectui @89cad75d55 packages/plugin-dashboard/src/): drawn in W MetricWidget.tsx#MetricWidget (`{value}%`, direction arrow), caption W MetricWidget.tsx#localizedTrendLabel (pickLocalized); declared on W MetricWidget.tsx#MetricWidgetProps, mirrored on W ObjectMetricWidget.tsx#ObjectMetricWidgetProps; replaced by a derived trend in W ObjectMetricWidget.tsx#effectiveTrend; [49] object-metric.drillDown (ObjectMetricDrillDownSchema; A = objectui @ab1879721595): enabled A packages/plugin-dashboard/src/ObjectMetricWidget.tsx#drillEnabled (isDrillEnabled); title A packages/plugin-dashboard/src/ObjectMetricWidget.tsx#drawerTitle (resolveDrillTitle against an empty event); target, columns, maxRows A packages/plugin-dashboard/src/ObjectMetricWidget.tsx#drillDrawer (DrillDownDrawer); `filter` / `mode` refused as objectui's own A packages/types/src/data-display.ts#ObjectMetricDrillDownConfig refuses them; measured writers, objectui's drill-down member pins A packages/plugin-dashboard/src/__tests__/objectMetricDrillDownMembers-8071.test.tsx and A packages/plugin-dashboard/src/__tests__/ObjectMetricWidget.drillRoutedToSharedDrawer-8970.test.tsx; [50] object-metric.drillDown.report (R = objectui @2e818d0b51ec packages/plugin-dashboard/src/): handed verbatim in R ObjectMetricWidget.tsx#drillDrawer; drawn when R DrillDownDrawer.tsx#isDatasetBoundReport holds (R DrillDownDrawer.tsx#isReportDrill), joining the metric filter into `runtimeFilter` (R DrillDownDrawer.tsx#body); typed `SpecReportInput` in objectui @2e818d0b51ec packages/types/src/data-display.ts; the fork measured at objectui @ab1879721595, both files byte-identical at objectui main 2abec3a96; [51] object-metric.compareTo (ObjectMetricCompareToSchema; A = objectui @ab1879721595): A packages/plugin-dashboard/src/ObjectMetricWidget.tsx#comparisonFilter (second aggregate over shiftFilterByCompareTo) and A packages/plugin-dashboard/src/ObjectMetricWidget.tsx#derivedTrend (compareToTrendLabelKey); both dispatch on `kind === 'previousYear'` in A packages/core/src/utils/compare-to.ts#shiftFilterByCompareTo and A packages/core/src/utils/compare-to.ts#compareToTrendLabelKey; `dimension` \"deliberately never read on this path\" in that module's header; [52] object-metric (ObjectMetricPropsSchema): objectui @eb7f586b packages/plugin-dashboard/src/ObjectMetricWidget.tsx#ObjectMetricWidgetProps (the complete destructured read set); [53] object-metric.icon (B = objectui @b9814b334 packages/plugin-dashboard/src/): published by B index.tsx#ObjectMetricBlock (`{ name: 'icon', type: 'string' }`); destructured and forwarded by B ObjectMetricWidget.tsx#ObjectMetricWidget; resolved and drawn by B MetricWidget.tsx#MetricWidget (getLazyIcon, guarded on a string); vocabulary objectui @b9814b334 packages/components/src/lib/lazy-icon.tsx#getLazyIcon; not B MetricCard.tsx (off this key's path); [54] object-metric.filter: the analytics wire lowering objectui @53ded82b packages/data-objectstack/src/index.ts#lowerAnalyticsFilterForWire (objectui#7754); before it the adapter at objectui @a472b07 posted an array verbatim (a 400, #15828); [55] object-kanban.columns[] lane (ObjectKanbanLaneSchema; K = objectui @89cad75d55 packages/plugin-kanban/src/): id K index.tsx#bucketCardsIntoColumns; title K ObjectKanban.tsx#localizeColumn; cards K index.tsx#bucketCardsIntoColumns (kept ahead of the bucketed records); limit K KanbanImpl.tsx#isLimitExceeded; className K KanbanImpl.tsx#KanbanColumnView; collapsed K KanbanImpl.tsx#isColumnCollapsed; [56] object-kanban (ObjectKanbanPropsSchema; E = objectui @eb7f586b packages/plugin-kanban/src/ObjectKanban.tsx): columns E#effectiveColumns; filter E#fetchData (`$filter`); data E#boundData; cardTitle, titleField E#explicitTitleField; cardFields E#explicitCardFields; swimlaneField, grouping E#effectiveSwimlaneField; coverImageField, conditionalFormatting via objectui @eb7f586b packages/plugin-kanban/src/index.tsx#KanbanRenderer (the spread of the authored bag); limit objectui @b9814b334 packages/plugin-kanban/src/ObjectKanban.tsx#query (`$top: resolveRowLimit(schema.limit, DEFAULT_KANBAN_FETCH_BATCH_SIZE)`), first re-read at objectui @87af769e9 where `$top: schema.limit ?? DEFAULT_KANBAN_LIMIT` had become resolveRowLimit; [57] object-kanban.columns (the first-entry dispatch): objectui @89cad75d55 packages/plugin-kanban/src/ObjectKanban.tsx#effectiveColumns (string arm only without `groupBy`); [58] object-kanban.filter: objectui @53ded82b packages/plugin-kanban/src/ObjectKanban.tsx#ObjectKanban (`$filter: schema.filter`, verbatim, lowered by ObjectStackAdapter.convertQueryParams through translateFilterArray); [59] object-kanban.limit (B = objectui @b9814b334): query B packages/plugin-kanban/src/ObjectKanban.tsx#query (`$top: resolveRowLimit(schema.limit, DEFAULT_KANBAN_FETCH_BATCH_SIZE)`) with B packages/plugin-kanban/src/ObjectKanban.tsx#DEFAULT_KANBAN_FETCH_BATCH_SIZE and the resolved filter B packages/plugin-kanban/src/ObjectKanban.tsx#filterScope; mapping B packages/plugin-kanban/src/index.tsx#OBJECT_KANBAN_DATA_SOURCE (`limit: 'limit'`); type B packages/types/src/objectql.ts#ObjectKanbanSchema (`limit?: number`); docs B content/docs/plugins/plugin-kanban.mdx (`limit: 250` snippet and Properties row); precedence B packages/react/src/element-data-source/ElementDataSourceGate.tsx#useElementDataSourceSchema (binding cap over this key; a view's pagination.pageSize only when unset) and B packages/react/src/element-data-source/ElementDataSourceGate.tsx#isUsableRowLimit; the saved-view fallback B packages/core/src/data-scope/element-data-source.ts#savedViewRawLimit; first re-read at objectui @87af769e9 2026-09-20; [60] object-master-detail-form (ObjectMasterDetailFormPropsSchema): objectui @eb7f586b packages/plugin-form/src/MasterDetailForm.tsx#MasterDetailForm; auto-derivation objectui @eb7f586b packages/plugin-form/src/deriveMasterDetail.ts; the `formType` pair objectui's MasterDetailFormSchema declares; [61] object-kanban.quickAdd (RETIRED, tombstone; K = objectui @b9814b334 packages/plugin-kanban/src/): K index.tsx#KanbanRenderer passes `quickAdd` / `onQuickAdd` (the host-mounted React component); gated on both in K KanbanImpl.tsx#KanbanColumnView; K KanbanBoardCore.tsx#KanbanBoardCoreProps reads neither off `schema` (objectui#11234); K ObjectKanban.tsx#ObjectKanban names `quickAdd` and `onQuickAdd` 2 times each (both in comments) against 11 for `onCardClick` (method: occurrences in that file, re-counted 2026-10-15); no `kanban-ui` registration at that sha (objectui#8257; the name survives only in tests and the changelog); through objectui @db11afd49 the board's spread forwarded the key; [62] object-kanban.conditionalFormatting (A = objectui @ab1879721595): A packages/plugin-kanban/src/KanbanBoardCore.tsx#processedColumns hands it to the board; A packages/plugin-kanban/src/KanbanImpl.tsx#getCardStyles evaluates it through A packages/core/src/evaluator/listConditional.ts#resolveConditionalFormatting, the evaluator of A packages/plugin-grid/src/ObjectGrid.tsx#getRowStyle; objectui's declaration A packages/types/src/zod/objectql.zod.ts#KanbanConditionalFormattingRuleSchema and A packages/types/src/objectql.ts#KanbanConditionalFormattingRule (objectui#11522, landed by objectui#11532 c73cdb5695); the stored-dialect arms in A packages/plugin-kanban/src/KanbanImpl.tsx#getCardStyles; blank-predicate fallback A packages/core/src/evaluator/listConditional.ts#evalRowPredicate; the four reader files byte-identical at objectui main 2e818d0b51; the writer census also read objectui at main; [63] object-kanban.navigation (K = objectui @b9814b334 packages/plugin-kanban/src/ObjectKanban.tsx): K#navConfig (`schema.navigation ?? { mode: 'drawer' }`) K#navigation (useNavigationOverlay) K#boardView (card click) K#renderRecordOverlay (NavigationOverlay); first re-derived at objectui @87af769e9, where the `(schema as any)` cast was still spelled; [64] object-calendar flat field keys (OBJECT_CALENDAR_FLAT_FIELD_GUIDANCE): objectui @b9814b334 packages/plugin-calendar/src/ObjectCalendar.tsx#getCalendarConfig (the backward-compat flat fallback); re-read 2026-10-15, first cited sha-less; [65] object-calendar (ObjectCalendarPropsSchema; C = objectui @eb7f586b packages/plugin-calendar/src/ObjectCalendar.tsx): calendar C#getCalendarConfig; defaultView C#schemaDefaultView; locale, loading via the registry shell objectui @eb7f586b packages/plugin-calendar/src/index.tsx; [66] object-calendar.calendar (CalendarConfigSchema by reference; C = objectui @89cad75d55 packages/plugin-calendar/src/ObjectCalendar.tsx): C#getCalendarConfig returns the block; its five bindings read in C#queryFilter, C#handleEventDropDefault and C#submitQuickCreate; [67] object-calendar.filter: objectui @53ded82b packages/plugin-calendar/src/ObjectCalendar.tsx#fetchData (`$filter: schema.filter`, verbatim); [68] object-tree.navigation: objectui @89cad75d55 packages/plugin-tree/src/ObjectTree.tsx#navigation (useNavigationOverlay); [69] object-calendar.sort: objectui @53ded82b packages/plugin-calendar/src/ObjectCalendar.tsx#fetchData (`$orderby: convertSortToQueryParams(schema.sort)`), whose sink objectui @53ded82b packages/core/src/utils/sort-query.ts#convertSortToQueryParams still lowered the string clause there; no `sort` input on objectui @53ded82b packages/plugin-calendar/src/index.tsx; [70] object-timeline flat config keys (OBJECT_TIMELINE_FLAT_CONFIG_GUIDANCE): the backward-compat flat rung (with the `dateField` alias) in objectui @b9814b334 packages/plugin-timeline/src/ObjectTimeline.tsx#startDateField and its siblings; emitted by objectui @b9814b334 packages/plugin-list/src/ListView.tsx#viewComponentSchema (`case 'timeline'`); re-read 2026-10-15, first cited sha-less; [71] object-calendar.navigation (C = objectui @b9814b334 packages/plugin-calendar/src/ObjectCalendar.tsx): C#navConfig (`schema.navigation ?? { mode: 'drawer' }`) C#navigation (useNavigationOverlay) C#calendarView (event click) C#renderRecordOverlay (NavigationOverlay); [72] object-timeline.mapping (ObjectTimelineMappingSchema; A = objectui @ab1879721595 packages/plugin-timeline/src/ObjectTimeline.tsx; unchanged at objectui main 94985a92ba): title A#titleField; date A#startDateField; description A#descField; variant A#variantField; declared on A#ObjectTimelineProps (`mapping`) and A#TimelineMappingSchema; [73] object-timeline.items[] arms (R = objectui @2e818d0b51ec packages/plugin-timeline/src/renderer.tsx; unchanged at objectui main 2abec3a96): gantt bar startDate / endDate R#dimensions R#calculateDateRange, variant and title in R#TimelineRenderer (the bar and its tooltip); feed entry time R#dateLabel and title, description, variant, icon, content (renderChildren), className in R#TimelineRenderer; gantt row label R#TimelineRenderer (TimelineGanttLabel), items R#classifyGanttRows; vocabulary objectui @2e818d0b51ec packages/types/src/data-display.ts#TimelineItemVariant; element shape and refinement objectui @2e818d0b51ec packages/types/src/zod/data-display.zod.ts#TimelineItemSchema and objectui @2e818d0b51ec packages/types/src/zod/data-display.zod.ts#timelineItemsFitVariant; [74] object-form.layout retired values `inline` / `grid` (OBJECT_FORM_LAYOUT_RETIRED; P = objectui @f8a9d0fb0 packages/plugin-form/src/): folded to 'vertical' in P ObjectForm.tsx#SimpleObjectForm; only 'vertical' / 'horizontal' passed through by P ObjectForm.tsx#drawerLayout, ObjectForm.tsx#modalLayout, DrawerForm.tsx#formLayout, ModalForm.tsx#formLayout; 'vertical' hard-coded in P TabbedForm.tsx#TabbedForm, SplitForm.tsx#panes, WizardForm.tsx#WizardForm; [75] object-form.mobile (ObjectFormMobileSchema; F = objectui @89cad75d55 packages/plugin-form/src/ObjectForm.tsx): F#mobileOpts (the flat arm); stickyActions F#formSchema; stepper, stepperMinFields, stepperFieldsPerStep F#stepperMode; fullscreenLongText F#fieldsWithMobile; the `data-mobile-form` marker in F#SimpleObjectForm; [76] object-form.fields and object-master-detail-form.fields (formFieldNameList): objectui @ab1879721595 packages/plugin-form/src/MasterDetailForm.tsx#parentSchema (hands `fields` over verbatim), read by objectui @ab1879721595 packages/plugin-form/src/ObjectForm.tsx#fieldsToShow and objectui @ab1879721595 packages/plugin-form/src/flatFields.ts#buildFlatFields (a stored `{ name }` entry drawn by name; any other object entry warned by warnUnresolvedTopLevelField); unchanged at objectui main 94985a92ba; the retired inline field entry an author was taught by objectui @ab1879721595 skills/objectui/guides/page-builder.md (its object-form example; first cited sha-less); [77] object-form.customFields[] `grid` widget keys (minRows, maxRows, allowAdd, allowDelete, allowReorder, totalField, addLabel, sortField; W = objectui @9dfaca654311 packages/fields/src/widgets/GridField.tsx): declared on objectui @9dfaca654311 packages/types/src/field-types.ts#GridFieldMetadata, carried by objectui @9dfaca654311 packages/types/src/form.ts#FormField, mirrored in objectui @9dfaca654311 packages/types/src/zod/form.zod.ts#FormFieldSchema; read W#cfg W#allowAdd W#removeRow (minRows) W#totalField W#showTotal W#emit (sortField) W#GridFieldBody (maxRows, allowReorder, addLabel); objectui#11610 (merge 2abec3a96c) camelCased them; the parent-field `totalField` hand-off objectui @9dfaca654311 packages/plugin-form/src/LineItemsPanel.tsx#gridField and objectui @9dfaca654311 packages/plugin-form/src/MasterDetailForm.tsx#MasterDetailLines; the writer shape (one `grid` field carrying all eight camelCase keys) objectui @9dfaca654311 examples/schema-catalog/src/schemas/fields-grid/line-items-grid.json; [78] object-form.customFields[] retired snake_case `grid` keys (OBJECT_FORM_GRID_WIDGET_SNAKE_KEYS): objectui @9dfaca654311 packages/types/src/field-types.ts#GRID_FIELD_RETIRED_KEYS; refused by objectui @9dfaca654311 packages/fields/src/widgets/GridField.tsx#GridField, tombstoned on objectui @9dfaca654311 packages/types/src/field-types.ts#GridFieldMetadata and objectui @9dfaca654311 packages/types/src/form.ts#FormField, named in objectui @9dfaca654311 packages/types/src/zod/form.zod.ts#FormFieldSchema (retiredGridFieldKey); [79] object-form.customFields[].validation: objectui @2e818d0b51ec packages/components/src/renderers/form/form.tsx#rules (inside renderFormField: minLength/maxLength/min/max spread as `{ value, message }`, `required` read only as a message string); objectui's declaration objectui @2e818d0b51ec packages/types/src/form.ts#FieldValidationRules; [80] object-timeline.navigation: objectui @b9814b334 packages/plugin-timeline/src/ObjectTimeline.tsx#navigation (useNavigationOverlay) and objectui @b9814b334 packages/plugin-timeline/src/ObjectTimeline.tsx#effectiveSchema (entry click, overlay); re-read 2026-10-15, first cited sha-less, when the read still spelled `(schema as any).navigation`; [81] object-form.customFields[].options[] (buildObjectFormRuntimeOption; F = objectui @2e818d0b51ec packages/components/src/renderers/form/form.tsx): label, value F#BuiltinSelectControl and objectui @2e818d0b51ec packages/core/src/evaluator/optionRules.ts#optionDisplayLabel (the four option widgets); visibleWhen F#cascade (resolveCascadingOptions; each widget's useCascadingOptions); description objectui @2e818d0b51ec packages/fields/src/widgets/LookupField.tsx#filteredOptions; `value` a runtime value: F#BuiltinSelectControl (matchOptionValue), objectui @2e818d0b51ec packages/types/src/zod/form.zod.ts#SelectOptionSchema (deliberate divergence comment), objectui @2e818d0b51ec packages/plugin-designer/src/ObjectManager.tsx#formSchema (writes `{ label, value }` pairs); unchanged at objectui main fd060f076; [82] object-form.customFields[] and the inline section entry (buildObjectFormRuntimeField; F = objectui @2e818d0b51ec packages/components/src/renderers/form/form.tsx, W = objectui @2e818d0b51ec packages/fields/src/widgets/): merge objectui @2e818d0b51ec packages/plugin-form/src/customFieldsMerge.ts#mergeCustomFields from objectui @2e818d0b51ec packages/plugin-form/src/ObjectForm.tsx#generateField; carrier F#builtinBranchHasNoControl (`field.field || field`); field row F#renderFormField (name, label, description, type, required, disabled, readonly, hidden, colSpan via F#colSpanClass), widget F#resolvedType, validation F#rules, the three `*When` rules F#ruleState, placeholder and inputType F#builtinBranchHasNoControl, options and dependsOn F#cascade, multiple F#resolvedType; layout objectui @2e818d0b51ec packages/plugin-form/src/autoLayout.ts#resolveColSpan (span, colSpan) and objectui @2e818d0b51ec packages/plugin-form/src/fieldGroups.ts#deriveFieldGroupSections (group); widgets W TextAreaField.tsx#rows, FileField.tsx#multiple (accept, multiple), VectorField.tsx#dimensions, LookupField.tsx#referenceTo, NumberField.tsx#NumberField (min, max), F#renderFieldComponent (minLength, maxLength), FormulaField.tsx#returnType, SummaryField.tsx#summaryType, GridField.tsx#allColumns (columns); read and refused: visibleOn F#ruleState, condition F#legacyConditionCel, id F#fieldKey; a measured writer, the object-manager create / edit dialog (a `formType: 'modal'` block): objectui @b9814b334 packages/plugin-designer/src/ObjectManager.tsx#formSchema (first cited sha-less); [83] object-form / object-master-detail-form sections[].fields[] `{ field }` entry (buildObjectFormSectionFieldEntry): objectui @2e818d0b51ec packages/plugin-form/src/sectionFields.ts#resolveSectionEntry (names an object field by `field`, every other key overrides its generated definition; label/placeholder/helpText copied as they stand); [84] object-form / object-master-detail-form sections[] (buildObjectFormSection; F = objectui @2e818d0b51ec packages/plugin-form/src/ObjectForm.tsx): handed over verbatim by objectui @2e818d0b51ec packages/plugin-form/src/MasterDetailForm.tsx#parentSchema; name, label F#sectionCtx (heading) F#inertPredicateSections (per-formType maps); collapsible, collapsed F#collapse (resolveSectionCollapse); visibleWhen F#sectionCtx (divider-row predicate); columns F#secCols F#clampCol (a NUMBER only); pane objectui @2e818d0b51ec packages/plugin-form/src/SplitForm.tsx#paneOf; the inline runtime field drawn as it stands in objectui @2e818d0b51ec packages/plugin-form/src/sectionFields.ts#resolveSectionEntry; objectui's own declaration objectui @2e818d0b51ec packages/types/src/objectql.ts#ObjectFormSection; [85] object-form (ObjectFormPropsSchema: the UNION of `schema.*` reads of the simple form and the sub-forms it forwards the bag into): objectui @eb7f586b packages/plugin-form/src/ObjectForm.tsx#ObjectForm, with TabbedForm, WizardForm, SplitForm, DrawerForm, ModalForm under objectui @eb7f586b packages/plugin-form/src/; `layout` `inline` / `grid` folded to `vertical` there while the registry inputs and the designer palette offered them; [86] object-form.fields (the `{ name }` entry retired from the authoring faces by objectui#11550): a census of objectui @ab1879721595 found it written only by fixtures probing the stored read and the refusal; [87] object-form.customFields: objectui @2e818d0b51ec packages/plugin-form/src/customFieldsMerge.ts#mergeCustomFields (merged over the generated fields); at objectui @2e818d0b51ec the open runtime field spelled the eight `grid` widget keys in snake_case, camelCased by objectui#11610 (carried at objectui @9dfaca654311); [88] object-form.contentLayout: objectui @89cad75d55 packages/plugin-form/src/ModalForm.tsx#renderContent (`schema.contentLayout === 'tabbed' && groups.length > 1`), declared on objectui @89cad75d55 packages/plugin-form/src/ModalForm.tsx#ModalFormSchema; [89] object-form.submitBehavior: objectui @89cad75d55 packages/plugin-form/src/ObjectForm.tsx#handleSubmit and objectui @89cad75d55 packages/plugin-form/src/WizardForm.tsx#handleStepSubmit (switch on `kind`), redirect `url` judged by objectui @89cad75d55 packages/plugin-form/src/submitRedirect.ts#resolveSubmitRedirect (FormViewSchema.safeParse); [90] object-form.navigateOnSuccess: objectui @89cad75d55 packages/plugin-form/src/successBehavior.ts#resolveSuccessNavigate, called from objectui @89cad75d55 packages/plugin-form/src/ObjectForm.tsx#handleSubmit and objectui @89cad75d55 packages/plugin-form/src/WizardForm.tsx#handleStepSubmit; a refused value reported on the success toast in objectui @89cad75d55 packages/plugin-form/src/ObjectForm.tsx#handleSubmit; [91] object-master-detail-form.details[] (the eleven entry keys): objectui @31971ff1e28f packages/plugin-form/src/MasterDetailForm.tsx#MasterDetailDetailConfig and every `d.KEY` read in objectui @31971ff1e28f packages/plugin-form/src/MasterDetailForm.tsx#MasterDetailLines; re-read at objectui @89cad75d5570 for #21589; [92] object-master-detail-form.details[].sortField (REMOVED, tombstone): objectui @89cad75d5570 packages/plugin-form/src/MasterDetailForm.tsx#MasterDetailDetailConfig (no `sortField` member), derived by objectui @89cad75d5570 packages/plugin-form/src/deriveMasterDetail.ts#deriveDetail (SORT_FIELD_NAMES) and handed to the grid as `sort_field` in objectui @89cad75d5570 packages/plugin-form/src/MasterDetailForm.tsx#MasterDetailLines; at objectui @9dfaca654311 the same hand-off spells `sortField` (objectui#11610) from objectui @9dfaca654311 packages/plugin-form/src/deriveMasterDetail.ts#deriveDetail; an entry the renderer does not resolve loads no child schema (objectui @9dfaca654311 packages/plugin-form/src/MasterDetailForm.tsx#entries); [93] object-master-detail-form.sections, .fields: objectui @2e818d0b51ec packages/plugin-form/src/MasterDetailForm.tsx#parentSchema (handed to the parent object-form verbatim); [94] object-map flat config keys (OBJECT_MAP_FLAT_CONFIG_GUIDANCE): objectui @b9814b334 packages/plugin-map/src/ObjectMap.tsx#getMapConfig (branch 2) and objectui @b9814b334 packages/plugin-map/src/ObjectMap.tsx#warnOnShadowedFlatMapKeys and objectui @b9814b334 packages/plugin-map/src/ObjectMap.tsx#FLAT_MAP_CONFIG_KEYS; emitted by objectui @b9814b334 packages/plugin-view/src/ObjectView.tsx#ObjectView and objectui @b9814b334 packages/plugin-list/src/ListView.tsx#viewComponentSchema (`case 'map'`); re-read 2026-10-15, first cited sha-less at the then pin; [95] object-map (ObjectMapPropsSchema, per key; P = objectui @b9814b334 packages/plugin-map/src/ObjectMap.tsx, C = objectui @b9814b334 packages/core/src/utils/record-source.ts, I = objectui @b9814b334 packages/plugin-map/src/index.tsx): data P#getDataConfig C#resolveRecordSourceConfig (rung 1; the deleted array head and its carriers are recorded in the docblock above P#FLAT_MAP_CONFIG_KEYS); staticData C#resolveRecordSourceConfig (rung 2); objectName C#resolveRecordSourceConfig (rung 3) P#navigation P#warnOnLegacyFilterMapConfig P#warnOnTopLevelStyleUrl P#warnOnShadowedFlatMapKeys (cache keys) P#recordSourceObjectName (useSettledSchema); filter P#queryFilter P#fetchData (`$filter` on both fetches); sort P#fetchData (convertSortToQueryParams); map P#getMapConfig (branch 1; validates against ObjectMapConfigSchema) I#ObjectMapRenderer (its registration's `map` input); mapStyle P#getMapConfig (`schema.mapStyle || schema.map?.style`) I#ObjectMapRenderer; navigation P#navigation I#ObjectMapRenderer; enableClustering P#clusteredData I#ObjectMapRenderer; measured and not declared: style P#warnOnTopLevelStyleUrl (read only to say it is not a map style); [96] object-map.filter: objectui @b9814b334 packages/plugin-map/src/ObjectMap.tsx#queryFilter (useResolvedFilter) and objectui @b9814b334 packages/plugin-map/src/ObjectMap.tsx#fetchData (`$filter` on the inline and the object fetch); the registration's `filter` input objectui @b9814b334 packages/plugin-map/src/index.tsx#ObjectMapRenderer; [97] object-map.sort: objectui @b9814b334 packages/plugin-map/src/ObjectMap.tsx#fetchData (convertSortToQueryParams to `$orderby`, both fetches); the registration's array-form `sort` input (objectui#8220) objectui @b9814b334 packages/plugin-map/src/index.tsx#ObjectMapRenderer; [98] object-map.navigation: objectui @89cad75d55 packages/plugin-map/src/ObjectMap.tsx#navigation (hands `schema.navigation` to useNavigationOverlay) and objectui @89cad75d55 packages/react/src/hooks/useNavigationOverlay.ts#useNavigationOverlay (`navigation?.mode ?? 'page'`); [99] object-gantt flat config keys (OBJECT_GANTT_FLAT_CONFIG_GUIDANCE): objectui @b9814b334 packages/plugin-gantt/src/ObjectGantt.tsx#FLAT_GANTT_CONFIG_KEYS and objectui @b9814b334 packages/plugin-gantt/src/ObjectGantt.tsx#getGanttConfig (branch 2 reads them; a present `gantt` block is taken whole and the flat keys beside it named as ignored); re-read 2026-10-15, first cited sha-less at the then pin; [100] object-gantt.markers (objectGanttMarker): G = objectui @ab1879721595 packages/plugin-gantt/src/GanttView.tsx: objectui @ab1879721595 packages/plugin-gantt/src/ObjectGantt.tsx#ganttView (hands `schema.markers` to GanttView verbatim); date G#markers (time-zone re-base) G#resolvedMarkers (axis mapping, drops unparsable or out-of-range dates); color G#resolvedMarkers (`m.color || 'hsl(var(--primary))'`); drawn in G#GanttView and in G#buildExportSvg; the declaration taken: objectui @ab1879721595 packages/types/src/objectql.ts#ObjectGanttSchema and objectui @ab1879721595 packages/types/src/zod/objectql.zod.ts#ObjectGanttSchema (`markers`); unchanged at objectui main 94985a92ba; [101] object-gantt (ObjectGanttPropsSchema, per key; P = objectui @b9814b334 packages/plugin-gantt/src/ObjectGantt.tsx, C = objectui @b9814b334 packages/core/src/utils/record-source.ts): data P#rawDataConfig C#resolveRecordSourceConfig (rung 1); staticData C#resolveRecordSourceConfig (rung 2); objectName (rung 3) P#persistLayoutKey P#navigation; filter P#queryFilter P#reload (`$filter`); sort P#reload (convertSortToQueryParams to `$orderby`); gantt P#getGanttConfig (branch 1, validated against the spec's GanttConfigSchema) and objectui @b9814b334 packages/plugin-gantt/src/index.tsx#ObjectGanttRenderer (its registration's `gantt` object input); navigation P#navConfig P#navigation; label P#ganttView (resolveInlineI18nLabel, export file-name chain); skipWeekends, holidays P#workingCalendar; persistLayout, viewName P#persistLayoutKey; markers, criticalPath, showBaselines, readOnly, mobileReadOnly P#ganttView, readOnly also P#renderRecordOverlay; measured and not declared: title (absent from the P#ganttView export-name chain), row cap (P#reload spreads nonGridRowCeilingQuery, marked not authorable), search and searchableFields P#searchTerm P#reload (host-generated by objectui @b9814b334 packages/plugin-list/src/ListView.tsx#viewComponentSchema, its `case 'gantt'` arm); [102] object-gantt.navigation: objectui @89cad75d55 packages/plugin-gantt/src/ObjectGantt.tsx#navConfig (`schema.navigation ?? { mode: 'drawer' }`) and objectui @89cad75d55 packages/plugin-gantt/src/ObjectGantt.tsx#navigation (useNavigationOverlay); [103] object-tree flat config keys (OBJECT_TREE_FLAT_CONFIG_GUIDANCE: parentField, labelField, titleField, fields, defaultExpandedDepth): objectui @b9814b334 packages/plugin-tree/src/ObjectTree.tsx#getTreeConfig (reads the flat spellings ahead of the `tree` block; the `schema.titleField` rung's retirement is recorded in its docblock above `fieldKey`, objectui#8841), objectui @b9814b334 packages/plugin-list/src/ListView.tsx#viewComponentSchema (its `case 'tree'` arm emits parentField, `labelField: treeCfg.labelField || 'name'`, fields, defaultExpandedDepth and no titleField since objectui 3fd862510), objectui @b9814b334 packages/plugin-tree/src/index.tsx#treeInputs (`{ name: 'tree', type: 'object' }`, no flat input); [104] object-tree (ObjectTreePropsSchema, per key; P = objectui @b9814b334 packages/plugin-tree/src/ObjectTree.tsx, C = objectui @b9814b334 packages/core/src/utils/record-source.ts): data P#dataConfig (resolveRecordSourceConfig with the `view-data` arm) and C#resolveRecordSourceConfig (rung 1, authoredDataIsOnTheDeclaredArm); staticData C#resolveRecordSourceConfig (rung 2); objectName (rung 3) P#headerObjectName P#navigation P#renderRecordOverlay (each behind resolveRecordSourceObjectName) P#parentField (detectParentField); filter P#queryFilter (useResolvedFilter) P#fetchData (`$filter` on the object fetch and on the inline ValueDataSource query); tree P#getTreeConfig and objectui @b9814b334 packages/plugin-tree/src/index.tsx#treeInputs; navigation P#navigation (useNavigationOverlay); host-prop `data` (not the authored key) P#passed; measured and not declared: sort (no `$orderby` in P#fetchData), row cap (P#fetchData spreads nonGridRowCeilingQuery); [105] object-tree.objectName (optional because no ElementDataSourceGate wiring): objectui @b9814b334 packages/plugin-tree/src/index.tsx#ObjectTreeRenderer has 0 occurrences of `ElementDataSourceGate` (re-counted 2026-10-15), against controls objectui @b9814b334 packages/plugin-gantt/src/index.tsx#ObjectGanttRenderer, packages/plugin-grid/src/index.tsx, packages/plugin-calendar/src/index.tsx (3 each) and packages/plugin-map/src/index.tsx#ObjectMapRenderer (4); method: occurrences of the identifier in each `src/index.tsx`; [106] object-timeline (ObjectTimelinePropsSchema, per key; P = objectui @b9814b334 packages/plugin-timeline/src/ObjectTimeline.tsx, R = objectui @b9814b334 packages/plugin-timeline/src/renderer.tsx, I = objectui @b9814b334 packages/plugin-timeline/src/index.tsx): objectName P#ObjectTimeline (fetch gate) P#useSettledSchema P#fetchesForItself P#invalidationNonce P#fetchData P#handleRefresh P#effectiveItems P#navigation; timeline P#timelineConfig; filter P#queryFilter P#filterKey P#fetchData; sort P#sortKey P#fetchData (convertSortToQueryParams); limit P#fetchData (resolveRowLimit) P#DEFAULT_TIMELINE_LIMIT P#describeRefusedRowLimit; items P#hasInlineItems P#fetchesForItself P#effectiveItems P#hasAuthoredItems; data P#hasInlineData P#fetchesForItself P#rawData; descriptionField P#descField; mapping P#titleField P#startDateField P#descField P#variantField; variant P#hasAuthoredItems (the `timeline-unsupported-variant` refusal, objectui#6655) and I#OBJECT_TIMELINE_INPUTS; navigation P#navigation P#effectiveSchema; dateFormat, rowLabel, minDate, maxDate R#TimelineRenderer (rowLabel and minDate/maxDate in its gantt branch, R#findUnusableGanttDate) off P#effectiveSchema; measured and not declared: bind P#boundData (useDataScope), className P#effectiveSchema; [107] object-timeline.data (read off React props, not `schema`; the prop channel is SchemaRenderer's `properties.*` hoist, objectui @b9814b334 packages/react/src/SchemaRenderer.tsx#SchemaRenderer, with `object-timeline` absent from objectui @b9814b334 packages/core/src/utils/record-source.ts#RECORD_SOURCE_DATA_ARM_BY_TYPE): objectui @b9814b334 packages/plugin-timeline/src/ObjectTimeline.tsx#hasInlineData (loading seed), objectui @b9814b334 packages/plugin-timeline/src/ObjectTimeline.tsx#fetchesForItself (the query skip the fetch effect reads), objectui @b9814b334 packages/plugin-timeline/src/ObjectTimeline.tsx#rawData (row source `(props as any).data || boundData || fetchedData`), objectui @b9814b334 packages/plugin-timeline/src/ObjectTimeline.tsx#effectiveItems (the `timeline` field bindings a fetched row takes); [108] object-timeline.items: objectui @2e818d0b51ec packages/types/src/data-display.ts#TimelineFeedItem and objectui @2e818d0b51ec packages/types/src/data-display.ts#TimelineGanttItem (the declared authored entry, ruled on objectui#6356), handed to the rail verbatim at objectui @2e818d0b51ec packages/plugin-timeline/src/ObjectTimeline.tsx#effectiveItems (`if (schema.items) return schema.items`); [109] record:reference_rail, record:alert, record:quick_actions, record:history, record:line_items (rows): key sets measured from the renderers' read points; the per-key read points are recorded under each props schema below; [110] record:approval_decision (row, EMPTY key set): objectui @d03b022 packages/app-shell/src/views/approval-decision/ApprovalDecisionPanel.tsx#ApprovalDecisionRenderer (landed in objectui a368ccb, module-internal at d03b022; discards `schema`, draws from the record context); [111] record:attachments (row, EMPTY key set): objectui @47b1f0bb71 packages/app-shell/src/views/record-attachments-renderer.tsx#RecordAttachmentsRenderer (registered as `attachments` in the `record` namespace; discards `schema`); [112] cloud-connection:panel, marketplace:installed-list (rows, EMPTY key sets): objectui @b9814b334 packages/app-shell/src/console/cloud-connection/CloudConnectionPanel.tsx#CloudConnectionPanel and objectui @b9814b334 packages/app-shell/src/console/marketplace/InstalledListWidget.tsx#InstalledList (each registration is `() => `; re-read 2026-10-15, first measured sha-less at the then pin); [113] mcp:connect-agent (row, EMPTY key set): objectui @b9814b334 packages/app-shell/src/console/connect/ConnectAgentWidget.tsx#ConnectAgent (registration `() => `; re-read 2026-10-15, first measured sha-less at the then pin); [114] element:definition-list, element:repeater (rows: the three-part string-arm evidence): objectui @b9814b334 packages/components/src/renderers/basic/data-list.tsx#DefinitionListRenderer and objectui @b9814b334 packages/components/src/renderers/basic/data-list.tsx#RepeaterRenderer (both registered in the `element` namespace), objectui @b9814b334 packages/core/src/registry/public-blocks.ts#PUBLIC_BLOCKS (publication), objectui @b9814b334 packages/app-shell/src/views/metadata-admin/previews/block-types.ts#BLOCK_TYPE_META (palette), objectui @b9814b334 packages/app-shell/src/views/metadata-admin/previews/block-config.ts#BLOCK_CONFIG (each block's inspector); [115] action:button, action:group, action:menu, action:icon (rows): objectui @b9814b334 packages/core/src/registry/public-blocks.ts#PUBLIC_BLOCKS (the four `action:*` members), registered under objectui packages/components/src/renderers/action/ in the `action` namespace; [116] object-map, object-gantt, object-tree (row key sets; per-key read points under each props schema): objectui @b9814b334 packages/plugin-map/src/index.tsx#ObjectMapRenderer, objectui @b9814b334 packages/plugin-map/src/ObjectMap.tsx#ObjectMap, objectui @b9814b334 packages/plugin-gantt/src/index.tsx#ObjectGanttRenderer, objectui @b9814b334 packages/plugin-gantt/src/ObjectGantt.tsx#ObjectGantt, objectui @b9814b334 packages/plugin-tree/src/index.tsx#ObjectTreeRenderer, objectui @b9814b334 packages/plugin-tree/src/ObjectTree.tsx#ObjectTree; [117] object-timeline (row key set; per-key read points under ObjectTimelinePropsSchema): objectui @b9814b334 packages/plugin-timeline/src/index.tsx#ObjectTimelineRenderer (the `object-timeline` and `timeline` registrations), objectui @b9814b334 packages/plugin-timeline/src/ObjectTimeline.tsx#ObjectTimeline, objectui @b9814b334 packages/plugin-timeline/src/renderer.tsx#TimelineRenderer; [118] EXPRESSION_BINDABLE_TEXT_KEYS_BY_COMPONENT rows statistic, card, button (the top-level text keys each renderer reads back; R = objectui @82a9417 packages/components/src/renderers/, re-verified identical at objectui 6c68b13 on 2026-08-18): statistic `label`, `value`, `description` R data-display/statistic.tsx#StatisticRenderer; card `title`, `description` R layout/card.tsx#CardRenderer; button `label` R form/button.tsx#ButtonRenderer; the deliberate `text` omission, `schema.content || schema.value`, at its `text` registration in R basic/text.tsx; [119] PageComponent.dataSource, the PLANNED API binding (ElementApiDataSourceSchema; its record is this file's `_note`; R = objectui @7578323a544e packages/core/src/): `read.url`'s refusal of a `./` path, measured against the console's `apiBase` promotion of a root-relative `/api/…` path only, R actions/ActionRunner.ts#navigateTo; `read.method` GET alone because every read (find, findOne, aggregate) sends an explicit `method: 'GET'`, R adapters/ApiDataSource.ts#ApiDataSource; no renderer reads the binding yet, the hook admitting only a string `object`, R data-scope/element-data-source.ts#isElementDataSourceConfig; [120] PageComponent.visibleWhen (the component-level evaluator's roots): the ambient scope (`current_user`, `user`, `ctx.user`, `os.user`, `features`, nothing else) objectui @1fb1ced36243 packages/app-shell/src/providers/ExpressionProvider.tsx#buildExpressionScope; composed with `record` (only when the page has a row) and `page` by objectui @1fb1ced36243 packages/react/src/SchemaRenderer.tsx#configEvaluationScope; no `data` root (objectui#9308) in objectui @1fb1ced36243 packages/react/src/SchemaRenderer.tsx#evaluatedSchema; the files identical at objectui main f228d710; the `page:tabs` item-level evaluator is the `page:tabs items[].visibleWhen` entry;" }, "assignedProfiles": { "status": "dead", @@ -67,7 +67,7 @@ }, "interfaceConfig": { "status": "live", - "note": "the entire Airtable-parity list surface (source/columns/filterBy/appearance/userFilters/userActions/addRecord/buttons/recordAction/...) — objectui app-shell/src/views/InterfaceListPage.tsx + PageView.tsx. Verified in browser during the dogfood (grid+kanban+filters). [#23005] This blanket `live` verdict covers `interfaceConfig.emptyState` too: the list page's own empty state, the list view's EmptyStateSchema by reference. It was declared ahead of its renderer, contract first, with the `[PLANNED — not yet rendered]` marker on its describe; the renderer half, objectstack-ai/objectui#12270 (objectui b434f5f47), is inside the console pin from objectui b9814b334, and the marker is dropped. Measured there: InterfaceListPage hands ListView `cfg.emptyState ?? view.emptyState` (InterfaceListPage.tsx:509), so the page's own empty state wins over the source view's. `page/interfaceConfig` is a recorded undrilled container (undrilled-containers.baseline.json), so the walk reads no row for the member." + "note": "the entire Airtable-parity list surface (source/columns/filterBy/appearance/userFilters/userActions/addRecord/buttons/recordAction/...) — objectui app-shell/src/views/InterfaceListPage.tsx + PageView.tsx. Verified in browser during the dogfood (grid+kanban+filters). [#23005] This blanket `live` verdict covers `interfaceConfig.emptyState` too: the list page's own empty state, the list view's EmptyStateSchema by reference. It was declared ahead of its renderer, contract first, with the `[PLANNED — not yet rendered]` marker on its describe; the renderer half, objectstack-ai/objectui#12270 (objectui b434f5f47), is inside the console pin from objectui b9814b334, and the marker is dropped. Measured there: InterfaceListPage hands ListView `cfg.emptyState ?? view.emptyState` (InterfaceListPage.tsx:509), so the page's own empty state wins over the source view's. `page/interfaceConfig` is a recorded undrilled container (undrilled-containers.baseline.json), so the walk reads no row for the member. The reading before objectui#12270 (first cited by the Studio-form omit row for `interfaceConfig.emptyState` in packages/spec/src/system/metadata-form-zod-reconciliation.test.ts, which #23568 removed once the renderer half shipped; recorded here by #23434): objectui @7af0ab3 packages/app-shell/src/views/InterfaceListPage.tsx#schema handed ListView the source view's `emptyState` only; at objectui @b9814b334 the same symbol hands `cfg.emptyState ?? view.emptyState`." }, "aria": { "status": "live", diff --git a/packages/spec/liveness/view.json b/packages/spec/liveness/view.json index eb03dbffb36..0db4d586190 100644 --- a/packages/spec/liveness/view.json +++ b/packages/spec/liveness/view.json @@ -92,7 +92,7 @@ }, "kanban": { "status": "live", - "note": "objectui: KanbanImpl.tsx:682 — field-by-field wired (audit L16)." + "note": "objectui: KanbanImpl.tsx:682 — field-by-field wired (audit L16). What `groupByField` names, on kanban, gantt and timeline alike (packages/spec/src/ui/view.zod.ts, the padded-spelling refusal; moved out of spec source, #23434): a lookup BY THAT NAME on data the server answers under the unpadded name, at objectui @dda8f3815 packages/plugin-list/src/ListView.tsx#laneField (`groupByField || groupField || detectStatusField(objectDef)`, inside #viewComponentSchema; cards bucketed by `card[laneField]`), objectui @dda8f3815 packages/plugin-gantt/src/ObjectGantt.tsx#groupByAccessor (splits on `.` and walks the record) and objectui @dda8f3815 packages/plugin-timeline/src/ObjectTimeline.tsx#effectiveItems (`item[groupByField]`)." }, "calendar": { "status": "live", @@ -186,7 +186,7 @@ "bulkActionDefs": { "status": "live", "evidence": "packages/spec/src/ui/bulk-action.zod.ts", - "note": "objectui: ListView.tsx:1343 forwards rich defs to ObjectGrid (BulkActionDialog). Dispatch: useBulkExecutor.ts run() — per-record fan-out by default; execution:'aggregate' defs go through the ONE-call bulkCall branch injecting params._selectedIds (ObjectGrid.runBulkActionAggregate, objectui#3139). Verified objectui@4bf612c. #4457 gave the def a SHAPE (it was z.record(z.any())): the entry schema is BulkActionDefSchema, strict, and it refuses the combinations that parse but the executor never reads — so this row's liveness now covers the keys inside a def, not just the array." + "note": "objectui: ListView.tsx:1343 forwards rich defs to ObjectGrid (BulkActionDialog). Dispatch: useBulkExecutor.ts run() — per-record fan-out by default; execution:'aggregate' defs go through the ONE-call bulkCall branch injecting params._selectedIds (ObjectGrid.runBulkActionAggregate, objectui#3139). Verified objectui@4bf612c. #4457 gave the def a SHAPE (it was z.record(z.any())): the entry schema is BulkActionDefSchema, strict, and it refuses the combinations that parse but the executor never reads — so this row's liveness now covers the keys inside a def, not just the array. A PROMOTED def's field-backed params (packages/spec/src/ui/bulk-action.zod.ts; moved out of spec source, #23434): resolved when the dialog opens by objectui main @dca25aff2 packages/plugin-grid/src/resolveBulkActions.ts#resolvePromotedBulkParams through `resolveActionParams` (objectui 29b99490), then renamed onto the bulk vocabulary by the same file's #toBulkParam, which carries the field's type, translated label, options, required, description, placeholder, defaultValue, multiple, `dependsOn`, `accept` and `maxSize` (plus a reference-bearing field's reference and display field) and drops the lookup settings." }, "virtualScroll": { "status": "dead", @@ -215,7 +215,7 @@ }, "appearance": { "status": "live", - "note": "objectui: allowedVisualizations gates the view-type switcher (audit L15); showDescription gates ListView.tsx:1728." + "note": "objectui: allowedVisualizations gates the view-type switcher (audit L15); showDescription gates ListView.tsx:1728. objectui's authoring mirror runs none of the spec's object-level checks on this block (packages/spec/src/ui/object-refinement-check-exports.test.ts; moved out of spec source, #23434): objectui @a472b07167a3 packages/types/src/zod/objectql.zod.ts#ListViewSchema extends `specFieldsExcept(SpecListViewSchema.shape, …)`, which carries the fields and drops every check attached to the object (the source counted six such sites at that pin); unchanged at objectui @53ded82bf7a4." }, "tabs": { "status": "dead", @@ -238,7 +238,7 @@ "emptyState": { "status": "live", "evidence": "packages/cli/src/utils/i18n-extract.ts", - "note": "objectui: ListView empty-state panel (audit L15); CLI i18n extract emits _views..emptyState keys (spec CHANGELOG). [#23004] This blanket `live` verdict covers `emptyState.link` too. It was declared ahead of its renderer, contract first, with the `[PLANNED — not yet rendered]` marker on its describe; the renderer half, objectstack-ai/objectui#12270 (objectui b434f5f47), is inside the console pin from objectui b9814b334, and the marker is dropped. Measured there: ListView hands ListEmptyState the `link` (ListView.tsx:5716), drawn through the shared EmptyStateLink in the grid and gallery empty branch, and ObjectGrid draws it in its authored empty state (ObjectGrid.tsx:6897-6899). `view/list.emptyState` is a recorded undrilled container (undrilled-containers.baseline.json), so the walk reads no row for the member." + "note": "objectui: ListView empty-state panel (audit L15); CLI i18n extract emits _views..emptyState keys (spec CHANGELOG). [#23004] This blanket `live` verdict covers `emptyState.link` too. It was declared ahead of its renderer, contract first, with the `[PLANNED — not yet rendered]` marker on its describe; the renderer half, objectstack-ai/objectui#12270 (objectui b434f5f47), is inside the console pin from objectui b9814b334, and the marker is dropped. Measured there: ListView hands ListEmptyState the `link` (ListView.tsx:5716), drawn through the shared EmptyStateLink in the grid and gallery empty branch, and ObjectGrid draws it in its authored empty state (ObjectGrid.tsx:6897-6899). `view/list.emptyState` is a recorded undrilled container (undrilled-containers.baseline.json), so the walk reads no row for the member. The `link`'s draw site beside the add-record action (packages/spec/src/ui/view.zod.ts, EmptyStateSchema; moved out of spec source, #23434): objectui @b9814b334 packages/plugin-list/src/ListEmptyState.tsx#addRecordAction, inside #ListEmptyState." }, "aria": { "status": "live", diff --git a/packages/spec/src/data/api-methods-batch-conformance.test.ts b/packages/spec/src/data/api-methods-batch-conformance.test.ts index 1b000ecc38e..41eda68572f 100644 --- a/packages/spec/src/data/api-methods-batch-conformance.test.ts +++ b/packages/spec/src/data/api-methods-batch-conformance.test.ts @@ -60,228 +60,8 @@ const SINGLE_RECORD_WRITE_ONLY: Record = { // one column: the `revoke_api_key` / `restore_api_key` row actions PATCH // `revoked` on ONE key. The multi-select surface this rule protects does not // exist for API keys, and the shape a future one would take does not need - // `bulk` either — both read off objectui `b9814b334` - // (re-read there 2026-10-14: - // in `packages/plugin-grid`, on the hop off `1fb1ced36`, `ObjectGrid.tsx` - // changed again (327 insertions, 80 deletions: objectui#12292's per-row - // If-Match saves, objectui#12303's save feedback, objectui#12030's spec 18 - // contract and objectui#12270's empty-state link), none of it inside the - // selection block, which only MOVED by +48: `ObjectGrid.tsx:5045-5072` here - // (`4997-5024` at `1fb1ced36`), still hashing to - // `c88443302d40c2db739ddb235470bafa29056e2e` (hash-object of the block), - // re-READ with the same reading below. No hunk sits beside that reading: the - // `delete` verdict line MOVED `:1793` -> `:1873` and the promoted def's - // params `:5181-5200` -> `:5229-5248` (called at `:5216` -> `:5264`), each - // byte-identical. `hooks/useBulkExecutor.ts` is byte-identical to `1fb1ced36` - // and its `:298-303` still hashes to - // `01083348330f10a201cdf1078b4c21c236402b6a`. At `1fb1ced36`, re-read there - // 2026-10-12: - // every objectui file this record cites is byte-identical across the hop from - // `7578323a5` (`git diff --quiet`), so every anchor held unmoved. At - // `7578323a5`, re-read there 2026-10-12: - // every objectui file this record cites is byte-identical across the hop from - // `9646e2ed2` (`git diff --quiet`), so every anchor held unmoved. At - // `9646e2ed2`, re-read there 2026-10-12: - // every objectui file this record cites is byte-identical across the hop from - // `088e0f705` (`git diff --quiet`), so every anchor held unmoved. At - // `088e0f705`, re-read there 2026-10-12: - // every objectui file this record cites is byte-identical across the hop from - // `e5311165a` (`git diff --quiet`), so every anchor held unmoved. At - // `e5311165a`, re-read there 2026-10-12: - // in `packages/plugin-grid`, on the hop off `499799502`, `ObjectGrid.tsx` - // changed again (27 insertions, 11 deletions: objectui#12110's authored - // column label and objectui#12143's grouped-pager button names), none of it - // inside the selection block, which only MOVED by +16: - // `ObjectGrid.tsx:4997-5024` here (`4981-5008` at `499799502`), still hashing to - // `c88443302d40c2db739ddb235470bafa29056e2e` (hash-object of the block), - // re-READ with the same reading below. No hunk sits beside that reading: the - // `delete` verdict line MOVED `:1787` -> `:1793` and the promoted def's params - // `:5165-5184` -> `:5181-5200` (called at `:5200` -> `:5216`), each - // byte-identical. `hooks/useBulkExecutor.ts` is byte-identical to `499799502` - // and its `:298-303` still hashes to - // `01083348330f10a201cdf1078b4c21c236402b6a`. At - // `499799502`, re-read there 2026-10-11: - // in `packages/plugin-grid`, on the hop off `0df67f237`, `ObjectGrid.tsx` - // changed again (89 insertions, 44 deletions: objectui#12081 item 3's - // whole-set footer and objectui#12082 leg A), none of it inside the - // selection block, which only MOVED by +84: `ObjectGrid.tsx:4981-5008` here - // (`4897-4924` at `0df67f237`), still hashing to - // `c88443302d40c2db739ddb235470bafa29056e2e` (hash-object of the block), - // re-READ with the same reading below. Two hunks sit beside that reading and - // were re-READ as well: objectui#12081 item 3 moved the `bulkFanoutParams` - // and `resolvedTotalMatching` declarations, their code byte-identical, above - // the footer's hook; and objectui#12082 now also withholds a bulk `update` - // def unless the edit verdict (`rowEditVerdict`) allows it, which narrows the - // declared defs and adds no implicit one. The `delete` verdict line MOVED - // `:1786` -> `:1787` and the promoted def's params `:5097-5116` -> - // `:5165-5184` (called at `:5132` -> `:5200`), each byte-identical. - // `hooks/useBulkExecutor.ts` is byte-identical to `0df67f237` and its - // `:298-303` still hashes to `01083348330f10a201cdf1078b4c21c236402b6a`. At - // `0df67f237`, re-read there 2026-10-11: - // in `packages/plugin-grid`, on the hop off `20c6d351a`, `ObjectGrid.tsx` - // changed again (82 insertions, 32 deletions: objectui#12082's - // affordance-to-grant map, objectui#12103's per-column write grant and - // objectui#12105's promoted bulk-action params), none of it inside the - // selection block, which only MOVED by +29: `ObjectGrid.tsx:4897-4924` here - // (`4868-4895` at `20c6d351a`), still hashing to - // `c88443302d40c2db739ddb235470bafa29056e2e` (hash-object of the block), - // re-READ with the same reading below. Two hunks touch that reading and were - // re-READ as well: the `delete` verdict the implicit bulk-delete is gated on - // now comes from `resolveAffordance('rowDelete', …)` (`:1786`), which still - // asks the managed-object policy and the object's effective API operations, - // with the caller's delete grant beside them; and a promoted def's params - // now resolve when its dialog opens (`:5097-5116`, called at `:5132`), its - // rows still fanned out through `useBulkExecutor`. `hooks/useBulkExecutor.ts` - // is byte-identical to `20c6d351a` and its `:298-303` still hashes to - // `01083348330f10a201cdf1078b4c21c236402b6a`. At `20c6d351a`, re-read there - // 2026-10-10: - // every objectui file this record cites is byte-identical across the hop - // from `47b1f0bb7` (`git diff --quiet`), so every anchor held unmoved: - // `ObjectGrid.tsx:4868-4895` still hashes to - // `c88443302d40c2db739ddb235470bafa29056e2e` and - // `hooks/useBulkExecutor.ts:298-303` to - // `01083348330f10a201cdf1078b4c21c236402b6a`. At `47b1f0bb7`, re-read there - // 2026-10-09: - // in `packages/plugin-grid`, on the hop off `f0268ad78`, `ObjectGrid.tsx` - // changed again (19 insertions, 11 deletions: objectui#11865 slice m, - // objectui `2063f7a96`: one import line, and the grouped pager's - // rows-per-page picker drawn with the shared `Select`), none of it inside the - // selection block and no hunk naming a bulk action, a delete affordance or - // `operations`, so the block only MOVED by +1: `ObjectGrid.tsx:4868-4895` - // here (`4867-4894` at `f0268ad78`), still hashing to - // `c88443302d40c2db739ddb235470bafa29056e2e` (hash-object of the block), - // re-READ with the same reading below; `hooks/useBulkExecutor.ts` is - // byte-identical to `f0268ad78` and its `:298-303` still hashes to - // `01083348330f10a201cdf1078b4c21c236402b6a`. At `f0268ad78`, re-read there - // 2026-10-09: - // in `packages/plugin-grid`, on the hop off `a58626c88`, `ObjectGrid.tsx` - // changed again (85 insertions, 27 deletions: objectui#11880 item 5's grid - // filter hand-off, objectui#6152 rounds 7 and 10, objectui#11817's link cell, - // objectui#11809's option order and objectui#11689's boolean face), none of - // it inside the selection block and no hunk naming a bulk action, a delete - // affordance or `operations`, so the block only MOVED by +54: - // `ObjectGrid.tsx:4867-4894` here (`4813-4840` at `a58626c88`), still hashing - // to `c88443302d40c2db739ddb235470bafa29056e2e` (hash-object of the block), - // re-READ with the same reading below; `hooks/useBulkExecutor.ts` is - // byte-identical to `a58626c88` and its `:298-303` still hashes to - // `01083348330f10a201cdf1078b4c21c236402b6a`. At `a58626c88`, re-read there - // 2026-10-06: - // every objectui file this record cites is byte-identical across the hop from - // `0abd4f9f8` (`git diff --quiet`), so every anchor held unmoved. - // At `0abd4f9f8`, re-read there 2026-10-05: - // every objectui file this record cites is byte-identical across the hop from - // `9dfaca654` (`git diff --quiet`), so every anchor held unmoved. - // At `9dfaca654`, `packages/plugin-grid`; re-measured at - // that pin, 2026-10-05: on the hop off `2e818d0b5`, `ObjectGrid.tsx` changed - // in three type docblocks only (9 insertions, 6 deletions: objectui#8347 - // re-wording its `BaseSchema` index-signature notes in the past tense), all - // above the selection block, which only MOVED by +3: - // `ObjectGrid.tsx:4813-4840` here (`4810-4837` at `2e818d0b5`), still - // hashing to `c88443302d40c2db739ddb235470bafa29056e2e` (hash-object of the - // block), re-READ with the same reading below; `hooks/useBulkExecutor.ts` is - // byte-identical to `2e818d0b5` and its `:298-303` still hashes to - // `01083348330f10a201cdf1078b4c21c236402b6a`. At `2e818d0b5`, 2026-10-04: - // `ObjectGrid.tsx` and `hooks/useBulkExecutor.ts` are - // byte-identical across the hop off `ab1879721` (`git diff --quiet`), so - // every anchor held unmoved. Re-measured at `ab1879721`, 2026-10-03. On the - // hop off `89cad75d5`, `ObjectGrid.tsx` changed - // again (74 insertions, 11 deletions: objectui#11544's group-header labels - // read from the object field's `options` only, objectui#11475's percent - // storage `max` copied onto the cell's field meta on three paths and one - // import edited in place, above the selection block; objectui#11068's - // `keyboardNavigation`, objectui#11475's mobile-card percent face and - // objectui#11227's resolved empty-state labels, below it), none of it inside - // the selection block, which only MOVED by +19: `ObjectGrid.tsx:4810-4837` - // here (`4791-4818` at `89cad75d5`), still hashing to - // `c88443302d40c2db739ddb235470bafa29056e2e` (hash-object of the block), - // re-READ with the same reading below; `hooks/useBulkExecutor.ts` is - // byte-identical to `89cad75d5` and its `:298-303` still hashes to - // `01083348330f10a201cdf1078b4c21c236402b6a`. At `89cad75d5`, 2026-10-02: - // on the hop off `31971ff1e`, `ObjectGrid.tsx` changed - // again (42 insertions, 17 deletions: objectui#11322's built-in bulk-delete - // gate asking `partitionRowsByPredicate` directly and objectui#11254's - // mobile-card percent width, plus their two import lines, each edited in - // place), every hunk either a same-length import edit or below the - // selection block, which did NOT move: `ObjectGrid.tsx:4791-4818` there as at - // `31971ff1e`, still hashing to `c88443302d40c2db739ddb235470bafa29056e2e` - // (hash-object of the block), re-READ with the same reading below; - // `hooks/useBulkExecutor.ts` is byte-identical to `31971ff1e` and its - // `:298-303` still hashes to `01083348330f10a201cdf1078b4c21c236402b6a`. - // At `31971ff1e`, 2026-10-01: on the hop off `e420df310`, `ObjectGrid.tsx` changed - // again (41 insertions, 1 deletion: objectui#10689's re-read on a harvested - // query input), every hunk above the selection block, which only MOVED by - // +40: `ObjectGrid.tsx:4791-4818` there (`4751-4778` at `e420df310`), still - // hashing to `c88443302d40c2db739ddb235470bafa29056e2e` (hash-object of the - // block), re-READ with the same reading below; `hooks/useBulkExecutor.ts` is - // byte-identical to `e420df310` and its `:298-303` still hashes to - // `01083348330f10a201cdf1078b4c21c236402b6a`. At `e420df310`, 2026-09-30: - // on the hop off `db11afd49`, `ObjectGrid.tsx` changed - // again (164 insertions, 48 deletions: objectui#9853's display page size, - // objectui#11021's searched grouped grid, objectui#9547's `onNavigate` prop, - // objectui#7297's `{record_id}` filter values and objectui#11070's - // `reference` spelling among them), none of it inside the selection block, - // which only MOVED by +114: `ObjectGrid.tsx:4751-4778` there (`4637-4664` at - // `db11afd49`), still hashing to - // `c88443302d40c2db739ddb235470bafa29056e2e` (hash-object of the block), - // re-READ with the same reading below; `hooks/useBulkExecutor.ts` is - // byte-identical to `db11afd49` and its `:298-303` still hashes to - // `01083348330f10a201cdf1078b4c21c236402b6a`. At `db11afd49`, 2026-09-29: - // on the hop off `dd3f7e1be`, `ObjectGrid.tsx` changed - // again (105 insertions, 16 deletions: objectui#11068's grid keys, - // objectui#11105's server-grouped column-name fix, objectui#10993's title - // locale map among them), none of it inside the selection block, which only - // MOVED by +32: `ObjectGrid.tsx:4637-4664` here (`4605-4632` at - // `dd3f7e1be`), still hashing to - // `c88443302d40c2db739ddb235470bafa29056e2e` (hash-object of the block), - // re-READ with the same reading below; `hooks/useBulkExecutor.ts` is - // byte-identical to `dd3f7e1be` and its `:298-303` still hashes to - // `01083348330f10a201cdf1078b4c21c236402b6a`. Previously measured at - // `89cad75d5`, `31971ff1e`, `e420df310`, `db11afd49`, `dd3f7e1be`, `f8a9d0fb0`, `62597c588`, - // `87af769e9`, `53ded82bf`, `a472b0716`, `00d3f09c5`, `67dadd602`, before - // that at `d8ec8d6d4`, `9602dc820`, `190fbd01d`, `9a3daf8d3`, originally at - // `6314e87f2`. On the hop off `f8a9d0fb0`, `ObjectGrid.tsx` changed hard - // (745 insertions, 161 deletions: objectui#7189's server-side grid grouping - // and objectui#10881's grouped-grid refusal among them), none of it inside - // the selection block, which only MOVED: `ObjectGrid.tsx:4605-4632` then - // (`4032-4059` at `f8a9d0fb0`), still hashing to - // `c88443302d40c2db739ddb235470bafa29056e2e`, re-READ with the same reading - // below — the one edit in the surrounding selection-mode region (from its - // `Determine selection mode` comment to `singleSelection`) is a comment tag - // (`[#3720]` -> `[objectstack#3720]`). On the hop off - // `62597c588`, `ObjectGrid.tsx` changed too (18 insertions, 5 deletions: - // objectui#10083's `rowActionsDeclared` row-menu signal and objectui#9909's - // currency display locale), none of it inside the selection block, which - // only MOVED then as well (`4024-4051` to `4032-4059`), re-READ with the - // same reading. `ObjectGrid.tsx` changed across the move off - // `87af769e9` too (119 insertions, 9 deletions), and THAT time the selection block's CONTENT - // moved, not only its position: objectui#10218 (`62597c588` itself) - // rewrote the `selection` arm to "presence enables; an explicit off wins", - // so an authored `selection` object with no `type` now selects - // (`DEFAULT_SELECTION_TYPE`, `'multiple'`) where it used to fall through. - // The block was `ObjectGrid.tsx:4024-4051` at `62597c588` — `3940-3955` at `87af769e9`, - // `3538-3553` at `53ded82bf` — and no longer hashes to the - // `6133933199230670e29d8c7f51c558d86a0af1d2` the previous three hops - // recorded (`c88443302d40c2db739ddb235470bafa29056e2e` now). Re-READ, the - // arm this record relies on is unchanged: with neither `selection` nor - // `selectable` authored, multi-select is still enabled only by - // `hasBulkActions`, and the rewritten arm is reachable only by a view that - // declares `selection` — which none of these four does. The earlier hop off - // `00d3f09c5` is the one that caught the previous record's OWN grid anchor as - // wrong rather than merely shifted: `3790-3805` there is - // `runBulkActionAggregate` and says nothing about selection. That is the - // class commit d1ba685ec gates, and the reason a citation refresh re-READS instead of moving - // numbers — arithmetic on a wrong anchor produces a fresh-looking span still - // describing the wrong function. The second claim, - // `hooks/useBulkExecutor.ts:298-303`, sits in a file that is byte-identical - // to `f8a9d0fb0`, to `62597c588` and to `87af769e9` on the last three hops - // (`git diff --quiet`; it last changed on the hop off `53ded82bf`, 36 - // insertions, 22 deletions, when it was re-READ rather than carried on file - // identity): the six lines hash to - // `01083348330f10a201cdf1078b4c21c236402b6a` at every one of those pins and - // still end on - // `label = 'bulk delete'`, the line the `284-288` span cited nine - // pins ago stopped short of, truncating the second of the two branches it - // names (byte-identity is never taken as proof an anchor is right): + // `bulk` either — both read off the console renderer (the read points are + // recorded in `packages/spec/liveness/object.json`, row `enable.apiMethods`): // // · No checkbox column is rendered. None of the object's four list views // declares `bulkActions` / `bulkActionDefs` / `selection`, and `ObjectGrid` diff --git a/packages/spec/src/data/field.form.ts b/packages/spec/src/data/field.form.ts index 2892cb941bf..6843367fefe 100644 --- a/packages/spec/src/data/field.form.ts +++ b/packages/spec/src/data/field.form.ts @@ -212,8 +212,9 @@ export const fieldForm = defineForm({ // already uses it one section down for `summaryOperations.filter`, the // sibling FilterConditionSchema key. // - // ⚠ What the hint renders as TODAY, measured at the pinned - // `.objectui-sha`, is the announced raw-JSON editor carrying the hint — + // ⚠ What the hint renders as TODAY (read points recorded in + // `liveness/field.json`, row `relatedListFilter`) is the announced + // raw-JSON editor carrying the hint — // ⛔ NOT a criteria builder. The renderer that consumes this registry is // the metadata-admin `SchemaForm`, whose own `WIDGETS` map registers no // `filter-condition` (the `FilterConditionField` of that name lives in diff --git a/packages/spec/src/data/field.zod.ts b/packages/spec/src/data/field.zod.ts index 7a7eafb0c41..47e43cb54cf 100644 --- a/packages/spec/src/data/field.zod.ts +++ b/packages/spec/src/data/field.zod.ts @@ -453,11 +453,11 @@ const CURRENCY_DECIMAL_PLACES_ARE_THE_CURRENCYS = * #19910 — 「a currency's decimal places are the currency's, not a setting」): * `precision` was a declared, validated key that no renderer or runtime ever * read. objectui's `CurrencyField` derives the width from the currency's ISO - * 4217 minor unit (`currencyFractionDigits(currency)`, the same line at the - * `.objectui-sha` pin and at objectui `main`), and no code read the key at all — - * measured with a lit control (`currencyConfig.currencyMode` IS read) over - * objectstack `packages/**` + `examples/**`, objectui at the pin and at `main`, - * and cloud `main`. Its only reader was + * 4217 minor unit (read points recorded in `liveness/field.json`, row + * `currencyConfig`), and no code read the key at all — measured with a lit + * control (`currencyConfig.currencyMode` IS read) over objectstack + * `packages/**` + `examples/**`, objectui (the console's tree and `main`) and + * cloud `main`. Its only reader was * its own ISO 4217 contradiction check (#7918), which policed a width nothing * applied; that check, the `.overwrite()` that baked a default `2` into parse * output (#11423), and the `decimals` / `scale` → `precision` aliases all left @@ -1710,9 +1710,9 @@ export const FieldSchema = lazySchema(() => { * this schema declares — the camelCase key is the authored one in every * case. * - * Claim dated to objectui ed971e8, re-measured 2026-09-09. It is a reading - * of a pinned foreign tree and no gate here re-checks it: when the console - * pin moves, this paragraph is what goes stale. + * The read points, dated, are recorded in `liveness/field.json`, row + * `lookupColumns`. It is a reading of a foreign tree and no gate here + * re-checks it. */ displayField: z.string().optional().describe("Field shown as each candidate's label in the picker/popover (defaults to the referenced object's name/title)."), descriptionField: z.string().optional().describe('Secondary field shown under the label in the quick-select popover.'), diff --git a/packages/spec/src/inline-grid-column-carriers.test.ts b/packages/spec/src/inline-grid-column-carriers.test.ts index 3d22da0a749..8672d4dbc7b 100644 --- a/packages/spec/src/inline-grid-column-carriers.test.ts +++ b/packages/spec/src/inline-grid-column-carriers.test.ts @@ -426,8 +426,8 @@ describe('the `field` → `name` respelling is a chain step on the form-view car const MASTER_DETAIL_PROPS = ComponentPropsMap['object-master-detail-form']; /** - * Every key objectui's `MasterDetailForm` reads off a detail entry (the - * `.objectui-sha` pin `31971ff1e28f`, re-read at `89cad75d5570`). `sortField` + * Every key objectui's `MasterDetailForm` reads off a detail entry (read + * points recorded in `liveness/page.json`, row `regions`). `sortField` * is not one of them: the renderer derives the line-position field from the * child object, and the key is a tombstone (#21589, * `ui/master-detail-detail-sort-field-retirement.test.ts`). @@ -621,7 +621,7 @@ describe('defineStack judges an identity-only detail column by the type it rende */ const LINE_ITEMS_PROPS = ComponentPropsMap['record:line_items']; -/** Every key objectui's `LineItemsPanel` reads off its schema (the `.objectui-sha` pin `31971ff1e28f`). */ +/** Every key objectui's `LineItemsPanel` reads off its schema (read points recorded in `liveness/page.json`, row `regions`). */ const FULL_LINE_ITEMS = { childObject: 'crm_invoice_line', relationshipField: 'invoice', diff --git a/packages/spec/src/kernel/functional-completeness.ts b/packages/spec/src/kernel/functional-completeness.ts index 62672664d58..8fe58366ad7 100644 --- a/packages/spec/src/kernel/functional-completeness.ts +++ b/packages/spec/src/kernel/functional-completeness.ts @@ -287,8 +287,8 @@ export function checkFieldCompleteness(def: unknown): CompletenessFinding[] { * "Calendar configuration required" refusal screen, which names * `startDateField` as the key to declare (objectui#8170 corrected that * screen's second clause: `titleField` is NOT required). ⚠️ The screen's - * copy is LOCALISED at the pin: objectui#10101 (objectui `afb228418`), which - * the pin now contains, routes it through `tt('calendar.configRequired', …)` + * copy is LOCALISED: objectui#10101 (objectui `afb228418`), which the + * console this repo ships contains, routes it through `tt('calendar.configRequired', …)` * (in `ObjectCalendar`), whose default and the `en` pack both * read "Calendar configuration required. Please specify startDateField, …", * while a non-English locale renders its own pack's words for the same @@ -307,8 +307,8 @@ export function checkFieldCompleteness(def: unknown): CompletenessFinding[] { * `case 'gantt'` spreads `startDateField` / `endDateField` / `titleField` * only when the view DECLARED them — the `'start_date'` / `'end_date'` * floors were deleted by objectui#7070 (landing commit objectui - * `5f4514f7b`, which the arm's comment cites in the issue's place at this - * pin: objectui `fec3b1a8b` re-cited objectui#7070 because it answers + * `5f4514f7b`, which the arm's comment cites in the issue's place on that + * console: objectui `fec3b1a8b` re-cited objectui#7070 because it answers * 404), and the `'progress'` / * `'dependencies'` floors by objectui#7499 (OMITTED, not refused: an absent * progress or dependency binding is a legitimate state). ② `ObjectGantt`'s @@ -324,7 +324,7 @@ export function checkFieldCompleteness(def: unknown): CompletenessFinding[] { * through `resolveTimelineDateBinding` and spreads it only when one was * declared — the `startDateField || 'created_at'` floor was deleted by * objectui#7070 step ③ (landing commit objectui `04a67b9dc`, which the - * arm's comment cites in the issue's place at this pin), on the ruling + * arm's comment cites in the issue's place on that console), on the ruling * 日期轴永不虚构 (a date axis is never * fabricated) — while its `titleField: dateBinding.titleField || 'name'` * line is kept on purpose (a title is not an axis). ② `ObjectTimeline`'s diff --git a/packages/spec/src/shared/editability-boundary.ts b/packages/spec/src/shared/editability-boundary.ts index d4d6618d76b..8031a64e19e 100644 --- a/packages/spec/src/shared/editability-boundary.ts +++ b/packages/spec/src/shared/editability-boundary.ts @@ -21,14 +21,13 @@ * ruling now, on a **re-measured** version of the ruling's own premise rather * than by analogy — the premise that carries it is "no editability semantics * exist here to enforce", and for an option that is a fact about the renderer, - * not an argument. Measured on objectui `origin/main` @ `aca27fa`: the - * object-field pipeline these options actually feed has **zero** per-option - * `disabled` consumers — `packages/fields/src/widgets/SelectField.tsx:161` - * calls the root-level `disabled` "the single authority" in its own comment, - * and `RadioField.tsx:124` reads only `props.disabled`. A shown-but-unselectable - * option does exist in objectui's **SDUI** family - * (`packages/components/src/renderers/form/select.tsx:62`), but on that - * package's own select-option vocabulary in `packages/types` — a different + * not an argument. Measured on objectui `main` (read points recorded in + * `liveness/field.json`, row `options`): the object-field pipeline these + * options actually feed has **zero** per-option `disabled` consumers — the + * select widget calls the root-level `disabled` "the single authority" in its + * own comment, and the radio widget reads only `props.disabled`. A + * shown-but-unselectable option does exist in objectui's **SDUI** family + * select renderer, but on that package's own select-option vocabulary in `packages/types` — a different * shape from this one, so it makes no key here enforced. * * The prescription it gets is NOT the siblings' text, because the siblings' diff --git a/packages/spec/src/system/i18n-resolver.ts b/packages/spec/src/system/i18n-resolver.ts index 247d364fd82..2e8baa02561 100644 --- a/packages/spec/src/system/i18n-resolver.ts +++ b/packages/spec/src/system/i18n-resolver.ts @@ -4281,8 +4281,9 @@ function lookupFlowScreenFieldCopy( * client-side route always had. Under maintainer ruling A on #22507 a client * overlay never touches a server-rendered slot. Its one caller, objectui's * `FlowRunner` `localizeScreen`, stops overlaying the heading in that ruling's - * consumer half, and this function retires once the `.objectui-sha` pin - * carries that change (the pinned console still imports it). + * consumer half, and this function retires once the console this repo ships + * carries that change (it still imports it: read point recorded in + * `liveness/translation.json`, row `flows.screens`). */ export function resolveFlowScreenTitle( bundle: TranslationBundle | undefined, diff --git a/packages/spec/src/ui/action-declarative-update.ts b/packages/spec/src/ui/action-declarative-update.ts index 3508c7554d6..29522057ef7 100644 --- a/packages/spec/src/ui/action-declarative-update.ts +++ b/packages/spec/src/ui/action-declarative-update.ts @@ -13,9 +13,8 @@ import { onSuccessBesideUpdateOperationRefusal } from './action-on-success-execu * the rows' half is pinned in `component-action-row-declarative-update.test.ts`. * * Both runtimes read `operation` before the executor. The console's action - * runner (`ActionRunner.execute`, `core/src/actions/ActionRunner.ts:1384-1388` - * at the `.objectui-sha` pin `b9814b334`, byte-identical at objectui `main` - * `524136e`) posts the write through its registered `script` dispatch and runs + * runner (`ActionRunner.execute`; read point recorded in `liveness/action.json`, + * row `operation`) posts the write through its registered `script` dispatch and runs * no executor of its own, whatever executor the action or row names. The * platform action route treats an action carrying `operation: 'update'` as the * write "whatever `type` says" (`isDeclarativeUpdateAction`, diff --git a/packages/spec/src/ui/action-url-target-scheme-set.test.ts b/packages/spec/src/ui/action-url-target-scheme-set.test.ts index 7fcbe9c012a..b839a16dcc8 100644 --- a/packages/spec/src/ui/action-url-target-scheme-set.test.ts +++ b/packages/spec/src/ui/action-url-target-scheme-set.test.ts @@ -179,9 +179,8 @@ describe("an admitted `type: 'url'` target parses unchanged", () => { // =========================================================================== describe("the door answers the runner's pinned cases the same way", () => { - // The cases objectui pins for `classifyNavigationTarget` - // (`packages/core/src/actions/__tests__/navigationTarget.mailto-12191.test.ts` - // at objectui 7578323). A literal target the runner opens must parse here, + // The cases objectui pins for `classifyNavigationTarget` (recorded in + // `liveness/action.json`, row `target`). A literal target the runner opens must parse here, // and one it refuses must be refused here — a wider door publishes a target // that fails at the click, a narrower one refuses a target that works. it.each([ diff --git a/packages/spec/src/ui/app.zod.ts b/packages/spec/src/ui/app.zod.ts index 05e1e68ce2b..6e21d3b1cff 100644 --- a/packages/spec/src/ui/app.zod.ts +++ b/packages/spec/src/ui/app.zod.ts @@ -430,8 +430,8 @@ export const ObjectNavItemSchema = lazySchema(() => strictObject(navItemSurface( type: z.literal('object'), objectName: z.string().describe('Target object name'), /** - * The rule this sentence states is the console's, read at the objectui commit - * `.objectui-sha` pins: an entry with no `viewName` links to the bare object + * The rule this sentence states is the console's (read points recorded in + * `liveness/app.json`, row `navigation.viewName`): an entry with no `viewName` links to the bare object * route, where `ObjectView` opens `defaultViewId || views[0]` — the view * `buildViewTabs` marks `isDefault` (the default `list`), else the first * declared list view. Its `all` tab is the fallback `buildViewTabs` adds only diff --git a/packages/spec/src/ui/bulk-action.zod.ts b/packages/spec/src/ui/bulk-action.zod.ts index 2e798a0be29..e3af9316d2e 100644 --- a/packages/spec/src/ui/bulk-action.zod.ts +++ b/packages/spec/src/ui/bulk-action.zod.ts @@ -155,7 +155,8 @@ export type BulkActionExecution = z.input; * dialog opens, through the single-record dialog's own resolver (objectui * `29b99490`: plugin-grid's `resolvePromotedBulkParams` calls * `resolveActionParams` from `@object-ui/fields`, then renames the result onto - * the bulk vocabulary). Measured on objectui `main` `dca25aff2`, that route + * the bulk vocabulary). Measured on objectui `main` (read points recorded in + * `liveness/view.json`, row `list.bulkActionDefs`), that route * carries the field's `type`, translated `label`, `options`, `required`, * `description` (as the help text), `placeholder`, `defaultValue`, `multiple`, * `accept` and `maxSize`, plus a reference-bearing field's `reference`, diff --git a/packages/spec/src/ui/component-action-element-rows-20371.test.ts b/packages/spec/src/ui/component-action-element-rows-20371.test.ts index d8d867a3f9a..0a5bfcbd3e7 100644 --- a/packages/spec/src/ui/component-action-element-rows-20371.test.ts +++ b/packages/spec/src/ui/component-action-element-rows-20371.test.ts @@ -8,9 +8,9 @@ // (outside every namespace the enum populates) and the two `element:*` lists // were refused as `component-type-unknown` (inside the reserved `element:` // namespace with neither an enum member nor a row). Each row is strict from -// birth, with its key set measured from the renderer's read points at the -// `.objectui-sha` pin — the per-key citations live in `component.zod.ts`, -// section 4b. The key sets are asserted WHOLE, as #18305's were: a row derived +// birth, with its key set measured from the renderer's read points (recorded +// in `liveness/page.json`, row `regions`; the rows sit in `component.zod.ts` +// section 4b). The key sets are asserted WHOLE, as #18305's were: a row derived // from read points is a claim about a complete set, and only an equality holds // a later addition to having been measured too. diff --git a/packages/spec/src/ui/component-action-member-params-array-only.pin.test.ts b/packages/spec/src/ui/component-action-member-params-array-only.pin.test.ts index 1be3f9a731f..a6514321666 100644 --- a/packages/spec/src/ui/component-action-member-params-array-only.pin.test.ts +++ b/packages/spec/src/ui/component-action-member-params-array-only.pin.test.ts @@ -11,9 +11,10 @@ * The member declared `params` as `z.unknown()`. Both containers forward an * array as the input list, forward any other value only for a `type: 'api'` * member (its request payload), and drop it for every other `type`, with a - * development-build warning only (objectui `static-params.ts:172-182` at the - * `.objectui-sha` pin `0abd4f9f8`). So an object `params` on a `navigate_edit` - * member passed the component-props gate and then reached no action. + * development-build warning only (read points recorded in + * `liveness/page.json`, row `regions`). So an object `params` on a + * `navigate_edit` member passed the component-props gate and then reached no + * action. * * ## What is pinned, and why each half * diff --git a/packages/spec/src/ui/component-action-row-effective-executor.test.ts b/packages/spec/src/ui/component-action-row-effective-executor.test.ts index 63bc4966713..09ed1d87e92 100644 --- a/packages/spec/src/ui/component-action-row-effective-executor.test.ts +++ b/packages/spec/src/ui/component-action-row-effective-executor.test.ts @@ -6,8 +6,8 @@ * console's action runner will RUN, not only against the executor key. * * The runner dispatches on `action.type || action.actionType || action.name` - * (objectui `core/src/actions/ActionRunner.ts:1265` at the `.objectui-sha` - * pin), and every row forwards its executor key as `type` beside its `name`. + * (read points recorded in `liveness/page.json`, row `regions`), and every row + * forwards its executor key as `type` beside its `name`. * So a row with no executor key whose `name` is `url` or `navigation` runs the * url navigator — and its `target` is judged by the one url-target grammar * (`navigation-target.ts`), whichever key spelled the executor. ⛔ `name` is diff --git a/packages/spec/src/ui/component-element-navigation-17987.test.ts b/packages/spec/src/ui/component-element-navigation-17987.test.ts index 02b980002e6..3e9f2b89ace 100644 --- a/packages/spec/src/ui/component-element-navigation-17987.test.ts +++ b/packages/spec/src/ui/component-element-navigation-17987.test.ts @@ -202,7 +202,7 @@ describe('the VIEW-level carrier is untouched', () => { describe('the `object-timeline` row judges the block in BOTH directions', () => { const TIMELINE = ComponentPropsMap['object-timeline']; - it('accepts the key set measured from the renderer at the `.objectui-sha` pin', () => { + it('accepts the key set measured from the renderer read points (`liveness/page.json`, row `regions`)', () => { const r = TIMELINE.safeParse({ objectName: 'task', timeline: { startDateField: 'start_at', endDateField: 'end_at', titleField: 'subject', colorField: 'status' }, @@ -237,8 +237,8 @@ describe('the `object-timeline` row judges the block in BOTH directions', () => * The contract review's BLOCKING finding, pinned so the row cannot lose the * key again. * - * `data` is read off REACT PROPS (`ObjectTimeline.tsx:171`, `:247`, - * `:256`), not off `schema`, and an authored `properties.data` reaches that + * `data` is read off REACT PROPS (read points recorded in + * `liveness/page.json`, row `regions`), not off `schema`, and an authored `properties.data` reaches that * channel through `SchemaRenderer`'s hoist-and-spread. The first cut of this * row measured `schema.*` read points only, so it declared fourteen keys and * refused this one BY NAME — reproducing this card's own defect sentence on diff --git a/packages/spec/src/ui/component-form-custom-fields-sections-typed.pin.test.ts b/packages/spec/src/ui/component-form-custom-fields-sections-typed.pin.test.ts index 1180ebbbf33..9dc8682b7ba 100644 --- a/packages/spec/src/ui/component-form-custom-fields-sections-typed.pin.test.ts +++ b/packages/spec/src/ui/component-form-custom-fields-sections-typed.pin.test.ts @@ -34,8 +34,8 @@ * one runtime field `customFields` takes. * - §4 THE REGISTRATION: the ADR-0087 D3 entries step 18 carries. * - §5 THE GRID WIDGET'S CAMELCASE KEYS (#21768): objectstack-ai/objectui#11610 - * renamed the `grid` widget's eight field-level keys to camelCase, and the - * `.objectui-sha` pin `9dfaca654311` carries it, so the runtime field declares + * renamed the `grid` widget's eight field-level keys to camelCase (read + * points recorded in `liveness/page.json`, row `regions`), so the runtime field declares * `minRows`, `maxRows`, `allowAdd`, `allowDelete`, `allowReorder`, * `totalField`, `addLabel` and `sortField` with the widget's value types. The * writer shape parses (§1); each snake_case spelling is refused by name with @@ -212,9 +212,9 @@ describe('§1 each member accepts every shape a measured writer authors', () => { name: 'rest', label: 'Rest', columns: 1, pane: 'secondary', fields: ['note'] }, ], }], - // §5 — objectui `examples/schema-catalog/src/schemas/fields-grid/line-items-grid.json` at the - // `.objectui-sha` pin `9dfaca654311`: the one `fields[]` entry, a `grid` field carrying all eight - // camelCase keys, the shape objectui's grid-field guide (`content/docs/fields/grid.mdx`) teaches. + // §5 — objectui's schema-catalog grid example (recorded in `liveness/page.json`, row `regions`): + // the one `fields[]` entry, a `grid` field carrying all eight camelCase keys, the shape objectui's + // grid-field guide (`content/docs/fields/grid.mdx`) teaches. ['the grid widget\'s camelCase keys on an inline grid field', 'object-form', { customFields: [{ name: 'line_items', label: 'Line Items', type: 'grid', @@ -249,8 +249,8 @@ describe('§1 each member accepts every shape a measured writer authors', () => }); } - // objectui `plugin-designer/src/ObjectManager.tsx` (about `:239`-`:247` at the `.objectui-sha` pin): - // the registered `object-manager` component's create / edit dialog, a `formType: 'modal'` block. + // objectui's registered `object-manager` component's create / edit dialog, a `formType: 'modal'` + // block (recorded in `liveness/page.json`, row `regions`). // Its two select members build their options from the file's own constants, each entry // `{ label: v, value: v }`; the labels it draws through `t(...)` are written out as strings here. it('object-form: parses the shipped object-manager dialog\'s inline fields byte-identical', () => { @@ -494,8 +494,8 @@ describe('§4 each narrowing is registered as the ADR-0087 D3 entry step 18 carr // ─────────────────────────────────────────────────────────────────────────── describe('§5 the grid widget\'s eight field-level keys, camelCase since objectui renamed them', () => { - // The rename, as objectui's `GRID_FIELD_RETIRED_KEYS` pairs it (`types/src/field-types.ts:1159` at the - // `.objectui-sha` pin `9dfaca654311`), with each key's value type as `GridFieldMetadata` declares it. + // The rename, as objectui's `GRID_FIELD_RETIRED_KEYS` pairs it (read points recorded in + // `liveness/page.json`, row `regions`), with each key's value type as `GridFieldMetadata` declares it. const GRID_KEYS = [ ['min_rows', 'minRows', 1, 'one'], ['max_rows', 'maxRows', 10, '10'], @@ -556,7 +556,7 @@ describe('§5 the grid widget\'s eight field-level keys, camelCase since objectu // Two keys spelled alike across siblings with opposite meanings is how a writer goes wrong: the grid's // `totalField` names the CHILD column it sums, the child-collection blocks' names the PARENT field the // sum is saved to (their child column is `amountField`, which their renderers hand the grid AS its - // `totalField` — `LineItemsPanel.tsx:710`, `MasterDetailForm.tsx:876` at the pin). + // `totalField` — read points recorded in `liveness/page.json`, row `regions`). it('`totalField` on the inline grid field is the CHILD column summed, the opposite of the same spelling on the child-collection blocks', () => { const description = (schema: unknown) => (schema as { description?: string }).description ?? ''; const grid = description(runtimeField().shape.totalField); diff --git a/packages/spec/src/ui/component-form-family-typed-members.pin.test.ts b/packages/spec/src/ui/component-form-family-typed-members.pin.test.ts index e9335236667..d07fb7297cd 100644 --- a/packages/spec/src/ui/component-form-family-typed-members.pin.test.ts +++ b/packages/spec/src/ui/component-form-family-typed-members.pin.test.ts @@ -16,8 +16,8 @@ * * ## The defect this file closes * - * Each of the four is read with one shape (measured at the `.objectui-sha` pin - * `89cad75d55`; the read points are in the members' docblocks), and the row + * Each of the four is read with one shape (read points recorded in + * `liveness/page.json`, row `regions`), and the row * declared them `z.unknown()`. So a `submitBehavior` whose `kind` the form does * not know, a `contentLayout: 'tabs'`, a numeric `navigateOnSuccess` and a * misspelled `mobile` member all passed the component-props gate, and the form diff --git a/packages/spec/src/ui/component-list-family-typed-members.pin.test.ts b/packages/spec/src/ui/component-list-family-typed-members.pin.test.ts index 9e3f9f0b59f..03782a36784 100644 --- a/packages/spec/src/ui/component-list-family-typed-members.pin.test.ts +++ b/packages/spec/src/ui/component-list-family-typed-members.pin.test.ts @@ -5,17 +5,17 @@ * typed: `object-grid` `columns`, `fields`, `selection`, `selectable`, * `rowActions`, `bulkActions` and `batchActions`, `object-kanban` `columns`, * and `object-calendar` `calendar`. Stage 2 typed eight and held `object-grid` - * `columns`, because at the `.objectui-sha` pin `89cad75d55` the grid's group - * headers drew a column's `options`, which the list view's column entry does - * not declare. objectui `b0bf413` (objectstack-ai/objectui#11544) retired that - * read: at the `.objectui-sha` pin `ab1879721595` the group-header labels come - * from the object field's `options` only, so stage 5 types `columns` too. + * `columns`, because the grid's group headers then drew a column's `options`, + * which the list view's column entry does not declare. objectui `b0bf413` + * (objectstack-ai/objectui#11544) retired that read: the group-header labels + * now come from the object field's `options` only, so stage 5 types `columns` + * too (read points recorded in `liveness/page.json`, row `regions`). * * ## The defect this file closes * - * Each renderer reads these members with one shape (measured at the - * `.objectui-sha` pin `89cad75d55`; the read points are in the members' - * docblocks), and each row declared them `z.unknown()`. So a grid column keyed + * Each renderer reads these members with one shape (read points recorded in + * `liveness/page.json`, row `regions`), and each row declared them + * `z.unknown()`. So a grid column keyed * `accessorKey`, an object entry in the grid's `fields`, a `{ name }` entry in * `bulkActions`, a kanban lane list mixing objects and strings and a calendar * block with no `startDateField` all passed the component-props gate, and the diff --git a/packages/spec/src/ui/component-metric-family-typed-members.pin.test.ts b/packages/spec/src/ui/component-metric-family-typed-members.pin.test.ts index fa5f8b6556d..f00c666b4da 100644 --- a/packages/spec/src/ui/component-metric-family-typed-members.pin.test.ts +++ b/packages/spec/src/ui/component-metric-family-typed-members.pin.test.ts @@ -14,10 +14,9 @@ * * ## The defect this file closes * - * Each member is read with one shape (measured at the `.objectui-sha` pin - * `89cad75d55` for `aggregate` / `trend`, and at the `.objectui-sha` pin - * `ab1879721595` for `drillDown` / `compareTo`; the read points are in the - * schemas' docblocks), and the row declared them `z.unknown()`. So + * Each member is read with one shape (read points recorded in + * `liveness/page.json`, row `regions`), and the row declared them + * `z.unknown()`. So * `aggregate: 'count'`, a function the engine does not have, `groupby` for * `groupBy`, a bare `trend: 'up'`, a `trend` with no `value`, a drill `filter` * and a comparison `dimension` all passed the component-props gate, and the @@ -101,8 +100,7 @@ describe('§1 each member accepts every shape a measured writer authors', () => ['a trend with a locale-map caption', { trend: { value: 12, direction: 'up', label: { en: 'Revenue records', 'zh-CN': '收入记录' } }, }], - // objectui's drill-down member pins (`plugin-dashboard/src/__tests__/objectMetricDrillDownMembers-8071.test.tsx`, - // `ObjectMetricWidget.drillRoutedToSharedDrawer-8970.test.tsx`) at the `.objectui-sha` pin `ab1879721595`. + // objectui's drill-down member pins (recorded in `liveness/page.json`, row `regions`). ['an empty drill-down block (on)', { drillDown: {} }], ['a drill-down switched off', { drillDown: { enabled: false } }], ['a drill-down with every list member', { diff --git a/packages/spec/src/ui/component-object-grid-declared-keys.pin.test.ts b/packages/spec/src/ui/component-object-grid-declared-keys.pin.test.ts index 693b89f5aac..cd53cd9f86b 100644 --- a/packages/spec/src/ui/component-object-grid-declared-keys.pin.test.ts +++ b/packages/spec/src/ui/component-object-grid-declared-keys.pin.test.ts @@ -8,9 +8,9 @@ * ## The gap this file closes * * `ComponentPropsMap['object-grid']` is a strict shape derived from the - * renderer's own read points, and it lagged the renderer: at the - * `.objectui-sha` pin the grid reads `schema.description` and - * `schema.emptyState`, while this row refused both by name. A metadata author + * renderer's own read points, and it lagged the renderer: the grid reads + * `schema.description` and `schema.emptyState` (read points recorded in + * `liveness/page.json`, row `regions`), while this row refused both by name. A metadata author * validating against the row had a key the renderer honours reported as * unknown, and objectui's registry could not list it either (its parity test * refuses an input the spec does not accept). diff --git a/packages/spec/src/ui/component-object-grid-typed-members.pin.test.ts b/packages/spec/src/ui/component-object-grid-typed-members.pin.test.ts index 7f666cff9ec..47c124a13af 100644 --- a/packages/spec/src/ui/component-object-grid-typed-members.pin.test.ts +++ b/packages/spec/src/ui/component-object-grid-typed-members.pin.test.ts @@ -10,8 +10,8 @@ * `rowHeight`, `aggregations`, `conditionalFormatting`, `rowColor`, * `navigation` and `operations` were `z.unknown()`, and `bulkActionDefs` an * array of it, while objectui's `ObjectGrid` reads each with one shape - * (measured at the `.objectui-sha` pin `89cad75d55`; the read points are in - * the row's docblock). So `rowHeight: 42` passed every door and rendered as a + * (read points recorded in `liveness/page.json`, row `regions`). So + * `rowHeight: 42` passed every door and rendered as a * compact grid, and every other off-shape value was substituted or dropped * with no report. `resizableColumns` was the second spelling of `resizable`, * read only when `resizable` was absent. @@ -203,8 +203,8 @@ describe('§3 the members hold the list view\'s own schemas, and the measured vo it('aggregations[].type is AggregationFunction, whose members are exactly the six functions the grid computes', () => { const type = shape().aggregations.unwrap().element.shape.type; expect(type._zod.def).toBe(AggregationFunction._zod.def); - // The grid's own vocabulary at the pin (`AggregationType` in - // `plugin-grid/src/useGroupedData.ts`). A member added to the query AST's + // The grid's own vocabulary (`AggregationType`, recorded in + // `liveness/page.json`, row `regions`). A member added to the query AST's // enum widens this door past the grid's reader; this line reds first. expect([...AggregationFunction.options].sort()).toEqual(['avg', 'count', 'count_distinct', 'max', 'min', 'sum']); }); diff --git a/packages/spec/src/ui/component-objectui-held-typed-members.pin.test.ts b/packages/spec/src/ui/component-objectui-held-typed-members.pin.test.ts index 0337f2f4f27..e396ac0a091 100644 --- a/packages/spec/src/ui/component-objectui-held-typed-members.pin.test.ts +++ b/packages/spec/src/ui/component-objectui-held-typed-members.pin.test.ts @@ -14,9 +14,9 @@ * * ## The defect this file closes * - * Each member is read with one shape (measured at the `.objectui-sha` pin - * `ab1879721595`, unchanged at objectui `main` `94985a92ba`; the read points - * are in the schemas' docblocks), and the rows declared them `z.unknown()`. So + * Each member is read with one shape (read points recorded in + * `liveness/page.json`, row `regions`), and the rows declared them + * `z.unknown()`. So * a marker with no `date`, a numeric `date`, a misspelled marker member, a * `mapping` written as a bare field name or keyed `titleField`, and a form * `fields` entry written `{ name }` or `{ field }` all passed the @@ -149,7 +149,7 @@ describe('§2 an off-shape value is refused with the code and the path', () => { // objectui `plugin-form/src/__tests__/objectFormFieldsMembers-8071.test.tsx:140` — the warn probe. ['a `{ field }` field entry', 'object-form', { fields: [{ field: 'note' }] }, 'invalid_type', 'fields.0'], ['a numeric field entry', 'object-form', { fields: [5] }, 'invalid_type', 'fields.0'], - // objectui's page-builder guide at `.objectui-sha` (respelled on objectui `main`). + // objectui's page-builder guide (recorded in `liveness/page.json`, row `regions`). ['the guide\'s retired inline field', 'object-form', { fields: [{ name: 'name', label: 'Name', type: 'text', required: true }, 'email'], }, 'invalid_type', 'fields.0'], diff --git a/packages/spec/src/ui/component-props-unknown-members.pin.test.ts b/packages/spec/src/ui/component-props-unknown-members.pin.test.ts index 4a077bd23c7..6ceac58f24a 100644 --- a/packages/spec/src/ui/component-props-unknown-members.pin.test.ts +++ b/packages/spec/src/ui/component-props-unknown-members.pin.test.ts @@ -65,7 +65,7 @@ * * ## The STAGED reason is debt, not a verdict — and the debt is paid * - * A `staged` member WAS read with a fixed shape at the `.objectui-sha` pin, its + * A `staged` member WAS read with a fixed shape by its renderer, its * reader cited on its line, and typing it was a later stage of #21464's * close-out (the census found more than one reviewable PR's worth), each stage * preceded by its own census of authored writers. The ledger could only lose @@ -191,7 +191,7 @@ type Reason = */ | { readonly kind: 'opaque'; readonly ruling: string; readonly why: string } /** - * Read with a fixed shape at the pin (`reader`); typing it is a named later + * Read with a fixed shape by a renderer (`reader`); typing it is a named later * stage. A `fork` line also names the viable shapes no ruling has chosen * between (`shapes`). */ @@ -232,9 +232,8 @@ const SLOT: Reason = { kind: 'slot' }; const RUNNER: Reason = { kind: 'runner' }; /** - * `ObjectUI` source paths are at the `.objectui-sha` pin `89cad75d55`, except - * the S-final stage's line (the timeline entry's `content`), read at the - * `.objectui-sha` pin `2e818d0b51ec`. + * The objectui read points a line's reason rests on are recorded in + * `liveness/page.json`, row `regions`. */ const LEDGER = new Map(); const on = (types: readonly string[], paths: readonly string[], reason: Reason): void => { @@ -300,7 +299,7 @@ on(['object-grid'], ['bulkActionDefs[].patch{}', 'bulkActionDefs[].params[].defa on(['action:button', 'action:icon'], ['patch{}'], RECORDS); on(['action:group', 'action:menu'], ['actions[].patch{}'], RECORDS); // A static board's card is a record row: `id` and `title` are typed, the rest -// is the row's own values (`plugin-kanban/src/index.tsx:155`, kept verbatim). +// is the row's own values (the kanban's lane bucketing keeps them verbatim). on(['object-kanban'], ['columns[].cards[].*'], RECORDS); on(['object-grid'], ['bulkActionDefs[].params[].options[].*'], BULK_OPTION_ENTRY); on(['object-form'], ['customFields[].summaryOperations.filter{}'], FILTER_CONDITION); @@ -313,7 +312,7 @@ on(['object-metric'], ['drillDown.report.runtimeFilter{}', 'drillDown.report.blo // The rest, one line each. on(['element:definition-list'], ['items[].description'], { kind: 'any-value', - why: 'shown as-is — a string or number as text, an object as JSON (`components/src/renderers/basic/data-list.tsx:68`, `toText`)', + why: 'shown as-is — a string or number as text, an object as JSON (the definition list\'s `toText`)', }); on(['object-grid'], ['pagination.*'], { kind: 'open-bag', @@ -321,8 +320,8 @@ on(['object-grid'], ['pagination.*'], { }); // Held opaque by a ruling. A timeline feed entry's `content` is child schema -// nodes the entry draws below its description (`plugin-timeline/src/renderer.tsx:1659`, -// `:1716`, `renderChildren`); the ruling on the decision card #21704's fork 4 +// nodes the entry draws below its description (through `renderChildren`); the +// ruling on the decision card #21704's fork 4 // (letter B) keeps it unjudged on this row rather than making it a slot // position the page walks visit, until a writer appears — the census found // none. @@ -351,8 +350,8 @@ describe('§1 every `z.unknown()` member across ComponentPropsMap is typed, or l expect( unlisted, 'A `z.unknown()` member with no recorded reason. A member a renderer reads with a fixed shape is TYPED ' - + '(by reference where a schema already declares it, otherwise to the renderer\'s read at the ' - + '`.objectui-sha` pin); a member handed verbatim to a runner keeps `z.unknown()` with that reason in its ' + + '(by reference where a schema already declares it, otherwise to the renderer\'s read point, recorded in ' + + '`liveness/page.json` row `regions`); a member handed verbatim to a runner keeps `z.unknown()` with that reason in its ' + '`.describe()` and a `runner` line here.', ).toEqual([]); }); @@ -396,7 +395,7 @@ describe('§2 each recorded reason holds', () => { expect(silent).toEqual([]); }); - it('a `staged` member names a declared stage and a reader at the pin', () => { + it('a `staged` member names a declared stage and a reader', () => { for (const [key, reason] of entries) { if (reason.kind !== 'staged') continue; expect(Object.keys(STAGES), key).toContain(reason.stage); diff --git a/packages/spec/src/ui/component-record-approvals-attachments-22537.test.ts b/packages/spec/src/ui/component-record-approvals-attachments-22537.test.ts index 6a9b2e8c2ce..e149813e4d9 100644 --- a/packages/spec/src/ui/component-record-approvals-attachments-22537.test.ts +++ b/packages/spec/src/ui/component-record-approvals-attachments-22537.test.ts @@ -10,7 +10,8 @@ * `enable.files` object. * * Both are declared now, each with a strict row measured from its renderer's - * read points at the `.objectui-sha` pin. Both rows accept no key: + * read points (recorded in `liveness/page.json`, row `regions`). Both rows + * accept no key: * `record:attachments` reads no props at all, and `record:approvals` reads * only the host's runtime channel (`approvals`, `currentUserId`), which the row * refuses with a prescription instead of declaring. The authoring rule ids diff --git a/packages/spec/src/ui/component-record-block-field-security.test.ts b/packages/spec/src/ui/component-record-block-field-security.test.ts index 8d58934c2ce..2be4c2fd5a3 100644 --- a/packages/spec/src/ui/component-record-block-field-security.test.ts +++ b/packages/spec/src/ui/component-record-block-field-security.test.ts @@ -11,7 +11,8 @@ // // The card declared the field-security PAIR first and held the third key until // its semantics were ruled (#19186 ruling B: an ADR-0066 capability set) and the -// renderers read it that way at the pin. Every pin below is an ACCEPT pin: a +// renderers read it that way (read points recorded in `liveness/page.json`, row +// `regions`). Every pin below is an ACCEPT pin: a // full `safeParse` success is the assertion (not merely "no // `unrecognized_keys`"), because the value arm is part of what is being // declared, and a key-only assertion would stay green over a declaration that diff --git a/packages/spec/src/ui/component-record-blocks.test.ts b/packages/spec/src/ui/component-record-blocks.test.ts index 1770ee8cc0b..729c240d33a 100644 --- a/packages/spec/src/ui/component-record-blocks.test.ts +++ b/packages/spec/src/ui/component-record-blocks.test.ts @@ -6,10 +6,9 @@ // renderer + enum/palette presence + no row = the #5068 gate's dispatch skips // the props bag and every authored key rides through in silence). // -// The key sets are measured from the renderers' read points at the -// `.objectui-sha` pin (`record-alert.tsx`, `record-quick-actions.tsx`, -// `record-history.tsx` + `HistoryTimeline.tsx`, `record-chatter.tsx`), NOT -// from the registrations' declared-input lists — and the divergences between +// The key sets are measured from the renderers' read points (recorded in +// `liveness/page.json`, row `regions`), NOT from the registrations' +// declared-input lists — and the divergences between // the two are the interesting pins below: quick_actions' `aria` (read under a // spelling the shared ARIA shape refuses), its `actions` (host channel), // history's `entries`/`loading` (host channel), and alert's `icon` (READ here, @@ -332,9 +331,10 @@ describe('ComponentPropsMap["record:activity"] — the host feed slot is named, }); it('the `record:chatter` / `record:discussion` `feed` (the same object) refuses both keys and says nothing reads them there', () => { - // At the `.objectui-sha` pin the chatter renderer reads its rows and its - // loading flag off the discussion context and never reads `feed.items` / - // `feed.loading`, so the mount-true clause is the one asserted here. + // The chatter renderer reads its rows and its loading flag off the + // discussion context and never reads `feed.items` / `feed.loading` (read + // points recorded in `liveness/page.json`, row `regions`), so the + // mount-true clause is the one asserted here. for (const type of ['record:chatter', 'record:discussion'] as const) { for (const key of ['items', 'loading'] as const) { const result = ComponentPropsMap[type].safeParse({ feed: { [key]: key === 'items' ? [] : true } }); diff --git a/packages/spec/src/ui/component-reference-rail.test.ts b/packages/spec/src/ui/component-reference-rail.test.ts index 18687f82e4f..250e2778e5b 100644 --- a/packages/spec/src/ui/component-reference-rail.test.ts +++ b/packages/spec/src/ui/component-reference-rail.test.ts @@ -11,8 +11,8 @@ // reproduced here as pins in both directions: the planted bogus key refused, // the real shape accepted byte-identically. // -// The key set is measured from the renderer's read points at the -// `.objectui-sha` pin (`record-reference-rail.tsx`), NOT from its TS interface +// The key set is measured from the renderer's read points (recorded in +// `liveness/page.json`, row `regions`), NOT from its TS interface // — `icon` is in the interface and emitted by the page synthesizer but read by // no render path, so it is a guidance refusal here, not a declared key. diff --git a/packages/spec/src/ui/component-report-items-action-members-typed.pin.test.ts b/packages/spec/src/ui/component-report-items-action-members-typed.pin.test.ts index b76f367626c..0b8d2024ead 100644 --- a/packages/spec/src/ui/component-report-items-action-members-typed.pin.test.ts +++ b/packages/spec/src/ui/component-report-items-action-members-typed.pin.test.ts @@ -19,9 +19,8 @@ * * ## The defect this file closes * - * Each member is read with a fixed shape (measured at the `.objectui-sha` pin - * `2e818d0b51ec`, unchanged at objectui `main` `2abec3a96`; the read points are - * in the schemas' docblocks), and the rows declared them `z.unknown()` / + * Each member is read with a fixed shape (read points recorded in + * `liveness/page.json`, row `regions`), and the rows declared them `z.unknown()` / * open records. So a drill report with no `dataset` or a bare report name, a * gantt row on a feed timeline (or a feed entry with no `title`), and an * action member keyed `actionType`, `endpoint` or a misspelling all passed the @@ -430,9 +429,9 @@ describe('§3 each shape is the declaration the ruling names', () => { // ─────────────────────────────────────────────────────────────────────────── /** - * objectui `plugin-dashboard/src/DrillDownDrawer.tsx:92-99` at the - * `.objectui-sha` pin `2e818d0b51ec`, restated: the drawer draws a drill report - * as a report exactly when it holds, and lists the records otherwise. The + * objectui's drill drawer predicate (`isDatasetBoundReport`, recorded in + * `liveness/page.json`, row `regions`), restated: the drawer draws a drill + * report as a report exactly when it holds, and lists the records otherwise. The * drawer reads the value as written — a page component's `properties` is * never parsed on the way. */ diff --git a/packages/spec/src/ui/component.test.ts b/packages/spec/src/ui/component.test.ts index 153642daeca..b1a0234b1b0 100644 --- a/packages/spec/src/ui/component.test.ts +++ b/packages/spec/src/ui/component.test.ts @@ -81,7 +81,8 @@ describe('PageHeaderProps', () => { }); // #6776 — the three record-chrome switches objectui's header renderer has always -// read (`containers.tsx:979-981`) and `PageHeaderProps` never declared. Until +// read (read points recorded in `liveness/page.json`, row `regions`) and +// `PageHeaderProps` never declared. Until // this declaration objectui's published manifest called them legal while // `validateComponentProps` (#5068) called them undeclared — two platform // authorities disagreeing about one key (#5435), with the renderer siding with @@ -229,9 +230,9 @@ describe('PageTabsProps', () => { // rather than being stripped in silence (the retiredKey contract). The reason // the concept had to change spelling at all is structural, not aesthetic: a // props key named `type` collides with the page component's own dispatch key, -// which is why objectui's `SchemaRenderer.tsx:253,264` refuses to hoist -// `properties.type` and why `sdui-parser`'s `BASE_PROPS` (`validate.ts:20-30`) -// skips it before any validation runs. +// which is why objectui's `SchemaRenderer` refuses to hoist `properties.type` +// and why `sdui-parser`'s `BASE_PROPS` skips it before any validation runs +// (read points recorded in `liveness/page.json`, row `regions`). describe('PageTabsProps tabStyle — renamed from `type`, which collides with the component `type`', () => { it('accepts the three declared styles under the new key', () => { for (const tabStyle of ['line', 'card', 'pill'] as const) { @@ -256,8 +257,8 @@ describe('PageTabsProps tabStyle — renamed from `type`, which collides with th }); }); -// #6776 — `page:accordion.variant`, read at objectui `containers.tsx:734` and -// visible on screen (`flush` draws the divider, `card` leaves the border to the +// #6776 — `page:accordion.variant`, read by objectui's accordion renderer (read +// points recorded in `liveness/page.json`, row `regions`) and visible on screen (`flush` draws the divider, `card` leaves the border to the // panel's own content), declared nowhere until now. describe('PageAccordionProps variant — declared because the accordion renderer reads it', () => { const accordion = ComponentPropsMap['page:accordion']; @@ -283,115 +284,10 @@ describe('PageAccordionProps variant — declared because the accordion renderer // #9881 — the accept-pin for `page:accordion` items[].icon, a key a liveness // sweep once read as declared-but-unenforced. It has a live cross-repo consumer: // objectui's `PageAccordionRenderer` renders `{item.icon && }` inside the `AccordionTrigger` -// (`packages/components/src/renderers/layout/containers.tsx:1173-1179`), and the -// same file's `ComponentRegistry.register('accordion', …)` publishes the key to -// the Studio block designer at `:1222` (the `items` input, documented as -// `[{ label, icon?, collapsed?, children }]`). Measured at objectui `b9814b334`, re-read there 2026-10-14: -// every objectui file this record cites is byte-identical across the hop from -// `1fb1ced36` (`git diff --quiet`), so every anchor held unmoved. At -// `1fb1ced36`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `7578323a5` (`git diff --quiet`), so every anchor held unmoved. At -// `7578323a5`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `9646e2ed2` (`git diff --quiet`), so every anchor held unmoved. At -// `9646e2ed2`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `088e0f705` (`git diff --quiet`), so every anchor held unmoved. At -// `088e0f705`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `e5311165a` (`git diff --quiet`), so every anchor held unmoved. At -// `e5311165a`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `499799502` (`git diff --quiet`), so every anchor held unmoved. At -// `499799502`, re-read there 2026-10-11: -// every objectui file this record cites is byte-identical across the hop from -// `0df67f237` (`git diff --quiet`), so every anchor held unmoved. At -// `0df67f237`, re-read there 2026-10-11: -// every objectui file this record cites is byte-identical across the hop from -// `20c6d351a` (`git diff --quiet`), so every anchor held unmoved. At -// `20c6d351a`, re-read there 2026-10-10: -// every objectui file this record cites is byte-identical across the hop from -// `47b1f0bb7` (`git diff --quiet`), so every anchor held unmoved. At -// `47b1f0bb7`, re-read there 2026-10-09: -// every objectui file this record cites is byte-identical across the hop from -// `f0268ad78` (`git diff --quiet`), so every anchor held unmoved. At -// `f0268ad78`, re-read there 2026-10-09: -// `containers.tsx` changed across the hop from `a58626c88` (+92/-6, -// objectui#11811: a record action disabled by its predicate says why, in the -// `page:header` renderer), every hunk at `:1512` or below, so both anchors were -// re-READ in place and NEITHER moved, each byte-identical. At `a58626c88`, -// re-read there 2026-10-06: -// every objectui file this record cites is byte-identical across the hop from -// `0abd4f9f8` (`git diff --quiet`), so every anchor held unmoved. -// At `0abd4f9f8`, re-read there 2026-10-05: -// every objectui file this record cites is byte-identical across the hop from -// `9dfaca654` (`git diff --quiet`), so every anchor held unmoved. -// At `9dfaca654`. Re-derived at that pin -// 2026-10-05: `containers.tsx` changed across the hop from `2e818d0b5` (89 -// insertions, 0 deletions: objectui#11619's record picture in the record -// chrome — three import lines at the top of the file, and the -// `recordPictureUrl` helpers and the `page:header` picture read below both -// anchors), so both anchors were re-READ rather than carried, and BOTH MOVED -// down three lines with their text byte-identical: the icon block -// `1170-1176` -> `1173-1179`, still inside `PageAccordionRenderer`'s -// `AccordionTrigger`, and the input `1219` -> `1222`, still inside the -// `register('accordion', …)` inputs. At `2e818d0b5`, re-derived there -// 2026-10-04: every objectui file this record cites is byte-identical across -// the hop from `ab1879721` (`git diff --quiet`), so every anchor held unmoved. -// At `ab1879721`, re-derived there 2026-10-03: `containers.tsx` changed -// across the hop from `89cad75d5` (23 -// insertions, 27 deletions: objectui#11445's i18next count family for the -// `page:tabs` count badge, and objectui#11438 dropping the `page:header` -// registration's inert `breadcrumb` input), so both anchors were re-READ rather -// than carried, and BOTH MOVED up one line with their text byte-identical (the -// tab-count translation table above them lost a net line): the icon block -// `1171-1177` -> `1170-1176`, still inside `PageAccordionRenderer`'s -// `AccordionTrigger`, and the input `1220` -> `1219`, still inside the -// `register('accordion', …)` inputs. At `89cad75d5`, re-derived at that pin -// 2026-10-02: `containers.tsx` is byte-identical across the hop from -// `31971ff1e` (`git diff --quiet`), and both anchors were re-READ in place and -// still say what this block says: the icon block `1171-1177`, inside -// `PageAccordionRenderer`'s `AccordionTrigger`, and the input `1220`, inside -// the `register('accordion', …)` inputs. At `31971ff1e`, re-derived at that pin -// 2026-10-01: `containers.tsx` is byte-identical across the hop from -// `e420df310` (`git diff --quiet`), and both anchors were re-READ in place and -// still say what this block says: the icon block `1171-1177`, inside -// `PageAccordionRenderer`'s `AccordionTrigger`, and the input `1220`, inside -// the `register('accordion', …)` inputs. At `e420df310`, re-derived at that pin -// 2026-09-30: `containers.tsx` changed again across the hop from `db11afd49` (40 -// insertions, 26 deletions: objectui#11166's `page:header` breadcrumb slot and -// objectui#11212's fail-closed permission gates), every hunk of it at `:1286` -// or below, so both anchors were re-READ in place and NEITHER moved, each -// byte-identical: the icon block `1171-1177`, still inside -// `PageAccordionRenderer`'s `AccordionTrigger`, and the input `1220`, still -// inside the `register('accordion', …)` inputs. At `db11afd49`, re-derived at -// that pin 2026-09-29: `containers.tsx` changed again across the hop from `dd3f7e1be` (46 -// insertions, 6 deletions: objectui `0ecaa7dbb`'s block-level nested `aria` bags -// and comment re-citations), so both anchors were re-READ rather than carried, -// and BOTH MOVED with their text byte-identical, the icon block by 32 and the -// registration input by 34: `1139-1145` -> `1171-1177`, `1186` -> `1220`. At -// `dd3f7e1be`, re-derived at that pin -// 2026-09-28: `containers.tsx` changed again across the hop from `f8a9d0fb0` -// (120 insertions, 63 deletions; the 70 net lines above both anchors are -// objectui `3261e6479`'s one `titleFormat` interpolator for the record title -// and an import line from objectui `f5178a272`; the rest lands below both, -// objectui `1dae95a41`'s two re-cited `page:header` comments among it), so -// both anchors were re-READ -// rather than carried — and both MOVED with their text byte-identical: the -// icon block `1069-1075` -> `1139-1145`, still inside `PageAccordionRenderer`'s -// `AccordionTrigger`, and the input `1116` -> `1186`, still inside the -// `register('accordion', …)` inputs. At `f8a9d0fb0` (2026-09-24, off -// `62597c588`: 36 insertions, 7 deletions, objectui `ba0b61a60`, an import -// line and the `page:header` title) NEITHER had moved, each byte-identical to -// its `62597c588` and `87af769e9` text. The hop onto `62597c588` (74 -// insertions, 16 deletions, objectui `4c6f549ef`) read the same. (The hop before, off `53ded82bf`, -// moved them from `919-925` and `966` and rewrote the input LINE — it now -// declares `of: 'object'`, carries a longer description and no `label` — -// while the member list this pin cites stayed unchanged.) Identity preserves a -// wrong anchor as faithfully as a right one, which is why neither was carried -// (commit d1ba685ec). +// name={item.icon} …/>}` inside the `AccordionTrigger`, and the same file's +// `accordion` registration publishes the key to the Studio block designer (the +// `items` input, documented as `[{ label, icon?, collapsed?, children }]`) +// (read points recorded in `liveness/page.json`, row `regions`). // // #9397 spent a full dispatch cycle re-deriving that read point from scratch // after the sweep proposed retiring the key. This block plus the `.describe()` @@ -469,110 +365,10 @@ describe('PageTabsProps items[].value / items[].count — declared because the t // #9881 accordion key: same file, same renderer, same `LazyIcon` slot, and the // same bare declaration a liveness sweep reads as declared-but-unenforced. // objectui's `PageTabsRenderer` renders `{item.icon && }` inside the `TabsTrigger` -// (`packages/components/src/renderers/layout/containers.tsx:948-954`), and the -// same file's `ComponentRegistry.register('tabs', …)` publishes the key to the -// Studio block designer at `:1007` (the `items` input, documented as -// `[{ label, value?, icon?, count?, visibleWhen?, children }]`). Measured at -// objectui `b9814b334`, re-read there 2026-10-14: -// every objectui file this record cites is byte-identical across the hop from -// `1fb1ced36` (`git diff --quiet`), so every anchor held unmoved. At -// `1fb1ced36`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `7578323a5` (`git diff --quiet`), so every anchor held unmoved. At -// `7578323a5`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `9646e2ed2` (`git diff --quiet`), so every anchor held unmoved. At -// `9646e2ed2`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `088e0f705` (`git diff --quiet`), so every anchor held unmoved. At -// `088e0f705`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `e5311165a` (`git diff --quiet`), so every anchor held unmoved. At -// `e5311165a`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `499799502` (`git diff --quiet`), so every anchor held unmoved. At -// `499799502`, re-read there 2026-10-11: -// every objectui file this record cites is byte-identical across the hop from -// `0df67f237` (`git diff --quiet`), so every anchor held unmoved. At -// `0df67f237`, re-read there 2026-10-11: -// every objectui file this record cites is byte-identical across the hop from -// `20c6d351a` (`git diff --quiet`), so every anchor held unmoved. At -// `20c6d351a`, re-read there 2026-10-10: -// every objectui file this record cites is byte-identical across the hop from -// `47b1f0bb7` (`git diff --quiet`), so every anchor held unmoved. At -// `47b1f0bb7`, re-read there 2026-10-09: -// every objectui file this record cites is byte-identical across the hop from -// `f0268ad78` (`git diff --quiet`), so every anchor held unmoved. At -// `f0268ad78`, re-read there 2026-10-09: -// `containers.tsx` changed across the hop from `a58626c88` (+92/-6, -// objectui#11811: a record action disabled by its predicate says why, in the -// `page:header` renderer), every hunk at `:1512` or below, so both anchors were -// re-READ in place and NEITHER moved, each byte-identical. At `a58626c88`, -// re-read there 2026-10-06: -// every objectui file this record cites is byte-identical across the hop from -// `0abd4f9f8` (`git diff --quiet`), so every anchor held unmoved. -// At `0abd4f9f8`, re-read there 2026-10-05: -// every objectui file this record cites is byte-identical across the hop from -// `9dfaca654` (`git diff --quiet`), so every anchor held unmoved. -// At `9dfaca654`. Re-derived at -// that pin 2026-10-05: `containers.tsx` changed across the hop from -// `2e818d0b5` (89 insertions, 0 deletions: objectui#11619's record picture in -// the record chrome — three import lines at the top of the file, and the -// `recordPictureUrl` helpers and the `page:header` picture read below both -// anchors), so both anchors were re-READ rather than carried, and BOTH MOVED -// down three lines with their text byte-identical: the icon block `945-951` -// -> `948-954`, still inside `PageTabsRenderer`'s `TabsTrigger`, and the -// input `1004` -> `1007`, still inside the `register('tabs', …)` inputs. At -// `2e818d0b5`, re-derived there 2026-10-04: every objectui file this record cites is byte-identical -// across the hop from `ab1879721` (`git diff --quiet`), so every anchor held -// unmoved. At `ab1879721`, re-derived there 2026-10-03: `containers.tsx` -// changed across the hop from `89cad75d5` -// (23 insertions, 27 deletions: objectui#11445's i18next count family, which -// rewrote the tab-count translation table above both anchors and the count -// badge's `aria-label` between them, and objectui#11438 dropping the -// `page:header` registration's inert `breadcrumb` input below them), so both -// anchors were re-READ rather than carried, and BOTH MOVED up one line with -// their text byte-identical: the icon block `946-952` -> `945-951`, still -// inside `PageTabsRenderer`'s `TabsTrigger`, and the input `1005` -> `1004`, -// still inside the `register('tabs', …)` inputs. At `89cad75d5`, re-derived at -// that pin 2026-10-02: `containers.tsx` is byte-identical across the hop from -// `31971ff1e` (`git diff --quiet`), and both anchors were re-READ in place and -// still say what this block says: the icon block `946-952`, inside -// `PageTabsRenderer`'s `TabsTrigger`, and the input `1005`, inside the -// `register('tabs', …)` inputs. At `31971ff1e`, re-derived at that pin -// 2026-10-01: `containers.tsx` is byte-identical across the hop from -// `e420df310` (`git diff --quiet`), and both anchors were re-READ in place and -// still say what this block says: the icon block `946-952`, inside -// `PageTabsRenderer`'s `TabsTrigger`, and the input `1005`, inside the -// `register('tabs', …)` inputs. At `e420df310`, re-derived at that pin -// 2026-09-30: `containers.tsx` changed again across the hop from -// `db11afd49` (40 insertions, 26 deletions: objectui#11166's `page:header` -// breadcrumb slot and objectui#11212's fail-closed permission gates), every hunk -// of it at `:1286` or below, so both anchors were re-READ in place and NEITHER -// moved, each byte-identical: the icon block `946-952`, still inside -// `PageTabsRenderer`'s `TabsTrigger`, and the input `1005`, still inside the -// `register('tabs', …)` inputs. At `db11afd49`, re-derived at -// that pin 2026-09-29: `containers.tsx` changed again across the hop from -// `dd3f7e1be` (46 insertions, 6 deletions: objectui `0ecaa7dbb`'s block-level -// nested `aria` bags and comment re-citations), so both anchors were re-READ -// rather than carried, and BOTH MOVED with their text byte-identical, the net +23 -// all landing above them: `923-929` -> `946-952`, `982` -> `1005`. At -// `dd3f7e1be`, re-derived -// at that pin 2026-09-28: `containers.tsx` changed again across the hop from -// `f8a9d0fb0` (120 insertions, 63 deletions, 70 net lines above both anchors: -// objectui `3261e6479`'s `titleFormat` interpolator and an import line), so -// both anchors were re-READ rather than carried — and both MOVED with their -// text byte-identical: the icon block `853-859` -> `923-929`, still inside -// `PageTabsRenderer`'s `TabsTrigger`, and the input `912` -> `982`, still -// inside the `register('tabs', …)` inputs. At `f8a9d0fb0` (2026-09-24, off -// `62597c588`: 36 insertions, 7 deletions, objectui `ba0b61a60`) NEITHER had -// moved, each byte-identical to its `62597c588` and `87af769e9` text; the hop -// onto `62597c588` (74 insertions, 16 deletions, objectui `4c6f549ef`) read -// the same. (The hop before, off `53ded82bf`, moved them from -// `730-736` and `789` and rewrote the input LINE — `of: 'object'`, a longer -// description, no `label` — while the member list this pin cites stayed -// unchanged.) Never inferred (commit d1ba685ec). +// name={item.icon} …/>}` inside the `TabsTrigger`, and the same file's +// `tabs` registration publishes the key to the Studio block designer (the +// `items` input, documented as `[{ label, value?, icon?, count?, visibleWhen?, +// children }]`) (read points recorded in `liveness/page.json`, row `regions`). // // #9397 spent a full dispatch cycle re-deriving the accordion's read point // after the sweep proposed retiring it. This block plus the `.describe()` it @@ -900,10 +696,9 @@ describe('RecordDetailsProps', () => { // #11661 — three more section keys in exactly the pre-#11289 position // (honoured by the renderer, refused by this shape), declared under the - // inherited ruling. Measured at the `.objectui-sha` pin (`190fbd01`): - // `defaultCollapsed` at `DetailSection.tsx:139` - // (`useState(section.defaultCollapsed ?? false)`), `icon` at - // `DetailSection.tsx:516/546`, `description` at `DetailSection.tsx:520/557`. + // inherited ruling. Measured on the section renderer (read points recorded in `liveness/page.json`, row `regions`): + // `defaultCollapsed` (`useState(section.defaultCollapsed ?? false)`), `icon` + // and `description`. it('preserves `defaultCollapsed` / `icon` / `description` verbatim', () => { const result = RecordDetailsProps.parse({ sections: [{ @@ -962,7 +757,7 @@ describe('RecordDetailsProps', () => { }); it('still refuses `title`, deliberately withheld as a second spelling of `label`', () => { - // Honoured by the renderer at the pin, and OUT of the accept set on + // Honoured by the renderer, and OUT of the accept set on // purpose: `title` is a second spelling of the heading slot `label` // declares (the `page:card` `body`-vs-`children` shape, which #5775 // converged rather than declared) and is held for a maintainer ruling. @@ -1681,8 +1476,8 @@ describe('ComponentPropsMap', () => { // #11575 — the two `@objectstack/cloud-connection` console widgets. Rows // exist so the #5068 gate's dispatch reaches them; the accepted key set is - // EMPTY, measured from the renderers' read points at the `.objectui-sha` - // pin (both registrations discard the schema node — `() => `). + // EMPTY, measured from the renderers' read points (both registrations + // discard the schema node — `() => `; read points recorded in `liveness/page.json`, row `regions`). describe('plugin console widgets', () => { it('declares rows for cloud-connection:panel and marketplace:installed-list', () => { expect(ComponentPropsMap['cloud-connection:panel']).toBeDefined(); @@ -1720,9 +1515,9 @@ describe('ComponentPropsMap', () => { // third instance over. Row exists so the #5068 gate's dispatch reaches it // (and so the mcp canonical-envelope gate's door 3 reads its bag instead of // carrying a standing exemption); the accepted key set is EMPTY, measured - // from the renderer's read points at the `.objectui-sha` pin (the - // registration discards the schema node — `() => ` — and - // the component function takes no parameters). + // from the renderer's read points (the registration discards the schema + // node — `() => ` — and the component function takes no + // parameters; read points recorded in `liveness/page.json`, row `regions`). describe('mcp console widget', () => { it('declares a row for mcp:connect-agent', () => { expect(ComponentPropsMap['mcp:connect-agent']).toBeDefined(); @@ -2490,15 +2285,14 @@ describe('the seven `object-*` `filter` doors — one filter orthography platfor r.success ? [] : r.error!.issues.filter((i) => i.path.join('.') === path); it.each(OBJECT_DOORS)('%s accepts a ViewFilterRule[] filter and echoes it — the acceptance criterion', (type) => { - // Measured at the objectui pin `53ded82b` before the declarations moved: + // Measured before the declarations moved (read points recorded in `liveness/page.json`, row `regions`): // grid lowers the rule array through `toFilterNode`; kanban and calendar // hand it verbatim to `$filter`, where `convertQueryParams` lowers it; the // metric's aggregate path lowers it through `translateFilterArray` and // `parseFilterAST` before `POST /analytics/query` (objectui#7754 — the - // door the family was sequenced behind, #15828 / the pin bump commit 30b099078). Re-measured at - // the same pin for the three #18305 doors: `ObjectMap.tsx:742`, - // `ObjectGantt.tsx:738` and `ObjectTree.tsx:474` each hand `schema.filter` - // verbatim to `$filter`, the kanban/calendar shape. + // door the family was sequenced behind, #15828 / the pin bump commit 30b099078). Re-measured + // for the three #18305 doors: the map, gantt and tree renderers each hand + // `schema.filter` verbatim to `$filter`, the kanban/calendar shape. const r = door(type).safeParse({ objectName: 'showcase_task', filter: RULES }); expect(r.success).toBe(true); expect(r.data!.filter).toEqual(RULES); @@ -2561,18 +2355,15 @@ describe('the seven `object-*` `filter` doors — one filter orthography platfor describe('the four `object-*` `sort` doors — one sort orthography, the array', () => { // `object-map` and `object-gantt` joined at #18305: both hand `schema.sort` // to the SAME shared sink the grid and the calendar do - // (`convertSortToQueryParams`, `core/src/utils/sort-query.ts`) — - // `ObjectMap.tsx:743`, `ObjectGantt.tsx:739` at the pin `53ded82b`. - // `object-tree` is deliberately NOT here: its fetch (`ObjectTree.tsx:473-484`) - // carries `$filter`, `$top` and `$expand` and no `$orderby` at all, so its row + // (`convertSortToQueryParams`; read points recorded in `liveness/page.json`, row `regions`). + // `object-tree` is deliberately NOT here: its fetch carries `$filter`, `$top` and `$expand` and no `$orderby` at all, so its row // declares no `sort` — a door with no read site is what this family refuses to // publish. const SORT_DOORS = ['object-grid', 'object-calendar', 'object-map', 'object-gantt'] as const; const ARRAY_FORM = [{ field: 'created_at', order: 'desc' }]; /** - * The legacy OData-ish clause `convertSortToQueryParams` honours at the - * objectui pin `53ded82b` (`core/src/utils/sort-query.ts:66-70`) and that - * `ObjectGrid.tsx:1845-1846` puts on `$orderby` verbatim. Retired by the + * The legacy OData-ish clause `convertSortToQueryParams` honoured when this + * was measured, and that the grid put on `$orderby` verbatim (read points recorded in `liveness/page.json`, row `regions`). Retired by the * ruling; refused here. */ const STRING_FORM = 'created_at desc'; @@ -2957,7 +2748,8 @@ describe('RecordChatterProps', () => { it("should accept exactly the renderer's position vocabulary", () => { // `RecordChatterPanel` branches on right/left (docked) vs bottom - // (in-flow) — measured at objectui pin 665661ab0932. One vocabulary. + // (in-flow) (read points recorded in `liveness/page.json`, row + // `regions`). One vocabulary. const positions = ['bottom', 'right', 'left'] as const; positions.forEach(position => { const result = RecordChatterProps.parse({ position }); @@ -3534,186 +3326,17 @@ describe('object-* block props schemas — declared, so the props gate has a sch // #16503 — the spec half of objectui#8172 (decision batch #68, 2026-09-07, // option A: the contract declares the capability that already ships, is -// documented and is in use). Measured at objectui `b9814b334` (re-read there 2026-10-14: -// `plugin-kanban/src/types.ts`, `ObjectKanban.tsx` and `plugin-kanban.mdx` are -// byte-identical across the hop from `1fb1ced36` (`git diff --quiet`), so the -// default `:100`, the `$top` read `:764`, the `limit` row and the `limit: 250` -// snippet did not move; `plugin-kanban/src/index.tsx` changed only below the -// mapping (+2/-2, objectui#12030, objectui `c5406c201`: a comment re-cited to -// `18.0.0-next.0`), which did not move from `488-492`; and `objectql.ts` -// (+15/-27, the same commit) changed only above the member, which MOVED `4838` -// -> `4826` byte-identical, still inside `ObjectKanbanSchema`. At `1fb1ced36`, -// re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `7578323a5` (`git diff --quiet`), so every anchor held unmoved. At -// `7578323a5`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `9646e2ed2` (`git diff --quiet`), so every anchor held unmoved. At -// `9646e2ed2`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `088e0f705` (`git diff --quiet`), so every anchor held unmoved. At -// `088e0f705`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `e5311165a` (`git diff --quiet`), so every anchor held unmoved. At -// `e5311165a`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `499799502` (`git diff --quiet`), so every anchor held unmoved. At -// `499799502`, re-read there 2026-10-11: -// every objectui file this record cites is byte-identical across the hop from -// `0df67f237` (`git diff --quiet`), so every anchor held unmoved. At -// `0df67f237`, re-read there 2026-10-11: -// `plugin-kanban/src/index.tsx`, `plugin-kanban/src/types.ts` and -// `objectql.ts` are byte-identical across the hop from `20c6d351a` (`git diff -// --quiet`), so the mapping `488-492` and the member `4838` did not move; -// `ObjectKanban.tsx` changed (+21/-1, objectui#12082, objectui `5af42dbbd`: -// the card move reads the affordance-to-grant map), two import lines above -// `:98` and every other hunk below the fetch, so the default MOVED `:98` -> -// `:100` and the `$top` read `:762` -> `:764`, both byte-identical; and -// `plugin-kanban.mdx` changed in its drag-and-drop feature bullet only -// (+5/-1, the same commit), above the `limit` row and the `limit: 250` -// snippet, neither of which changed. At `20c6d351a`, re-read there -// 2026-10-10: -// every objectui file this record cites is byte-identical across the hop from -// `47b1f0bb7` (`git diff --quiet`) except `objectql.ts` (+14/-13, comments -// only: objectui#12063, objectui `c7b30bd66`, and objectui#6152 round 15, -// objectui `025341692`, re-word the `object-chart` legacy-axis docblocks), -// whose every hunk is below the member, at `:5441` or later, so the member -// `4838` did not move and is byte-identical, still inside -// `ObjectKanbanSchema`; the `$top` read `:762`, the default `:98`, the `limit` -// row, the `limit: 250` snippet and the mapping `488-492` held unmoved. At -// `47b1f0bb7`, re-read there 2026-10-09: -// every objectui file this record cites is byte-identical across the hop from -// `f0268ad78` (`git diff --quiet`), so every anchor held unmoved. At -// `f0268ad78`, re-read there 2026-10-09: -// `ObjectKanban.tsx`, `plugin-kanban/src/types.ts` and `plugin-kanban.mdx` are -// byte-identical to `a58626c88` (`git diff --quiet`), so the `$top` read -// `:762`, the default `:98`, the `limit` row and the `limit: 250` snippet did -// not move or change; `plugin-kanban/src/index.tsx` changed only below the -// mapping (+3/-3: a comment re-cited to 17.7.0, and objectui#6152 round 8's -// `filter` input description), which did not move from `488-492`; and -// `objectql.ts` (+218/-125: objectui#6152 rounds 7 to 12, objectui#11880 item 5 -// and the 17.7.0 moves) MOVED the member `4754` -> `4838` byte-identical, still -// inside `ObjectKanbanSchema`. At `a58626c88`, re-read there 2026-10-06: -// `ObjectKanban.tsx` changed (+65/-2, objectui#11629: each column header totals -// the view's `summarizeField`), which MOVED the `$top` read `752` -> `762` and -// the default `:97` -> `:98`, both byte-identical; `plugin-kanban.mdx` gained -// one feature bullet above the `limit` row and the `limit: 250` snippet, -// neither of which changed; and `plugin-kanban/src/index.tsx`, -// `plugin-kanban/src/types.ts` and `objectql.ts` are byte-identical (`git diff -// --quiet`), so `488-492` and `4754` did not move. -// At `0abd4f9f8`, re-read there 2026-10-05: -// every objectui file this record cites is byte-identical across the hop from -// `9dfaca654` (`git diff --quiet`), so every anchor held unmoved. -// At `9dfaca654`; re-measured there 2026-10-05 — -// `plugin-kanban/src/types.ts` and `plugin-kanban.mdx` are byte-identical to -// `2e818d0b5` (`git diff --quiet`), so `types.ts` still declares no `limit` -// and the mdx still teaches `limit: 250` with its Properties row; the other -// three files changed, so their anchors were re-READ, and each held or MOVED -// with its text byte-identical. `ObjectKanban.tsx` (+35/-5, objectui#8347) -// edited its type import in place (`:10`) and, below `:97` and above the -// fetch, added a private `GateBoundKanbanSchema` read type, a re-worded -// docblock and a widened `schema` binding, so the constant did not move and -// the query line MOVED `722` -> `752`; `plugin-kanban/src/index.tsx` (+34/-3) gained one -// docblock line above the mapping (objectui#8347) and objectui#11605's -// `requiresObject` gate and non-required `objectName` input below it, so -// `OBJECT_KANBAN_DATA_SOURCE` MOVED `487-491` -> `488-492`, still mapping -// `limit: 'limit'`; `objectql.ts` (+85/-45: objectui#11615's section-entry -// arm, objectui#11266's `InlineGridColumn` columns and objectui#8347 among -// them) MOVED the member `4720` -> `4754`, still inside -// `ObjectKanbanSchema`. At `2e818d0b5`, re-measured there 2026-10-04 — -// `plugin-kanban/src/types.ts`, `ObjectKanban.tsx` and -// `plugin-kanban/src/index.tsx` are byte-identical to `ab1879721` -// (`git diff --quiet`), so `types.ts` still declares no `limit` and `:722` / -// `:97` / `487-491` did not move; `plugin-kanban.mdx` gained a `grouping` -// Properties row below the `limit` row (objectui#11216) and still teaches -// `limit: 250`; `objectql.ts` changed only below the member (objectui#11216's -// `grouping` declaration on `ObjectKanbanSchema`), so `:4720` did not move. -// At `ab1879721`, re-measured there 2026-10-03 — -// `plugin-kanban/src/types.ts`, `ObjectKanban.tsx` and `plugin-kanban.mdx` are -// byte-identical to `89cad75d5` (`git diff --quiet`), so `types.ts` still -// declares no `limit`, `:722` / `:97` did not move and were re-read in place, -// and the mdx still teaches `limit: 250` with its Properties row; `index.tsx` -// changed only below the mapping (objectui#11438's 17.6.0 re-citation in the -// `ObjectKanbanRenderer` docblock and objectui#11522's `{ condition, style }` -// description on the `conditionalFormatting` input), so `487-491` did not -// move and still maps `limit: 'limit'`; `objectql.ts` MOVED the member `4661` -// -> `4720` byte-identical, still inside `ObjectKanbanSchema` (the 59 lines -// are declarations the range changed above it: the spec `{ condition, style }` -// rule types of objectui#11533 / objectui#11522, `ObjectGridSchema`'s -// `keyboardNavigation` of objectui#11068 and its 17.6.0 members of -// objectui#11438 among them). At `89cad75d5`, re-measured there 2026-10-02 — -// `plugin-kanban/src/types.ts` and `ObjectKanban.tsx` are byte-identical to -// `31971ff1e` (`git diff --quiet`), so `types.ts` still declares no `limit` -// and `:722` / `:97` did not move and were re-read in place; `index.tsx` -// changed only in the `ObjectKanbanRenderer` docblock and the `navigation` -// input's description (objectui#11293's record-navigator `page` mode), at -// `:626` and below, so the mapping did not move from `487-491`; `objectql.ts` -// MOVED the member `4588` -> `4661` byte-identical, still inside -// `ObjectKanbanSchema` (the 73 lines are declarations eight objectui commits -// of the range added above it); and `plugin-kanban.mdx` rewrote its -// `navigation` Properties row and gained a `swimlaneField` row, both below the -// `limit` row, which is byte-identical, and still teaches `limit: 250`. At -// `31971ff1e`, re-measured there 2026-10-01 — -// `plugin-kanban/src/types.ts` is byte-identical to `e420df310` and still -// declares no `limit`; `ObjectKanban.tsx` changed only in the comment above -// its `navigation` read (objectui#8652), below both anchors, so `:722` and -// `:97` did not move; `index.tsx` changed below the mapping (objectui#8652's -// `navigation` input), which did not move from `487-491`; `objectql.ts` -// MOVED the member `4430` -> `4588` byte-identical, still inside -// `ObjectKanbanSchema`; and `plugin-kanban.mdx` gained a `navigation` -// Properties row below the `limit` row and still teaches `limit: 250`. At -// `e420df310`, re-measured there 2026-09-30 — -// `plugin-kanban/src/types.ts` is byte-identical to `db11afd49` and still -// declares no `limit`; the other four files changed, so their anchors were -// re-READ. `ObjectKanban.tsx` changed at BOTH anchors: objectui#9853 renamed -// the default `DEFAULT_KANBAN_LIMIT` to `DEFAULT_KANBAN_FETCH_BATCH_SIZE` -// (still 100, now documented as the board's fetch batch, not a page size) and -// objectui#11234 moved the board into an internal `KanbanBoardCore`, so the -// query line now reads `$top: resolveRowLimit(schema.limit, -// DEFAULT_KANBAN_FETCH_BATCH_SIZE)` and moved `712` -> `722`, and the constant -// `:88` -> `:97` — same cap, same default, a renamed name. `index.tsx` lost -// the Quick Add plumbing above the mapping (objectui#8285, objectui#11234), so -// `OBJECT_KANBAN_DATA_SOURCE` MOVED `506-510` -> `487-491` byte-identical; -// `objectql.ts` MOVED the member `4302` -> `4430` byte-identical, still inside -// `ObjectKanbanSchema`; `plugin-kanban.mdx` still teaches `limit: 250` and its -// Properties row. At `db11afd49`, re-measured there 2026-09-29 — -// `plugin-kanban.mdx`, `plugin-kanban/src/types.ts` and -// `plugin-kanban/src/index.tsx` are byte-identical to `dd3f7e1be`; -// `ObjectKanban.tsx` changed in one comment line (`:1367`), so `:712` and `:88` -// did not move; `objectql.ts` gained declarations above the member, which is -// still inside `ObjectKanbanSchema`, and MOVED `4139` -> `4302` with its text -// byte-identical. At `dd3f7e1be`, re-measured there 2026-09-28 — -// `plugin-kanban.mdx` is byte-identical to `f8a9d0fb0`; the other four files -// changed, so their anchors were re-READ, and every one MOVED with its text -// byte-identical: `ObjectKanban.tsx` gained three imports and, above the -// fetch, objectui#10666's resolved `$filter` and objectui#10572's -// invalidation re-read (`687` -> `712`, `:85` -> `:88`; its comments also -// re-cite objectui#8307 as `5591f03bd`, objectui `1dae95a41`, one line for -// one); `index.tsx` changed -// above the mapping in exports and docblocks only (objectui#10582's -// `ColumnWidthConfig`, objectui#8522's `useColumnWidths`), net one line up -// (`507-511` -> `506-510`); `objectql.ts` grew above the member, which is -// still inside `ObjectKanbanSchema` (`3832` -> `4139`); and `types.ts` only -// dropped the same `ColumnWidthConfig` re-export and still declares no -// `limit`. At `f8a9d0fb0` (2026-09-24) `types.ts` and `plugin-kanban.mdx` -// were byte-identical to `62597c588`, `676` -> `687` and `:84` -> `:85` moved -// with their text byte-identical, the mapping gained `sort: true` beside the -// `limit` it cites (objectui#10068, `447-450` -> `507-511`), and the -// `limit?: number` member moved `3735` -> `3832`. All five files were -// byte-identical across the hop onto `62597c588`, and were last re-READ at -// `87af769e9` 2026-09-20, the hop that moved every anchor and renamed one -// face outright): `plugin-kanban/src/ObjectKanban.tsx:762` -// queries `$top: resolveRowLimit(schema.limit, DEFAULT_KANBAN_FETCH_BATCH_SIZE)` -// (100, `:98`; the bare `??` became `resolveRowLimit` in objectui#9925, which drops -// and reports a cap the contract refuses), -// `plugin-kanban/src/index.tsx:488-492` maps `limit: 'limit'` in -// `OBJECT_KANBAN_DATA_SOURCE`, ⚠️ `KanbanSchema` is RETIRED at this pin and -// `plugin-kanban/src/types.ts` declares the member no more — the published -// twin is `ObjectKanbanSchema`, declaring `limit?: number` at -// `packages/types/src/objectql.ts:4826` — and `content/docs/plugins/plugin-kanban.mdx` -// teaches `limit: 250` with a Properties row. The strict map refused the key by -// name — the same `unrecognized_keys` verdict as the `bogusProp` control — so an -// author following the published docs wrote a node the save gate rejected. +// documented and is in use). Read points recorded in `liveness/page.json`, row +// `regions`: the board queries `$top: resolveRowLimit(schema.limit, +// DEFAULT_KANBAN_FETCH_BATCH_SIZE)` (100; the bare `??` became +// `resolveRowLimit` in objectui#9925, which drops and reports a cap the +// contract refuses), the registration maps `limit: 'limit'` in +// `OBJECT_KANBAN_DATA_SOURCE`, ⚠️ `KanbanSchema` is RETIRED and the published +// twin is `ObjectKanbanSchema`, declaring `limit?: number` — and objectui's +// kanban plugin guide teaches `limit: 250` with a Properties row. The strict +// map refused the key by name — the same `unrecognized_keys` verdict as the +// `bogusProp` control — so an author following the published docs wrote a node +// the save gate rejected. describe('ObjectKanbanPropsSchema limit — the row cap four objectui faces already implement', () => { const kanban = ComponentPropsMap['object-kanban']; @@ -3771,133 +3394,14 @@ describe('ObjectKanbanPropsSchema limit — the row cap four objectui faces alre // named the React-host `kanban-ui` block as where it stays, and objectui has // since retired that block, objectui#8257). Unlike `limit` above — a key four // objectui faces already implemented, so the spec was the half that was wrong -// — `quickAdd` was FORWARDED and never read: at objectui `b9814b334` (re-read there 2026-10-14: -// every objectui file this record cites is byte-identical across the hop from -// `1fb1ced36` (`git diff --quiet`), so every anchor held unmoved. At -// `1fb1ced36`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `7578323a5` (`git diff --quiet`), so every anchor held unmoved. At -// `7578323a5`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `9646e2ed2` (`git diff --quiet`), so every anchor held unmoved. At -// `9646e2ed2`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `088e0f705` (`git diff --quiet`), so every anchor held unmoved. At -// `088e0f705`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `e5311165a` (`git diff --quiet`), so every anchor held unmoved. At -// `e5311165a`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `499799502` (`git diff --quiet`), so every anchor held unmoved. At -// `499799502`, re-read there 2026-10-11: -// every objectui file this record cites is byte-identical across the hop from -// `0df67f237` (`git diff --quiet`), so every anchor held unmoved. At -// `0df67f237`, re-read there 2026-10-11: -// all three files changed across the hop from `20c6d351a` for one commit, -// objectui#12082 (objectui `5af42dbbd`: a card is draggable only for a caller -// the `kanbanCardMove` row of the affordance-to-grant map allows), no hunk on -// a cited line, so every anchor MOVED with its line byte-identical: -// `KanbanBoardCore.tsx` (+9/-0) `:78` -> `:85` and `:111-112` -> `:120-121`, -// `ObjectKanban.tsx` (+21/-1) the spread `:1731` -> `:1751`, and -// `KanbanImpl.tsx` (+30/-7) the gate `:671` / `:684` -> `:688` / `:701`, still -// reading `quickAdd && onQuickAdd`; and the counts re-read the same, 2 / 2 / -// 11. At `20c6d351a`, re-read there 2026-10-10: -// `KanbanBoardCore.tsx` and `ObjectKanban.tsx` are byte-identical across the -// hop from `47b1f0bb7` (`git diff --quiet`), so `:78`, `:111-112` and the -// spread `:1731` did not move, and the counts re-read the same, 2 / 2 / 11; -// `KanbanImpl.tsx` changed (+9/-6, objectui#6349 batch 7, objectui -// `c0c0a0d56`: its rule type spelled `KanbanConditionalFormattingRule` by that -// name, three lines of comment added above the props), so the gate MOVED -// `:668` / `:681` -> `:671` / `:684`, both lines byte-identical and still -// reading `quickAdd && onQuickAdd`. At `47b1f0bb7`, re-read there 2026-10-09: -// `KanbanBoardCore.tsx`, `ObjectKanban.tsx` and `KanbanImpl.tsx` are -// byte-identical across the hop from `f0268ad78` (`git diff --quiet`), so -// `:78`, `:111-112`, the spread `:1731` and the gate `:668` / `:681` did not -// move, and the counts re-read the same, 2 / 2 / 11. At `f0268ad78`, re-read -// there 2026-10-09: -// `KanbanBoardCore.tsx`, `ObjectKanban.tsx` and `KanbanImpl.tsx` are -// byte-identical across the hop from `a58626c88` (`git diff --quiet`), so -// `:78`, `:111-112`, the spread `:1731` and the gate `:668` / `:681` did not -// move, and the counts re-read the same, 2 / 2 / 11. At `a58626c88`, re-read -// there 2026-10-06: -// `KanbanBoardCore.tsx` is byte-identical (`git diff --quiet`), so `:78` and -// `:111-112` did not move; `ObjectKanban.tsx` (+65/-2) and `KanbanImpl.tsx` -// (+48/-1) changed for objectui#11629 (each column header totals the view's -// `summarizeField`), and every anchor in them MOVED with its line -// byte-identical: the spread `1669` -> `1731`, still the `schema` bag handed to -// `KanbanBoardCore`, and the gate `624` -> `668` and `637` -> `681`; and the -// counts re-read the same, 2 / 2 / 11. -// At `0abd4f9f8`, re-read there 2026-10-05: -// every objectui file this record cites is byte-identical across the hop from -// `9dfaca654` (`git diff --quiet`), so every anchor held unmoved. -// At `9dfaca654`; re-measured there 2026-10-05 — -// `KanbanBoardCore.tsx` and `KanbanImpl.tsx` are byte-identical to `2e818d0b5` -// (`git diff --quiet`), so `:78`, `:111-112`, `:624` and `:637` did not move; -// `ObjectKanban.tsx` changed above the spread (+35/-5, objectui#8347's private -// `GateBoundKanbanSchema` read type and widened `schema` binding), which MOVED -// `1639` -> `1669` with its line byte-identical, still the `schema` bag handed -// to `KanbanBoardCore`; and the counts re-read the same, 2 / 2 / 11. At -// `2e818d0b5`, re-measured there 2026-10-04 — -// every objectui file this record cites is byte-identical across the hop from -// `ab1879721` (`git diff --quiet`), so every anchor held unmoved. At -// `ab1879721`, re-measured there 2026-10-03 — -// `KanbanBoardCore.tsx` and `ObjectKanban.tsx` are byte-identical to -// `89cad75d5` (`git diff --quiet`), so `:78`, `:111-112` and the spread -// `:1639` did not move and were re-read in place, and the counts re-read the -// same, 2 / 2 / 11; `KanbanImpl.tsx` changed only in two comments above the -// gate (objectui#11522: card formatting is the spec `{ condition, style }` -// rule alone, +8/-5), so both gate lines MOVED by 3 byte-identical, `621` -> -// `624` and `634` -> `637`, and still read `quickAdd && onQuickAdd`. At -// `89cad75d5`, re-measured there 2026-10-02 — -// `KanbanImpl.tsx`, `KanbanBoardCore.tsx` and `ObjectKanban.tsx` are -// byte-identical to `31971ff1e` (`git diff --quiet`), so `:621`, `:634`, -// `:78`, `:111-112` and the spread `:1639` did not move and were re-read in -// place, and the counts re-read the same, 2 / 2 / 11. At `31971ff1e`, -// re-measured there 2026-10-01 — -// `KanbanImpl.tsx` and `KanbanBoardCore.tsx` are byte-identical to -// `e420df310`, so `:621`, `:634`, `:78` and `:111-112` did not move and were -// re-read in place; `ObjectKanban.tsx` changed only in the comment above its -// `navigation` read (objectui#8652), above the spread, which MOVED `1641` -> -// `1639` with its line byte-identical; and the counts re-read the same, 2 / -// 2 / 11. At `e420df310`, re-measured there 2026-09-30 — -// `KanbanImpl.tsx` is byte-identical to `db11afd49`, so `:621` and `:634` did -// not move and were re-read in place; `ObjectKanban.tsx` changed, carrying -// objectui's own half of this retirement: objectui#8285 stopped forwarding -// `quickAdd`, and objectui#11234 retired `onQuickAdd` and mounts an internal -// `KanbanBoardCore` where it mounted `KanbanRenderer`. So the spread MOVED -// `1614` -> `1641` with its line byte-identical, but it now feeds -// `KanbanBoardCore`, which reads neither key off `schema` and takes the pair -// only as explicit props (`KanbanBoardCore.tsx:78`, `:111-112`) that -// `ObjectKanban` never passes. ⚠️ The 0 / 0 / 11 counts did NOT re-read the -// same: `ObjectKanban.tsx` now spells `quickAdd` 2 times and `onQuickAdd` 2 -// times, all four inside the two objectui#11234 comments that record the cut, -// so it still names neither half in code, against 11 for `onCardClick`. At -// `db11afd49`, re-measured there 2026-09-29 — -// `KanbanImpl.tsx` is byte-identical to `dd3f7e1be` and `ObjectKanban.tsx` -// changed in one comment line (`:1367`), so `:1614`, `:621` and `:634` did not -// move and were re-read in place, and the 0 / 0 / 11 counts re-read the same. At -// `dd3f7e1be`, re-measured there 2026-09-28 — -// `KanbanImpl.tsx` changed in three comment lines only (objectui `1dae95a41` -// re-citing objectui#8307 as `5591f03bd`, one line for one), so `:621` / -// `:634` did not move and re-read the same, and `ObjectKanban.tsx` changed -// above the spread (objectui#10666's resolved `$filter`, objectui#10572's -// invalidation re-read, objectui#10663's error clear), which MOVED `1578` -> -// `1614` with its text byte-identical, still the `schema` bag handed to -// `KanbanRenderer` — the same re-citation touched the comment on the line -// below it, not the spread; at `f8a9d0fb0` (2026-09-24) it -// had MOVED `1563` -> `1578` the same way (objectui#10068's `$orderby`); both -// files were byte-identical across the hop onto `62597c588`, and every anchor -// was re-READ at `87af769e9` 2026-09-20) `ObjectKanban.tsx:1731` -// spreads the -// authored bag into `KanbanBoardCore` (into `KanbanRenderer` until -// objectui#11234, which reads the pair off it no more) and `KanbanImpl` gates the affordance on -// `quickAdd && onQuickAdd` (`KanbanImpl.tsx:671`, `:684` — the file is spelled -// here because those two ranges are NOT in `ObjectKanban.tsx`), while -// `onQuickAdd` is a -// host-supplied FUNCTION no producer puts on an `object-kanban` node -// (`ObjectKanban.tsx` names neither half in code: 0 each re-counted at this pin -// outside the two comments that record objectui's cut, 2 each with them, -// against 11 for the sibling `onCardClick` in the same file). +// — `quickAdd` was FORWARDED and never read (read points recorded in +// `liveness/page.json`, row `regions`): the board spreads the authored bag into +// `KanbanBoardCore` (into `KanbanRenderer` until objectui#11234, which reads +// the pair off it no more), `KanbanImpl` gates the affordance on +// `quickAdd && onQuickAdd`, and `onQuickAdd` is a host-supplied FUNCTION no +// producer puts on an `object-kanban` node (`ObjectKanban` names neither half +// in code: 0 each outside the two comments that record objectui's cut, 2 each +// with them, against 11 for the sibling `onCardClick` in the same file). describe('ObjectKanbanPropsSchema quickAdd is retired', () => { const kanban = ComponentPropsMap['object-kanban']; @@ -3991,131 +3495,27 @@ describe('ObjectKanbanPropsSchema quickAdd is retired', () => { // that sent #9397 on a full dispatch cycle re-deriving the accordion read point. // #9881 and commit 60e0f900a recorded the accordion and tab items; these two close the set. // -// The button record re-measured at objectui `b9814b334`, re-read there 2026-10-14: -// every objectui file this record cites is byte-identical across the hop from -// `1fb1ced36` (`git diff --quiet`), so every anchor held unmoved. At -// `1fb1ced36`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `7578323a5` (`git diff --quiet`), so every anchor held unmoved. At -// `7578323a5`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `9646e2ed2` (`git diff --quiet`), so every anchor held unmoved. At -// `9646e2ed2`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `088e0f705` (`git diff --quiet`), so every anchor held unmoved. At -// `088e0f705`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `e5311165a` (`git diff --quiet`), so every anchor held unmoved. At -// `e5311165a`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `499799502` (`git diff --quiet`), so every anchor held unmoved. At -// `499799502`, re-read there 2026-10-11: -// every objectui file this record cites is byte-identical across the hop from -// `0df67f237` (`git diff --quiet`), so every anchor held unmoved. At -// `0df67f237`, re-read there 2026-10-11: -// every objectui file this record cites is byte-identical across the hop from -// `20c6d351a` (`git diff --quiet`), so every anchor held unmoved. At -// `20c6d351a`, re-read there 2026-10-10: -// every objectui file this record cites is byte-identical across the hop from -// `47b1f0bb7` (`git diff --quiet`), so every anchor held unmoved. At -// `47b1f0bb7`, re-read there 2026-10-09: -// every objectui file this record cites is byte-identical across the hop from -// `f0268ad78` (`git diff --quiet`), so every anchor held unmoved. At -// `f0268ad78`, re-read there 2026-10-09: -// every objectui file this record cites is byte-identical across the hop from -// `a58626c88` (`git diff --quiet`), so every anchor held unmoved. At -// `a58626c88`, re-read there 2026-10-06: -// every objectui file this record cites is byte-identical across the hop from -// `0abd4f9f8` (`git diff --quiet`), so every anchor held unmoved. -// At `0abd4f9f8`, re-read there 2026-10-05: -// every objectui file this record cites is byte-identical across the hop from -// `9dfaca654` (`git diff --quiet`), so every anchor held unmoved. -// At `9dfaca654`, re-derived there 2026-10-05: every objectui -// file this record cites (`button.tsx` read as both the `renderers/form` -// registration and the `ui` primitive) is byte-identical to `2e818d0b5` -// (`git diff --quiet`), so every anchor below holds unmoved. At `2e818d0b5`, -// re-derived there 2026-10-04: every objectui -// file this record cites is byte-identical to `ab1879721` (`git diff --quiet`), -// so every anchor below holds unmoved. At `ab1879721`, re-derived there -// 2026-10-03: `button.tsx`, -// `lazy-icon.tsx`, `resolve-icon.ts` and the generated -// `lucide-record-icon-names.ts` are byte-identical to `89cad75d5` -// (`git diff --quiet`), so every anchor below holds unmoved and was re-read in -// place. At `89cad75d5`, 2026-10-02: `button.tsx`, -// `lazy-icon.tsx`, `resolve-icon.ts` and the generated -// `lucide-record-icon-names.ts` are byte-identical to `31971ff1e` -// (`git diff --quiet`), so every anchor below holds unmoved and was re-read in -// place. At `31971ff1e`, 2026-10-01: `button.tsx`, -// `lazy-icon.tsx`, `resolve-icon.ts` and the generated -// `lucide-record-icon-names.ts` are byte-identical to `e420df310` -// (`git diff --quiet`), so every anchor below holds unmoved and was re-read in -// place. At `e420df310`, 2026-09-30: `button.tsx`, -// `lazy-icon.tsx`, `resolve-icon.ts` and the generated -// `lucide-record-icon-names.ts` are byte-identical to `db11afd49`, so every -// anchor below holds unmoved and was re-read in place. At `db11afd49`, -// 2026-09-29: `button.tsx`, -// `lazy-icon.tsx` and `resolve-icon.ts` are byte-identical to `dd3f7e1be`, so -// every anchor below holds unmoved. At `dd3f7e1be`, 2026-09-28: `button.tsx` and -// `lazy-icon.tsx` are byte-identical to `f8a9d0fb0` (`git diff --quiet`), so -// the `:43` / `:72` / `:74` anchors below hold by identity, and -// `resolve-icon.ts` changed (42 insertions, 17 deletions, objectui -// `fb336df01`: lucide-react 1.31.0 -> 1.43.0), so its anchors were re-READ: -// `toPascalCase` `:153-158` and the rename map `:143-145` did not move, and -// `describeIconLookup` `302-305` -> `327-330` and `resolveIcon` `322-328` -> -// `347-353` MOVED byte-identical. What changed in the file sits past the -// name lookup — the lazily loaded module's path data is now read from -// `__iconData.node` before `__iconNode`, the `IconNode` type is narrowed with -// `NonNullable`, and the `lucideClassNames` docblock was rewritten over -// unchanged code — so the SPELLINGS an author may write resolve exactly as -// before. The VOCABULARY moved with the bump, in the generated table -// `recordIconName` decodes (`lib/lucide-record-icon-names.ts`, 1781 -> 1818 -// entries): 38 names in and one out, `Trash2`, so `trash-2` now resolves to -// `null` and draws nothing, like any unknown name. At `f8a9d0fb0` -// (2026-09-24) `resolve-icon.ts` and `lazy-icon.tsx` were byte-identical to `62597c588` -// and `87af769e9`, and `button.tsx` changed only inside its registration's -// input list (objectui#9910 added a `children` slot input, no `icon` one), so -// the button anchors did not move; all three files were byte-identical across -// the hop onto `62597c588`. The hop before that, -// onto `87af769e9` (re-derived 2026-09-20), moved both files in this chain — -// `resolve-icon.ts` +203/-7 and `button.tsx` +6/-11 against `53ded82bf` — so -// no anchor below was carried there and every one was re-READ (commit d1ba685ec). ⚠️ `resolveIcon` itself was rewritten: its tail no -// longer indexes `lucide-react`'s `icons` record, it asks `recordIconName` -// for the kebab-case name and hands the pair to `lazyIconComponent`, so the -// glyph arrives lazily. What an author may write did not change with it. The -// earlier hop -// onto `00d3f09c5` was the one that changed this record's SUBSTANCE and not -// merely its line numbers: `resolve-icon.ts` was restructured (110 -// insertions), so `resolveIcon` no longer PascalCases and maps inline — it -// delegates to the `describeIconLookup` seam (now `:327-330`), and the tokeniser -// splits on hyphen, underscore AND whitespace (`/[-_\s]+/`), where this record -// used to say "splits on `-` only". That sentence was true when written and -// was false by then, which is exactly why a citation refresh re-READS instead -// of moving numbers (commit d1ba685ec). -// The one move that changed the button READ POINT and not merely its line -// numbers was the one onto `9602dc820`: objectui#5993 deleted `button.tsx`'s -// file-local `toPascalCase` + `iconNameMap` + `icons` index and routed the -// button through the SHARED `resolveIcon` that every `action:*` site already -// used, so the resolution anchor hops into `renderers/action/resolve-icon.ts`. -// What an author sees did not move with it: an unknown name still resolves to -// `null` and draws nothing, which is still the `LazyIcon` contrast the third -// test below pins. The moves before that were line-number drift only — #10137 -// moved the pin while the #9881 / commit 60e0f900a records still cited `82a94170c`, commit d1ba685ec re-measured -// those four onto `9a3daf8d3`, and `button.tsx` was byte-identical at -// `9a3daf8d3` and `190fbd01d`. +// The button record's read points are recorded in `liveness/page.json`, row +// `regions`. The one move that changed the button READ POINT and not merely its +// line numbers was objectui#5993: it deleted the button's file-local +// `toPascalCase` + `iconNameMap` + `icons` index and routed the button through +// the SHARED `resolveIcon` that every `action:*` site already used. What an +// author sees did not move with it: an unknown name still resolves to `null` +// and draws nothing, which is still the `LazyIcon` contrast the third test +// below pins. The resolver's tokeniser splits on hyphen, underscore AND +// whitespace (`/[-_\s]+/`); it asks `recordIconName` for the kebab-case name +// and hands the pair to `lazyIconComponent`, so the glyph arrives lazily. describe('ElementButtonPropsSchema icon liveness', () => { const button = ComponentPropsMap['element:button']; it('accepts an icon on a button — the value objectui resolves through the lucide `icons` map', () => { - // objectui `packages/components/src/renderers/form/button.tsx:43` hands the - // name to the shared `resolveIcon` - // (`packages/components/src/renderers/action/resolve-icon.ts:347-353`), - // which delegates to `describeIconLookup` (`:327-330`): that PascalCases - // through `toPascalCase` (`:153-158`, splitting on hyphen, underscore or - // whitespace) and applies the one-entry rename map (`:143-145`) before the - // lookup, which at this pin runs through `recordIconName` + - // `lazyIconComponent` rather than indexing `icons` from `lucide-react` - // directly; `button.tsx:72` / `:74` - // draw it either side of the label per `iconPosition`. + // The button renderer hands the name to the shared `resolveIcon`, which + // delegates to `describeIconLookup`: that PascalCases through + // `toPascalCase` (splitting on hyphen, underscore or whitespace) and + // applies the one-entry rename map before the lookup, which runs through + // `recordIconName` + `lazyIconComponent` rather than indexing `icons` from + // `lucide-react` directly; the renderer draws it either side of the label + // per `iconPosition`. const result = button.safeParse({ label: 'Save', icon: 'arrow-right' }); expect(result.success).toBe(true); const parsed = (result.success ? result.data : undefined) as { icon?: string } | undefined; @@ -4156,14 +3556,10 @@ describe('ObjectMetricPropsSchema icon liveness', () => { const metric = ComponentPropsMap['object-metric']; it('accepts an icon on the metric tile — the value objectui resolves via getLazyIcon', () => { - // objectui `plugin-dashboard/src/index.tsx:299` publishes the input - // (this read `:161` until the `a472b0716` re-measure: wrong since written, - // not shifted — that line is a sentence in the registry shell's docblock, - // not the `object-metric` registration's `icon` input, and the file is - // byte-identical at both pins, so only a re-READ could find it); - // `ObjectMetricWidget.tsx:142` destructures it and forwards it at `:474` to - // `MetricWidget`, which resolves it at `MetricWidget.tsx:312-321` and draws - // it at `:373-382` in the `colorVariant`-tinted square. + // The `object-metric` registration publishes the input; `ObjectMetricWidget` + // destructures it and forwards it to `MetricWidget`, which resolves it and + // draws it in the `colorVariant`-tinted square (read points recorded in + // `liveness/page.json`, row `regions`). const result = metric.safeParse({ objectName: 'task', icon: 'circle-alert' }); expect(result.success).toBe(true); const parsed = (result.success ? result.data : undefined) as { icon?: string } | undefined; @@ -4194,148 +3590,8 @@ describe('ObjectMetricPropsSchema icon liveness', () => { // 「8348 以协议为准」 (batch #83, 2026-09-08) and batch #136 item 3 (Q1-C). // // The acceptance the card names, pinned: each row's KEY SET is the one the -// renderer's read points support at objectui `b9814b334` (re-read there 2026-10-14: -// every objectui file this record cites is byte-identical across the hop from -// `1fb1ced36` (`git diff --quiet`), so every anchor held unmoved. At -// `1fb1ced36`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `7578323a5` (`git diff --quiet`), so every anchor held unmoved. At -// `7578323a5`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `9646e2ed2` (`git diff --quiet`), so every anchor held unmoved. At -// `9646e2ed2`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `088e0f705` (`git diff --quiet`), so every anchor held unmoved. At -// `088e0f705`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `e5311165a` (`git diff --quiet`), so every anchor held unmoved. At -// `e5311165a`, re-read there 2026-10-12: -// every objectui file this record cites is byte-identical across the hop from -// `499799502` (`git diff --quiet`), so every anchor held unmoved. At -// `499799502`, re-read there 2026-10-11: -// `ObjectTree.tsx`, `ObjectMap.tsx` and `record-source.ts` are byte-identical -// across the hop from `0df67f237` (`git diff --quiet`), so every anchor in them -// held unmoved; `ObjectGantt.tsx` +27/-4 changed for objectui#12082 (objectui -// `752504520`: the chart's in-chart writes read the affordance-to-grant map, -// so a bar's drag, its links and the row Delete are handlers withheld from a -// caller without the grant), no hunk on a `schema.*` read, and the set of -// `schema.*` keys it reads is the same at both pins (49 names). At -// `0df67f237`, re-read there 2026-10-11: -// every objectui file this record cites is byte-identical across the hop from -// `20c6d351a` (`git diff --quiet`), so every anchor held unmoved. At -// `20c6d351a`, re-read there 2026-10-10: -// every objectui file this record cites is byte-identical across the hop from -// `47b1f0bb7` (`git diff --quiet`), so every anchor held unmoved. At -// `47b1f0bb7`, re-read there 2026-10-09: -// every objectui file this record cites is byte-identical across the hop from -// `f0268ad78` (`git diff --quiet`), so every anchor held unmoved. At -// `f0268ad78`, re-read there 2026-10-09: -// `ObjectGantt.tsx`, `ObjectTree.tsx` and `record-source.ts` are byte-identical -// across the hop off `a58626c88` (`git diff --quiet`), so every anchor in them -// held unmoved, the tree's three reads at `:714`, `:945` and `:1135`; -// `ObjectMap.tsx` +85/-4 changed for objectui#11819 (without WebGL2 the map -// lists its records instead of crashing), none of it on a read, and the set of -// `schema.*` keys each renderer reads is the same at both pins. At `a58626c88`, -// re-read there 2026-10-06: -// `ObjectMap.tsx`, `ObjectGantt.tsx` and `record-source.ts` are byte-identical -// across the hop off `0abd4f9f8` (`git diff --quiet`), so every anchor in them -// held unmoved; `ObjectTree.tsx` +81/-3 changed for objectui#11686 (a tree cell -// draws the same field face a list cell does), none of it on a cited line, so -// the tree's rung-1 call `resolveRecordSourceConfig(schema, 'view-data')` MOVED -// `:637` -> `:714`, its shorthand read `(rest as any).data` `:868` -> `:945` -// and its `schema.navigation` read `:1057` -> `:1135`, each byte-identical, and -// the set of `schema.*` keys each renderer reads is the same at both pins. -// At `0abd4f9f8`, re-read there 2026-10-05: -// every objectui file this record cites is byte-identical across the hop from -// `9dfaca654` (`git diff --quiet`), so every anchor held unmoved. -// At `9dfaca654`; re-measured there 2026-10-05 — -// `ObjectMap.tsx` and `record-source.ts` are byte-identical across the hop off -// `2e818d0b5` (`git diff --quiet`), so every anchor in them held unmoved; -// `ObjectTree.tsx` +8/-5 and `ObjectGantt.tsx` +4/-3 changed in docblocks only -// (objectui#8347 putting `BaseSchema`'s index signature in the past tense), so -// the tree's rung-1 call `resolveRecordSourceConfig(schema, 'view-data')` -// MOVED `:634` -> `:637`, its shorthand read `(rest as any).data` `:865` -> -// `:868` and its `schema.navigation` read `:1054` -> `:1057`, each -// byte-identical, and the set of `schema.*` keys each renderer reads is the -// same at both pins. At `2e818d0b5`, re-measured there 2026-10-04 — -// `ObjectMap.tsx`, `ObjectTree.tsx`, `ObjectGantt.tsx` and `record-source.ts` -// are byte-identical across the hop off `ab1879721` (`git diff --quiet`), so -// every anchor in them held unmoved. At `ab1879721`, re-measured there -// 2026-10-03 — -// `ObjectMap.tsx`, `ObjectTree.tsx` and `record-source.ts` are byte-identical -// across the hop off `89cad75d5` (`git diff --quiet`), so every anchor in them -// held unmoved; `ObjectGantt.tsx` +8/-0 gained one import line and, in the -// tooltip's `percent` row, a `percentCellScale(def)` argument scaling at the -// field definition's declared storage (objectui#11475) — a field-definition -// read, not a node key — so the set of `schema.*` keys each renderer reads is -// the same at both pins. At `89cad75d5`, 2026-10-02 — all four -// cited files changed on the hop off `31971ff1e`, so each was re-READ, and -// the set of `schema.*` keys each renderer reads is the same at both pins. -// `ObjectMap.tsx` +11/-4: the shadowed-flat-key warning reads `schema[key]` -// typed instead of through a cast (objectui#11355), and the declared `map` -// block's return path now takes the same `style` as every other path, -// `mapStyle` first, then `map.style` (objectui#11168 slice 3) — the two keys -// it reads are unchanged, and the `style` line MOVED `400` -> `407` -// byte-identical. `ObjectGantt.tsx` +47/-10: the same typed shadowed-key read -// (objectui#11355) and the tooltip's number and percent rows taking their -// width from the field definition's `scale` through `resolveFieldScale` -// (objectui#11254) — a field-definition read, not a node key. -// `ObjectTree.tsx` +8/-17: the `navigation` read lost its cast -// (`(schema as any).navigation` `:1063` -> `schema.navigation` `:1054`, -// objectui#11168 slice 3 declaring it), the same key; the rung-1 call -// `resolveRecordSourceConfig(schema, 'view-data')` `:634` and the shorthand -// read `(rest as any).data` `:865` did not move. ⚠️ `record-source.ts` -// +4/-5 no longer lists FOUR tree tags as `view-data`: objectui `990a2d616` -// (objectui#10859 batch 8) retired the bare `tree` / `view:tree` node types, -// so the arm table keeps `object-tree` and `plugin-tree:object-tree` only, -// still `view-data`. The in-test notes below stay dated to the pins they -// name. At `31971ff1e`, 2026-10-01 — `ObjectMap.tsx`, `ObjectGantt.tsx`, -// `ObjectTree.tsx` and `record-source.ts` were byte-identical across the hop -// off `e420df310` (`git diff --quiet`), so every anchor held unmoved and the -// set of `schema.*` keys each renderer reads was the one recorded at -// `e420df310`. At `e420df310`, 2026-09-30 — all -// three renderers changed on the hop off `db11afd49`, `ObjectMap.tsx` in one docblock -// only, `ObjectGantt.tsx` +85/-26 (objectui#11141's inclusive date-only end, -// objectui#8348, objectui#11070) and `ObjectTree.tsx` +29/-26, and the set of -// `schema.*` keys each reads is the same at both pins. ⚠️ One read changed -// CONTENT, in the direction of this row: objectui#8348 (objectui `846cec0ef`) -// moved the tree's rung-1 call from `resolveRecordSourceConfig(schema, -// 'undeclared')` to `'view-data'` (`ObjectTree.tsx:632` -> `:634`), the arm its -// siblings pass, and `record-source.ts` now lists the four tree tags as -// `view-data`; and the shorthand read `(rest as any).data ?? schema.data` -// became `(rest as any).data` (`:862` -> `:865`), so the renderer no longer -// honours an authored bare array at all. The in-test notes below that say the -// tree's arm is `'undeclared'` and WIDER than this row are the `87af769e9` -// reading they are dated to; at this pin the renderer and the row agree. At -// `db11afd49`, 2026-09-29 — `ObjectMap.tsx` -// is byte-identical to `dd3f7e1be`, `ObjectTree.tsx` changed in one comment line -// and `ObjectGantt.tsx` in its date-only DST handling and citations, and the set -// of `schema.*` keys each reads is the same at both pins. At `dd3f7e1be`, -// 2026-09-28: all three -// renderers changed on this hop, `ObjectMap.tsx` +193/-72, `ObjectGantt.tsx` -// +285/-56 and `ObjectTree.tsx` +136/-30, so each was re-READ: no declared key -// set moved. The set of `schema.*` keys the code reads is the same at both -// pins in all three files; what changed is how reads are MADE — each now -// resolves its own `filter` through `useResolvedFilter` before the fetch -// (objectui#10666) and re-reads on the data-invalidation bus — while the -// tree's fetch still carries no `$orderby`, so `sort` stays off its row, and -// `record-source.ts` lost only the retired bare `map` / `view:map` arm entries -// (objectui#10393). At `f8a9d0fb0` (2026-09-24) the map's `ObjectMap.tsx` was -// byte-identical to `62597c588`, and the gantt and tree renderers changed and -// were re-READ with no declared key set moving (the gantt's new -// host-generated `search` / `searchableFields` reads, objectui#10250, stay -// undeclared and are recorded in its header; the tree's `filter.tree` stash -// read was deleted, objectui#9549). At `62597c588` -// the gantt and tree renderers were byte-identical to `87af769e9`, and the -// map's `ObjectMap.tsx` had changed only in a docblock and a dev-warning -// string (objectui `2252653d0`), so its anchors MOVED with their cited text -// byte-identical and no key set moved. They were re-READ at `87af769e9` -// 2026-09-22 — all three renderers moved hard on the hop from `53ded82bf`, -// so NO anchor in this block was carried and every one was re-derived. Four -// changed CONTENT rather than position and say so where they are cited: the -// map's array-shorthand head, the tree's record-source ARM, the cast on the -// tree's `schema.data` read, and the tree's `titleField` rung), and -// `Object.keys(ComponentPropsMap)` lists the three. The key sets are asserted +// renderer's read points support (recorded in `liveness/page.json`, row +// `regions`), and `Object.keys(ComponentPropsMap)` lists the three. // WHOLE rather than by spot-check — a row derived from read points is a claim // about a complete set, and only an equality can hold a later addition to // having been measured too. @@ -4351,29 +3607,24 @@ describe('object-map / object-gantt / object-tree — key sets derived from the }; it('object-map declares exactly its measured read set', () => { - // ObjectMap.tsx @ 87af769e9: data (:183 — `getDataConfig` is now that one - // `resolveRecordSourceConfig(schema, 'view-data')` statement; the - // array-shorthand head that used to open it is GONE, and the docblock at - // :168-176 records that an authored array now reaches this renderer only - // through the React props channel), staticData / objectName (the shared - // ladder's rungs 2 and 3, record-source.ts :296 and :303), filter (:814 - // and :895 — the inline and the object fetch), sort (:815 and :896), map - // (:382), mapStyle (:377), navigation (:1042), enableClustering (:1058). + // One key per read point: data (the shared ladder's rung 1 — the + // array-shorthand head that used to open `getDataConfig` is GONE, so an + // authored array reaches this renderer only through the React props + // channel), staticData / objectName (rungs 2 and 3), filter and sort (the + // inline and the object fetch), map, mapStyle, navigation, + // enableClustering (read points recorded in `liveness/page.json`, row + // `regions`). expect(keysOf('object-map')).toEqual([ 'data', 'enableClustering', 'filter', 'map', 'mapStyle', 'navigation', 'objectName', 'sort', 'staticData', ]); }); it('object-gantt declares exactly its measured read set', () => { - // ObjectGantt.tsx @ 87af769e9: the ladder (:613), filter (:844), sort - // (:845), gantt (:501-503), navigation (:1615), label (:2181 — the - // `resolveI18nLabel` call in the export-file-name chain; the retired - // reading :1849 was the COMMENT above that chain even at its own pin, and - // at this one is a BLANK line two above an unrelated callback's comment), - // skipWeekends (:1333), holidays (:1334), persistLayout (:1485), - // viewName (:1487), markers (:2136), - // criticalPath (:2139), showBaselines (:2142), readOnly (:1985 and - // :2143), mobileReadOnly (:2144). + // One key per read point: the ladder, filter, sort, gantt, navigation, + // label (the i18n-label call in the export-file-name chain), skipWeekends, + // holidays, persistLayout, viewName, markers, criticalPath, showBaselines, + // readOnly, mobileReadOnly (read points recorded in `liveness/page.json`, + // row `regions`). expect(keysOf('object-gantt')).toEqual([ 'criticalPath', 'data', 'filter', 'gantt', 'holidays', 'label', 'markers', 'mobileReadOnly', 'navigation', 'objectName', 'persistLayout', 'readOnly', 'showBaselines', 'skipWeekends', @@ -4383,21 +3634,13 @@ describe('object-map / object-gantt / object-tree — key sets derived from the it('object-tree declares exactly its measured read set — and `data` IS in it', () => { // The card's open question, answered by measurement rather than by family - // symmetry: ObjectTree.tsx @ 87af769e9 reaches `schema.data` through - // `resolveRecordSourceConfig(schema, 'undeclared')` at :582 — rung 1 of - // the shared ladder, which returns the authored value VERBATIM. That - // ONE site is the whole support for the object arm, and it is sufficient. - // ⚠️ The ARM is `'undeclared'` here, not the `'view-data'` its siblings - // pass, so rung 1 honours any truthy value at the renderer — WIDER than - // this row, which is the harmless direction: the door below refuses the - // bare array the renderer would have taken. - // ⛔ :775 is NOT a second one: `(rest as any).data ?? schema.data` (the - // cast on `schema` went away with objectui#8655) is gated by - // `Array.isArray(passed)` on the next line, so it honours only - // the bare-ARRAY shorthand this row REFUSES (pinned below). objectui#9234 - // left the rung-1 read marked `undeclared` because neither published face - // carried the key; the protocol row follows the READ POINTS, which is what - // 「以协议为准」 resolving for this block means. + // symmetry: the tree renderer reaches `schema.data` through the shared + // ladder's rung 1, which returns the authored value VERBATIM. That ONE + // site is the whole support for the object arm, and it is sufficient; the + // host-prop `data` beside it is not a second one, it honours only the + // bare-ARRAY shorthand this row REFUSES (pinned below). The protocol row + // follows the READ POINTS, which is what 「以协议为准」 resolving for this + // block means (read points recorded in `liveness/page.json`, row `regions`). expect(keysOf('object-tree')).toEqual([ 'data', 'filter', 'navigation', 'objectName', 'staticData', 'tree', ]); @@ -4469,19 +3712,17 @@ describe('object-map / object-gantt / object-tree — key sets derived from the // renderer's flat branch still reads beside `dependenciesField`. expect(setFor('object-gantt', 'OBJECT_GANTT_FLAT_CONFIG_KEYS')) .toEqual([...Object.keys(GanttConfigSchema.shape), 'dependencyField'].sort()); - // tree: `TreeConfigSchema`'s shape PLUS `titleField`. ⚠️ Re-READ at - // 87af769e9 and the read point is GONE, not moved: objectui#8841 deleted - // the `?? schema.titleField` rung from `getTreeConfig` (now :235-248), - // and the docblock above it (:197-218) records why — it read the - // FLATTENED NODE, never the block, and the key is declared on neither - // face. The key stays in THIS set, and the prescription that names it + // tree: `TreeConfigSchema`'s shape PLUS `titleField`. ⚠️ The read point is + // GONE, not moved: objectui#8841 deleted the `?? schema.titleField` rung + // from `getTreeConfig`, and the docblock above it records why — it read + // the FLATTENED NODE, never the block, and the key is declared on neither + // face (read points recorded in `liveness/page.json`, row `regions`). The key stays in THIS set, and the prescription that names it // stays TRUE, on the OTHER half of the sentence: `ListView`'s flatten - // still resolves `treeCfg.titleField` into `labelField` before emitting - // (`ListView.tsx:3270` at this pin), so an authored `titleField` is still - // only ever the block's `labelField`. What died is the renderer's own - // fallback, not the flatten's. At `20c6d351a` the flatten's rung is gone - // too (objectui#6152 round 14, objectui `3fd862510`: `ListView.tsx:3985` - // reads `treeCfg.labelField || 'name'`), so no half reads `titleField`; + // resolved `treeCfg.titleField` into `labelField` before emitting, so an + // authored `titleField` was only ever the block's `labelField`. What died + // first is the renderer's own fallback, not the flatten's. The flatten's + // rung is gone too (objectui#6152 round 14, objectui `3fd862510`: it reads + // `treeCfg.labelField || 'name'`), so no half reads `titleField`; // it stays in the set for the prescription it carries, which names // `tree.labelField` (see `OBJECT_TREE_FLAT_CONFIG_GUIDANCE`). expect(setFor('object-tree', 'OBJECT_TREE_FLAT_CONFIG_KEYS')) @@ -4489,7 +3730,7 @@ describe('object-map / object-gantt / object-tree — key sets derived from the }); it('the config blocks are the spec own schemas where the renderer names one, `z.unknown()` where it does not', () => { - // gantt: `ObjectGantt.tsx:503` validates the authored block against + // gantt: the renderer validates the authored block against // `GanttConfigSchema` imported from `@objectstack/spec/ui`, so the read // point names the schema and the door takes it — a misspelling inside the // block is refused here exactly as the renderer's own safeParse warns. @@ -4504,7 +3745,7 @@ describe('object-map / object-gantt / object-tree — key sets derived from the expect(door('object-tree').safeParse({ tree: { labelFeild: 'name' } }).success).toBe(false); // map: `ListMapConfigSchema` — the ratchet the previous posture deferred, // taken now that `style` is declared on that block (the key `getMapConfig` - // reads at `ObjectMap.tsx:377`, `schema.mapStyle || schema.map?.style`). The + // reads, `schema.mapStyle || schema.map?.style`). The // two assertions that used to record the divergence are INVERTED here: the // list-view face accepts the style URL, and the door accepts it through the // spec's own schema rather than through an open value. @@ -4547,7 +3788,8 @@ describe('object-map / object-gantt / object-tree — key sets derived from the // #19228 — the react tier's own precedence sentence was narrower than the // guard it names. These pins hold the structural facts the repair rests on, -// measured first-hand at the objectui pin `87af769e9` on 2026-09-21T06:30-06:40Z. +// measured first-hand (read points recorded in `liveness/page.json`, row +// `regions`). describe('row caps on the object-bound blocks — a bound view fills `limit` only when the authored one is not a usable cap', () => { const timeline = ComponentPropsMap['object-timeline']; const kanban = ComponentPropsMap['object-kanban']; @@ -4555,7 +3797,7 @@ describe('row caps on the object-bound blocks — a bound view fills `limit` onl it('leaves the ELEMENT-face `limit` undefaulted — the fact that keeps the gate arm alive', () => { // `ElementDataSourceGate` lowers a bound view's cap into this key only // when it does not already carry a USABLE one - // (`ElementDataSourceGate.tsx:316-331`, `!fromView || !isUsableRowLimit`). + // (`!fromView || !isUsableRowLimit`). // An applied default here would make every parsed node carry a usable cap // and kill that arm outright — the failure #19228 feared, on the schema it // would actually happen to. ⛔ Do not "fix" a red here by deleting the pin. @@ -4587,8 +3829,9 @@ describe('row caps on the object-bound blocks — a bound view fills `limit` onl // · SPEC side — reds. A `.nullable()`, a `0` sentinel, dropping `.int()` // or adding a `.default()` each fail a specific expect below. // · GATE side — ⛔ CANNOT red. `usableToTheGate` is a TRANSCRIPTION of - // `isUsableRowLimit` as it read at objectui pin `87af769e9`, not an - // import — nothing here resolves into objectui. A rewrite of that + // `isUsableRowLimit` as it reads at its recorded read point + // (`liveness/page.json`, row `regions`), not an import — nothing here + // resolves into objectui. A rewrite of that // predicate at objectui HEAD leaves this test green. It is re-read on // a PIN BUMP, by hand, and that is the only thing that refreshes it. const usableToTheGate = (v: unknown): boolean => diff --git a/packages/spec/src/ui/dashboard.test.ts b/packages/spec/src/ui/dashboard.test.ts index 0f5b4e42343..86f3ef32b5a 100644 --- a/packages/spec/src/ui/dashboard.test.ts +++ b/packages/spec/src/ui/dashboard.test.ts @@ -874,9 +874,9 @@ describe('DashboardHeaderAction fields carry an item-level `title`', () => { * - the sentence being trimmed back to the plural framing. * * The renderer half is deliberately NOT pinned here: which chart types consult - * the forwarded order is objectui's fact, measured against this repo's - * `.objectui-sha` pin and reported on the issue, not something `packages/spec` - * can assert. + * the forwarded order is objectui's fact, measured against the console this + * repo ships and reported on the issue, not something `packages/spec` can + * assert. */ /** A minimal dataset-bound widget — everything `stageOrder` is not. */ const WIDGET_BASE = { diff --git a/packages/spec/src/ui/dashboard.zod.ts b/packages/spec/src/ui/dashboard.zod.ts index aa640707681..17ac776359a 100644 --- a/packages/spec/src/ui/dashboard.zod.ts +++ b/packages/spec/src/ui/dashboard.zod.ts @@ -266,8 +266,9 @@ const WIDGET_HISTORY = * renderer setting. `dataset` is required, so every widget renders through * objectui's dataset-bound path (`DatasetWidget`), and that path is meant to * read exactly the five keys declared below. Any other key parses and reaches - * no renderer. The #20960 census measured that at objectui `main` `5262f7dd` - * over every key this repo's examples wrote here (`format`, `currency`, + * no renderer. The #20960 census measured that on objectui `main` (read points + * recorded in `liveness/dashboard.json`, row `widgets.options`) over every key + * this repo's examples wrote here (`format`, `currency`, * `color`, `suffix`, `showLegend`, `horizontal`, `showDataLabels`): none is * read on that path — only the legacy inline arm, which a widget carrying a * `dataset` never reaches, spreads the bag into component props — so each was @@ -690,7 +691,8 @@ const SINGLE_MEASURE_WIDGET_TYPES = ['metric', 'kpi', 'gauge', 'solid-gauge', 'b * the dataset, not about the widget, and unreachable from this schema — a * tile naming one measure nobody declared parses exactly as before. * 5. **objectui's CLIENT-SIDE authoring door**, a `.shape` mirror that runs no - * object-level check of this schema's: at the `.objectui-sha` pin, + * object-level check of this schema's (read point recorded in + * `liveness/dashboard.json`, row `widgets`): * `@object-ui/types` builds its own `DashboardWidgetSchema` from * `specFieldsExcept(SpecDashboardWidgetSchema.shape, …).extend({…}).strict()` * and re-attaches none of this file's exported checks. Until it imports and diff --git a/packages/spec/src/ui/dataset.zod.ts b/packages/spec/src/ui/dataset.zod.ts index e555259e8d7..f386424e34e 100644 --- a/packages/spec/src/ui/dataset.zod.ts +++ b/packages/spec/src/ui/dataset.zod.ts @@ -277,132 +277,16 @@ export const DatasetMeasureSchema = lazySchema(() => strictObject({ * `relative` (`3 days ago` inside a ±7-day window, the absolute form outside * it) — the same two words `DateCellRenderer` honours from `field.format`. * - * ⚠️ The datetime half of that sentence is NEW at the pin below and is the - * one thing this record's previous revision got wrong the moment the pin - * moved: until objectui#8352 a DATETIME value ignored `format` altogether, + * ⚠️ The datetime half of that sentence is the newer one: until objectui#8352 a DATETIME value ignored `format` altogether, * and this docblock and the `describe` beneath it both said so. * - * Measured at objectui `b9814b334` (re-read there 2026-10-14: - * every objectui file this record cites is byte-identical across the hop from - * `1fb1ced36` (`git diff --quiet`), so every anchor held unmoved. At - * `1fb1ced36`, re-read there 2026-10-12: - * every objectui file this record cites is byte-identical across the hop from - * `7578323a5` (`git diff --quiet`), so every anchor held unmoved. At - * `7578323a5`, re-read there 2026-10-12: - * every objectui file this record cites is byte-identical across the hop from - * `9646e2ed2` (`git diff --quiet`), so every anchor held unmoved. At - * `9646e2ed2`, re-read there 2026-10-12: - * every objectui file this record cites is byte-identical across the hop from - * `088e0f705` (`git diff --quiet`), so every anchor held unmoved. At - * `088e0f705`, re-read there 2026-10-12: - * every objectui file this record cites is byte-identical across the hop from - * `e5311165a` (`git diff --quiet`), so every anchor held unmoved. At - * `e5311165a`, re-read there 2026-10-12: - * every objectui file this record cites is byte-identical across the hop from - * `499799502` (`git diff --quiet`), so every anchor held unmoved. At - * `499799502`, re-read there 2026-10-11: - * every objectui file this record cites is byte-identical across the hop from - * `0df67f237` (`git diff --quiet`), so every anchor held unmoved. At - * `0df67f237`, re-read there 2026-10-11: - * every objectui file this record cites is byte-identical across the hop from - * `20c6d351a` (`git diff --quiet`), so every anchor held unmoved. At - * `20c6d351a`, re-read there 2026-10-10: - * every objectui file this record cites is byte-identical across the hop from - * `47b1f0bb7` (`git diff --quiet`), so every anchor held unmoved. At - * `47b1f0bb7`, re-read there 2026-10-09: - * every objectui file this record cites is byte-identical across the hop from - * `f0268ad78` (`git diff --quiet`), so every anchor held unmoved. At - * `f0268ad78`, re-read there 2026-10-09: - * `dataset-format.ts` is byte-identical to `a58626c88` (`git diff --quiet`), so - * `formatMeasureDate` `:240-274`, its datetime arm `:270`-`:272` and its call - * `:380` held unmoved; `date-display.ts` changed (+152/-16, objectui#11693, - * objectui `d50f7241b`: an instant renders in the time zone the server answers - * for the workspace), so `formatDate` `445-480` -> `568-605` was re-READ: it - * now names an instant's day in that display zone, while its style handling — - * `short`, `relative`, and the default face for anything else, a date pattern - * included — reads unchanged, and the ±7-day fallback `399` -> `522` MOVED - * byte-identical, its day count now taken in the display zone above it. At - * `a58626c88`, re-read there 2026-10-06: - * every objectui file this record cites is byte-identical across the hop from - * `0abd4f9f8` (`git diff --quiet`), so every anchor held unmoved. - * At `0abd4f9f8`, re-read there 2026-10-05: - * every objectui file this record cites is byte-identical across the hop from - * `9dfaca654` (`git diff --quiet`), so every anchor held unmoved. - * At `9dfaca654`; re-derived at that pin 2026-10-05 — `date-display.ts` and - * `dataset-format.ts` are byte-identical to `2e818d0b5` (`git diff --quiet`), - * so every anchor held unmoved. At `2e818d0b5`, re-derived there - * 2026-10-04 — `date-display.ts` and - * `dataset-format.ts` are byte-identical to `ab1879721` (`git diff --quiet`), - * so every anchor held unmoved. At `ab1879721`, re-derived there - * 2026-10-03 — `date-display.ts` is - * byte-identical to `89cad75d5`, so `formatDate` `445-480` and the ±7-day - * fallback `399` held unmoved and were re-READ in place; `dataset-format.ts` - * changed (+53/-36, objectui#11475: `scalePercent` scales at the storage - * its caller states, through the spec's `percentScaleOf`, instead of - * guessing from the value's magnitude), above `formatMeasureDate` in the - * percent-scaling helpers and below it in `formatMeasure`'s docblock (line - * for line) and its percent arm, so - * `formatMeasureDate` `:229-263` -> `:240-274`, its datetime arm `:259`-`:261` - * -> `:270`-`:272` and its call `:369` -> `:380` MOVED byte-identical, - * re-READ with the same reading below. At `89cad75d5` (2026-10-02) - * `dataset-format.ts` and `date-display.ts` were byte-identical to - * `31971ff1e`, so every anchor below held unmoved and was re-READ in place. - * At `31971ff1e` (2026-10-01) both - * files were byte-identical to `e420df310`, so every anchor held unmoved and - * was re-READ in place. At `e420df310` (2026-09-30) - * `dataset-format.ts` was byte-identical to `db11afd49`, so `formatMeasureDate` `:229-263`, its call - * at `:369` and its datetime arm `:259`-`:261` did not move and were re-READ - * in place, and `date-display.ts` changed (+68/-1, objectui `858eafb4f`, - * objectui#11141: a header comment naming the new `toDisplayEndDate` / - * `toInclusiveEndDay` exports, and those two functions added after - * `toDisplayDate`, above every anchor here), so `formatDate` `378-413` -> - * `445-480` and the ±7-day fallback `332` -> `399` MOVED byte-identical, - * re-READ with the same reading below. At `db11afd49` (2026-09-29) - * `date-display.ts` was - * byte-identical to `dd3f7e1be` and `dataset-format.ts` changed only in three - * comment lines (`:529`, `:589`, `:660`, objectui `63ab76112`), all below every - * anchor here, so `formatDate` `378-413`, the ±7-day fallback `332`, - * `formatMeasureDate` `:229-263`, its call at `:369` and its datetime arm - * `:259`-`:261` were re-READ and did not move. At `dd3f7e1be` (2026-09-28) - * both files changed again on - * that hop (objectui `544aca24f`, objectui#10301, the date-time half of - * objectui#10026: `toDisplayDate` now refuses a date-TIME written on a day - * that does not exist as well, and `dataset-format.ts` rewrote the - * `ISO_DATETIME_RE` docblock that said such a value still rolls over, four - * comment lines for four, so nothing below it moved), while the style - * handling this record cites re-reads unchanged: `formatDate` `355-390` -> - * `378-413` and the ±7-day fallback `309` -> `332` MOVED byte-identical, and - * `formatMeasureDate` `:229-263`, its call at `:369` and its datetime arm at - * `:259`-`:261` did not move. At `f8a9d0fb0` (2026-09-24) both files had - * moved (objectui `ad694ac3d`, objectui#10026: the shared date path refuses a - * date-only calendar day that does not exist, with the dash it renders for - * any unparsable value, and `dataset-format.ts` rewrote - * `formatMeasureDate`'s comment on that case), while the style handling - * re-read unchanged: `formatDate` `271-306` -> `355-390` and the ±7-day - * fallback `225` -> `309` MOVED byte-identical, and `formatMeasureDate` came - * out one comment line shorter. At `62597c588` (2026-09-23) - * `dataset-format.ts` was byte-identical to `87af769e9`, and `date-display.ts` - * had moved (objectui `516583b54`, +77/-4): `formatDate` parses through - * `toDisplayDate`, so a date-only value renders the calendar day it names in - * every timezone. At `87af769e9` (2026-09-20) every anchor below MOVED and - * one of them changed SUBSTANCE, so nothing here was carried there either: - * `formatMeasureDate`'s datetime arm no longer calls - * `formatDateTime(v, { locale })` unconditionally, it SELECTS a formatter, - * because `formatDateTime(value, options?)` has no style parameter to thread - * into and widening that published signature was refused) in - * objectui - * `packages/core/src/utils/dataset-format.ts`: `formatMeasure` routes a - * non-numeric value through `formatMeasureDate` (`:240-274`, was `:229-263`, - * `:229-264` and before that `:185-198`) at `:380`, - * whose date-only arm threads `format` into the STYLE parameter of - * `formatDate` (`utils/date-display.ts:568-605`, was `:445-480`, `:378-413`, `:355-390`, `:271-306`, - * `:198-233` and before that `:131-164`, whose `relative` branch falls back to the absolute form - * beyond ±7 days at `:522`, was `:399`, `:332`, `:309`, `:225`, `:152` and `:117` — the fallback strips the style through - * `absoluteFallbackOptions`), while its datetime arm answers `relative` with - * `formatRelativeDate` (`:270`), `short` with - * `formatDateTime(v, { locale, style: 'compact' })` (`:271`) and everything - * else — a date PATTERN included — with the bare - * `formatDateTime(v, { locale })` (`:272`). + * Measured on the renderer's shared measure formatter: its date-only arm + * threads `format` into the date STYLE parameter (whose `relative` branch + * falls back to the absolute form beyond ±7 days), while its datetime arm + * answers `relative`, `short` (the compact style) and everything else — a + * date PATTERN included — with the bare locale default. The read points are + * recorded by symbol in `packages/spec/liveness/dataset.json`, row + * `measures.format`. * Teaching * the shared path a pattern grammar would change every list cell that reads * it, so that gap is still DOCUMENTED here rather than closed (objectui#7178 diff --git a/packages/spec/src/ui/expression-bindable-text-keys.zod.ts b/packages/spec/src/ui/expression-bindable-text-keys.zod.ts index f66ade845fa..b69e07b517f 100644 --- a/packages/spec/src/ui/expression-bindable-text-keys.zod.ts +++ b/packages/spec/src/ui/expression-bindable-text-keys.zod.ts @@ -46,16 +46,15 @@ * ## Why these rows (measured, not inferred) * * Rows are a RECORD of what each component renderer already reads back from - * the node's top level, measured at the `.objectui-sha` pin `82a9417` - * (re-verified identical at objectui `origin/main` `6c68b13` on - * 2026-08-18) — declaring a key a renderer does not read back would recreate + * the node's top level (read points recorded in `liveness/page.json`, row + * `regions`) — declaring a key a renderer does not read back would recreate * the evaluated-but-blank half of the objectui#4795 table: * - * | type | renderer read points (top level, within the closed set) | - * |-------------|----------------------------------------------------------| - * | `statistic` | `schema.label`, `schema.value`, `schema.description` (`data-display/statistic.tsx`) | - * | `card` | `schema.title`, `schema.description` (`layout/card.tsx`) | - * | `button` | `schema.label` (`form/button.tsx`) | + * | type | renderer reads (top level, within the closed set) | + * |-------------|---------------------------------------------------| + * | `statistic` | `schema.label`, `schema.value`, `schema.description` | + * | `card` | `schema.title`, `schema.description` | + * | `button` | `schema.label` | * * These are the measured motivating cases from objectui#4795 (dashboard * workhorses). Other registered renderers also read keys from this closed set @@ -64,7 +63,7 @@ * declared yet: form-control `value` is interactive state rather than display * text, and each row is an accept-surface widening that should arrive with its * own measurement, not ride this one (startup scope discipline). `text` reads - * back `schema.content || schema.value` (`basic/text.tsx`) — unlike the rows + * back `schema.content || schema.value` (the same ledger entry) — unlike the rows * above, its omission is not merely unmeasured: `value` there is a fallback * spelling for the same slot `content` already evaluates (see `content` NOT a * member, above), not a second read point, so a `text: ['value']` row would diff --git a/packages/spec/src/ui/list-view-export-options.ts b/packages/spec/src/ui/list-view-export-options.ts index d46c7deb2f7..e9cfb0e2a64 100644 --- a/packages/spec/src/ui/list-view-export-options.ts +++ b/packages/spec/src/ui/list-view-export-options.ts @@ -116,8 +116,8 @@ const exportOptionsUnknownKeyError = strictObjectError({ /** * Object form of `view.exportOptions` (#8010, maintainer ruling 2026-08-12 — * option A). The declared key set is exactly what the only renderer reads, - * measured on objectui `origin/main@878140b` (`ObjectGrid.tsx:1596–1642`): - * `formats`, `maxRecords`, `includeHeaders`, `fileNamePrefix`, and the + * measured on objectui (read points recorded in `liveness/view.json`, row + * `list.exportOptions`): `formats`, `maxRecords`, `includeHeaders`, `fileNamePrefix`, and the * previously UNDECLARED `streaming` opt-out — declared here so no * undeclared-but-read key survives the fix. Declaring anything more would be * capability surface with no reader; declaring less recreates the defect. diff --git a/packages/spec/src/ui/navigation-target.ts b/packages/spec/src/ui/navigation-target.ts index 4e17e5e83de..f7ce7d8d6bf 100644 --- a/packages/spec/src/ui/navigation-target.ts +++ b/packages/spec/src/ui/navigation-target.ts @@ -25,9 +25,9 @@ * * ## Why the door answers exactly what the runner answers * - * The runner's half is objectui's `classifyNavigationTarget` - * (`packages/core/src/actions/navigationTarget.ts`, read at objectui - * `7578323`), which both the action runner and the entitlement CTA call. For a + * The runner's half is objectui's `classifyNavigationTarget` (read point + * recorded in `liveness/action.json`, row `target`), which both the action + * runner and the entitlement CTA call. For a * literal target this function admits exactly what that classifier admits and * refuses exactly what it refuses. A door wider than the runner is the trap the * set exists to close: the target publishes clean and fails at click. A door diff --git a/packages/spec/src/ui/object-refinement-check-exports.test.ts b/packages/spec/src/ui/object-refinement-check-exports.test.ts index 211b22708d1..2f4ec3d1c0e 100644 --- a/packages/spec/src/ui/object-refinement-check-exports.test.ts +++ b/packages/spec/src/ui/object-refinement-check-exports.test.ts @@ -5,9 +5,10 @@ * and each export IS the check its schema runs — the spec half of objectui#7715. * * Why: objectui derives its zod mirrors from a spec object's `.shape` - * (`specFieldsExcept(SpecListViewSchema.shape, …)`, six sites at the pinned - * `.objectui-sha`). `.shape` carries the FIELDS by reference and drops every - * check attached to the OBJECT, so at 17.3.0 objectui's authoring door accepted + * (`specFieldsExcept(SpecListViewSchema.shape, …)`; read points recorded in + * `liveness/view.json`, row `list.appearance`). `.shape` carries the FIELDS by + * reference and drops every check attached to the OBJECT, so at 17.3.0 + * objectui's authoring door accepted * `appearance.allowedVisualizations: ['calendar']` with no `calendar:` block * while the spec's publish door refused it. A mirror needs the rule as a * function it can attach, and that function must be the schema's own — never a @@ -573,11 +574,10 @@ const MIRRORED: MirroredSchema[] = [ cleanFixtures: [{ field: 'created_at', type: 'date' }], }, // `dashboard.widgets[]` is mirrored the same way and for the same reason, so - // the ADR-0049 `stageOrder` type gate belongs in this catalogue: measured at - // the `.objectui-sha` pin `53ded82bf7a494f54e344e19099dbf00854b8694`, - // objectui's `packages/types/src/zod/complex.zod.ts` builds its own - // `DashboardWidgetSchema` from - // `specFieldsExcept(SpecDashboardWidgetSchema.shape, …)` — a `.shape` spread, + // the ADR-0049 `stageOrder` type gate belongs in this catalogue: objectui + // builds its own `DashboardWidgetSchema` from + // `specFieldsExcept(SpecDashboardWidgetSchema.shape, …)` (read point recorded + // in `liveness/dashboard.json`, row `widgets`) — a `.shape` spread, // which carries the FIELDS and drops every object-level check. That mirror // re-attaches none of these exports today, which is a live gap recorded on // the check's own docblock rather than a reason to leave the export diff --git a/packages/spec/src/ui/page.zod.ts b/packages/spec/src/ui/page.zod.ts index 1c7b4058a15..df31e4aadbc 100644 --- a/packages/spec/src/ui/page.zod.ts +++ b/packages/spec/src/ui/page.zod.ts @@ -317,8 +317,9 @@ export const ElementDataSourceSchema = lazySchema(() => strictObject({ * which that classifier admits for a NAVIGATION, is refused HERE on a * measurement of the renderer, not by analogy: a read is a `fetch`, and the * `apiBase` promotion the console applies to a `url` action - * (`ActionRunner.navigateTo`, measured at objectui `7578323a544e`) prefixes a - * root-relative `/api/…` path only — a `./` path would resolve against the + * (`ActionRunner.navigateTo`; read points recorded in `liveness/page.json`, + * row `regions`) prefixes a root-relative `/api/…` path only — a `./` path + * would resolve against the * console route the page is mounted on, on the SPA host, and read the wrong * thing in the silent direction. * @@ -421,7 +422,7 @@ const ELEMENT_API_NO_CREDENTIALS = * refused with {@link ELEMENT_API_NO_CREDENTIALS}. There is no `write` block: * a page element READS. * - * `method` admits `GET` alone. Measured at objectui `7578323a544e`: the adapter + * `method` admits `GET` alone. Measured on the renderer: the adapter * the ruling names, `@object-ui/core`'s `ApiDataSource`, issues every read — * `find`, `findOne`, `aggregate` — with an explicit `method: 'GET'` that * overrides the request config's own, so any other value here would declare a @@ -429,7 +430,7 @@ const ELEMENT_API_NO_CREDENTIALS = * * ## Planned, not yet rendered * - * At objectui `7578323a544e` no renderer reads this binding: the page-element + * No renderer reads this binding yet: the page-element * data-source hook (`useElementDataSource`, `@object-ui/react`) admits only a * binding with a string `object` (`isElementDataSourceConfig`), so an `api` * binding reaches no block yet. The generic blocks that read it — stat, usage @@ -642,21 +643,18 @@ export const PageComponentSchema = lazySchema(() => strictObject({ * unavailable — they are NOT promises, and widening the contract to cover * them is an open question, deliberately not decided here. * - * Measured at the `.objectui-sha` pin `1fb1ced36243` (the four files below - * are identical at objectui `origin/main` `f228d710`): - * `app-shell/src/providers/ExpressionProvider.tsx:181-190` - * (`buildExpressionScope`) builds the ambient scope — `current_user`, - * `user`, `ctx.user`, `os.user`, `features`, and nothing else; - * `react/src/SchemaRenderer.tsx:665-676` (`configEvaluationScope`) composes - * this surface's evaluator from it, adding `record` (only when the page has - * a row) and `page`. + * Measured on the renderer (read points recorded in `liveness/page.json`, + * row `regions`): `buildExpressionScope` builds the ambient scope — + * `current_user`, `user`, `ctx.user`, `os.user`, `features`, and nothing + * else; `configEvaluationScope` composes this surface's evaluator from it, + * adding `record` (only when the page has a row) and `page`. * * ## No `data` root on this surface * * The shipping renderer mounts NO `data` root here. objectui#9308 (ruling * B, 2026-09-13) removed `data` as a runtime expression root: * `SchemaRenderer` stopped publishing its injected data source under that - * name (`SchemaRenderer.tsx:1194`), and a data scope reaches a predicate + * name, and a data scope reaches a predicate * only through the predicate-scope channel (`PredicateScopeProvider`), * whose one shipping feeder, `ExpressionProvider`, publishes no `data`. So a * `data.*` read here faults, and a faulting `visibleWhen` renders the @@ -672,7 +670,7 @@ export const PageComponentSchema = lazySchema(() => strictObject({ * ## ⚠️ A `page:tabs` item is a different evaluator * * On a `page:tabs` **item-level** `visibleWhen`, a separate evaluator - * (`components/src/renderers/layout/containers.tsx:642-649`) binds `data` to + * (recorded in the same evidence list) binds `data` to * the record **ROW** and spreads the row's bare fields as well — a binding * this surface does not have. See that key's own describe in * `component.zod.ts` rather than assuming either environment carries over. @@ -925,10 +923,11 @@ export const InterfacePageConfigSchema = lazySchema(() => strictObject({ * * It was declared ahead of its renderer, with the planned marker on its * describe. The renderer half, objectstack-ai/objectui#12270 (objectui - * `b434f5f47`), is inside the console pin from objectui `b9814b334`, so the - * marker is dropped. Measured there: `InterfaceListPage` hands `ListView` - * `emptyState: cfg.emptyState ?? view.emptyState` (`InterfaceListPage.tsx:509`), - * so the page's own empty state wins over its source view's, whole, and the + * `b434f5f47`), is inside the console's objectui pin, so the marker is + * dropped. Measured there (read point recorded in `liveness/page.json`, row + * `interfaceConfig`): `InterfaceListPage` hands `ListView` `emptyState: + * cfg.emptyState ?? view.emptyState`, so the page's own empty state wins + * over its source view's, whole, and the * source view's stays the fallback. Where `ListView` draws an empty state at * all (the `grid` and `gallery` visualizations), that is the one it draws, * `link` included. diff --git a/packages/spec/src/ui/view.test.ts b/packages/spec/src/ui/view.test.ts index cd903dd7611..f21ceedd608 100644 --- a/packages/spec/src/ui/view.test.ts +++ b/packages/spec/src/ui/view.test.ts @@ -3931,7 +3931,7 @@ describe('ListViewSchema — retired striped/bordered/virtualScroll (every reade // 2026-08-12, option A). The spec published a bare format array while the only // renderer read `exportOptions.formats`, so no declaration was both type-legal // and functional. The object form declares exactly the five renderer-read keys -// (measured objectui origin/main@878140b, ObjectGrid.tsx:1596–1642); the +// (read points recorded in `liveness/view.json`, row `list.exportOptions`); the // legacy bare array stays accepted and lifts to `{ formats }` at parse. // `'pdf'` left the enum in the same change (#1301 NOT_PLANNED). The removal // stands under the 2026-10-08 ruling on #8346 (B′), which re-planned PDF diff --git a/packages/spec/src/ui/view.zod.ts b/packages/spec/src/ui/view.zod.ts index 0e18860ffca..8d14dc3c89d 100644 --- a/packages/spec/src/ui/view.zod.ts +++ b/packages/spec/src/ui/view.zod.ts @@ -1307,8 +1307,8 @@ function checkGroupingFieldName(raw: string): string | undefined { * about `grouping.fields[].field` and explicitly not about this axis, so * quoting its date here would attribute a decision that was never made. * - * What the name is used for, measured in the consumer (objectui `dda8f3815`): - * the kanban board resolves its lane as + * What the name is used for, measured in the consumer (read points recorded in + * `liveness/view.json`, row `list.kanban`): the kanban board resolves its lane as * `laneField = groupByField || groupField || detectStatusField(objectDef)` * and buckets cards by `card[laneField]`; `ObjectGantt`'s `groupByAccessor` * splits the name on `.` and walks the backing record @@ -1894,14 +1894,14 @@ const emptyStateLinkSchema = () => strictObject({ * reaches, the `object-grid` page block's, and a list page's own * `interfaceConfig.emptyState` (`page.zod.ts`, #23005). It was declared ahead * of its renderer, with the planned marker on its describe. The renderer half, - * objectstack-ai/objectui#12270 (objectui `b434f5f47`), is inside the console - * pin from objectui `b9814b334`, so the marker is dropped. Measured there: - * `ListView` hands `ListEmptyState` the `link` (`ListView.tsx:5716`), which - * draws it beside the add-record action (`ListEmptyState.tsx:153`) in the - * empty branch of the `grid` and `gallery` visualizations; `ObjectGrid` draws - * it in `DataEmptyState`'s action slot (`ObjectGrid.tsx:6897-6899`); and - * `InterfaceListPage` hands `ListView` the page's own `emptyState` ahead of - * the source view's (`InterfaceListPage.tsx:509`). Each draws it through the + * objectstack-ai/objectui#12270 (objectui `b434f5f47`), is inside the console's + * objectui pin, so the marker is dropped. Measured there (read points recorded + * in `liveness/view.json`, row `list.emptyState`, and `liveness/page.json`, + * row `interfaceConfig`): `ListView` hands `ListEmptyState` the `link`, which + * draws it beside the add-record action in the empty branch of the `grid` and + * `gallery` visualizations; `ObjectGrid` draws it in `DataEmptyState`'s action + * slot; and `InterfaceListPage` hands `ListView` the page's own `emptyState` + * ahead of the source view's. Each draws it through the * one shared `EmptyStateLink` (`@object-ui/components`), which follows it as a * `type: 'url'` action through the action runner, so a click opens what the * runner opens for that target. @@ -2629,10 +2629,10 @@ const LIST_CHART_BINDING_KEYS = ['dataset', 'values'] as const; * - A declared `chart` block ⇒ nothing here: its own strict schema already * requires both keys, at `chart.dataset` / `chart.values`. * - * What such a view renders is a dead screen either way: at the pin the - * renderer fabricates a binding nobody wrote (an aggregate over `'name'` / - * `'value'`); from objectui#6152 round 15 (objectui `0253416`) that floor is - * retired and `ObjectChart` refuses on screen (`chart-missing-category-axis`). + * What such a view renders is a dead screen either way: before objectui#6152 + * round 15 (objectui `0253416`) the renderer fabricated a binding nobody wrote + * (an aggregate over `'name'` / `'value'`); since then that floor is retired + * and `ObjectChart` refuses on screen (`chart-missing-category-axis`). * ⛔ Never fabricate a default here either — only the author knows the dataset. * * Reads `type` as the door hands it: the authoring shapes apply the `grid` @@ -4241,8 +4241,8 @@ function refineFormFieldFeaturesRoot( // enum-VALUE narrowing: the enum's own error map carries the prescription, // keyed on `issue.input`, so only a value which used to be legal gets the "was // removed" message. The twin of `OBJECT_FORM_LAYOUT_RETIRED` -// (component.zod.ts), which carries the renderer measurement at the -// `.objectui-sha` pin: every form presentation folds 'grid' and 'inline' to +// (component.zod.ts), whose renderer measurement is recorded in +// `liveness/page.json`, row `regions`: every form presentation folds 'grid' and 'inline' to // 'vertical', and multi-column is `columns`, honoured under every arm. The // ADR-0087 conversion `form-layout-inline-grid-to-vertical` rewrites both to // 'vertical' in every form payload a view carries (`form`, `formViews.*`, a