Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1959033
wip(spec): move component-member read points out of component.zod.ts …
claude Oct 15, 2026
f53faf1
wip(spec): component-member read points, object-map through object-ti…
claude Oct 15, 2026
18b1116
wip(spec): component-member read points, object-form through object-map
claude Oct 15, 2026
926ec3d
wip(spec): component-member read points, calendar and form helpers
claude Oct 15, 2026
8ee6992
wip(spec): component-member read points, object-kanban
claude Oct 15, 2026
8250883
wip(spec): component-member read points, object-metric
claude Oct 15, 2026
c87c084
wip(spec): component-member read points, object-grid
claude Oct 15, 2026
7cce696
wip(spec): component-member read points, action blocks and element lists
claude Oct 15, 2026
3666aaf
wip(spec): component-member read points, record and element blocks
claude Oct 15, 2026
63ca56f
wip(spec): component-member read points, the rest of component.zod.ts
claude Oct 15, 2026
e7b0941
wip(spec): component-member read points, component.test.ts
claude Oct 15, 2026
5584184
wip(spec): component-member read points, pin tests and the other grou…
claude Oct 15, 2026
13f7626
wip(spec): held-back moves, the other spellings, and the assembled ev…
claude Oct 15, 2026
1646d39
wip(spec): regenerate the reference doc
claude Oct 15, 2026
6e281bf
Merge remote-tracking branch 'origin/main' into claude/issue-23434-pi…
claude Oct 15, 2026
f3c640e
docs(spec): leave component.zod.ts to parts 1 and 2, add the part-3 c…
claude Oct 15, 2026
03d0be9
docs(spec): three more dated objectui citations into their ledger rows
claude Oct 15, 2026
7ea3bb0
Merge origin/main into claude/issue-23434-pin-citations-pr2
claude Oct 15, 2026
be8657f
docs(spec): page.json interfaceConfig note, past tense for the remove…
claude Oct 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/23434-component-read-points-part-3.md
Original file line number Diff line number Diff line change
@@ -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.
17 changes: 8 additions & 9 deletions content/docs/references/ui/expression-bindable-text-keys.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions packages/spec/liveness/action.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/spec/liveness/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/spec/liveness/dashboard.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/spec/liveness/dataset.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
Loading
Loading