Repository navigation
Commit c30c8dd
fix(types)!: the AI node declarations stop offering members no runtime honours (objectui#10874) (#10890)
Fixes #10874
Clause-②: yes
Implemented on `claude/issue-10874-ai-node-faces-agree` by session
`session_014mXUNuFomfj24w7s1pZzhN` (`domain:ui` seat 2 dispatch, claim
`5862305527`). Serial after PR objectui#10870, which has landed. Head
`ce2958f243`: fix round 1 on the contract review, which reset the
handler members per key by the objectui#6124 rule.
## What changed
- **TypeScript face** (`packages/types/src/ai.ts`), settled per key on
`AIFormAssistSchema`, `AIRecommendationsSchema` and `NLQuerySchema`:
- **Runtime slots, now callable.** `AIRecommendationsSchema.onSelect`
and `.onDismiss` become `(item: AIRecommendationItem) => void`, and
`NLQuerySchema.onSubmit` becomes `(query: string) => void`. Each was
typed `string`.
- Why: each node type is registered to its raw component,
`SchemaRenderer` spreads the node's own keys onto the component's props,
and `AIRecommendations` and `NLQueryInput` call these keys.
- Each carries a RUNTIME SLOT docblock in the objectui#7344 form, like
`DetailViewSchema.onBack`.
- **Four keys become `?: never` tombstones, on seven member slots**,
each with a RETIRED docblock, its prescription and `@deprecated`:
- `onApplySuggestion` and `onRejectSuggestion`: nothing reads either;
- `config` on all three node types;
- `context` on form assist and recommendations.
- The `AIConfig` docblock, and the retired-insights tombstone docblock
in the same file, carry a dated note: no node member takes `AIConfig`
now, and the type stays exported.
- **zod face** (`packages/types/src/zod/ai.zod.ts`).
- `config` and `context` become `retirementTombstone` members. The
guidance names the key and the node, and says: "configure the model in
your host's own AI service (ObjectUI has no AI-provider API; these
components call no model), and hand its output to the node as" the right
key for that node.
- The five `on*` arms keep their `handlerKeyRefusal`, unchanged. For the
three slots that is the objectui#6124 shape again: a named refusal on
the JSON face beside a callable twin.
- The module docblock says so.
- **`packages/plugin-ai/README.md`** gains a migration table for the
four retired keys (seven member slots), and a paragraph on the three
callable slots.
- **Changesets.**
- New `.changeset/10874-ai-node-faces-agree.md`: `'@object-ui/types':
minor`, a BREAKING (authoring) banner that says which members became
callable and which became `never`, the Clause-② line and the migration.
- Dated notes in the objectui#10533 form go on
`.changeset/10859-ai-node-zod-arms.md` and
`.changeset/8800-retire-ai-insights.md`, each on a sentence this change
made false. The frontmatter md5 is unchanged on both.
- **Tests.**
- New: `ai-node-faces-agree-10874.test.ts`.
- `ai-zod-arms-10859.test.ts`: `assertionHandlerDivergence` becomes
`assertionHandlerFacesSettled`, and the member-type rows are updated.
- `zod-mirror-parity.test.ts`: one stale entry deleted, two entries
reworded, and the header figures moved.
- `component-docs-retired-handler-keys-7340.test.ts`: the census moves.
No renderer changes. `AIFormAssist`, `AIRecommendations` and
`NLQueryInput` keep their props interfaces untouched. `tombstone.zod.ts`
is untouched: round 1's docblock edit there is reverted, because the
three slots no longer need an exception.
## H1: the disagreement, reproduced on base `1c5ee334a2`
The probes were temporary: never committed, and deleted after the
reading.
- **TypeScript face.** Three literals, each annotated with its
declaration, were compiled by `tsc` under
`packages/types/tsconfig.test.json`: `{ type: 'ai-recommendations',
onSelect: 'x' }`, `{ type: 'ai-form-assist', config: { provider:
'openai', model: 'gpt-4' }, context: { record: 1 } }` and `{ type:
'nl-query', config: { temperature: 0.2 }, onSubmit: 'go' }`. Result:
exit 0, the probe file listed by `--listFiles`, zero diagnostics.
- **zod face, same base.**
- `safeValidateSchema({ type: 'ai-recommendations', onSelect: 'x' })` is
refused: code `custom`, path `onSelect`, and a message naming `onSelect`
a RUNTIME SLOT. The strict face refuses it too.
- These three were ACCEPTED by both `safeValidateSchema` and
`StrictAnyComponentSchema`:
- `ai-form-assist` with `config` and `context`;
- `ai-recommendations` with `config` and `context`;
- `nl-query` with `config`.
- **Nothing reads `config` or `context`.**
- Source: `git grep -w -E 'config|context' -- packages/plugin-ai/src
':!*.test.*'` gives 0 hits. The control `showScores`, in the same scope,
gives 3.
- Run time, through the real `SchemaRenderer` and registry. Each of the
three nodes was rendered with and without `config` and `context`.
- The markup was byte-identical on all three:
`identical-with-config-context true` ×3.
- A lit control changed the markup on all three: `lit-control-differs
true` ×3. The controls were `showConfidence`, `showScores` and
`placeholder`.
- **The three slots ARE read.** The seat re-read this on main during the
review:
- the registrations are the raw components;
- `SchemaRenderer`'s `outgoingComponentProps` spread strips no `on*`
key;
- `AIRecommendations` calls `onSelect?.(item)` and `onDismiss?.(item)`;
- `NLQueryInput` calls `onSubmit`.
- So a function on the node is called, and a string was called as a
function and threw.
## H2: host census, zero readers of `config` / `context`
- **Direct reads.** `git grep -E
'(schema|node|props)\??\.(config|context)\b'` over non-test
`packages/*/src/**` and `apps/*/src/**` gives 32 hits, all on other
carriers:
- 30 are a flow node's `config` in the app-shell metadata-admin code;
- 2 are the chart node's `config` in `plugin-charts`' `ChartRenderer`.
- None is on an AI node.
- The control `(schema|node|props)\??\.columns\b`, in the same scope,
gives 88.
- The bracket form gives 0.
- **Which tracked files name an AI node type at all:** plugin-ai sources
and tests, types sources and tests, cli `known-schema-types.ts` and its
ratchet test, the `ai.*` strings in i18n `en.ts`, and changesets.
Nothing in `apps/`, `examples/`, `content/` or `skills/` names one.
## H3: teaching census
No doc, README, example or skill authors a retired member on an AI node,
or a string on one of the three slots.
- `packages/plugin-ai/README.md` teaches `onApply`, `onSelect` and
`onSubmit` as function props only.
- The `apps/console` UI proposal has an `onSelect` hit, but it is a
command-palette item, not an AI node.
Nothing needed correcting. The README gains the migration material
(AGENTS.md #2).
## Pins
- **`ai-node-faces-agree-10874.test.ts` (new).**
- Type level, read by `tsc -p tsconfig.test.json`:
- `assertionRetiredMembersReadAsTombstones`, 7 rows;
- `assertionRuntimeSlotsAreCallable`, 3 rows: the exact function types;
- `assertionZodFaceRefusesConfigAndContext`, 5 rows;
- `assertionLiveMembersKeepTheirTypes`, the control;
- 13 `@ts-expect-error` rows: the retired members, plus a STRING on each
runtime slot on fresh literals and widened values;
- one accepting row: a function on each slot compiles and is callable.
- Runtime:
- 5 `safeValidateSchema` rows, each asserting code `invalid_type`, path
equal to the key, and a message that names the key, `objectui#10874` and
"host's own AI service";
- 5 strict-face rows;
- the control: each node without them is accepted on both faces.
- **`ai-zod-arms-10859.test.ts`.**
- `assertionHandlerFacesSettled` (formerly
`assertionHandlerDivergence`): the two retired keys read `undefined` on
both faces; the three slots read their function types on the TS face and
`undefined` on the zod face.
- `assertionMemberTypesAgree` excepts exactly `'onSelect' | 'onDismiss'`
and `'onSubmit'`, the objectui#6124 exception. It excepts nothing on
form assist.
- **`component-docs-retired-handler-keys-7340.test.ts`.**
- The census goes from 26 to 28, with `ai.ts` at 2.
- `onApplySuggestion` and `onRejectSuggestion` join the names no shipped
interface declares callable.
**Red-then-green method.** The implementation was committed first. Then
`packages/types/src/ai.ts` and `packages/types/src/zod/ai.zod.ts` were
checked out from each reference ref in turn. A trap restored them from
`HEAD` afterwards and proved the restore by blob hash: `RESTORED: git
diff HEAD empty; blobs match HEAD`. The on-disk landing was confirmed
before each leg.
**Leg: base `1c5ee334a2`** (`onSelect?: string` present once).
- `tsc` exits 2 with 44 errors. In the new file: 15 TS2344, 13 TS2578, 3
TS2322, 3 TS2349 and 3 TS7006. In `ai-zod-arms-10859.test.ts`: 6 TS2344.
In the parity file: 1 TS2322 ledger mismatch.
- vitest over the four files: `Tests 12 failed | 85 passed (97)`.
**Leg: round-1 head `15ec3ece42`** (`onSelect?: never` present once).
- `tsc` exits 2 with 18 errors: the function-type rows and the accepting
row fail against `never`, and the parity ledger mismatches.
- vitest: `Tests 7 failed | 90 passed (97)`. Red: the 7340 census, and
the five rendering-face rows, whose prescription anchor moved to the
host's AI service.
**Head `ce2958f243`:** see Gates.
## The parity census
- The `KnownDrift` ledger holds **48 entries and 87 keys**. That was
counted two ways:
- by an independent parse of the ledger, which also lists the two AI
rows;
- by the file's own header pins, green at head.
- The figures were 49 / 89 before this change. Only
`ai.zod.ts#AIFormAssistSchema` LEFT, with its two keys: both faces
refuse them now.
- `ai.zod.ts#AIRecommendationsSchema` (`onDismiss`, `onSelect`) and
`ai.zod.ts#NLQuerySchema` (`onSubmit`) STAY, unchanged in content and
reworded as ordinary objectui#6124 rows. The ratchet section's
restatement reads 48, and says those two pairs joined the #6124 class.
- `tsc -p packages/types/tsconfig.test.json` exits 0 at head, so the
ledger equals the measured drift.
- No other ledger row. `config` and `context` are retired on both faces
and agree. `EXPECTED_MIRROR_PAIRS` is unchanged.
## Gates, head `ce2958f243`
- **vitest:** `pnpm exec vitest run packages/types/ packages/plugin-ai/
packages/cli/` plus 14 script tests gives `Test Files 298 passed (298)`
and `Tests 6361 passed (6361)`, with lock verdict `command-exit 0`. That
is 284 package files plus these 14:
- `check-handler-key-read-sites`;
- the text pins over changesets and READMEs:
`changeset-polarity-census`, `body-dialect-census`,
`dollar-dialect-alias-census`, `check-changeset-claims`,
`check-changeset-no-major`, `check-changeset-presence`,
`check-changeset-overwrite`, `check-changeset-fixed`,
`check-pending-changeset-literals`, `render-changeset-claims-comment`,
`doc-version-claims`, `markdown-test-inputs` and `check-control-bytes`.
- The package tests that read the parity file, `ai.ts`, `ai.zod.ts` or
the plugin-ai README ran inside the package scope, all green:
`app-hidden-catalogue-flag-7542`, `combobox-command-optional-6033`,
`wrapper-class-declared-7722`, `ai-insights-retired-8800`,
`ai-zero-read-members-retired-8178`, `ai-zod-arms-10859`,
`component-docs-retired-handler-keys-7340` and cli
`registered-types-validate-ratchet-10859`.
- **type-check and handler reads:** `pnpm --filter @object-ui/types
type-check` and `pnpm check:handler-key-reads` both pass, with lock
verdict `command-exit 0`. The handler-reads gate prints: "OK 108 arm(s),
211 registration(s) … 49 judged … every judged read is a declared member
of its arm."
- **Changeset gates:** presence exits 0 ("6 source file(s) of 1 released
package(s) changed, and this change declares 1 changeset(s):
.changeset/10874-ai-node-faces-agree.md"). `no-major`, `fixed`,
`overwrite` (report-only), `claims` (report-only) and
`pending-changeset-literals` each exit 0.
- **Other gates:** `check-control-bytes` OK;
`check-new-cross-file-line-citations` 0 new; `check-type-check-coverage`
OK; `check-test-path-roots` 0; `check-doc-links` 0;
`check-governed-queue-guard --test` NOT GOVERNED (10 paths).
- **CI on round-1 head `15ec3ece42`:** all 43 check-runs are complete.
Test shards 1/8, 2/8 and 6/8 are `success`, as are the other test
shards, Type Check, Lint, README Export Check and Doc Snippet Type
Check. The two coverage jobs were `skipped`.
## Serial
- PR objectui#10870 has landed.
- Open PR objectui#10888 (objectui#10872) also edits
`zod-mirror-parity.test.ts`, adding `EXCLUSIONS` rows only. Round 1
measured `git merge-tree --write-tree` against its head `4679a31209`,
and against main `b2683a2c0e`: clean both ways.
- This round restores two `KnownDrift` rows next to where they stood,
and touches no `EXCLUSIONS` row.
## Acceptance notes
1. **Round 1 made the three runtime slots `?: never`, following the
order; the contract review measured that as wrong.** A function on the
node IS called. This round types them as the callables the components
invoke, per the objectui#6124 per-key rule and the
`DetailViewSchema.onBack` precedent.
2. **The runtime refusal's remedy for the `on*` keys.** The shared
remedy in `handlerKeyRefusal` reads "Author behaviour as a NODE TYPE …
an action:button node". That does not fit these three nodes, which host
no child node.
- Left unchanged, because the order keeps `handlerKeyRefusal`.
- The TypeScript docblocks, the README and the changeset carry the right
prescription.
- Carrier: none.
3. **`AIConfig`, `AIConfigSchema`, `AIProvider` and `AIModelType` stay
exported, but no node member takes them.** Retiring a published type is
a separate decision, so this is not filed: it is zero-pull residue.
4. **ObjectUI has no AI-provider API.** The prescription therefore names
the host's own AI service. The exported `AIProvider` is a string union,
not an API.
---
_Generated by [Claude
Code](https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 35d68c4 commit c30c8dd
10 files changed
Lines changed: 609 additions & 79 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 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 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
52 | 58 | | |
53 | 59 | | |
54 | 60 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
197 | 197 | | |
198 | 198 | | |
199 | 199 | | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
200 | 221 | | |
201 | 222 | | |
202 | 223 | | |
| |||
0 commit comments