diff --git a/.changeset/10117-page-header-title-lookup-fallback.md b/.changeset/10117-page-header-title-lookup-fallback.md index f4dceec079..09b6faf866 100644 --- a/.changeset/10117-page-header-title-lookup-fallback.md +++ b/.changeset/10117-page-header-title-lookup-fallback.md @@ -2,7 +2,7 @@ '@object-ui/components': patch --- -fix(components): `page:header` resolves a lookup title candidate instead of handing its expanded object to JSX (objectui#10117) +fix(components): `page:header` resolves a lookup title candidate instead of handing its expanded object to JSX A record whose declared name field was empty took the whole `page:header` block down with "Objects are not valid as a React child" (React #31), leaving the red diff --git a/.changeset/10120-form-omits-fls-denied-fields.md b/.changeset/10120-form-omits-fls-denied-fields.md index ae166c7dba..7586bedd08 100644 --- a/.changeset/10120-form-omits-fls-denied-fields.md +++ b/.changeset/10120-form-omits-fls-denied-fields.md @@ -3,7 +3,7 @@ '@object-ui/fields': patch --- -A form no longer submits — nor offers — a field the CALLER may read but not edit (objectui#10120). +A form no longer submits — nor offers — a field the CALLER may read but not edit. **Clause-②: no** — no exported symbol is added, removed, renamed or retyped, no key on a published payload moves, and no accept set is relaxed. `sanitizeFormData` gains an optional third argument and `fieldWriteGate` / `applyFieldPermissions` are new, but `@object-ui/plugin-form` publishes `.` only, from `index.tsx`, which re-exports neither module — measured on the built `dist/index.d.ts`, where neither name appears. `LookupField`'s props are unchanged. What moves is what the client PUTS on the wire and which controls it draws. diff --git a/.changeset/10129-action-param-field-backed-picker.md b/.changeset/10129-action-param-field-backed-picker.md index 22ca959205..9932454068 100644 --- a/.changeset/10129-action-param-field-backed-picker.md +++ b/.changeset/10129-action-param-field-backed-picker.md @@ -5,8 +5,7 @@ --- A field-backed action param reaches its record picker, and a param whose backing -field cannot be read is refused instead of rendered as an empty text box -(objectui#10129). +field cannot be read is refused instead of rendered as an empty text box. An action declaring a `params` entry backed by a `lookup` field rendered as a bare text input: no options, no typeahead, and no request for the referenced object on diff --git a/.changeset/10132-dashboard-i18n-declared-not-resolved.md b/.changeset/10132-dashboard-i18n-declared-not-resolved.md index e8a0eae11a..8666a8269f 100644 --- a/.changeset/10132-dashboard-i18n-declared-not-resolved.md +++ b/.changeset/10132-dashboard-i18n-declared-not-resolved.md @@ -3,7 +3,7 @@ '@object-ui/plugin-dashboard': patch --- -fix(dashboard,charts): two dashboard surfaces the spec types as translatable now resolve (objectui#10132) +fix(dashboard,charts): two dashboard surfaces the spec types as translatable now resolve `@objectstack/spec` declares both of these translatable and the Console resolved neither, so an author could set a key the contract documents and nothing happened. They turned out to be diff --git a/.changeset/10163-line-items-field-write-gate.md b/.changeset/10163-line-items-field-write-gate.md index 930707a5bb..e87bf300ee 100644 --- a/.changeset/10163-line-items-field-write-gate.md +++ b/.changeset/10163-line-items-field-write-gate.md @@ -4,7 +4,7 @@ A `record:line_items` grid no longer offers a cell the CALLER may read but not edit (objectui#10163). -**What it was.** objectui#10120 taught the record-form containers to render a field the caller's permission set marks `editable: false` as disabled, through one shared render pass. The line-items panel stayed outside that pass: it read `readonly` and nothing else, so on the same page the same column rendered as a live, editable cell — inviting an edit the server refuses. +**What it was.** `80c54122e` taught the record-form containers to render a field the caller's permission set marks `editable: false` as disabled, through one shared render pass. The line-items panel stayed outside that pass: it read `readonly` and nothing else, so on the same page the same column rendered as a live, editable cell — inviting an edit the server refuses. **What changed, in observable terms.** diff --git a/.changeset/10563-form-arms-write-payload.md b/.changeset/10563-form-arms-write-payload.md index cf8384507b..6e4e5167d4 100644 --- a/.changeset/10563-form-arms-write-payload.md +++ b/.changeset/10563-form-arms-write-payload.md @@ -7,7 +7,7 @@ The `tabbed`, `wizard` and `split` form layouts now write what the simple form w **Clause-②: no.** No exported symbol, type or prop changes. The shared sequence lives in an internal module that `index.tsx` does not re-export. The change is in what the client sends, and ⚠️ in what a host `submitHandler` receives from these layouts (see below). -**Before.** The simple form, `ModalForm` and `DrawerForm` strip a save before sending it, and on an edit they send only the fields that differ from the record they read (objectui#10108, objectui#10120, objectui#10156). `TabbedForm`, `SplitForm` and `WizardForm` did neither. An edit sent every value the form held: `id`, `owner_id`, `created_by`, `updated_at`, formula columns, and fields the caller's field-level security refuses. The server answered with a `403` or an unknown-field refusal. A create seeded with a whole record, such as a copy of an existing one, posted those columns too. A simple form whose mobile `stepper` option shows it one step at a time renders through `WizardForm`, so it had the same defect. +**Before.** The simple form, `ModalForm` and `DrawerForm` strip a save before sending it, and on an edit they send only the fields that differ from the record they read (objectui#10108, `80c54122e`, objectui#10156). `TabbedForm`, `SplitForm` and `WizardForm` did neither. An edit sent every value the form held: `id`, `owner_id`, `created_by`, `updated_at`, formula columns, and fields the caller's field-level security refuses. The server answered with a `403` or an unknown-field refusal. A create seeded with a whole record, such as a copy of an existing one, posted those columns too. A simple form whose mobile `stepper` option shows it one step at a time renders through `WizardForm`, so it had the same defect. **What changed, in observable terms.** diff --git a/.changeset/10746-joined-report-clears-binding.md b/.changeset/10746-joined-report-clears-binding.md index c2f76dd8ed..77793c7ffa 100644 --- a/.changeset/10746-joined-report-clears-binding.md +++ b/.changeset/10746-joined-report-clears-binding.md @@ -9,8 +9,8 @@ to `joined`, and in the same render hid its dataset / values / rows / columns / controls, while the spec's own `reportForm` hid the whole "Dataset binding" section (`order` included) through `visibleWhen: "data.type != 'joined'"`. A report that was bound first and switched second kept every one of those keys invisibly. `ReportSchema`'s -joined arm refuses a container `dataset` / `rows` / `columns` / `values` (objectstack PR -#20160: "a `joined` report selects per block — move `KEY` onto `blocks[]`, or delete it") +joined arm refuses a container `dataset` / `rows` / `columns` / `values` (PR +objectstack#20160: "a `joined` report selects per block — move `KEY` onto `blocks[]`, or delete it") and has always refused a container `order`, so the save was refused at a path no control on the Properties tab could reach; only the JSON source tab could delete the key. diff --git a/.changeset/10803-dead-citation-sweep-sixth-batch.md b/.changeset/10803-dead-citation-sweep-sixth-batch.md new file mode 100644 index 0000000000..ba2f432eb8 --- /dev/null +++ b/.changeset/10803-dead-citation-sweep-sixth-batch.md @@ -0,0 +1,24 @@ +--- +--- + +Comment-only in `@object-ui/app-shell`, `@object-ui/components`, `@object-ui/core`, +`@object-ui/data-objectstack`, `@object-ui/fields`, `@object-ui/layout`, +`@object-ui/plugin-dashboard`, `@object-ui/plugin-designer`, `@object-ui/plugin-detail`, +`@object-ui/plugin-form`, `@object-ui/plugin-grid`, `@object-ui/plugin-kanban`, +`@object-ui/plugin-list`, `@object-ui/plugin-view` and `@object-ui/types` (whose runtime +text is declared separately below): docblocks and code comments that cited objectui issues +which answer 404 now cite the commit that landed each change, as a 9-character sha, and +where nothing answers they name the card by role (objectui#10803, the sixth batch). Bare +numbers that were sister-repo cards or pull requests are now written `objectstack#N`, and +where the sister-repo pull request itself answers 404 the sentence cites its commit in +that repository instead. Two `@object-ui/plugin-grid` sites beside `objectstack#3720` that +named the apiMethods whitelist card bare now read `objectstack#3391`, the card they meant. +Some of these docblocks reach the +emitted `.d.ts` and `.js`; none of them moves a claim, and none of these comment edits +changes a code or type token. No published behaviour changes through them, so this +declares no release. The one runtime string the sweep touches, a zod `.describe()` string +in `@object-ui/types`, is declared on its own as a patch in +`10803-sixth-batch-runtime-strings.md`. + +The same repair in the pending changesets that carried these citations is prose-only, and +their frontmatter is byte-identical. diff --git a/.changeset/10803-sixth-batch-runtime-strings.md b/.changeset/10803-sixth-batch-runtime-strings.md new file mode 100644 index 0000000000..87e3800d3c --- /dev/null +++ b/.changeset/10803-sixth-batch-runtime-strings.md @@ -0,0 +1,14 @@ +--- +'@object-ui/types': patch +--- + +fix(types): `InputSchema.wrapperClass`'s description no longer points at an objectui issue that answers 404 + +The zod `.describe()` text of `InputSchema.wrapperClass` ended with a pointer to an +objectui issue that answers 404. A reader of a zod `description` has no repository to +resolve a commit against, so the pointer is dropped rather than replaced: the description +now reads "Classes on the wrapper div around the input and its label" (objectui#10803). + +Nothing else in the string moves, and no key, path, issue code, accept set, refusal or +severity changes: every document that parsed before parses the same way and is refused at +the same path with the same code. diff --git a/.changeset/14026-list-import-mappings-pin.md b/.changeset/14026-list-import-mappings-pin.md index 8dd77c188a..9d7bedd692 100644 --- a/.changeset/14026-list-import-mappings-pin.md +++ b/.changeset/14026-list-import-mappings-pin.md @@ -2,5 +2,5 @@ --- Pin `listImportMappings(objectName)` against the framework's measured -`GET /api/v1/meta/mapping` body (objectui#14026). Test only; no package is +`GET /api/v1/meta/mapping` body (objectstack#14026). Test only; no package is released by this change. diff --git a/.changeset/6594-headercolor-mirror-enum.md b/.changeset/6594-headercolor-mirror-enum.md index 9b27ab1330..53e803f35e 100644 --- a/.changeset/6594-headercolor-mirror-enum.md +++ b/.changeset/6594-headercolor-mirror-enum.md @@ -8,7 +8,7 @@ the contract — the TypeScript declaration and the `@object-ui/types/zod` mirro objectstack#12126). The six are `muted`, `muted/50`, `accent`, `primary/10`, `secondary/10` and `destructive/10`: exactly what `@object-ui/plugin-detail`'s `HEADER_COLOR_CLASSES` resolves (objectui#6178) and exactly what `@objectstack/spec` declares on its strict -`record:details` section schema (objectstack PR #12616). +`record:details` section schema (PR objectstack#12616). ## ⚠️ Accept-set narrowing — these spellings stop validating diff --git a/.changeset/6748-preview-mode-provenance-ratchet.md b/.changeset/6748-preview-mode-provenance-ratchet.md index c58fbad87c..00aa51efa2 100644 --- a/.changeset/6748-preview-mode-provenance-ratchet.md +++ b/.changeset/6748-preview-mode-provenance-ratchet.md @@ -5,7 +5,7 @@ Test-only change in `@object-ui/auth`: `auth-spec-parity.test.ts` now watches `PreviewModeConfig`, the `@objectstack/spec/kernel` symbol that `packages/auth/README.md:328` claims the preview-mode prop aligns with. Upstream retired that symbol in source and registered it as `kernel/PreviewModeConfig` in -`RETIRED_DEFS_BY_MAJOR[18]` (objectstack#11846, landed as objectstack PR #12718), so +`RETIRED_DEFS_BY_MAJOR[18]` (objectstack#11846, landed as PR objectstack#12718), so it leaves the published set at spec major 18 — it is still exported by the 17.2.0 this repo resolves, which is why the assertion ships green. It goes red at the spec-18 bump, the exact moment the README sentence stops being true, which is the signal to correct diff --git a/.changeset/6985-wizard-card-r-alignment.md b/.changeset/6985-wizard-card-r-alignment.md index 504e84a80f..628292fca4 100644 --- a/.changeset/6985-wizard-card-r-alignment.md +++ b/.changeset/6985-wizard-card-r-alignment.md @@ -6,7 +6,7 @@ Wizard view v1, the objectui half (Card R, objectui#6985) — alignment + pins for the ruled `type: 'wizard'` tightening (objectstack#13622 D1–D8, maintainer ruling -2026-08-31; spec half objectstack PR #13733). +2026-08-31; spec half PR objectstack#13733). The renderer was already aligned: `WizardStepConfig` carries no predicate/collapse keys (objectui#6237's ruled split), the wizard route drops-and-reports an authored @@ -42,5 +42,5 @@ exemption since #2959. This card lands the residue: half records the 17.2.x accept-set it measured. `steps:` is pinned refused on every spec line. -No teaching material — the #13337/#13086 fence lifts only after both halves land; +No teaching material — the objectstack#13337 / objectstack#13267 fence lifts only after both halves land; docs changes here are TSDoc/comments only. diff --git a/.changeset/7415-action-type-input-rename.md b/.changeset/7415-action-type-input-rename.md index 9c1923125e..085c2a2d95 100644 --- a/.changeset/7415-action-type-input-rename.md +++ b/.changeset/7415-action-type-input-rename.md @@ -19,7 +19,7 @@ actually set it: last, so `type="api"` replaced the component discriminator and the node stopped resolving to a component at all. `validate.ts` cannot report that: `type` is in `BASE_PROPS`, so it is skipped before the declared-input check runs. Two mechanisms, one outcome, no - diagnostic. objectstack PR #14274 landed a refusal on this tier whose prescription + diagnostic. PR objectstack#14274 landed a refusal on this tier whose prescription ("write the tag you meant") is wrong for exactly these two components. - **react-page tier** — the wrapper stamps `type: tag` last and parks the author's value under `specType` (objectui#2880), which neither action renderer reads. diff --git a/.changeset/7714-lookup-draft-stays-client-side.md b/.changeset/7714-lookup-draft-stays-client-side.md index 6847291ed8..0496d57457 100644 --- a/.changeset/7714-lookup-draft-stays-client-side.md +++ b/.changeset/7714-lookup-draft-stays-client-side.md @@ -31,7 +31,7 @@ objectstack#4001 closed. **The whitespace row follows the contract; it is not a local opinion.** The predicate is `typeof reference === 'string' && reference.trim() !== ''`. It was a declared -divergence when written — 17.3.0's #13632 refinement spelled its emptiness test as an +divergence when written — 17.3.0's objectstack#13632 refinement spelled its emptiness test as an equality against `''`, so the spec accepted `reference: ' '` while these writers refused it. objectstack#16920 applies that test to the TRIMMED value, so the spec now refuses the identical shape under the same `custom` issue at the same `reference` diff --git a/.changeset/7722-wrapper-class-five-more.md b/.changeset/7722-wrapper-class-five-more.md index 19ac257db6..49bba09ce6 100644 --- a/.changeset/7722-wrapper-class-five-more.md +++ b/.changeset/7722-wrapper-class-five-more.md @@ -43,6 +43,6 @@ admitted unexamined on all five mirrors, pinned per mirror with a control key the renderer does not read. `InputSchema.wrapperClass`, declared on the TS face only, was a recorded row of the parity ledger (`UnmirroredDeclared`) and this card left it there; the new sweep pin carried it as a self-expiring exemption. -objectui#8072 has since mirrored that key, so the row and the exemption are both +`c974edf14` has since mirrored that key, so the row and the exemption are both gone — the exemption expired exactly as designed, and the sweep now judges all nine readers alike. diff --git a/.changeset/7735-zod-mirrors-stop-authoring-defaults.md b/.changeset/7735-zod-mirrors-stop-authoring-defaults.md index ece941b9e0..a745fcb4f3 100644 --- a/.changeset/7735-zod-mirrors-stop-authoring-defaults.md +++ b/.changeset/7735-zod-mirrors-stop-authoring-defaults.md @@ -26,7 +26,7 @@ was not even self-consistent. **What changed.** All 41 `.default()` call sites under `packages/types/src/zod/` are removed — `layout.zod.ts` 22, `crud.zod.ts` 11, `form.zod.ts` 5, `views.zod.ts` 2, `app.zod.ts` 1. `@object-ui/components` reconciles the third -face objectui#8229 found: `flex`'s registration `defaultProps.align` seeded +face a separate finding found: `flex`'s registration `defaultProps.align` seeded `'center'`, the value its own renderer never applies, so a designer-made node laid out differently from a hand-authored one; it now seeds `'start'`. diff --git a/.changeset/7741-list-import-mappings-discriminate.md b/.changeset/7741-list-import-mappings-discriminate.md index 18530a9e29..2efac50337 100644 --- a/.changeset/7741-list-import-mappings-discriminate.md +++ b/.changeset/7741-list-import-mappings-discriminate.md @@ -40,6 +40,6 @@ a channel added ALONGSIDE that contract, not a change to it. user without devtools open can tell "there are none" from "we could not find out". Three new `console.importMappings*` keys ship in all ten locale packs. -This applies framework #13906 decision 1 option A — *a thing that could not be READ is +This applies framework objectstack#13906 decision 1 option A — *a thing that could not be READ is not a thing that is ABSENT* — at this seam. It is an already-adopted discrimination, not a new principle. diff --git a/.changeset/8072-input-wrapper-class-mirrored.md b/.changeset/8072-input-wrapper-class-mirrored.md index e731f38582..68bc6ac90a 100644 --- a/.changeset/8072-input-wrapper-class-mirrored.md +++ b/.changeset/8072-input-wrapper-class-mirrored.md @@ -2,7 +2,7 @@ '@object-ui/types': patch --- -Mirror `wrapperClass` on `InputSchema` (objectui#8072 — the last +Mirror `wrapperClass` on `InputSchema` (the last `schema.wrapperClass` reader whose value the validator admitted unexamined). `packages/components/src/renderers/form/input.tsx` reads diff --git a/.changeset/8137-record-activity-calibration-repoint.md b/.changeset/8137-record-activity-calibration-repoint.md index 48401d08a9..189c7ed4d3 100644 --- a/.changeset/8137-record-activity-calibration-repoint.md +++ b/.changeset/8137-record-activity-calibration-repoint.md @@ -2,7 +2,7 @@ --- Re-point the `record:activity.types` member calibration control in -`registry-inputs-spec-parity` from `'Account'` to `''` (objectui#8137). +`registry-inputs-spec-parity` from `'Account'` to `''`. `@objectstack/spec` 17.3.0 made that vocabulary open — `z.array(z.union([FeedItemType, z.string().min(1)]))`, objectstack#11658 executing the maintainer's 2026-08-24 ruling on objectstack#11507 — so the old diff --git a/.changeset/8204-refuseskind-invalid-type-decisive.md b/.changeset/8204-refuseskind-invalid-type-decisive.md index 215d941eff..6a8f0ad248 100644 --- a/.changeset/8204-refuseskind-invalid-type-decisive.md +++ b/.changeset/8204-refuseskind-invalid-type-decisive.md @@ -5,7 +5,7 @@ Test-only change: repair the `registry-inputs-spec-parity` kind classifier so a whose contract is a `z.union` is judged by what the union actually refused. No published behaviour changes; nothing outside `apps/console/src/__tests__/` is touched. -objectui#8204. `refusesKind` asked "is EVERY complaint at this node a kind complaint", +`refusesKind` asked "is EVERY complaint at this node a kind complaint", and Zod keeps running a schema's checks after its type check has failed: for `z.string().min(1)` the probe `[]` comes back as both `invalid_type` AND `too_small`. Read by `every`, that pair is a CONTENT refusal — so the array probe that diff --git a/.changeset/8236-collapsible-open-intercept-retire.md b/.changeset/8236-collapsible-open-intercept-retire.md index 0089e766c9..338bf35a00 100644 --- a/.changeset/8236-collapsible-open-intercept-retire.md +++ b/.changeset/8236-collapsible-open-intercept-retire.md @@ -26,7 +26,7 @@ change the block runs on its own published `inputs` (`defaultOpen` / `disabled` **The order is load-bearing and is not an implementation detail.** The renderer-side exclusion lands FIRST and the retirement second, because the render path runs no -`safeParse` (objectui#9585 measured it NOT GATED). Retiring the declaration alone would +`safeParse` (a pin in this change measures it NOT GATED). Retiring the declaration alone would have deleted the author's only warning while leaving the takeover running — strictly worse than doing nothing. Both halves are in this change; ⛔ neither is safe to remove alone. diff --git a/.changeset/8291-workspace-admin-reads-the-rung.md b/.changeset/8291-workspace-admin-reads-the-rung.md index 52c3142f70..971f47ee16 100644 --- a/.changeset/8291-workspace-admin-reads-the-rung.md +++ b/.changeset/8291-workspace-admin-reads-the-rung.md @@ -29,7 +29,7 @@ the two scalar legs, where they are the raw membership role and the stored `user The docblock that argued AGAINST reading `user.isPlatformAdmin` — on the grounds that the server computed it as `'platform_admin' in positions`, "two spellings for one fact" — is rewritten rather -than left standing. After #15948 the flag derives from the unscoped `admin_full_access` grant and +than left standing. After objectstack#15948 the flag derives from the unscoped `admin_full_access` grant and the array does not, so they can disagree and the rung is right. No behaviour changes for a genuine administrator: a platform administrator on a single-tenant diff --git a/.changeset/8307-kanban-lane-count-honesty.md b/.changeset/8307-kanban-lane-count-honesty.md index 0013528573..aca4e2db26 100644 --- a/.changeset/8307-kanban-lane-count-honesty.md +++ b/.changeset/8307-kanban-lane-count-honesty.md @@ -2,8 +2,7 @@ '@object-ui/plugin-kanban': patch --- -A Kanban lane header over a windowed fetch now says `77+` instead of `77` -(objectui#8307). +A Kanban lane header over a windowed fetch now says `77+` instead of `77`. **The defect.** `object-kanban` fetches with a real `$top` (objectui#4025) and then groups **what came back** into lanes client-side, so `col.cards.length` counts fetched diff --git a/.changeset/8408-public-form-title-description.md b/.changeset/8408-public-form-title-description.md index dec4b8b026..4f351715b1 100644 --- a/.changeset/8408-public-form-title-description.md +++ b/.changeset/8408-public-form-title-description.md @@ -3,7 +3,7 @@ --- Public form `/f/:slug` renders its authored `title` and `description` instead of the -object API name (objectui#8408). +object API name. The one Console surface an **unauthenticated** visitor sees greeted them with a database table name. `GET /api/v1/forms/:slug` serves the authored copy intact diff --git a/.changeset/8458-parity-header-pairs-figure.md b/.changeset/8458-parity-header-pairs-figure.md index 9ed2de08c0..2f7df455cf 100644 --- a/.changeset/8458-parity-header-pairs-figure.md +++ b/.changeset/8458-parity-header-pairs-figure.md @@ -9,7 +9,7 @@ so the file never reaches a consumer. Declared as releasing nothing. - objectui#8458: the WIDER ledger's objectui#7760 movement sentence said "16 pairs", the one figure in that paragraph no `headerFigures` pin reached. Re-derived as 18 (13 emptied + 5 reduced), defined, and pinned. -- objectui#8248: objectui#8222's exclusion note called the seed decomposition a LIVE +- objectui#8222's exclusion note called the seed decomposition a LIVE figure, which objectui#8243 made historical. The guidance clause is amended per the maintainer ruling (decision batch #71); the two measurement-record clauses are untouched, and the record-vs-guidance rule is now written once in the file header. diff --git a/.changeset/8603-record-details-hide-empty-restored.md b/.changeset/8603-record-details-hide-empty-restored.md index f5b77023e9..7ffac37256 100644 --- a/.changeset/8603-record-details-hide-empty-restored.md +++ b/.changeset/8603-record-details-hide-empty-restored.md @@ -13,6 +13,6 @@ **What did NOT change.** The empty ROWS of a section that still has a filled row stay with `DetailSection`'s auto-hide heuristic and the reader's "Show N empty fields" toggle, which no authored value overrides in either polarity (objectui#7129 Q2-C, left standing by the ruling). Inline-edit mode renders an all-empty section either way, so its fields never become unreachable. `record:reference_rail`'s own component-level `hideEmpty` and the `detail.hideEmptyFields` toggle label are different keys and are untouched. -**Why it moved twice.** objectui#7129 (maintainer 2026-09-01) retired the declaration and the read because `@objectstack/spec` REFUSED the key — true at the 17.2.0 pin this repo held. Upstream had already declared it (17.3.0, upstream #11289, maintainer ruling 2026-08-23 direction 1, taken from a measured symptom), so once the pin moved the contract promised an author a behaviour the renderer no longer had, and it parsed green at publish. objectui#8603 (director seat batch #137 item 3, maintainer 2026-09-15) ruled the protocol correct and restored the read; #7129's Q1-A is superseded for this key only. +**Why it moved twice.** objectui#7129 (maintainer 2026-09-01) retired the declaration and the read because `@objectstack/spec` REFUSED the key — true at the 17.2.0 pin this repo held. Upstream had already declared it (17.3.0, upstream objectstack#11289, maintainer ruling 2026-08-23 direction 1, taken from a measured symptom), so once the pin moved the contract promised an author a behaviour the renderer no longer had, and it parsed green at publish. objectui#8603 (director seat batch #137 item 3, maintainer 2026-09-15) ruled the protocol correct and restored the read; #7129's Q1-A is superseded for this key only. All four parties now agree — the spec declares it, `@object-ui/types` declares it, the `DetailViewSectionSchema` zod mirror carries it, and the renderer reads it — pinned together in `packages/plugin-detail/src/renderers/__tests__/record-details.hideEmptyRetired-7129.test.tsx`. diff --git a/.changeset/8648-ui-action-four-undeclared-keys.md b/.changeset/8648-ui-action-four-undeclared-keys.md index ea7f838733..79d393685c 100644 --- a/.changeset/8648-ui-action-four-undeclared-keys.md +++ b/.changeset/8648-ui-action-four-undeclared-keys.md @@ -50,17 +50,17 @@ mirrors the contract's block — hand-writes `title` / `description` / was hiding two defects at once: a missing declaration on the read side and a drifted type on the write side. The repair lands in `@object-ui/core` plus the dialog's own resolver and turns on an i18n-resolution decision, so it is filed -as objectui#9542 and ⛔ not guessed at here. Meanwhile the WRITE carries a +as a separate card and ⛔ not guessed at here. Meanwhile the WRITE carries a narrowing assertion to `ActionDef['resultDialog']` at both forward sites — strictly narrower than the `as any` it replaces, since every other member of the forward literal is compiler-checked again — documented as a ledger entry and pinned so it cannot regress to `as any` and cannot outlive its cause in silence. ⇒ **It did not outlive it, and the paragraph above is superseded in this same -release.** objectui#9542 landed the derivation: `ResultDialogSpec` derives its +release.** `43c0d1710` landed the derivation: `ResultDialogSpec` derives its label members from the contract, so BOTH narrowing assertions and the ledger entry that pinned them are gone and the write is compiler-checked with nothing between it and `ActionDef`. What the pin still refuses is the `as any`. (Noted -here by the objectui#9542 seat, because this body publishes verbatim into the +here by the seat that landed `43c0d1710`, because this body publishes verbatim into the CHANGELOG and its present tense would otherwise describe a workaround that the same release removes.) diff --git a/.changeset/8813-public-form-section-label-i18n.md b/.changeset/8813-public-form-section-label-i18n.md index de93e2dbd9..be0c8c8911 100644 --- a/.changeset/8813-public-form-section-label-i18n.md +++ b/.changeset/8813-public-form-section-label-i18n.md @@ -3,7 +3,7 @@ --- Public form `/f/:slug` now translates its section headings and field labels -(objectui#8813, seam 2 of objectui#8408). +(objectui#8813, seam 2 of the card behind `8241a4400`). A visitor arriving in `zh-CN` was greeted by the strings the author typed while building the form — "Your application" — even when the app bundle carried diff --git a/.changeset/8841-tree-view-config-derive.md b/.changeset/8841-tree-view-config-derive.md index 647362090f..9afbaebcb5 100644 --- a/.changeset/8841-tree-view-config-derive.md +++ b/.changeset/8841-tree-view-config-derive.md @@ -11,7 +11,7 @@ protocol refuses on `ListView.tree` (objectui#8841). **What was wrong.** `@object-ui/types` published `TreeViewConfig` as a hand-written interface — a copy of the protocol's `ListView.tree` block under a second name — and the copy declared a fifth key, `titleField`. `@objectstack/spec@17.4.0` refuses that key -there by name: `TreeConfigSchema` is a `strictObject` since spec #15469 closed the +there by name: `TreeConfigSchema` is a `strictObject` since spec objectstack#15469 closed the `.passthrough()` window 17.3.0 left open. So this package's published face accepted what the contract rejects, and an author who followed `@object-ui/types` was refused at publish with `Unrecognized key(s) on this tree configuration: 'titleField'`. The copy was diff --git a/.changeset/9231-create-app-accessible-name.md b/.changeset/9231-create-app-accessible-name.md index 83e421907c..4a4aec1058 100644 --- a/.changeset/9231-create-app-accessible-name.md +++ b/.changeset/9231-create-app-accessible-name.md @@ -3,7 +3,7 @@ --- Studio: the create-draft dialog's confirm control no longer shares a name with the -affordance that opens it (objectui#9231). +affordance that opens it. **BREAKING** — a published, user-visible accessible name changes. The confirm control of the shared Studio create dialog (`engine.studio.createDraft`) reads `Save as draft` diff --git a/.changeset/9365-bridgelistview-present-tense.md b/.changeset/9365-bridgelistview-present-tense.md index d477163c6b..057465a055 100644 --- a/.changeset/9365-bridgelistview-present-tense.md +++ b/.changeset/9365-bridgelistview-present-tense.md @@ -3,7 +3,7 @@ --- Date the dead `bridgeListView` producer evidence in `BaseSchema` instead of -letting it read as live (objectui#9365). +letting it read as live. `base.ts` stated the factual basis of objectui#4580's revised Q1 ruling in the PRESENT tense — "that is what a spec producer already writes into this slot: diff --git a/.changeset/9373-listview-detail-overlay-inline-locale-label.md b/.changeset/9373-listview-detail-overlay-inline-locale-label.md index f6a0970c07..01e28f20bc 100644 --- a/.changeset/9373-listview-detail-overlay-inline-locale-label.md +++ b/.changeset/9373-listview-detail-overlay-inline-locale-label.md @@ -2,8 +2,7 @@ '@object-ui/plugin-list': patch --- -`ListView`'s record-detail overlay heading resolves an inline locale map -(objectui#9373). +`ListView`'s record-detail overlay heading resolves an inline locale map. `ListViewSchema.label` is an `I18nLabel` — a plain string **or** an inline locale map such as `{ en: 'Accounts', 'zh-CN': '客户' }`. The `detailTitle` computation diff --git a/.changeset/9375-base-i18n-vocabulary-docblock.md b/.changeset/9375-base-i18n-vocabulary-docblock.md index b4c0a7811e..7f9cccb2f2 100644 --- a/.changeset/9375-base-i18n-vocabulary-docblock.md +++ b/.changeset/9375-base-i18n-vocabulary-docblock.md @@ -3,7 +3,7 @@ --- Correct the i18n vocabulary prose in `BaseSchema` — the two label shapes do NOT -accept each other, and an instrument in the tree re-derives that (objectui#9375). +accept each other, and an instrument in the tree re-derives that. `base.ts` said of the INLINE locale map (`label` / `description`) and the KEYED bundle reference (`ariaLabel`) that they "each accept the other's shape diff --git a/.changeset/9542-result-dialog-i18nlabel.md b/.changeset/9542-result-dialog-i18nlabel.md index 3bcc60b6ec..95a52644e7 100644 --- a/.changeset/9542-result-dialog-i18nlabel.md +++ b/.changeset/9542-result-dialog-i18nlabel.md @@ -5,7 +5,7 @@ --- Accept an inline per-locale label on an action's `resultDialog`, and resolve it -against the display language (objectui#9542). +against the display language. `@object-ui/core`'s `ResultDialogSpec` claimed in its own docblock to mirror `Action.resultDialog` in `@objectstack/spec` and did not: `title`, diff --git a/.changeset/9943-viewtype-totals-page-row-retired.md b/.changeset/9943-viewtype-totals-page-row-retired.md index b36600b154..69f0a8ed13 100644 --- a/.changeset/9943-viewtype-totals-page-row-retired.md +++ b/.changeset/9943-viewtype-totals-page-row-retired.md @@ -3,7 +3,7 @@ `plugin-view`: the three totals over the whole `ViewType` survive the spec retiring a member (objectui#9943). Type-level and tooling only — `packages/plugin-view/dist/` is BYTE-IDENTICAL across this change, measured by building the package from these sources and from this branch's base and comparing sha256 over all 13 emitted files. No package is released by it. -`ViewSwitcher`'s `DEFAULT_VIEW_LABELS` and `DEFAULT_VIEW_ICONS` and `ObjectView`'s `iconMap` were annotated `Record`. An annotated object literal is EXACT IN BOTH DIRECTIONS, and only one of those directions was wanted. It catches a member the spec ADDS — the guard objectui#5321 and objectui#8127 installed, and the reason a host `tree` view stopped rendering with the grid icon. It also makes a member the spec RETIRES an excess property (TS2353), whose only repair is deleting the row. objectstack#17063 retired the list-view kind `page`, so all three tables go red against a spec built from objectstack `main` while the `@objectstack/spec` this repository RESOLVES still publishes `page` and an author can still write one. +`ViewSwitcher`'s `DEFAULT_VIEW_LABELS` and `DEFAULT_VIEW_ICONS` and `ObjectView`'s `iconMap` were annotated `Record`. An annotated object literal is EXACT IN BOTH DIRECTIONS, and only one of those directions was wanted. It catches a member the spec ADDS — the guard objectui#5321 and `ca3942729` installed, and the reason a host `tree` view stopped rendering with the grid icon. It also makes a member the spec RETIRES an excess property (TS2353), whose only repair is deleting the row. objectstack#17063 retired the list-view kind `page`, so all three tables go red against a spec built from objectstack `main` while the `@objectstack/spec` this repository RESOLVES still publishes `page` and an author can still write one. Each table now carries `satisfies Record` — value constraint kept, exactness dropped — plus a separate totality assert, `Exclude extends never`. The retired row goes inert at type level and stays live at runtime; a member the spec adds still fails to compile. ⛔ This is deliberately NOT objectui#9880's repair: that card's tables PARTITION the drawable half, so it could derive the other half with `Extract` and let a retired row drop out on its own. These are total over the whole union, so there is no second half to derive from and the exhaustiveness has to be asserted separately — and dropping the assert while making `page` inert would trade one guard for the other. diff --git a/.changeset/ci-decide-markdown-trace-9241.md b/.changeset/ci-decide-markdown-trace-9241.md index bd51f1eabf..929b37d0fb 100644 --- a/.changeset/ci-decide-markdown-trace-9241.md +++ b/.changeset/ci-decide-markdown-trace-9241.md @@ -1,7 +1,7 @@ --- --- -ci(workflows): replace the `type-check` / `e2e` markdown claim in `ci.yml` with a traced reading (objectui#9241) +ci(workflows): replace the `type-check` / `e2e` markdown claim in `ci.yml` with a traced reading `ci.yml`'s `test` job carried the reason for being the only job with a markdown second stage as an assertion — "measured: `type-check` reads no document, and no diff --git a/.changeset/listviews-refused-read-discrimination.md b/.changeset/listviews-refused-read-discrimination.md index 77895ea30d..680753c52e 100644 --- a/.changeset/listviews-refused-read-discrimination.md +++ b/.changeset/listviews-refused-read-discrimination.md @@ -46,5 +46,5 @@ down to its wording. `console.savedViews*` keys ship in all ten locale packs; the `console.importMappings*` copy is untouched. -This applies framework #13906 decision 1 option A — *a thing that could not be READ is not +This applies framework objectstack#13906 decision 1 option A — *a thing that could not be READ is not a thing that is ABSENT* — at the second seam that needed it. diff --git a/.changeset/nav-item-visible-nesting-10119.md b/.changeset/nav-item-visible-nesting-10119.md index 7b810f14f3..de331bd8ba 100644 --- a/.changeset/nav-item-visible-nesting-10119.md +++ b/.changeset/nav-item-visible-nesting-10119.md @@ -2,7 +2,7 @@ '@object-ui/layout': minor --- -Nav `visible`: an ancestor's predicate now reaches the whole subtree, and a group no longer outlives its children (objectui#10119) +Nav `visible`: an ancestor's predicate now reaches the whole subtree, and a group no longer outlives its children The card reported that an app navigation item's `visible` CEL predicate was served to the browser and then ignored, so the entry rendered for every user. That headline no longer diff --git a/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx b/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx index f8dab33e82..15e6680d50 100644 --- a/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx +++ b/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx @@ -158,7 +158,7 @@ export function useConsoleActionRuntime(opts: ConsoleActionRuntimeOptions): Cons * see it, and a discarded-and-recomputed context value would rebuild this * callback with no change in what it reads. * - * ⭐ Why this hook reaches for it at all (objectui#10129). Field-backed action + * ⭐ Why this hook reaches for it at all (`6cc910b6d`). Field-backed action * params resolve against `ctx.objects`, and the `objects` OPTION is whatever * the caller happened to hold: `ConsoleShell`'s root runtime passes NONE, and * `DeclaredActionsBar` passes exactly ONE object (and none at all when it is @@ -233,7 +233,7 @@ export function useConsoleActionRuntime(opts: ConsoleActionRuntimeOptions): Cons // backed the narrowing out. const paramCollectionHandler = useCallback((params: ActionParamDef[], action?: ConsoleActionDispatch) => { return new Promise | null>((resolve) => { void (async () => { - // ⭐ Ask the store for the object type BEFORE resolving (objectui#10129). + // ⭐ Ask the store for the object type BEFORE resolving (`6cc910b6d`). // `ensureType` is idempotent and answers from cache in a microtask once // warm, so the cost is nil on the path a user actually takes — but it is // what makes "this field does not exist" an ANSWER rather than a race. diff --git a/packages/app-shell/src/services/MetadataService.ts b/packages/app-shell/src/services/MetadataService.ts index ebbafee167..9907ce0a68 100644 --- a/packages/app-shell/src/services/MetadataService.ts +++ b/packages/app-shell/src/services/MetadataService.ts @@ -318,7 +318,7 @@ function describeUnusableTarget(reference: unknown): string { * ## The `.trim()` MIRRORS the contract — it is no longer a local opinion * * The predicate is `typeof reference === 'string' && reference.trim() !== ''`. - * It WAS a declared divergence when written: 17.3.0's #13632 refinement spelled + * It WAS a declared divergence when written: 17.3.0's objectstack#13632 refinement spelled * its emptiness test as an equality against `''`, so the spec accepted a * whitespace-only target while this writer refused it. objectstack#16920 * (merged 2026-09-08, closing objectstack#16126) applies that test to the diff --git a/packages/app-shell/src/utils/flowResponse.ts b/packages/app-shell/src/utils/flowResponse.ts index ee760414e3..8c2717cf6f 100644 --- a/packages/app-shell/src/utils/flowResponse.ts +++ b/packages/app-shell/src/utils/flowResponse.ts @@ -292,7 +292,7 @@ export function interpretFlowResponse( return { kind: 'paused', runId: data.runId, screen: data.screen as S, data }; } - // The run reached an `end` node declaring `outcome: 'refused'` (#14945): + // The run reached an `end` node declaring `outcome: 'refused'` (objectstack#14945): // a successful evaluation that said NO. Terminal exactly like `completed` // and deliberately distinct from `failed` — nothing threw, so classifying // it as a failure would be the opposite error — which is why it is checked diff --git a/packages/app-shell/src/utils/resolveActionParams.ts b/packages/app-shell/src/utils/resolveActionParams.ts index 10da98437c..bae6ae85fa 100644 --- a/packages/app-shell/src/utils/resolveActionParams.ts +++ b/packages/app-shell/src/utils/resolveActionParams.ts @@ -517,7 +517,7 @@ export function resolveActionParam( // resolution: a field-backed `{ field: 'contract_type' }` param declaring no // inline `type` becomes a `text` param here, which is how a lookup param // reached the user as an unannotated empty box with no dropdown and no - // request for the referenced object on the wire (objectui#10129). + // request for the referenced object on the wire (`6cc910b6d`). // // ⭐ That shape stays — a partially-cached environment must not crash — but // it no longer travels ANONYMOUSLY. `unresolvedField` names the pair that @@ -708,7 +708,7 @@ export function resolveActionParam( * Union the objects a CALLER holds with the ones the console's metadata store * holds, caller first — the object list `resolveActionParams()` should be given. * - * ## Why a union and not simply the store (objectui#10129) + * ## Why a union and not simply the store (`6cc910b6d`) * * The two lists answer different questions and neither contains the other. * The caller's list is the world that caller is rendering: `ObjectView` passes diff --git a/packages/app-shell/src/views/ActionParamDialog.tsx b/packages/app-shell/src/views/ActionParamDialog.tsx index c091611940..0bb989bc7a 100644 --- a/packages/app-shell/src/views/ActionParamDialog.tsx +++ b/packages/app-shell/src/views/ActionParamDialog.tsx @@ -371,7 +371,7 @@ export function ActionParamDialog({ state, onOpenChange }: ActionParamDialogProp /** * Params whose FIELD-BACKED declaration could not be resolved against object * metadata — `resolveActionParams()` names the `.` pair it - * could not find on each one (objectui#10129). + * could not find on each one (`6cc910b6d`). * * ## Why the dialog REFUSES instead of rendering an input * @@ -444,7 +444,7 @@ export function ActionParamDialog({ state, onOpenChange }: ActionParamDialogProp if (anyUploading) return; // A param the resolver could not read has no value this dialog could // legitimately collect, so the action is not launchable from here. Confirm - // is disabled too; this guards keyboard submit (objectui#10129). + // is disabled too; this guards keyboard submit (`6cc910b6d`). if (unresolvedKey) return; // Validate required fields const newErrors: Record = {}; @@ -499,7 +499,7 @@ export function ActionParamDialog({ state, onOpenChange }: ActionParamDialogProp }; // The refusal comes FIRST — ahead of `paramToField()`, which would // otherwise build a widget from the placeholder type the resolver - // fell back to (objectui#10129). + // fell back to (`6cc910b6d`). if (rawParam.unresolvedField) { return (
@@ -511,7 +511,7 @@ export function ActionParamDialog({ state, onOpenChange }: ActionParamDialogProp >

{t('actionDialog.unresolvedParam')}

{/* The locator is its own node rather than an interpolation - (objectui#10129): `.` is an identifier, so + (`6cc910b6d`): `.` is an identifier, so it must render verbatim in every locale, never be re-ordered by a translator, and stay readable when the surrounding sentence has not been translated yet. */} diff --git a/packages/app-shell/src/views/ActionResultDialog.tsx b/packages/app-shell/src/views/ActionResultDialog.tsx index 1f85c3036c..5406f1d5aa 100644 --- a/packages/app-shell/src/views/ActionResultDialog.tsx +++ b/packages/app-shell/src/views/ActionResultDialog.tsx @@ -17,7 +17,7 @@ * - `title`, `description`, `acknowledge` and each `fields[].label` are * `I18nLabel` on the contract — a plain string OR an inline per-locale map * — and every one is resolved against the display language before it - * reaches the DOM (objectui#9542). + * reaches the DOM (`43c0d1710`). * - The dialog has NO close button — the user must click acknowledge. * This is the whole point: a toast would let them dismiss the value * before reading it. @@ -81,7 +81,7 @@ export function ActionResultDialog({ state, onAcknowledge }: ActionResultDialogP // an inline per-locale map. Resolved here, once, on the way INTO JSX — an // unresolved map is an object, and React refuses an object as a child, so the // dialog threw rather than mis-rendering and the one-shot value it exists to - // reveal was lost on an action that had already succeeded (objectui#9542). + // reveal was lost on an action that had already succeeded (`43c0d1710`). // `|| fallback` is kept over `??`: the resolver answers `undefined` for a map // with no usable entry, and an empty string must reach the default too. const title = resolveInlineI18nLabel(spec?.title, language); diff --git a/packages/app-shell/src/views/CreateViewDialog.tsx b/packages/app-shell/src/views/CreateViewDialog.tsx index 7dc993f94f..1071e31323 100644 --- a/packages/app-shell/src/views/CreateViewDialog.tsx +++ b/packages/app-shell/src/views/CreateViewDialog.tsx @@ -87,9 +87,9 @@ interface ViewTypeMeta { /** * Build the picker's rows from a TOTAL record over the visualizations. * - * A total `Record` (objectui#8127). This picker was a + * A total `Record` (`ca3942729`). This picker was a * nine-entry array whose `type` was plain `string`, so it was never compared - * against any view-type union — one of the sites the card records as "drifted, + * against any view-type union — one of the sites the card behind `ca3942729` records as "drifted, * with no type-checking at all". Keyed on the derived visualization union it now * fails the build when the spec adds a visualization, instead of quietly * becoming unofferable. diff --git a/packages/app-shell/src/views/metadata-admin/form-spec.ts b/packages/app-shell/src/views/metadata-admin/form-spec.ts index 3d4df17c38..0d1ae0fbad 100644 --- a/packages/app-shell/src/views/metadata-admin/form-spec.ts +++ b/packages/app-shell/src/views/metadata-admin/form-spec.ts @@ -136,7 +136,7 @@ export interface FormFieldSpec { * this comment exists to close. * * ⚠️ RULED 2026-08-28 (objectui#6263 / objectstack#12868, executed - * upstream by objectstack PR #13033), so this is no longer an open + * upstream by objectstack `c459da6bc`), so this is no longer an open * question this file is holding open: the FORM-VIEW option vocabulary * does not accept a per-option `default`, and the drop above is now the * ruled shape rather than a pending decision. **Where the pre-selected diff --git a/packages/app-shell/src/views/metadata-admin/i18n.ts b/packages/app-shell/src/views/metadata-admin/i18n.ts index 3a10d0e42f..afeb5a0a52 100644 --- a/packages/app-shell/src/views/metadata-admin/i18n.ts +++ b/packages/app-shell/src/views/metadata-admin/i18n.ts @@ -2351,7 +2351,7 @@ const ENGINE_STRINGS_EN: Record = { 'engine.studio.cancel': 'Cancel', 'engine.studio.create': 'Create', 'engine.studio.creating': 'Creating…', - // objectui#9231 — this is the CONFIRM control of the shared create dialog. + // `383502b23` — this is the CONFIRM control of the shared create dialog. // Never give it a name that shares a leading run with the affordance that // OPENS that dialog (`engine.studio.app.create`, `Create app` — and the // dialog itself is titled after it, which is correct). It used to read @@ -5003,7 +5003,7 @@ const ENGINE_STRINGS_ZH: Record = { 'engine.studio.cancel': '取消', 'engine.studio.create': '创建', 'engine.studio.creating': '创建中…', - // objectui#9231 — see the English entry. This value must share no leading + // `383502b23` — see the English entry. This value must share no leading // run with `engine.studio.app.create`, which opens the same dialog. 'engine.studio.createDraft': '存为草稿', 'engine.studio.saveDraft': '保存草稿', diff --git a/packages/app-shell/src/views/metadata-admin/inspectors/ReportDefaultInspector.tsx b/packages/app-shell/src/views/metadata-admin/inspectors/ReportDefaultInspector.tsx index a9cb7e9c2f..3c2628a977 100644 --- a/packages/app-shell/src/views/metadata-admin/inspectors/ReportDefaultInspector.tsx +++ b/packages/app-shell/src/views/metadata-admin/inspectors/ReportDefaultInspector.tsx @@ -77,7 +77,7 @@ const REPORT_CURATED_FIELDS = new Set([ * block binds its own `dataset` and picks its own `rows` / `columns` / * `values` — so `ReportSchema`'s joined arm refuses the four selection keys on * the container ("a `joined` report selects per block — move `KEY` onto - * `blocks[]`, or delete it", objectstack PR #20160) and has always refused a + * `blocks[]`, or delete it", PR objectstack#20160) and has always refused a * container `order` ("a `joined` report orders per block"). `chart` is inert * on a joined container (objectstack#20161) and is hidden by the same switch. * diff --git a/packages/app-shell/src/views/metadata-admin/previews/ViewPreview.tsx b/packages/app-shell/src/views/metadata-admin/previews/ViewPreview.tsx index 640f50c21b..94d183e6d8 100644 --- a/packages/app-shell/src/views/metadata-admin/previews/ViewPreview.tsx +++ b/packages/app-shell/src/views/metadata-admin/previews/ViewPreview.tsx @@ -69,7 +69,7 @@ function isPlainObject(v: unknown): v is Record { * refuses `options`, and `plugin-view` no longer reads it off a named view. So * the bag is folded here, before the node is built: each `options.KIND` block * becomes the top-level `KIND` block, and where both spell a key the TOP-LEVEL - * value wins — the same per-key merge the #20051 door describes and the one + * value wins — the same per-key merge the objectstack#20051 door describes and the one * `ObjectView` applied while it still read the bag. `options` itself is not * relayed; the renderer read nothing from it but these blocks. * diff --git a/packages/app-shell/src/views/metadata-admin/previews/simulator/flow-sim-validate.ts b/packages/app-shell/src/views/metadata-admin/previews/simulator/flow-sim-validate.ts index 5aac87610b..21d0e8145d 100644 --- a/packages/app-shell/src/views/metadata-admin/previews/simulator/flow-sim-validate.ts +++ b/packages/app-shell/src/views/metadata-admin/previews/simulator/flow-sim-validate.ts @@ -235,7 +235,7 @@ function evalCelPredicate( * * - A blank `expression` is read as `false`, as `evaluateCondition` reads an * empty source; objectstack main refuses it at `registerFlow` - * (`predicateSlotRefusal`, #17493) and the installed 17.4.0 admits it — a + * (`predicateSlotRefusal`, objectstack#17493) and the installed 17.4.0 admits it — a * declared divergence. * - A non-string `expression` is refused with the spec's * `predicateSlotRefusal`, the refusal `registerFlow` applies to this slot. diff --git a/packages/components/src/renderers/action/action-button.tsx b/packages/components/src/renderers/action/action-button.tsx index e4c0835d3c..a4727175f5 100644 --- a/packages/components/src/renderers/action/action-button.tsx +++ b/packages/components/src/renderers/action/action-button.tsx @@ -270,7 +270,7 @@ const ActionButtonRenderer = forwardRef< // // Both ends uncast: the READ since objectui#8648 (`resultDialog` is // declared on the mirror, so the compiler types it as the contract's - // own block), and the WRITE since objectui#9542 retired the narrowing + // own block), and the WRITE since `43c0d1710` retired the narrowing // assertion that stood here — `ActionDef['resultDialog']` now DERIVES // its label members from the contract instead of hand-writing them as // `string`, so the whole forward type-checks against one declared diff --git a/packages/components/src/renderers/action/action-icon.tsx b/packages/components/src/renderers/action/action-icon.tsx index e3b4c4ece7..db2a80f0fb 100644 --- a/packages/components/src/renderers/action/action-icon.tsx +++ b/packages/components/src/renderers/action/action-icon.tsx @@ -180,7 +180,7 @@ const ActionIconRenderer = forwardRef< // OAuth secret). Without it the runner falls back to the success // toast and the value the user was meant to copy is gone. The READ is // uncast since objectui#8648; the write-side narrowing that stood - // here went with objectui#9542, which made `ResultDialogSpec` derive + // here went with `43c0d1710`, which made `ResultDialogSpec` derive // its label members from the contract. ⛔ Never widen either end back // to `as any`. resultDialog: schema.resultDialog, diff --git a/packages/components/src/renderers/form/form.tsx b/packages/components/src/renderers/form/form.tsx index 735978c1c2..268af5d2fd 100644 --- a/packages/components/src/renderers/form/form.tsx +++ b/packages/components/src/renderers/form/form.tsx @@ -172,7 +172,7 @@ const BARE_SPAN_CLASS: Record = { /** * The col-span classes a field must carry to occupy `targetCols` cells of * `containerClass` — ONE CLASS PER TIER, not one class for the widest tier - * (objectui#9244). + * (`bd0995738`). * * The form's column count is resolved per tier, by container queries on the * field container (`grid-cols-1 @md:grid-cols-2 @2xl:grid-cols-3` is three @@ -2842,7 +2842,7 @@ ComponentRegistry.register('form', const containerClass = schema.fieldContainerClass || gridClass; // One class PER TIER, derived from the container class — see // {@link spanLadderFor} for why a single widest-tier class under-spans - // every intermediate width (objectui#9244), and for why the tier + // every intermediate width (`bd0995738`), and for why the tier // prefixes are load-bearing rather than decoration. const pickSpanClass = (targetCols: number): string => spanLadderFor(containerClass, targetCols); diff --git a/packages/components/src/renderers/layout/containers.tsx b/packages/components/src/renderers/layout/containers.tsx index 3e5d220a85..1289e3ab1b 100644 --- a/packages/components/src/renderers/layout/containers.tsx +++ b/packages/components/src/renderers/layout/containers.tsx @@ -2109,7 +2109,7 @@ const PageHeaderRenderer: React.FC = ({ schema, className, ...props }) => { // - the unified ADR-0079 resolver (type-aware derivation, once the two // rungs above have declined), // - that same resolver's record-key rung, but ONLY for an object that - // names no title field at all (objectui#10117), + // names no title field at all (`4c6f549ef`), // - `${objectLabel} ${id}` as a last-resort. // // ⭐ The declared pointer OUTRANKS `titleFormat` (objectui#9436, ruled C1). @@ -2186,7 +2186,7 @@ const PageHeaderRenderer: React.FC = ({ schema, className, ...props }) => { const resolved = getRecordDisplayName(objSchema, data, { deriveFromRecordKeys: false }); return isResolverFloor(resolved) ? '' : resolved; })(); - // objectui#10117 — the record-key safety net, and the two rules that make + // `4c6f549ef` — the record-key safety net, and the two rules that make // it safe. It used to be spelled out here as a raw // `data?.name || data?.full_name || data?.title || data?.subject || …` // chain: a SECOND implementation of the very question diff --git a/packages/components/src/renderers/layout/flex.tsx b/packages/components/src/renderers/layout/flex.tsx index 893a6b4e7f..36c216d20c 100644 --- a/packages/components/src/renderers/layout/flex.tsx +++ b/packages/components/src/renderers/layout/flex.tsx @@ -142,7 +142,7 @@ ComponentRegistry.register('flex', // eight lines from the top of this file never applies (`schema.align || // 'start'`). A designer-made node therefore laid out differently from a // hand-authored one that simply omitted the key: one component, two - // answers (objectui#8229, folded into objectui#7735's ruling). + // answers (the finding `8b7ea3945` reconciled, folded into objectui#7735's ruling). // // The renderer fallback is the single authoritative default under that // ruling — the zod mirror stopped authoring `align` in the same change, diff --git a/packages/core/src/actions/ActionRunner.ts b/packages/core/src/actions/ActionRunner.ts index d62f82a725..e418b95063 100644 --- a/packages/core/src/actions/ActionRunner.ts +++ b/packages/core/src/actions/ActionRunner.ts @@ -567,7 +567,7 @@ export type ParamCollectionHandler = ( * The contract's own result-dialog block, and one entry of its field list. * * ⭐ The two interfaces below DERIVE their label members from these instead of - * restating them, and that is the whole repair of objectui#9542: the three + * restating them, and that is the whole repair of `43c0d1710`: the three * label members were hand-written `string` while the producer declares each as * `I18nLabel` — a plain string **or** an inline per-locale map, both authorized * and neither deprecated — so this mirror refused what the platform accepts @@ -753,7 +753,7 @@ export interface ActionParamDef { * not apply either. A lookup param that should have rendered a record picker * renders an unannotated empty box instead — no options, no dropdown, and no * request for the referenced object on the wire, because no picker was ever - * built (objectui#10129). + * built (`6cc910b6d`). * * ⛔ It is not a widget config key and `paramToField()` deliberately does not * map it: the whole point is that the param's type is UNKNOWN, so there is no diff --git a/packages/core/src/utils/chart-presentation.ts b/packages/core/src/utils/chart-presentation.ts index 5db7c302af..31ba01bd56 100644 --- a/packages/core/src/utils/chart-presentation.ts +++ b/packages/core/src/utils/chart-presentation.ts @@ -87,7 +87,7 @@ const isRecord = (v: unknown): v is Record => * a string. * * ⚠️ Its one remaining caller is {@link seriesPresentation}'s `label`. The axis - * `title` read this too until objectui#10132 and no longer does — a slot whose + * `title` read this too until `061f5e829` and no longer does — a slot whose * value reaches the DOM unchanged cannot be served by a pick, and that one had * no overriding caller (see {@link axisPresentation}). * @@ -110,7 +110,7 @@ function labelText(v: unknown): string | undefined { /** * An authored `I18nLabel` that travels to the renderer **unresolved** — the * chart's own `title` / `subtitle` / `description` (objectui#9038), and an - * axis's `title` (objectui#10132, which moved that one arm off + * axis's `title` (`061f5e829`, which moved that one arm off * {@link labelText}; see {@link axisPresentation} for why it was never covered * by the ledger the pick rests on). * @@ -225,7 +225,7 @@ export function seriesPresentation(raw: Record): AuthoredSeries export function axisPresentation(raw: unknown): Record { const out: Record = {}; if (!isRecord(raw)) return out; - // `title` is FORWARDED, not picked (objectui#10132). `ChartAxisSchema.title` + // `title` is FORWARDED, not picked (`061f5e829`). `ChartAxisSchema.title` // is the spec's `I18nLabel`, and collapsing it here with `labelText` handed // the axis whichever limb the author happened to type FIRST — measured both // ways round on one map: English to a `zh-CN` viewer, Chinese to an `en` one. diff --git a/packages/core/src/utils/dashboard-filters.ts b/packages/core/src/utils/dashboard-filters.ts index 94bde5c484..2d3a0f24f2 100644 --- a/packages/core/src/utils/dashboard-filters.ts +++ b/packages/core/src/utils/dashboard-filters.ts @@ -66,7 +66,7 @@ export interface DashboardFilterDef { field: string; /** * The object `field` lives on, carried through from - * @objectstack/spec's `GlobalFilterSchema.object` (objectui#10132). + * @objectstack/spec's `GlobalFilterSchema.object` (`061f5e829`). * * The spec's own describe text for that key states the contract: "Object * whose `fields..` translation-bundle entry resolves this @@ -547,7 +547,7 @@ export function resolveDashboardFilterDefs( byName.set(name, { name, field: f.field, - // Spread rather than assigned (objectui#10132): every filter authored + // Spread rather than assigned (`061f5e829`): every filter authored // before this key existed keeps the exact key set it resolved to, so // `'object' in def` stays the discriminator the render side branches on // and no consumer sees a new own-property carrying `undefined`. diff --git a/packages/core/src/utils/normalize-list-view.ts b/packages/core/src/utils/normalize-list-view.ts index 3b6a6394f7..cfe989eec2 100644 --- a/packages/core/src/utils/normalize-list-view.ts +++ b/packages/core/src/utils/normalize-list-view.ts @@ -140,7 +140,7 @@ const ARIA_KEY_ALIASES: Record = { * A visualization `ListView` actually draws — one member per `case` in its * `viewComponentSchema` switch (`packages/plugin-list/src/ListView.tsx`). * - * DERIVED from {@link ViewType} (objectui#8127), which is itself derived from + * DERIVED from {@link ViewType} (`ca3942729`), which is itself derived from * `@objectstack/spec/ui` `ListView['type']`. Spelled as an `Exclude` of the * members this renderer does not draw rather than as the spec's own * `VisualizationType`, deliberately: `Exclude` leaves the SPEC's list as the @@ -153,7 +153,7 @@ const ARIA_KEY_ALIASES: Record = { * ⭐ What it subtracts is {@link UndrawableViewKind} — the keys of * {@link UNDRAWABLE_VIEW_KINDS} the current `ViewType` still carries — and * never a literal list of kinds (objectui#9880). A literal list is a second - * hand-written copy of the vocabulary, the failure class objectui#8127 removed + * hand-written copy of the vocabulary, the failure class `ca3942729` removed * from `ViewType` itself, and it rots in the direction the spec actually moved: * `Exclude` kept subtracting `'page'` * after objectstack RETIRED it, where `Exclude` of a non-member is a silent @@ -174,7 +174,7 @@ export type ListViewVisualization = Exclude; * union fails the build HERE instead of silently staying unreadable authored * input. * - * ⚠️ That promise was FALSE between `@objectstack/spec@17.3.0` and objectui#8127. + * ⚠️ That promise was FALSE between `@objectstack/spec@17.3.0` and `ca3942729`. * `ViewType` was a hand-written copy of the spec's list, so this map was total * over the copy and compiled green while the spec's `page` had nowhere to land. * Both faces are derived now, and the two totality structures below close the @@ -261,7 +261,7 @@ const warnedUndrawableKinds = new Set(); * Fail-open is **loud**, not silent — the convention this repo already applies * to unevaluable predicates (`../evaluator/fieldRules.ts`, objectstack#5149). * - * The bug objectui#8127 records is not only that `type: 'page'` degrades to a + * The bug `ca3942729` records is not only that `type: 'page'` degrades to a * grid; it is that it degrades IDENTICALLY to a typo. Measured on `5505aec`, * `specType: 'page'` and `specType: 'nonsense'` both left `viewType: 'grid'` * with no error, no warning and no console line — and `@object-ui/types`' @@ -292,7 +292,7 @@ function warnUndrawableViewKind(kind: string): void { * Exported so a read site cannot restate the membership question as its own * literal array: the gate and the seam must answer one question, which is the * rule this file's own `availableViews` notes already argue for (objectui#5042, - * objectui#7544, and now objectui#8127). + * objectui#7544, and now `ca3942729`). */ export function isListViewVisualization(value: unknown): value is ListViewVisualization { return typeof value === 'string' && Object.prototype.hasOwnProperty.call(LIST_VIEW_KINDS, value); diff --git a/packages/data-objectstack/src/index.ts b/packages/data-objectstack/src/index.ts index f2ff140246..5ae3c3fcff 100644 --- a/packages/data-objectstack/src/index.ts +++ b/packages/data-objectstack/src/index.ts @@ -2312,7 +2312,7 @@ export type ImportMappingsFailureKind = MetadataReadFailureKind; * a repo whose wizard had been correct since `@object-ui/data-objectstack` * 17.1.0), and the misdiagnosis travelled further than the fault would have. * - * This is the discrimination framework #13906 decision 1 option A already + * This is the discrimination framework objectstack#13906 decision 1 option A already * adopted at the tenancy-posture seam — *a thing that could not be READ is not * a thing that is ABSENT* — applied here. It is not a new principle. * @@ -2459,7 +2459,7 @@ export function classifyImportMappingsFailure(error: unknown): { * a published-mode one instead of falling through to the code-less residual. * What it must never read is "is the result an empty array": that is what BOTH * a served-zero and a refusal produce, so a test on it can never fail for the - * condition it is supposed to be about. This is framework #13906 decision 1 + * condition it is supposed to be about. This is framework objectstack#13906 decision 1 * option A — *a thing that could not be READ is not a thing that is ABSENT*. */ export function classifyViewsFailure(error: unknown): { @@ -5394,7 +5394,7 @@ export class ObjectStackAdapter implements DataSource { * ⛔ The RETURN is untouched. This method has answered `Promise`, never * throwing, since `@object-ui/data-objectstack@17.1.0`; a consumer that reads * nothing new sees exactly what it saw before, including on the loud arms. - * Applying framework #13906 decision 1 option A — *a thing that could not be + * Applying framework objectstack#13906 decision 1 option A — *a thing that could not be * READ is not a thing that is ABSENT* — is done by ADDING a channel, not by * moving that contract. */ @@ -5537,7 +5537,7 @@ export class ObjectStackAdapter implements DataSource { * ⛔ The RETURN is untouched. This method has answered `Promise`, never * throwing, since `@object-ui/data-objectstack@17.1.0`; a consumer that reads * nothing new sees exactly what it saw before, including on the loud arms. - * Applying framework #13906 decision 1 option A — *a thing that could not be + * Applying framework objectstack#13906 decision 1 option A — *a thing that could not be * READ is not a thing that is ABSENT* — is done by ADDING a channel, not by * moving that contract. */ diff --git a/packages/fields/src/widgets/LookupField.tsx b/packages/fields/src/widgets/LookupField.tsx index 33db2c606e..e062a97595 100644 --- a/packages/fields/src/widgets/LookupField.tsx +++ b/packages/fields/src/widgets/LookupField.tsx @@ -1401,7 +1401,7 @@ export function LookupField({ value, onChange, field, readonly, error: fieldErro * picker trigger and the browse button while leaving the chip's ✕ live, so * the one control that could still CHANGE the value was the one control the * gate had missed — a reporter could clear a master-detail parent the server - * would then refuse to unset (objectui#10120). ⭐ A refusal the UI invites is + * would then refuse to unset (`80c54122e`). ⭐ A refusal the UI invites is * worse than a refusal it prevents: the chips stay, the affordance goes. */ const chipsRemovable = !props.disabled; diff --git a/packages/layout/src/NavigationRenderer.tsx b/packages/layout/src/NavigationRenderer.tsx index 3a90108f7e..a3a59d3eb8 100644 --- a/packages/layout/src/NavigationRenderer.tsx +++ b/packages/layout/src/NavigationRenderer.tsx @@ -391,7 +391,7 @@ export interface NavigationVisibilityOptions { * Extracted because the sequence had been written out three times — in * `NavigationItemRenderer`, in `hasVisibleNavigationItems`, and nowhere at all * in `collectPinnedItems`, which is how the Favorites section came to render an - * entry out of a subtree the same guards had already removed (objectui#10119). + * entry out of a subtree the same guards had already removed (`73a3c89af`). * A gate that holds on one path into a subtree and not on another is the * authoring trap this predicate exists to prevent, so the three callers share * the statement rather than agreeing about it. @@ -1084,7 +1084,7 @@ function NavigationItemRenderer({ .slice() .sort((a, b) => (a.order ?? 0) - (b.order ?? 0)); - // A group survives only through its children (objectui#10119). Without + // A group survives only through its children (`73a3c89af`). Without // this the group's own label rendered as a disclosure that opens onto // nothing once every child was gated away — and it contradicted // `hasVisibleNavigationItems`, which already scores such a group as @@ -1506,7 +1506,7 @@ function collectPinnedItems( ): NavigationItem[] { const pinned: NavigationItem[] = []; for (const item of items) { - // A gated-away node takes its whole subtree with it (objectui#10119). + // A gated-away node takes its whole subtree with it (`73a3c89af`). // This walk is a SECOND path into the same children, so without the guard // an author who gated a group watched a pinned descendant keep rendering // under Favorites — the group's `visible` predicate evaluated, answered diff --git a/packages/plugin-dashboard/src/DashboardFilterBar.tsx b/packages/plugin-dashboard/src/DashboardFilterBar.tsx index 010cd9583f..9d93a32b56 100644 --- a/packages/plugin-dashboard/src/DashboardFilterBar.tsx +++ b/packages/plugin-dashboard/src/DashboardFilterBar.tsx @@ -65,7 +65,7 @@ import { * TRANSLATED "Date range", the others to the raw `def.name`. Folding those * together here would have made an unlabelled date filter read `dateRange`. * - * ## The translation-bundle rung (objectui#10132) + * ## The translation-bundle rung (`061f5e829`) * * `GlobalFilterSchema.object` names the object whose bundle entry keys this * filter's labels — the spec's describe text for it reads "Object whose @@ -503,7 +503,7 @@ function SelectFilter({ def, value, onChange, dataSource }: { def: DashboardFilt return authored.map((o) => ({ value: o.value, label: pickLocalized(o.label, language) || o.value })); }, [def.options, dynamicOptions, language]); - // The second half of `GlobalFilterSchema.object` (objectui#10132): the spec + // The second half of `GlobalFilterSchema.object` (`061f5e829`): the spec // gives that key BOTH the field label and the option labels, and // `translateOptions` is the same convention's option resolver — the one lists // and forms call, keyed by option VALUE under the field this filter reads. diff --git a/packages/plugin-designer/src/MetadataFieldsPage.tsx b/packages/plugin-designer/src/MetadataFieldsPage.tsx index 4e39a62b97..7e776b5181 100644 --- a/packages/plugin-designer/src/MetadataFieldsPage.tsx +++ b/packages/plugin-designer/src/MetadataFieldsPage.tsx @@ -612,7 +612,7 @@ function describeUnusableTarget(reference: unknown): string { * ## The `.trim()` MIRRORS the contract — it is no longer a local opinion * * The predicate is `typeof reference === 'string' && reference.trim() !== ''`. - * It WAS a declared divergence when written: 17.3.0's #13632 refinement spelled + * It WAS a declared divergence when written: 17.3.0's objectstack#13632 refinement spelled * its emptiness test as an equality against `''`, so the spec accepted a * whitespace-only target while this page refused it. objectstack#16920 (merged * 2026-09-08, closing objectstack#16126) applies that test to the TRIMMED value diff --git a/packages/plugin-detail/src/DetailView.tsx b/packages/plugin-detail/src/DetailView.tsx index 858d0a9002..393ac7ef81 100644 --- a/packages/plugin-detail/src/DetailView.tsx +++ b/packages/plugin-detail/src/DetailView.tsx @@ -233,7 +233,7 @@ export interface DetailViewProps { * it and `SchemaErrorBoundary` blanked the whole `record:details` component — * one authored key erasing every sibling section on the page (objectui#8497). * The crash predates the key it fires on: it arrived with `e99770841` - * (2026-05-01), months before #13855 declared `group`, so it was never a + * (2026-05-01), months before objectstack#13855 declared `group`, so it was never a * deliberate refusal of anything. */ const sectionFieldEntries = (sections: any[] | undefined): any[] => diff --git a/packages/plugin-detail/src/renderers/record-details.tsx b/packages/plugin-detail/src/renderers/record-details.tsx index 3bdd60f86f..0931535011 100644 --- a/packages/plugin-detail/src/renderers/record-details.tsx +++ b/packages/plugin-detail/src/renderers/record-details.tsx @@ -747,7 +747,7 @@ export const RecordDetailsRenderer: React.FC = ({ // objection was to the AUTHORING // SHAPE — every application hand-writing `hideEmpty: false` per section // as per-app tax — and it was made KNOWING the key was declared - // upstream (objectstack PR #11662); the deliverable was a sparse record + // upstream (PR objectstack#11662); the deliverable was a sparse record // keeping a full structural skeleton with zero app-side authoring. The // spec-refusal reading came later, out of that card's own execution, // and was routed to objectui#7129; ⛔ it was not this ruling's ground. diff --git a/packages/plugin-form/src/DrawerForm.tsx b/packages/plugin-form/src/DrawerForm.tsx index 7f4c1f5c71..881ed952da 100644 --- a/packages/plugin-form/src/DrawerForm.tsx +++ b/packages/plugin-form/src/DrawerForm.tsx @@ -229,7 +229,7 @@ export const DrawerForm: React.FC = ({ * objectui#10612): FLS drops non-readable fields and locks non-editable ones, * and the ADR-0092 D4 managed-object lock disables every field when the * object's affordance for the mode is closed. The drawer used to carry - * neither half: FLS joined in objectui#10120, the lock in objectui#10612 — + * neither half: FLS joined in `80c54122e`, the lock in objectui#10612 — * before it, a managed object drew live inputs here. */ const gateFields = useCallback( @@ -533,7 +533,7 @@ export const DrawerForm: React.FC = ({ let result; // FLS defence-in-depth, inside the ONE outbound filter: react-hook-form // retains state for unmounted/disabled fields, so the render gate above - // is not enough on its own (objectui#10120). + // is not enough on its own (`80c54122e`). const payload = sanitizeFormData(data, objectSchema, { canEdit: fieldWriteGate(perms, schema.objectName), }); diff --git a/packages/plugin-form/src/ModalForm.tsx b/packages/plugin-form/src/ModalForm.tsx index f6f8f3a642..1dd7131a2e 100644 --- a/packages/plugin-form/src/ModalForm.tsx +++ b/packages/plugin-form/src/ModalForm.tsx @@ -258,7 +258,7 @@ export const ModalForm: React.FC = ({ const [objectSchema, setObjectSchema] = useState(null); // The ONE field-gate step every layout draws through (`gateFormFields`, // objectui#10612): FLS drops non-readable fields and disables non-editable - // ones (objectui#10120; fail-open when no PermissionProvider is mounted), and + // ones (`80c54122e`; fail-open when no PermissionProvider is mounted), and // the ADR-0092 D4 managed-object lock disables every field when the object's // affordance for the mode is closed — before objectui#10612 a managed object // drew live inputs here. @@ -585,7 +585,7 @@ export const ModalForm: React.FC = ({ // FLS defence-in-depth, inside the ONE outbound filter: react-hook-form // retains state for unmounted/disabled fields, so the gate above is not // enough on its own — but the verdict is the same resolver's, adapted by - // `fieldWriteGate` rather than copied here (objectui#10120). + // `fieldWriteGate` rather than copied here (`80c54122e`). const payload = sanitizeFormData(data, objectSchema, { canEdit: fieldWriteGate(perms, schema.objectName), }); diff --git a/packages/plugin-form/src/ObjectForm.tsx b/packages/plugin-form/src/ObjectForm.tsx index fcf4f511d6..1ed1739cc0 100644 --- a/packages/plugin-form/src/ObjectForm.tsx +++ b/packages/plugin-form/src/ObjectForm.tsx @@ -246,7 +246,7 @@ export const ObjectForm: React.FC = ({ if (!perms?.isLoaded) return withGroups(base); // ONE render gate, shared with `ModalForm` and `DrawerForm` — see // `fieldWriteGate.ts` for why the copy each container used to carry is the - // defect rather than the style (objectui#10120). + // defect rather than the style (`80c54122e`). const filterArr = (arr?: any[]) => applyFieldPermissions(arr, { perms, objectName: base.objectName, mode: base.mode }); return withGroups({ @@ -1155,7 +1155,7 @@ const SimpleObjectForm: React.FC = ({ // `tabbed`, `split` and `wizard` layouts (objectui#10563): strip what a form never // writes (server-owned, computed, read-only, unknown to the object, and // refused by the caller's field-level security through `fieldWriteGate`, - // objectui#10108 / objectui#10120), omit the producer-owned defaults on a + // objectui#10108 / `80c54122e`), omit the producer-owned defaults on a // create (#4069), and on an EDIT keep only the fields that differ from the // record this form read (objectui#10156). `writePayload` goes to BOTH write // routes below: the host-owned seam, which is how a master-detail form's diff --git a/packages/plugin-form/src/SplitForm.tsx b/packages/plugin-form/src/SplitForm.tsx index 05c5486ad6..53276ef764 100644 --- a/packages/plugin-form/src/SplitForm.tsx +++ b/packages/plugin-form/src/SplitForm.tsx @@ -319,7 +319,7 @@ export const SplitForm: React.FC = ({ }); // The ONE field-gate step every layout draws through (`gateFormFields`, // objectui#10612), applied to the RESOLVED fields. Its field-level half - // (objectui#10120): `ObjectForm` gates a section's field OBJECTS before + // (`80c54122e`): `ObjectForm` gates a section's field OBJECTS before // routing here, but a field named by a bare string has no `name` to ask // about until it is resolved just above. Without this pass a field the // caller may read but not edit rendered as a live input, and with the diff --git a/packages/plugin-form/src/TabbedForm.tsx b/packages/plugin-form/src/TabbedForm.tsx index 33f696d954..048cc0b598 100644 --- a/packages/plugin-form/src/TabbedForm.tsx +++ b/packages/plugin-form/src/TabbedForm.tsx @@ -420,7 +420,7 @@ export const TabbedForm: React.FC = ({ }); // The ONE field-gate step every layout draws through (`gateFormFields`, // objectui#10612), applied to the RESOLVED fields. Its field-level half - // (objectui#10120): `ObjectForm` gates a section's field OBJECTS before + // (`80c54122e`): `ObjectForm` gates a section's field OBJECTS before // routing here, but a field named by a bare string has no `name` to ask // about until it is resolved just above. Without this pass a field the // caller may read but not edit rendered as a live input, and with the diff --git a/packages/plugin-form/src/WizardForm.tsx b/packages/plugin-form/src/WizardForm.tsx index 8d9a737816..5ebead8144 100644 --- a/packages/plugin-form/src/WizardForm.tsx +++ b/packages/plugin-form/src/WizardForm.tsx @@ -673,7 +673,7 @@ export const WizardForm: React.FC = ({ }); // The ONE field-gate step every layout draws through (`gateFormFields`, // objectui#10612), applied to the RESOLVED fields. Its field-level half - // (objectui#10120): `ObjectForm` gates a section's field OBJECTS before + // (`80c54122e`): `ObjectForm` gates a section's field OBJECTS before // routing here, but a field named by a bare string has no `name` to ask // about until it is resolved just above. Without this pass a field the // caller may read but not edit rendered as a live input, and with the diff --git a/packages/plugin-form/src/autoLayout.ts b/packages/plugin-form/src/autoLayout.ts index 634f120185..7e99a05668 100644 --- a/packages/plugin-form/src/autoLayout.ts +++ b/packages/plugin-form/src/autoLayout.ts @@ -139,7 +139,7 @@ function clampGrid(n: number | undefined): number { * ⚠️ «Whole row» is a claim about what RENDERS, and this function only returns * a number. It is kept by the form renderer's `spanLadderFor`, which turns * that number into one col-span class per container-query tier. Until - * objectui#9244 the renderer emitted a single class for the widest tier only, + * `bd0995738` the renderer emitted a single class for the widest tier only, * so this sentence was true of the return value and false on screen at every * intermediate width. ⛔ A pin under this function cannot observe that — see * `__tests__/spanFullTierLadder-9244.test.tsx` for the leg that can. diff --git a/packages/plugin-form/src/fieldWriteGate.ts b/packages/plugin-form/src/fieldWriteGate.ts index a5350a45e8..50b199b5a0 100644 --- a/packages/plugin-form/src/fieldWriteGate.ts +++ b/packages/plugin-form/src/fieldWriteGate.ts @@ -8,7 +8,7 @@ /** * 「May this caller edit this field?」 — asked once, of the resolver that owns - * the answer (objectui#10120). + * the answer (`80c54122e`). * * ## The one answer, and where it lives * diff --git a/packages/plugin-form/src/sanitize.ts b/packages/plugin-form/src/sanitize.ts index 9c115af88c..754695ce15 100644 --- a/packages/plugin-form/src/sanitize.ts +++ b/packages/plugin-form/src/sanitize.ts @@ -119,7 +119,7 @@ const COMPUTED_FIELD_TYPES = new Set([ * predicate. It arrives here, at the single outbound filter, rather than as a * strip loop after each container's call, because every such loop is a copy * that can be forgotten — and one of the three containers had forgotten it - * (objectui#10120). + * (`80c54122e`). */ export function sanitizeFormData( data: Record, diff --git a/packages/plugin-form/src/writePayload.ts b/packages/plugin-form/src/writePayload.ts index 097d2a6d58..07c46ba70c 100644 --- a/packages/plugin-form/src/writePayload.ts +++ b/packages/plugin-form/src/writePayload.ts @@ -55,7 +55,7 @@ export interface FormWritePayloads { * 1. `sanitizeFormData` drops what a form never writes — server-owned columns * (objectui#10108), computed / formula / read-only columns, keys the object * does not declare, and every field the caller's field-level security - * refuses (`canEdit`, objectui#10120). + * refuses (`canEdit`, `80c54122e`). * 2. On a create, `omitServerResolvedDefaults` drops an empty field whose * runtime default the producer resolves (#4069). * 3. On an edit, `dirtyEditPayload` keeps only the fields that differ from the diff --git a/packages/plugin-grid/src/rowCrudAffordances.ts b/packages/plugin-grid/src/rowCrudAffordances.ts index 0162b5f494..d36439321a 100644 --- a/packages/plugin-grid/src/rowCrudAffordances.ts +++ b/packages/plugin-grid/src/rowCrudAffordances.ts @@ -33,7 +33,7 @@ * `Delete` action, and the generic entries would otherwise render a * confusing duplicate); * c. [objectstack#3720] the SERVER's effective API operation set for the object - * (`/me/permissions` `apiOperations`, #3391) — `edit` is ANDed with + * (`/me/permissions` `apiOperations`, objectstack#3391) — `edit` is ANDed with * `update` and `delete` with `delete`, so a row never offers a * mutation the server would reject. * @@ -181,7 +181,7 @@ export function resolveRowCrudAffordances(opts: { userActions?: { edit?: RowCrudUserAction; delete?: RowCrudUserAction } | null; /** * [objectstack#3720] The server-resolved effective API operation set for this object - * (`/me/permissions` `apiOperations`, #3391). `undefined` / `null` (old + * (`/me/permissions` `apiOperations`, objectstack#3391). `undefined` / `null` (old * backend, unrestricted object, no provider) leaves the object verdict * untouched; an empty array means "expose nothing" → both entries hidden. */ diff --git a/packages/plugin-kanban/src/KanbanImpl.tsx b/packages/plugin-kanban/src/KanbanImpl.tsx index 6ecfdd141e..894f27d8e7 100644 --- a/packages/plugin-kanban/src/KanbanImpl.tsx +++ b/packages/plugin-kanban/src/KanbanImpl.tsx @@ -146,7 +146,7 @@ export interface KanbanBoardProps { swimlaneField?: string /** * The cards in `columns` are a fetched WINDOW, not the whole group - * (objectui#8307) — see `laneCountLabel`. Injected by `ObjectKanban`, the + * (`5591f03bd`) — see `laneCountLabel`. Injected by `ObjectKanban`, the * only entry point that issues the windowed query; a board handed its rows * whole leaves it unset and keeps the bare number. */ @@ -454,7 +454,7 @@ function LaneHeading({ * the window that number is not the size of the group: every lane is short, * the lanes sum to the window, and the result is CREDIBLE — 77 / 19 / 2 reads * as a plausible funnel against a true 88 / 46 / 28 / 14 / 9 / 15, so nothing - * prompts the reader to distrust it (objectui#8307). + * prompts the reader to distrust it (`5591f03bd`). * * The fix is not a better number — this component does not have one, and * getting one means a server-side group-count aggregate over the whole @@ -513,7 +513,7 @@ function KanbanColumnView({ * doesn't read as N redundant copies of the same message. */ suppressEmptyPlaceholder?: boolean - /** The cards handed in are a fetched window — see `laneCountLabel` (objectui#8307). */ + /** The cards handed in are a fetched window — see `laneCountLabel` (`5591f03bd`). */ countsAreWindowed?: boolean /** * Whether this lane renders collapsed right now — the authored diff --git a/packages/plugin-kanban/src/ObjectKanban.tsx b/packages/plugin-kanban/src/ObjectKanban.tsx index 52688002f0..2fb0c8a0bd 100644 --- a/packages/plugin-kanban/src/ObjectKanban.tsx +++ b/packages/plugin-kanban/src/ObjectKanban.tsx @@ -403,7 +403,7 @@ export const ObjectKanban: React.FC = ({ const [fetchedData, setFetchedData] = useState([]); /** * Did the last fetch come back SATURATED — as many rows as the window - * allowed (objectui#8307)? + * allowed (`5591f03bd`)? * * The fetch below is windowed at a real `$top` (objectui#4025). The board * then groups WHAT CAME BACK into lanes client-side, so every lane header @@ -685,13 +685,13 @@ export const ObjectKanban: React.FC = ({ // or a bound view's `pagination.pageSize`) can set it. // // The query is a NAMED OBJECT rather than an inline literal so that - // the saturation reading below (objectui#8307) compares the row + // the saturation reading below (`5591f03bd`) compares the row // count against `query.$top` — the very number this request // carried. One spelling of the window, read back from the request // itself: a second `resolveRowLimit(schema.limit, …)` kept in a // local for the comparison could drift from the one on the wire, // and a marker computed against a window the server was never asked - // for is exactly the silent wrongness objectui#8307 is about. That + // for is exactly the silent wrongness `5591f03bd` fixed. That // reasoning is why objectui#9925's refusal was put INSIDE this // named object rather than beside it: the resolver runs once, and // the saturation reading keeps reading the number that left. @@ -719,7 +719,7 @@ export const ObjectKanban: React.FC = ({ if (isMounted) { setFetchedData(data); - // objectui#8307 — see `fetchWindowSaturated`. Recorded HERE, + // `5591f03bd` — see `fetchWindowSaturated`. Recorded HERE, // against the window THIS request carried, because that is the // only point where the two numbers are both in hand. setFetchWindowSaturated(data.length >= query.$top); @@ -768,7 +768,7 @@ export const ObjectKanban: React.FC = ({ const rawData = (hasExternalData ? externalData : undefined) || boundData || schema.data || fetchedData; /** - * Are the lane counts about to be drawn counts of a WINDOW (objectui#8307)? + * Are the lane counts about to be drawn counts of a WINDOW (`5591f03bd`)? * * Only when the rows on screen are the ones this component fetched. External, * bound and inline data arrive whole from whoever owns them; this board @@ -1612,7 +1612,7 @@ export const ObjectKanban: React.FC = ({ onCardMove={handleCardMove} schema={{ ...effectiveSchema, - // objectui#8307 — the lane headers count rows that came back, so when + // `5591f03bd` — the lane headers count rows that came back, so when // the fetch saturated its window they must say `77+`, not `77`. countsAreWindowed, // ⛔ Calls `handleClick` and NOTHING ELSE. An authored `onCardClick` diff --git a/packages/plugin-kanban/src/index.tsx b/packages/plugin-kanban/src/index.tsx index ee45a6a6ca..dff6d6fcb6 100644 --- a/packages/plugin-kanban/src/index.tsx +++ b/packages/plugin-kanban/src/index.tsx @@ -247,7 +247,7 @@ export interface KanbanRendererProps { conditionalFormatting?: KanbanConditionalFormattingRule[]; /** * The lane counts below are counts of a fetched WINDOW, not of the group - * (objectui#8307). Injected by `ObjectKanban`, the only entry point that + * (`5591f03bd`). Injected by `ObjectKanban`, the only entry point that * issues the windowed `$top` query and can therefore know the answer; * `ObjectKanban` supplies nothing on the schema-only `kanban-ui` entry, * whose `data` arrives whole from its author and whose counts are complete diff --git a/packages/plugin-list/src/ListView.tsx b/packages/plugin-list/src/ListView.tsx index cb8e884042..b401c18e24 100644 --- a/packages/plugin-list/src/ListView.tsx +++ b/packages/plugin-list/src/ListView.tsx @@ -2910,7 +2910,7 @@ export const ListView = React.forwardRef(({ // Always allow switching back to the viewType defined in schema — but only // when it names a visualization this renderer actually draws. // - // The membership test was a nine-name literal array (objectui#8127), a copy + // The membership test was a nine-name literal array (`ca3942729`), a copy // of `LIST_VIEW_KINDS` in `@object-ui/core` that nothing compared against // it. `isListViewVisualization` IS that map's own predicate, so the gate and // the seam answer one question — the same rule the kanban/chart rungs above @@ -3033,7 +3033,7 @@ export const ListView = React.forwardRef(({ // // `label` is an `I18nLabel`, so it is a string OR an inline locale map, and // the map has to be resolved BEFORE it reaches the interpolation options - // (objectui#9373): `createSafeTranslation`'s options bag is a record of + // (`c1006ed8e`): `createSafeTranslation`'s options bag is a record of // `unknown`, so a raw map is accepted without a diagnostic and both // interpolators stringify it — the heading read `[object Object] Detail`. // This is the same resolution the view label and the nested `aria` bag diff --git a/packages/plugin-list/src/ViewSwitcher.tsx b/packages/plugin-list/src/ViewSwitcher.tsx index 5111fc565a..173a178496 100644 --- a/packages/plugin-list/src/ViewSwitcher.tsx +++ b/packages/plugin-list/src/ViewSwitcher.tsx @@ -27,11 +27,11 @@ import { /** * The visualizations this switcher offers. * - * DERIVED from `@object-ui/core`'s {@link ListViewVisualization} (objectui#8127), + * DERIVED from `@object-ui/core`'s {@link ListViewVisualization} (`ca3942729`), * which derives from `@objectstack/spec/ui` through `@object-ui/types`' `ViewType`. * * This nine-arm union was written out by hand here, and that is a THIRD - * re-declaration of one vocabulary — objectui#8127 was filed against the two in + * re-declaration of one vocabulary — the card behind `ca3942729` was filed against the two in * `@object-ui/types`, and the maps below were described there as total over the * shared union when they were in fact total over this private copy. A map that * is total over a copy is exactly as incomplete as the copy, so the two diff --git a/packages/plugin-list/src/index.tsx b/packages/plugin-list/src/index.tsx index c9cb61c936..32d1d73ced 100644 --- a/packages/plugin-list/src/index.tsx +++ b/packages/plugin-list/src/index.tsx @@ -37,7 +37,7 @@ export type { ViewSwitcherProps, ViewType } from './ViewSwitcher'; * Designer label per visualization, and the `viewType` enum options both * registrations below are built from. * - * A total `Record` (objectui#8127). The two + * A total `Record` (`ca3942729`). The two * registrations each carried their own seven-entry `{ label, value }` literal, * compared against nothing — so both had drifted, missing `chart` and `tree`, * which `ListView` has drawn for releases. Deriving the options from one total diff --git a/packages/plugin-view/src/ObjectView.tsx b/packages/plugin-view/src/ObjectView.tsx index b3891b799c..06cf3dd947 100644 --- a/packages/plugin-view/src/ObjectView.tsx +++ b/packages/plugin-view/src/ObjectView.tsx @@ -1595,7 +1595,7 @@ export const ObjectView: React.FC = ({ // `chart-gantt` → `ChartGantt` both resolve, and both agree with the // components `ViewSwitcher.DEFAULT_VIEW_ICONS` names for the same view // types. Every value here is pinned by `ViewSwitcher.test.tsx`. - // `page` (objectui#8127): keyed on the FULL `ViewType`, which + // `page` (`ca3942729`): keyed on the FULL `ViewType`, which // `@objectstack/spec@17.3.0` widened. `layout-template` is the kebab // spelling of the `LayoutTemplate` this map's consumer — // `ViewSwitcher.DEFAULT_VIEW_ICONS` — draws for the same view type, and diff --git a/packages/plugin-view/src/ViewSwitcher.tsx b/packages/plugin-view/src/ViewSwitcher.tsx index b7551fc9d7..90f6036004 100644 --- a/packages/plugin-view/src/ViewSwitcher.tsx +++ b/packages/plugin-view/src/ViewSwitcher.tsx @@ -74,7 +74,7 @@ export type ViewSwitcherProps = { * Record` keeps the value constraint, drops the exactness, and lets * one spelling compile against both specs; this alias puts the added-member * half back, because `satisfies Record` alone would silently drop - * the guard objectui#5321 and objectui#8127 installed. + * the guard objectui#5321 and `ca3942729` installed. * * ⛔ NOT objectui#9880's repair either. That one derives the undrawable half of * a PARTITION with `Extract`, so a retired row @@ -87,7 +87,7 @@ export type ViewSwitcherProps = { */ type _AssertNever = T; -// `page` (objectui#8127): this switcher is keyed on the FULL `ViewType`, not on +// `page` (`ca3942729`): this switcher is keyed on the FULL `ViewType`, not on // the visualization subset, because `schema.views` may name any view type the // spec allows — and `@objectstack/spec@17.3.0` added `page`. Unlike the // visualization switcher in `plugin-list`, a missing entry here is a label diff --git a/packages/types/src/designer.ts b/packages/types/src/designer.ts index d9a968d276..39f9bc3d5b 100644 --- a/packages/types/src/designer.ts +++ b/packages/types/src/designer.ts @@ -416,7 +416,7 @@ export interface ViewColumnConfig { /** * View type union for the designer's view-configuration panel. * - * DERIVED from `@objectstack/spec/ui` `VisualizationType` (objectui#8127) — the + * DERIVED from `@objectstack/spec/ui` `VisualizationType` (`ca3942729`) — the * spec's own union of the visualizations a user can switch between, and the one * `AppearanceConfig.allowedVisualizations` is typed on. The hand-written * eight-arm union this replaces had already drifted: it was missing `tree`, diff --git a/packages/types/src/disclosure.ts b/packages/types/src/disclosure.ts index 531b790c30..87eff72684 100644 --- a/packages/types/src/disclosure.ts +++ b/packages/types/src/disclosure.ts @@ -210,7 +210,7 @@ export interface CollapsibleSchema extends BaseSchema { * then hands control back to the user, which is a different capability. * * ⚠️ Retiring this declaration does NOT by itself stop the takeover — the - * render path runs no `safeParse` (objectui#9585 measured NOT GATED), so a + * render path runs no `safeParse` (`ee70287e4`'s pin measures it NOT GATED), so a * refused key still rides the spread. The renderer-side named exclusion * landed FIRST, in the same change, for exactly that reason; ⛔ do not remove * it on the strength of this tombstone. diff --git a/packages/types/src/views.ts b/packages/types/src/views.ts index 7392666118..3651274d2b 100644 --- a/packages/types/src/views.ts +++ b/packages/types/src/views.ts @@ -26,7 +26,7 @@ import type { ListView as SpecListView } from '@objectstack/spec/ui'; * objectui view CATEGORIES that are not list-view types at all. * * DERIVED from `@objectstack/spec/ui` `ListView['type']`, never re-declared - * (objectui#8127). The eleven-arm hand-written union this replaces was total + * (`ca3942729`). The eleven-arm hand-written union this replaces was total * over a copy of the spec's list of 17.2.0, and `@objectstack/spec@17.3.0` * added `page` to that list. Nothing went red, because every structure keyed * on this union was total over the COPY — a total map is only as honest as the @@ -77,7 +77,7 @@ export type ViewType = NonNullable | 'list' | 'detail'; * * It had already drifted when it landed. The copy declared a fifth key, * `titleField`, which `@objectstack/spec@17.4.0` REFUSES on `ListView.tree` by - * name — `TreeConfigSchema` is a `strictObject` since spec #15469 closed the + * name — `TreeConfigSchema` is a `strictObject` since spec objectstack#15469 closed the * `.passthrough()` window 17.3.0 left open. So this package's published face * accepted what the protocol rejects, and an author who followed this type was * refused at publish. 协议为基准: this type now IS the spec's, so the next key @@ -440,7 +440,7 @@ export interface DetailViewSection { * ## Provenance * * Declared by `@objectstack/spec` on the `record:details` section entry - * (`RecordDetailsProps.sections[]`, 17.3.0+, upstream #11289, maintainer + * (`RecordDetailsProps.sections[]`, 17.3.0+, upstream objectstack#11289, maintainer * ruling 2026-08-23 direction 1). objectui#7129 (maintainer 2026-09-01) * retired this declaration and the renderer read on the premise that the * spec REFUSED the key — true at the 17.2.0 pin, false upstream by the time diff --git a/packages/types/src/zod/base.zod.ts b/packages/types/src/zod/base.zod.ts index fc05888c0c..407fa98702 100644 --- a/packages/types/src/zod/base.zod.ts +++ b/packages/types/src/zod/base.zod.ts @@ -788,7 +788,7 @@ export const HTMLAttributesSchema = z.record(z.string(), z.any()).describe('HTML * nothing reads `schema.events`: whatever a document writes under that key, * no renderer runs it. `BaseSchemaCore` is `.passthrough()`, so such a node is * KEPT, judged by nothing and run by nothing. ⛔ Do not send an author there. - * ⛔ No count of authored `events` keys is stated here — objectui#9553 carries + * ⛔ No count of authored `events` keys is stated here — a separate card carried * that census. A census answer frozen into a comment is the defect AGENTS.md * commandment #9 forbids, and this note shipped one once already: the first * version of this paragraph named a total that was wrong on the day it was @@ -801,7 +801,7 @@ export const HTMLAttributesSchema = z.record(z.string(), z.any()).describe('HTML * exempts index signatures by design ("no keys to compare"). This note, not a * gate, is what records the absence. * - * Precedent of the same shape: objectstack#12009 / PR #13413. + * Precedent of the same shape: objectstack#12009 / objectstack `89448a52b`. */ /** diff --git a/packages/types/src/zod/form.zod.ts b/packages/types/src/zod/form.zod.ts index d78c187c47..b46ba51270 100644 --- a/packages/types/src/zod/form.zod.ts +++ b/packages/types/src/zod/form.zod.ts @@ -243,9 +243,9 @@ export const InputSchema = BaseSchema.extend({ // `cn("grid w-full items-center gap-1.5", schema.wrapperClass)` — and the TS // face has declared it all along; the mirror had not, so a wrong-typed value // rode through `.passthrough()` unexamined while all eight sibling readers - // refused it at the key (objectui#8072). + // refused it at the key (`c974edf14`). wrapperClass: z.string().optional() - .describe('Classes on the wrapper div around the input and its label (objectui#8072)'), + .describe('Classes on the wrapper div around the input and its label'), min: z.number().optional().describe('Minimum value (for number type)'), max: z.number().optional().describe('Maximum value (for number type)'), step: z.number().optional().describe('Step value (for number type)'), @@ -1093,7 +1093,7 @@ export const InputShorthandSchema = InputSchema.omit({ type: true, inputType: tr inputType: retirementTombstone(SHORTHAND_INPUT_TYPE_REFUSAL), // ⭐ `wrapperClass` used to be RE-DECLARED here, because {@link InputSchema} // did not carry it and copying the gap into a NEW pair would have minted a - // second parity-ledger row for a key that is demonstrably read. objectui#8072 + // second parity-ledger row for a key that is demonstrably read. `c974edf14` // shrank that row instead: the key is on {@link InputSchema} now, so this arm // inherits it through the `.omit()` above like every other key, and the // restatement is gone rather than left standing as a duplicate of it. diff --git a/packages/types/src/zod/views.zod.ts b/packages/types/src/zod/views.zod.ts index 93ee06fbac..4ce32a4737 100644 --- a/packages/types/src/zod/views.zod.ts +++ b/packages/types/src/zod/views.zod.ts @@ -46,7 +46,7 @@ const SpecListViewTypeEnum = SpecListViewSchema.shape.type.removeDefault(); /** * View Type Schema — the zod face of {@link ViewType} (`../views.ts`). * - * DERIVED from `@objectstack/spec/ui` `ListViewSchema.type` (objectui#8127), so + * DERIVED from `@objectstack/spec/ui` `ListViewSchema.type` (`ca3942729`), so * the two faces cannot drift from the spec or from each other. Both were * hand-written eleven-arm copies of the spec's 17.2.0 list; `@objectstack/spec@17.3.0` * added `page` and neither followed, while `ViewKindEnum` in `./objectql.zod.ts`