Skip to content

chore(objectui): bump the console pin to dd3f7e1be356 (carries the injected-client boot fix) with the showcase div→box and trash-icon follow-through - #20436

Merged
hotlong merged 10 commits into
mainfrom
claude/objectui-update-local-test-915c35
Sep 28, 2026
Merged

hotlong merged 10 commits into
mainfrom
claude/objectui-update-local-test-915c35

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Clause-②: no

Moves the console pin from objectui f8a9d0fb0596 to objectui main dd3f7e1be356, which is 325 upstream changesets and 41 of them declared breaking on objectui's own surfaces. The PR also lands the three pieces this repo needs so the console built at that pin actually works. A local boot from a zero-user database, driven in a headless browser from the first-run registration onward, found each of them.

Why this pin, and not an earlier main

At any objectui main from 8cc0e2984 onward, a console built by scripts/build-console.sh dies at boot. The page stays on the splash screen with TypeError: u is not a function.

The script always injects this tree's @objectstack/client through OBJECTSTACK_CLIENT_DIST. That client resolved outside node_modules, so it missed the vendor-objectstack group and landed in framework. framework imports data-adapter, and data-adapter requires the CJS client at its top level before framework has defined it.

The Console Pin Gate only builds the bundle and never loads it, so the gate stays green on this breakage. The fix landed upstream as objectui#10921 (for objectui#10920), and dd3f7e1be356 is its merge commit. At this pin chunk-membership.json reads client: { "vendor-objectstack": 1 }, and the console boots.

What changes

Piece Change
.objectui-sha + .changeset/console-dd3f7e1be356.md bump-objectui.sh over the whole range f8a9d0fb0596...dd3f7e1be356: 325 releasing changesets, 41 annotated breaking, 0 declared major. ADR-0087 disposition: not-required (no-migration-prescription), worded as in the last bump that carried breaking entries.
sdui.manifest.json + record Regenerated from the pin's built tree: 107 components, up from 59. The 48 additions are the html-tier intrinsic tags objectui now registers (a, p, h1–h6, ul/ol/li, table, img, …). div is not among them. check-sdui-manifest --require-objectui passes.
packages/sdui-parser/objectui-lockstep.json Re-recorded against .cache/objectui-dd3f7e1be356: 214 grammar lines, byte-identical, and all 26 codes agree.
47 pin citations in packages/spec/src Re-measured, not sha-swapped, over both hops f8a9d0fb0 → 733fd5ac6 → dd3f7e1be. Byte-identical files hold by identity. Moved spans are re-pointed. Changed spans are re-read, with the change stated where cited. No read point died. The corpus counts in the six semantic migration entries were calibrated at f8a9d0fb0 first, then re-taken at the pin; all 96 dark tokens are still 0. The view.zod.ts map-config record's five quoted anchors are re-pointed; none changed what it reads.
Showcase html pages Capability Map (the showcase landing page), Start Here and the HTML Command Center rendered HTML page failed to compile at the pin. objectui now refuses div on kind:'html' pages and names box as the drop-in swap (objectui#10757). Every wrapper moves to box, and all three render with their styles intact.
packages/lint JSX ratchet With the regenerated manifest and the migrated pages, the wired gate emits zero findings over the shipped pages. Every ledger row went stale, so sdui-jsx-baseline.json is deleted, as its header prescribes, and the census assertion inverts to "the wired run is clean".
trash-2 → trash lucide 1.43, which the pin ships, dropped Trash2 from the runtime icons record the console resolves names through. icon: 'trash-2' draws nothing, while trash is the identical glyph (objectui measured it node for node). Changed: four @objectstack/platform-objects delete actions, the app-todo delete action, the flow builder's delete_record palette entry, and the BulkAction.icon describe example. The changeset is patch for platform-objects and spec.
FormField.span describe The describe names the pin it was re-read at; the claim still holds. Separate spec patch changeset; the reference docs are regenerated.

Verification

Gates, run locally at the head:

  • check:objectui-pin-citations: 47 asserting citations match, and 7 anchor-content assertions are verified against objectui at dd3f7e1be.
  • check:generated: all 15 artifacts are up to date.
  • check:sdui-lockstep OK, check-sdui-manifest --require-objectui OK, check:console-sha OK, check:cross-package-test-inputs OK.
  • packages/lint: 4704 passed.
  • examples/app-showcase: 384 passed.
  • packages/platform-objects: 911 passed.
  • packages/spec: 17121 passed and 2 failed. Both failures reproduce here on Node 22 as well. They are script-harness tests, not tests of this diff: build-schemas-check-mode sees a stamp mismatch, and check-liveness sees a child's JSON cut at byte 65478. I left them for CI to arbitrate rather than claim either way. An earlier run under heavy machine load showed 18 failures; all 16 extra were load timeouts that pass on a quiet re-run.

End to end, on a fresh zero-user database (objectstack dev --no-seed-admin), with the console built at the pin, driven in headless Chromium:

  • First run. The /setup wizard creates the owner. A CJK-only organization name renames the bootstrap org (slug kept) instead of creating a second one. The exit is a full-document navigation, and bootstrap-status flips to hasOwner:true.
  • Signup gate. An uninvited sign-up is refused 403 SELF_REGISTRATION_CLOSED, both at the API and through /register. Anonymous /setup redirects to /login?redirect=%2Fsetup, and the owner's /setup opens Setup.
  • Invitation. The owner invites through the org record page; the role list is the closed four-name vocabulary. The invited email registers through /register and joins as member.
  • Member gates. Five forged admin calls as the member (invite admin, invite member, rename org, demote owner, create team) are each refused 403. The member sees no create affordance, and a direct create is refused 403.
  • Owner CRUD. Create through the form dialog (lookup and required-field validation work), edit and delete a project, each checked server-side.
  • Self-service. The member renames, uploads an avatar (now stored as /api/v1/storage/files/<id>, not a data: URL), changes the password (old one refused, new one accepted) and signs out from the user menu.
  • Page sweep. 95 surfaces as the owner (showcase and Setup) and 58 as the member (showcase and account) show no page errors and no 5xx. The only failures at 9f0c84a were the three html pages this PR migrates.

Findings outside this PR's scope, noted here and not filed: several strings are untranslated on the zh-CN console (Delegated Admin, the raw server message on a refused sign-up, the wizard's Cancel/Next/Step 1 of 3, MEMBER COUNT). A read-only member's wizard shows disabled inputs with no explanation. Record delete confirms through the native window.confirm. The console probes GET /api/v1/usage/storage, which answers 404 on this server.


Generated by Claude Code

hotlong and others added 10 commits September 28, 2026 14:09
fix(plugin-view,plugin-dashboard): object-view's non-grid views and a dashboard filter's optionsFrom options re-read on the data-invalidation bus (objectui#10887) (#10898)

objectui@9f0c84a448d1bcb7c009bac37a8b006944038be3
The bump generator leaves an `adr-0087: TODO` marker because the range
carries 40 declared-breaking objectui entries. This diff moves no
ObjectStack-authorable key, so the disposition is not-required
(no-migration-prescription), in the wording of the previous bump.

Claude-Session: https://claude.ai/code/session_local_f929252f-7e71-4327-b98b-bd46424b5e05
Co-authored-by: Claude <noreply@anthropic.com>
`node scripts/gen-sdui-manifest-node.mjs` over the built tree at the new
pin: 107 components (59 before). The 48 additions are the html-tier
elements objectui now registers (a, p, h1-h6, ul/ol/li, table, img, ...);
nothing is removed. `check-sdui-manifest --require-objectui` passes.

Claude-Session: https://claude.ai/code/session_local_f929252f-7e71-4327-b98b-bd46424b5e05
Co-authored-by: Claude <noreply@anthropic.com>
…JSX ratchet closes at zero

objectui at the new pin refuses `div` on kind:'html' pages and names
`box` as the drop-in swap (objectui#10757). Without this the showcase
landing page (Capability Map), Start Here and the HTML Command Center
render "HTML page failed to compile" instead of their content.

Every `div` wrapper in the three pages becomes `box`. Measured in a
headless browser against a local boot: all three render, with their
inline styles intact.

With the regenerated manifest (which declares the html tier's `a`, `p`,
`h1`-`h6`, ...) and the migrated pages, the wired SDUI JSX gate emits
zero findings over the shipped pages. Every row of the ratchet-to-zero
ledger went stale, so the ledger is deleted, as its header prescribes,
and the census assertion inverts to "the wired run is clean".

Claude-Session: https://claude.ai/code/session_local_f929252f-7e71-4327-b98b-bd46424b5e05
Co-authored-by: Claude <noreply@anthropic.com>
…733fd5a (sha placeholders pending the final pin)

Every asserting `.objectui-sha` record in packages/spec/src was re-read
against objectui 733fd5a. Byte-identical cited files hold by
identity. Moved spans are re-pointed. Spans whose content changed are
re-read and the change is stated where cited. No read point died, and no
dark corpus token turned non-zero; the corpus counts were calibrated at
f8a9d0f first. Only comments and the six semantic migration entries'
`reason` text changed (the registry copies are kept in sync); no
.describe() except `FormField.span`'s pin sha.

The asserting shas are written as length-preserving placeholders
(__PIN9__, __PIN12__, __PIN40__). They are replaced by the final pin in
the commit that moves `.objectui-sha` there, once the cited files are
shown byte-identical between 733fd5a and that pin.

Claude-Session: https://claude.ai/code/session_local_f929252f-7e71-4327-b98b-bd46424b5e05
Co-authored-by: Claude <noreply@anthropic.com>
…3 still resolves

The console at the new objectui pin ships lucide-react 1.43, whose
runtime `icons` record dropped `Trash2`, so an authored `icon: 'trash-2'`
draws no glyph. `trash` is the identical glyph. Four platform-object
delete actions, the app-todo delete action, the flow builder's
delete_record palette entry and the BulkAction.icon description example
move to it; the two test fixtures follow.

Claude-Session: https://claude.ai/code/session_local_f929252f-7e71-4327-b98b-bd46424b5e05
Co-authored-by: Claude <noreply@anthropic.com>
…njected-client chunk fix

The pin moves from f8a9d0f to objectui main dd3f7e1, whose tip
is objectui#10921 (fixes objectui#10920). At 9f0c84a a console built by
build-console.sh, which injects this tree's client, died at boot with
"TypeError: u is not a function": the injected client landed in
`framework` and closed a framework <-> data-adapter import cycle. At
dd3f7e1 the client sits in vendor-objectstack and the console boots.

- The console changeset is regenerated over the whole range
  f8a9d0f...dd3f7e1 (it replaces the interim 9f0c84a
  one). Its ADR-0087 disposition is not-required
  (no-migration-prescription).
- `scripts/sdui-manifest.record.json` is re-recorded at the new pin. The
  manifest bytes are unchanged from the 9f0c84a generation.
- The sdui-parser lockstep is re-recorded against the built tree at the
  pin; the grammar is byte-identical and all 26 codes agree.
- The 47 asserting pin citations get the pin's prefix in place of the
  placeholders. Each record was re-measured over both hops
  (f8a9d0f -> 733fd5a -> dd3f7e1).
- The view.zod.ts map-config record's five quoted anchors are re-pointed
  after the gate redded them. None of them changed what it reads.
  `check:objectui-pin-citations`: 47 asserting citations match, 7 anchor
  content assertions verified.

Claude-Session: https://claude.ai/code/session_local_f929252f-7e71-4327-b98b-bd46424b5e05
Co-authored-by: Claude <noreply@anthropic.com>
…an pin and the `trash` icon example

check:generated found exactly one stale gate, check:docs. Two
`.describe()` sentences moved: FormField.span now names the pin
dd3f7e1 it was re-read at, and BulkAction.icon's example names
`trash`. A spec patch changeset records the re-pointed published
sentence, following the previous bump.

Claude-Session: https://claude.ai/code/session_local_f929252f-7e71-4327-b98b-bd46424b5e05
Co-authored-by: Claude <noreply@anthropic.com>
…e-local-test-915c35

# Conflicts:
#	packages/spec/src/migrations/entries/semantic/18.system-metrics-jsdoc-durations-unit-in-key.ts
#	packages/spec/src/migrations/registry.ts
…reference pages check clean

The merge of origin/main deferred content/docs/references/ui/{view,bulk-action}.mdx
to regeneration. They were regenerated from the merged tree (gen:schema then
gen:docs) and staged into the merge itself; `check:generated` reports all 15
artifacts up to date, so this commit carries no further change.

Claude-Session: https://claude.ai/code/session_local_f929252f-7e71-4327-b98b-bd46424b5e05
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/lint, @objectstack/platform-objects, @objectstack/sdui-parser, @objectstack/spec, touching 16 documentable anchor(s). ⚠️ 3 changed file(s) yielded no anchor (packages/lint/src/sdui-jsx-baseline.json, packages/sdui-parser/objectui-lockstep.json, packages/spec/src/kernel/functional-completeness.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/ai/connect-mcp.mdx (via delete_record (literal, a string literal in BUILT_IN_NODE_DESCRIPTORS))
  • content/docs/api/index.mdx (via delete_record (literal, a string literal in BUILT_IN_NODE_DESCRIPTORS))
  • content/docs/automation/flows.mdx (via delete_record (literal, a string literal in BUILT_IN_NODE_DESCRIPTORS))
  • content/docs/data-modeling/analytics.mdx (via DatasetMeasureSchema (symbol, a top-level const))
  • content/docs/deployment/cli.mdx (via delete_record (literal, a string literal in BUILT_IN_NODE_DESCRIPTORS))
  • content/docs/protocol/objectui/layout-dsl.mdx (via ComponentPropsMap (symbol, a top-level const object))

⛔ 7 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v15.mdx (via PageTabsProps (symbol, a top-level const object))
  • content/docs/releases/v17/17-0.mdx (via delete_record (literal, a string literal in BUILT_IN_NODE_DESCRIPTORS))
  • content/docs/releases/v17/17-1.mdx (via ComponentPropsMap (symbol, a top-level const object))
  • content/docs/releases/v17/17-2.mdx (via delete_record (literal, a string literal in BUILT_IN_NODE_DESCRIPTORS))
  • content/docs/releases/v17/17-3.mdx (via ComponentPropsMap (symbol, a top-level const object))
  • content/docs/releases/v17/17-4.mdx (via ComponentPropsMap (symbol, a top-level const object))
  • content/docs/releases/v17/17-5.mdx (via delete_record (literal, a string literal in BUILT_IN_NODE_DESCRIPTORS))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 3 changed file(s) yielded no anchor (packages/lint/src/sdui-jsx-baseline.json, packages/sdui-parser/objectui-lockstep.json, packages/spec/src/kernel/functional-completeness.ts) — pages documenting those are invisible to this run
  • 3 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 138 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json e4d3f2ca696f4b388286c0dfdda23e50c850ca92 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 54c9fb152924eab9f2c2da275ace5228dcacc1fd — the merge of head 4e305cf7eec1c4c2bf88a09b13a1d259fe4e7ef6 into base e4d3f2ca696f4b388286c0dfdda23e50c850ca92, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 54c9fb152924eab9f2c2da275ace5228dcacc1fd && git checkout 54c9fb152924eab9f2c2da275ace5228dcacc1fd
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e4d3f2ca696f4b388286c0dfdda23e50c850ca92 4e305cf7eec1c4c2bf88a09b13a1d259fe4e7ef6 && git checkout -B drift-repro e4d3f2ca696f4b388286c0dfdda23e50c850ca92 && git merge --no-ff 4e305cf7eec1c4c2bf88a09b13a1d259fe4e7ef6

node scripts/docs-audit/affected-docs.mjs --json e4d3f2ca696f4b388286c0dfdda23e50c850ca92

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs e4d3f2ca696f4b388286c0dfdda23e50c850ca92 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@hotlong
hotlong added this pull request to the merge queue Sep 28, 2026
Merged via the queue into main with commit 3cf6449 Sep 28, 2026
36 checks passed
@hotlong
hotlong deleted the claude/objectui-update-local-test-915c35 branch September 28, 2026 14:11
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
…and element:definition-list/repeater (objectstack-ai#20420)

Fixes objectstack-ai#20371

Clause-②: yes

## What this does

`ComponentPropsMap` gains six rows for the curated objectui public
blocks that had none: `action:button`, `action:group`, `action:menu`,
`action:icon`, `element:definition-list`, `element:repeater`. Each row
is a `strictObject` from birth, and each key set is measured from the
renderer's read points in objectui, not transcribed from
`UIActionSchema`, from the registrations' `inputs`, or from this
package's object-metadata `ActionSchema` (triage execution note 1).

Before this change the six failed in two ways:

- The four `action:*` types sit outside every namespace the
`PageComponentType` enum populates, so the props gate skipped them. Any
key inside `properties` parsed, was stored, and was ignored by the
renderer.
- The two `element:*` types sit inside the reserved `element:` namespace
with no enum member and no row, so `component-type-unknown` refused the
whole node (severity error), although objectui registers, publishes and
offers both.

A row closes both. `component-type-vocabulary.ts` derives the known set
from `Object.keys(ComponentPropsMap)`, so the two `element:*` types join
the `element:` vocabulary through their rows. This is the
`element:metadata_viewer` shape: no enum member and no string-arm ledger
entry (the vocabulary test forbids a ledger entry for a type the map
declares). The three-part evidence the ledger comment asks for
(registration, publication, authorship) is written on the map rows, with
pin citations.

Findings stay at the props gate's existing warning tier.
`PageComponentSchema` parsing is unchanged, because the open `type` arm
already admitted all six.

## Read points: measured at the pin (`.objectui-sha` `dd3f7e1be356`)

First measured at `f8a9d0fb0596`. After objectstack-ai#20436 moved the pin, every read
point was re-derived at `dd3f7e1be356` (2026-09-28) and re-anchored
together with the sha.

Per-key citations are in `component.zod.ts` section 4b and in each
schema's docblock. The decisions the measurement made:

| Row | Declared keys | Measured, not assumed |
|:---|:---|:---|
| `action:button` | `name`, `label`, `icon`, `actionType`, `variant`,
`size`, `visible`, `disabled` + 21 keys forwarded to the runner
(`params`, `target`, `openIn`, `endpoint`, `method`, `bodyExtra`,
`bodyShape`, `operation`, `patch`, `confirmText`, `successMessage`,
`errorMessage`, `refreshAfter`, `undoable`, `recordIdField`,
`locations`, `toast`, `resultDialog`, `onSuccess`, `description`,
`objectName`) | `name` is **optional**, because the renderer reads
`schema.name ?? schema.label` (`action-button.tsx:119`). `variant`
accepts `primary` and `size` accepts `md` because the renderer maps both
(`:137-138`). `type` is refused with a rename to `actionType`. `enabled`
(the legacy fallback) and `autoTrigger` (a host transport flag, "NOT
persisted metadata") are refused with a prescription. |
| `action:icon` | same as the button, minus `size`, `undoable`,
`recordIdField` | `:107` pins the icon size, so there is no `size`.
`undoable` and `recordIdField` are not in its forward (`:134-196`). |
| `action:group` | `actions`, `display`, `location`, `label`, `icon`,
`variant`, `size`, `visible` | `actions` is a **list** of action objects
(`:248`); the registration publishes `type: 'object'`. There is no
group-level `name`: the registration publishes it (`:415`), nothing
reads it, and it is refused with a prescription. `size` takes the
primitive's four values: `md` is mapped only on the dropdown trigger
(`:357`) and reaches the Button primitive unmapped in the default inline
mode (`:398`, `:91`). |
| `action:menu` | `actions`, `label`, `icon`, `variant`, `size`,
`visible` | The trigger `variant` and `size` go to the Button primitive
unmapped (`:230-231`), so there is no `primary` and no `md`. |
| `element:definition-list` | `items` (strict `{ term, description? }`),
`columns`, `inline` | `columns` is the **number** `1 \| 2`, because the
renderer compares `=== 2` (`data-list.tsx:49`). The registration's enum
publishes the strings `'1'`/`'2'`, and the string `'2'` is refused with
a prescription. `items` is optional: absent and empty both render "No
details". `term` is required. |
| `element:repeater` | `object` (required), `titleField`, `fields`,
`filter`, `sort`, `limit`, `emptyText`, `divided` | `filter` and `sort`
use the family's one orthography, `ViewFilterRule[]` and `SortItem[]`.
Both reach the query: `ObjectStackAdapter.find` lowers rule arrays and
serializes sort items. `fields` takes a name or `{ field }`; the `label`
that the TS type advertises is never rendered and is refused. |

The value posture follows objectstack-ai#7751. A key the renderer interprets itself
gets a value schema. A key it only forwards to the action runner gets
the scalar that `ActionDef` declares for it, or `z.unknown()` where
`ActionDef` uses a spec-derived block.

**`objectName`, carried by the new pin.** At `dd3f7e1be356`,
`action:button` and `action:icon` forward `objectName` to the runner
(`action-button.tsx:307`, `action-icon.tsx:195`), and the console
dispatches to that object instead of the page object. Both rows declare
`objectName` as the `ActionDef` string scalar. `action:group` and
`action:menu` forward it per member (`action-group.tsx:323`,
`action-menu.tsx:313`), so it rides each member object and the container
rows gain no key. The same pin reads static values from
`properties.params` (objectui#10289, `static-params.ts:91-101`). On a
page node that is the row itself, so `params` keeps its meaning: an
array is the input list and an object is the static values. Its value
schema is unchanged.

## Surface beyond the claim, and why

The claim lists `component.zod.ts`, tests in `src/ui/`, generator output
and `.changeset/`. Three gates required three more files, two in
`packages/spec` and one in `packages/qa/dogfood`. All three edits follow
from the new rows, and none was stopped on:

- `dropped-refinements.baseline.json`: `ElementRepeaterProps` publishes
`ViewFilterRuleSchema`, whose refinement the JSON Schema projection
drops. The build refuses to publish until the site is declared. I added
the entry the build printed, plus the header totals its test holds
(211→212 schemas, 609→610 sites).
- `type-alias-convention.pin.test.ts`: `gen:docs` requires a type alias
for every documented schema, and `check:spec-parsed-alias` requires an
isomorphic alias to be pinned. `ElementDefinitionListProps` is the only
isomorphic one of the six, so it gets one pin. The count is 780→781
after the merges with objectstack-ai#19920's 786→783 and main's connector-retirement
783→780 (both intents stacked). The other five declare `XParsed`.
- `packages/qa/dogfood/test/expression-conformance.ledger.ts`:
gate-forced by the `Dogfood Regression Gate`
(`expression-conformance.test.ts`, ADR-0060 checkLedger). The six
`visible` / `disabled` predicate positions the new action rows declare
needed a classification. It has three rows, split by fault face as the
objectui renderers and `SchemaRenderer`'s node gate compose at the pin:
button/menu `visible` fail-closed, icon/group `visible` fail-soft-log,
button/icon `disabled` fail-closed. Round 4 re-anchored the three rows
at `dd3f7e1be356`; their fault faces are unchanged, because the
evaluators are code-identical across the pin hop.

`component-type-vocabulary.ts` was **not** edited. Its
`KNOWN_COMPONENT_TYPES` docblock lists the string-arm rows "exactly"
(`element:metadata_viewer`, the plugin widgets, `object-*`), and that
list no longer covers the six new rows. It is noted below, not fixed
here.

`element:repeater`'s `filter` is the bare
`z.array(ViewFilterRuleSchema)` door that `record:related_list`
declares, not a `ruleArrayFilterError` door. That prescription speaks to
a door that used to take the record form, and wiring it would pull the
repeater into the reach of the stored-row conversion
`page-component-filter-record-to-rule-array` (`conversions/registry.ts`,
whose test holds the two equal). That registry is outside this card.

## Premise checks (order zone 2)

- **A1 holds.** At `ab6fb027`, `git grep` for each quoted type in
`packages/spec/src`: the four `action:*` hit only
`action.zod.ts:983-986` (the `Action.component` enum) plus
`expression-bindable-text-keys.test.ts:126`. The two `element:*` types
hit 0. Control: `'element:text'` hits `component.zod.ts:4609` (the row).
- **A2.** Measured at the pin. The objectui checkout here is shallow and
does not contain the pin, so the pin was fetched into a scratch clone.
The two differences from `origin/main` are listed above. The pin was not
bumped.
- **A3.** The objectstack-ai#7751 / objectstack-ai#8691 / objectstack-ai#8744 method is followed. The three-part
evidence is on the two `element:*` rows.
- **A4.** `node scripts/check-sdui-manifest.mjs`: see the gate table.
The manifest was not touched.
- **A5.** In this repo, **zero** authored nodes of the six types:
pattern
`['"]action:(button|group|menu|icon)['"]|['"]element:(definition-list|repeater)['"]`
plus the `type:` YAML form, excluding `node_modules`/`dist`. The hits
are `sdui.manifest.json`, `action.zod.ts`'s `component` enum, three
generated reference pages (a type union) and one test. Control with the
same shape: `'element:text'` gives 105 occurrences in 29 files,
including `examples/**` and `platform-objects`. No shipped metadata is
refused by the new rows.

## Through the lint door

A one-off probe (not a permanent test) runs `validateComponentTypes` and
`validateComponentProps` from `packages/lint/src` against the built
spec, using one stack with a planted typo on each row plus
`element:repeatr` as a control:

- **After:** `component-type-unknown` fires only on `element:repeatr`.
`component-props-unknown-key` (warning) fires on
`action:button.typo_key`, on `action:group.name`, and on
`element:repeater.fields.0.label`, which the lone union arm unpacks.
- **Before**, emulated in process by deleting the six rows and the two
known types from the same module instance the rules read:
`component-type-unknown` fires on both `element:*` types and on
`element:repeatr`, and the props gate reports **nothing**.

## Tests

The readings below are at HEAD `5e50899a4` (after merging `origin/main`
at `3cf644938`, the pin bump):

- `pnpm --filter @objectstack/spec test` (the `local` project): **565
files, 16684 passed**, 1 todo, exit 0. This includes the new
`src/ui/component-action-element-rows-20371.test.ts` (45 tests):
  - key sets asserted whole;
  - one objectui-sourced accepted example per type;
  - an unknown-key refusal on every row;
  - each measured decision above;
  - vocabulary admission, with the `element:repeatr` control.
- `pnpm --filter @objectstack/spec test:repo`: **37 files, 684 passed**,
exit 0.
- `pnpm --filter @objectstack/spec typecheck` (`tsc --noEmit`,
`check:scripts-typecheck`, `check:test-typecheck`): exit 0.
- Consumers, downstream of spec (not a full `...@objectstack/spec`
sweep: the three the order names):
- `pnpm --filter @objectstack/lint test`: **115 files, 5331 passed**,
exit 0.
- `pnpm --filter @objectstack/metadata-core test`: **16 files, 289
passed**, exit 0.
- `packages/qa/dogfood` `test/expression-conformance.test.ts`: **7
passed**, exit 0.
- `@objectstack/cli` unit layer (`exec vitest run --project unit`; not
re-run this round, this is the round-1 reading): **2579 passed**, 29
skipped, 0 assertion failures. 52 files are **NOT MEASURED**: they fail
to load on `MODULE_NOT_FOUND` for workspace dependencies not built here
(`@objectstack/plugin-email`, `create-objectstack`,
`@objectstack/verify`, `@objectstack/cloud-connection`, the cli's own
`dist`). The integration layer is declared to CI, since the diff touches
no spawn entry.
- Builds (upstream direction):
  - `@objectstack/spec` itself;
- lint's upstream (`--filter @objectstack/formula --filter
@objectstack/sdui-parser --filter @objectstack/lint`);
- client-react's upstream (`--filter "@objectstack/client-react..."
--filter "!@objectstack/spec"`) for `check:skill-examples`.
- eslint, a proven narrowing rather than the repo-wide `pnpm lint` (that
one is CI's):
- `eslint --no-inline-config --format json` over the diff's three TS
files (the only lintable files in it; the rest are JSON, MD and MDX,
which the config's `files` globs do not select): **3 files, 0 errors, 0
warnings**.
  - `--print-config` resolves a config for each of the three files.
- `eslint.config.mjs` enables no type-aware linting (no
`parserOptions.project`, as its own comment near line 327 states), so
this diff cannot move any untouched file's verdict.
- Before the merge, the spec suite had one failure: the
dropped-refinements header totals. `7bd546c1` fixed it.

## Gates

The derived union at `5e50899a4` is 108 commands (`node
scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`, with no paths, off the merge base). It is a
superset of the 72-line dispatch list, adding 36 families for the
changeset, the docs and the pin test. Every exit code was written to
disk before its output was read.

- **107 exit 0.** These include `check:type-check-debt` (to a verdict
this time: 4 ledger entries re-measured, none above its recorded
number), `check:dts-closure`, `check:generated`, `check:api-surface`,
`check:authorable-surface`, `check:docs`, `check:strictness-ledger`,
`check:objectui-pin-citations`, `check:spec-parsed-alias`,
`check:yaml-examples`, `check:liveness`, `check:issue-citations`,
`check:nul-bytes` and `check:skill-examples` (after building the client
closure).
- **NOT MEASURED: `pnpm check:dual-build-cjs-loads`.** It exited 3
(PREREQUISITE NOT MET) because it reads every workspace package's
`dist`, which needs a whole-repo build. This diff changes only
`@objectstack/spec`'s build output.
- `dispatch-gates --ran`: "108 derived famil(ies) accounted for — 107
run, 1 NOT-MEASURED".
- `node scripts/check-sdui-manifest.mjs`: exit 0. The manifest is
untouched by this branch and recorded at pin `dd3f7e1be356` (A4: no
lockstep gate moved).
- `check:objectui-pin-citations`: exit 0 (49 asserting citations match
`dd3f7e1be`, 61 historical). `--verify-anchors` against a `dd3f7e1be`
clone: exit 0.

## Acceptance notes (not filed; the seat decides)

- **objectui producer side** (carrier: the objectui#10872 follow-up the
seat files at ACCEPT; its `registry-inputs-spec-parity` gate will
surface each of these on the spec bump):
- the `action:group` registration publishes `name`, which nothing reads,
and a `size` enum with `md`, which inline mode does not map;
- the `element:definition-list` registration's `columns` enum is the
strings `'1'`/`'2'` (the designer writes numbers);
- `element:repeater`'s TS type and registration description advertise
`fields[].label`, which is never rendered.
- **objectui renderer, read-only inference, not reproduced** (carrier:
none):
- `action:menu` spreads `...rest` onto its trigger after
`disabled={loading}`, so `SchemaRenderer`'s `disabled: undefined` can
override the in-flight disable (the objectui#9131 shape it fixed on
button/icon);
- `action:menu` and inline `action:group` spread hoisted props
(`actions`, `label`, …) raw onto DOM elements.
- **`component-type-vocabulary.ts`'s `KNOWN_COMPONENT_TYPES` docblock**
enumerates the string-arm rows as "exactly" `element:metadata_viewer`,
the plugin widgets and `object-*`; the six new rows are not in that
list. This is prose drift and not edited, because the file is outside
the claimed surface (carrier: the next edit of that file).
- **`element:repeater.filter`** is a bare rule-array door. Wiring it to
`ruleArrayFilterError` means adding `element:repeater` to
`RULE_ARRAY_FILTER_BLOCK_TYPES` in `conversions/registry.ts` in the same
change (carrier: none).

## Changeset

`@objectstack/spec` `minor`: six new public rows, two types admitted to
the `element:` vocabulary, and nothing that a declared row accepted is
refused.

## Downstream

objectui#10872 can now arm the six by reference. When it bumps
`@objectstack/spec`, its `registry-inputs-spec-parity` gate will judge
the six in both directions:

- The forward direction flags `name` on `action:group` and the
`'1'`/`'2'` string enum on `columns`.
- The reverse direction lists the read-but-unpublished keys: 22 on
`action:button`, 20 on `action:icon`, `location`/`visible` on the group,
and `size`/`visible` on the menu.

That reconciliation belongs to objectui.

The `Surface beyond the claim` section was amended by the `domain:spec`
seat 1 (`session_01B3TqpoQbTAfG7G74GMDWNW`) after the patch round, from
the dev's delta `5868569396`.

Round 4 (after objectstack-ai#20436 moved `.objectui-sha` to `dd3f7e1be356`): the
read-points heading, the table anchors, the `objectName` paragraph,
Tests, Gates, Acceptance notes and Downstream were amended by the same
seat from the dev’s delta (report `5873527479`).

---------

Co-authored-by: Claude <noreply@anthropic.com>
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
…`tier: 'html'`, ported from objectui's lockstep copy (objectstack-ai#20582)

Fixes objectstack-ai#20112
Clause-②: yes (widening)

Executes the one item left on this card after PR objectstack-ai#20436: port objectui's
`tier` plumbing into this repository's copy of `packages/sdui-parser`,
regenerate `sdui.manifest.json` so the html tier's intrinsic tags carry
`tier: 'html'`, and re-record. This follows ruling A (`5852014527`) as
triage re-derived it at pin `dd3f7e1be356` (`5882165416`). Base
`origin/main` `f11b5f20a2`, head `ffbb0c4802`.

## What changed

- **`packages/sdui-parser/src/{index,types,codegen}.ts`**: the three
hunks of objectui `baac95a261` (objectui#10735), copied byte-for-byte
from objectui at the pin `dd3f7e1be356`:
  - `RegistryConfigLike.tier` admits `'html'`;
  - `ManifestComponent.tier?: 'html'` is new;
  - `manifestFromConfigs` writes exactly `'html'` or omits the key;
- `generateBlockList` titles the curated count and lists the html tier
in its own section.
- **`sdui.manifest.json` + `scripts/sdui-manifest.record.json`**:
regenerated with `scripts/gen-sdui-manifest-node.mjs` over objectui's
built tree at the pin. `"tier": "html"` lands on 48 entries and nothing
else moves. The record's sha256 and date are re-recorded.
- **`packages/sdui-parser/src/__tests__/html-tier-manifest.test.ts`**:
objectui's own ten cases for the mechanism
(`html-tier-manifest-10735.test.ts` at the pin), with this repository's
header.
- **`.changeset/20112-sdui-parser-html-tier-stamp.md`**: `minor` for
`@objectstack/sdui-parser` (a new accepted value and a new type member)
and for `@objectstack/console` (its `dist` ships the regenerated
manifest).

**Byte-faithfulness, measured.** For each of the three files, the
`+`/`-` lines of this diff equal the `+`/`-` lines of objectui
`baac95a261` for the same file (index 15 lines, types 17, codegen 39;
`cmp` identical). Every added line is present verbatim in the file at
the pin. `generateBlockList`, `ManifestComponent` and
`manifestFromConfigs` extracted whole are `cmp`-identical to the pin.
The one later objectui commit to these files in the window (`fec3b1a8b`)
rewrites two citation comments unrelated to `tier`.

## The PM's mechanism hypotheses, measured

- **H0: holds.** At the pin, objectui declares `tier?: 'public' |
'internal' | 'html'` (`index.ts:87`) and `ManifestComponent.tier`
(`types.ts:165`, member at `:180`). This copy declared `'public' |
'internal'` (`index.ts:72`). The tracked manifest had 107 components and
**0** carrying any `tier` key.
- **H1: holds, with a different count: 48, not 47.** After the port and
regeneration, the manifest keeps the same 107 keys in the same order. 48
entries gain `"tier": "html"`, no other tier value appears, and 0
entries change any other field (each entry deep-compared with `tier`
removed). `git diff --stat`: 48 insertions, 0 deletions. The 48th tag is
`code`. objectui#10756 (PR objectui#10776, `29b45f6a0`) added it to
`HTML_TIER_INTRINSICS` after the triage count was taken, and the pin
contains it. `div` and `kbd` stay absent.
- **Control leg for the build tree:** the UNPORTED serializer over the
same built tree reproduced the tracked artefact byte-for-byte (sha256
`4073897dcdc1…`, unchanged file).
- After the port: sha256 `d0666ac585db…`, 103874 bytes. `node
scripts/check-sdui-manifest.mjs`: `✓ … intact (sha256 d0666ac585db…, 107
components) and recorded at the live objectui pin dd3f7e1…`.
- **H2: holds.** `check:sdui-lockstep` is green both before and after
the port. With the three source files restored to `f11b5f20a2` (the
drift state: 0 hits for the three-arm union, 0 for
`ManifestComponent.tier`), it prints `OK — this copy is byte-identical
to objectui@dd3f7e1be356 …`. It compares the grammar region, the
diagnostic-code set and the `not-a-container` predicate, never a type
union. So it cannot see this drift. The gate is not widened (see
Acceptance notes).
- **Re-record:** `gen:sdui-lockstep` rewrote
`packages/sdui-parser/objectui-lockstep.json` byte-identically (0 diff
lines), so the lockstep record was already current at the pin. The
re-recorded artefact is the manifest's record.
- **H3: holds.** `compile()` over the three shipped `kind:'html'` pages
(`capability-map`, `command-center-jsx`, `start-here`), against the old
manifest and against the new one:
  - all three are `ok=true` with 0 errors and 0 warnings on both sides;
- the full result objects, tree included, are deep-equal (0 of 3 moved).
- Lit control: the same harness with `box` deleted from the manifest
turns `start-here` to `ok=false`, 54 errors.
- `@objectstack/lint`'s production witness reads the regenerated file
from disk and passes (`wired run over the three shipped html pages is
clean`).

## Verification

All runs are at head `ffbb0c4802` unless noted.

- `pnpm --filter @objectstack/sdui-parser build`, then `exec vitest run
--maxWorkers=2`, then `typecheck`: 12 files, 199 tests passed; `tsc
--noEmit` clean. `--listFiles` shows the typecheck program includes the
new test file.
- **Ablation, from the committed fix at `9a77e00415`.** The three source
files were restored to `f11b5f20a2`, with a restore trap:
- landing proven by `grep -c` (three-arm union 1→0, `tier?: 'html'` 0,
html section 0);
- the new test file goes 2 failed / 8 passed: the stamp case and the
block-list section case. The other eight pin the readers the stamp must
not change, and stay green by design;
- `tsc --noEmit` exits 2 with six errors on the test (TS2322 ×4, TS2339,
TS2367).
- Restored with `git checkout HEAD -- …`. Each blob `hash-object` equals
`HEAD`, and `git diff HEAD` is empty.
- **Consumer**, the only importer: `pnpm --filter
'@objectstack/lint^...' build`, then `@objectstack/lint` `vitest run
--maxWorkers=2` (115 files, 5358 passed, 5 skipped) and `typecheck`
(`tsc --noEmit` plus `check:test-typecheck` OK).
- `pnpm lint` (eslint over the whole repository, `--no-inline-config`):
exit 0 at `ffbb0c4802`. This was a full run, not a narrowed one.
- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands`: 71 derived commands, all run. Reconciled with `--ran`: `71
derived famil(ies) accounted for — 68 run, 3 NOT-MEASURED`.
- NOT MEASURED (exit 3, PREREQUISITE NOT MET, each needs every package's
`dist`): `check:dual-build-cjs-loads`, `check:lean-entry-closure`,
`check:type-check-debt`. Narrowed stand-in for the first: `require()` of
`dist/index.js` and `import()` of `dist/index.mjs` both load
`manifestFromConfigs` / `compile`.
- One red, host-bound: `check:bash32-floor`'s `--self-test` fails 7 of
179 cases on this macOS host (bash 3.2). Control: the same command in a
detached worktree at `f11b5f20a2` fails the same 7 of 179. This diff
touches no shell and not that script.
- Artifact-roster rows that could apply, all exit 0:
`check-changeset-fixed`, `check-published-list-mirrors`,
`check:authz-resolver`, `check:console-injection`,
`check:error-code-casing`, `check:filter-alias-parity`,
`check:i18n-stale-fill`, `check:lockstep-package-count`,
`check:widget-option-census`, `spec check:react-blocks`,
`check:cli-examples-parity`, `check:scaffold-emission-policy`.
`check:published-readme-exports` is NOT MEASURED (exit 3).
`@objectstack/sdui-parser` ships no README.
- `node scripts/check-issue-citations.mjs --base origin/main`: exit 0 (4
cross-repo citations). `origin/main` is still `f11b5f20a2`, an ancestor
of the head, so nothing needed merging.
- `check-changeset-no-major`, `check-empty-changeset` and
`check-adr-0087-registration` (all `--base origin/main`): exit 0.

## Acceptance notes

- **`check:sdui-lockstep` cannot see a type-union drift between the two
copies**, as H2 measures. This port closes the one such drift the card
named. The gate is deliberately not widened.
- **The two copies still differ outside the lockstep's three
comparisons.** objectui's `RegistryConfigLike` also carries `lazy?:
boolean` and a longer `isContainer` docblock. objectui has
`body-dialect.ts` and `provenance.ts`, which this copy lacks.
`kanban-quick-add.ts`, `dashboard-widget-options.ts`, `parse.ts` and
`validate.ts` differ outside the recorded region. None of that is this
card's. Carrier: none.
- **`scripts/gen-sdui-manifest-node.mjs` fails on macOS with the default
`TMPDIR`.** Its resolve hook compares `parentURL` with a runner URL
built from `os.tmpdir()` (`/var/folders/…`), but Node reports the
realpath (`/private/var/folders/…`). So no bare specifier is
re-anchored, and the run exits 1 with 16 `Cannot find package
'@object-ui/…'` failures. With `TMPDIR` set to a realpath directory it
succeeds. That is how this PR's regeneration ran. Reported to the seat,
not fixed here.
- **The generator's header says `check:sdui-lockstep` "holds the two
copies byte-equal".** It holds the grammar region, the code set and the
containment predicate only. The header is stale prose. Carrier: none.
- **The pending `.changeset/console-dd3f7e1be356.md` states that the
published manifest declares the html tier's elements "marked `tier:
'html'`".** For the manifest `@objectstack/console` ships, that was
untrue until this PR. It is true once this lands in the same release.
- **The regeneration read an existing built objectui tree at the pin**
(another worktree's `.cache/objectui-dd3f7e1be356`, symlinked read-only
into this worktree's `.cache/` and removed afterwards), rather than
rebuilding objectui on a shared host. The control leg under H1 proves
that tree reproduces the tracked artefact byte-for-byte.
- **`Clause-②: no` is the claim's declaration, copied verbatim.** The
changeset grades both packages `minor` (a new accepted value and a new
type member; a new key on the shipped manifest), so the level axis
passes under either reading of the line.

**Declared narrowing — verification ran UNLOCKED.**
`scripts/pm/os-verify-lock.sh`
could not take the shared verify lock on this host: no usable `flock`.
The shared
verify lock is declared Linux-only (`flock` is util-linux, and a stock
macOS does
not ship it), so the command below was run directly, without the lock —
a declared narrowing, not a silent one. No serialization guarantee held
for this
run, nor for any sibling agent in this container while it ran.

    pnpm --filter @objectstack/sdui-parser build
pnpm --filter @objectstack/sdui-parser build && pnpm --filter
@objectstack/sdui-parser exec vitest run --maxWorkers=2 && pnpm --filter
@objectstack/sdui-parser typecheck
    pnpm lint
pnpm --workspace-concurrency=2 --filter '@objectstack/lint^...' build
pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2
--reporter=verbose src/validate-jsx-pages.production-witness.test.ts
src/validate-jsx-pages.test.ts
pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 && pnpm
--filter @objectstack/lint typecheck

---
_Generated by [Claude
Code](https://claude.ai/code/session_local_1d2a197c-c20e-4e90-9be8-413d4d432289)_

---------

Co-authored-by: Jack Zhuang <50353452+hotlong@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
…ai#20623)

## What this is

The release-time half of the 17.5.0 release notes. The page
`content/docs/releases/v17/17-5.mdx` landed before the cut (objectstack-ai#20396) with
a `RELEASE-TIME TODO` comment listing four edits to make once 17.5.0 was
on npm. 17.5.0 was published on 2026-09-29 (`@objectstack/cli@17.5.0` at
07:58Z, the last package, `@objectstack/spec`, at 08:09Z). This PR makes
those edits, deletes both TODO comments, and updates
`content/docs/releases/v17/index.mdx`.

Docs-only: two files under `content/docs/releases/`, which is
release-owned, so this is the dedicated docs-only PR `AGENTS.md`
sanctions for that tree. It publishes nothing from any package, hence
`skip-changeset`.

## What changed

**`17-5.mdx`**

- **Publish date.** "What's new" now opens: 17.5.0 was published to the
`latest` tag on 2026-09-29, 20 days after 17.4.0.
- **Count.** The draft said it was compiled from "868 changesets pending
on `main` at `ab6fb027`". The version commit `8c87d26a` (objectstack-ai#17076)
actually consumed **958** changesets (the `.changeset/*.md` files it
deletes, README excluded). The page now states 958 as its measure and
cross-checks it against the CHANGELOGs: the 69 package `CHANGELOG.md`
files that carry a 17.5.0 section at `8c87d26a` list **1,372 per-package
entries** (703 minor, 669 patch, 0 major) in 56 of those files, and
those entries de-duplicate to exactly the same 958.
- **The 90 changesets the draft never read**, the ones consumed by
`8c87d26a` but not pending at `ab6fb027`, were each read in full and
folded in:
- **Breaking changes & migration: 45.** Two new subsections: *Written
values are held to the field's declared type* (date and datetime ISO
spellings on a real day, the year range 0001–9999, the numeric string
grammar, `precision`, `progress` bounds, `/import` thousands commas,
with a Migration table) and *An edge-branched decision takes its first
matching branch* (objectstack-ai#20344, with the stored-row caveat). The rest joined
existing subsections: RLS cross-class comparisons; org-less grants; cube
`public`; number comparands, `having` placeholders and double
accumulation; flow node config, `connector_action`, `api` flow secrets
and the connector resilience keys; list-view `tabs`, action `aria` and
view round-trip keys; `/diff` `/history` `/audit` as authoring doors and
OpenAPI `info`; remote Turso and unbuildable indexes; the one stack
authoring shape and new lint positions; QA `requires`, narrowed
published types and `retiredAfter`.
- **New capabilities: 11.** Studio form rows for 27 structured keys, the
staged `$empty` operator, the new `ComponentPropsMap` rows, and email
verification under `open`.
- **Notable fixes: 15.** Dispatcher-only hosts, `/diff` default range,
plain-text email faces, auth-settings sibling isolation, SQLite
`reclaimSpace()`, zh-CN/ja-JP/es-ES object labels, aggregate `search`,
and the OSV sweep.
- **New in Console: 2.** The fourth objectui pin move and the `trash-2`
→ `trash` icon.
- **Judged too minor to surface: 17.** Each is text only, with no
behaviour change an app or operator can reach: describe, docblock and
comment rewrites, `os migrate meta` guidance text, liveness-ledger data
and layout, a form row's declared language, a test-only import change in
`plugin-dev`, and the successor `Link` header of the deprecated
`?layers=true` flag on the environment-scoped mount.
- **Highlights** gain three bullets drawn from the above (decision
first-match, written values, the stack authoring shape). The "running
deployment" warning list gains five lines.
- Every breaking entry that needs an operator action has an
upgrade-checklist line, marked *Not exercised* unless the HotCRM upgrade
below exercised it.
- **Console.** Four pin moves now, not three: `f8a9d0fb0596 →
dd3f7e1` (`3cf6449`, objectstack-ai#20436) carries 325 releasing objectui
changesets, 41 of them declared breaking upstream. The Highlights,
"What's new" and Console sections all say four.
- **Dependencies.** `nodemailer` is `^10.0.2`, not `^9.1.1`. That is a
major bump for GHSA-6vj9-mwq6-2f5v, which has no 9.x fix. The line also
carries the operator-visible note from objectstack-ai#20564's changeset: from
nodemailer 10.0.12, `requireTLS` wins over `ignoreTLS`, so a
`transportOptions: { ignoreTLS: true }` override on a port other than
465 now upgrades to STARTTLS or fails the send, and `secure: false` is
the way to connect in the clear.
- **New subsection "Also shipped in 17.5.0 — not in its CHANGELOG".**
The publish ran from `main` at `0f6dcac5` (Release run 36536081716), 8
first-parent commits after the version commit, so the npm packages also
contain `6e3aa75e a093ce3 92fe081 3a89d45 7001918 c96beb2 ba4648d
0f6dcac`. Their changesets are still unconsumed in `.changeset/`. The
subsection gives one line per commit and says they will be listed again
in 17.6.0's CHANGELOG and that the cause is tracked in objectstack-ai#20613. The
breaking `92fe0814` (objectstack-ai#20458, cube member inner `name` retired) gets a
Migration note taken from its own changeset and a checklist entry, and
the checklist preface says where that note lives.

**`v17/index.mdx`** (following the 17.4.0 curation precedent `b11bfb9a`)

- frontmatter description: "17.0.0 through 17.5.0";
- status blockquote: 17.5.0 is released and current, published
2026-09-29, taking over from 17.4.0; a plain install resolves 17.5.0;
the minors warning names 17.5.0;
- a "17.5.0 stays in that register" paragraph drawn from the page's
Highlights, linking `#breaking-changes--migration-in-1750` and
`#upgrade-checklist`;
- the per-release list marks 17.5.0 current and 17.4.0 no longer
current;
- the checklist callout records that 17.4.0 → 17.5.0 has been exercised
only in part (seven lines, on HotCRM), and the per-release checklist
links lead with 17.5.0.

## Findings from a HotCRM 17.4.0 → 17.5.0 upgrade

These were folded in at the coordinator's request; the parent session
verified them.

- **Decision-mode flip** (objectstack-ai#20344): now a 17.4.0 → 17.5.0 table, a
standing warning that flows stored in `sys_metadata` take the new
meaning without being rewritten, and a checklist line. The line says to
review each `mode: 'inclusive'` that `os migrate meta --from 17` offers,
deleting it where the conditions partition, because applied blindly it
draws `flow-decision-inclusive-overlap`. It then says to review the
`--stored` list.
- **`specVersion` / `engines.protocol`**: the checklist now says what an
app does after a 17.x minor, from the code. `PROTOCOL_VERSION` is still
`17.0.0`, and the handshake compares only the major, so
`engines.protocol: '^17'` stays, a `^17.0.0` `specVersion` admits
17.5.0, and a `^18` range is refused `OS_PROTOCOL_INCOMPATIBLE`.
"Protocol 18" is the migration registry's next major; the 17.5.0 schemas
already refuse its shapes, which is why `os migrate meta --from 17` runs
to 18. The Breaking-changes intro carries the same sentence.
- **Seven checklist lines** are marked *Exercised on HotCRM (a 17.4.0
app with a 17.4.0-created SQLite DB), 2026-09-29* with the observed
result: `os doctor` scheduled-work reading, the `account-issuer`
pre-flight, `os migrate meta --from 17` (41 refusals in 874 lines, 240
of them generic protocol-18 notices, so filter the output), the decision
review with `--stored` (0 rows), `page.assignedProfiles`, lookup screen
field `reference`, and `chartConfig` (34 sites). Every other line stays
*Not exercised*, and the preface and the v17 index callout say the hop
was exercised only in part.

## Citations

Every added `#N` was resolved on the board: 144 candidate numbers from
the 90 commits and the 8 post-version commits, all resolving, and objectstack-ai#20613
is open. SHAs are 7-character short SHAs, and each was verified to
resolve unambiguously.

## Gates run (workspace installed)

The full sweep ran on `2b3b323b`. The head `664854a4` changes one phrase
in one checklist line, and on it the MDX parse, `check:doc-anchors`,
`check:role-word`, `check:issue-citations --base origin/main`, the
audit-scope gate and the release-page gates were re-run, all green.

Named in the task, all exit 0:

- `pnpm check:doc-anchors`: 391 internal fragment links, all resolve.
- `node scripts/check-issue-citations.mjs --base origin/main`: 119
citations judged (104 resolve as pull requests, 1 as an issue, 14
cross-repo `objectui#N` unjudged); every added citation resolves.
- `pnpm check:role-word`: no new occurrences.
- `node scripts/docs-audit/check-audit-scope.mjs`: in sync, and
release-owned pages are review-only.
- `check-release-page-status`, `check-release-section-coverage` (plain
and `--strict`) and `check-release-notes`: all OK.
- MDX parse: both pages compile with `@mdx-js/mdx` 3 + `remark-gfm`, and
all 7 tables on `17-5.mdx` parse with no ragged rows.

Derived with `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands`: 47 commands, **all 47 exit 0**.
The first sweep hit 5 prerequisite refusals (exit 3, or `check:docs` on
the missing gitignored `json-schema` tree) from unbuilt
`@objectstack/spec`, `@objectstack/formula`, `@objectstack/lint` and
`@objectstack/client-react`. None was a finding. Those packages were
built and the whole list was re-run. Among the 47:
`check:doc-authoring`, `check:docs-single-h1`, `check:docs-redirects`,
`check:corpus-claim-drift`, `check:docs-transcript-drift`,
`@objectstack/spec check:docs` / `check:skill-examples` /
`check:liveness`, `@objectstack/lint check:doc-formula-expressions` /
`check:doc-security-posture`, `check-doc-frontmatter`,
`check-docs-section-name`, `check-section-landing-index` and
`check:nul-bytes`.

The diff was also re-read by hand; the fixes from that pass are the
second commit (`da443bdb`).

## Not in this PR

`content/docs/upgrading.mdx`'s per-release table still reads "v17.4.0 —
⛔ checklist not written; machine-draft notes only" and has no 17.5.0
row. It is a hand-written tree outside `content/docs/releases/`, so it
is left for a separate change.


---
_Generated by [Claude
Code](https://claude.ai/code/session_014VGCS11YUtYAiinRcdqQwL)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
…ough @objectstack/console/package.json, so a project without its own manifest gets full component checking (objectstack-ai#20589)

Fixes objectstack-ai#19922
Clause-②: no

## What this changes

`resolveSduiManifest()` (`packages/cli/src/utils/sdui-manifest.ts`) is
the one resolver `os validate`, `os compile` / `os build` and `os lint`
use to arm the JSX page gate. `os dev` and `os start` run `compile`
before they boot when `dist/objectstack.json` is missing or `--compile`
is passed, and `dev`'s default watch mode reruns it when a watched file
changes. The resolver's second place to look, the copy
`@objectstack/console` ships as `dist/sdui.manifest.json`, asked Node
for that file by its own subpath. The console's `exports` map publishes
`./package.json` alone, so the resolve threw
`ERR_PACKAGE_PATH_NOT_EXPORTED`, a `catch` swallowed it, and every
project with no `sdui.manifest.json` of its own had its `kind: 'html'`
pages checked at parse level only.

The fallback now resolves `@objectstack/console/package.json` from the
CLI's own location (`import.meta.url`, the CLI's declared dependency in
the same fixed release group) and joins `dist/sdui.manifest.json` to its
directory, through a new `consoleSduiManifestPath(origin)`. The
console's `exports` stays closed. `resolveSduiManifest(cwd,
consoleOrigin)` gains an optional origin, used only by the pins.

The old module header handed one decision to whoever made this leg
reachable: what a broken shipped copy should do. It now gets the project
leg's rule. A shipped copy that is present but cannot be read or parsed
is `unusable` (new `source: 'console'`), and the command is refused with
exit 1, naming the file, with the remedy "reinstall
@objectstack/console". It is never read as "not found". With no page to
check it is read by nothing and not refused, the same as the project
leg.

## This round (the seat's unlock record `5890591366` on objectstack-ai#19922)

The ledger entry `ui-html-page-div-refused` landed on `main` (objectstack-ai#20592, PR
objectstack-ai#20610), and Version Packages objectstack-ai#17076 consumed
`.changeset/sdui-manifest-one-producer.md`. This round:

1. **Merged `origin/main` at `f1e921ab8e`** (a merge, not a rebase;
merge commit `f9cb969f44`). One conflict:
`.changeset/sdui-manifest-one-producer.md`, modify/delete, resolved in
favour of `main`'s deletion.
`build-json-failure-conversions.e2e.test.ts` and
`validate-json-failure-conversions.e2e.test.ts` auto-merged: `main`
changed other regions of both, and the `box` fixture line and its
docblock sentence survived. The branch's delta against `main` is exactly
the 7 intended files. `main` has since gained one commit (`cd901d7a5f`),
which touches none of them.
2. **The correction moved into this PR's changeset.** The released note
is `@objectstack/console` 17.5.0, patch entry `28ce612`. A new paragraph
goes through its closing paragraph one sentence at a time:
- two sentences stop being true with this release: the file is no longer
"only present in the tarball", and the CLI fallback no longer "keeps
parse-level validation";
   - two still hold: `exports` is unchanged.

No `CHANGELOG.md`, no `content/docs/releases/` and nothing under
`packages/spec/` is edited.
3. **The ADR-0087 marker** now reads `not-required (already-registered
ui-html-page-div-refused)` with its reason. The gate's verdict:
"check-adr-0087-registration: 1 declared-breaking changeset(s), each
carrying an ADR-0087 disposition … not-required (already-registered)",
exit 0.
4. **Two sentences re-measured and corrected:**
- **"the html-tier renderer still renders `div`" was false.** At the
pinned objectui `dd3f7e1be3`, read with `git show` from the sibling
checkout (nothing checked out, nothing edited):
- `packages/components/src/renderers/layout/page.tsx:487-488` builds the
html compile's whitelist from `getKnownTypes()` minus `deprecationFor(t,
'html')`;
- `packages/components/src/renderers/basic/div.tsx` registers `div` with
`deprecated.surfaces: ['json', 'html']`;
- `nameHtmlTierReplacement` turns the resulting `forbidden-tag` into a
refusal naming the replacement.

That pin shipped in `@objectstack/console` 17.5.0: its CHANGELOG entry
`3cf6449` says a `kind:'html'` page that authors a `div` "is refused at
compile time, and the error names `box`". The changeset now says the
console has refused `div` since 17.5.0, and that what is new is every
other tag the manifest does not declare. The console's html compile
accepts every non-deprecated registered component, while the manifest
declares the public contract plus the html intrinsics. Measured below
with `avatar`.
- **"`objectstack compile` (which `dev` and `start` run first)" was
inexact.** It now says exactly when they run it: `dev.ts:319` compiles
on `flags.compile` or a missing artifact, and `dev.ts:383` re-runs it in
watch mode; `start.ts:228-232` has the same condition.

## Premise and hypotheses, measured

Round-1 readings (on `f11b5f20a2`) are kept where they still hold.
Round-2 readings are on `77338a7186`: Node v26.7.0, macOS.

- **H0 (premise holds).** On unmodified `f11b5f20a2`, a real `os init`
project with a `kind: 'html'` page rooted in `div` passes `os validate`,
`os compile` and `os lint` at exit 0. Each prints only the parse-level
notice, and it does so even with a `cmp`-identical copy of the tracked
manifest at `packages/console/dist/sdui.manifest.json`. From
`packages/cli/dist`, the old subpath throws
`ERR_PACKAGE_PATH_NOT_EXPORTED`.
- **H1 (the route finds the file in both layouts).**
- Workspace: `consoleSduiManifestPath()` answers
`packages/console/dist/sdui.manifest.json`.
- Installed package: `npm pack` of `packages/console` with a stand-in
dist lists `dist/sdui.manifest.json`. Extracted under a scratch
`node_modules`, the old subpath throws `ERR_PACKAGE_PATH_NOT_EXPORTED`
from a sibling CLI origin, while `resolveSduiManifest` answers
`resolved`.
- **H2 (Clause-② arm: narrowing).** Round 2, merged tree, with the
console copy present (`cmp`-identical stand-in):
- a `div` page gives exit 1 (`jsx-forbidden-tag`,
`jsx-unknown-component`);
  - a `box` page gives exit 0, with no findings;
- an `avatar` page gives exit 1 (`jsx-forbidden-tag`,
`jsx-unknown-component`).

With no copy, all three exit 0 with the notice only. `avatar` is
registered at the pin (`renderers/data-display/avatar.tsx:17`) and not
deprecated, so the console's html compile renders it and nothing refused
it before this change. That is the narrowing the `(narrowing)` arm and
BREAKING rest on.
- ⚠️ **Round 1 misread this half.** It took "the renderer still renders
`div`" from ruling A's reading, which predates objectui#10757, instead
of reading the pin. The pin had landed on `main` (objectstack-ai#20436) before round 1
ran. For `div`, this change moves a refusal the 17.5.0 console already
gives at render time to author time. The arm still holds because of the
undeclared tags.
- **CLI fixtures the live fallback newly refuses** (round 1, with the
console copy present): 23 tests went red across
`build-json-failure-conversions.e2e` (5),
`validate-json-failure-conversions.e2e` (4) and
`jsx-gate-manifest-notice.e2e` (14). `lint-conversion-notices.e2e`
stayed green, but its page is refused too. The three conversion fixtures
moved from `div` to `box`. The notice file's 14 manifest-less cases are
skipped by name where the CLI's own console copy exists. They run in the
CI job, which builds no console, and their rules are pinned hermetically
in `src/utils/sdui-manifest.test.ts`.
- **Examples:** only `examples/app-showcase` carries html pages (three).
- **H3 (shipped pages stay clean)**, round 2, merged tree. `main`
brought a regenerated `sdui.manifest.json` carrying `tier: 'html'` marks
(objectstack-ai#20582).
  - `validate-jsx-pages.production-witness.test.ts`: 5/5 pass.
- `examples/app-showcase` with the console copy present: exit 0 on `os
validate` / `os compile` / `os lint`, with zero `jsx-*` / `sdui/*`
findings.
- **H4 (ablation, round 1)**, through `node
scripts/ablation-replace.mjs` in WRAP mode:
- the anchor `resolve(CONSOLE_PACKAGE_JSON)` went 1 → 0, and
`resolve(CONSOLE_SDUI_MANIFEST)` (the old subpath) 0 → 1;
  - 4 console-leg pins went red ("expected undefined to be defined");
- restore: the blob is back at the HEAD blob `be1f8d4ad33e`, and `git
diff HEAD` is empty.

The pins import the subject from `src/`, so no `dist/` sits on that
path. This round changed no source or test file.

## Tests, at `77338a7186`

- The whole CLI `unit` project (`--project unit --maxWorkers=2`) with a
real-path `TMPDIR`: 234/234 files, 3347/3347 tests. With the default
macOS `TMPDIR`: 232/234. The 2 files are `published-subpath-console.pin`
and `published-subpath-hook-body.pin`, 5 cases comparing `/var` against
`/private/var`. They are host-only and untouched here.
- `pnpm --filter @objectstack/cli typecheck` (`tsc --noEmit` plus
`check:test-typecheck`): exit 0.
- The four touched nightly `*.e2e` files (`OS_TEST_TIERS=nightly
--project integration`):
  - with the console copy present: 53 passed, 14 skipped;
  - without it (the CI state): 67/67 passed.

  The rest of the integration layer is declared to CI.

## Gates, at `77338a7186`

- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands`: 63 derived, the same 63 as round 1. All 63 exit 0, and
`--ran` reconciled "63 run, 0 NOT-MEASURED (a DERIVED zero — all 63
recorded an exit code and none of them is 3)".
`check:dual-build-cjs-loads` first answered PREREQUISITE NOT MET, then
exit 0 after building its eight missing packages.
- `check-adr-0087-registration --base origin/main`,
`check-empty-changeset --base origin/main` (it now reads "No changeset
from the merge base modified or deleted by this diff") and
`check-changeset-no-major --base origin/main`: all exit 0.
- Roster rows that could apply, all exit 0: `check-changeset-fixed`,
`check-sdui-manifest` (plus `--self-test`), `check:authz-resolver`,
`check:error-code-casing`, `check:filter-alias-parity`,
`check:cli-examples-parity`, `check:published-readme-exports`,
`check:scaffold-emission-policy`, `check:console-injection`.
- `pnpm lint` (full repo, not narrowed): exit 0, no output.
- `node scripts/check-issue-citations.mjs --base origin/main`: exit 0.
- `check:nul-bytes`: exit 0, plus a control-byte scan of the 7 changed
files: 0.

**Declared narrowing — verification ran UNLOCKED.**
`scripts/pm/os-verify-lock.sh`
could not take the shared verify lock on this host: no usable `flock`.
The shared
verify lock is declared Linux-only (`flock` is util-linux, and a stock
macOS does
not ship it), so the command below was run directly, without the lock —
a declared narrowing, not a silent one. No serialization guarantee held
for this
run, nor for any sibling agent in this container while it ran.

every build, test, typecheck, ablation and `pnpm lint` command named
above

## Acceptance notes

- **Where the `div` → `box` prescription reaches an upgrader.** The
CLI's refusal text does not carry it: the gate answers "is not an
allowed component" / "is not a known component", from
`@objectstack/sdui-parser` (`parse.ts`). The upgrade guide does not
carry it either: `packages/spec/scripts/build-upgrade-guide.ts:78` loops
majors up to `PROTOCOL_MAJOR`, `PROTOCOL_VERSION` is `17.0.0`, and
`docs/protocol-upgrade-guide.md` does not name
`ui-html-page-div-refused`. What does carry it:
  - this changeset's FROM → TO table;
  - the console's own render-time refusal, which names `box`;
- `objectstack migrate meta --from 17`. Measured on a stack with a `div`
page, it lists the entry as one of 242 "manual change(s) require your
judgment", headed "⚠ [protocol 18] kind:'html' page source …", with
`box` as the replacement, and exits 0.
- The ledger entry's own `why` text
(`packages/spec/src/migrations/entries/semantic/18.ui-html-page-div-refused.ts`,
the spec seat's file) still says "`objectstack compile` (which `dev` and
`start` run first)", the phrasing corrected here. Noted, not edited.
- Release order, flagged by the seat in `5890591366`: Version Packages
PR objectstack-ai#20639 carries the ledger entry's changeset. If it merges before this
PR, the ledger row ships one release ahead of the CLI refusal it
describes.
- `packages/cli/src/utils/scaffold-validate.ts` (the note at :128-:133)
was re-read. It is true now, so it is not edited. A pre-existing
imprecision stays as it was: `os init` reads the invoker's directory,
which may carry its own `sdui.manifest.json` (this repository's root
does).
- Comment drift outside this claim, noted only:
- `.github/workflows/lint.yml` (:899) and
`scripts/check-sdui-manifest.mjs` (:28-30, :240) still say
`resolveSduiManifest()` degrades to parse-only silently;
- the header of `packages/lint/src/validate-jsx-pages.ts` still calls
manifest validation "not wired";
- `docs/qa/platform-checklist/areas/studio-authoring.json` describes the
showcase tree as flex/div/a.
- The five macOS-only `published-subpath-*` failures come from a
`tmpdir()` path compared with the real path that module resolution
returns. They are host-specific.
- Measurement scaffolding was all in scratch, or in this worktree's
gitignored `packages/console/dist/`, with each stand-in trap-removed.
`git status --porcelain` printed 0 lines after every run.

---
_Generated by [Claude
Code](https://claude.ai/code/session_local_1d2a197c-c20e-4e90-9be8-413d4d432289)_

---------

Co-authored-by: Jack Zhuang <50353452+hotlong@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…119, the objectui#11105 fix) (objectstack-ai#20706)

Fixes objectstack-ai#20638

Clause-②: no

Bumps the console pin: `.objectui-sha` `dd3f7e1be356` -> `db11afd4967c`
(objectui `origin/main` HEAD at bump time, on main). The pin ships
`@object-ui/console` 17.6.0 (`apps/console/package.json` at the pin and
`@object-ui/core` workspace version both 17.6.0; `npm view
@object-ui/console@17.6.0 version` answers 17.6.0).

## Containment (objectui `merge-base --is-ancestor C db11afd`, full
clone, not shallow)

| commit | subject | result |
|---|---|---|
| `a5841be351` | flow designer shows a connector action's description
(objectui#11028) | exit 0, contained |
| `99878d8e3a` | validateTree no longer calls a declared bind unknown
(objectui#11008) | exit 0, contained |
| `49f76725aa` | matrix report with columns draws its declared chart
(objectui#10964) | exit 0, contained |
| `ae582b70a` | number field useGrouping (objectui#11026) | exit 0,
contained |
| `5b2ea17570` | Studio previews show authored descriptions and labels
(objectui#11027) | exit 0, contained |
| `3100bef65c` | server-grouped grid selects field names
(objectui#11105, PR objectstack-ai#11119) | exit 0, contained |
| control `dd3f7e1be3` (old pin) | | exit 0 |

The target is objectui `origin/main` HEAD (`git branch -r --contains`
lists `origin/main`).

## What changed

- `.objectui-sha`, `sdui.manifest.json`,
`scripts/sdui-manifest.record.json` (via
`scripts/gen-sdui-manifest-node.mjs`, no arguments: the
`--objectui-version` route is retired per that script), and
`.changeset/console-db11afd4967c.md` (written by
`scripts/bump-objectui.sh`, minor).
- `pnpm objectui:build` at the new pin built the console dist (64404 KB)
with no API break against framework code.

## Follow-up (pushed on this branch, two commits, written
2026-09-29T20:01Z)

Requested by the PM seat after the first report: the two gates that sat
outside the first file surface.

- `packages/sdui-parser/objectui-lockstep.json` re-recorded with `pnpm
gen:sdui-lockstep` against `db11afd4967c`; `pnpm check:sdui-lockstep`
now exits 0.
- The 49 spec sources that asserted the old pin were re-measured, not
re-labelled: each anchor mapped through the `dd3f7e1be356..db11afd`
diff and re-read, hashes and counts re-taken with the method each record
names, and only then the pin and the moved numbers updated. Files:
`component.zod.ts`, `component.test.ts`, `view.zod.ts`,
`dataset.zod.ts`, `functional-completeness.ts`,
`api-methods-batch-conformance.test.ts`, six
`migrations/entries/semantic/18.*` files and the regenerated
`migrations/registry.ts`. `pnpm --filter @objectstack/spec run
check:objectui-pin-citations` exits 0 (49 asserting citations match, 7
anchor content assertions verified).
- `content/docs/references/ui/view.mdx` regenerated with `gen:docs`: the
`FormField.span` describe names the pin, so its sha moved. This is the
only non-comment text change; no schema or behaviour moved.
- One claim was falsified, not just moved: objectui#5157 closed
`ObjectMapConfigSchema` with `.strict()`, so the `ListMapConfigSchema`
record in `view.zod.ts` no longer holds when it says the renderer's
schema is a plain `z.object` that parses an undeclared key clean. The
three anchors were re-pointed and the two false sentences were qualified
in the comment. That prose wants a human read.
- These edits ship inside published text, so
`.changeset/objectui-pin-citations-db11afd4967c.md` carries an
`@objectstack/spec` patch (commit `d8bac3877d`).

## Resolved since the first report

- **ADR-0087 disposition, the maintainer's ruling** (takeover claim
`5903537467` on objectstack-ai#20638): `.changeset/console-db11afd4967c.md` now
carries `adr-0087: not-required (no-migration-prescription)`, worded as
in objectstack-ai#20436.
- The diff moves the pin and what travels with it, and adds, removes or
renames no ObjectStack-authorable key.
- The pin-citation re-measure changes describe and migration-entry text
only.
- The 6 declared-breaking entries are objectui's own surfaces
(`@object-ui/types`, `@object-ui/core`, `@object-ui/plugin-dashboard`).
  - `check-adr-0087-registration --base origin/main` exits 0.
- **Base:** `origin/main` `7053333e1e` merged at `ef6276c0e9` through
`scripts/pm/os-regen-merge.sh`. Only
`packages/spec/src/migrations/registry.ts` overlapped; it text-merged,
and `check:migration-registry` reads it current.
- **The two local NOT MEASURED gates now run green:**
`check:dual-build-cjs-loads` and `check:type-check-debt` both exit 0
after a whole-workspace build.

## Boot at the new pin

- `scripts/build-console.sh` at `db11afd4967c`, with this tree's client
and spec injected and the canary present.
`assert-console-spec-injection` passes: 105 of 145 injected-only
descriptions present, all 160 published-only absent. `chunk-membership`
reads `client` in `vendor-objectstack`.
- `examples/app-showcase` booted with `objectstack dev --ui --fresh
--no-seed-admin`; `bootstrap-status` read `hasOwner: false`.
- Headless Chromium opened `/_console/setup` and submitted the owner
form. `bootstrap-status` flipped to `hasOwner: true`, and the session is
the new owner (`platform_admin`, `org_owner`). The wizard landed on
`/_console/home`, with Showcase and Setup listed.
- Signed in again through `/_console/login`: Showcase opens its
Capability Map html page, and Setup opens System Overview. 0 page
errors, 0 `TypeError`, 0 5xx. The only 404 is `GET
/api/v1/usage/storage`, as at the old pin.

## Governed surface

None of the 20 changed paths is a row of `GOVERNED_SURFACES` in
`scripts/pm/check-governed-merges.mjs` (rows: `docs/adr/`, `.claude/`,
`skills/`, `AGENTS.md`, `CLAUDE.md`, `docs/NORTH-STAR.md`). No release
act performed.

Gates at head `ef6276c0e9`: 125 derived, 125 run, 0 NOT-MEASURED, 0
unrun; all 125 exit 0. `packages/spec` tests: 16991 passed (576 files).
`sdui-parser`: 199 passed. Spec and sdui-parser typecheck clean.

---
🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant