Skip to content

Commit c59b1bf

Browse files
refactor(ui)!: retire @object-ui/plugin-ai and its three node types (#27042)
<!-- claude-projects-attribution: {"github_login":"os-warren"} --> _Requested by **Warren** · [project thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ81VrFexr8r2XyceuMm7ZUF6)_ Part of #25921 (the plugin-ai item only; the card's other items stay open). Supersedes #26365 (its commits are carried here unchanged, then `main` merged and the changeset finished). Clause-②: no (narrowing) Metadata-protocol: narrowed Skills-docs: follow-up #27048 (the two `skills/objectui/guides/*` mentions, kept out of this PR by ruling 6439038287) ## 维护者速读 - 改了什么:删除没有任何加载者的 `@object-ui/plugin-ai` 包;十个语言包删掉它的 `ai.*` 文案块;`@object-ui/types` 里它的类型/zod 镜像改为 `@deprecated` 的 `never` 墓碑,三个节点类型按名拒收并给出处方。changeset 改用整包操作数 `@object-ui/plugin-ai#*`。 - 为什么改:执行 #25921 的裁决 A(6398569691);ADR-0087 D9 的整包操作数已随 #26766 落地,按 6439038287 的「同意」改写 changeset 标记后门禁转绿。 - 风险与回滚:npm 外部使用者未实测(changeset 的 BREAKING 说明覆盖);回滚即 revert 本 PR。`check-empty-changeset` 故意红着:合并 main 后待发的 26080 changeset 列着已删除的包,本 PR 删掉那一行,需要你确认。 - 先看:`.changeset/25921-retire-plugin-ai.md`(整包标记与 FROM 行)、`.changeset/26080-readme-contributing-links.md`(删掉的一行)、`ui/packages/types/src/zod/ai.zod.ts`(拒收 arm 与墓碑)。 - 两分钟验证:`node scripts/check-adr-0087-registration.mjs --base origin/main`,预期 exit 0,并打印 `verified: @object-ui/plugin-ai#* (package, ui/packages/plugin-ai/package.json)`。 - 需要你做的:确认删掉 26080 changeset 里 `'@object-ui/plugin-ai': patch` 这一行(包已删除,留着会让 `changeset version` 失败),然后按 Tier H 批准(改动超过 3,000 行)。 ## What changes - **Package deleted.** `ui/packages/plugin-ai/**`, its `pnpm-lock.yaml` importer and its `ui/vitest.config.mts` alias. - **Release group.** Its entry leaves the fixed group in `.changeset/config.json`. - **Locale packs.** The `ai.*` block (`ai.nlQuery.*`, `ai.formAssist.*`, `ai.recommendations.*`) is deleted from all ten packs. - **Types tombstones (ruling A).** `ui/packages/types/src/ai.ts` keeps its nine exported names as `@deprecated` `never` aliases with the remedy, and `AnySchema` loses the three node members. In `zod/ai.zod.ts` the three arms become `retiredNodeType()` refusal arms, listed in `AnyComponentSchema` and not exported from the barrel. The eight published zod names stay exported as `@deprecated` `z.never()` schemas that accept nothing; when they leave the published face is #25912's sunset rule. - **CLI.** `known-schema-types.ts` loses the three keys. - **Docs.** `ui/content/docs/plugins/plugin-ai.mdx` and its `meta.json` entry. The package-table row now lives in the root `CONTRIBUTING.md` (main folded `ui/README.md` into it), and is removed there. - **Changeset.** `.changeset/25921-retire-plugin-ai.md`: `@object-ui/types`, `@object-ui/i18n`, `@object-ui/cli` minor, BREAKING. The ADR-0087 marker is `not-required (runtime-export-retired @object-ui/plugin-ai#*)`, and the three per-export FROM lines are one line naming the package (`FROM` the package `TO nothing`, the TO text kept). - **Pending changeset corrected.** `.changeset/26080-readme-contributing-links.md` (landed on main after #26365 was built) listed `'@object-ui/plugin-ai': patch`. A pending changeset naming a deleted package breaks `changeset version`, so that one line is deleted. Nothing else in that note changes. **Follow-up, not here (Tier H):** `skills/objectui/guides/project-setup.md` and `skills/objectui/guides/page-builder.md` still name `@object-ui/plugin-ai`. They are governed, so they go in a separate PR after this one. ### Forced test edits (each follows the deletion, none relaxes a check) - New pin `ui/packages/types/src/__tests__/plugin-ai-retired-25921.test.ts`: compile-time absence from `AnySchema` and the `never` names, the named refusal for each literal with its remedy, a firing control (`button` still accepted), a generic-path control for an unknown literal, and the eight zod names accepting nothing. - Deleted, because their subject left with the package: `ai-zod-arms-10859.test.ts`, `ai-node-faces-agree-10874.test.ts`, `ai-zero-read-members-retired-8178.test.ts` and the package's own test files. - `zod-mirror-parity.test.ts`, `arm-named-export-8784.test.ts`, `any-component-emit-headroom-11573.test.ts`, `component-docs-retired-handler-keys-7340.test.ts`, `content-channel-family-d-9256.test.ts`, `content-channel-parser-tier-clause-10928.test.ts`, `silence-clause-parser-tier-closure-10981.test.ts`, `ai-insights-retired-8800.test.ts`: rows and counts that named the three nodes, as in #26365. - `ui/packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts`: the "armed" case becomes a by-name refusal case with a generic-path control. `ui/scripts/markdown-test-inputs.mjs` follows: the entries that read the plugin-ai README go, and so does the `notRead: ['packages/cli/README.md']` that main added to the cli entry (the scanner found that path only through the plugin-ai README's bare `README.md` segment, so the audit reports it stale once the README is gone). - Deletions-only ledgers lose their plugin-ai rows: `clickableNonControlCensus-13461`, `iconOnlyButtonCensus-12143`, `runner-package-test-entry-3746`, `doc-version-claims`; `percent-storage-callers-11475` loses its plugin-ai exclusion set. - `console-vite-alias-closure-4925.test.ts`: its named negative control is now `@object-ui/cli`. `select-type-check-scope.test.ts`: its leaf-package fixture moves to `plugin-markdown`. - `ui/scripts/__tests__/package-files-exist.test.ts`: main's side taken on merge. #26576 moved both floors to the shared published-package constant, which a retirement does not move, so #26365's hand-lowered floors are gone. ## Tests <details><summary>Readings at head 7040d92 (merge of main acc9c57)</summary> - `pnpm --dir ui exec vitest run packages/types packages/i18n packages/cli`: 511 files passed, 12453 tests passed, 13 skipped. - `pnpm --dir ui exec vitest run scripts/` at 53dfa40: 178 files, 1 failed (`markdown-test-inputs.test.ts`, the stale `notRead` above), 5017 passed. Fixed in 7040d92; that file rerun: 17 / 17 passed. - `type-check` of `@object-ui/types`, `@object-ui/i18n`, `@object-ui/cli` (script names echoed): exit 0. `pnpm --dir ui run type-check:scripts`: exit 0. - Build: `turbo run build` with the root script's filters, 115 / 115 tasks. - Ablation of the new pin (from #26365, its test unchanged here): `RetiredNLQueryNodeSchema` deleted from `AnyComponentSchema` turns exactly the two `nl-query` cases red (2 failed, 15 passed of 17); restored, blob equals HEAD. </details> ## Acceptance notes - **ADR-0087 predicate 2 and two code-string mentions.** Classified with the gate's own `maskComments` and `packageReference`, `ui/packages/types/src/zod/ai.zod.ts` at this head names `@object-ui/plugin-ai` in code twice: both are the refusal remedy strings of the retired node arms (the text ruling A asks for). Every other mention in `ui/packages/types/src/zod/*.zod.ts` is a comment. Predicate 2 judges the package at the merge base, where all four mentions on main are comments, so the gate is green. The seat ruled to proceed as is. - **Tracker number in runtime strings.** Those refusal strings carry `(objectstack#25921)`. AGENTS.md says runtime strings carry no tracker number (`check:doc-authoring`); that gate is not in this diff's derived set and nothing derived refused it, so the strings are left as built. - **`check-empty-changeset` red by design.** It refuses the one-line correction of `.changeset/26080-readme-contributing-links.md` (DELIBERATE CORRECTION class). The gate stays red until a maintainer confirms the correction on this PR. - External npm consumers of `@object-ui/plugin-ai`: NOT MEASURED; the changeset carries the FROM / TO line. - Leftover pre-merge notes under `ui/.changeset/` still name `@object-ui/plugin-ai`; the root changesets config does not read that directory (`ui/AGENTS.md`), so they are untouched. - Unrelated `plugin-ai` mentions under `packages/spec`, `packages/metadata-protocol` and `scripts/check-service-providers.mjs` name the platform's own AI service plugin, not this package. ## Verification - `node scripts/check-adr-0087-registration.mjs --base origin/main`: exit 0, `verified: @object-ui/plugin-ai#* (package, ui/packages/plugin-ai/package.json)`. - `pnpm changeset status`: exit 0. - Lockfile: `pnpm install --frozen-lockfile` exit 0 after the merge, and `pnpm install --lockfile-only` leaves `pnpm-lock.yaml` byte-identical (0 `plugin-ai` mentions). - `node scripts/pm/dispatch-gates.mjs --ran`: 56 derived, 56 run, 0 NOT-MEASURED. Red: `check-empty-changeset --base origin/main` (by design, above) and the first `vitest run scripts/` (fixed, above). `check:dts-closure`, `check:dual-build-cjs-loads` and `check:lean-entry-closure` exit 3 or 1 before the build and 0 after it. - `node scripts/check-platform-checklist.mjs --changed-from origin/main`: 0 anchored files in the change set. - `dispatch-gates --tier`: no path-derived mandate (TIER_DEFAULT). Changed lines 7568 (+439 / −7129): over the 3,000-line threshold, so this lands by an authorized approval. - Full dogfood on the final head: reading goes in the card's os-dev report. ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. - [x] 2. Every name I added uses the casing for its kind. - [x] 3. No formatter ran; `pnpm lint` passes (CI-owned run). - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. - [x] 6. `packages/spec` touched: not touched. - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English. --- _Generated by [Claude Code](https://claude.ai/code/session_01JQwxbYonqfPMAts3ErwZAK)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2302b92 commit c59b1bf

61 files changed

Lines changed: 439 additions & 7129 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
"@object-ui/types": minor
3+
"@object-ui/i18n": minor
4+
"@object-ui/cli": minor
5+
---
6+
7+
refactor(ui)!: retire `@object-ui/plugin-ai` and its three node types
8+
9+
Clause-②: no (narrowing)
10+
Metadata-protocol: narrowed
11+
12+
<!-- adr-0087: not-required (runtime-export-retired @object-ui/plugin-ai#*) the retired operands are React components of a published ui package that nothing in the workspace loaded; no packages/spec schema, no ADR-0087 conversion surface and no stored shape names them, so there is nothing for `objectstack migrate meta` to rewrite, and the three node literals are refused by name on the zod face -->
13+
14+
**BREAKING.** `@object-ui/plugin-ai` is retired: `18.2.0` is its last published version, and it gets no further release. The console registered it neither eagerly nor lazily and no workspace package imported it, so the `ai-form-assist`, `ai-recommendations` and `nl-query` node types it registered rendered nowhere in the product.
15+
16+
What leaves, and what to do:
17+
18+
- FROM `@object-ui/plugin-ai` TO nothing: drop the `@object-ui/plugin-ai` import and dependency. A host that still wants the component keeps its own copy and registers it under its own type key.
19+
- Page nodes of type `ai-form-assist`, `ai-recommendations` or `nl-query`: remove them from the page. `safeValidateSchema` and `objectui validate` now refuse each of the three literals by name at `type`, with that remedy; before, the page rendered the "Unknown component type" panel in their place whenever the package was not loaded.
20+
- `@object-ui/types`: `AIFormAssistSchema`, `AIRecommendationsSchema`, `NLQuerySchema`, `AIConfig`, `AIFieldSuggestion`, `AIRecommendationItem`, `NLQueryResult`, `AIProvider` and `AIModelType` are `@deprecated` and now `never`, and `AnySchema` has no member for the three literals. The zod names (`AIFormAssistSchema` and the rest under `@object-ui/types/zod`, `AIComponentSchema` included) are `@deprecated` `z.never()` schemas that accept nothing. Remove every reference. The names stay importable until the tombstone sunset rule decides when they leave the published face.
21+
- `@object-ui/i18n`: the `ai.*` message block (`ai.nlQuery.*`, `ai.formAssist.*`, `ai.recommendations.*`) is gone from all ten packs. A host that registered overrides for those keys can delete them.
22+
- `@object-ui/cli`: `objectui check` no longer lists the three literals as known component types.

‎.changeset/26080-readme-contributing-links.md‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
'@object-ui/layout': patch
1212
'@object-ui/mobile': patch
1313
'@object-ui/permissions': patch
14-
'@object-ui/plugin-ai': patch
1514
'@object-ui/plugin-calendar': patch
1615
'@object-ui/plugin-charts': patch
1716
'@object-ui/plugin-chatbot': patch

‎.changeset/config.json‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@
9494
"@object-ui/layout",
9595
"@object-ui/mobile",
9696
"@object-ui/permissions",
97-
"@object-ui/plugin-ai",
9897
"@object-ui/plugin-calendar",
9998
"@object-ui/plugin-charts",
10099
"@object-ui/plugin-chatbot",

‎CONTRIBUTING.md‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,6 @@ Atoms and fields stay light; heavy widgets live in lazy-loaded plugins.
504504
| [`@object-ui/plugin-editor`](ui/packages/plugin-editor) | Rich text editor powered by Monaco |
505505
| [`@object-ui/plugin-markdown`](ui/packages/plugin-markdown) | Markdown rendering |
506506
| [`@object-ui/plugin-chatbot`](ui/packages/plugin-chatbot) | Chatbot interface |
507-
| [`@object-ui/plugin-ai`](ui/packages/plugin-ai) | Schema generation and conversational assistants (Vercel AI SDK) |
508507

509508
#### Tooling
510509

‎pnpm-lock.yaml‎

Lines changed: 0 additions & 49 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎ui/content/docs/plugins/meta.json‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
{
22
"title": "Plugins",
33
"pages": [
4-
"plugin-ai",
54
"plugin-calendar",
65
"plugin-calendar-view",
76
"plugin-charts",

‎ui/content/docs/plugins/plugin-ai.mdx‎

Lines changed: 0 additions & 26 deletions
This file was deleted.

‎ui/packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts‎

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
* (`validateAuthoredDocument`, objectui#5250), whose root is the strict twin of
1717
* `AnyComponentSchema` — a union discriminated on `type`. A registered type
1818
* with no arm in that union is refused with one `invalid_union` issue at
19-
* `['type']`, whatever else the document says: `ai-form-assist`, which
20-
* `packages/plugin-ai/README.md` teaches, was one of them. The mirror-parity
19+
* `['type']`, whatever else the document says: `ai-form-assist`, which the
20+
* since-retired `@object-ui/plugin-ai` README taught, was one of them. The mirror-parity
2121
* ratchet in `@object-ui/types` counts declaration↔mirror PAIRS and the keys
2222
* inside them; a registered type with no mirror at all is in neither, so
2323
* nothing counted this family until this file.
@@ -379,7 +379,7 @@ describe('registered component types refused at `type` — a ratchet (objectui#1
379379
// was 200 until objectui#10859 batch 8 unregistered thirty bare keys.)
380380
expect(BARE_KEYS.length).toBeGreaterThan(150);
381381
expect(BARE_KEYS).toContain('timeline');
382-
expect(BARE_KEYS).toContain('ai-form-assist');
382+
expect(BARE_KEYS).toContain('pivot');
383383
});
384384

385385
it('tells a claimed type from an unclaimed one — both instrument controls fire', () => {
@@ -392,11 +392,24 @@ describe('registered component types refused at `type` — a ratchet (objectui#1
392392
expect(refusedAtType('no-such-component-10859')).toBe(true);
393393
});
394394

395-
it('counts the three plugin-ai keys as armed (objectui#10859 batch 1)', () => {
395+
it('refuses the three plugin-ai keys BY NAME at `type` — the package retired and their arms became refusals (objectstack#25921)', () => {
396+
// objectui#10859 batch 1 armed them; the registrations left with
397+
// `@object-ui/plugin-ai`, so registration and arm leaving in one change move
398+
// neither count. Each arm stays as `retiredNodeType()`'s refusal, the
399+
// `cloud:plan-status` shape below.
396400
for (const key of ['ai-form-assist', 'ai-recommendations', 'nl-query']) {
397-
expect(BARE_KEYS, key).toContain(key);
401+
expect(BARE_KEYS, key).not.toContain(key);
402+
const result = validateAuthoredDocument({ type: key });
403+
expect(result.success, key).toBe(false);
404+
const named = (result.success ? [] : result.error.issues).filter(
405+
(issue) => issue.code === 'custom' && issue.path.length === 1 && issue.path[0] === 'type',
406+
);
407+
expect(named, key).toHaveLength(1);
408+
expect(named[0]?.message, key).toContain('@object-ui/plugin-ai');
398409
expect(refusedAtType(key), key).toBe(false);
399410
}
411+
// Control: an unknown bare literal still takes the generic path.
412+
expect(refusedAtType('ai-no-such-node-25921')).toBe(true);
400413
});
401414

402415
it('counts the three keys batch 2 armed (objectui#10859 batch 2)', () => {
@@ -712,7 +725,6 @@ describe('registered NAMESPACED component types refused at `type` — a ratchet
712725

713726
/** Rooted on this file, never on `process.cwd()`. */
714727
const HERE = dirname(fileURLToPath(import.meta.url));
715-
const PLUGIN_AI_README = join(HERE, '..', '..', '..', 'plugin-ai', 'README.md');
716728
const QUICK_START = join(HERE, '..', '..', '..', '..', 'content', 'docs', 'guide', 'quick-start.md');
717729

718730
/** See `validate-root-path-line.test.ts` — the escape byte is never spelled. */
@@ -750,26 +762,6 @@ afterEach(() => {
750762
rmSync(dir, { recursive: true, force: true });
751763
});
752764

753-
describe('objectui validate — the plugin-ai README document (objectui#10859)', () => {
754-
it('validates the "Schema-Driven Usage" `ai-form-assist` document', async () => {
755-
const readme = readFileSync(PLUGIN_AI_README, 'utf8');
756-
const section = readme.slice(readme.indexOf('## Schema-Driven Usage'));
757-
const fence = /```json\n([\s\S]*?)\n```/.exec(section);
758-
expect(fence, 'no ```json fence under "## Schema-Driven Usage"').not.toBeNull();
759-
const file = join(dir, 'ai-form-assist.json');
760-
writeFileSync(file, (fence as RegExpExecArray)[1], 'utf-8');
761-
// Lit control on the extraction: it is the taught node.
762-
expect(JSON.parse((fence as RegExpExecArray)[1]).type).toBe('ai-form-assist');
763-
764-
await validate(file);
765-
766-
const text = out.join('\n').replace(ANSI, '');
767-
expect(text).not.toContain('Schema validation failed');
768-
expect(text).toContain('Schema is valid');
769-
expect(exitCodes).toEqual([0]);
770-
});
771-
});
772-
773765
/* ── End to end: a page of public blocks through `objectui validate` ─────── */
774766

775767
describe('objectui validate — a page built from ADR-0080 public blocks (objectui#10872)', () => {

‎ui/packages/cli/src/utils/known-schema-types.ts‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ export const KNOWN_SCHEMA_TYPES: readonly string[] = [
3939
'action:icon',
4040
'action:menu',
4141
'address',
42-
'ai-form-assist',
43-
'ai-recommendations',
4442
'ai:feedback',
4543
'ai:input',
4644
'ai:suggestion',
@@ -222,7 +220,6 @@ export const KNOWN_SCHEMA_TYPES: readonly string[] = [
222220
'nav:breadcrumb',
223221
'nav:menu',
224222
'navigation-menu',
225-
'nl-query',
226223
'object-calendar',
227224
'object-chart',
228225
'object-data-table',

‎ui/packages/components/src/__tests__/clickableNonControlCensus-13461.test.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,6 @@ const NOT_CONTROLS: Array<[file: string, key: string, reason: string]> = [
262262
const BACKLOG: Array<[file: string, key: string]> = [
263263
// The data form preview selects a field by a click delegated from its frame.
264264
['packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx', 'div · closest,getAttribute,some,readFields,setFieldSel'],
265-
['packages/plugin-ai/src/AIRecommendations.tsx', 'div · handleSelect'],
266265
['packages/plugin-designer/src/DashboardEditor.tsx', 'div · onSelect'],
267266
['packages/plugin-designer/src/DataModelDesigner.tsx', 'span · stopPropagation,handleStartFieldEdit'],
268267
['packages/plugin-designer/src/PageDesigner.tsx', 'div · stopPropagation,toggle'],

0 commit comments

Comments
 (0)