Commit 48eb9c1
Fixes #21504
Clause-②: yes (narrowing)
Retires the `ai:chat_window` page component type under ADR-0049
enforce-or-remove, refused by name through
`RETIRED_PAGE_COMPONENT_TYPES`, per triage ruling `5963897014` and the
`user:profile` precedent (#14159, landed in PR #15112). No renderer for
it ever shipped: objectui leaves it unregistered on purpose, because the
floating chat overlay is the supported AI chat entry point. So an
authored node validated clean and then drew "Unknown component type".
`ai:suggestion` is not touched; triage ruled it a different class.
## What changed
| surface | change |
| --- | --- |
| `ui/page.zod.ts` | `'ai:chat_window'` leaves `PageComponentType` and
joins `RETIRED_PAGE_COMPONENT_TYPES`. Its message names the floating
chat overlay as the supported entry point and carries no tracker number.
`PageComponentSchema.type` already consults the map, so the open string
arm cannot re-admit the name. The type's `.describe()` and the enum
docblock name the new member. |
| `ui/component.zod.ts` | `AIChatWindowProps` (`mode`, `agentId`,
`context`, `aria`) is deleted. The `ComponentPropsMap['ai:chat_window']`
row stays as `retiredComponentProps('ai:chat_window')`: a `z.never` that
refuses every bag, `{}` included, with the same prescription. |
| ADR-0087 | D3 semantic entry `ui-ai-chat-window-retired`, one
`STEP18_RATIONALE` fragment, and the `RETIRED_DEFS_BY_MAJOR[18]` entry
`ui/AIChatWindowProps`, generated by `gen:migration-registry`. No D2
conversion, see below. |
| pins | `component.test.ts` gets a new describe with `code`, `path`,
`params` and the first sentence at each door: the map, the row (empty
and populated bags), `PageComponentSchema`, `PageSchema` at the element
path, and the enum error map. `ai:suggestion` is the control and parses
at every door, and the open arm stays open.
`component-type-vocabulary.test.ts`: the type stays known, leaves the
typo candidates, and `ai:` stays reserved. The old `should parse
ai:chat_window with default` accept pin is flipped into the new
describe. |
| enumeration pin | `component-props-unknown-members.pin.test.ts` (PR
#21502) loses its `ai:chat_window` `context{}` "no-reader" line, because
the member left with the def. |
| generated | `api-surface/`, `export-origins/`, `declaration-map/`,
`authorable-surface/` (4 lines, proof: "def no longer emitted by this
build"), `authorable-defaults/`, `json-schema.manifest/` (1 key),
`docs-import-surface.baseline.json`, the strictness-ledger counts, and
the reference docs. Each was regenerated by its generator. The manifest
and authorable-surface lines were deleted as the build gate prescribes,
and its proof check accepted them. `authorable-surface.base.json` is
untouched. |
| docs | `content/docs/ui/pages.mdx` component list says the truth. |
| lint (comment only) | `validate-page-field-bindings.ts` used
`AIChatWindowProps.agentId` as its example of a non-field string prop.
The example now names a live one, `ElementImagePropsSchema.alt`. |
| changeset | `@objectstack/spec` `minor`, BREAKING banner, `Clause-②:
yes (narrowing)`, FROM → TO table, ADR-0087 marker `registered
ui-ai-chat-window-retired`. |
## How this follows the precedent, and the one place the claim's wording
differs
The PR #15112 shape is matched element by element: the enum value
removed, a map entry, the enum error map and the node-level check
sharing one string, the row kept as `retiredComponentProps`, pins at the
three doors with a control, a vocabulary pin, regenerated artifacts, the
docs list and a `minor` changeset. The claim and the dispatch said that
"the `ComponentPropsMap` row ... go[es]". The precedent does not delete
the row. It replaces the row's props schema with a whole-bag refusal,
and its docblock says why: deleting the row would turn a loud retirement
into a silent skip on every reader that dispatches on it. Two tests on
this tree depend on that. `component-type-vocabulary.test.ts` requires
every member of `RETIRED_PAGE_COMPONENT_TYPES` to stay
`isKnownComponentType`. objectui's `registry-inputs-spec-parity.test.ts`
at the pin classifies a retired type through its kept row. So the row
stays and `AIChatWindowProps` goes. That is how "the type leaves ... its
`ComponentPropsMap` row" lands here.
Two kit items go beyond PR #15112, because the ruling asked for them.
#15112 had no D3 entry: its row had zero keys, and its disposition was
`not-required (no-migration-prescription)`. This one has a D3 entry
because triage named one. It is in the element-node shape of
`element-filter-and-form-node-refused`. It also has a
`RETIRED_DEFS_BY_MAJOR` entry, because a published def leaves the build.
No D2 conversion is registered. The precedent registered none, and the
only edit is deleting an authored page node, which is a layout decision
that conversions must not make.
## Census (A1), at `529d9711fb`
- `ai:chat_window`: `examples/**` 0, `packages/apps/**` 0, `apps/**` 0,
`skills/**` 0. `content/docs/**` had 3 hits: 2 in the auto-generated
references and 1 in the `pages.mdx` list. The other `packages/**` hits
were the spec's own list and row, its tests, and a frozen formatter
fixture in `scripts/format-type.test.ts`, which is left as is, as #15112
left `user:profile` in it. Control in the same query shape:
`element:divider` appears in 3 example files and `record:details` in 12.
- `AIChatWindowProps`: the def, the generated artifacts, and one
illustrative comment in `packages/lint`. That comment is updated, and it
is not a consumer.
- objectui at the pin `89cad75d55`: there is no renderer, and the
palette excludes the type. No non-test source imports
`AIChatWindowProps` or indexes the row. The `'ai:chat_window'` key sits
only in `Record` objects keyed by string, and in comments and tests. So
the Console Pin Gate's build does not reference the removed names.
- `skills/**` names nothing, so it is untouched (no Tier H).
## Ablation (both legs committed first, mutated through
`scripts/ablation-replace.mjs` with an anchor that must hit, restored by
blob)
The subject resolves by relative import (`./page.zod`,
`./component.zod`) to `src`, so no `dist` rebuild was involved.
- **Leg A, the dispatch's ablation:** delete the `ai:chat_window` entry
from `RETIRED_PAGE_COMPONENT_TYPES` (anchor 1 → 0, blob `7d2faf0b1f79` →
`501c4251ee90`). The observed direction is louder than the pin.
`component.test.ts` fails to load, with 1 file failed and no tests run.
The error reads: retiredComponentProps: `ai:chat_window` has no
RETIRED_PAGE_COMPONENT_TYPES entry (page.zod.ts). The kept row's factory
refuses a row that has lost its prescription.
- **Leg B, so the by-name pins themselves judge:** the same deletion,
plus the row turned into a non-throwing `emptyProps('ai:chat_window')`.
Result: 5 failed and 360 passed. The failures are the map pin, the row
pin, `PageComponentSchema` by name, `PageSchema` at the element path,
and the enum error map.
- **Restore:** both files' blobs equal `HEAD` (`7d2faf0b1f79`,
`891d947a3232`), `git diff HEAD` is empty, and `git status` is clean.
Re-run after the restore: `component.test.ts` has 365 of 365 passing.
## Gates, at `3f17d5229e` (merged `origin/main` `88fb5e85a0`, which
touched no spec, lint or docs file this PR edits)
- `@objectstack/spec` `build` exit 0. `check:generated`: `✓ All 15
generated artifacts are up to date`.
- `@objectstack/spec` `test` (`--project local`): 603 files, 17845
passed and 1 todo. `typecheck` exit 0, with `check:test-typecheck: OK`.
- `@objectstack/lint` build closure, then `test`: 119 files, 5615 passed
and 5 skipped. `flags ai:chat_window` runs off the map. `typecheck` exit
0.
- `check:liveness` exit 0, `check:migration-registry` `✓ ... is current
(354 semantic, 246 retired-key, 218 retired-def)`, `check:doc-authoring`
exit 0, `check:nul-bytes` `OK (... no raw ASCII control bytes)`.
- Changeset gates, with this body as the `--event` payload:
`check-adr-0087-registration` `✓ 1 declared-breaking changeset(s) ...
registered ui-ai-chat-window-retired (new here:
ui-ai-chat-window-retired)`. `check-changeset-no-major` `✓ LEVEL AXIS:
this PR declares clause-② yes (narrowing)`. `check-empty-changeset` exit
0.
- `dispatch-gates --ran`: `✓ 114 derived famil(ies) accounted for — 112
run, 2 NOT-MEASURED`.
- NOT MEASURED: `check:dual-build-cjs-loads`, reason: it needs a
whole-repo `pnpm build`, and it exited 3 with PREREQUISITE NOT MET.
- NOT MEASURED: `check:type-check-debt`, reason: repo-wide tsc, killed
by a 420s per-gate timeout on the shared box. The two packages this PR
touches typecheck green.
- Both are declared to CI.
- NOT MEASURED: `@objectstack/spec` `test:repo` (`--project repo`),
reason: one idle worker under 2% CPU for 10 minutes with no output,
stopped. It is CI's.
## Acceptance notes
- **objectui will need a reconciliation when it next bumps
`@objectstack/spec`.** It is the same step objectui#7122 took for
`user:profile` and `element:form`. At the pin, `block-config.test.ts`
("every exclusion names a real spec type") and the
`UNJUDGED_SPEC_BLOCKS` entry in `registry-inputs-spec-parity.test.ts`
("NOT REGISTERED, DELIBERATELY", whose class is exclusive with "RETIRED
UPSTREAM") both still treat `ai:chat_window` as a live enum member. They
are objectui's tests against its installed spec, so they do not affect
this repo's CI. Who picks it up: the next objectui spec-bump PR.
- The enumeration pin's `no-reader` reason kind no longer has a user. It
is left as ledger vocabulary.
- The prescription names `defaultAgent` for what `agentId` was used for.
objectui at the pin forwards `app.defaultAgent`, limited to `ask` /
`build`, to the chat dock's one agent resolver
(`app-shell/src/layout/ChatDock.tsx:242`,
`hooks/surfaceAgent.ts:127-128`).
---
_Generated by [Claude
Code](https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 31d2255 commit 48eb9c1
22 files changed
Lines changed: 393 additions & 74 deletions
File tree
- .changeset
- content/docs
- references
- ui
- ui
- docs/audits/2026-07-unknown-key-strictness-ledger.counts
- packages
- lint/src
- spec
- api-surface
- authorable-defaults
- authorable-surface
- declaration-map
- export-origins
- json-schema.manifest
- src
- migrations
- entries
- retired-defs
- semantic
- ui
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
36 | | - | |
37 | | - | |
| 36 | + | |
| 37 | + | |
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
| |||
363 | 363 | | |
364 | 364 | | |
365 | 365 | | |
366 | | - | |
| 366 | + | |
367 | 367 | | |
368 | 368 | | |
369 | 369 | | |
| |||
374 | 374 | | |
375 | 375 | | |
376 | 376 | | |
377 | | - | |
| 377 | + | |
378 | 378 | | |
379 | 379 | | |
380 | 380 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | | - | |
| 16 | + | |
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | | - | |
| 20 | + | |
21 | 21 | | |
22 | 22 | | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | 23 | | |
46 | 24 | | |
47 | 25 | | |
| |||
0 commit comments