Repository navigation
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
Conversation
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>
📓 Docs Drift CheckThis PR changes 4 package(s): 6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 7 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 138 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # 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
|
…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>
…`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>
…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>
…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>
…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>
Clause-②: no
Moves the console pin from objectui
f8a9d0fb0596to objectuimaindd3f7e1be356, 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
mainAt any objectui
mainfrom8cc0e2984onward, a console built byscripts/build-console.shdies at boot. The page stays on the splash screen withTypeError: u is not a function.The script always injects this tree's
@objectstack/clientthroughOBJECTSTACK_CLIENT_DIST. That client resolved outsidenode_modules, so it missed thevendor-objectstackgroup and landed inframework.frameworkimportsdata-adapter, anddata-adapterrequires the CJS client at its top level beforeframeworkhas 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
dd3f7e1be356is its merge commit. At this pinchunk-membership.jsonreadsclient: { "vendor-objectstack": 1 }, and the console boots.What changes
.objectui-sha+.changeset/console-dd3f7e1be356.mdbump-objectui.shover the whole rangef8a9d0fb0596...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+ recorda,p,h1–h6,ul/ol/li,table,img, …).divis not among them.check-sdui-manifest --require-objectuipasses.packages/sdui-parser/objectui-lockstep.json.cache/objectui-dd3f7e1be356: 214 grammar lines, byte-identical, and all 26 codes agree.packages/spec/srcf8a9d0fb0→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 atf8a9d0fb0first, then re-taken at the pin; all 96 dark tokens are still 0. Theview.zod.tsmap-config record's five quoted anchors are re-pointed; none changed what it reads.HTML page failed to compileat the pin. objectui now refusesdivonkind:'html'pages and namesboxas the drop-in swap (objectui#10757). Every wrapper moves tobox, and all three render with their styles intact.packages/lintJSX ratchetsdui-jsx-baseline.jsonis deleted, as its header prescribes, and the census assertion inverts to "the wired run is clean".trash-2→trashTrash2from the runtimeiconsrecord the console resolves names through.icon: 'trash-2'draws nothing, whiletrashis the identical glyph (objectui measured it node for node). Changed: four@objectstack/platform-objectsdelete actions, the app-todo delete action, the flow builder'sdelete_recordpalette entry, and theBulkAction.icondescribe example. The changeset ispatchforplatform-objectsandspec.FormField.spandescribespecpatch 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 atdd3f7e1be.check:generated: all 15 artifacts are up to date.check:sdui-lockstepOK,check-sdui-manifest --require-objectuiOK,check:console-shaOK,check:cross-package-test-inputsOK.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-modesees a stampmismatch, andcheck-livenesssees 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:/setupwizard 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, andbootstrap-statusflips tohasOwner:true.403 SELF_REGISTRATION_CLOSED, both at the API and through/register. Anonymous/setupredirects to/login?redirect=%2Fsetup, and the owner's/setupopens Setup./registerand joins asmember./api/v1/storage/files/<id>, not adata:URL), changes the password (old one refused, new one accepted) and signs out from the user menu.9f0c84awere 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'sCancel/Next/Step 1 of 3,MEMBER COUNT). A read-only member's wizard shows disabled inputs with no explanation. Record delete confirms through the nativewindow.confirm. The console probesGET /api/v1/usage/storage, which answers 404 on this server.Generated by Claude Code