From a12a6a264f4872298c49a333efcc39d4cc37d4af Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 22:18:42 +0000 Subject: [PATCH 01/13] chore(ui): delete @object-ui/plugin-ai and its release-group entries (WIP) Part of the plugin-ai retirement (ruling A on the zero-consumer card). Claude-Session: https://claude.ai/code/session_01VBDBR9oS1MNUcjBVXds5D6 Co-authored-by: Claude --- .changeset/25831-object-ui-18-1-to-merge.md | 1 - .changeset/config.json | 1 - pnpm-lock.yaml | 49 - ui/packages/plugin-ai/CHANGELOG.md | 2888 ----------------- ui/packages/plugin-ai/LICENSE | 21 - ui/packages/plugin-ai/README.md | 232 -- ui/packages/plugin-ai/package.json | 79 - ui/packages/plugin-ai/src/AIFormAssist.tsx | 193 -- ...tions.layoutCarouselRetired-10330.test.tsx | 111 - ...mmendations.rendersEveryItem-8178.test.tsx | 99 - .../plugin-ai/src/AIRecommendations.tsx | 178 - ui/packages/plugin-ai/src/NLQueryInput.tsx | 216 -- .../src/aiComponents.i18n-10232.test.tsx | 146 - ui/packages/plugin-ai/src/index.tsx | 77 - .../src/registrationInputs-8178.test.ts | 171 - ui/packages/plugin-ai/src/useAiTranslation.ts | 72 - ui/packages/plugin-ai/tsconfig.json | 31 - ui/packages/plugin-ai/tsconfig.test.json | 45 - ui/packages/plugin-ai/vite.config.ts | 78 - ui/vitest.config.mts | 1 - 20 files changed, 4689 deletions(-) delete mode 100644 ui/packages/plugin-ai/CHANGELOG.md delete mode 100644 ui/packages/plugin-ai/LICENSE delete mode 100644 ui/packages/plugin-ai/README.md delete mode 100644 ui/packages/plugin-ai/package.json delete mode 100644 ui/packages/plugin-ai/src/AIFormAssist.tsx delete mode 100644 ui/packages/plugin-ai/src/AIRecommendations.layoutCarouselRetired-10330.test.tsx delete mode 100644 ui/packages/plugin-ai/src/AIRecommendations.rendersEveryItem-8178.test.tsx delete mode 100644 ui/packages/plugin-ai/src/AIRecommendations.tsx delete mode 100644 ui/packages/plugin-ai/src/NLQueryInput.tsx delete mode 100644 ui/packages/plugin-ai/src/aiComponents.i18n-10232.test.tsx delete mode 100644 ui/packages/plugin-ai/src/index.tsx delete mode 100644 ui/packages/plugin-ai/src/registrationInputs-8178.test.ts delete mode 100644 ui/packages/plugin-ai/src/useAiTranslation.ts delete mode 100644 ui/packages/plugin-ai/tsconfig.json delete mode 100644 ui/packages/plugin-ai/tsconfig.test.json delete mode 100644 ui/packages/plugin-ai/vite.config.ts diff --git a/.changeset/25831-object-ui-18-1-to-merge.md b/.changeset/25831-object-ui-18-1-to-merge.md index a17d8569526..b0289d17327 100644 --- a/.changeset/25831-object-ui-18-1-to-merge.md +++ b/.changeset/25831-object-ui-18-1-to-merge.md @@ -13,7 +13,6 @@ "@object-ui/layout": minor "@object-ui/mobile": minor "@object-ui/permissions": minor -"@object-ui/plugin-ai": minor "@object-ui/plugin-calendar": minor "@object-ui/plugin-charts": minor "@object-ui/plugin-chatbot": minor diff --git a/.changeset/config.json b/.changeset/config.json index a6098cd3aac..daf3291ae23 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -95,7 +95,6 @@ "@object-ui/layout", "@object-ui/mobile", "@object-ui/permissions", - "@object-ui/plugin-ai", "@object-ui/plugin-calendar", "@object-ui/plugin-charts", "@object-ui/plugin-chatbot", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 350bfb01bde..3d89dafd4eb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4823,55 +4823,6 @@ importers: specifier: ^4.1.10 version: 4.1.12(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@4.1.12)(@vitest/ui@4.1.10)(happy-dom@21.0.4)(jsdom@30.1.1(@noble/hashes@2.4.0))(msw@2.15.0(@types/node@26.6.3)(typescript@6.0.3))(vite@8.3.5(@types/node@26.6.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15(patch_hash=4d1d35da1809be2a945519cb26890e48107810b9b66c6913a6a89abb5ad6a1e6))(yaml@2.9.1)) - ui/packages/plugin-ai: - dependencies: - '@object-ui/components': - specifier: workspace:* - version: link:../components - '@object-ui/core': - specifier: workspace:* - version: link:../core - '@object-ui/i18n': - specifier: workspace:* - version: link:../i18n - '@object-ui/types': - specifier: workspace:* - version: link:../types - lucide-react: - specifier: ^1.43.0 - version: 1.43.0(react@19.3.0) - react: - specifier: 19.3.0 - version: 19.3.0 - react-dom: - specifier: 19.3.0 - version: 19.3.0(react@19.3.0) - devDependencies: - '@object-ui/test-support': - specifier: workspace:* - version: link:../test-support - '@types/node': - specifier: ^26.2.0 - version: 26.6.3 - '@types/react': - specifier: 19.3.0 - version: 19.3.0 - '@types/react-dom': - specifier: 19.3.0 - version: 19.3.0(@types/react@19.3.0) - '@vitejs/plugin-react': - specifier: ^6.0.5 - version: 6.0.5(vite@8.3.5(@types/node@26.6.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15(patch_hash=4d1d35da1809be2a945519cb26890e48107810b9b66c6913a6a89abb5ad6a1e6))(yaml@2.9.1)) - vite: - specifier: ^8.2.1 - version: 8.3.5(@types/node@26.6.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15(patch_hash=4d1d35da1809be2a945519cb26890e48107810b9b66c6913a6a89abb5ad6a1e6))(yaml@2.9.1) - vite-plugin-dts: - specifier: ^5.0.3 - version: 5.0.3(@microsoft/api-extractor@7.58.2(@types/node@26.6.3))(esbuild@0.28.2)(rolldown@1.2.15)(rollup@4.62.2)(typescript@6.0.3)(vite@8.3.5(@types/node@26.6.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15(patch_hash=4d1d35da1809be2a945519cb26890e48107810b9b66c6913a6a89abb5ad6a1e6))(yaml@2.9.1)) - vitest: - specifier: ^4.1.10 - version: 4.1.12(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@4.1.12)(@vitest/ui@4.1.10)(happy-dom@21.0.4)(jsdom@30.1.1(@noble/hashes@2.4.0))(msw@2.15.0(@types/node@26.6.3)(typescript@6.0.3))(vite@8.3.5(@types/node@26.6.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15(patch_hash=4d1d35da1809be2a945519cb26890e48107810b9b66c6913a6a89abb5ad6a1e6))(yaml@2.9.1)) - ui/packages/plugin-calendar: dependencies: '@object-ui/components': diff --git a/ui/packages/plugin-ai/CHANGELOG.md b/ui/packages/plugin-ai/CHANGELOG.md deleted file mode 100644 index bfe8fdf5d88..00000000000 --- a/ui/packages/plugin-ai/CHANGELOG.md +++ /dev/null @@ -1,2888 +0,0 @@ -# @object-ui/plugin-ai - -## 18.1.0 - -### Patch Changes - -- Updated dependencies [8556295] -- Updated dependencies [cd14d8e] -- Updated dependencies [8701f1e] -- Updated dependencies [5235d6d] -- Updated dependencies [7c405b7] -- Updated dependencies [0093b2c] -- Updated dependencies [3aafc0d] -- Updated dependencies [ff77cfc] -- Updated dependencies [49a536d] -- Updated dependencies [241cb78] -- Updated dependencies [404ccae] -- Updated dependencies [ec627ab] -- Updated dependencies [0f4adc3] -- Updated dependencies [fa31856] -- Updated dependencies [8e90779] -- Updated dependencies [f1a4b08] -- Updated dependencies [7f60e9d] -- Updated dependencies [d9aff15] -- Updated dependencies [4eeb174] -- Updated dependencies [afcf654] -- Updated dependencies [2c3dc72] -- Updated dependencies [2958f17] -- Updated dependencies [a78608f] -- Updated dependencies [1d178e0] -- Updated dependencies [2c4e5f5] -- Updated dependencies [6fb31dc] -- Updated dependencies [fbb4b23] -- Updated dependencies [c8f8b10] -- Updated dependencies [ad74e98] - - @object-ui/types@18.1.0 - - @object-ui/components@18.1.0 - - @object-ui/i18n@18.1.0 - - @object-ui/core@18.1.0 - -## 18.0.0 - -### Major Changes - -- 6f3299f: objectui moves to major 18, following `@objectstack/*` from 17 to 18 (objectui#12334). objectui's major is not a count of its own breaking changes: it tracks the major of the `@objectstack/spec` its packages depend on, so that the same major means compatible across the two projects. These packages already declare `@objectstack/*` on the 18 line (objectui#12030), so every package in the fixed release group crosses to 18 together. It is the one `major` that rule admits: objectui's own breaking changes ship as `minor`, described in their entries. - - The 18 line is released from Changesets pre mode on the `next` tag (`.changeset/pre.json`), as `@objectstack/*` 18 is: these releases are `18.0.0-next.N` on the npm `next` dist-tag, and they do not move `latest`. The first `18.0.0` on `latest` comes when `@objectstack` 18 is generally available and this repository leaves pre mode. - - What a consumer must do to take it is in the `@objectstack/*` 18 entry of this release (objectui#12030): move its own `@objectstack/*` packages to the 18 line at the same time. - -### Patch Changes - -- Updated dependencies [18d7b48] -- Updated dependencies [172acc3] -- Updated dependencies [f0496bd] -- Updated dependencies [c4c506b] -- Updated dependencies [9db9ff3] -- Updated dependencies [c096f03] -- Updated dependencies [d92b2a1] -- Updated dependencies [92f4e2b] -- Updated dependencies [b10c68e] -- Updated dependencies [bdc9049] -- Updated dependencies [e8c0b96] -- Updated dependencies [b92329c] -- Updated dependencies [2e818d0] -- Updated dependencies [fd060f0] -- Updated dependencies [8b14aec] -- Updated dependencies [2abec3a] -- Updated dependencies [9dfaca6] -- Updated dependencies [c000398] -- Updated dependencies [73b5d77] -- Updated dependencies [7b17705] -- Updated dependencies [fc3c2cc] -- Updated dependencies [f9f4a62] -- Updated dependencies [848ba0e] -- Updated dependencies [57d82cb] -- Updated dependencies [de96f3d] -- Updated dependencies [9ca3cac] -- Updated dependencies [c910630] -- Updated dependencies [ce464d9] -- Updated dependencies [e6dcd85] -- Updated dependencies [96fcf30] -- Updated dependencies [7a2c60b] -- Updated dependencies [834c559] -- Updated dependencies [22b503c] -- Updated dependencies [9fc68aa] -- Updated dependencies [d50f724] -- Updated dependencies [17acfbb] -- Updated dependencies [6be0f7a] -- Updated dependencies [89cc738] -- Updated dependencies [c0862c1] -- Updated dependencies [c0862c1] -- Updated dependencies [c0862c1] -- Updated dependencies [c0862c1] -- Updated dependencies [c0862c1] -- Updated dependencies [c0862c1] -- Updated dependencies [ded4494] -- Updated dependencies [d172f63] -- Updated dependencies [4c0de52] -- Updated dependencies [a80fef7] -- Updated dependencies [3bf8894] -- Updated dependencies [e06365c] -- Updated dependencies [1e1f09e] -- Updated dependencies [455c646] -- Updated dependencies [f34e877] -- Updated dependencies [f34e877] -- Updated dependencies [9844bbf] -- Updated dependencies [cef0eee] -- Updated dependencies [7ebff39] -- Updated dependencies [054fd84] -- Updated dependencies [5275d1f] -- Updated dependencies [7241a81] -- Updated dependencies [74add0c] -- Updated dependencies [aaba865] -- Updated dependencies [f1781be] -- Updated dependencies [fbad078] -- Updated dependencies [45d5853] -- Updated dependencies [4f4fc03] -- Updated dependencies [2312bd9] -- Updated dependencies [6d5eb34] -- Updated dependencies [c5406c2] -- Updated dependencies [c5406c2] -- Updated dependencies [d99b731] -- Updated dependencies [2571a3e] -- Updated dependencies [2a48bd4] -- Updated dependencies [8a55f0c] -- Updated dependencies [c7b30bd] -- Updated dependencies [20c6d35] -- Updated dependencies [417e245] -- Updated dependencies [69bca0a] -- Updated dependencies [023f00d] -- Updated dependencies [158c75d] -- Updated dependencies [7525045] -- Updated dependencies [5af42db] -- Updated dependencies [ce991bd] -- Updated dependencies [3c0f806] -- Updated dependencies [1071393] -- Updated dependencies [5f75cfa] -- Updated dependencies [6757a93] -- Updated dependencies [b7cd5c8] -- Updated dependencies [5330038] -- Updated dependencies [c9fc10b] -- Updated dependencies [dc6d28e] -- Updated dependencies [c0c2f79] -- Updated dependencies [0665c3b] -- Updated dependencies [d2500d8] -- Updated dependencies [e723f58] -- Updated dependencies [82bb1d5] -- Updated dependencies [f3d5408] -- Updated dependencies [b231607] -- Updated dependencies [b231607] -- Updated dependencies [466a219] -- Updated dependencies [6fb281d] -- Updated dependencies [54159d3] -- Updated dependencies [fa778e6] -- Updated dependencies [334f0cf] -- Updated dependencies [c08c933] -- Updated dependencies [c4dd707] -- Updated dependencies [c4dd707] -- Updated dependencies [7578323] -- Updated dependencies [38bb373] -- Updated dependencies [923504f] -- Updated dependencies [10aba37] -- Updated dependencies [5d3f437] -- Updated dependencies [51c661e] -- Updated dependencies [7af0ab3] -- Updated dependencies [b434f5f] -- Updated dependencies [9f282ef] -- Updated dependencies [fd4872f] -- Updated dependencies [f0532d8] -- Updated dependencies [cea429b] -- Updated dependencies [3092aca] -- Updated dependencies [82e5f53] -- Updated dependencies [d81e460] -- Updated dependencies [8106db1] -- Updated dependencies [068a540] -- Updated dependencies [ca45c6d] -- Updated dependencies [093d55b] -- Updated dependencies [b64fda3] -- Updated dependencies [a8051d6] -- Updated dependencies [72594f2] -- Updated dependencies [524136e] -- Updated dependencies [2b06a91] -- Updated dependencies [6f3299f] -- Updated dependencies [065a1cf] -- Updated dependencies [f2b17c6] -- Updated dependencies [52d6194] -- Updated dependencies [23bf859] -- Updated dependencies [1c72d77] -- Updated dependencies [2b4d3f1] -- Updated dependencies [d75f212] -- Updated dependencies [37db09f] -- Updated dependencies [ff9df88] -- Updated dependencies [70e843f] -- Updated dependencies [caac3b2] -- Updated dependencies [a2225f9] -- Updated dependencies [5c4deb5] -- Updated dependencies [a8e5b73] -- Updated dependencies [d9d9d98] -- Updated dependencies [c950345] -- Updated dependencies [21f9577] -- Updated dependencies [044f231] -- Updated dependencies [f8267df] -- Updated dependencies [9b82ced] -- Updated dependencies [3821a3a] -- Updated dependencies [3eb3ec1] -- Updated dependencies [92c2a69] -- Updated dependencies [646caea] -- Updated dependencies [1ffd6a2] -- Updated dependencies [5f8ecf6] -- Updated dependencies [8ed7e5a] -- Updated dependencies [6c4d00d] -- Updated dependencies [db8ce2f] -- Updated dependencies [726d8c9] -- Updated dependencies [f0e760a] -- Updated dependencies [a5bb7ac] -- Updated dependencies [f78efdd] -- Updated dependencies [8a8bcf3] -- Updated dependencies [f74638c] -- Updated dependencies [ef71a09] -- Updated dependencies [8614705] -- Updated dependencies [619e3f7] -- Updated dependencies [ca7a4c3] -- Updated dependencies [c9e6742] -- Updated dependencies [f62b6ff] -- Updated dependencies [608033b] -- Updated dependencies [efa9cb4] -- Updated dependencies [d66d7e8] -- Updated dependencies [15d8ca6] -- Updated dependencies [378896d] -- Updated dependencies [9d857c9] -- Updated dependencies [670ea10] -- Updated dependencies [ff215d9] -- Updated dependencies [a8add5c] -- Updated dependencies [33ee9de] -- Updated dependencies [4839f5c] -- Updated dependencies [a23e804] -- Updated dependencies [36e2f7b] -- Updated dependencies [ac59073] -- Updated dependencies [44cb469] -- Updated dependencies [e058d76] -- Updated dependencies [c999d4c] -- Updated dependencies [5483297] -- Updated dependencies [ed43c79] -- Updated dependencies [260ef71] -- Updated dependencies [655e897] -- Updated dependencies [8d80845] -- Updated dependencies [69a408f] -- Updated dependencies [d2b21a1] -- Updated dependencies [64acb7e] -- Updated dependencies [4ff5b6b] -- Updated dependencies [473e5a0] -- Updated dependencies [b279016] -- Updated dependencies [bb6c38c] -- Updated dependencies [26323c4] -- Updated dependencies [9753811] -- Updated dependencies [2727527] -- Updated dependencies [3da39ae] -- Updated dependencies [bc281f4] -- Updated dependencies [d637be3] -- Updated dependencies [2cfdc99] -- Updated dependencies [2cfdc99] -- Updated dependencies [1e6d83b] -- Updated dependencies [6058bf8] -- Updated dependencies [c219165] -- Updated dependencies [fb501bc] -- Updated dependencies [30083da] -- Updated dependencies [7f9cc37] -- Updated dependencies [025130c] -- Updated dependencies [76f91f8] -- Updated dependencies [ea34d9b] -- Updated dependencies [8aa8e3e] -- Updated dependencies [cb80001] -- Updated dependencies [909c64e] -- Updated dependencies [9e7e1a5] -- Updated dependencies [2493e85] -- Updated dependencies [d0b9648] -- Updated dependencies [46dbf32] -- Updated dependencies [a928aaa] -- Updated dependencies [47508f3] -- Updated dependencies [61ed5ad] -- Updated dependencies [1b94d50] -- Updated dependencies [6f877f1] -- Updated dependencies [1ce0711] -- Updated dependencies [fb85637] -- Updated dependencies [9f1dd79] -- Updated dependencies [df71568] -- Updated dependencies [2d8a8b7] -- Updated dependencies [cbac9d7] -- Updated dependencies [9eb1479] -- Updated dependencies [74e37aa] -- Updated dependencies [961b69f] -- Updated dependencies [52907d7] -- Updated dependencies [f466b0a] -- Updated dependencies [673a71c] -- Updated dependencies [616df65] -- Updated dependencies [58798ec] -- Updated dependencies [8803ca5] -- Updated dependencies [e70c21d] -- Updated dependencies [4b92c5a] -- Updated dependencies [06be396] -- Updated dependencies [3650d94] -- Updated dependencies [7c05f21] -- Updated dependencies [8cd4db4] -- Updated dependencies [daeb77f] -- Updated dependencies [eb4552e] -- Updated dependencies [3c3115e] -- Updated dependencies [55e90fd] -- Updated dependencies [9b75509] -- Updated dependencies [b13ea3c] -- Updated dependencies [d7e9e9a] -- Updated dependencies [cf62edf] -- Updated dependencies [0253416] -- Updated dependencies [5d77c09] -- Updated dependencies [3fd8625] -- Updated dependencies [1473757] -- Updated dependencies [d73d987] -- Updated dependencies [12ff256] -- Updated dependencies [26b0bd2] -- Updated dependencies [26b0bd2] -- Updated dependencies [d328698] -- Updated dependencies [d328698] -- Updated dependencies [b4e0787] -- Updated dependencies [088e0f7] -- Updated dependencies [78b07f4] -- Updated dependencies [f962cdc] -- Updated dependencies [b403bb3] -- Updated dependencies [8a25fee] -- Updated dependencies [4016370] -- Updated dependencies [12dc7b4] - - @object-ui/types@18.0.0 - - @object-ui/components@18.0.0 - - @object-ui/core@18.0.0 - - @object-ui/i18n@18.0.0 - -## 17.7.0 - -### Minor Changes - -- f98eddf: Retire `carousel` from `AIRecommendationsSchema.layout` and from the - `ai-recommendations` designer enum (objectui#10330, ADR-0049 enforce-or-remove). - - **Breaking, deliberately, and scored `minor` per this repo's convention** (a - `major` in the fixed group would push every package off `@objectstack`'s cadence; - `scripts/check-changeset-no-major.mjs` enforces it). - - - FROM `layout?: 'list' | 'grid' | 'carousel'` TO `layout?: 'list' | 'grid'`. - A TypeScript node that sets `layout: 'carousel'` is now a compile-time error. - - The `ai-recommendations` registration no longer offers `Carousel` in its - `layout` enum, so the designer stops suggesting it. - - The value was declared and offered but never implemented: `AIRecommendations` - renders only `grid` specially, so `carousel` always rendered as a list, with no - warning. Runtime behaviour does not change. A stored JSON node that still carries - `layout: 'carousel'` renders the list layout, exactly as before. - - Migration: replace `layout: 'carousel'` with `layout: 'list'` (what it rendered) - or `layout: 'grid'`. -- 496d31d: Retire the seven zero-read members of the three AI schemas, and the designer - inputs that advertised them (objectui#8178, ADR-0049 enforce-or-remove, - director decision batch #78, 2026-09-07, maintainer verbatim 「同意」). - - **Breaking, deliberately — and scored `minor` per this repo's convention** - (objectui#3161 family: a `major` in the fixed group pushes all 39 packages off - `@objectstack`'s cadence, so breaking semantics are spelled out here instead; - `scripts/check-changeset-no-major.mjs` enforces it). - - `AIFormAssistSchema.formId` / `.objectName` / `.fields` / `.autoFill`, - `AIRecommendationsSchema.objectName` / `.maxResults` and `NLQuerySchema.objectName` - are `?: never` retirement tombstones. A node that carries one is now a - compile-time error on the published `.d.ts`; a stored JSON document that carries - one keeps parsing exactly as it did, and the value keeps being ignored, exactly - as it was. Seven `inputs` entries across the three `ComponentRegistry` - registrations are gone, so the field designer no longer offers the keys, and the - `packages/plugin-ai/README.md` examples that taught them are rewritten. - - **Nothing behavioural changes.** Not one of the seven had a reader. The census - was re-derived per key on this branch's base with a lit control beside every - zero — `AIFormAssist` 0 for `formId`/`objectName`/`fields` against `suggestions` - 9 and `showConfidence` 2; `AIRecommendations` 0 for `objectName`/`maxResults` - against `recommendations` 8; `NLQueryInput` 0 for `objectName` against `result` - 14 — plus the two channels that consume a key without naming it: zero - `{...props}` / `{...rest}` spreads in all four of the package's sources (control: - `collapsible.tsx` matches the same patterns) and zero dynamic `schema[…]` access - (control: four files under `packages/` use that form). `autoFill` was the one - non-zero: destructured with a default and never referenced again, so nothing was - ever auto-filled. - - `maxResults` is the member that made this user-visible. It was documented as - "Maximum number of results to display" and read by nothing, so `maxResults: 5` - against a fifty-item list rendered fifty rows with no diagnostic. - `AIRecommendations` now STATES in its docblock that it renders every item and - that there is no cap, and a fifty-item render pins it in both layouts. - - **Migration.** Slice `recommendations` before handing it over; pass - `suggestions` in and act on `onApply` instead of `formId` / `fields` / - `autoFill`; scope a query by object in the host that answers it instead of - `objectName`. The README carries the same table. - - **Why not Enforce.** Implementing reads nobody asked for is capability growth - without pull, and nothing in the repo pulls on these. An AI backend that later - wants `objectName` / `fields` as call context is a feature card with its own - business case. - - **Why tombstones and not deletions.** All three schemas extend `BaseSchema`, - which carries `[key: string]: any`: a deleted optional member is absorbed - silently at any value, defeating both excess-property checking and the weak-type - check. Deletion would have left precisely the silent no-op this retirement ends, - and the ruling's first pin — refused by the schema types, at compile time — - would have been unsatisfiable. `packages/plugin-ai/README.md` taught all seven as - working, which is prong 2 of the tombstone discriminator (objectui#5941, #7526, - `5f8190c8c`) on its own. - - `AIInsightsSchema.objectName` was deliberately left out of this retirement, and - the reason for that exclusion has since been spent. The half that survives is the - SCOPE: objectui#8178's ruling named three schemas, so this entry retires seven - members on those three and touches no fourth. The half that does not survive is - the reason — when this entry was written that fourth schema had been neither - screened nor decided, so a pin held the member at `string | undefined` against a - later sweep of "the AI `objectName`s". objectui#8800 has since screened it and - the director seat decided it (decision batch #137 item 2, 2026-09-15, maintainer - verbatim 「同意」, letter A), retiring `AIInsightsSchema` and the `ai-insights` - node type WHOLE from the published type face: there is no member left to pin, and - that ruling rewrote the exclusion pin — the only thing that was ever allowed to - rewrite it. Its entry publishes in this same release, and the successor pin is - `packages/types/src/__tests__/ai-insights-retired-8800.test.ts`. - - ⚠️ **Dated note, 2026-09-28 — the validator now reaches these tombstones — objectui#10859.** Later in - this same release `AnyComponentSchema` gains an arm for each of the three AI node types, so - `safeValidateSchema` — and `objectui validate` — refuses a stored JSON document that carries one of - the seven retired members BY NAME, at that member, where before it refused every document of these - three types at `type`. The renderer still ignores the value. The rest of this entry is kept as the - reading of this change; the objectui#10859 entry states what the validator now does. -- 0ea7054: Remove 37 runtime dependencies that no file in the declaring package consumes, and gate - the direction so the next one cannot land (objectui#8198). - - `check:phantom-deps` judges imports that are not declared; nothing judged the reverse, - so a declaration could outlive its last consumer indefinitely. That is what happened to - `recharts` in `@object-ui/components` after objectui#7397 deleted its only importer — it - was removed by hand on objectui#7625, and nothing would have reported the next one. The - new `pnpm check:unused-deps` asks the reverse question over `dependencies` and - `optionalDependencies` of every released package. - - **Potentially breaking, for consumers relying on hoisting.** Nothing these packages ship - changes: their Vite `external` predicates are path-based and never read `dependencies`, - so no built artifact moves. What changes is the install graph — a project that imports - one of the removed packages while depending only on the ObjectUI package that used to - drag it in will no longer resolve it. Declare it directly; that is the correct - dependency edge in either case. The removals, by package: - - - `@object-ui/plugin-designer`: `@dnd-kit/core`, `@dnd-kit/sortable`, `@dnd-kit/utilities`, `@object-ui/fields` - - `@object-ui/plugin-chatbot`: `react-markdown`, `react-syntax-highlighter`, `remark-gfm` (and the orphaned `@types/react-syntax-highlighter`) - - `@object-ui/plugin-report`: `@object-ui/plugin-grid`, `clsx`, `react-i18next`, `tailwind-merge` - - `@object-ui/plugin-map`: `@objectstack/spec`, `lucide-react`, `zod` - - `@object-ui/runner`: `class-variance-authority`, `clsx`, `tailwind-merge` - - `@object-ui/core`: `lodash`, `zod` - - `@object-ui/layout`: `clsx`, `tailwind-merge`, and `react-dom` — which it pinned at an exact version in `dependencies` while also declaring it as a peer range, i.e. a library hard-depending on the renderer it asks its host to supply - - `@object-ui/plugin-dashboard`: `clsx`, `tailwind-merge`, and the same `react-dom` defect - - `@object-ui/plugin-ai`: `@object-ui/react`, `clsx`, `tailwind-merge` - - `@object-ui/fields`: `clsx`, `tailwind-merge` - - `@object-ui/console`: `@object-ui/react-runtime`, `sucrase` - - `@object-ui/auth`: `@object-ui/types` - - `@object-ui/plugin-calendar`: `@object-ui/fields` - - `@object-ui/plugin-editor`, `@object-ui/plugin-markdown`: `@object-ui/react` - - `@object-ui/react`: `react-hook-form` - - Every one was verified by a whole-package grep before removal — the name appeared nowhere - under the package but its own manifest and CHANGELOG — and the whole workspace builds, - type-checks and tests green afterwards. - -### Patch Changes - -- bb5d4ee: Two display-locale faces now follow the session's display locale (objectui#10232). - - - `@object-ui/plugin-ai` is wired to `@object-ui/i18n` as a whole. The `nl-query`, - `ai-form-assist` and `ai-recommendations` components read their strings from a new - `ai.*` namespace, translated in all ten locale packs, instead of hard-coded English. - The `nl-query` history date, which was formatted in the machine's locale, and the - confidence and score percentages now format in the display locale - (`useDisplayLocale()`). The package's manifest now names `@object-ui/i18n`, which also - puts it inside the repo-wide machine-locale census. - - `@object-ui/app-shell`: the Studio home page's "recently viewed" times and the Data - pillar's last-saved time format in the display locale. They used to be handed - `useMetadataLocale()`, which picks the designer's string table and is `en-US` for every - language other than zh, so a de-DE session read US English. That hook's doc comment - now says it must never be used as an `Intl` locale. -- 4a1adb7: The AI form assistant's suggestion and applied counts read the right noun form in every language at every count (objectui#11445): each passes `count` to one i18next count family instead of choosing a `…CountOne` key on `=== 1`, so Arabic reads `3 اقتراحات` rather than a count label and Russian `2 предложения`. -- 39f4309: Published typings from every `vite-plugin-dts` package now carry an explicit extension on - every relative specifier, and a type error in the declaration build now fails the build - instead of being printed and ignored (objectui#5439, objectui#5483). - - **Consumers on `moduleResolution: nodenext` or `node16` may see NEW type errors, and that - is the fix working.** These packages re-export mostly through NAMED re-exports — - `export { useObjectChat } from './useObjectChat'`. TypeScript could not follow the - extensionless hop, but it still DECLARED the name, so the symbol resolved to a silent - `any`. Nothing errored; consumers simply got no types. With the extension emitted, the - symbol carries its real type, and any call site that was relying on the `any` now type - checks for the first time. This is the mode that produced the 21 residual `TS7006` on - `@object-ui/app-shell` reported against objectui#5365 — a type hole that opened quietly, - unlike objectui#5365's own `export * from './ui'` packages where the same defect surfaced - immediately as `TS2305: has no exported member`. - - 410 extensionless relative specifiers across 19 packages were emitted before this change; - the count is now 0 in all 22 packages that build typings through `vite-plugin-dts`. - `@object-ui/fields` was already clean — its sources write explicit `.js` specifiers — and - is wired so it stays that way. - - The second half changes no emitted output today: 22/22 packages built green unmodified, so - making the declaration step's exit code honest turns nothing red. It changes what a FUTURE - regression does — print and exit 0, versus fail the build. -- c6198c2: **Breaking for authored metadata:** `ComponentInput.label`, `ComponentInput.defaultValue` and - `ComponentInput.advanced` are RETIRED on both faces (objectui#7493 item ① and objectui#7781; - maintainer ruling A of 2026-09-06, immediate, no deprecation window; ADR-0049 enforce-or-remove). - They are the three keys the manifest serializer does not forward, and nothing read them on any - publication or consumption path. - - No manifest ever published them, so no consumer could ever have read them. `sdui-parser`'s - serializer (`packages/sdui-parser/src/index.ts`) forwards exactly seven keys per input — `name`, - `type`, `of`, `required`, `enum`, `binding`, `description` — so a value authored under any of the three - never reached `sdui.manifest.json`, the generated JSX `.d.ts`, or a diagnostic; its boundary type - has no slot for them; the registry's data-source seam reads `name` only; and neither the designer - nor the app-shell inspectors consult registry `inputs` at all. A structural census over every - `inputs:` array in the repository (re-measured on this change's merge-base, `name` 951 and `type` - 951 as the controls) counted the writes: `label` 908, `defaultValue` 245, `advanced` 9 — written on - nearly every registration, read by nothing. - - FROM → TO, per key — all three **TOMBSTONED, not removed**, because the route was measured on - the built face before it was chosen: `ComponentInputSchema` is a non-strict `z.object`, and an - undeclared key parses GREEN and is silently STRIPPED, so a deletion would have swallowed 1,162 - authored values in silence. The tombstone is what makes the refusal loud and by name. - - - `label?: string` → `label?: never` on the interface, `retirementTombstone()` on the Zod mirror. - Migration: delete the key. An input is identified by its `name` on every path that reaches it; - nothing ever rendered a label for it. - - `defaultValue?: any` → `defaultValue?: never` / `retirementTombstone()`. Migration: delete the - key. The renderer's own fallback read IS the default; tell the author about it in `description`, - which IS published. (Tightening the type to `unknown` was ruled out: it closes no error class, - since nothing reads the value.) - - `advanced?: boolean` → `advanced?: never` / `retirementTombstone()`. Migration: delete the key. - No designer surface ever hid an "advanced" input; there is nothing to write instead. - - The retirement kit: `?: never` on `ComponentInput` (`packages/types/src/base.ts`), so authoring one - is a `tsc` error at the registration site; `retirementTombstone()` on `ComponentInputSchema` - (`packages/types/src/zod/base.zod.ts`), so an authored value is REFUSED at parse time with - `code: 'invalid_type'`, the key named in the issue `path`, and the migration note as the message - (one string, both channels). Pinned in - `packages/types/src/__tests__/component-input-retired-keys-7493.test.ts`, which also holds a - tree-scoped absence census over every `inputs:` array under `packages/**` and `apps/**`. - - Accept-set change, stated plainly for reviewers: a document that sets any of the three keys on a - `ComponentInput` used to parse GREEN (the value was then dropped by the serializer) and now parses - RED. Every in-repo authoring site — 1,199 keys across 110 registration files, the three standalone - `ComponentInput[]` arrays and the two named input arrays `tsc` found included — is deleted in the same change, as the ruling's split rule - requires; the `WidgetRegistry` seam no longer copies the widget-manifest values onto the synthesized - `ComponentInput` (they fed nothing), and the data-source declaration `ELEMENT_DATA_SOURCE_INPUT` - drops its `label`. The patch entries on the other packages record exactly that: their registrations - stop authoring inert keys, with no runtime or published-manifest change. - - The nine test files that read `defaultValue` off a registration were re-pinned against the - renderer's ACTUAL default (its own fallback read, or the `defaultProps` it ships) instead of the - declaration that went away; two assertions that only restated the shadow default were dropped with - the reason on the line. - - The in-repo zero is what was measured. Whether anything OUTSIDE this repository writes these keys - is not measurable from here (the objectui#5674 limit); converting such a write from a silent drop - into a named refusal is exactly what the tombstones buy. `WidgetInput`'s own `label` / - `defaultValue` / `advanced` (the widget-manifest face) stay declared and writable — nothing has - ruled on that face; that it now has no reader either is recorded as objectui#7911. -- fb336df: Move `lucide-react` from `^1.31.0` to `^1.43.0` in every package that declares it, and - repair what the jump breaks, so icons resolved from a STRING keep drawing a glyph. - - Measured once against the installed 1.43.0 artifact when this change was made; nothing in - the repository re-derives these readings. Across the jump lucide removes no runtime export, - no public type name and no `lucide-react/dynamic.mjs` name, and every name this repository - imports from `lucide-react` resolves. Exactly one key leaves the runtime `icons` record: - `Trash2`, retired in favour of `trash`. - - What a user sees change: - - - Icons the lazy icon seam draws (`resolveIcon`, objectui#9251) get their path data again. - lucide 1.43.0 icon modules export their path data inside `__iconData` and no longer - export `__iconNode`; the seam now reads both. Reading only `__iconNode` against 1.43.0 - leaves every such glyph an empty box, with nothing thrown or logged. - - `DetailView`'s delete action and three schema-catalog examples spell their icon - `trash`, not `trash-2`, so they keep resolving. The glyph is unchanged: 1.43.0's `trash` - path data is byte-identical to the `trash-2` path data of 1.31.0 and 1.35.0. Anything - that already authored `trash` now draws that same artwork, because lucide moved it under - the `trash` name; a few other glyphs were also redrawn upstream. - - Icons imported as COMPONENTS carry lucide 1.43.0's own classes: one class per declared - alias (a spinner renders `class="lucide lucide-loader-circle lucide-loader-2 ..."`), and - no longer the class lucide used to derive from the PascalCase key where that differs - (`ArrowDown01` no longer carries `lucide-arrow-down01`). The canonical - `lucide-` class is still there, so a `.lucide-loader-circle` selector still - matches. - - Icons resolved from a STRING through the seam keep the classes they had: `lucide`, the - canonical `lucide-` class and the key-derived class. They do not carry - lucide's per-alias classes. That is a maintainer ruling (objectui#8941, option C): the - alias list lives only inside each lazily loaded icon module, and it was not moved into - the eager name list. A selector that targets an alias class matches a component-imported - icon and not a string-resolved one. This also dates the example in the objectui#9251 - entry: `trash-2` no longer resolves as a string, so no seam-drawn glyph carries - `lucide-trash2 lucide-trash-2`; a digit-bearing name that still resolves, such as - `arrow-down-0-1`, carries `lucide-arrow-down01 lucide-arrow-down-0-1`. -- Updated dependencies [97abedc] -- Updated dependencies [b46c58f] -- Updated dependencies [a507334] -- Updated dependencies [ad694ac] -- Updated dependencies [6f96fca] -- Updated dependencies [afb2284] -- Updated dependencies [3ac2de8] -- Updated dependencies [63f4f92] -- Updated dependencies [777fca2] -- Updated dependencies [c131d9e] -- Updated dependencies [5f00ff4] -- Updated dependencies [c9e073a] -- Updated dependencies [7b395d8] -- Updated dependencies [0879812] -- Updated dependencies [8cedb0d] -- Updated dependencies [4c6f549] -- Updated dependencies [6cc910b] -- Updated dependencies [061f5e8] -- Updated dependencies [2dd4d3f] -- Updated dependencies [e686f4d] -- Updated dependencies [4ab4f1b] -- Updated dependencies [b57107d] -- Updated dependencies [e3ea4f9] -- Updated dependencies [bb5d4ee] -- Updated dependencies [dc666f7] -- Updated dependencies [3335767] -- Updated dependencies [8b1f066] -- Updated dependencies [af243c1] -- Updated dependencies [cff4b77] -- Updated dependencies [961ceaa] -- Updated dependencies [f3f4e4c] -- Updated dependencies [a05c350] -- Updated dependencies [1dbb993] -- Updated dependencies [808f339] -- Updated dependencies [6cf5999] -- Updated dependencies [274e14a] -- Updated dependencies [8c10f4f] -- Updated dependencies [90dac98] -- Updated dependencies [6096f20] -- Updated dependencies [544aca2] -- Updated dependencies [ea02938] -- Updated dependencies [a14fb23] -- Updated dependencies [ae98f1d] -- Updated dependencies [f98eddf] -- Updated dependencies [ce6bd99] -- Updated dependencies [a5b08c9] -- Updated dependencies [86982ac] -- Updated dependencies [cdefa2a] -- Updated dependencies [0961d5e] -- Updated dependencies [6276478] -- Updated dependencies [5e67837] -- Updated dependencies [ff14e29] -- Updated dependencies [9b28151] -- Updated dependencies [64563a9] -- Updated dependencies [1a5003f] -- Updated dependencies [09ab32b] -- Updated dependencies [8acc51b] -- Updated dependencies [ea9d17f] -- Updated dependencies [45362a3] -- Updated dependencies [2a943bf] -- Updated dependencies [93a689d] -- Updated dependencies [4357a27] -- Updated dependencies [e5f4343] -- Updated dependencies [3261e64] -- Updated dependencies [f5178a2] -- Updated dependencies [8740e86] -- Updated dependencies [d22b37b] -- Updated dependencies [1daf477] -- Updated dependencies [6c2f3c5] -- Updated dependencies [fb13e85] -- Updated dependencies [c2d8659] -- Updated dependencies [e0f8202] -- Updated dependencies [ac2d6f1] -- Updated dependencies [9fbbb17] -- Updated dependencies [c3a26cc] -- Updated dependencies [a66e58e] -- Updated dependencies [d89492c] -- Updated dependencies [9a5f998] -- Updated dependencies [9327397] -- Updated dependencies [17cc3a3] -- Updated dependencies [02e6d36] -- Updated dependencies [4758b33] -- Updated dependencies [4758b33] -- Updated dependencies [f905090] -- Updated dependencies [9c78ebe] -- Updated dependencies [9c78ebe] -- Updated dependencies [12809a5] -- Updated dependencies [7afc81d] -- Updated dependencies [f9c06ef] -- Updated dependencies [5ad3b88] -- Updated dependencies [f9d772b] -- Updated dependencies [97b6c21] -- Updated dependencies [26ca2ad] -- Updated dependencies [41ae65b] -- Updated dependencies [baac95a] -- Updated dependencies [13220af] -- Updated dependencies [29b45f6] -- Updated dependencies [39b8d51] -- Updated dependencies [17b323e] -- Updated dependencies [b956e69] -- Updated dependencies [33e58d8] -- Updated dependencies [256b4c9] -- Updated dependencies [c5ec15c] -- Updated dependencies [fec3b1a] -- Updated dependencies [b8e0941] -- Updated dependencies [0c50f18] -- Updated dependencies [1dae95a] -- Updated dependencies [e32dae1] -- Updated dependencies [30b11ad] -- Updated dependencies [4aebea0] -- Updated dependencies [f976774] -- Updated dependencies [3d6badf] -- Updated dependencies [25cb364] -- Updated dependencies [a60539b] -- Updated dependencies [de1b879] -- Updated dependencies [c6678b1] -- Updated dependencies [0638322] -- Updated dependencies [e3782d2] -- Updated dependencies [db0beb2] -- Updated dependencies [997ce38] -- Updated dependencies [ae0b9d3] -- Updated dependencies [ad1785c] -- Updated dependencies [3b469c8] -- Updated dependencies [990a2d6] -- Updated dependencies [1c84036] -- Updated dependencies [1c84036] -- Updated dependencies [6650259] -- Updated dependencies [4f8b7f8] -- Updated dependencies [9e6619f] -- Updated dependencies [f6ae5e2] -- Updated dependencies [eb97ce6] -- Updated dependencies [7343376] -- Updated dependencies [b2683a2] -- Updated dependencies [dded788] -- Updated dependencies [b45d463] -- Updated dependencies [54a7830] -- Updated dependencies [f3135a4] -- Updated dependencies [b5696d3] -- Updated dependencies [3f9d926] -- Updated dependencies [e978ed5] -- Updated dependencies [6a7f24e] -- Updated dependencies [b3c96d6] -- Updated dependencies [8d0ca91] -- Updated dependencies [c30c8dd] -- Updated dependencies [244d516] -- Updated dependencies [deca847] -- Updated dependencies [ac15833] -- Updated dependencies [ac15833] -- Updated dependencies [e2dffc9] -- Updated dependencies [328abeb] -- Updated dependencies [dd0d78f] -- Updated dependencies [24d3e65] -- Updated dependencies [95a7c8d] -- Updated dependencies [e227156] -- Updated dependencies [cc4e476] -- Updated dependencies [92970c4] -- Updated dependencies [d570eaa] -- Updated dependencies [4b742f4] -- Updated dependencies [42687ba] -- Updated dependencies [6cd8f66] -- Updated dependencies [24a0f14] -- Updated dependencies [797a30f] -- Updated dependencies [b4075c0] -- Updated dependencies [9b85600] -- Updated dependencies [e327c89] -- Updated dependencies [2eaf5be] -- Updated dependencies [bf7ab35] -- Updated dependencies [99878d8] -- Updated dependencies [3c13675] -- Updated dependencies [63ab761] -- Updated dependencies [0eb9f36] -- Updated dependencies [ae582b7] -- Updated dependencies [51c2949] -- Updated dependencies [a4b017e] -- Updated dependencies [8732846] -- Updated dependencies [559a2e2] -- Updated dependencies [db11afd] -- Updated dependencies [154075a] -- Updated dependencies [582edef] -- Updated dependencies [19f484f] -- Updated dependencies [0a78a20] -- Updated dependencies [615346d] -- Updated dependencies [75dcc81] -- Updated dependencies [55a12a8] -- Updated dependencies [edfcf5a] -- Updated dependencies [0a3e540] -- Updated dependencies [c021b35] -- Updated dependencies [f61dab1] -- Updated dependencies [b0a05dd] -- Updated dependencies [dd5ff19] -- Updated dependencies [54997ff] -- Updated dependencies [81f8498] -- Updated dependencies [a782fa7] -- Updated dependencies [1d6a23d] -- Updated dependencies [76e9df0] -- Updated dependencies [0ecaa7d] -- Updated dependencies [b24f93a] -- Updated dependencies [6158e4c] -- Updated dependencies [cff8641] -- Updated dependencies [c27b575] -- Updated dependencies [84b275c] -- Updated dependencies [0e6e76b] -- Updated dependencies [bf43afa] -- Updated dependencies [385ebc5] -- Updated dependencies [858eafb] -- Updated dependencies [a8c5509] -- Updated dependencies [c2a8d23] -- Updated dependencies [0ffc423] -- Updated dependencies [3cc4fe5] -- Updated dependencies [cd5b19a] -- Updated dependencies [cd5b19a] -- Updated dependencies [17dc167] -- Updated dependencies [20d23be] -- Updated dependencies [20d23be] -- Updated dependencies [2e3da72] -- Updated dependencies [1263e40] -- Updated dependencies [e6bc087] -- Updated dependencies [9419df1] -- Updated dependencies [8bab157] -- Updated dependencies [a7557a7] -- Updated dependencies [7d074ba] -- Updated dependencies [6158e4c] -- Updated dependencies [6158e4c] -- Updated dependencies [52aad5c] -- Updated dependencies [18d1a0a] -- Updated dependencies [7fed09d] -- Updated dependencies [58da8ae] -- Updated dependencies [138ad45] -- Updated dependencies [138ad45] -- Updated dependencies [5262f7d] -- Updated dependencies [6aa029b] -- Updated dependencies [be52115] -- Updated dependencies [770cc5b] -- Updated dependencies [1a88ce2] -- Updated dependencies [a1a44d6] -- Updated dependencies [e0a9c67] -- Updated dependencies [5638529] -- Updated dependencies [5638529] -- Updated dependencies [d0fba91] -- Updated dependencies [c476be0] -- Updated dependencies [c82ff39] -- Updated dependencies [6c3da53] -- Updated dependencies [31987bd] -- Updated dependencies [3c3ce15] -- Updated dependencies [063119f] -- Updated dependencies [e100589] -- Updated dependencies [304f611] -- Updated dependencies [e46ee77] -- Updated dependencies [f9c8c4e] -- Updated dependencies [6e9c8d2] -- Updated dependencies [9547063] -- Updated dependencies [3f6efd6] -- Updated dependencies [55d18c6] -- Updated dependencies [c4ab6d0] -- Updated dependencies [0e9058b] -- Updated dependencies [c4ab6d0] -- Updated dependencies [5988b6b] -- Updated dependencies [6158e4c] -- Updated dependencies [00ccdf7] -- Updated dependencies [9d9ed54] -- Updated dependencies [4a1adb7] -- Updated dependencies [4a1adb7] -- Updated dependencies [50c73fe] -- Updated dependencies [ca3de72] -- Updated dependencies [83e3f83] -- Updated dependencies [401611b] -- Updated dependencies [2c0ddf2] -- Updated dependencies [4abc0aa] -- Updated dependencies [f560ded] -- Updated dependencies [2b188fa] -- Updated dependencies [f68e0a0] -- Updated dependencies [aea682a] -- Updated dependencies [fcdc8ec] -- Updated dependencies [2d576e4] -- Updated dependencies [8366acc] -- Updated dependencies [95e58a3] -- Updated dependencies [9d7419b] -- Updated dependencies [fc7db05] -- Updated dependencies [9ed8d0f] -- Updated dependencies [c73cdb5] -- Updated dependencies [6f5719e] -- Updated dependencies [d0097af] -- Updated dependencies [432882b] -- Updated dependencies [64dae8e] -- Updated dependencies [b06e374] -- Updated dependencies [06a8af5] -- Updated dependencies [6a91586] -- Updated dependencies [a04d7c6] -- Updated dependencies [5ccc500] -- Updated dependencies [f3c2bb0] -- Updated dependencies [978507b] -- Updated dependencies [778138e] -- Updated dependencies [9801765] -- Updated dependencies [9cebfca] -- Updated dependencies [460575f] -- Updated dependencies [d796c8d] -- Updated dependencies [594704f] -- Updated dependencies [d3995fe] -- Updated dependencies [d88e20f] -- Updated dependencies [2d7304d] -- Updated dependencies [636b236] -- Updated dependencies [4172589] -- Updated dependencies [d6d8fb9] -- Updated dependencies [64d624d] -- Updated dependencies [053fdc8] -- Updated dependencies [41b7ce3] -- Updated dependencies [ae476b8] -- Updated dependencies [39f4309] -- Updated dependencies [95bad12] -- Updated dependencies [d2fb6ef] -- Updated dependencies [7cd3987] -- Updated dependencies [ee3b878] -- Updated dependencies [e304a4e] -- Updated dependencies [fda49e5] -- Updated dependencies [490d9a9] -- Updated dependencies [fc62bb4] -- Updated dependencies [41df893] -- Updated dependencies [0cba1b7] -- Updated dependencies [00f3eb5] -- Updated dependencies [1ec291c] -- Updated dependencies [453dbaa] -- Updated dependencies [95f8704] -- Updated dependencies [f8cdbf2] -- Updated dependencies [69a2163] -- Updated dependencies [24e027e] -- Updated dependencies [2c3cd1b] -- Updated dependencies [e176053] -- Updated dependencies [e30ed15] -- Updated dependencies [90665e0] -- Updated dependencies [8d3a529] -- Updated dependencies [5ac2e2c] -- Updated dependencies [194fae1] -- Updated dependencies [7e19d03] -- Updated dependencies [b08b7eb] -- Updated dependencies [1e946c9] -- Updated dependencies [546ddf7] -- Updated dependencies [864154e] -- Updated dependencies [b023625] -- Updated dependencies [75bd83d] -- Updated dependencies [44d075b] -- Updated dependencies [40c479a] -- Updated dependencies [971d387] -- Updated dependencies [ee851c3] -- Updated dependencies [6414dfd] -- Updated dependencies [a8d5c71] -- Updated dependencies [905b21f] -- Updated dependencies [88e9109] -- Updated dependencies [2c45966] -- Updated dependencies [db3a600] -- Updated dependencies [3a3db76] -- Updated dependencies [0d723a3] -- Updated dependencies [0c95d3d] -- Updated dependencies [3e4fa2c] -- Updated dependencies [b5b928a] -- Updated dependencies [6fd2cf7] -- Updated dependencies [5fa06c4] -- Updated dependencies [52a43de] -- Updated dependencies [195052f] -- Updated dependencies [e4559d1] -- Updated dependencies [2c71482] -- Updated dependencies [129bcc5] -- Updated dependencies [a26b9e4] -- Updated dependencies [5ef9c4f] -- Updated dependencies [46f0bb4] -- Updated dependencies [06b82b8] -- Updated dependencies [8ec11e1] -- Updated dependencies [6f81384] -- Updated dependencies [f8c70f4] -- Updated dependencies [5d3a2d1] -- Updated dependencies [8f1d995] -- Updated dependencies [b362c1b] -- Updated dependencies [f9c34df] -- Updated dependencies [dddb942] -- Updated dependencies [00c665e] -- Updated dependencies [29754cf] -- Updated dependencies [d7de534] -- Updated dependencies [3c2b6f7] -- Updated dependencies [6e88630] -- Updated dependencies [b84dc18] -- Updated dependencies [ac8abb0] -- Updated dependencies [9d86e1d] -- Updated dependencies [3a5817f] -- Updated dependencies [99a3c2d] -- Updated dependencies [5961030] -- Updated dependencies [f24de8b] -- Updated dependencies [c8ea8af] -- Updated dependencies [9602dc8] -- Updated dependencies [3190414] -- Updated dependencies [4e480f5] -- Updated dependencies [38a123c] -- Updated dependencies [299102e] -- Updated dependencies [d7acad6] -- Updated dependencies [45a9aeb] -- Updated dependencies [713db46] -- Updated dependencies [c71e14d] -- Updated dependencies [bf3a03c] -- Updated dependencies [cb55718] -- Updated dependencies [831be72] -- Updated dependencies [29cb85b] -- Updated dependencies [3e028c8] -- Updated dependencies [d0889e2] -- Updated dependencies [ce503e5] -- Updated dependencies [f20dcf0] -- Updated dependencies [aff3d7a] -- Updated dependencies [4ca30d0] -- Updated dependencies [7a5da14] -- Updated dependencies [fff9645] -- Updated dependencies [9c3b7ce] -- Updated dependencies [2c1c967] -- Updated dependencies [9486ac6] -- Updated dependencies [9486ac6] -- Updated dependencies [4d5f9b4] -- Updated dependencies [d6ceb8d] -- Updated dependencies [969ba84] -- Updated dependencies [4c68077] -- Updated dependencies [7977ff9] -- Updated dependencies [3beef6d] -- Updated dependencies [06b8c42] -- Updated dependencies [b97790a] -- Updated dependencies [dbd5194] -- Updated dependencies [7c9b044] -- Updated dependencies [b392674] -- Updated dependencies [4f3a1e2] -- Updated dependencies [846889b] -- Updated dependencies [2acd8e1] -- Updated dependencies [26896c6] -- Updated dependencies [045d20b] -- Updated dependencies [a2d2515] -- Updated dependencies [c18d099] -- Updated dependencies [adb2a86] -- Updated dependencies [4562ea5] -- Updated dependencies [3619792] -- Updated dependencies [3561bd2] -- Updated dependencies [bf97b98] -- Updated dependencies [320374d] -- Updated dependencies [b0d308d] -- Updated dependencies [40f34b4] -- Updated dependencies [8063bcb] -- Updated dependencies [b74a859] -- Updated dependencies [d4493fd] -- Updated dependencies [240b80f] -- Updated dependencies [77cb489] -- Updated dependencies [bfaa158] -- Updated dependencies [777e5c6] -- Updated dependencies [0c386dd] -- Updated dependencies [9e37d9b] -- Updated dependencies [5ad86dd] -- Updated dependencies [16a725f] -- Updated dependencies [4dfdcc3] -- Updated dependencies [6a449fc] -- Updated dependencies [446d93d] -- Updated dependencies [ecd9cb2] -- Updated dependencies [98d4108] -- Updated dependencies [0e3b3be] -- Updated dependencies [a29ae2d] -- Updated dependencies [220c18d] -- Updated dependencies [00d3f09] -- Updated dependencies [4388f71] -- Updated dependencies [0b1ac58] -- Updated dependencies [c93b4d5] -- Updated dependencies [c1fe272] -- Updated dependencies [3cab570] -- Updated dependencies [8ad218d] -- Updated dependencies [3e41187] -- Updated dependencies [5f78953] -- Updated dependencies [639114c] -- Updated dependencies [639114c] -- Updated dependencies [1490691] -- Updated dependencies [1f31d3a] -- Updated dependencies [d1842ab] -- Updated dependencies [78ca238] -- Updated dependencies [d8ec8d6] -- Updated dependencies [351eb31] -- Updated dependencies [866cd1d] -- Updated dependencies [20c04b2] -- Updated dependencies [01c9023] -- Updated dependencies [48c19bd] -- Updated dependencies [a6d8b8d] -- Updated dependencies [4b5bb95] -- Updated dependencies [b652514] -- Updated dependencies [adbda1b] -- Updated dependencies [adbda1b] -- Updated dependencies [8952395] -- Updated dependencies [e2b3826] -- Updated dependencies [0348bc9] -- Updated dependencies [e8c553b] -- Updated dependencies [2e32ed4] -- Updated dependencies [7c3df8f] -- Updated dependencies [a4514e8] -- Updated dependencies [db3896c] -- Updated dependencies [b9f5ff1] -- Updated dependencies [e75f4c9] -- Updated dependencies [19f1639] -- Updated dependencies [4704aa4] -- Updated dependencies [47547d0] -- Updated dependencies [1bee5d0] -- Updated dependencies [858cd72] -- Updated dependencies [cfc9b6d] -- Updated dependencies [554f2b6] -- Updated dependencies [72f55c9] -- Updated dependencies [26e06d7] -- Updated dependencies [669d71b] -- Updated dependencies [ed27d7c] -- Updated dependencies [52c8cf7] -- Updated dependencies [2ceb43a] -- Updated dependencies [7cdd2b9] -- Updated dependencies [52c8cf7] -- Updated dependencies [3399704] -- Updated dependencies [7bf244b] -- Updated dependencies [f0bb9fa] -- Updated dependencies [81a2eb1] -- Updated dependencies [caa0cd3] -- Updated dependencies [caa0cd3] -- Updated dependencies [20cb8db] -- Updated dependencies [25c7d58] -- Updated dependencies [00d2fa6] -- Updated dependencies [77b2a18] -- Updated dependencies [c6198c2] -- Updated dependencies [721d1e0] -- Updated dependencies [c2f0f48] -- Updated dependencies [1237ae4] -- Updated dependencies [2f61238] -- Updated dependencies [51eb515] -- Updated dependencies [c354ce5] -- Updated dependencies [8fe8e5c] -- Updated dependencies [feac439] -- Updated dependencies [9ae871d] -- Updated dependencies [efbd566] -- Updated dependencies [2a5bf45] -- Updated dependencies [9587fc9] -- Updated dependencies [e62c44e] -- Updated dependencies [daf9d57] -- Updated dependencies [23b9958] -- Updated dependencies [c15d7ec] -- Updated dependencies [5d0876c] -- Updated dependencies [f7ace0a] -- Updated dependencies [b041b9c] -- Updated dependencies [ce2aaef] -- Updated dependencies [544ecba] -- Updated dependencies [bc640ec] -- Updated dependencies [1e215c4] -- Updated dependencies [da6e191] -- Updated dependencies [3e377c9] -- Updated dependencies [a3eb5d0] -- Updated dependencies [4ce14f1] -- Updated dependencies [aef97e5] -- Updated dependencies [2af1fa7] -- Updated dependencies [c14d3a0] -- Updated dependencies [a137d0c] -- Updated dependencies [caf477f] -- Updated dependencies [f6375da] -- Updated dependencies [967e5d8] -- Updated dependencies [c907a9c] -- Updated dependencies [a4611b3] -- Updated dependencies [20316ba] -- Updated dependencies [d3499b3] -- Updated dependencies [91f9276] -- Updated dependencies [309c75e] -- Updated dependencies [c9f9bae] -- Updated dependencies [18897a4] -- Updated dependencies [8b7ea39] -- Updated dependencies [a915064] -- Updated dependencies [dcbf0b2] -- Updated dependencies [52cac38] -- Updated dependencies [93fea2e] -- Updated dependencies [1422a92] -- Updated dependencies [d05fe17] -- Updated dependencies [a480f79] -- Updated dependencies [f08d1a8] -- Updated dependencies [64a252d] -- Updated dependencies [786bc91] -- Updated dependencies [75fca96] -- Updated dependencies [7ca6ddd] -- Updated dependencies [f1cd290] -- Updated dependencies [5a41ce7] -- Updated dependencies [8d50bc2] -- Updated dependencies [604476d] -- Updated dependencies [335abea] -- Updated dependencies [edea22a] -- Updated dependencies [0f5cadf] -- Updated dependencies [4f9f1ee] -- Updated dependencies [66e8b2a] -- Updated dependencies [aa083cd] -- Updated dependencies [12b5992] -- Updated dependencies [b93e245] -- Updated dependencies [c842594] -- Updated dependencies [290de37] -- Updated dependencies [e1c27e4] -- Updated dependencies [8c8da45] -- Updated dependencies [8cd8eb5] -- Updated dependencies [cf1d29e] -- Updated dependencies [1bd79c8] -- Updated dependencies [af9e957] -- Updated dependencies [b1030c7] -- Updated dependencies [c974edf] -- Updated dependencies [ad852b6] -- Updated dependencies [6778809] -- Updated dependencies [ad66d79] -- Updated dependencies [0758bd8] -- Updated dependencies [ee4d19f] -- Updated dependencies [496d31d] -- Updated dependencies [0ea7054] -- Updated dependencies [9a853f2] -- Updated dependencies [cb847fd] -- Updated dependencies [ee70287] -- Updated dependencies [3e98e13] -- Updated dependencies [a695f50] -- Updated dependencies [fc32921] -- Updated dependencies [4eaa835] -- Updated dependencies [8f9d87a] -- Updated dependencies [b1777ae] -- Updated dependencies [24845c4] -- Updated dependencies [6f864cf] -- Updated dependencies [24d1edd] -- Updated dependencies [645087c] -- Updated dependencies [33f4a19] -- Updated dependencies [6e9a3d4] -- Updated dependencies [4a292d2] -- Updated dependencies [5323168] -- Updated dependencies [841dd2b] -- Updated dependencies [3014fc0] -- Updated dependencies [dacb402] -- Updated dependencies [846cec0] -- Updated dependencies [91facae] -- Updated dependencies [b38014e] -- Updated dependencies [474797d] -- Updated dependencies [704e695] -- Updated dependencies [a407bd6] -- Updated dependencies [317dbce] -- Updated dependencies [309728c] -- Updated dependencies [aa08d7e] -- Updated dependencies [3a43a15] -- Updated dependencies [868e825] -- Updated dependencies [f76f436] -- Updated dependencies [ce45a03] -- Updated dependencies [421544b] -- Updated dependencies [fb01022] -- Updated dependencies [e9d9212] -- Updated dependencies [ecfb693] -- Updated dependencies [abc1b18] -- Updated dependencies [81a51db] -- Updated dependencies [67749c7] -- Updated dependencies [507b61b] -- Updated dependencies [512c84b] -- Updated dependencies [c300267] -- Updated dependencies [fb3a101] -- Updated dependencies [d4733f2] -- Updated dependencies [7c9145f] -- Updated dependencies [1570eac] -- Updated dependencies [f391ede] -- Updated dependencies [f5cfbbd] -- Updated dependencies [f5cfbbd] -- Updated dependencies [8b532cb] -- Updated dependencies [64c3cdd] -- Updated dependencies [4d65991] -- Updated dependencies [c42554e] -- Updated dependencies [555b4ec] -- Updated dependencies [1ccfc23] -- Updated dependencies [542718f] -- Updated dependencies [7f27bc5] -- Updated dependencies [0a174f3] -- Updated dependencies [676f677] -- Updated dependencies [f95b140] -- Updated dependencies [541ce4e] -- Updated dependencies [6479086] -- Updated dependencies [d79f525] -- Updated dependencies [d1865d2] -- Updated dependencies [55ba3ff] -- Updated dependencies [f1190b0] -- Updated dependencies [561abef] -- Updated dependencies [ef52001] -- Updated dependencies [6a4680b] -- Updated dependencies [c3a4273] -- Updated dependencies [abf710d] -- Updated dependencies [093af32] -- Updated dependencies [1bd1be7] -- Updated dependencies [d234fa9] -- Updated dependencies [adf5812] -- Updated dependencies [5058336] -- Updated dependencies [2f6b2bf] -- Updated dependencies [2028b31] -- Updated dependencies [63601ab] -- Updated dependencies [c372b29] -- Updated dependencies [152f0a7] -- Updated dependencies [8693b85] -- Updated dependencies [e82dad1] -- Updated dependencies [58b7b3d] -- Updated dependencies [84defab] -- Updated dependencies [681d3f1] -- Updated dependencies [969d4f2] -- Updated dependencies [f3bc481] -- Updated dependencies [b79aac2] -- Updated dependencies [93fc0e7] -- Updated dependencies [a4b723f] -- Updated dependencies [2b10ca0] -- Updated dependencies [7db4a81] -- Updated dependencies [19a0b0e] -- Updated dependencies [526fc11] -- Updated dependencies [f8e3e9a] -- Updated dependencies [3b6bc69] -- Updated dependencies [6214db6] -- Updated dependencies [6732df4] -- Updated dependencies [fe9e0d0] -- Updated dependencies [63fb72c] -- Updated dependencies [804831c] -- Updated dependencies [279e48e] -- Updated dependencies [8700d6d] -- Updated dependencies [8db2a0f] -- Updated dependencies [30443fb] -- Updated dependencies [da45e6b] -- Updated dependencies [7533465] -- Updated dependencies [835f0f3] -- Updated dependencies [a9d97be] -- Updated dependencies [ed35b44] -- Updated dependencies [9ba7e9c] -- Updated dependencies [729e851] -- Updated dependencies [96919a4] -- Updated dependencies [345e24a] -- Updated dependencies [20b507a] -- Updated dependencies [2e471dc] -- Updated dependencies [6748587] -- Updated dependencies [be50942] -- Updated dependencies [775e079] -- Updated dependencies [7e8b3c0] -- Updated dependencies [53374dc] -- Updated dependencies [f6fb83f] -- Updated dependencies [2049b03] -- Updated dependencies [2bf34f7] -- Updated dependencies [15b33ae] -- Updated dependencies [7cbc724] -- Updated dependencies [7098eed] -- Updated dependencies [3df7c5c] -- Updated dependencies [fb91ac9] -- Updated dependencies [fb91ac9] -- Updated dependencies [641fb55] -- Updated dependencies [8524372] -- Updated dependencies [7cbefa5] -- Updated dependencies [72d6587] -- Updated dependencies [a272a4f] -- Updated dependencies [55f39ee] -- Updated dependencies [0970a0e] -- Updated dependencies [e427e9c] -- Updated dependencies [bbc9dc3] -- Updated dependencies [02f1813] -- Updated dependencies [1ef89c0] -- Updated dependencies [ba0b61a] -- Updated dependencies [ba0b61a] -- Updated dependencies [ac716ff] -- Updated dependencies [f0f3cd5] -- Updated dependencies [ab856ed] -- Updated dependencies [20f3e65] -- Updated dependencies [bbba098] -- Updated dependencies [87af769] -- Updated dependencies [3be720e] -- Updated dependencies [43c0d17] -- Updated dependencies [c3df43a] -- Updated dependencies [d16d0e9] -- Updated dependencies [bbe57fd] -- Updated dependencies [272a530] -- Updated dependencies [1779e8d] -- Updated dependencies [f7fcc2c] -- Updated dependencies [f0f4d6c] -- Updated dependencies [4128188] -- Updated dependencies [b253c4e] -- Updated dependencies [78a9c67] -- Updated dependencies [4a7ef0d] -- Updated dependencies [4598f6d] -- Updated dependencies [dea17b4] -- Updated dependencies [89bb77a] -- Updated dependencies [06611e4] -- Updated dependencies [44152c4] -- Updated dependencies [66abbde] -- Updated dependencies [dc3893d] -- Updated dependencies [1bbaa16] -- Updated dependencies [6ee259a] -- Updated dependencies [e708426] -- Updated dependencies [3b6d53b] -- Updated dependencies [276d174] -- Updated dependencies [2982ed9] -- Updated dependencies [a8198de] -- Updated dependencies [05a49f2] -- Updated dependencies [a78cd37] -- Updated dependencies [5ea623e] -- Updated dependencies [5eabe86] -- Updated dependencies [ca5d671] -- Updated dependencies [32bf2d6] -- Updated dependencies [ff0c384] -- Updated dependencies [af4fb29] -- Updated dependencies [c698a81] -- Updated dependencies [ff5ef1c] -- Updated dependencies [9a97800] -- Updated dependencies [6bca0e4] -- Updated dependencies [81c0bc4] -- Updated dependencies [3c76801] -- Updated dependencies [60500cb] -- Updated dependencies [2fcefb9] -- Updated dependencies [77f846a] -- Updated dependencies [bc5870c] -- Updated dependencies [b55a346] -- Updated dependencies [065bba7] -- Updated dependencies [f760064] -- Updated dependencies [dd19463] -- Updated dependencies [6791717] -- Updated dependencies [8ea3bee] -- Updated dependencies [100547e] -- Updated dependencies [3a58149] -- Updated dependencies [6d1c155] -- Updated dependencies [d7573b3] -- Updated dependencies [bf3edfe] -- Updated dependencies [2c8474c] -- Updated dependencies [6ce89da] -- Updated dependencies [0e05aac] -- Updated dependencies [ae61ad4] -- Updated dependencies [5aed9e4] -- Updated dependencies [83c77dc] -- Updated dependencies [18a8e7d] -- Updated dependencies [e7957ab] -- Updated dependencies [f7e34ca] -- Updated dependencies [e719ebd] -- Updated dependencies [516583b] -- Updated dependencies [f9e4f91] -- Updated dependencies [6ef48b1] -- Updated dependencies [fa429cf] -- Updated dependencies [ed8df3e] -- Updated dependencies [fe76ece] -- Updated dependencies [8b446f5] -- Updated dependencies [8e74b27] -- Updated dependencies [7102b20] -- Updated dependencies [8ebd57f] -- Updated dependencies [617707a] -- Updated dependencies [c40f3b8] -- Updated dependencies [58770f3] -- Updated dependencies [aefe428] -- Updated dependencies [485f096] -- Updated dependencies [7357447] -- Updated dependencies [199d31b] -- Updated dependencies [b655a9d] -- Updated dependencies [3e01cb5] -- Updated dependencies [7138bc1] -- Updated dependencies [cef27e2] -- Updated dependencies [4e8622b] -- Updated dependencies [dffd752] -- Updated dependencies [06973aa] -- Updated dependencies [50798f3] -- Updated dependencies [6a576c9] -- Updated dependencies [105f3c5] -- Updated dependencies [3ccd9e8] -- Updated dependencies [689b979] -- Updated dependencies [c70f865] -- Updated dependencies [e546222] -- Updated dependencies [fd13f52] -- Updated dependencies [d7bd274] -- Updated dependencies [fffa30d] -- Updated dependencies [fb336df] -- Updated dependencies [9d9040d] -- Updated dependencies [20e317c] -- Updated dependencies [0fce2ef] -- Updated dependencies [42df928] -- Updated dependencies [0e2ddd4] -- Updated dependencies [b7479ab] -- Updated dependencies [de570cc] -- Updated dependencies [b2ea297] -- Updated dependencies [5b5a5c3] -- Updated dependencies [14582b8] -- Updated dependencies [51e144e] -- Updated dependencies [19cbf10] -- Updated dependencies [ab92940] -- Updated dependencies [a691c0b] -- Updated dependencies [0b1326d] -- Updated dependencies [1e66879] -- Updated dependencies [c5200f0] -- Updated dependencies [af3861f] -- Updated dependencies [515f171] -- Updated dependencies [1f4e029] -- Updated dependencies [258d264] -- Updated dependencies [8033ad1] -- Updated dependencies [fa140b8] -- Updated dependencies [71cba28] -- Updated dependencies [190fbd0] -- Updated dependencies [c00bf28] -- Updated dependencies [93127bd] -- Updated dependencies [f2158ec] -- Updated dependencies [759606e] -- Updated dependencies [fd8dace] -- Updated dependencies [72ffc34] -- Updated dependencies [a51fa0c] -- Updated dependencies [51f3d8d] -- Updated dependencies [78cbdb5] -- Updated dependencies [b7543a9] -- Updated dependencies [6c6cee7] -- Updated dependencies [42887e0] -- Updated dependencies [83fe6e7] -- Updated dependencies [d1ab06f] -- Updated dependencies [38a9568] -- Updated dependencies [91783c4] -- Updated dependencies [982885d] -- Updated dependencies [ca39427] -- Updated dependencies [bd09957] -- Updated dependencies [5a07e67] -- Updated dependencies [2d36552] -- Updated dependencies [45d8288] -- Updated dependencies [b2437a7] -- Updated dependencies [f157423] -- Updated dependencies [7a90afd] -- Updated dependencies [eddc1dd] -- Updated dependencies [490f482] -- Updated dependencies [27308c5] -- Updated dependencies [8689166] -- Updated dependencies [c9327c9] -- Updated dependencies [920165d] -- Updated dependencies [9101be5] -- Updated dependencies [968dc1e] -- Updated dependencies [57f9b07] -- Updated dependencies [3c73d99] -- Updated dependencies [ed71d9e] -- Updated dependencies [7776fc2] -- Updated dependencies [e76634c] -- Updated dependencies [fb96ecb] -- Updated dependencies [1170ed1] -- Updated dependencies [92814db] -- Updated dependencies [4d73b07] - - @object-ui/core@17.7.0 - - @object-ui/types@17.7.0 - - @object-ui/i18n@17.7.0 - - @object-ui/components@17.7.0 - -## 17.6.0 - -### Patch Changes - -- Updated dependencies [88085e3] -- Updated dependencies [516663d] -- Updated dependencies [460c4d0] -- Updated dependencies [0ae27f7] -- Updated dependencies [2533ec5] -- Updated dependencies [78c0f9a] -- Updated dependencies [bbe8b86] -- Updated dependencies [8477be5] -- Updated dependencies [279fb13] -- Updated dependencies [2e82ab2] -- Updated dependencies [ad07b65] -- Updated dependencies [41f498b] -- Updated dependencies [e1d4251] -- Updated dependencies [40d3a33] -- Updated dependencies [8b9dc62] -- Updated dependencies [1184192] -- Updated dependencies [a2a9747] -- Updated dependencies [a1609a6] -- Updated dependencies [53f23bc] -- Updated dependencies [c4533dc] -- Updated dependencies [be60815] -- Updated dependencies [37f6844] -- Updated dependencies [93de4f6] -- Updated dependencies [2b50261] -- Updated dependencies [384f30d] -- Updated dependencies [ac600e5] -- Updated dependencies [97fba31] -- Updated dependencies [232f61a] -- Updated dependencies [d374caf] -- Updated dependencies [5673576] -- Updated dependencies [c1ef923] -- Updated dependencies [911ceaa] -- Updated dependencies [98eab36] -- Updated dependencies [af5e292] -- Updated dependencies [3fbbea1] -- Updated dependencies [7f96b10] -- Updated dependencies [167ec42] -- Updated dependencies [616a2a5] -- Updated dependencies [0046d8f] -- Updated dependencies [f1d4748] -- Updated dependencies [b1119ec] -- Updated dependencies [9f23d2b] -- Updated dependencies [578e025] -- Updated dependencies [af025ee] -- Updated dependencies [598c89a] -- Updated dependencies [4a0bd17] -- Updated dependencies [b8b9af4] -- Updated dependencies [31676be] -- Updated dependencies [8c0d52e] -- Updated dependencies [aff10e2] -- Updated dependencies [70a774b] -- Updated dependencies [9ce096f] -- Updated dependencies [e05db88] -- Updated dependencies [7458a41] -- Updated dependencies [5ffcc14] -- Updated dependencies [d971e51] -- Updated dependencies [97abb24] -- Updated dependencies [deb157a] -- Updated dependencies [d2ce342] -- Updated dependencies [9695da7] -- Updated dependencies [75444e3] -- Updated dependencies [58b8346] -- Updated dependencies [2d0bd16] -- Updated dependencies [dad51e5] -- Updated dependencies [1c9c342] -- Updated dependencies [787c738] -- Updated dependencies [8396656] -- Updated dependencies [dbbd38a] -- Updated dependencies [93fe362] -- Updated dependencies [dfc6975] -- Updated dependencies [3cf4de0] -- Updated dependencies [c9dc811] -- Updated dependencies [144ef9b] -- Updated dependencies [138ab04] -- Updated dependencies [a0b9e91] -- Updated dependencies [99bd015] - - @object-ui/types@17.6.0 - - @object-ui/react@17.6.0 - - @object-ui/components@17.6.0 - - @object-ui/core@17.6.0 - -## 17.5.0 - -### Patch Changes - -- d0c3b26: Every plain ` - {pendingSuggestions.length > 1 && ( - - )} - - - - - - {pendingSuggestions.map((suggestion) => ( -
- -
-
- {suggestion.fieldName} - → - - {typeof suggestion.value === 'object' - ? JSON.stringify(suggestion.value) - : String(suggestion.value)} - -
- {showConfidence && ( -
- {t('ai.formAssist.confidence', { percent: formatPercent(suggestion.confidence, displayLocale) })} -
- )} - {showReasoning && suggestion.reasoning && ( -
- {suggestion.reasoning} -
- )} -
-
- - -
-
- ))} - - {/* Applied suggestions feedback */} - {appliedFields.size > 0 && ( -
- - {t('ai.formAssist.appliedCount', { count: appliedFields.size })} -
- )} -
- - ); -}; diff --git a/ui/packages/plugin-ai/src/AIRecommendations.layoutCarouselRetired-10330.test.tsx b/ui/packages/plugin-ai/src/AIRecommendations.layoutCarouselRetired-10330.test.tsx deleted file mode 100644 index 470a2c94562..00000000000 --- a/ui/packages/plugin-ai/src/AIRecommendations.layoutCarouselRetired-10330.test.tsx +++ /dev/null @@ -1,111 +0,0 @@ -/** - * ObjectUI - * Copyright (c) 2024-present ObjectStack Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -/** - * `carousel` is retired from `AIRecommendationsSchema.layout` and from the - * `ai-recommendations` designer enum (objectui#10330, ADR-0049 - * enforce-or-remove). - * - * The value was declared on the type and offered in the designer, but - * `AIRecommendations` renders only `grid` specially: an author who picked - * `carousel` stored a value the renderer never honoured and got a list with no - * diagnostic. It was never implemented, so it is removed rather than enforced. - * - * Two pins: - * - * 1. The designer enum and the type union AGREE, in both directions. The - * union side is checked by the compiler (`tsconfig.test.json` compiles this - * file), the enum side at runtime against the live registration. A value - * added to one and not the other turns this red. - * 2. A node that still STORES `layout: 'carousel'` does not crash: it falls - * through to the list layout, the same as before the retirement. `grid` is - * the control that the instrument can tell the two layouts apart. - */ - -import { afterEach, describe, expect, it } from 'vitest'; -import { render, screen, cleanup } from '@testing-library/react'; -import { ComponentRegistry } from '@object-ui/core'; -import type { AIRecommendationItem, AIRecommendationsSchema } from '@object-ui/types'; -import { AIRecommendations } from './AIRecommendations'; -import './index'; - -type Equal = - (() => T extends X ? 1 : 2) extends (() => T extends Y ? 1 : 2) ? true : false; -type Expect = T; - -type Layout = NonNullable; - -/** - * Every value of the `layout` union, spelled once. `satisfies` refuses a value - * the union does not declare; the `Equal` below refuses a union member missing - * from this list. Together they make this tuple the union, checked by `tsc`. - */ -const LAYOUTS = ['list', 'grid'] as const satisfies readonly Layout[]; - -export type assertionLayoutsTupleIsTheWholeUnion = [ - Expect>, -]; - -const layoutEnumValues = (): unknown[] => { - const input = ComponentRegistry.getMeta('ai-recommendations')?.inputs?.find( - (entry) => entry.name === 'layout', - ); - return (input?.enum ?? []).map((option) => - typeof option === 'object' && option !== null ? (option as { value: unknown }).value : option, - ); -}; - -const items: AIRecommendationItem[] = [ - { id: 'rec-1', title: 'Recommendation 1', score: 0.9 }, - { id: 'rec-2', title: 'Recommendation 2', score: 0.5 }, - { id: 'rec-3', title: 'Recommendation 3', score: 0.2 }, -]; - -afterEach(() => cleanup()); - -describe('the `ai-recommendations` designer enum agrees with `AIRecommendationsSchema.layout`', () => { - it('offers exactly the values the type union declares', () => { - const offered = layoutEnumValues(); - // Lit control first: an empty enum would satisfy the absence check below - // for the wrong reason. - expect(offered.length, 'the `layout` input has no enum at all').toBeGreaterThan(0); - expect([...offered].sort()).toEqual([...LAYOUTS].sort()); - }); - - it('no longer offers `carousel`', () => { - expect(layoutEnumValues()).not.toContain('carousel'); - }); -}); - -describe('a stored `layout: \'carousel\'` still renders', () => { - const renderWith = (layout: unknown) => { - // `as never`: the value is outside the union now, which is the point — this - // is metadata stored before the retirement, not something a caller can type. - const schema = { type: 'ai-recommendations', recommendations: items, layout } as never; - return render(); - }; - - it('does not crash and falls through to the list layout', () => { - const { container } = renderWith('carousel'); - expect(screen.getAllByText(/^Recommendation \d$/)).toHaveLength(items.length); - expect(container.querySelector('.grid'), 'carousel rendered the grid layout').toBeNull(); - }); - - it('renders the same DOM as an explicit `list`', () => { - const carousel = renderWith('carousel').container.innerHTML; - cleanup(); - const list = renderWith('list').container.innerHTML; - expect(carousel).toBe(list); - }); - - it('CONTROL — the same instrument sees the grid layout when it is asked for', () => { - const { container } = renderWith('grid'); - expect(screen.getAllByText(/^Recommendation \d$/)).toHaveLength(items.length); - expect(container.querySelector('.grid')).not.toBeNull(); - }); -}); diff --git a/ui/packages/plugin-ai/src/AIRecommendations.rendersEveryItem-8178.test.tsx b/ui/packages/plugin-ai/src/AIRecommendations.rendersEveryItem-8178.test.tsx deleted file mode 100644 index 773f09a6a14..00000000000 --- a/ui/packages/plugin-ai/src/AIRecommendations.rendersEveryItem-8178.test.tsx +++ /dev/null @@ -1,99 +0,0 @@ -/** - * ObjectUI - * Copyright (c) 2024-present ObjectStack Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -/** - * `AIRecommendations` renders EVERY item it is handed, in both layouts, and its - * docblock says so (objectui#8178, ADR-0049, director decision batch #78). - * - * ## The promise this replaces - * - * `AIRecommendationsSchema.maxResults` was documented as *"Maximum number of - * results to display"*, offered as a designer input, and read by nothing: a - * node carrying `maxResults: 5` against a fifty-item list rendered fifty rows - * with no diagnostic. The ruling retired the key rather than implementing the - * slice — nothing pulled on it — and required the component to STATE what it - * does instead of promising a cap it never honoured. - * - * Both halves are owed, because either alone rots. This file is the - * behavioural half — fifty items in, fifty rendered. The documentary half (the - * docblock says so, and no cap survives in the code) reads the component source - * off disk, which needs the node project, so it lives in - * `registrationInputs-8178.test.ts`. A contributor who implements a cap breaks - * this file; one who deletes the sentence breaks that one. - * - * The count is fifty on purpose: it is the finding's own example, and it is - * comfortably past any plausible default cap someone might reintroduce. - */ - -import { describe, expect, it } from 'vitest'; -import { render, screen, cleanup } from '@testing-library/react'; -import type { AIRecommendationItem, AIRecommendationsSchema } from '@object-ui/types'; -import { AIRecommendations } from './AIRecommendations'; - -const ITEM_COUNT = 50; - -const items: AIRecommendationItem[] = Array.from({ length: ITEM_COUNT }, (_, i) => ({ - id: `rec-${i + 1}`, - title: `Recommendation ${i + 1}`, - score: 0.5, -})); - -describe.each(['list', 'grid'] as const)( - 'every item reaches the DOM in the `%s` layout', - (layout) => { - it(`renders all ${ITEM_COUNT} titles`, () => { - const schema: AIRecommendationsSchema = { - type: 'ai-recommendations', - recommendations: items, - layout, - }; - render(); - expect(screen.getAllByText(/^Recommendation \d+$/)).toHaveLength(ITEM_COUNT); - // The first and the last, by name: a length check alone would pass on a - // component that rendered the same item fifty times. - expect(screen.getByText('Recommendation 1')).toBeTruthy(); - expect(screen.getByText(`Recommendation ${ITEM_COUNT}`)).toBeTruthy(); - cleanup(); - }); - - it('reports that same count in the header badge', () => { - const schema: AIRecommendationsSchema = { - type: 'ai-recommendations', - recommendations: items, - layout, - }; - render(); - expect(screen.getByText(String(ITEM_COUNT))).toBeTruthy(); - cleanup(); - }); - }, -); - -describe('the control — the instrument counts what is rendered, not a constant', () => { - it('renders three when handed three', () => { - const schema: AIRecommendationsSchema = { - type: 'ai-recommendations', - recommendations: items.slice(0, 3), - }; - render(); - expect(screen.getAllByText(/^Recommendation \d+$/)).toHaveLength(3); - cleanup(); - }); - - it('renders the empty message when handed none', () => { - const schema: AIRecommendationsSchema = { - type: 'ai-recommendations', - recommendations: [], - emptyMessage: 'Nothing yet', - }; - render(); - expect(screen.getByText('Nothing yet')).toBeTruthy(); - expect(screen.queryAllByText(/^Recommendation \d+$/)).toHaveLength(0); - cleanup(); - }); -}); diff --git a/ui/packages/plugin-ai/src/AIRecommendations.tsx b/ui/packages/plugin-ai/src/AIRecommendations.tsx deleted file mode 100644 index f3d3387a7a9..00000000000 --- a/ui/packages/plugin-ai/src/AIRecommendations.tsx +++ /dev/null @@ -1,178 +0,0 @@ -/** - * ObjectUI - * Copyright (c) 2024-present ObjectStack Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; -import { Card, CardContent, CardHeader, CardTitle, Button, Badge } from '@object-ui/components'; -import type { AIRecommendationsSchema, AIRecommendationItem } from '@object-ui/types'; -import { Sparkles, Star, ExternalLink, X, ThumbsUp, ThumbsDown } from 'lucide-react'; -import { useDisplayLocale } from '@object-ui/i18n'; -import { useAiTranslation, formatPercent } from './useAiTranslation'; - -export interface AIRecommendationsProps { - schema: AIRecommendationsSchema; - /** Callback when a recommendation is selected */ - onSelect?: (item: AIRecommendationItem) => void; - /** Callback when a recommendation is dismissed */ - onDismiss?: (item: AIRecommendationItem) => void; -} - -/** - * AIRecommendations - AI-powered recommendation component - * - * Renders EVERY item in `schema.recommendations`, in both the `list` and the - * `grid` layout, and the count badge in the header reports that same length. - * There is no cap: the caller decides how many items to hand over, and slicing - * — if it is wanted — happens before the array reaches this component. - * - * That sentence replaces a promise this component never honoured. - * `AIRecommendationsSchema.maxResults` was declared as *"Maximum number of - * results to display"*, offered in the designer, and read by nothing: a node - * carrying `maxResults: 5` against a 50-item list rendered 50 rows with no - * diagnostic. The key is retired (objectui#8178, ADR-0049, director decision - * batch #78) rather than implemented, because nothing pulled on it; it is a - * `?: never` tombstone on the schema now, and the behaviour stated here is - * pinned by `AIRecommendations.rendersEveryItem-8178.test.tsx`. - */ -export const AIRecommendations: React.FC = ({ schema, onSelect, onDismiss }) => { - const { t } = useAiTranslation(); - const displayLocale = useDisplayLocale(); - const { - recommendations = [], - showScores = false, - layout = 'list', - loading = false, - emptyMessage = t('ai.recommendations.empty'), - } = schema; - - const handleSelect = (item: AIRecommendationItem) => { - onSelect?.(item); - }; - - const handleDismiss = (item: AIRecommendationItem) => { - onDismiss?.(item); - }; - - if (loading) { - return ( - - - -

{t('ai.recommendations.generating')}

-
-
- ); - } - - if (recommendations.length === 0) { - return ( - - - {emptyMessage} - - - ); - } - - const renderScoreBadge = (score: number) => { - if (!showScores) return null; - const variant = score >= 0.7 ? 'default' : score >= 0.4 ? 'secondary' : 'outline'; - return {formatPercent(score, displayLocale)}; - }; - - const renderListItem = (item: AIRecommendationItem) => ( -
handleSelect(item)} - > -
- -
-
-
- {item.title} - {item.category && ( - {item.category} - )} - {renderScoreBadge(item.score)} -
- {item.description && ( -

{item.description}

- )} -
-
- {item.action && ( - - )} - -
-
- ); - - const renderGridItem = (item: AIRecommendationItem) => ( - handleSelect(item)} - > - -
- - {renderScoreBadge(item.score)} -
-

{item.title}

- {item.description && ( -

{item.description}

- )} - {item.category && ( - {item.category} - )} -
- - -
-
-
- ); - - return ( - - - - - {t('ai.recommendations.title')} - {recommendations.length} - - - - {layout === 'grid' ? ( -
- {recommendations.map(renderGridItem)} -
- ) : ( -
- {recommendations.map(renderListItem)} -
- )} -
-
- ); -}; diff --git a/ui/packages/plugin-ai/src/NLQueryInput.tsx b/ui/packages/plugin-ai/src/NLQueryInput.tsx deleted file mode 100644 index 8c5e9704dd0..00000000000 --- a/ui/packages/plugin-ai/src/NLQueryInput.tsx +++ /dev/null @@ -1,216 +0,0 @@ -/** - * ObjectUI - * Copyright (c) 2024-present ObjectStack Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React, { useState } from 'react'; -import { Card, CardContent, CardHeader, CardTitle, Button, Input, Badge } from '@object-ui/components'; -import type { NLQuerySchema, NLQueryResult } from '@object-ui/types'; -import { Search, Sparkles, Clock, ArrowRight, Loader2, Table } from 'lucide-react'; -import { useDisplayLocale } from '@object-ui/i18n'; -import { useAiTranslation, formatPercent } from './useAiTranslation'; - -export interface NLQueryInputProps { - schema: NLQuerySchema; - /** Callback when a query is submitted */ - onSubmit?: (query: string) => void; -} - -/** - * NLQueryInput - Natural language query interface - * Allows users to query data using natural language, with AI-powered parsing. - */ -export const NLQueryInput: React.FC = ({ schema, onSubmit: onSubmitProp }) => { - const { t } = useAiTranslation(); - // Dates and percentages read the DISPLAY locale, never the machine's - // (objectui#10232). - const displayLocale = useDisplayLocale(); - const { - placeholder = t('ai.nlQuery.placeholder'), - result: initialResult, - suggestions = [], - showHistory = false, - history = [], - loading: externalLoading = false, - } = schema; - - const [query, setQuery] = useState(''); - const [result, setResult] = useState(initialResult); - const [loading, setLoading] = useState(false); - - const isLoading = loading || externalLoading; - - const handleSubmit = (q?: string) => { - const queryText = q || query; - if (!queryText.trim()) return; - - setLoading(true); - onSubmitProp?.(queryText); - - // Simulate AI processing when no external handler - if (!onSubmitProp) { - setTimeout(() => { - setResult({ - query: queryText, - summary: t('ai.nlQuery.simulatedSummary', { query: queryText }), - confidence: 0.85, - data: [], - columns: [], - }); - setLoading(false); - }, 1000); - } else { - setLoading(false); - } - }; - - const handleSuggestionClick = (suggestion: string) => { - setQuery(suggestion); - handleSubmit(suggestion); - }; - - return ( -
- {/* Query Input */} - - -
-
- - ) => setQuery(e.target.value)} - onKeyDown={(e: React.KeyboardEvent) => e.key === 'Enter' && handleSubmit()} - placeholder={placeholder} - className="pl-10 pr-20" - disabled={isLoading} - /> -
- -
-
-
- - {/* Suggestions */} - {suggestions.length > 0 && !result && ( -
- {suggestions.map((suggestion: string, idx: number) => ( - - ))} -
- )} -
-
- - {/* Results */} - {result && ( - - -
- - - {t('ai.nlQuery.results')} - - {result.confidence !== undefined && ( - = 0.7 ? 'default' : 'secondary'} className="text-xs"> - {t('ai.nlQuery.match', { percent: formatPercent(result.confidence, displayLocale) })} - - )} - - - - {result.summary && ( -

{result.summary}

- )} - - {/* Result Table */} - {result.data && result.data.length > 0 && result.columns && ( -
-
- - - {result.columns.map((col: { name: string; label?: string; type?: string }, idx: number) => ( - - ))} - - - - {result.data.map((row: Record, rowIdx: number) => ( - - {result.columns!.map((col: { name: string; label?: string; type?: string }, colIdx: number) => ( - - ))} - - ))} - -
- {col.label || col.name} -
- {String(row[col.name] ?? '')} -
-
- )} - - {/* Empty results */} - {(!result.data || result.data.length === 0) && ( -
- {t('ai.nlQuery.noResults')} -
- )} - -
- )} - - {/* Query History */} - {showHistory && history.length > 0 && ( - - - - - {t('ai.nlQuery.recentQueries')} - - - - {history.slice(0, 5).map((item: { query: string; timestamp: string }, idx: number) => ( - - ))} - - - )} -
- ); -}; diff --git a/ui/packages/plugin-ai/src/aiComponents.i18n-10232.test.tsx b/ui/packages/plugin-ai/src/aiComponents.i18n-10232.test.tsx deleted file mode 100644 index 00df3d05506..00000000000 --- a/ui/packages/plugin-ai/src/aiComponents.i18n-10232.test.tsx +++ /dev/null @@ -1,146 +0,0 @@ -/** - * ObjectUI - * Copyright (c) 2024-present ObjectStack Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -/** - * `@object-ui/plugin-ai` speaks the session's language and formats in the - * session's DISPLAY locale (objectui#10232). - * - * Before this card the package had no `@object-ui/i18n` dependency: every - * string was hard-coded English, and `nl-query`'s history date was - * `toLocaleDateString()` with no argument — the MACHINE's locale, which also - * kept the package outside the repo-wide machine-locale census (that census's - * population is read from the manifests, and this manifest named no - * `@object-ui/i18n`). - * - * Three things are pinned here: - * - the history date reads the declared display locale — two sessions that - * differ ONLY in that locale must read differently, which the runner's own - * locale cannot satisfy, and the runtime tripwire sees every locale-taking - * call receive the declared tag; - * - the strings follow the UI language; - * - the provider-less defaults map is byte-identical to the `en` pack, row for - * row, in both directions. - */ - -import * as React from 'react'; -import { describe, it, expect, afterEach } from 'vitest'; -import { render, cleanup, screen } from '@testing-library/react'; -import { I18nProvider, LocalizationProvider, en } from '@object-ui/i18n'; -import { isMachineLocale, recordLocaleArgumentsAsync } from '@object-ui/test-support'; -import type { NLQuerySchema } from '@object-ui/types'; -import { NLQueryInput } from './NLQueryInput'; -import { AIFormAssist } from './AIFormAssist'; -import { AIRecommendations } from './AIRecommendations'; -import { AI_DEFAULT_TRANSLATIONS } from './useAiTranslation'; - -afterEach(cleanup); - -/** 2020-03-04 15:30:00 UTC — the suite runs in UTC. */ -const STORED = '2020-03-04T15:30:00.000Z'; - -const historySchema = { - type: 'nl-query', - showHistory: true, - history: [{ query: 'open deals', timestamp: STORED }], -} as unknown as NLQuerySchema; - -function session(language: string, locale: string | undefined, node: React.ReactNode) { - return render( - - {node} - , - ); -} - -/** The history row's text under UI language `en` and the given display locale. */ -async function readHistory(locale: string): Promise { - session('en', locale, ); - const query = await screen.findByText('open deals'); - const text = query.parentElement?.textContent ?? ''; - cleanup(); - return text; -} - -describe('nl-query — the history date follows the display locale (objectui#10232)', () => { - it('says the de-DE face under a de-DE session', async () => { - const text = await readHistory('de-DE'); - expect(text, `got: ${text}`).toContain('4.3.2020'); - }); - - it('keeps the en face under an en session', async () => { - const text = await readHistory('en'); - expect(text, `got: ${text}`).toContain('3/4/2020'); - }); - - it('is a reading of the session, not of the machine', async () => { - expect(await readHistory('de-DE')).not.toBe(await readHistory('en')); - }); - - it('every locale-taking call receives the declared tag', async () => { - const calls = await recordLocaleArgumentsAsync(async () => { - await readHistory('de-DE'); - }); - expect(calls.some((c) => c.locale === 'de-DE'), `saw: ${JSON.stringify(calls).slice(0, 2000)}`).toBe(true); - expect(calls.filter(isMachineLocale), 'these call sites formatted in the machine locale').toEqual([]); - }); -}); - -describe('plugin-ai strings follow the UI language (objectui#10232)', () => { - it('nl-query renders the de pack', async () => { - session('de', undefined, ); - expect(await screen.findByText('Letzte Abfragen')).toBeTruthy(); - expect(screen.getByText('Fragen')).toBeTruthy(); - expect(screen.getByPlaceholderText('Stellen Sie eine Frage zu Ihren Daten…')).toBeTruthy(); - }); - - it('ai-form-assist picks the singular key at exactly one, and formats its confidence in the display locale', async () => { - session( - 'de', - 'de-DE', - , - ); - expect(await screen.findByText('KI-Vorschläge')).toBeTruthy(); - expect(screen.getByText('1 Vorschlag')).toBeTruthy(); - // de-DE percent: a (narrow no-break) space before the sign. - expect(screen.getByText(/^85\s%\sKonfidenz$/)).toBeTruthy(); - }); - - it('ai-recommendations renders the de pack for its empty state', async () => { - session('de', undefined, ); - expect(await screen.findByText('Keine Empfehlungen verfügbar')).toBeTruthy(); - }); - - it('with no provider, renders English rather than raw keys', () => { - render(); - expect(screen.getByText('Generating recommendations…')).toBeTruthy(); - }); -}); - -describe('AI_DEFAULT_TRANSLATIONS mirrors the en pack (objectui#10232)', () => { - const at = (dotted: string): unknown => - dotted.split('.').reduce((n, p) => (n as Record | undefined)?.[p], en); - - it('every row is byte-identical to its en pack value', () => { - for (const [key, value] of Object.entries(AI_DEFAULT_TRANSLATIONS)) { - expect(at(key), key).toBe(value); - } - }); - - it('every en `ai.*` key has a row', () => { - const keys: string[] = []; - const walk = (node: unknown, prefix: string) => { - if (typeof node === 'string') keys.push(prefix); - else for (const [k, v] of Object.entries(node as Record)) walk(v, `${prefix}.${k}`); - }; - walk((en as Record).ai, 'ai'); - expect(keys.length).toBeGreaterThan(0); - expect(keys.filter((k) => !(k in AI_DEFAULT_TRANSLATIONS))).toEqual([]); - }); -}); diff --git a/ui/packages/plugin-ai/src/index.tsx b/ui/packages/plugin-ai/src/index.tsx deleted file mode 100644 index 17ce89feca0..00000000000 --- a/ui/packages/plugin-ai/src/index.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/** - * ObjectUI - * Copyright (c) 2024-present ObjectStack Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -/** - * The three AI components' registrations. - * - * Every `inputs` entry below names a key the component beneath it actually - * reads. Seven entries used to sit here that no renderer read — `formId`, - * `objectName`, `fields`, `autoFill` on `ai-form-assist`, `objectName` and - * `maxResults` on `ai-recommendations`, `objectName` on `nl-query` — so the - * designer offered them from a list, the JSON validated, and the runtime - * dropped them silently. They are retired (objectui#8178, ADR-0049, director - * decision batch #78, 2026-09-07) and are `?: never` tombstones on their - * schemas; their absence from these lists is pinned by - * `registrationInputs-8178.test.ts`. - */ - -import { ComponentRegistry } from '@object-ui/core'; -import { AIFormAssist } from './AIFormAssist'; -import { AIRecommendations } from './AIRecommendations'; -import { NLQueryInput } from './NLQueryInput'; - -export { AIFormAssist, AIRecommendations, NLQueryInput }; - -// Register AI form assist component -ComponentRegistry.register( - 'ai-form-assist', - AIFormAssist, - { - label: 'AI Form Assist', - category: 'AI', - inputs: [ - { name: 'suggestions', type: 'code' }, - { name: 'showConfidence', type: 'boolean' }, - { name: 'showReasoning', type: 'boolean' }, - ] - } -); - -// Register AI recommendations component -ComponentRegistry.register( - 'ai-recommendations', - AIRecommendations, - { - label: 'AI Recommendations', - category: 'AI', - inputs: [ - { name: 'recommendations', type: 'code' }, - { name: 'showScores', type: 'boolean' }, - { name: 'layout', type: 'enum', enum: [ - { label: 'List', value: 'list' }, - { label: 'Grid', value: 'grid' }, - ] }, - { name: 'emptyMessage', type: 'string' }, - ] - } -); - -// Register NL Query component -ComponentRegistry.register( - 'nl-query', - NLQueryInput, - { - label: 'Natural Language Query', - category: 'AI', - inputs: [ - { name: 'placeholder', type: 'string' }, - { name: 'suggestions', type: 'array' }, - { name: 'showHistory', type: 'boolean' }, - ] - } -); diff --git a/ui/packages/plugin-ai/src/registrationInputs-8178.test.ts b/ui/packages/plugin-ai/src/registrationInputs-8178.test.ts deleted file mode 100644 index 629617101e3..00000000000 --- a/ui/packages/plugin-ai/src/registrationInputs-8178.test.ts +++ /dev/null @@ -1,171 +0,0 @@ -/** - * ObjectUI - * Copyright (c) 2024-present ObjectStack Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -/** - * Every designer input the three AI registrations advertise names a key the - * component beneath it actually reads (objectui#8178, ADR-0049, director - * decision batch #78, 2026-09-07). - * - * ## What this closes - * - * The finding's sharpest half was not the declaration — it was the PUSH. Seven - * `inputs` entries across the three registrations (five distinct names: - * `formId`, `objectName`, `fields`, `autoFill`, `maxResults`) offered keys the - * renderers never read, so the field designer put them in a list for an author - * to pick, the JSON validated, and the runtime dropped the value in silence. An - * author who chose `maxResults: 5` against a fifty-item list got fifty rows and - * no diagnostic. - * - * ## Two assertions, and why the second one exists - * - * The absence pin alone would go green if the whole registration disappeared, - * and it says nothing about the NEXT key someone adds. So the second block - * derives the property the retirement was really about — a declared input names - * a key its component reads — from the component sources themselves, with a - * name no component reads as the control that the derivation is a real reading. - */ - -import { describe, expect, it } from 'vitest'; -import { readFileSync } from 'node:fs'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { ComponentRegistry } from '@object-ui/core'; -import './index'; -// @ts-expect-error — plain-JS shared helper, intentionally untyped (`allowJs: false`) -import { maskComments } from '../../../scripts/js-comment-mask.mjs'; - -/** Local annotation, since the import above is untyped — the call site stays checked. */ -const mask: (source: string) => string = maskComments; - -/** - * This file is `.test.ts` (the node project) rather than `.test.tsx`, on - * purpose: it reads sibling sources off disk, and under the happy-dom project - * `import.meta.url` is not a `file:` URL — `new URL('./x', import.meta.url)` - * resolves against the document base and the read fails. The behavioural half - * of this card's third pin, which needs a DOM, is - * `AIRecommendations.rendersEveryItem-8178.test.tsx`. - */ -const HERE = dirname(fileURLToPath(import.meta.url)); - -/** The seven entries this card removed, by registration. */ -const RETIRED: Record = { - 'ai-form-assist': ['formId', 'objectName', 'fields', 'autoFill'], - 'ai-recommendations': ['objectName', 'maxResults'], - 'nl-query': ['objectName'], -}; - -/** The component each registration renders, for the read census below. */ -const SOURCE: Record = { - 'ai-form-assist': 'AIFormAssist.tsx', - 'ai-recommendations': 'AIRecommendations.tsx', - 'nl-query': 'NLQueryInput.tsx', -}; - -const inputNamesOf = (type: string): string[] => - (ComponentRegistry.getMeta(type)?.inputs ?? []).map((input) => input.name); - -/** - * The component's CODE, with comments stripped. - * - * Load-bearing: each of these files now names the retired keys in its docblock, - * on purpose — that is where the retirement is explained to the next reader. - * A census over raw source would read those sentences as reads and report the - * exact opposite of the truth. - */ -const readSource = (file: string): string => readFileSync(join(HERE, file), 'utf8'); - -const stripComments = (src: string): string => - mask(src); - -const codeOf = (type: string): string => stripComments(readSource(SOURCE[type])); - -describe.each(Object.keys(RETIRED))('`%s` no longer advertises its retired keys', (type) => { - it('offers none of them to the designer', () => { - const names = inputNamesOf(type); - // Lit control FIRST: an empty list would satisfy the absence check below - // for the wrong reason — a registration that failed to load reads as clean. - expect(names.length, `\`${type}\` declares no inputs at all — the reading is vacuous`) - .toBeGreaterThan(0); - expect(names).toEqual(expect.not.arrayContaining([...RETIRED[type]])); - }); - - it('still offers exactly the keys its component reads — the census, not a snapshot', () => { - const src = codeOf(type); - const undeclared = inputNamesOf(type).filter((name) => !new RegExp(`\\b${name}\\b`).test(src)); - expect(undeclared, `advertised to the designer but unread by ${SOURCE[type]}`).toEqual([]); - }); - - it('CONTROL — the same instrument reports an unread name as unread', () => { - // Without this, the census above would pass on a matcher that matches - // everything (a source that failed to read, a stripper that ate the code). - const src = codeOf(type); - expect(new RegExp('\\bbogusUnreadKey\\b').test(src)).toBe(false); - // …and it still sees the code: a key the component DOES read is found. - const live = inputNamesOf(type)[0]; - expect(new RegExp(`\\b${live}\\b`).test(src), `the stripped code lost \`${live}\``).toBe(true); - for (const retired of RETIRED[type]) { - // …and every key this card retired is genuinely absent from the source - // too, `autoFill`'s dead destructure included. This is the read half of - // the retirement, measured rather than assumed. - expect(new RegExp(`\\b${retired}\\b`).test(src), `${retired} still appears in ${SOURCE[type]}`) - .toBe(false); - } - }); -}); - -describe('the registrations themselves are intact', () => { - it('registers all three components with their labels', () => { - expect(Object.keys(RETIRED).map((type) => ComponentRegistry.getMeta(type)?.label)).toEqual([ - 'AI Form Assist', - 'AI Recommendations', - 'Natural Language Query', - ]); - }); - - it('keeps the live inputs each designer needs', () => { - expect(inputNamesOf('ai-form-assist')).toEqual([ - 'suggestions', - 'showConfidence', - 'showReasoning', - ]); - expect(inputNamesOf('ai-recommendations')).toEqual([ - 'recommendations', - 'showScores', - 'layout', - 'emptyMessage', - ]); - expect(inputNamesOf('nl-query')).toEqual(['placeholder', 'suggestions', 'showHistory']); - }); -}); - -describe('`AIRecommendations` states what it does instead of promising a cap', () => { - // The behavioural half — fifty items in, fifty rendered, both layouts — is - // pinned next door in `AIRecommendations.rendersEveryItem-8178.test.tsx`. - // This half is the sentence, and both are owed: a contributor who implements - // a cap breaks that file, one who deletes the sentence breaks this one. - const source = readSource('AIRecommendations.tsx'); - const docblock = - /\/\*\*[\s\S]*?\*\/\s*export const AIRecommendations/.exec(source)?.[0] ?? ''; - - it('found the component docblock — the lit control for the reads below', () => { - expect(docblock).toContain('AIRecommendations - AI-powered recommendation component'); - }); - - it('says every item is rendered, and that there is no cap', () => { - expect(docblock).toContain('Renders EVERY item'); - expect(docblock).toContain('There is no cap'); - }); - - it('carries no surviving cap in the code', () => { - const code = stripComments(source); - expect(/\bmaxResults\b/.test(code)).toBe(false); - expect(/\.slice\(/.test(code)).toBe(false); - // Lit control: the stripper left the code intact. - expect(/\brecommendations\b/.test(code)).toBe(true); - }); -}); diff --git a/ui/packages/plugin-ai/src/useAiTranslation.ts b/ui/packages/plugin-ai/src/useAiTranslation.ts deleted file mode 100644 index 4049809f620..00000000000 --- a/ui/packages/plugin-ai/src/useAiTranslation.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * ObjectUI - * Copyright (c) 2024-present ObjectStack Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import { createSafeTranslation } from '@object-ui/i18n'; - -/** - * Locale wiring for the three AI components — objectui#10232. - * - * The package used to have no `@object-ui/i18n` dependency at all: every string - * was hard-coded English and the `nl-query` history date was formatted in the - * MACHINE's locale. Wiring it as a whole — strings and display locale together — - * also puts the package inside the repo-wide machine-locale census - * (`packages/i18n/src/__tests__/machineLocaleCensus-9909.test.ts`), whose - * population is every package whose manifest names `@object-ui/i18n`. - * - * A module of its own, the shape `plugin-chatbot`'s `useAiApprovalsTranslation.ts` - * uses: the defaults map has to be importable by the test that compares it - * against the `en` pack, and exporting a non-component from a component file - * trips `react-refresh/only-export-components`. It is not exported from the - * package barrel, so it stays off the published API surface. - * - * English fallbacks for every key the components read. `createSafeTranslation` - * serves these when no `I18nProvider` is mounted, so a provider-less render - * shows English rather than raw keys. Every row is byte-identical to its `en` - * pack value — `aiComponents.i18n-10232.test.tsx` holds it there. - * - * The count rows are i18next count families (objectui#11445): the components - * pass `count`, and the provider-less path reads the `_one` / `_other` row for - * a numeric count, in the order i18next reads the `en` pack. - */ -export const AI_DEFAULT_TRANSLATIONS: Record = { - 'ai.nlQuery.placeholder': 'Ask a question about your data…', - 'ai.nlQuery.ask': 'Ask', - 'ai.nlQuery.results': 'Results', - 'ai.nlQuery.match': '{{percent}} match', - 'ai.nlQuery.simulatedSummary': 'Results for: {{query}}', - 'ai.nlQuery.noResults': 'No matching records found', - 'ai.nlQuery.recentQueries': 'Recent Queries', - 'ai.formAssist.title': 'AI Suggestions', - 'ai.formAssist.suggestionCount': '{{count}} suggestions', - 'ai.formAssist.suggestionCount_one': '{{count}} suggestion', - 'ai.formAssist.suggestionCount_other': '{{count}} suggestions', - 'ai.formAssist.applyAll': 'Apply All', - 'ai.formAssist.confidence': '{{percent}} confidence', - 'ai.formAssist.appliedCount': '{{count}} suggestions applied', - 'ai.formAssist.appliedCount_one': '{{count}} suggestion applied', - 'ai.formAssist.appliedCount_other': '{{count}} suggestions applied', - 'ai.recommendations.title': 'Recommendations', - 'ai.recommendations.generating': 'Generating recommendations…', - 'ai.recommendations.empty': 'No recommendations available', -}; - -/** - * Safe wrapper for `useObjectTranslation` that falls back to the map above when - * no `I18nProvider` is mounted — the factory form, because the components - * interpolate `{{count}}`, `{{percent}}` and `{{query}}`. - */ -export const useAiTranslation = createSafeTranslation(AI_DEFAULT_TRANSLATIONS, 'ai.nlQuery.ask'); - -/** - * A 0–1 score as a whole percentage in the DISPLAY locale (`useDisplayLocale()`), - * so `de` reads `85 %` rather than `85%` — the number face the - * `{{percent}}` holes above receive, already formatted. - */ -export function formatPercent(score: number, locale: string): string { - return new Intl.NumberFormat(locale, { style: 'percent', maximumFractionDigits: 0 }).format(score); -} diff --git a/ui/packages/plugin-ai/tsconfig.json b/ui/packages/plugin-ai/tsconfig.json deleted file mode 100644 index 7140092e858..00000000000 --- a/ui/packages/plugin-ai/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "baseUrl": ".", - // Overriding `paths` drops the root config's `@object-ui/* -> packages/*/src` - // mappings, so workspace deps resolve through their built `.d.ts` instead of - // pulling sibling sources into this program (which would be outside rootDir). - // vite.config.ts already does the same for the dts program. - "paths": { - "@/*": ["src/*"] - } - }, - "include": ["src"], - // Tooling is excluded by DIRECTORY, not just by file NAME. A helper under - // `__tests__/` that is not itself called `*.test.ts` is otherwise a program - // input, and an EMITTING program writes it into the published `dist` - // (objectui#4006 / #4836 / #6943 -- the same defect three times, each found - // by hand). Enforced repo-wide by `pnpm check:published-tsconfig-exclude` - // (objectui#7212), which derives these directory names from `TOOLING_FILE` - // in scripts/check-phantom-dependencies.mjs. - "exclude": [ - "node_modules", - "dist", - "**/__tests__/**", - "**/__mocks__/**", - "**/__benchmarks__/**", - "**/*.test.ts", - "**/*.test.tsx" - ] -} diff --git a/ui/packages/plugin-ai/tsconfig.test.json b/ui/packages/plugin-ai/tsconfig.test.json deleted file mode 100644 index 8c1fd50f15a..00000000000 --- a/ui/packages/plugin-ai/tsconfig.test.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - // Type-checks this package's TESTS, which `tsconfig.json` excludes. - // See `packages/types/tsconfig.test.json` for why that exclusion is a hole on - // its own: the build correctly keeps tests out of `dist`, but nothing else - // compiled them, so a test could assert a contract the compiler never checked - // and then read as evidence that the contract holds (objectui#3009, #2911). - // - // That hole was live here. `packages/plugin-ai/src` contained no test file at - // all, so this package's test coverage was vacuously complete; objectui#8178 - // added the first two, and without this project `node - // scripts/check-type-check-coverage.mjs` exits 1 naming both of them. - "extends": "../../tsconfig.json", - "compilerOptions": { - // objectui#8691: test programs only. One notch above the package build's - // ES2020, mirroring packages/app-shell. The package SOURCE tsconfig is - // untouched, so what this package SHIPS still compiles at the ES2020 - // baseline; nothing here is emitted. - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "noEmit": true, - // `registrationInputs-8178.test.ts` reads the three component sources off - // disk (`node:fs` / `node:path` / `node:url`) to derive the keys each - // renderer really reads, instead of trusting a list a re-key could hold - // constant. Naming `types` at all switches off automatic `@types/*` - // inclusion -- which is why `@testing-library/jest-dom` is NOT named - // alongside `node` the way `plugin-kanban` names it: neither test file here - // uses a jest-dom matcher, and declaring one this package does not depend on - // is the objectui#8714 divergence pointing the other way. `@types/react` - // still reaches the `.tsx` test through ordinary module resolution - // (`react/jsx-runtime`, under the root config's `"jsx": "react-jsx"`). - "types": ["node"], - // Drop the root tsconfig's source-tree `paths` so `@object-ui/types` and - // `@object-ui/core` resolve through the workspace dependency's built - // `.d.ts` instead of pulling sibling sources in as program inputs (TS6059). - "paths": {} - // No `composite: false` override, unlike 32 of the 36 siblings that carry - // one: it is inert in all of them. These projects extend the ROOT config, - // which sets no `composite`, so a package BUILD config's value is never - // inherited -- `packages/types` has `"composite": true` in its build and - // omits the override with no effect. Here it is moot twice over, since this - // package builds with `vite build` and sets no `composite` either. The - // other non-composite builds (`layout`, `create-plugin`, - // `vscode-extension`) likewise omit it. - }, - "include": ["src/**/*.test.ts", "src/**/*.test.tsx"] -} diff --git a/ui/packages/plugin-ai/vite.config.ts b/ui/packages/plugin-ai/vite.config.ts deleted file mode 100644 index 73226efa2c8..00000000000 --- a/ui/packages/plugin-ai/vite.config.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { defineConfig } from 'vite'; -import react from '@vitejs/plugin-react'; -import dts from 'vite-plugin-dts'; -import { resolve } from 'path'; - -import { createDtsExplicitExtensions } from '../../scripts/vite-dts-explicit-extensions.ts'; -import { createDtsFailOnTypeErrors } from '../../scripts/vite-dts-fail-on-type-errors.ts'; - -// objectui#3240 — with the per-package `vitest.config.*` files gone, Vitest -// launched from THIS directory falls back to this file and uses it as its test -// config. Guard that entry point (route 4 in -// scripts/vitest-invocation-guard.mjs, which carries the mechanism and the -// measurement). Gated on `VITEST` because the same file is the BUILD config: -// Vitest sets that variable when it loads a config, `vite build` does not, so -// a test run is refused and a build never is. -import { - assertCanonicalVitestInvocation, - repoRootFrom, -} from '../../scripts/vitest-invocation-guard.mjs'; - -if (process.env.VITEST) { - assertCanonicalVitestInvocation({ repoRoot: repoRootFrom(import.meta.url) }); -} - -export default defineConfig({ - plugins: [ - react(), - dts({ - insertTypesEntry: true, - // Clear the inherited tsconfig `paths` so the dts type program resolves - // `@object-ui/*` to each dependency's published `dist/*.d.ts` (external) - // instead of following the workspace `src` aliases into files outside - // this package's `rootDir` — which would emit TS6059 rootDir errors. - compilerOptions: { rootDir: resolve(__dirname, 'src'), paths: {} }, - aliasesExclude: [/^@object-ui\//], - include: ['src'], - exclude: ['**/*.test.ts', '**/*.test.tsx', 'node_modules'], - // Relative specifiers in the EMITTED typings get their explicit extension - // here — objectui#5365 / #5439. A NAMED re-export through an extensionless - // hop still declares the name under `nodenext` and silently types it `any`. - ...createDtsExplicitExtensions({ packageDir: __dirname }), - // A type error the declaration program already found and printed used to - // leave `vite build` exiting 0 — objectui#5370 / #5483. This makes it fatal. - ...createDtsFailOnTypeErrors({ packageDir: __dirname }), - }), - ], - resolve: { - alias: { - '@': resolve(__dirname, './src'), - '@object-ui/core': resolve(__dirname, '../core/src'), - '@object-ui/types': resolve(__dirname, '../types/src'), - '@object-ui/react': resolve(__dirname, '../react/src'), - '@object-ui/components': resolve(__dirname, '../components/src'), - '@object-ui/fields': resolve(__dirname, '../fields/src'), - }, - }, - build: { - lib: { - entry: resolve(__dirname, 'src/index.tsx'), - name: 'ObjectUIPluginAI', - fileName: 'index', - }, - rollupOptions: { - external: (id) => !/^[./]/.test(id) && !id.startsWith(__dirname), - output: { - inlineDynamicImports: true, - globals: { - react: 'React', - 'react-dom': 'ReactDOM', - '@object-ui/components': 'ObjectUIComponents', - '@object-ui/core': 'ObjectUICore', - '@object-ui/react': 'ObjectUIReact', - '@object-ui/types': 'ObjectUITypes', - }, - }, - }, - }, -}); diff --git a/ui/vitest.config.mts b/ui/vitest.config.mts index a302b05b0c8..91a4f728ba9 100644 --- a/ui/vitest.config.mts +++ b/ui/vitest.config.mts @@ -537,7 +537,6 @@ export default defineConfig({ '@object-ui/plugin-timeline': path.resolve(__dirname, './packages/plugin-timeline/src'), '@object-ui/plugin-view': path.resolve(__dirname, './packages/plugin-view/src'), '@object-ui/plugin-report': path.resolve(__dirname, './packages/plugin-report/src'), - '@object-ui/plugin-ai': path.resolve(__dirname, './packages/plugin-ai/src'), '@object-ui/plugin-designer': path.resolve(__dirname, './packages/plugin-designer/src'), '@object-ui/runner': path.resolve(__dirname, './packages/runner/src'), '@object-ui/auth': path.resolve(__dirname, './packages/auth/src'), From 133f219b960973e18dfe51584f7bdd14389cffa5 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 22:19:17 +0000 Subject: [PATCH 02/13] chore(i18n): delete the ai.* block plugin-ai owned in all ten packs (WIP) Claude-Session: https://claude.ai/code/session_01VBDBR9oS1MNUcjBVXds5D6 Co-authored-by: Claude --- ui/packages/i18n/src/locales/ar.ts | 35 ------------------------------ ui/packages/i18n/src/locales/de.ts | 27 ----------------------- ui/packages/i18n/src/locales/en.ts | 31 -------------------------- ui/packages/i18n/src/locales/es.ts | 29 ------------------------- ui/packages/i18n/src/locales/fr.ts | 29 ------------------------- ui/packages/i18n/src/locales/ja.ts | 27 ----------------------- ui/packages/i18n/src/locales/ko.ts | 27 ----------------------- ui/packages/i18n/src/locales/pt.ts | 29 ------------------------- ui/packages/i18n/src/locales/ru.ts | 31 -------------------------- ui/packages/i18n/src/locales/zh.ts | 27 ----------------------- 10 files changed, 292 deletions(-) diff --git a/ui/packages/i18n/src/locales/ar.ts b/ui/packages/i18n/src/locales/ar.ts index a55382e4b64..5a8617ee558 100644 --- a/ui/packages/i18n/src/locales/ar.ts +++ b/ui/packages/i18n/src/locales/ar.ts @@ -3745,41 +3745,6 @@ const ar = { cloudWorkspaceTimezoneNotice: { seeded: "تم تعيين المنطقة الزمنية للبيئة إلى {{zone}} وفقًا لمتصفحك عند إنشائها. يمكنك تغييرها من الإعدادات → التوطين.", }, - ai: { - nlQuery: { - placeholder: "اطرح سؤالًا حول بياناتك…", - ask: "اسأل", - results: "النتائج", - match: "نسبة التطابق: {{percent}}", - simulatedSummary: "نتائج: {{query}}", - noResults: "لم يتم العثور على سجلات مطابقة", - recentQueries: "الاستعلامات الأخيرة", - }, - formAssist: { - title: "اقتراحات الذكاء الاصطناعي", - suggestionCount: "عدد الاقتراحات: {{count}}", - suggestionCount_zero: "{{count}} اقتراح", - suggestionCount_one: "{{count}} اقتراح", - suggestionCount_two: "اقتراحان ({{count}})", - suggestionCount_few: "{{count}} اقتراحات", - suggestionCount_many: "{{count}} اقتراحًا", - suggestionCount_other: "{{count}} اقتراح", - applyAll: "تطبيق الكل", - confidence: "الثقة: {{percent}}", - appliedCount: "عدد الاقتراحات المطبّقة: {{count}}", - appliedCount_zero: "تم تطبيق {{count}} اقتراح", - appliedCount_one: "تم تطبيق {{count}} اقتراح", - appliedCount_two: "تم تطبيق اقتراحين ({{count}})", - appliedCount_few: "تم تطبيق {{count}} اقتراحات", - appliedCount_many: "تم تطبيق {{count}} اقتراحًا", - appliedCount_other: "تم تطبيق {{count}} اقتراح", - }, - recommendations: { - title: "التوصيات", - generating: "جارٍ إنشاء التوصيات…", - empty: "لا توجد توصيات متاحة", - }, - }, aiApprovals: { title: "موافقات الذكاء الاصطناعي", description: "إجراءات اقترحها وكيل ذكاء اصطناعي وتحتاج إلى مراجعة بشرية قبل تنفيذها.", diff --git a/ui/packages/i18n/src/locales/de.ts b/ui/packages/i18n/src/locales/de.ts index 383f8ec23bd..d22d600ddec 100644 --- a/ui/packages/i18n/src/locales/de.ts +++ b/ui/packages/i18n/src/locales/de.ts @@ -3488,33 +3488,6 @@ const de = { cloudWorkspaceTimezoneNotice: { seeded: "Die Zeitzone der Umgebung wurde beim Erstellen anhand Ihres Browsers auf {{zone}} festgelegt. Sie können sie unter Einstellungen → Lokalisierung ändern.", }, - ai: { - nlQuery: { - placeholder: "Stellen Sie eine Frage zu Ihren Daten…", - ask: "Fragen", - results: "Ergebnisse", - match: "{{percent}} Übereinstimmung", - simulatedSummary: "Ergebnisse für: {{query}}", - noResults: "Keine passenden Datensätze gefunden", - recentQueries: "Letzte Abfragen", - }, - formAssist: { - title: "KI-Vorschläge", - suggestionCount: "{{count}} Vorschläge", - suggestionCount_one: "{{count}} Vorschlag", - suggestionCount_other: "{{count}} Vorschläge", - applyAll: "Alle übernehmen", - confidence: "{{percent}} Konfidenz", - appliedCount: "{{count}} Vorschläge übernommen", - appliedCount_one: "{{count}} Vorschlag übernommen", - appliedCount_other: "{{count}} Vorschläge übernommen", - }, - recommendations: { - title: "Empfehlungen", - generating: "Empfehlungen werden generiert…", - empty: "Keine Empfehlungen verfügbar", - }, - }, aiApprovals: { title: "KI-Genehmigungen", description: "Von einem KI-Agenten vorgeschlagene Aktionen, die vor der Ausführung eine menschliche Prüfung benötigen.", diff --git a/ui/packages/i18n/src/locales/en.ts b/ui/packages/i18n/src/locales/en.ts index b439ef3d0af..1436704fceb 100644 --- a/ui/packages/i18n/src/locales/en.ts +++ b/ui/packages/i18n/src/locales/en.ts @@ -4218,37 +4218,6 @@ const en = { cloudWorkspaceTimezoneNotice: { seeded: 'The environment timezone was set to {{zone}} from your browser when the environment was created. You can change it in Settings → Localization.', }, - // `@object-ui/plugin-ai` — the `nl-query`, `ai-form-assist` and - // `ai-recommendations` components (objectui#10232). The two count labels are - // i18next count families (objectui#11445): the component passes `count` and - // each pack carries every CLDR category its language selects. - ai: { - nlQuery: { - placeholder: 'Ask a question about your data…', - ask: 'Ask', - results: 'Results', - match: '{{percent}} match', - simulatedSummary: 'Results for: {{query}}', - noResults: 'No matching records found', - recentQueries: 'Recent Queries', - }, - formAssist: { - title: 'AI Suggestions', - suggestionCount: '{{count}} suggestions', - suggestionCount_one: '{{count}} suggestion', - suggestionCount_other: '{{count}} suggestions', - applyAll: 'Apply All', - confidence: '{{percent}} confidence', - appliedCount: '{{count}} suggestions applied', - appliedCount_one: '{{count}} suggestion applied', - appliedCount_other: '{{count}} suggestions applied', - }, - recommendations: { - title: 'Recommendations', - generating: 'Generating recommendations…', - empty: 'No recommendations available', - }, - }, // The AI HITL approval inbox (`@object-ui/plugin-chatbot`'s // `AiPendingActionsInbox`) — objectui#7173. Its four relative-time phrases // are NOT here: it borrows `detail.justNow` / `minutesAgo` / `hoursAgo` / diff --git a/ui/packages/i18n/src/locales/es.ts b/ui/packages/i18n/src/locales/es.ts index 64b07d1885f..55b072ed4e8 100644 --- a/ui/packages/i18n/src/locales/es.ts +++ b/ui/packages/i18n/src/locales/es.ts @@ -3553,35 +3553,6 @@ const es = { cloudWorkspaceTimezoneNotice: { seeded: "La zona horaria del entorno se configuró en {{zone}} a partir de su navegador al crearlo. Puede cambiarla en Configuración → Localización.", }, - ai: { - nlQuery: { - placeholder: "Haga una pregunta sobre sus datos…", - ask: "Preguntar", - results: "Resultados", - match: "Coincidencia: {{percent}}", - simulatedSummary: "Resultados de: {{query}}", - noResults: "No se encontraron registros coincidentes", - recentQueries: "Consultas recientes", - }, - formAssist: { - title: "Sugerencias de IA", - suggestionCount: "{{count}} sugerencias", - suggestionCount_one: "{{count}} sugerencia", - suggestionCount_many: "{{count}} de sugerencias", - suggestionCount_other: "{{count}} sugerencias", - applyAll: "Aplicar todo", - confidence: "Confianza: {{percent}}", - appliedCount: "{{count}} sugerencias aplicadas", - appliedCount_one: "{{count}} sugerencia aplicada", - appliedCount_many: "{{count}} de sugerencias aplicadas", - appliedCount_other: "{{count}} sugerencias aplicadas", - }, - recommendations: { - title: "Recomendaciones", - generating: "Generando recomendaciones…", - empty: "No hay recomendaciones disponibles", - }, - }, aiApprovals: { title: "Aprobaciones de IA", description: "Acciones propuestas por un agente de IA que necesitan revisión humana antes de ejecutarse.", diff --git a/ui/packages/i18n/src/locales/fr.ts b/ui/packages/i18n/src/locales/fr.ts index 95c9cf48b5b..dc2b89caee6 100644 --- a/ui/packages/i18n/src/locales/fr.ts +++ b/ui/packages/i18n/src/locales/fr.ts @@ -3549,35 +3549,6 @@ const fr = { cloudWorkspaceTimezoneNotice: { seeded: "Le fuseau horaire de l’environnement a été défini sur {{zone}} d’après votre navigateur lors de sa création. Vous pouvez le modifier dans Paramètres → Localisation.", }, - ai: { - nlQuery: { - placeholder: "Posez une question sur vos données…", - ask: "Demander", - results: "Résultats", - match: "Correspondance : {{percent}}", - simulatedSummary: "Résultats pour : {{query}}", - noResults: "Aucun enregistrement correspondant", - recentQueries: "Requêtes récentes", - }, - formAssist: { - title: "Suggestions de l’IA", - suggestionCount: "{{count}} suggestions", - suggestionCount_one: "{{count}} suggestion", - suggestionCount_many: "{{count}} de suggestions", - suggestionCount_other: "{{count}} suggestions", - applyAll: "Tout appliquer", - confidence: "Confiance : {{percent}}", - appliedCount: "{{count}} suggestions appliquées", - appliedCount_one: "{{count}} suggestion appliquée", - appliedCount_many: "{{count}} de suggestions appliquées", - appliedCount_other: "{{count}} suggestions appliquées", - }, - recommendations: { - title: "Recommandations", - generating: "Génération des recommandations…", - empty: "Aucune recommandation disponible", - }, - }, aiApprovals: { title: "Approbations IA", description: "Actions proposées par un agent IA qui nécessitent une validation humaine avant exécution.", diff --git a/ui/packages/i18n/src/locales/ja.ts b/ui/packages/i18n/src/locales/ja.ts index a8891a78470..06192d6efb4 100644 --- a/ui/packages/i18n/src/locales/ja.ts +++ b/ui/packages/i18n/src/locales/ja.ts @@ -3490,33 +3490,6 @@ const ja = { cloudWorkspaceTimezoneNotice: { seeded: "環境のタイムゾーンは、作成時にブラウザーに合わせて {{zone}} に設定されました。「設定 → ローカリゼーション」で変更できます。", }, - ai: { - nlQuery: { - placeholder: "データについて質問してください…", - ask: "質問", - results: "結果", - match: "一致度 {{percent}}", - simulatedSummary: "検索結果: {{query}}", - noResults: "一致するレコードが見つかりません", - recentQueries: "最近のクエリ", - }, - formAssist: { - title: "AI の提案", - suggestionCount: "{{count}} 件の提案", - suggestionCount_one: "{{count}} 件の提案", - suggestionCount_other: "{{count}} 件の提案", - applyAll: "すべて適用", - confidence: "信頼度 {{percent}}", - appliedCount: "{{count}} 件の提案を適用しました", - appliedCount_one: "{{count}} 件の提案を適用しました", - appliedCount_other: "{{count}} 件の提案を適用しました", - }, - recommendations: { - title: "おすすめ", - generating: "おすすめを生成しています…", - empty: "おすすめはありません", - }, - }, aiApprovals: { title: "AI 承認", description: "AI エージェントが提案した操作です。実行前に人による確認が必要です。", diff --git a/ui/packages/i18n/src/locales/ko.ts b/ui/packages/i18n/src/locales/ko.ts index 6b547e82c61..e2dbcf44598 100644 --- a/ui/packages/i18n/src/locales/ko.ts +++ b/ui/packages/i18n/src/locales/ko.ts @@ -3487,33 +3487,6 @@ const ko = { cloudWorkspaceTimezoneNotice: { seeded: "환경 시간대는 생성 시 브라우저에 맞춰 {{zone}}(으)로 설정되었습니다. 설정 → 현지화에서 변경할 수 있습니다.", }, - ai: { - nlQuery: { - placeholder: "데이터에 대해 질문하세요…", - ask: "질문", - results: "결과", - match: "일치도 {{percent}}", - simulatedSummary: "검색 결과: {{query}}", - noResults: "일치하는 레코드가 없습니다", - recentQueries: "최근 쿼리", - }, - formAssist: { - title: "AI 제안", - suggestionCount: "제안 {{count}}개", - suggestionCount_one: "제안 {{count}}개", - suggestionCount_other: "제안 {{count}}개", - applyAll: "모두 적용", - confidence: "신뢰도 {{percent}}", - appliedCount: "제안 {{count}}개 적용됨", - appliedCount_one: "제안 {{count}}개 적용됨", - appliedCount_other: "제안 {{count}}개 적용됨", - }, - recommendations: { - title: "추천", - generating: "추천을 생성하는 중…", - empty: "추천 항목이 없습니다", - }, - }, aiApprovals: { title: "AI 승인", description: "AI 에이전트가 제안한 작업으로, 실행 전에 사람의 검토가 필요합니다.", diff --git a/ui/packages/i18n/src/locales/pt.ts b/ui/packages/i18n/src/locales/pt.ts index d345eebebca..5d6a0eee9f1 100644 --- a/ui/packages/i18n/src/locales/pt.ts +++ b/ui/packages/i18n/src/locales/pt.ts @@ -3548,35 +3548,6 @@ const pt = { cloudWorkspaceTimezoneNotice: { seeded: "O fuso horário do ambiente foi definido como {{zone}} com base no seu navegador quando ele foi criado. Você pode alterá-lo em Configurações → Localização.", }, - ai: { - nlQuery: { - placeholder: "Faça uma pergunta sobre seus dados…", - ask: "Perguntar", - results: "Resultados", - match: "Correspondência: {{percent}}", - simulatedSummary: "Resultados para: {{query}}", - noResults: "Nenhum registro correspondente encontrado", - recentQueries: "Consultas recentes", - }, - formAssist: { - title: "Sugestões de IA", - suggestionCount: "{{count}} sugestões", - suggestionCount_one: "{{count}} sugestão", - suggestionCount_many: "{{count}} de sugestões", - suggestionCount_other: "{{count}} sugestões", - applyAll: "Aplicar tudo", - confidence: "Confiança: {{percent}}", - appliedCount: "{{count}} sugestões aplicadas", - appliedCount_one: "{{count}} sugestão aplicada", - appliedCount_many: "{{count}} de sugestões aplicadas", - appliedCount_other: "{{count}} sugestões aplicadas", - }, - recommendations: { - title: "Recomendações", - generating: "Gerando recomendações…", - empty: "Nenhuma recomendação disponível", - }, - }, aiApprovals: { title: "Aprovações de IA", description: "Ações propostas por um agente de IA que precisam de revisão humana antes da execução.", diff --git a/ui/packages/i18n/src/locales/ru.ts b/ui/packages/i18n/src/locales/ru.ts index b4cea4cf882..23d0ffd898f 100644 --- a/ui/packages/i18n/src/locales/ru.ts +++ b/ui/packages/i18n/src/locales/ru.ts @@ -3627,37 +3627,6 @@ const ru = { cloudWorkspaceTimezoneNotice: { seeded: "Часовой пояс окружения был задан по вашему браузеру при его создании: {{zone}}. Его можно изменить в разделе «Настройки → Локализация».", }, - ai: { - nlQuery: { - placeholder: "Задайте вопрос о своих данных…", - ask: "Спросить", - results: "Результаты", - match: "Совпадение: {{percent}}", - simulatedSummary: "Результаты по запросу: {{query}}", - noResults: "Подходящие записи не найдены", - recentQueries: "Недавние запросы", - }, - formAssist: { - title: "Предложения ИИ", - suggestionCount: "Предложений: {{count}}", - suggestionCount_one: "{{count}} предложение", - suggestionCount_few: "{{count}} предложения", - suggestionCount_many: "{{count}} предложений", - suggestionCount_other: "{{count}} предложения", - applyAll: "Применить все", - confidence: "Уверенность: {{percent}}", - appliedCount: "Применено предложений: {{count}}", - appliedCount_one: "Применено {{count}} предложение", - appliedCount_few: "Применено {{count}} предложения", - appliedCount_many: "Применено {{count}} предложений", - appliedCount_other: "Применено {{count}} предложения", - }, - recommendations: { - title: "Рекомендации", - generating: "Формирование рекомендаций…", - empty: "Нет доступных рекомендаций", - }, - }, aiApprovals: { title: "Согласования ИИ", description: "Действия, предложенные ИИ-агентом: перед выполнением их должен проверить человек.", diff --git a/ui/packages/i18n/src/locales/zh.ts b/ui/packages/i18n/src/locales/zh.ts index d0bdf4b4fce..dabbcf968ff 100644 --- a/ui/packages/i18n/src/locales/zh.ts +++ b/ui/packages/i18n/src/locales/zh.ts @@ -3670,33 +3670,6 @@ const zh = { cloudWorkspaceTimezoneNotice: { seeded: '环境时区在创建时已按你的浏览器设为 {{zone}},可在「设置 → 本地化」中修改。', }, - ai: { - nlQuery: { - placeholder: '就您的数据提问…', - ask: '提问', - results: '结果', - match: '匹配度 {{percent}}', - simulatedSummary: '查询结果:{{query}}', - noResults: '未找到匹配的记录', - recentQueries: '最近的查询', - }, - formAssist: { - title: 'AI 建议', - suggestionCount: '{{count}} 条建议', - suggestionCount_one: '{{count}} 条建议', - suggestionCount_other: '{{count}} 条建议', - applyAll: '全部应用', - confidence: '置信度 {{percent}}', - appliedCount: '已应用 {{count}} 条建议', - appliedCount_one: '已应用 {{count}} 条建议', - appliedCount_other: '已应用 {{count}} 条建议', - }, - recommendations: { - title: '推荐', - generating: '正在生成推荐…', - empty: '暂无推荐', - }, - }, aiApprovals: { title: 'AI 审批', description: 'AI 智能体提出的操作,需要人工审核后才会执行。', From 61bf86daf773c5c766267a1798173528d8459be9 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 22:22:07 +0000 Subject: [PATCH 03/13] refactor(types): tombstone the plugin-ai node mirrors (WIP) Claude-Session: https://claude.ai/code/session_01VBDBR9oS1MNUcjBVXds5D6 Co-authored-by: Claude --- ui/packages/types/src/ai.ts | 790 ++----------------------- ui/packages/types/src/index.ts | 17 +- ui/packages/types/src/zod/README.md | 4 +- ui/packages/types/src/zod/ai.zod.ts | 345 ++--------- ui/packages/types/src/zod/index.zod.ts | 14 +- 5 files changed, 147 insertions(+), 1023 deletions(-) diff --git a/ui/packages/types/src/ai.ts b/ui/packages/types/src/ai.ts index 5d792af2614..38eebb0b56e 100644 --- a/ui/packages/types/src/ai.ts +++ b/ui/packages/types/src/ai.ts @@ -7,761 +7,93 @@ */ /** - * @object-ui/types - AI Schema + * @object-ui/types - AI Schema — RETIRED * - * Defines AI-related UI component schemas for intelligent form assistance, - * recommendations, natural language queries, and data insights. - */ - -import type { BaseSchema } from './base.js'; - -/** - * AI Provider Type + * Every declaration in this module mirrored `@object-ui/plugin-ai`, which + * registered the `ai-form-assist`, `ai-recommendations` and `nl-query` node + * types. That package is RETIRED (objectstack#25921, ruling A: the console + * registered it neither eagerly nor lazily, and nothing in the workspace + * imported it), so no renderer serves the three literals any more: a page + * carrying one renders the OBJUI-001 "Unknown component type" panel. + * + * **What changed.** `AnySchema` has no member for the three literals, and every + * name below is `never`, so a node literal annotated with one is refused at the + * authoring site instead of type-checking in front of a runtime refusal. The + * zod face refuses the same three literals BY NAME at `type` + * (`./zod/ai.zod.ts`). + * + * **Why the names stay.** The ruling retires the mirrors under the tombstone + * rule: `@deprecated` + `never`, remedy in the text. When a tombstone leaves + * the published face is the sunset rule objectstack#25912 decides; until it + * lands the names stay importable, so an upgrading consumer reads a + * deprecation and a `never` instead of a TS2305 on its import line. + * + * **Remedy.** Remove the node from the page: there is no replacement node + * type. A host that still wants these components keeps its own copy and + * registers it under its own type key. + * + * `ai-insights` (`AIInsightsSchema`) left earlier, whole, under objectui#8800; + * its absence stays pinned in `./__tests__/ai-insights-retired-8800.test.ts`. + * The executable half of this retirement is + * `./__tests__/plugin-ai-retired-25921.test.ts`. + * + * @module ai */ -export type AIProvider = 'openai' | 'anthropic' | 'google' | 'azure' | 'custom'; /** - * AI Model Type + * @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921). No node + * type reads it; remove the reference. */ -export type AIModelType = 'gpt-4' | 'gpt-3.5-turbo' | 'claude-3' | 'gemini-pro' | 'custom'; +export type AIProvider = never; /** - * AI Configuration - * - * ⚠️ No node member takes this type any more (objectui#10874): the `config` - * members of {@link AIFormAssistSchema}, {@link AIRecommendationsSchema} and - * {@link NLQuerySchema} are retirement tombstones, because none of the three - * components calls a model — the model is configured in the host's own AI - * service (ObjectUI has no AI-provider API). - * The type itself stays exported: objectui#10874 retired the members, not the - * published type. + * @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921). No node + * type reads it; remove the reference. */ -export interface AIConfig { - /** - * AI provider to use - */ - provider?: AIProvider; - - /** - * Model identifier - */ - model?: AIModelType | string; - - /** - * Custom API endpoint URL - */ - apiEndpoint?: string; - - /** - * Sampling temperature (0-1) - */ - temperature?: number; - - /** - * Maximum tokens for response - */ - maxTokens?: number; - - /** - * System prompt for the AI model - */ - systemPrompt?: string; -} +export type AIModelType = never; /** - * AI Field Suggestion + * @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921). No node + * type reads it; configure the model in the host's own AI service. */ -export interface AIFieldSuggestion { - /** - * Name of the field being suggested - */ - fieldName: string; - - /** - * Suggested value for the field - */ - value: any; - - /** - * Confidence score (0-1) - */ - confidence: number; - - /** - * Explanation for the suggestion - */ - reasoning?: string; -} +export type AIConfig = never; /** - * AI Form Assist Schema - Intelligent form field suggestions + * @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921): it typed + * the `suggestions` of the retired `ai-form-assist` node. Remove the reference. */ -export interface AIFormAssistSchema extends BaseSchema { - type: 'ai-form-assist'; - - /** - * RETIRED (objectui#8178, ADR-0049, director decision batch #78, 2026-09-07) — - * `AIFormAssist` takes `({ schema, onApply, onRefresh })` and never reads - * `formId`: the key named a form nothing looked up. Measured on the retiring - * branch's own head, whole package: zero occurrences of the identifier in - * `AIFormAssist.tsx`, with the sibling `showConfidence` (2 occurrences: the - * destructure and its read) lit as the control, and zero `{...props}` / - * `{...rest}` spreads anywhere in `@object-ui/plugin-ai` — the - * `SchemaRenderer` prop channel objectui#8410 found (a renderer consuming a - * key it never names) does not exist in this package, with - * `components/src/renderers/disclosure/collapsible.tsx` lit as that - * instrument's control. - * - * A tombstone rather than a plain removal on PRONG 2 of the discriminator - * (objectui#5941, #7526, `5f8190c8c`): this package's README taught the key as - * working, in the Quick Start, in the `AIFormAssist` API example and in the - * schema-driven JSON example. {@link BaseSchema} carried `[key: string]: any` - * when this was written, so a DELETED member was absorbed silently at ANY - * value — deletion was not a quieter refusal, it was no refusal. Since - * objectui#8347 a deletion is refused on a fresh literal, but a widened value - * still carries the key silently, and the tombstone is what makes the - * compile-time refusal exist in both positions, by name. - * - * ⛔ Not enforced instead (the ruling's words): implementing reads nobody asked - * for is capability growth without pull. If an AI backend later needs the - * form's identity as context, that is a feature card with its own business - * case. - * @deprecated Not part of this contract — the value was inert. - */ - formId?: never; - - /** - * RETIRED (objectui#8178, ADR-0049, director decision batch #78, 2026-09-07) — see - * {@link AIFormAssistSchema.formId} for the measurement and the tombstone's - * grounds. `AIFormAssist.tsx` has zero occurrences of `objectName`; the - * component reaches no data source at all (it imports nothing from - * `@object-ui/core`, so the shared record-source ladder that reads - * `schema.objectName` for the grid/tree/map/calendar/gantt blocks is not on - * any path a node of this type can take). - * @deprecated Not part of this contract — the value was inert. - */ - objectName?: never; - - /** - * RETIRED (objectui#8178, ADR-0049, director decision batch #78, 2026-09-07) — see - * {@link AIFormAssistSchema.formId}. Zero occurrences in `AIFormAssist.tsx`: - * the suggestions the component renders come in already-formed on - * `suggestions`, each carrying its own `fieldName`, so this key never chose - * anything. - * @deprecated Not part of this contract — the value was inert. - */ - fields?: never; - - /** - * RETIRED (objectui#10874, ADR-0049) — see {@link AIFormAssistSchema.config}. - * Nothing read `context` either: the component asks no model for anything, so - * there was no request for this value to be context to. - * - * **Instead:** pass the context to your host's own AI service when you ask it - * for suggestions, and delete the key from the node. - * @deprecated Not part of this contract — the value was inert. - */ - context?: never; - - /** - * RETIRED (objectui#10874, ADR-0049) — nothing read `config`, on this node or - * on either sibling. `AIFormAssist` takes `({ schema, onApply, onRefresh })` - * and destructures only `suggestions`, `showConfidence` and `showReasoning` - * off the schema; it calls no model and fetches nothing, so a `provider`, - * `model` or `systemPrompt` written here configured nothing and changed - * nothing on screen. The same holds for `AIRecommendations` and - * `NLQueryInput`: all three are presentation only. - * - * **Instead:** 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 `suggestions`. - * - * A tombstone rather than a deletion on the grounds - * {@link AIFormAssistSchema.formId} records: {@link BaseSchema} carried - * `[key: string]: any`, so a DELETED member was absorbed silently at any value - * (since objectui#8347, through a widened value only), and the tombstone is - * what makes the compile-time refusal exist, by name. The - * zod mirror refuses the key by name with the same prescription - * (`retirementTombstone`), so the two faces agree. - * - * ⛔ Not enforced instead: a per-node AI configuration is capability growth - * nothing pulls on (the objectui#10874 triage ruling). If a component later - * needs one, that is a feature card with its own business case. - * @deprecated Not part of this contract — the value was inert. - */ - config?: never; - - /** - * Current suggestions - */ - suggestions?: AIFieldSuggestion[]; - - /** - * RETIRED (objectui#8178, ADR-0049, director decision batch #78, 2026-09-07) — see - * {@link AIFormAssistSchema.formId}. This one was a DEAD DESTRUCTURE rather - * than an absent one: `AIFormAssist.tsx` destructured `autoFill = false` and - * never referenced the binding again — the single occurrence of the identifier - * in the file was that line, against `showConfidence` and `showReasoning` at - * two each (destructure plus read). Nothing was ever filled automatically. - * @deprecated Not part of this contract — the value was inert. - */ - autoFill?: never; - - /** - * Show confidence scores for suggestions - */ - showConfidence?: boolean; - - /** - * Show reasoning for suggestions - */ - showReasoning?: boolean; - - /** - * RETIRED (objectui#10874, the objectui#6182 / objectui#6124 house rule) — it - * was typed `string`, the handler-expression dialect objectui#6182 withdrew as - * an authoring form on EITHER face, and nothing read it: `AIFormAssist` never - * names `onApplySuggestion`. The zod mirror already refused it by name - * (`handlerKeyRefusal(…, 'retired')`, objectui#10859); this face now agrees, - * so a node that carries it is a compile error rather than a silent no-op. - * - * **Instead:** pass a handler as a component prop — `onApply` on - * `AIFormAssist`, which the component calls with the applied suggestion. - * @deprecated Not part of this contract — a string here ran nothing. - */ - onApplySuggestion?: never; - - /** - * RETIRED (objectui#10874) — see {@link AIFormAssistSchema.onApplySuggestion}. - * Nothing read it, and there is no reject callback to wire it to: - * `AIFormAssist` dismisses a suggestion in its own state and reports nothing. - * - * **Instead:** delete the key. The component's handlers are component props - * (`onApply`, `onRefresh`), never members of the node. - * @deprecated Not part of this contract — a string here ran nothing. - */ - onRejectSuggestion?: never; - /** - * REFUSED BY NAME (objectui#9256, ADR-0049) — `ai-form-assist` reads NEITHER - * content channel: no renderer read consumes `body` or `children` for this - * node. - * - * MEASURED with the TypeScript TYPE CHECKER and not with grep, across all 24 - * packages that register components plus the generic traversers — a docblock - * mention is not a read, and `body: schema.requestBody` in - * `packages/plugin-chatbot/src/renderer.tsx` is the kind of prefix hit grep - * scores. Every read is filed under the TYPE of the object it is read from; - * this declaration carries none. This renderer takes its configuration from - * the props bag `SchemaRenderer` spreads rather than from `schema.*`, and - * carries zero `body` / `children` reads either way. - * - * Before objectui#9256 tombstoned them here, `body` and `children` were both - * inherited-and-optional from {@link BaseSchema} — so authoring either here - * type-checked, parsed green through `.passthrough()`, and rendered NOTHING: - * no render-time error or warning and no element; only the parser tier's - * `not-a-container` warning (objectui#9910) noticed it. objectui#6771 has since retired `body` on - * `BaseSchema` itself; `BaseSchema` still declares `children`, so this node's - * own tombstone is what refuses it here. `SchemaRenderer` strips both keys - * out of the props bag it spreads, so neither reaches the component by - * another route either. - * - * @deprecated Not a channel `ai-form-assist` reads — nothing renders it. - */ - body?: never; - /** - * REFUSED BY NAME (objectui#9256, ADR-0049) — `ai-form-assist` reads NEITHER - * content channel: no renderer read consumes `body` or `children` for this - * node. - * - * MEASURED with the TypeScript TYPE CHECKER and not with grep, across all 24 - * packages that register components plus the generic traversers — a docblock - * mention is not a read, and `body: schema.requestBody` in - * `packages/plugin-chatbot/src/renderer.tsx` is the kind of prefix hit grep - * scores. Every read is filed under the TYPE of the object it is read from; - * this declaration carries none. This renderer takes its configuration from - * the props bag `SchemaRenderer` spreads rather than from `schema.*`, and - * carries zero `body` / `children` reads either way. - * - * Before objectui#9256 tombstoned them here, `body` and `children` were both - * inherited-and-optional from {@link BaseSchema} — so authoring either here - * type-checked, parsed green through `.passthrough()`, and rendered NOTHING: - * no render-time error or warning and no element; only the parser tier's - * `not-a-container` warning (objectui#9910) noticed it. objectui#6771 has since retired `body` on - * `BaseSchema` itself; `BaseSchema` still declares `children`, so this node's - * own tombstone is what refuses it here. `SchemaRenderer` strips both keys - * out of the props bag it spreads, so neither reaches the component by - * another route either. - * - * @deprecated Not a channel `ai-form-assist` reads — nothing renders it. - */ - children?: never; -} +export type AIFieldSuggestion = never; /** - * AI Recommendation Item + * @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921): + * `ai-form-assist` is no longer a node type and nothing renders it. Remove the + * node from the page; there is no replacement node type. */ -export interface AIRecommendationItem { - /** - * Unique identifier - */ - id: string; - - /** - * Recommendation title - */ - title: string; - - /** - * Recommendation description - */ - description?: string; - - /** - * Relevance score (0-1) - */ - score: number; - - /** - * Recommendation category - */ - category?: string; - - /** - * Additional metadata - */ - metadata?: Record; - - /** - * Action to perform when selected - */ - action?: { - /** - * Action type - */ - type: string; - - /** - * Action target - */ - target?: string; - }; -} +export type AIFormAssistSchema = never; /** - * AI Recommendations Schema - Intelligent content recommendations + * @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921): it typed + * the `recommendations` of the retired `ai-recommendations` node. Remove the + * reference. */ -export interface AIRecommendationsSchema extends BaseSchema { - type: 'ai-recommendations'; - - /** - * RETIRED (objectui#8178, ADR-0049, director decision batch #78, 2026-09-07) — see - * {@link AIFormAssistSchema.formId} for the measurement and the tombstone's - * grounds. Zero occurrences in `AIRecommendations.tsx`, which renders the - * `recommendations` it is handed and fetches nothing. - * @deprecated Not part of this contract — the value was inert. - */ - objectName?: never; - - /** - * RETIRED (objectui#10874, ADR-0049) — see {@link AIFormAssistSchema.config} - * and {@link AIFormAssistSchema.context}. `AIRecommendations` renders the - * `recommendations` it is handed and reads no `context`. - * - * **Instead:** pass the context to your host's own AI service when you ask it - * for recommendations, and delete the key from the node. - * @deprecated Not part of this contract — the value was inert. - */ - context?: never; - - /** - * RETIRED (objectui#10874, ADR-0049) — see {@link AIFormAssistSchema.config}. - * `AIRecommendations` calls no model and reads no `config`. - * - * **Instead:** 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 `recommendations`. - * @deprecated Not part of this contract — the value was inert. - */ - config?: never; - - /** - * Current recommendations - */ - recommendations?: AIRecommendationItem[]; - - /** - * RETIRED (objectui#8178, ADR-0049, director decision batch #78, 2026-09-07) — the - * sharpest member of the seven, and the reason the finding was filed. Its doc - * comment read *"Maximum number of results to display"*, and - * `AIRecommendations` renders EVERY item in `recommendations` — no slice, no - * cap, zero occurrences of the identifier in the component. An author who - * wrote `maxResults: 5` against a 50-item list got 50 rows and no diagnostic. - * - * ⛔ Not implemented as a slice instead: that is the Enforce direction the - * ruling declined — nothing in the repo pulls on it. The renderer's docblock - * now states that it renders every item, so the promise is withdrawn rather - * than left unhonoured, and `AIRecommendations.rendersEveryItem-8178.test.tsx` - * pins the behaviour. Cap the array before you hand it over. - * @deprecated Not part of this contract — the value was inert. - */ - maxResults?: never; - - /** - * Show relevance scores - */ - showScores?: boolean; - - /** - * Display layout. - * - * `'carousel'` is RETIRED from this union (objectui#10330, ADR-0049 - * enforce-or-remove). It was declared here and offered in the designer, but - * `AIRecommendations` renders only `grid` specially, so a stored `carousel` - * silently rendered as a list. It was never implemented, so it is removed - * rather than enforced. A node that still carries it renders the list layout; - * write `'list'` or `'grid'` instead. - */ - layout?: 'list' | 'grid'; - - /** - * Selection callback — RUNTIME SLOT (objectui#10874, the objectui#6124 shape): - * a host-supplied function, NOT authorable metadata. `ai-recommendations` is - * registered to the raw `AIRecommendations` component, `SchemaRenderer` - * spreads the node's own keys onto its props, and `AIRecommendations` CALLS - * `onSelect?.(item)` when a recommendation is chosen — so this declares the - * callable the renderer invokes. It used to declare the handler-expression - * STRING, which objectui#6182 ruled is not an authoring form: a string there - * was called as a function and threw at click. The zod twin refuses the key - * by name (`handlerKeyRefusal(…, 'runtime-slot')`); supply it from a React - * host, on the node or as the component's own `onSelect` prop. - */ - onSelect?: (item: AIRecommendationItem) => void; - - /** - * Dismiss callback — RUNTIME SLOT (objectui#10874, the objectui#6124 shape): - * the same channel as {@link AIRecommendationsSchema.onSelect}. - * `AIRecommendations` CALLS `onDismiss?.(item)` when a recommendation is - * dismissed, so this declares that callable. It used to declare a STRING, - * which was called as a function and threw at click. The zod twin refuses the - * key by name; supply it from a React host. - */ - onDismiss?: (item: AIRecommendationItem) => void; - - /** - * Loading state - */ - loading?: boolean; - - /** - * Message to display when no recommendations are available - */ - emptyMessage?: string; - /** - * REFUSED BY NAME (objectui#9256, ADR-0049) — `ai-recommendations` reads - * NEITHER content channel: no renderer read consumes `body` or `children` for - * this node. - * - * MEASURED with the TypeScript TYPE CHECKER and not with grep, across all 24 - * packages that register components plus the generic traversers — a docblock - * mention is not a read, and `body: schema.requestBody` in - * `packages/plugin-chatbot/src/renderer.tsx` is the kind of prefix hit grep - * scores. Every read is filed under the TYPE of the object it is read from; - * this declaration carries none. This renderer takes its configuration from - * the props bag `SchemaRenderer` spreads rather than from `schema.*`, and - * carries zero `body` / `children` reads either way. - * - * Before objectui#9256 tombstoned them here, `body` and `children` were both - * inherited-and-optional from {@link BaseSchema} — so authoring either here - * type-checked, parsed green through `.passthrough()`, and rendered NOTHING: - * no render-time error or warning and no element; only the parser tier's - * `not-a-container` warning (objectui#9910) noticed it. objectui#6771 has since retired `body` on - * `BaseSchema` itself; `BaseSchema` still declares `children`, so this node's - * own tombstone is what refuses it here. `SchemaRenderer` strips both keys - * out of the props bag it spreads, so neither reaches the component by - * another route either. - * - * @deprecated Not a channel `ai-recommendations` reads — nothing renders it. - */ - body?: never; - /** - * REFUSED BY NAME (objectui#9256, ADR-0049) — `ai-recommendations` reads - * NEITHER content channel: no renderer read consumes `body` or `children` for - * this node. - * - * MEASURED with the TypeScript TYPE CHECKER and not with grep, across all 24 - * packages that register components plus the generic traversers — a docblock - * mention is not a read, and `body: schema.requestBody` in - * `packages/plugin-chatbot/src/renderer.tsx` is the kind of prefix hit grep - * scores. Every read is filed under the TYPE of the object it is read from; - * this declaration carries none. This renderer takes its configuration from - * the props bag `SchemaRenderer` spreads rather than from `schema.*`, and - * carries zero `body` / `children` reads either way. - * - * Before objectui#9256 tombstoned them here, `body` and `children` were both - * inherited-and-optional from {@link BaseSchema} — so authoring either here - * type-checked, parsed green through `.passthrough()`, and rendered NOTHING: - * no render-time error or warning and no element; only the parser tier's - * `not-a-container` warning (objectui#9910) noticed it. objectui#6771 has since retired `body` on - * `BaseSchema` itself; `BaseSchema` still declares `children`, so this node's - * own tombstone is what refuses it here. `SchemaRenderer` strips both keys - * out of the props bag it spreads, so neither reaches the component by - * another route either. - * - * @deprecated Not a channel `ai-recommendations` reads — nothing renders it. - */ - children?: never; -} +export type AIRecommendationItem = never; /** - * Natural Language Query Result + * @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921): + * `ai-recommendations` is no longer a node type and nothing renders it. Remove + * the node from the page; there is no replacement node type. */ -export interface NLQueryResult { - /** - * Original query string - */ - query: string; - - /** - * Parsed query representation - */ - parsedQuery?: Record; - - /** - * Result data - */ - data?: any[]; - - /** - * Column definitions for the result data - */ - columns?: Array<{ - /** - * Column name - */ - name: string; - - /** - * Display label - */ - label?: string; - - /** - * Column data type - */ - type?: string; - }>; - - /** - * AI-generated summary of the results - */ - summary?: string; - - /** - * Confidence score for the query interpretation (0-1) - */ - confidence?: number; -} +export type AIRecommendationsSchema = never; /** - * Natural Language Query Schema - Query data using natural language + * @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921): it typed + * the `result` of the retired `nl-query` node. Remove the reference. */ -export interface NLQuerySchema extends BaseSchema { - type: 'nl-query'; - - /** - * RETIRED (objectui#8178, ADR-0049, director decision batch #78, 2026-09-07) — see - * {@link AIFormAssistSchema.formId} for the measurement and the tombstone's - * grounds. Zero occurrences in `NLQueryInput.tsx`: the component collects a - * query string and hands it to `onSubmit`, and any object scoping belongs to - * the host that answers it. - * @deprecated Not part of this contract — the value was inert. - */ - objectName?: never; - - /** - * Input placeholder text - */ - placeholder?: string; - - /** - * RETIRED (objectui#10874, ADR-0049) — see {@link AIFormAssistSchema.config}. - * `NLQueryInput` collects a query string and hands it on; it calls no model - * and reads no `config`. - * - * **Instead:** configure the model in your host's own AI service (ObjectUI has - * no AI-provider API; these components call no model), and hand its answer to - * the node as `result`. - * @deprecated Not part of this contract — the value was inert. - */ - config?: never; - - /** - * Current query result - */ - result?: NLQueryResult; - - /** - * Example queries to suggest - */ - suggestions?: string[]; - - /** - * Show query history - */ - showHistory?: boolean; - - /** - * Query history entries - */ - history?: Array<{ - /** - * Query string - */ - query: string; - - /** - * Timestamp of the query - */ - timestamp: string; - }>; - - /** - * Loading state - */ - loading?: boolean; - - /** - * Submit callback — RUNTIME SLOT (objectui#10874, the objectui#6124 shape): a - * host-supplied function, NOT authorable metadata. `nl-query` is registered - * to the raw `NLQueryInput` component, `SchemaRenderer` spreads the node's own - * keys onto its props, and `NLQueryInput` CALLS `onSubmit` with the query - * text — so this declares the callable the renderer invokes. It used to - * declare the handler-expression STRING objectui#6182 withdrew: a string there - * was called as a function and threw on submit. The zod twin refuses the key - * by name (`handlerKeyRefusal(…, 'runtime-slot')`); supply it from a React - * host. - */ - onSubmit?: (query: string) => void; - /** - * REFUSED BY NAME (objectui#9256, ADR-0049) — `nl-query` reads NEITHER - * content channel: no renderer read consumes `body` or `children` for this - * node. - * - * MEASURED with the TypeScript TYPE CHECKER and not with grep, across all 24 - * packages that register components plus the generic traversers — a docblock - * mention is not a read, and `body: schema.requestBody` in - * `packages/plugin-chatbot/src/renderer.tsx` is the kind of prefix hit grep - * scores. Every read is filed under the TYPE of the object it is read from; - * this declaration carries none. This renderer takes its configuration from - * the props bag `SchemaRenderer` spreads rather than from `schema.*`, and - * carries zero `body` / `children` reads either way. - * - * Before objectui#9256 tombstoned them here, `body` and `children` were both - * inherited-and-optional from {@link BaseSchema} — so authoring either here - * type-checked, parsed green through `.passthrough()`, and rendered NOTHING: - * no render-time error or warning and no element; only the parser tier's - * `not-a-container` warning (objectui#9910) noticed it. objectui#6771 has since retired `body` on - * `BaseSchema` itself; `BaseSchema` still declares `children`, so this node's - * own tombstone is what refuses it here. `SchemaRenderer` strips both keys - * out of the props bag it spreads, so neither reaches the component by - * another route either. - * - * @deprecated Not a channel `nl-query` reads — nothing renders it. - */ - body?: never; - /** - * REFUSED BY NAME (objectui#9256, ADR-0049) — `nl-query` reads NEITHER - * content channel: no renderer read consumes `body` or `children` for this - * node. - * - * MEASURED with the TypeScript TYPE CHECKER and not with grep, across all 24 - * packages that register components plus the generic traversers — a docblock - * mention is not a read, and `body: schema.requestBody` in - * `packages/plugin-chatbot/src/renderer.tsx` is the kind of prefix hit grep - * scores. Every read is filed under the TYPE of the object it is read from; - * this declaration carries none. This renderer takes its configuration from - * the props bag `SchemaRenderer` spreads rather than from `schema.*`, and - * carries zero `body` / `children` reads either way. - * - * Before objectui#9256 tombstoned them here, `body` and `children` were both - * inherited-and-optional from {@link BaseSchema} — so authoring either here - * type-checked, parsed green through `.passthrough()`, and rendered NOTHING: - * no render-time error or warning and no element; only the parser tier's - * `not-a-container` warning (objectui#9910) noticed it. objectui#6771 has since retired `body` on - * `BaseSchema` itself; `BaseSchema` still declares `children`, so this node's - * own tombstone is what refuses it here. `SchemaRenderer` strips both keys - * out of the props bag it spreads, so neither reaches the component by - * another route either. - * - * @deprecated Not a channel `nl-query` reads — nothing renders it. - */ - children?: never; -} +export type NLQueryResult = never; /** - * `AIInsightsSchema` (`type: 'ai-insights'`) — RETIRED. This comment is the - * tombstone, because a TypeScript interface erases and leaves no runtime residue - * that could carry a marker of its own. - * - * RETIRED under ADR-0049 enforce-or-remove by the director-seat ruling recorded - * on objectui#8800 (decision batch #137 item 2, 2026-09-15, maintainer verbatim - * 「同意」), letter **A** of the three the card offered: retire the declaration - * and its barrel export. ⛔ Not B (write a renderer for it) and ⛔ not C (keep it - * as a documented forward declaration) — both were refused by that ruling, and - * ⛔ neither may be re-proposed by a card that measured something adjacent. The - * whole-family precedent kept as a module tombstone is `./blocks.ts`; the - * member-level precedent one screen up is objectui#8178. - * - * **What went.** The exported `AIInsightsSchema` interface and its - * `@object-ui/types` barrel re-export. On top of {@link BaseSchema} it declared - * `objectName`, `data`, `config`, `insights[]` (each with `title`, - * `description`, `type`, `severity` and `metric`), `loading`, `autoRefresh` and - * `refreshInterval`. There is no replacement key and no replacement type. - * - * **Why.** Declared and unrenderable. NOTHING in this repository ever registered - * the `ai-insights` discriminant, so no node carrying that spelling ever reached - * a renderer, and the spelling occurred exactly once tree-wide — the `type` - * literal inside this very declaration. The three siblings this file still - * declares (`ai-form-assist`, `ai-recommendations`, `nl-query`) landed in the - * same commit as this one and were given renderers and registrations twenty-two - * minutes later by `@object-ui/plugin-ai`; this fourth one never was, for the - * seven months that followed. ⛔ The census is not copied into this prose: it is - * re-derived, with a lit control in the same pass, by the pin named at the foot - * of this block. - * - * **What an author got, and why a LOUD failure was still ruled a defect.** - * `SchemaRenderer` resolves an unregistered discriminant to the OBJUI-001 - * "Unknown component type" panel, and the CLI validator reports an unknown - * schema type. Two loud refusals — ⛔ not a silent swallow, which is why the card - * was p3 and not urgent. But TypeScript said YES, because this declaration sat - * on the PUBLISHED `.d.ts`. A compile-time blessing in front of a guaranteed - * runtime refusal is what misleads an author — and misleads an AI reading the - * `.d.ts` as its authoring manual. The retirement moves the refusal from run - * time to the moment the node is written, which is the whole of what it buys. - * - * ⚠️ **The zero above is the IN-REPO HALF and nothing more.** This repository was - * enumerated; customer applications and published documents outside it were NOT, - * and the ruling was taken with that limit attached. `@object-ui/types` is a - * published package, so an external TypeScript consumer that authored this node - * is structurally unobservable from here and gets a compile error naming the - * symbol. That consumer's document was already being refused at run time; the - * FROM/TO in this retirement's changeset is what it greps. - * - * ⚠️ **NOT retired, deliberately.** {@link AIFormAssistSchema}, - * {@link AIRecommendationsSchema} and {@link NLQuerySchema} above are registered - * and rendered, and stay. Their seven zero-read MEMBERS are objectui#8178's - * `?: never` tombstones — a different retirement, a different ruling, and ⛔ not - * reachable from this one. The support types {@link AIConfig}, - * {@link AIFieldSuggestion}, {@link AIRecommendationItem} and - * {@link NLQueryResult} stay too: each has a reader among those three, so none - * is orphaned by this removal. - * - * ⚠️ AMENDED 2026-09-28 (objectui#10874): {@link AIConfig} no longer has a reader - * among those three — their `config` members are retirement tombstones now — and - * it stays exported as a type only. The sentence above records the tree this - * retirement was written against. - * - * The pin that makes this tombstone executable — the absence of the symbol, the - * absence of the spelling, and the lit controls that keep both from being - * vacuous — is `__tests__/ai-insights-retired-8800.test.ts`. + * @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921): + * `nl-query` is no longer a node type and nothing renders it. Remove the node + * from the page; there is no replacement node type. */ +export type NLQuerySchema = never; diff --git a/ui/packages/types/src/index.ts b/ui/packages/types/src/index.ts index ba49cb923cd..cbe87c6d14e 100644 --- a/ui/packages/types/src/index.ts +++ b/ui/packages/types/src/index.ts @@ -872,7 +872,6 @@ import type { FieldDesignerSchema, } from './designer.js'; import type { ReportComponentSchema, ReportBuilderSchema, ReportViewerSchema } from './reports.js'; -import type { AIFormAssistSchema, AIRecommendationsSchema, NLQuerySchema } from './ai.js'; import type { ViewComponentSchema } from './views.js'; import type { ActionBarSchema } from './ui-action.js'; @@ -964,7 +963,9 @@ export { // `@objectstack/spec` flow schema. export type { - // AI System + // AI System — RETIRED with `@object-ui/plugin-ai` (objectstack#25921). Every + // name below is a `@deprecated` `never` tombstone; `./ai.ts` carries the + // remedy, and the names leave this block under objectstack#25912's sunset rule. AIProvider, AIModelType, AIConfig, @@ -979,10 +980,8 @@ export type { // enforce-or-remove, director ruling of 2026-09-15, decision batch #137 // item 2, maintainer verbatim 「同意」, letter A). Nothing ever registered // the discriminant, so the declaration could only ever route an author to a - // node that type-checks and then renders the OBJUI-001 panel. The reasoning, - // the boundary against the three AI schemas that DO render, and the in-repo - // limit on its census live at the tombstone in `./ai.ts`; the executable half - // is `./__tests__/ai-insights-retired-8800.test.ts`. + // node that type-checks and then renders the OBJUI-001 panel. Its executable + // half is `./__tests__/ai-insights-retired-8800.test.ts`. } from './ai.js'; export type { @@ -1111,9 +1110,9 @@ export type AnySchema = | ReportComponentSchema | ReportBuilderSchema | ReportViewerSchema - | AIFormAssistSchema - | AIRecommendationsSchema - | NLQuerySchema + // `ai-form-assist` / `ai-recommendations` / `nl-query` retired with + // `@object-ui/plugin-ai` (objectstack#25921); their names in `./ai.ts` are + // `never` tombstones. | ViewComponentSchema | ActionBarSchema // objectui#11515 — the TS twin of a zod-only arm; its sibling diff --git a/ui/packages/types/src/zod/README.md b/ui/packages/types/src/zod/README.md index c66d295cfb2..3cebf517684 100644 --- a/ui/packages/types/src/zod/README.md +++ b/ui/packages/types/src/zod/README.md @@ -297,8 +297,8 @@ The ADR-0080 public blocks `object-metric`, `object-master-detail-form`, `object - `ObjectMetricBlockSchema`, `ObjectMasterDetailFormBlockSchema`, `ObjectTimelineBlockSchema`, `ObjectFormBlockSchema`, `ObjectMapBlockSchema`, `ObjectChartBlockSchema`, `ObjectGanttBlockSchema`, `ObjectGridBlockSchema` - `ObjectPivotBlockSchema`, `EmbeddableFormBlockSchema`, `ListViewBlockSchema` -### AI Components (3) -- `AIFormAssistSchema`, `AIRecommendationsSchema`, `NLQuerySchema` +### AI Components — RETIRED +`ai-form-assist`, `ai-recommendations` and `nl-query` retired with `@object-ui/plugin-ai` (objectstack#25921). Their arms stay in `AnyComponentSchema` as refusals, not exported: a node naming one is refused by name at `type`, with the remedy of removing the node. `AIFormAssistSchema`, `AIRecommendationsSchema`, `NLQuerySchema` and their support schemas stay exported as `@deprecated` `z.never()` tombstones that accept nothing, until objectstack#25912's sunset rule takes them off the face. ### Designer Components (6) The `@object-ui/plugin-designer` nodes (objectui#10859 batch 7). Each arm mirrors its TypeScript declaration in `designer.ts` member for member, and the record types under them (`DesignerComponentSchema`, `BPMNNodeSchema`, `ReportDesignerSectionSchema`, …) are exported beside them. `lanes` and `version` on `process-designer` are mirrored since objectui#11434 gave each a reader, with `BPMNLaneSchema` for the lane record. objectui#11434 also respelled `DataModelRelationship.onDelete` as `deleteBehavior`, in `@objectstack/spec`'s vocabulary (`set_null`, `cascade`, `restrict`): `onDelete` is refused by name with the migration. A report element's `properties` record refuses `field` by name, with the migration to the element's declared `dataBinding`, which `ReportDesigner` reads instead (objectui#11434). Members objectui#11434 retired are refused by name on both faces, each with what to write instead: `autoLayout` on `data-model-designer`, `previewMode` on `report-designer`, and the record members `DesignerComponent.parentId`, `DataModelRelationship.onUpdate`, `BPMNNode.serviceEndpoint`, `ObjectDefinition.relationships` and `DesignerFieldDefinition.validationRules`. The element types of the last two, `ObjectDefinitionRelationship` and `DesignerValidationRule`, and their mirrors left the package. None of the six components reads a content channel, so every arm refuses `children` and `body` by name (objectui#9256). diff --git a/ui/packages/types/src/zod/ai.zod.ts b/ui/packages/types/src/zod/ai.zod.ts index 3c30f22fc5f..5d493c039fa 100644 --- a/ui/packages/types/src/zod/ai.zod.ts +++ b/ui/packages/types/src/zod/ai.zod.ts @@ -7,58 +7,30 @@ */ /** - * @object-ui/types/zod - AI Component Zod Validators + * @object-ui/types/zod - AI Component Zod Validators — RETIRED * - * Zod mirrors of the three AI node declarations in `../ai.ts` — - * `AIFormAssistSchema`, `AIRecommendationsSchema` and `NLQuerySchema` — which - * `@object-ui/plugin-ai` registers as `ai-form-assist`, `ai-recommendations` - * and `nl-query`. + * This module mirrored the three node declarations of `../ai.ts`, which + * `@object-ui/plugin-ai` registered as `ai-form-assist`, `ai-recommendations` + * and `nl-query`. That package is RETIRED (objectstack#25921, ruling A), so no + * renderer serves the three literals and a page carrying one renders the + * OBJUI-001 "Unknown component type" panel. * - * ## Why this module exists (objectui#10859) + * Two halves, one per audience: * - * The three keys were REGISTERED, DECLARED on the published TypeScript face and - * TAUGHT by `packages/plugin-ai/README.md` ("Schema-Driven Usage"), while - * `AnyComponentSchema` carried no arm for them — so `safeValidateSchema` and - * `objectui validate` refused every document naming them with `invalid_union` - * at `type`, the README's own example included. The arms below restate the - * declarations member for member; `AIComponentSchema` is the category union - * `AnyComponentSchema` lists. + * - **The refusal arms** ({@link RetiredAIFormAssistNodeSchema} and its two + * siblings) take the arms' place inside `AnyComponentSchema`, in the shape + * of `cloud.zod.ts#RetiredCloudPlanStatusNodeSchema` (objectui#12954): a + * stale document is refused BY NAME, at `type`, with the remedy. Deleting + * the arms would already refuse, but with the union's remedy-free + * `Invalid input`. They are deliberately NOT re-exported from + * `index.zod.ts`: nothing outside this package parses against a refusal arm. + * - **The published names** (`AIFormAssistSchema` and the rest) are + * `@deprecated` `z.never()` tombstones, the zod spelling of the `never` + * their TypeScript twins in `../ai.ts` carry. Every parse against one fails + * with the remedy. They leave the published face under objectstack#25912's + * sunset rule, not here. * - * ## The `on*` members: a named refusal here, per key on the TypeScript face - * - * The five `on*` members were declared `string` on the TypeScript face — the - * handler-expression string dialect that objectui#6182 (maintainer ruling, - * option A) withdrew as an authoring form on EITHER face. The zod face follows - * that ruling and objectui#6124's shape, `handlerKeyRefusal`, with the - * disposition measured per key on `@object-ui/plugin-ai`: - * - * - `onSelect` / `onDismiss` (`AIRecommendations`) and `onSubmit` - * (`NLQueryInput`) are RUNTIME SLOTS — the registration is the raw - * component, `SchemaRenderer` spreads the node's keys onto it as props, and - * each component CALLS the prop (`onSelect?.(item)`, - * `onSubmitProp?.(queryText)`), so an authored string reaches a call site; - * - `onApplySuggestion` / `onRejectSuggestion` have NO reader in the package — - * `AIFormAssist` takes `onApply` / `onRefresh`, not these — so they are - * RETIRED. - * - * When objectui#10859 wrote these arms every TypeScript twin still read - * `string`, the dialect this face refuses. objectui#10874 settled that face per - * key, by the objectui#6124 rule: the three RUNTIME SLOTS now declare the - * callable their component invokes (`onSelect` / `onDismiss` take an - * `AIRecommendationItem`, `onSubmit` the query string) — the objectui#6124 - * shape, a named refusal on this JSON face and a callable twin on the - * TypeScript one — and the two RETIRED keys are `?: never` there, so both - * faces refuse those two. Both halves are pinned in - * `../__tests__/ai-zod-arms-10859.test.ts` and - * `../__tests__/ai-node-faces-agree-10874.test.ts`. - * - * ## `config` / `context`: retired on both faces (objectui#10874) - * - * Nothing in `@object-ui/plugin-ai` read either member: the three components - * are presentation only and call no model. Both are `retirementTombstone`s here - * and `?: never` on the TypeScript face, and the refusal says what to do - * instead — configure the model in the host's own AI service (ObjectUI has no - * AI-provider API) and hand its output to the node. + * Pinned in `../__tests__/plugin-ai-retired-25921.test.ts`. * * ⛔ No `.default()` anywhere in this module — see the "authors no default" * note in `index.zod.ts`. @@ -68,256 +40,69 @@ */ import { z } from 'zod'; -import { BaseSchema } from './base.zod.js'; -import { handlerKeyRefusal, retirementTombstone } from './tombstone.zod.js'; +import { retiredNodeType } from './tombstone.zod.js'; -/** - * The objectui#8178 retirement guidance, one string per retired member so the - * refusal an author reads names the key and the reason (the TypeScript face - * carries the same members as `?: never` tombstones). - */ -const retiredAiMember = (node: string, key: string, detail: string) => - retirementTombstone( - `RETIRED (objectui#8178, ADR-0049) — \`${key}\` on \`${node}\` had no reader: ${detail} ` - + 'The value was inert, so it is refused by name rather than silently ignored.', - ); +/** The remedy every refusal in this module carries — one string, both channels. */ +const PLUGIN_AI_RETIRED = + 'It retired with `@object-ui/plugin-ai` (objectstack#25921): nothing registers it, so the page rendered ' + + 'the "Unknown component type" panel in its place. Remove the node from the page; there is no ' + + 'replacement node type.'; -/** - * The objectui#10874 retirement guidance for `config` / `context`: the refusal - * names the key and the node, says why, and prescribes what to do instead. - */ -const retiredAiInput = (node: string, key: 'config' | 'context', instead: string) => - retirementTombstone( - `RETIRED (objectui#10874, ADR-0049) — \`${key}\` on \`${node}\` had no reader: the component is ` - + 'presentation only and calls no model, so the value reached nothing and changed nothing on screen. ' - + `Instead: ${instead} Delete the key from the node.`, - ); +/** A published name kept as a `never` tombstone: every parse fails, naming the remedy. */ +const retiredExport = (name: string) => { + const text = `RETIRED — \`${name}\` mirrored a node type of \`@object-ui/plugin-ai\`, which retired ` + + '(objectstack#25921), and accepts nothing. Remove the reference.'; + return z.never({ error: text }).describe(text); +}; -/** - * The objectui#9256 family-D guidance: the renderer reads NEITHER content - * channel, so both are refused by name on this face as on the TypeScript one. - */ -const neitherChannel = (node: string, renders: string) => - 'REFUSED (objectui#9256, ADR-0049) — `' + node + '` reads NEITHER content channel: measured with the ' - + 'TypeScript type checker across all 24 registering packages, no renderer read consumes `body` or ' - + '`children` for this node, and `SchemaRenderer` strips both out of the props bag it spreads. An ' - + 'authored value therefore rendered NOTHING — no render-time error or warning and no element; only the parser tier\'s `not-a-container` warning (objectui#9910) noticed it. ' - + `What it renders instead: ${renders}.`; +/** ⛔ `ai-form-assist` is RETIRED — its named refusal inside `AnyComponentSchema`. */ +export const RetiredAIFormAssistNodeSchema = retiredNodeType('ai-form-assist', PLUGIN_AI_RETIRED); -/** - * AI Configuration — mirrors `AIConfig` (`../ai.ts`). - * - * No arm below takes it any more: the three `config` members it typed are - * retirement tombstones since objectui#10874. It stays exported beside its - * TypeScript twin, which also stays. - */ -export const AIConfigSchema = z.looseObject({ - provider: z.enum(['openai', 'anthropic', 'google', 'azure', 'custom']).optional().describe('AI provider to use'), - // `AIModelType | string` on the TypeScript face — the named models are - // examples, not a closed set, so the accepted value is any string. - model: z.string().optional().describe('Model identifier'), - apiEndpoint: z.string().optional().describe('Custom API endpoint URL'), - temperature: z.number().optional().describe('Sampling temperature (0-1)'), - maxTokens: z.number().optional().describe('Maximum tokens for response'), - systemPrompt: z.string().optional().describe('System prompt for the AI model'), -}); +/** ⛔ `ai-recommendations` is RETIRED — its named refusal inside `AnyComponentSchema`. */ +export const RetiredAIRecommendationsNodeSchema = retiredNodeType('ai-recommendations', PLUGIN_AI_RETIRED); -/** - * AI Field Suggestion — mirrors `AIFieldSuggestion` (`../ai.ts`). - */ -export const AIFieldSuggestionSchema = z.looseObject({ - fieldName: z.string().describe('Name of the field being suggested'), - value: z.any().describe('Suggested value for the field'), - confidence: z.number().describe('Confidence score (0-1)'), - reasoning: z.string().optional().describe('Explanation for the suggestion'), -}); +/** ⛔ `nl-query` is RETIRED — its named refusal inside `AnyComponentSchema`. */ +export const RetiredNLQueryNodeSchema = retiredNodeType('nl-query', PLUGIN_AI_RETIRED); -/** - * AI Form Assist Schema — mirrors `AIFormAssistSchema` (`../ai.ts`). - */ -export const AIFormAssistSchema = BaseSchema.extend({ - type: z.literal('ai-form-assist'), - formId: retiredAiMember( - 'ai-form-assist', - 'formId', - '`AIFormAssist` takes `({ schema, onApply, onRefresh })` and never reads it.', - ), - objectName: retiredAiMember( - 'ai-form-assist', - 'objectName', - '`AIFormAssist` reaches no data source at all.', - ), - fields: retiredAiMember( - 'ai-form-assist', - 'fields', - 'each suggestion already names its own `fieldName`.', - ), - context: retiredAiInput( - 'ai-form-assist', - 'context', - 'pass the context to your host\'s own AI service when you ask it for suggestions.', - ), - config: retiredAiInput( - 'ai-form-assist', - 'config', - '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 `suggestions`.', - ), - suggestions: z.array(AIFieldSuggestionSchema).optional().describe('Current suggestions'), - autoFill: retiredAiMember( - 'ai-form-assist', - 'autoFill', - 'the component destructured it and never used the binding; apply suggestions through `onApply`.', - ), - showConfidence: z.boolean().optional().describe('Show confidence scores for suggestions'), - showReasoning: z.boolean().optional().describe('Show reasoning for suggestions'), - onApplySuggestion: handlerKeyRefusal('onApplySuggestion', 'retired', 'Callback when a suggestion is applied'), - onRejectSuggestion: handlerKeyRefusal('onRejectSuggestion', 'retired', 'Callback when a suggestion is rejected'), - body: retirementTombstone(neitherChannel('ai-form-assist', '`showConfidence`, `showReasoning`, `suggestions`')), - children: retirementTombstone(neitherChannel('ai-form-assist', '`showConfidence`, `showReasoning`, `suggestions`')), -}); +/** @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921); accepts nothing. Remove the reference. */ +export const AIConfigSchema = retiredExport('AIConfigSchema'); -/** - * AI Recommendation Item — mirrors `AIRecommendationItem` (`../ai.ts`). - */ -export const AIRecommendationItemSchema = z.looseObject({ - id: z.string().describe('Unique identifier'), - title: z.string().describe('Recommendation title'), - description: z.string().optional().describe('Recommendation description'), - score: z.number().describe('Relevance score (0-1)'), - category: z.string().optional().describe('Recommendation category'), - metadata: z.record(z.string(), z.any()).optional().describe('Additional metadata'), - action: z - .looseObject({ - type: z.string().describe('Action type'), - target: z.string().optional().describe('Action target'), - }) - .optional() - .describe('Action to perform when selected'), -}); +/** @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921); accepts nothing. Remove the reference. */ +export const AIFieldSuggestionSchema = retiredExport('AIFieldSuggestionSchema'); /** - * AI Recommendations Schema — mirrors `AIRecommendationsSchema` (`../ai.ts`). + * @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921): + * `ai-form-assist` is no longer a node type; accepts nothing. Remove the node + * from the page. */ -export const AIRecommendationsSchema = BaseSchema.extend({ - type: z.literal('ai-recommendations'), - objectName: retiredAiMember( - 'ai-recommendations', - 'objectName', - '`AIRecommendations` renders the `recommendations` it is handed and fetches nothing.', - ), - context: retiredAiInput( - 'ai-recommendations', - 'context', - 'pass the context to your host\'s own AI service when you ask it for recommendations.', - ), - config: retiredAiInput( - 'ai-recommendations', - 'config', - '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 `recommendations`.', - ), - recommendations: z.array(AIRecommendationItemSchema).optional().describe('Current recommendations'), - maxResults: retiredAiMember( - 'ai-recommendations', - 'maxResults', - '`AIRecommendations` renders EVERY item in `recommendations`; cap the array before handing it over.', - ), - showScores: z.boolean().optional().describe('Show relevance scores'), - // `'carousel'` left this union with objectui#10330 (never implemented), so - // the two layouts the renderer draws are the whole vocabulary. - layout: z.enum(['list', 'grid']).optional().describe('Display layout'), - onSelect: handlerKeyRefusal('onSelect', 'runtime-slot', 'Callback when a recommendation is selected'), - onDismiss: handlerKeyRefusal('onDismiss', 'runtime-slot', 'Callback when a recommendation is dismissed'), - loading: z.boolean().optional().describe('Loading state'), - emptyMessage: z.string().optional().describe('Message to display when no recommendations are available'), - body: retirementTombstone( - neitherChannel('ai-recommendations', '`emptyMessage`, `layout`, `loading`, `recommendations`, `showScores`'), - ), - children: retirementTombstone( - neitherChannel('ai-recommendations', '`emptyMessage`, `layout`, `loading`, `recommendations`, `showScores`'), - ), -}); +export const AIFormAssistSchema = retiredExport('AIFormAssistSchema'); -/** - * Natural Language Query Result — mirrors `NLQueryResult` (`../ai.ts`). - */ -export const NLQueryResultSchema = z.looseObject({ - query: z.string().describe('Original query string'), - parsedQuery: z.record(z.string(), z.any()).optional().describe('Parsed query representation'), - data: z.array(z.any()).optional().describe('Result data'), - columns: z - .array( - z.looseObject({ - name: z.string().describe('Column name'), - label: z.string().optional().describe('Display label'), - type: z.string().optional().describe('Column data type'), - }), - ) - .optional() - .describe('Column definitions for the result data'), - summary: z.string().optional().describe('AI-generated summary of the results'), - confidence: z.number().optional().describe('Confidence score for the query interpretation (0-1)'), -}); +/** @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921); accepts nothing. Remove the reference. */ +export const AIRecommendationItemSchema = retiredExport('AIRecommendationItemSchema'); /** - * Natural Language Query Schema — mirrors `NLQuerySchema` (`../ai.ts`). + * @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921): + * `ai-recommendations` is no longer a node type; accepts nothing. Remove the + * node from the page. */ -export const NLQuerySchema = BaseSchema.extend({ - type: z.literal('nl-query'), - objectName: retiredAiMember( - 'nl-query', - 'objectName', - '`NLQueryInput` collects a query string and hands it to `onSubmit`; scope the query in the host that answers it.', - ), - placeholder: z.string().optional().describe('Input placeholder text'), - config: retiredAiInput( - 'nl-query', - 'config', - 'configure the model in your host\'s own AI service (ObjectUI has no AI-provider API; these components ' - + 'call no model), and hand its answer to the node as `result`.', - ), - result: NLQueryResultSchema.optional().describe('Current query result'), - suggestions: z.array(z.string()).optional().describe('Example queries to suggest'), - showHistory: z.boolean().optional().describe('Show query history'), - history: z - .array( - z.looseObject({ - query: z.string().describe('Query string'), - timestamp: z.string().describe('Timestamp of the query'), - }), - ) - .optional() - .describe('Query history entries'), - loading: z.boolean().optional().describe('Loading state'), - onSubmit: handlerKeyRefusal('onSubmit', 'runtime-slot', 'Callback when a query is submitted'), - body: retirementTombstone( - neitherChannel('nl-query', '`history`, `loading`, `placeholder`, `result`, `showHistory`, `suggestions`'), - ), - children: retirementTombstone( - neitherChannel('nl-query', '`history`, `loading`, `placeholder`, `result`, `showHistory`, `suggestions`'), - ), -}); +export const AIRecommendationsSchema = retiredExport('AIRecommendationsSchema'); + +/** @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921); accepts nothing. Remove the reference. */ +export const NLQueryResultSchema = retiredExport('NLQueryResultSchema'); /** - * Union of the AI component arms — the category member `AnyComponentSchema` - * lists (objectui#10859). + * @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921): + * `nl-query` is no longer a node type; accepts nothing. Remove the node from + * the page. */ -const AIComponentSchemaInferred = z.discriminatedUnion('type', [ - AIFormAssistSchema, - AIRecommendationsSchema, - NLQuerySchema, -]); +export const NLQuerySchema = retiredExport('NLQuerySchema'); /** - * The TYPE of {@link AIComponentSchema}, NAMED so declaration emit prints it by - * reference (objectui#11573): see "Why every category union's TYPE is named" - * on `AnyComponentSchema` (`index.zod.ts`). It adds no member. + * @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921). It was + * the category union of the three arms; `AnyComponentSchema` now lists their + * refusal arms instead. Accepts nothing. Remove the reference. */ -export interface AIComponentZodType extends AIComponentSchemaInferredType { - options: AIComponentSchemaInferredType['options']; -} -type AIComponentSchemaInferredType = typeof AIComponentSchemaInferred; +export const AIComponentSchema = retiredExport('AIComponentSchema'); -/** The union above, typed by its named {@link AIComponentZodType}. */ -export const AIComponentSchema: AIComponentZodType = AIComponentSchemaInferred; +/** @deprecated RETIRED with `@object-ui/plugin-ai` (objectstack#25921). Remove the reference. */ +export type AIComponentZodType = typeof AIComponentSchema; diff --git a/ui/packages/types/src/zod/index.zod.ts b/ui/packages/types/src/zod/index.zod.ts index 3f618a2080c..47822d33d19 100644 --- a/ui/packages/types/src/zod/index.zod.ts +++ b/ui/packages/types/src/zod/index.zod.ts @@ -441,8 +441,13 @@ export { } from './views.zod.js'; // ============================================================================ -// AI Components - `@object-ui/plugin-ai` (objectui#10859) +// AI Components — RETIRED with `@object-ui/plugin-ai` (objectstack#25921) // ============================================================================ +// Every name below is a `@deprecated` `z.never()` tombstone that accepts +// nothing; it leaves this block under objectstack#25912's sunset rule. The three +// literals' refusal arms (`RetiredAIFormAssistNodeSchema` and its siblings) take +// the arms' place inside `AnyComponentSchema` and are deliberately NOT exported — +// nothing outside this package parses against a refusal arm. export { AIConfigSchema, AIFieldSuggestionSchema, @@ -574,7 +579,7 @@ import { ObjectQLComponentSchema, ObjectQLPublicBlockComponentSchema } from './o import { CRUDComponentSchema } from './crud.zod.js'; import { ReportUnionSchema } from './reports.zod.js'; import { ViewComponentSchema } from './views.zod.js'; -import { AIComponentSchema } from './ai.zod.js'; +import { RetiredAIFormAssistNodeSchema, RetiredAIRecommendationsNodeSchema, RetiredNLQueryNodeSchema } from './ai.zod.js'; import { ProductBlockComponentSchema } from './product-blocks.zod.js'; import { DesignerUnionSchema } from './designer.zod.js'; import { PublicBlockComponentSchema } from './public-blocks.zod.js'; @@ -681,7 +686,10 @@ export const AnyComponentSchema = defineNodeComponentUnion(z.discriminatedUnion( CRUDComponentSchema, ReportUnionSchema, ViewComponentSchema, - AIComponentSchema, + // objectstack#25921 — the three retired `@object-ui/plugin-ai` literals' named refusals. + RetiredAIFormAssistNodeSchema, + RetiredAIRecommendationsNodeSchema, + RetiredNLQueryNodeSchema, DesignerUnionSchema, PublicBlockComponentSchema, // objectui#12954 — the retired `cloud:plan-status` literal's named refusal. From 22dc89f31f4cf9e2a0039b04362ac023fba57253 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 22:23:36 +0000 Subject: [PATCH 04/13] test: follow the plugin-ai retirement in the cli ratchet, markdown inputs and the types pins (WIP) Claude-Session: https://claude.ai/code/session_01VBDBR9oS1MNUcjBVXds5D6 Co-authored-by: Claude --- ...tered-types-validate-ratchet-10859.test.ts | 44 +-- .../cli/src/utils/known-schema-types.ts | 3 - .../ai-insights-retired-8800.test.ts | 40 ++- .../ai-node-faces-agree-10874.test.ts | 262 --------------- .../ai-zero-read-members-retired-8178.test.ts | 276 ---------------- .../src/__tests__/ai-zod-arms-10859.test.ts | 308 ------------------ ui/scripts/markdown-test-inputs.mjs | 31 +- 7 files changed, 45 insertions(+), 919 deletions(-) delete mode 100644 ui/packages/types/src/__tests__/ai-node-faces-agree-10874.test.ts delete mode 100644 ui/packages/types/src/__tests__/ai-zero-read-members-retired-8178.test.ts delete mode 100644 ui/packages/types/src/__tests__/ai-zod-arms-10859.test.ts diff --git a/ui/packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts b/ui/packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts index b24788d6531..0840084fa4f 100644 --- a/ui/packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts +++ b/ui/packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts @@ -16,8 +16,8 @@ * (`validateAuthoredDocument`, objectui#5250), whose root is the strict twin of * `AnyComponentSchema` — a union discriminated on `type`. A registered type * with no arm in that union is refused with one `invalid_union` issue at - * `['type']`, whatever else the document says: `ai-form-assist`, which - * `packages/plugin-ai/README.md` teaches, was one of them. The mirror-parity + * `['type']`, whatever else the document says: `ai-form-assist`, which the + * since-retired `@object-ui/plugin-ai` README taught, was one of them. The mirror-parity * ratchet in `@object-ui/types` counts declaration↔mirror PAIRS and the keys * inside them; a registered type with no mirror at all is in neither, so * nothing counted this family until this file. @@ -379,7 +379,7 @@ describe('registered component types refused at `type` — a ratchet (objectui#1 // was 200 until objectui#10859 batch 8 unregistered thirty bare keys.) expect(BARE_KEYS.length).toBeGreaterThan(150); expect(BARE_KEYS).toContain('timeline'); - expect(BARE_KEYS).toContain('ai-form-assist'); + expect(BARE_KEYS).toContain('pivot'); }); 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 expect(refusedAtType('no-such-component-10859')).toBe(true); }); - it('counts the three plugin-ai keys as armed (objectui#10859 batch 1)', () => { + it('refuses the three plugin-ai keys BY NAME at `type` — the package retired and their arms became refusals (objectstack#25921)', () => { + // objectui#10859 batch 1 armed them; the registrations left with + // `@object-ui/plugin-ai`, so registration and arm leaving in one change move + // neither count. Each arm stays as `retiredNodeType()`'s refusal, the + // `cloud:plan-status` shape below. for (const key of ['ai-form-assist', 'ai-recommendations', 'nl-query']) { - expect(BARE_KEYS, key).toContain(key); + expect(BARE_KEYS, key).not.toContain(key); + const result = validateAuthoredDocument({ type: key }); + expect(result.success, key).toBe(false); + const named = (result.success ? [] : result.error.issues).filter( + (issue) => issue.code === 'custom' && issue.path.length === 1 && issue.path[0] === 'type', + ); + expect(named, key).toHaveLength(1); + expect(named[0]?.message, key).toContain('@object-ui/plugin-ai'); expect(refusedAtType(key), key).toBe(false); } + // Control: an unknown bare literal still takes the generic path. + expect(refusedAtType('ai-no-such-node-25921')).toBe(true); }); 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 /** Rooted on this file, never on `process.cwd()`. */ const HERE = dirname(fileURLToPath(import.meta.url)); -const PLUGIN_AI_README = join(HERE, '..', '..', '..', 'plugin-ai', 'README.md'); const QUICK_START = join(HERE, '..', '..', '..', '..', 'content', 'docs', 'guide', 'quick-start.md'); /** See `validate-root-path-line.test.ts` — the escape byte is never spelled. */ @@ -750,26 +762,6 @@ afterEach(() => { rmSync(dir, { recursive: true, force: true }); }); -describe('objectui validate — the plugin-ai README document (objectui#10859)', () => { - it('validates the "Schema-Driven Usage" `ai-form-assist` document', async () => { - const readme = readFileSync(PLUGIN_AI_README, 'utf8'); - const section = readme.slice(readme.indexOf('## Schema-Driven Usage')); - const fence = /```json\n([\s\S]*?)\n```/.exec(section); - expect(fence, 'no ```json fence under "## Schema-Driven Usage"').not.toBeNull(); - const file = join(dir, 'ai-form-assist.json'); - writeFileSync(file, (fence as RegExpExecArray)[1], 'utf-8'); - // Lit control on the extraction: it is the taught node. - expect(JSON.parse((fence as RegExpExecArray)[1]).type).toBe('ai-form-assist'); - - await validate(file); - - const text = out.join('\n').replace(ANSI, ''); - expect(text).not.toContain('Schema validation failed'); - expect(text).toContain('Schema is valid'); - expect(exitCodes).toEqual([0]); - }); -}); - /* ── End to end: a page of public blocks through `objectui validate` ─────── */ describe('objectui validate — a page built from ADR-0080 public blocks (objectui#10872)', () => { diff --git a/ui/packages/cli/src/utils/known-schema-types.ts b/ui/packages/cli/src/utils/known-schema-types.ts index 1fbf40cbe0b..2c7c0a3c3bf 100644 --- a/ui/packages/cli/src/utils/known-schema-types.ts +++ b/ui/packages/cli/src/utils/known-schema-types.ts @@ -39,8 +39,6 @@ export const KNOWN_SCHEMA_TYPES: readonly string[] = [ 'action:icon', 'action:menu', 'address', - 'ai-form-assist', - 'ai-recommendations', 'ai:feedback', 'ai:input', 'ai:suggestion', @@ -222,7 +220,6 @@ export const KNOWN_SCHEMA_TYPES: readonly string[] = [ 'nav:breadcrumb', 'nav:menu', 'navigation-menu', - 'nl-query', 'object-calendar', 'object-chart', 'object-data-table', diff --git a/ui/packages/types/src/__tests__/ai-insights-retired-8800.test.ts b/ui/packages/types/src/__tests__/ai-insights-retired-8800.test.ts index 7d41e085c03..3688f3b756e 100644 --- a/ui/packages/types/src/__tests__/ai-insights-retired-8800.test.ts +++ b/ui/packages/types/src/__tests__/ai-insights-retired-8800.test.ts @@ -118,21 +118,23 @@ describe('objectui#8800 — `ai-insights` stays retired (type face)', () => { // declaration is caught even by a run that never type-checks. const source = readFileSync(DECLARATION_PATH, 'utf8'); - expect(/^export interface AIInsightsSchema\b/m.test(source)).toBe(false); - expect(/^\s*type: 'ai-insights';/m.test(source)).toBe(false); - - // ⭐ LIT CONTROLS — the SAME two matchers, one sibling over, still fire. The - // three AI schemas this file keeps are registered and rendered by - // `@object-ui/plugin-ai`; only the fourth was retired. Without these, both - // absences above would also be satisfied by a truncated read or a matcher - // that can never match. - expect(/^export interface AIRecommendationsSchema\b/m.test(source)).toBe(true); - expect(/^\s*type: 'ai-recommendations';/m.test(source)).toBe(true); - - // The tombstone itself must stay: it is where the ruling, the boundary - // against the three live schemas, and the in-repo limit are recorded. A - // retirement whose reasoning is deleted is re-litigated by the next sweep. - expect(source).toContain('`AIInsightsSchema` (`type: \'ai-insights\'`) — RETIRED'); + const declares = (name: string) => new RegExp(`^export (?:interface|type) ${name}\\b`, 'm'); + const literal = /^\s*type: 'ai-insights';/m; + expect(declares('AIInsightsSchema').test(source)).toBe(false); + expect(literal.test(source)).toBe(false); + + // ⭐ LIT CONTROLS — the SAME two matchers still fire. The three sibling names + // stay declared in this file (as `never` tombstones since `@object-ui/plugin-ai` + // retired, objectstack#25921), so the declaration matcher finds one; no + // node literal is declared here any more, so the literal matcher is lit on + // the line it was written to catch. Without these, both absences above would + // also be satisfied by a truncated read or a matcher that can never match. + expect(declares('AIRecommendationsSchema').test(source)).toBe(true); + expect(literal.test(" type: 'ai-insights';")).toBe(true); + + // The module keeps pointing at this retirement, so the next sweep finds the + // ruling instead of re-litigating it. + expect(source).toContain('objectui#8800'); }); it('the published BARREL no longer re-exports the symbol', () => { @@ -162,9 +164,6 @@ describe('objectui#8800 — nothing in this TRACKED tree carries either spelling * a tombstone comment that names the symbol on purpose; a scan reddening on * them would be asserting the retirement had not landed. That they no longer * DECLARE it is asserted above, by matchers shaped for a declaration. - * - `ai-zero-read-members-retired-8178.test.ts` — objectui#8178's pin, whose - * exclusion paragraph was rewritten BY THIS RULING and names the symbol to - * record what it used to hold. * - this pin, which must write both spellings to probe for them. */ const EXCLUDED = [ @@ -172,7 +171,6 @@ describe('objectui#8800 — nothing in this TRACKED tree carries either spelling ':!.changeset/', ':!packages/types/src/ai.ts', ':!packages/types/src/index.ts', - ':!packages/types/src/__tests__/ai-zero-read-members-retired-8178.test.ts', ':!packages/types/src/__tests__/ai-insights-retired-8800.test.ts', ]; @@ -201,7 +199,7 @@ describe('objectui#8800 — nothing in this TRACKED tree carries either spelling expect(grepTree('ai-insights')).toEqual([]); }); - it('LIT CONTROLS — the same probe finds the three siblings that DO render', () => { + it('LIT CONTROLS — the same probe finds the three siblings (tombstoned since objectstack#25921)', () => { // Without these, both zeros above would also be produced by a broken // `git grep` invocation, a wrong cwd, or an exclusion list that swallowed // the tree — and a swallowed tree renders as a successful retirement. @@ -242,7 +240,7 @@ describe('objectui#8800 — PREMISE: the zod face never accepted the spelling', expect('AIInsightsSchema' in ZodFace).toBe(false); // ⭐ LIT CONTROLS — the same probes find a sibling the mirror DOES carry, so // the absences above are readings and not a broken path join or namespace. - expect(source).toMatch(/z\.literal\('ai-recommendations'\)/); + expect(source).toMatch(/retiredNodeType\('ai-recommendations'/); expect('AIRecommendationsSchema' in ZodFace).toBe(true); }); }); diff --git a/ui/packages/types/src/__tests__/ai-node-faces-agree-10874.test.ts b/ui/packages/types/src/__tests__/ai-node-faces-agree-10874.test.ts deleted file mode 100644 index e6421418e52..00000000000 --- a/ui/packages/types/src/__tests__/ai-node-faces-agree-10874.test.ts +++ /dev/null @@ -1,262 +0,0 @@ -/** - * ObjectUI - * Copyright (c) 2024-present ObjectStack Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -/** - * The three `@object-ui/plugin-ai` node declarations stop offering members no - * runtime honours, and type the handler slots their components call - * (objectui#10874). - * - * ## The defect these pin - * - * `AIFormAssistSchema`, `AIRecommendationsSchema` and `NLQuerySchema` (`../ai.ts`) - * offered an author two kinds of member that nothing honoured: - * - * - five `on*` members typed `string` — `onApplySuggestion`, - * `onRejectSuggestion`, `onSelect`, `onDismiss`, `onSubmit` — the - * handler-expression dialect objectui#6182 withdrew on either face. The zod - * arms objectui#10859 wrote refuse all five by name (`handlerKeyRefusal`), so - * `onSelect: 'x'` passed `tsc` and was refused by `safeValidateSchema`. For - * the three a component CALLS (`onSelect`, `onDismiss`, `onSubmit` — the - * registration is the raw component and `SchemaRenderer` spreads the node's - * keys onto its props) that string was called as a function and threw; - * - `config` (all three) and `context` (two of them), read by nothing in - * `@object-ui/plugin-ai` — the components are presentation only and call no - * model — and accepted by BOTH faces, so a `config: { … }` passed `tsc`, - * parsed green and changed nothing. - * - * The ruling on objectui#10874 settles it per key, by the objectui#6124 rule - * ("a function type only where a runtime consumer reads the key as a function, - * else `?: never`", as `handler-keys-string-any-mirrors-7344.test.ts` quotes - * it): - * - * - `onSelect` / `onDismiss` / `onSubmit` are RUNTIME SLOTS: the TypeScript - * twin declares the callable the component invokes, and the zod face keeps - * refusing an authored value by name — the objectui#6124 shape; - * - `onApplySuggestion` / `onRejectSuggestion` have no reader: `?: never`; - * - `config` / `context` retire on both faces, with a refusal that says what - * to do instead — configure the model in the host's own AI service - * (ObjectUI has no AI-provider API) and hand its output to the node. - * - * ## Two instruments, and which half each one reads - * - * The `Expect` / `Equal` aliases and the `@ts-expect-error` directives are - * TYPE-level: `tsc -p tsconfig.test.json` (the third leg of this package's - * `type-check` script) reads them, and a re-widened member fails there — on an - * unused directive or on a `false` row. vitest strips types and reads none of - * it. The `safeValidateSchema` / `StrictAnyComponentSchema` rows are RUNTIME and - * vitest reads them. A green run of either one alone says nothing about the - * other. - * - * The runtime refusal of the five `on*` members is not re-pinned here: it - * predates this card and is `ai-zod-arms-10859.test.ts`'s, beside this file, - * whose `assertionHandlerFacesSettled` pins both faces of all five together. - */ - -import { describe, expect, it } from 'vitest'; -import type { z } from 'zod'; - -import { - AIFormAssistSchema, - AIRecommendationsSchema, - NLQuerySchema, - StrictAnyComponentSchema, - safeValidateSchema, -} from '../zod/index.zod.js'; -import type { - AIFieldSuggestion, - AIFormAssistSchema as Ts_AIFormAssistSchema, - AIRecommendationItem, - AIRecommendationsSchema as Ts_AIRecommendationsSchema, - NLQueryResult, - NLQuerySchema as Ts_NLQuerySchema, -} from '../ai'; - -/* ── Type-level pins: the `tsc` channel ────────────────────────────────────── */ - -/** Invariant equality — `extends` both ways would accept a narrowing. */ -type Equal = - (() => T extends A ? 1 : 2) extends (() => T extends B ? 1 : 2) ? true : false; -type Expect = T; - -type ShapeOf = M extends { shape: infer S } ? S : never; -type InputOf = T extends z.ZodType ? z.input : never; - -/** - * Each retired member READS as `undefined` on the TypeScript face — `?: never` - * without `exactOptionalPropertyTypes` is `never | undefined`, which collapses. - * `Equal` separates that from the `string` the two retired `on*` members - * carried, from the `AIConfig` / record types `config` / `context` carried, and - * from the `any` a DELETION would leave on this `BaseSchema`-derived carrier. - */ -export type assertionRetiredMembersReadAsTombstones = [ - Expect>, - Expect>, - Expect>, - Expect>, - Expect>, - Expect>, - Expect>, -]; - -/** - * The three RUNTIME SLOTS declare the callable their component invokes — the - * signature of `AIRecommendationsProps.onSelect` / `.onDismiss` and - * `NLQueryInputProps.onSubmit` — not the `string` they carried, and not a - * tombstone: a function on the node IS called at run time. - */ -export type assertionRuntimeSlotsAreCallable = [ - Expect void) | undefined>>, - Expect void) | undefined>>, - Expect void) | undefined>>, -]; - -/** The zod face agrees for `config` / `context`: each arm accepts nothing but absence. */ -export type assertionZodFaceRefusesConfigAndContext = [ - Expect['config']>, undefined>>, - Expect['context']>, undefined>>, - Expect['config']>, undefined>>, - Expect['context']>, undefined>>, - Expect['config']>, undefined>>, -]; - -/** - * The non-vacuity twin: members the components DO read keep their real types on - * both faces, so the rows above measure those members and not a file that - * stopped resolving. - */ -export type assertionLiveMembersKeepTheirTypes = [ - Expect>, - Expect>, - Expect>, - Expect['placeholder']>, string | undefined>>, -]; - -/* ── The `tsc` half on a fresh literal and on a widened value ─────────────── */ - -const suggestions: AIFieldSuggestion[] = [{ fieldName: 'company', value: 'ObjectStack Inc.', confidence: 0.92 }]; -const recommendations: AIRecommendationItem[] = [{ id: 'r1', title: 'Renew', score: 0.8 }]; - -describe('authoring a retired AI member, or a string on a runtime slot, is a `tsc` error (objectui#10874)', () => { - it('refuses all four on `ai-form-assist` — presence is the error, not the value', () => { - const node: Ts_AIFormAssistSchema = { - type: 'ai-form-assist', - suggestions, - // @ts-expect-error `config` is retired (objectui#10874) — configure the model in the host's AI service - config: { provider: 'openai', model: 'gpt-4' }, - // @ts-expect-error `context` is retired (objectui#10874) — pass it to the host's AI service - context: { record: 'r1' }, - // @ts-expect-error `onApplySuggestion` is retired (objectui#10874) — pass `onApply` as a component prop - onApplySuggestion: 'apply', - // @ts-expect-error `onRejectSuggestion` is retired (objectui#10874) — nothing reads it - onRejectSuggestion: 'reject', - }; - expect(node.type).toBe('ai-form-assist'); - }); - - it('refuses `config` / `context`, and a STRING on either runtime slot, on `ai-recommendations`', () => { - const node: Ts_AIRecommendationsSchema = { - type: 'ai-recommendations', - recommendations, - // @ts-expect-error `config` is retired (objectui#10874) — configure the model in the host's AI service - config: { provider: 'anthropic' }, - // @ts-expect-error `context` is retired (objectui#10874) — pass it to the host's AI service - context: { segment: 'smb' }, - // @ts-expect-error `onSelect` is a RUNTIME SLOT (objectui#10874) — a string is not the callable it invokes - onSelect: 'select', - // @ts-expect-error `onDismiss` is a RUNTIME SLOT (objectui#10874) — a string is not the callable it invokes - onDismiss: 'dismiss', - }; - expect(node.type).toBe('ai-recommendations'); - }); - - it('refuses `config`, and a STRING on the runtime slot, on `nl-query`', () => { - const node: Ts_NLQuerySchema = { - type: 'nl-query', - placeholder: 'Ask anything', - // @ts-expect-error `config` is retired (objectui#10874) — configure the model in the host's AI service - config: { temperature: 0.2 }, - // @ts-expect-error `onSubmit` is a RUNTIME SLOT (objectui#10874) — a string is not the callable it invokes - onSubmit: 'submit', - }; - expect(node.type).toBe('nl-query'); - }); - - it('ACCEPTS a function on each runtime slot — the control that the slots are callable, not tombstones', () => { - const picked: string[] = []; - const recs: Ts_AIRecommendationsSchema = { - type: 'ai-recommendations', - recommendations, - onSelect: (item) => picked.push(`select:${item.id}`), - onDismiss: (item) => picked.push(`dismiss:${item.id}`), - }; - const query: Ts_NLQuerySchema = { type: 'nl-query', onSubmit: (text) => picked.push(`submit:${text}`) }; - recs.onSelect?.(recommendations[0]); - recs.onDismiss?.(recommendations[0]); - query.onSubmit?.('top accounts'); - expect(picked).toEqual(['select:r1', 'dismiss:r1', 'submit:top accounts']); - }); - - it('refuses a WIDENED value too — the shape no excess-property check reaches', () => { - const rawAssist = { type: 'ai-form-assist' as const, suggestions, config: { model: 'gpt-4' } }; - // @ts-expect-error `config` is retired (objectui#10874), reached through a widened value - const assist: Ts_AIFormAssistSchema = rawAssist; - const rawRecs = { type: 'ai-recommendations' as const, recommendations, onSelect: 'select' }; - // @ts-expect-error `onSelect` is a RUNTIME SLOT (objectui#10874): a widened string is still refused - const recs: Ts_AIRecommendationsSchema = rawRecs; - const rawQuery = { type: 'nl-query' as const, onSubmit: 'submit' }; - // @ts-expect-error `onSubmit` is a RUNTIME SLOT (objectui#10874): a widened string is still refused - const query: Ts_NLQuerySchema = rawQuery; - expect([assist.type, recs.type, query.type]).toEqual(['ai-form-assist', 'ai-recommendations', 'nl-query']); - }); -}); - -/* ── The runtime half: `config` / `context` refused by name, with the prescription ── */ - -describe('the zod face refuses `config` / `context` by name (objectui#10874)', () => { - const CASES = [ - ['ai-form-assist', 'config', { provider: 'openai', model: 'gpt-4' }], - ['ai-form-assist', 'context', { record: 'r1' }], - ['ai-recommendations', 'config', { provider: 'anthropic' }], - ['ai-recommendations', 'context', { segment: 'smb' }], - ['nl-query', 'config', { temperature: 0.2 }], - ] as const; - - it.each(CASES)('%s refuses `%s` on the rendering face', (type, key, value) => { - const result = safeValidateSchema({ type, [key]: value }); - expect(result.success).toBe(false); - if (result.success) return; - const [issue] = result.error.issues; - expect(issue.code).toBe('invalid_type'); - expect(issue.path).toEqual([key]); - // The refusal names the key and carries the prescription — the host's own - // AI service — which is the half a bare `z.never()` would drop. - expect(issue.message).toContain(`\`${key}\``); - expect(issue.message).toContain('objectui#10874'); - expect(issue.message).toContain("host's own AI service"); - }); - - it.each(CASES)('%s refuses `%s` on the strict authoring face', (type, key, value) => { - const result = StrictAnyComponentSchema.safeParse({ type, [key]: value }); - expect(result.success).toBe(false); - if (result.success) return; - expect(result.error.issues.map((i) => i.path[0])).toContain(key); - }); - - it('accepts each node WITHOUT them, on both faces — the control', () => { - const docs = [ - { type: 'ai-form-assist', suggestions, showConfidence: true }, - { type: 'ai-recommendations', recommendations, layout: 'grid' }, - { type: 'nl-query', placeholder: 'Ask anything', showHistory: false }, - ]; - for (const doc of docs) { - const loose = safeValidateSchema(doc); - expect(loose.success, JSON.stringify(loose.success ? null : loose.error.issues)).toBe(true); - expect(StrictAnyComponentSchema.safeParse(doc).success, doc.type).toBe(true); - } - }); -}); diff --git a/ui/packages/types/src/__tests__/ai-zero-read-members-retired-8178.test.ts b/ui/packages/types/src/__tests__/ai-zero-read-members-retired-8178.test.ts deleted file mode 100644 index 41c68814be8..00000000000 --- a/ui/packages/types/src/__tests__/ai-zero-read-members-retired-8178.test.ts +++ /dev/null @@ -1,276 +0,0 @@ -/** - * ObjectUI - * Copyright (c) 2024-present ObjectStack Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -/** - * The seven zero-read members of the three AI schemas are ADR-0049 RETIREMENT - * TOMBSTONES (objectui#8178, director decision batch #78, 2026-09-07, - * maintainer verbatim 「同意」), and their refusal is TYPE-LEVEL ONLY. - * - * ## The seven, and what was measured on each - * - * `AIFormAssistSchema.formId` / `.objectName` / `.fields` / `.autoFill`, - * `AIRecommendationsSchema.objectName` / `.maxResults`, `NLQuerySchema.objectName`. - * Each was declared by this file's neighbour `../ai`, five of the names were - * offered as designer `inputs` by `@object-ui/plugin-ai`'s registrations - * (seven entries across the three), all seven were taught by that package's - * README — and no component read any of them. - * - * The census was re-derived on this branch's base rather than inherited, per - * key, with a LIT CONTROL beside every zero so a nil reading cannot be a broken - * matcher (`git grep -c -w KEY` over the component that each schema drives): - * - * | schema | zero reads | lit control | - * |--------------------|-----------------------------------------|------------------------------------------------| - * | `AIFormAssist` | `formId` 0, `objectName` 0, `fields` 0 | `suggestions` 9, `showConfidence` 2 | - * | `AIFormAssist` | `autoFill` 1 — the destructure, unused | `showReasoning` 2 (destructure PLUS a read) | - * | `AIRecommendations`| `objectName` 0, `maxResults` 0 | `recommendations` 8, `showScores` 2, `layout` 2| - * | `NLQueryInput` | `objectName` 0 | `placeholder` 2, `result` 14, `history` 3 | - * - * Two channels that could have consumed a key WITHOUT naming it were measured - * too, because "no `schema.KEY` read site" does not imply inert (objectui#8410): - * `@object-ui/plugin-ai` has zero `{...props}` / `{...rest}` spreads in all - * four of its sources (control: `components/src/renderers/disclosure/collapsible.tsx` - * matches the same two patterns once), and zero dynamic `schema[…]` access - * (control: four files under `packages/` do use that form). The shared - * record-source ladder that reads `schema.objectName` structurally for the - * grid/tree/map/calendar/gantt blocks is not reachable either — none of its - * call sites takes an AI schema, and this package imports nothing from - * `@object-ui/core` except `ComponentRegistry`. - * - * ## Why tombstones and not deletions — the carrier decides - * - * All three schemas extend {@link BaseSchema}, which carried - * `[key: string]: any` when this was written. On such a carrier a DELETED - * optional member was absorbed silently at any value: the index signature - * defeated excess-property checking on a fresh literal and the weak-type check - * on a widened one (since objectui#8347 a fresh literal is refused, and a - * widened value still rides through). Deletion would - * therefore have left exactly the silent no-op the retirement exists to end, - * and the ruling's first pin — *refused by the schema types (compile-time)* — - * would have been unsatisfiable. The routes are loud-vs-silent here, not - * louder-vs-quieter, which is the discriminator's carrier branch as corrected - * on objectui#7678; PRONG 2 licenses it independently, since - * `packages/plugin-ai/README.md` taught every one of the seven as working. - * - * The `@ts-expect-error` directives below are REAL enforcement: this package - * type-checks its tests through `tsconfig.test.json`, which its `type-check` - * script chains, so re-widening any of the seven fails on the now-unused - * directive. A green `vitest` run says nothing about them — type assertions are - * erased before it runs. The "deleted" row is pinned LIVE, as an undeclared key - * carrying no directive, so the contrast cannot rot into prose. - * - * ⛔ Not Enforce. Implementing reads nobody asked for is capability growth - * without pull; an AI backend that later needs `objectName` / `fields` as - * context is a feature card with its own business case. - */ - -import { describe, it, expect } from 'vitest'; -import type { - AIFieldSuggestion, - AIFormAssistSchema, - AIRecommendationItem, - AIRecommendationsSchema, - NLQuerySchema, -} from '../ai'; - -/* ── type-level pins: the `tsc` channel ──────────────────────────────────── */ - -/** Invariant equality — `extends` both ways would accept a narrowing. */ -type Equal = - (() => T extends A ? 1 : 2) extends (() => T extends B ? 1 : 2) ? true : false; -type Expect = T; - -/** - * What each retired member READS as under the tombstone: `undefined`, not - * `never` — `?: never` without `exactOptionalPropertyTypes` is - * `never | undefined`, which collapses. `Equal` still separates that from the - * `any` a DELETION leaves behind on this carrier, so every row below reddens on - * a deletion as well as on a re-widening. - */ -export type assertionRetiredMembersReadAsTombstones = [ - Expect>, - Expect>, - Expect>, - Expect>, - Expect>, - Expect>, - Expect>, -]; - -/** - * The non-vacuity twin of the block above: keys the components DO read still - * carry their real types, so the seven rows are a measurement of those seven - * members and not of a file that stopped resolving. - */ -export type assertionLiveMembersKeepTheirTypes = [ - Expect>, - Expect>, - Expect>, -]; - -/** - * ⚠️ The `AIInsightsSchema.objectName` EXCLUSION that used to be pinned here is - * REWRITTEN BY RULING, and this paragraph is what it was rewritten into. - * - * What stood here was a type-level `Equal` holding that member at - * string-or-undefined, with the reason that objectui#8178's ruling named three - * schemas and that fourth one "was neither screened nor decided". Both halves of - * that reason have since been discharged: objectui#8800 screened it and the - * director seat decided it (decision batch #137 item 2, 2026-09-15, maintainer - * verbatim 「同意」, letter A), retiring `AIInsightsSchema` and the `ai-insights` - * node type WHOLE from the published type face under ADR-0049. The member is not - * reachable to be pinned, because the interface that declared it is gone. - * - * ⛔ That exclusion changed by RULING, ⛔ never by refactor — which is exactly what - * the note it replaces existed to enforce, and the authorisation is on - * objectui#8800 in those words. ⛔ It authorises this paragraph and nothing else - * in this file: the seven tombstones below are objectui#8178's, they are - * untouched, and no part of that retirement moves with this one. - * - * The successor pin — the symbol's absence, the spelling's absence, and the lit - * controls that keep both from passing vacuously — is - * `ai-insights-retired-8800.test.ts`, beside this file. - */ - -const suggestions: AIFieldSuggestion[] = [ - { fieldName: 'company', value: 'ObjectStack Inc.', confidence: 0.92 }, -]; -const recommendations: AIRecommendationItem[] = [{ id: 'r1', title: 'Renew', score: 0.8 }]; - -describe('the seven tombstones make authoring a `tsc` error, on a fresh literal', () => { - it('refuses all four retired `ai-form-assist` members — presence is the error, not the value', () => { - const node: AIFormAssistSchema = { - type: 'ai-form-assist', - suggestions, - // @ts-expect-error `formId` is a retirement tombstone (objectui#8178) — the host owns the form - formId: 'new-contact', - // @ts-expect-error `objectName` is a retirement tombstone (objectui#8178) — nothing read it - objectName: 'Contact', - // @ts-expect-error `fields` is a retirement tombstone (objectui#8178) — each suggestion names its own `fieldName` - fields: ['name', 'email'], - // @ts-expect-error `autoFill` is a retirement tombstone (objectui#8178) — apply suggestions through `onApply` - autoFill: true, - }; - expect(node.type).toBe('ai-form-assist'); - }); - - it('refuses both retired `ai-recommendations` members', () => { - const node: AIRecommendationsSchema = { - type: 'ai-recommendations', - recommendations, - // @ts-expect-error `objectName` is a retirement tombstone (objectui#8178) - objectName: 'Product', - // @ts-expect-error `maxResults` is a retirement tombstone (objectui#8178) — every item is rendered; slice before handing over - maxResults: 5, - }; - expect(node.type).toBe('ai-recommendations'); - }); - - it('refuses the retired `nl-query` member', () => { - const node: NLQuerySchema = { - type: 'nl-query', - placeholder: 'Ask anything...', - // @ts-expect-error `objectName` is a retirement tombstone (objectui#8178) — scope the query in the host that answers it - objectName: 'Order', - }; - expect(node.type).toBe('nl-query'); - }); - - it('refuses a WRONG-TYPED value too — the row that goes silent under deletion', () => { - const node: AIRecommendationsSchema = { - type: 'ai-recommendations', - recommendations, - // @ts-expect-error `maxResults` is a retirement tombstone (objectui#8178) - maxResults: 'five', - }; - expect(node.type).toBe('ai-recommendations'); - }); -}); - -describe('the tombstones survive the shape no excess-property check reaches', () => { - it('refuses a WIDENED value carrying a retired key, on each of the three schemas', () => { - const rawAssist = { type: 'ai-form-assist' as const, suggestions, formId: 'new-lead' }; - // @ts-expect-error `formId` is a retirement tombstone (objectui#8178), reached through a widened value - const assist: AIFormAssistSchema = rawAssist; - const rawRecs = { type: 'ai-recommendations' as const, recommendations, maxResults: 10 }; - // @ts-expect-error `maxResults` is a retirement tombstone (objectui#8178), reached through a widened value - const recs: AIRecommendationsSchema = rawRecs; - const rawQuery = { type: 'nl-query' as const, objectName: 'Order' }; - // @ts-expect-error `objectName` is a retirement tombstone (objectui#8178), reached through a widened value - const query: NLQuerySchema = rawQuery; - expect([assist.type, recs.type, query.type]).toEqual([ - 'ai-form-assist', - 'ai-recommendations', - 'nl-query', - ]); - }); -}); - -describe('the controls — without these, a broken import would satisfy every pin above', () => { - it('keeps every key the three components DO read writable', () => { - const assist: AIFormAssistSchema = { - type: 'ai-form-assist', - suggestions, - showConfidence: true, - showReasoning: false, - }; - const recs: AIRecommendationsSchema = { - type: 'ai-recommendations', - recommendations, - showScores: true, - layout: 'grid', - loading: false, - emptyMessage: 'Nothing yet', - }; - const query: NLQuerySchema = { - type: 'nl-query', - placeholder: 'Ask anything...', - suggestions: ['Recent orders'], - showHistory: true, - history: [{ query: 'Recent orders', timestamp: '2026-09-09T00:00:00.000Z' }], - }; - expect([assist.showConfidence, recs.layout, query.showHistory]).toEqual([true, 'grid', true]); - }); - - it('an UNDECLARED key is refused on a fresh literal, and still rides a widened value — the DELETED row, live', () => { - // This is where each of the seven would sit had it been deleted instead of - // tombstoned. Until objectui#8347 `BaseSchema`'s `[key: string]: any` - // absorbed it at any value in every shape, so a deletion produced no - // diagnostic at all. The signature is gone: a fresh literal is refused, - // with no name for the remedy, while a value that reached its annotation - // through a wider variable is still not re-checked (no excess-property - // check on a non-fresh object). The tombstone is what refuses both. - // @ts-expect-error — `AIFormAssistSchema` declares no `bogusUndeclared` (objectui#8347) - const fresh: AIFormAssistSchema = { type: 'ai-form-assist', suggestions, bogusUndeclared: 1 }; - const raw = { type: 'ai-recommendations' as const, recommendations, bogusUndeclared: 1 }; - const widened: AIRecommendationsSchema = raw; - expect([fresh.type, widened.type]).toEqual(['ai-form-assist', 'ai-recommendations']); - }); - - it('…and the same undeclared key IS refused on a carrier without an index signature — the instrument control', () => { - // `AIFieldSuggestion` and `AIRecommendationItem` declare no index - // signature, so the compiler's two ordinary guards fire here: excess-property - // checking on a fresh literal (TS2353) and the weak-type check on a - // lone-key widened value (TS2559). Their firing proved the silence above - // was the index signature and not a blind run; since objectui#8347 the only - // silence left above is the widened value's (no excess-property check on a - // non-fresh object). - const fresh: AIFieldSuggestion = { - fieldName: 'company', - value: 'ObjectStack Inc.', - confidence: 0.92, - // @ts-expect-error TS2353 — `AIFieldSuggestion` has no index signature, so a fresh undeclared key is refused - bogusUndeclared: 1, - }; - const loneKey = { bogusUndeclared: 1 }; - // @ts-expect-error TS2559 — the weak-type check fires on a lone-key widened value without an index signature - const widened: AIRecommendationItem = loneKey; - expect(fresh.fieldName).toBe('company'); - expect(widened).toBeDefined(); - }); -}); diff --git a/ui/packages/types/src/__tests__/ai-zod-arms-10859.test.ts b/ui/packages/types/src/__tests__/ai-zod-arms-10859.test.ts deleted file mode 100644 index 7c346d5834b..00000000000 --- a/ui/packages/types/src/__tests__/ai-zod-arms-10859.test.ts +++ /dev/null @@ -1,308 +0,0 @@ -/** - * ObjectUI - * Copyright (c) 2024-present ObjectStack Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -/** - * The three `@object-ui/plugin-ai` node types have a zod arm in - * `AnyComponentSchema` (objectui#10859, batch 1). - * - * ## The defect these pin - * - * `ai-form-assist`, `ai-recommendations` and `nl-query` are registered by - * `@object-ui/plugin-ai`, declared on the published TypeScript face - * (`../ai.ts`) and taught by that package's README — and `AnyComponentSchema` - * carried no arm for any of them, so `safeValidateSchema` refused every - * document naming one with `invalid_union` at `type`. The README's own - * "Schema-Driven Usage" document was refused, and `objectui validate` printed - * "Schema validation failed" for it. - * - * ## Two instruments, and which half each one reads - * - * The `Expect` / `Equal` block below is TYPE-level: `tsc -p tsconfig.test.json` - * (the third leg of this package's `type-check` script) reads it, and vitest — - * which strips types — does not. The `describe` blocks are RUNTIME and vitest - * reads them. A green run of either one alone says nothing about the other. - * - * ## What the parity half asserts - * - * For every member the TypeScript declaration declares, the arm's INPUT type - * for that member is invariantly equal to the declaration's — with exactly the - * objectui#6124 exception: a RUNTIME SLOT, whose TypeScript twin is the callable - * its component invokes while the zod face refuses an authored value by name. - * When this file was written every `on*` member was an exception of a different - * kind: the TypeScript face declared all five `string`, the handler-expression - * dialect objectui#6182 withdrew on EITHER face, and that divergence was pinned - * both ways so that a repair of the TypeScript face would redden this file. - * objectui#10874 made that repair per key — `?: never` for the two keys nothing - * reads, the callable for the three runtime slots — and the pins below record - * the settled shape. - */ - -import { readFileSync } from 'node:fs'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; - -import { describe, expect, it } from 'vitest'; -import { z } from 'zod'; -import type { WithoutCatchall } from './zod-catchall-type'; - -import { - AIConfigSchema, - AIFieldSuggestionSchema, - AIFormAssistSchema, - AIRecommendationItemSchema, - AIRecommendationsSchema, - NLQueryResultSchema, - NLQuerySchema, - StrictAnyComponentSchema, - safeValidateSchema, -} from '../zod/index.zod.js'; -import type { - AIConfig as Ts_AIConfig, - AIFieldSuggestion as Ts_AIFieldSuggestion, - AIFormAssistSchema as Ts_AIFormAssistSchema, - AIRecommendationItem as Ts_AIRecommendationItem, - AIRecommendationsSchema as Ts_AIRecommendationsSchema, - NLQueryResult as Ts_NLQueryResult, - NLQuerySchema as Ts_NLQuerySchema, -} from '../ai'; - -/* ── Type-level parity: the `tsc` channel ────────────────────────────────── */ - -/** Invariant equality — `extends` both ways would accept a narrowing. */ -type Equal = - (() => T extends A ? 1 : 2) extends (() => T extends B ? 1 : 2) ? true : false; -type Expect = T; - -/** The mirror's own shape. */ -type ShapeOf = M extends { shape: infer S } ? S : never; -/** What a shape entry ACCEPTS (input side, so `.optional()` shows), read without the - * `.passthrough()` catchall every mirror object carries since objectstack#25923 (`./zod-catchall-type`). */ -type InputOf = T extends z.ZodType ? WithoutCatchall> : never; -/** The declaration's DECLARED keys — any index signature dropped (`BaseSchema` carried one until objectui#8347). */ -type DeclaredKeys = Extract< - keyof { [K in keyof D as string extends K ? never : number extends K ? never : K]: D[K] }, - string ->; -/** The mirror's declared keys, read from its own shape. */ -type MirroredKeys = Extract, string>; - -/** Declared members whose accepted type differs from the declaration's. */ -type MismatchedKeys = { - [K in DeclaredKeys & MirroredKeys]: Equal[K]>, D[K]> extends true ? never : K; -}[DeclaredKeys & MirroredKeys]; - -/** Every arm and sub-schema declares exactly the declaration's key set — none added, none dropped. */ -export type assertionKeySetsAgree = [ - Expect, DeclaredKeys>>, - Expect, DeclaredKeys>>, - Expect, DeclaredKeys>>, - Expect, DeclaredKeys>>, - Expect, DeclaredKeys>>, - Expect, DeclaredKeys>>, - Expect, DeclaredKeys>>, -]; - -/** - * Every member's accepted type equals the declaration's — the four sub-schemas - * and the form-assist arm with no exception, the other two arms with exactly - * their objectui#6124 runtime slots excepted (objectui#10874). - */ -export type assertionMemberTypesAgree = [ - Expect, never>>, - Expect, never>>, - Expect, never>>, - Expect, never>>, - Expect, never>>, - Expect, 'onSelect' | 'onDismiss'>>, - Expect, 'onSubmit'>>, -]; - -/** - * The handler members, both halves — SETTLED per key since objectui#10874. This - * was `assertionHandlerDivergence`: the TypeScript face typed each `string` - * while the zod face accepted nothing but absence, and the first half was - * written to go red when the TypeScript face was repaired. objectui#10874 - * repaired it by the objectui#6124 rule: the two keys nothing reads are - * `?: never` (both faces refuse them), and the three RUNTIME SLOTS declare the - * callable their component invokes while the zod face keeps refusing an - * authored value. Re-widening either face — a `string` back on the TypeScript - * side, a tombstone on a slot a component calls, an accepting arm on the zod - * side — reddens a row. - */ -export type assertionHandlerFacesSettled = [ - Expect>, - Expect>, - Expect void) | undefined>>, - Expect void) | undefined>>, - Expect void) | undefined>>, - Expect['onApplySuggestion']>, undefined>>, - Expect['onRejectSuggestion']>, undefined>>, - Expect['onSelect']>, undefined>>, - Expect['onDismiss']>, undefined>>, - Expect['onSubmit']>, undefined>>, -]; - -/** - * Non-vacuity: the comparison can fail. A member typed differently on the two - * sides is reported by name, and an unmirrored key is caught by the key-set row. - */ -export type assertionInstrumentFires = [ - Expect, { a: number }>, 'a'>>, - Expect>, DeclaredKeys<{ a: string; b: string }>>, false>>, -]; - -/* ── Runtime: the documents that were refused ────────────────────────────── */ - -/** One minimal document per armed key — the `type` alone. */ -const MINIMAL = [ - { type: 'ai-form-assist' }, - { type: 'ai-recommendations' }, - { type: 'nl-query' }, -] as const; - -/** Rooted on this file, never on `process.cwd()` (AGENTS.md, test path roots). */ -const HERE = dirname(fileURLToPath(import.meta.url)); -const PLUGIN_AI_README = join(HERE, '..', '..', '..', 'plugin-ai', 'README.md'); - -/** - * The document `packages/plugin-ai/README.md` teaches under "Schema-Driven - * Usage", read off the README itself so the pin follows the page rather than a - * copy of it. - */ -function readmeFormAssistDocument(): Record { - const readme = readFileSync(PLUGIN_AI_README, 'utf8'); - const section = readme.slice(readme.indexOf('## Schema-Driven Usage')); - const fence = /```json\n([\s\S]*?)\n```/.exec(section); - if (!fence) throw new Error('no ```json fence under "## Schema-Driven Usage" in plugin-ai/README.md'); - return JSON.parse(fence[1]) as Record; -} - -describe('the registered AI node types validate (objectui#10859)', () => { - it.each(MINIMAL)('$type is accepted by safeValidateSchema', (doc) => { - const result = safeValidateSchema(doc); - expect(result.success, JSON.stringify(result.success ? null : result.error.issues)).toBe(true); - }); - - it.each(MINIMAL)('$type is accepted by the strict authoring face', (doc) => { - const result = StrictAnyComponentSchema.safeParse(doc); - expect(result.success, JSON.stringify(result.success ? null : result.error.issues)).toBe(true); - }); - - it("accepts the plugin-ai README's `ai-form-assist` document on both faces", () => { - const doc = readmeFormAssistDocument(); - // Lit control on the extraction: the fence really is the taught document. - expect(doc.type).toBe('ai-form-assist'); - expect(safeValidateSchema(doc).success).toBe(true); - expect(StrictAnyComponentSchema.safeParse(doc).success).toBe(true); - }); - - it('accepts a fully populated document of each type', () => { - // `config` and `context` left these documents with objectui#10874, which - // retired both on both faces; their refusal is pinned in - // `ai-node-faces-agree-10874.test.ts`, beside this file. - const docs = [ - { - type: 'ai-form-assist', - suggestions: [{ fieldName: 'company', value: 'ObjectStack', confidence: 0.9, reasoning: 'domain' }], - showConfidence: true, - showReasoning: false, - }, - { - type: 'ai-recommendations', - recommendations: [ - { id: 'r1', title: 'Renew', score: 0.8, category: 'sales', metadata: { a: 1 }, action: { type: 'open', target: 'r1' } }, - ], - showScores: true, - layout: 'grid', - loading: false, - emptyMessage: 'Nothing yet', - }, - { - type: 'nl-query', - placeholder: 'Ask anything', - result: { query: 'q', data: [{ a: 1 }], columns: [{ name: 'a', label: 'A', type: 'number' }], summary: 's', confidence: 0.5 }, - suggestions: ['top accounts'], - showHistory: true, - history: [{ query: 'q', timestamp: '2026-09-27T00:00:00Z' }], - loading: false, - }, - ]; - for (const doc of docs) { - expect(safeValidateSchema(doc).success, doc.type).toBe(true); - expect(StrictAnyComponentSchema.safeParse(doc).success, doc.type).toBe(true); - } - }); -}); - -describe('the arms are closed where the declaration is (objectui#10859)', () => { - it('refuses an undeclared member on the strict face, naming it — the strictness control', () => { - const doc = { type: 'nl-query', placeholder: 'Ask', inventedKey: true }; - const strict = StrictAnyComponentSchema.safeParse(doc); - expect(strict.success).toBe(false); - if (strict.success) return; - const issue = strict.error.issues.find((i) => i.code === 'unrecognized_keys'); - expect(issue, JSON.stringify(strict.error.issues)).toBeDefined(); - expect((issue as { keys?: string[] }).keys).toEqual(['inventedKey']); - // The rendering face keeps its `.passthrough()` — the second face is the - // one that closes, exactly as for every other arm. - expect(safeValidateSchema(doc).success).toBe(true); - }); - - it.each([ - ['ai-form-assist', 'formId', 'new-contact'], - ['ai-form-assist', 'objectName', 'Contact'], - ['ai-form-assist', 'fields', ['name']], - ['ai-form-assist', 'autoFill', true], - ['ai-recommendations', 'objectName', 'Product'], - ['ai-recommendations', 'maxResults', 5], - ['nl-query', 'objectName', 'Order'], - ] as const)('%s refuses the objectui#8178 tombstone `%s` by name', (type, key, value) => { - const result = safeValidateSchema({ type, [key]: value }); - expect(result.success).toBe(false); - if (result.success) return; - const [issue] = result.error.issues; - expect(issue.code).toBe('invalid_type'); - expect(issue.path).toEqual([key]); - expect(issue.message).toContain('objectui#8178'); - expect(issue.message).toContain(`\`${key}\``); - }); - - it.each([ - ['ai-form-assist', 'onApplySuggestion', 'RETIRED'], - ['ai-form-assist', 'onRejectSuggestion', 'RETIRED'], - ['ai-recommendations', 'onSelect', 'RUNTIME SLOT'], - ['ai-recommendations', 'onDismiss', 'RUNTIME SLOT'], - ['nl-query', 'onSubmit', 'RUNTIME SLOT'], - ] as const)('%s refuses an authored handler string on `%s` (objectui#6182 / #6124, %s)', (type, key, disposition) => { - const result = safeValidateSchema({ type, [key]: 'handleIt' }); - expect(result.success).toBe(false); - if (result.success) return; - const [issue] = result.error.issues; - expect(issue.code).toBe('custom'); - expect(issue.path).toEqual([key]); - expect(issue.message).toContain(`\`${key}\``); - expect(issue.message).toContain(disposition); - }); - - it.each(['ai-form-assist', 'ai-recommendations', 'nl-query'])('%s refuses both content channels (objectui#9256 family D)', (type) => { - for (const key of ['body', 'children'] as const) { - const result = safeValidateSchema({ type, [key]: [{ type: 'text', content: 'x' }] }); - expect(result.success, `${type}.${key}`).toBe(false); - if (result.success) continue; - expect(result.error.issues[0].path).toEqual([key]); - expect(result.error.issues[0].message).toContain('objectui#9256'); - } - }); - - it('refuses the objectui#10330 retired `carousel` layout value', () => { - const result = safeValidateSchema({ type: 'ai-recommendations', layout: 'carousel' }); - expect(result.success).toBe(false); - if (result.success) return; - expect(result.error.issues[0].path).toEqual(['layout']); - }); -}); diff --git a/ui/scripts/markdown-test-inputs.mjs b/ui/scripts/markdown-test-inputs.mjs index adce3f02eac..597fc08b383 100644 --- a/ui/scripts/markdown-test-inputs.mjs +++ b/ui/scripts/markdown-test-inputs.mjs @@ -250,19 +250,17 @@ export const ADJUDICATED = new Map([ walker: 'not-markdown: sweeps package `src` directories for `.ts` sources', }, ], - // objectui#10859. Extracts `@object-ui/plugin-ai`'s README "Schema-Driven - // Usage" block and runs it through `objectui validate` end to end -- so an edit - // to that README IS an edit to this test's input. The scanner also resolves the - // bare `'README.md'` segment of that path against this package and the root; - // an `fs` trace of the file's run at objectui#10859 opened the plugin-ai README - // and nothing else. objectui#10872 batch 4 added the second read (its - // `QUICK_START` path): the first json fence under the quick-start's - // "### Add Actions" heading -- the taught `action:button` node -- which the - // page pin runs through `objectui validate` beside a `page:header`. + // objectui#10872 batch 4 (its `QUICK_START` path): the first json fence under + // the quick-start's "### Add Actions" heading -- the taught `action:button` + // node -- which the page pin runs through `objectui validate` beside a + // `page:header`. The scanner also resolves the bare `'README.md'` segment of + // the file's prose against this package and the root; neither is read. (Its + // other read, `@object-ui/plugin-ai`'s README, left with that package, + // objectstack#25921.) [ 'packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts', { - reads: ['packages/plugin-ai/README.md', 'content/docs/guide/quick-start.md'], + reads: ['content/docs/guide/quick-start.md'], notRead: ['README.md', 'packages/cli/README.md'], }, ], @@ -478,19 +476,6 @@ export const ADJUDICATED = new Map([ walker: 'markdown-tree: walks `content/docs` and refuses the retired callback shape in every `.md`/`.mdx` under it', }, ], - // objectui#10859. Extracts `@object-ui/plugin-ai`'s README "Schema-Driven - // Usage" block and runs it through `safeValidateSchema` and the strict - // authoring face -- the `timeline-readme-schema-example-10824.test.ts` shape. - // The scanner also resolves the bare `'README.md'` segment of that path against - // this package and the root; an `fs` trace of the file's run opened the - // plugin-ai README and nothing else. - [ - 'packages/types/src/__tests__/ai-zod-arms-10859.test.ts', - { - reads: ['packages/plugin-ai/README.md'], - notRead: ['README.md', 'packages/types/README.md'], - }, - ], [ 'packages/types/src/__tests__/alert-dialog-read-dialect-7104.test.ts', { reads: ['content/docs/components/overlay/alert-dialog.mdx'] }, From ae61439ceff06f2aa759045943d7ba85bc169e3a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 22:27:44 +0000 Subject: [PATCH 05/13] test,docs: follow the plugin-ai retirement in census pins, docs and the new retirement pin (WIP) Claude-Session: https://claude.ai/code/session_01VBDBR9oS1MNUcjBVXds5D6 Co-authored-by: Claude --- ui/README.md | 1 - ui/content/docs/plugins/meta.json | 1 - ui/content/docs/plugins/plugin-ai.mdx | 26 ---- .../clickableNonControlCensus-13461.test.ts | 1 - .../iconOnlyButtonCensus-12143.test.ts | 2 - .../any-component-emit-headroom-11573.test.ts | 6 +- .../__tests__/arm-named-export-8784.test.ts | 18 +++ ...ent-docs-retired-handler-keys-7340.test.ts | 12 +- .../content-channel-family-d-9256.test.ts | 9 +- ...t-channel-parser-tier-clause-10928.test.ts | 11 +- .../__tests__/plugin-ai-retired-25921.test.ts | 145 ++++++++++++++++++ ...e-clause-parser-tier-closure-10981.test.ts | 2 +- .../src/__tests__/zod-mirror-parity.test.ts | 56 +++---- .../console-vite-alias-closure-4925.test.ts | 17 +- .../__tests__/doc-version-claims.test.ts | 1 - .../percent-storage-callers-11475.test.ts | 25 --- .../runner-package-test-entry-3746.test.ts | 1 - .../__tests__/select-type-check-scope.test.ts | 6 +- 18 files changed, 215 insertions(+), 125 deletions(-) delete mode 100644 ui/content/docs/plugins/plugin-ai.mdx create mode 100644 ui/packages/types/src/__tests__/plugin-ai-retired-25921.test.ts diff --git a/ui/README.md b/ui/README.md index cd47b895cd8..42267742f82 100644 --- a/ui/README.md +++ b/ui/README.md @@ -415,7 +415,6 @@ Grouped by dependency weight — atoms and fields stay light, heavy widgets live | **[@object-ui/plugin-editor](./packages/plugin-editor)** | Rich text editor powered by Monaco | | **[@object-ui/plugin-markdown](./packages/plugin-markdown)** | Markdown rendering | | **[@object-ui/plugin-chatbot](./packages/plugin-chatbot)** | Chatbot interface | -| **[@object-ui/plugin-ai](./packages/plugin-ai)** | Schema generation and conversational assistants (Vercel AI SDK) | ### Tooling diff --git a/ui/content/docs/plugins/meta.json b/ui/content/docs/plugins/meta.json index a1a2c10555a..8af37eb0c76 100644 --- a/ui/content/docs/plugins/meta.json +++ b/ui/content/docs/plugins/meta.json @@ -1,7 +1,6 @@ { "title": "Plugins", "pages": [ - "plugin-ai", "plugin-calendar", "plugin-calendar-view", "plugin-charts", diff --git a/ui/content/docs/plugins/plugin-ai.mdx b/ui/content/docs/plugins/plugin-ai.mdx deleted file mode 100644 index 3727951b052..00000000000 --- a/ui/content/docs/plugins/plugin-ai.mdx +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: "Plugin AI" -description: "AI-driven UI plugin for Object UI — schema generation, conversational assistants and Vercel AI SDK integration" ---- - -# @object-ui/plugin-ai - -AI-driven UI plugin for Object UI — schema generation, conversational assistants and Vercel AI SDK integration - -## Installation - -```bash -pnpm add @object-ui/plugin-ai -``` - -## Status - -This plugin is part of the v3.3.0 release. For full API reference and usage -examples, see the package README: - -- 📦 [`@object-ui/plugin-ai` on npm](https://www.npmjs.com/package/@object-ui/plugin-ai) -- 📝 [Package README](https://github.com/objectstack-ai/objectui/tree/main/packages/plugin-ai) -- 🐛 [Report an issue](https://github.com/objectstack-ai/objectui/issues) - -> **Note:** A rich interactive documentation page for this plugin is coming soon. -> In the meantime, refer to the package README for schema, props and examples. diff --git a/ui/packages/components/src/__tests__/clickableNonControlCensus-13461.test.ts b/ui/packages/components/src/__tests__/clickableNonControlCensus-13461.test.ts index c40b9950639..142d69c6fa1 100644 --- a/ui/packages/components/src/__tests__/clickableNonControlCensus-13461.test.ts +++ b/ui/packages/components/src/__tests__/clickableNonControlCensus-13461.test.ts @@ -262,7 +262,6 @@ const NOT_CONTROLS: Array<[file: string, key: string, reason: string]> = [ const BACKLOG: Array<[file: string, key: string]> = [ // The data form preview selects a field by a click delegated from its frame. ['packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx', 'div · closest,getAttribute,some,readFields,setFieldSel'], - ['packages/plugin-ai/src/AIRecommendations.tsx', 'div · handleSelect'], ['packages/plugin-designer/src/DashboardEditor.tsx', 'div · onSelect'], ['packages/plugin-designer/src/DataModelDesigner.tsx', 'span · stopPropagation,handleStartFieldEdit'], ['packages/plugin-designer/src/PageDesigner.tsx', 'div · stopPropagation,toggle'], diff --git a/ui/packages/components/src/__tests__/iconOnlyButtonCensus-12143.test.ts b/ui/packages/components/src/__tests__/iconOnlyButtonCensus-12143.test.ts index f0b9efe99d1..3fe55ab8387 100644 --- a/ui/packages/components/src/__tests__/iconOnlyButtonCensus-12143.test.ts +++ b/ui/packages/components/src/__tests__/iconOnlyButtonCensus-12143.test.ts @@ -228,8 +228,6 @@ const unnamed: Judged[] = iconOnly.filter((j) => !j.named); * that card's fix. */ const BACKLOG: Array<[file: string, icons: string[]]> = [ - ['packages/plugin-ai/src/AIFormAssist.tsx', ['RefreshCw', 'X', 'Check', 'X']], - ['packages/plugin-ai/src/AIRecommendations.tsx', ['ExternalLink', 'X', 'ThumbsUp', 'ThumbsDown']], ['packages/plugin-detail/src/DetailView.tsx', ['ArrowLeft', 'ChevronLeft', 'ChevronRight']], ['packages/plugin-detail/src/RecordComments.tsx', ['Send']], ['packages/plugin-list/src/ListView.tsx', ['X']], diff --git a/ui/packages/types/src/__tests__/any-component-emit-headroom-11573.test.ts b/ui/packages/types/src/__tests__/any-component-emit-headroom-11573.test.ts index 2b3d142ba9d..b553f9b2f06 100644 --- a/ui/packages/types/src/__tests__/any-component-emit-headroom-11573.test.ts +++ b/ui/packages/types/src/__tests__/any-component-emit-headroom-11573.test.ts @@ -82,7 +82,6 @@ import { fileURLToPath } from 'node:url'; import ts from 'typescript'; import type { z } from 'zod'; import { AnyComponentSchema } from '../zod/index.zod.js'; -import type { AIComponentZodType } from '../zod/ai.zod.js'; import type { ComplexZodType } from '../zod/complex.zod.js'; import type { CRUDComponentZodType } from '../zod/crud.zod.js'; import type { DataDisplayZodType } from '../zod/data-display.zod.js'; @@ -310,7 +309,9 @@ describe('`AnyComponentSchema` declaration emit headroom (objectui#11573)', () = { member: 'CRUDComponentSchema', kind: 'category union', printsAs: 'CRUDComponentZodType' }, { member: 'ReportUnionSchema', kind: 'category union', printsAs: 'ReportUnionZodType' }, { member: 'ViewComponentSchema', kind: 'category union', printsAs: 'ViewComponentZodType' }, - { member: 'AIComponentSchema', kind: 'category union', printsAs: 'AIComponentZodType' }, + { member: 'RetiredAIFormAssistNodeSchema', kind: 'arm', printsAs: 'inline' }, + { member: 'RetiredAIRecommendationsNodeSchema', kind: 'arm', printsAs: 'inline' }, + { member: 'RetiredNLQueryNodeSchema', kind: 'arm', printsAs: 'inline' }, { member: 'DesignerUnionSchema', kind: 'category union', printsAs: 'DesignerUnionZodType' }, { member: 'PublicBlockComponentSchema', kind: 'category union', printsAs: 'PublicBlockComponentZodType' }, { member: 'RetiredCloudPlanStatusNodeSchema', kind: 'arm', printsAs: 'inline' }, @@ -369,7 +370,6 @@ type _ObjectQLPublicBlockComponent = Assert>; type _ReportUnion = Assert>; type _ViewComponent = Assert>; -type _AIComponent = Assert>; type _DesignerUnion = Assert>; type _PublicBlockComponent = Assert>; diff --git a/ui/packages/types/src/__tests__/arm-named-export-8784.test.ts b/ui/packages/types/src/__tests__/arm-named-export-8784.test.ts index d0574295f68..ba7d97e6fbb 100644 --- a/ui/packages/types/src/__tests__/arm-named-export-8784.test.ts +++ b/ui/packages/types/src/__tests__/arm-named-export-8784.test.ts @@ -190,6 +190,24 @@ const ABSENT_BY_DECISION: Readonly> = { + 'retired raw-HTML `html` literal (objectui#13449), in the shape of the `kanban` row above. ' + 'The decision is written on the barrel beside the `data-display.zod.js` block: "it is ' + 'deliberately NOT exported — nothing outside this package parses against a refusal arm".', + 'ai-form-assist': + 'ai.zod.ts#RetiredAIFormAssistNodeSchema — the same ADR-0049 refusal arm, for a literal `@object-ui/plugin-ai` ' + + 'registered until it retired (objectstack#25921), in the shape of the `kanban` row above. The ' + + 'barrel\'s `AIFormAssistSchema`-family names are `z.never()` tombstones, not this arm; the decision ' + + 'is written beside the `ai.zod.js` block: "deliberately NOT exported — nothing outside this ' + + 'package parses against a refusal arm".', + 'ai-recommendations': + 'ai.zod.ts#RetiredAIRecommendationsNodeSchema — the same ADR-0049 refusal arm, for a literal `@object-ui/plugin-ai` ' + + 'registered until it retired (objectstack#25921), in the shape of the `kanban` row above. The ' + + 'barrel\'s `AIFormAssistSchema`-family names are `z.never()` tombstones, not this arm; the decision ' + + 'is written beside the `ai.zod.js` block: "deliberately NOT exported — nothing outside this ' + + 'package parses against a refusal arm".', + 'nl-query': + 'ai.zod.ts#RetiredNLQueryNodeSchema — the same ADR-0049 refusal arm, for a literal `@object-ui/plugin-ai` ' + + 'registered until it retired (objectstack#25921), in the shape of the `kanban` row above. The ' + + 'barrel\'s `AIFormAssistSchema`-family names are `z.never()` tombstones, not this arm; the decision ' + + 'is written beside the `ai.zod.js` block: "deliberately NOT exported — nothing outside this ' + + 'package parses against a refusal arm".', // ⭐ The two rows objectui#9067 moved here out of ABSENT_PENDING_DECISION. They // quote `.changeset/8499-node-slot-registered-arms.md` rather than cite it, diff --git a/ui/packages/types/src/__tests__/component-docs-retired-handler-keys-7340.test.ts b/ui/packages/types/src/__tests__/component-docs-retired-handler-keys-7340.test.ts index 0d34be21093..71f7c15aafb 100644 --- a/ui/packages/types/src/__tests__/component-docs-retired-handler-keys-7340.test.ts +++ b/ui/packages/types/src/__tests__/component-docs-retired-handler-keys-7340.test.ts @@ -339,12 +339,16 @@ describe('the retired population is measured off the shipped tree (objectui#7340 // new key, which `DataModelDesigner` draws — but the old spelling is now the // same `on*?: never` shape this census reads, refused by name with the // migration, so it is counted here like `onUpdate` above. + // + // ⭐ 31 → 29, and `ai.ts` leaves this census: `@object-ui/plugin-ai` RETIRED + // (objectstack#25921) and its three declarations are `never` tombstones, so + // `onApplySuggestion` / `onRejectSuggestion` left the shipped tree whole — + // the #8802 kind of shrink. The three runtime slots left LIVE with them. const split: Record = {}; for (const m of RETIRED) split[m.file] = (split[m.file] ?? 0) + 1; expect({ total: RETIRED.length, split }).toEqual({ - total: 31, + total: 29, split: { - 'ai.ts': 2, 'complex.ts': 2, 'crud.ts': 3, 'data-display.ts': 4, @@ -374,8 +378,6 @@ describe('the retired population is measured off the shipped tree (objectui#7340 // to classify. They are not "live again": the document that could have // carried them is refused at its `type`. expect(UNAMBIGUOUSLY_RETIRED_NAMES).toEqual([ - // objectui#10874 — declared on `AIFormAssistSchema` alone, read by nothing. - 'onApplySuggestion', // objectui#9342 — no shipped interface declares an `onCardMove` callable // any more. `KanbanRendererProps.onCardMove` is a React prop on // `@object-ui/plugin-kanban`, not a member of a `@object-ui/types` @@ -400,8 +402,6 @@ describe('the retired population is measured off the shipped tree (objectui#7340 // `@object-ui/types` document interface, so it is not in this census's // population — the same reading as `onCardMove` above. 'onQuickAdd', - // objectui#10874 — declared on `AIFormAssistSchema` alone, read by nothing. - 'onRejectSuggestion', 'onSave', 'onSelectChange', 'onSendMessage', diff --git a/ui/packages/types/src/__tests__/content-channel-family-d-9256.test.ts b/ui/packages/types/src/__tests__/content-channel-family-d-9256.test.ts index b93f8f19dcd..e4691e40238 100644 --- a/ui/packages/types/src/__tests__/content-channel-family-d-9256.test.ts +++ b/ui/packages/types/src/__tests__/content-channel-family-d-9256.test.ts @@ -236,7 +236,6 @@ import type { ObjectGallerySchema } from '../objectql'; import type { CarouselSchema, ChatbotSchema, ChatbotEnhancedSchema } from '../complex'; import type { ReportViewerSchema } from '../reports'; import type { ViewSwitcherSchema } from '../views'; -import type { NLQuerySchema } from '../ai'; import type { TextSchema, ImageSchema, IconSchema, TabsSchema } from '../layout'; import type { AccordionSchema } from '../disclosure'; import type { CalendarSchema } from '../form'; @@ -555,8 +554,8 @@ describe('objectui#9256 — the TypeScript face refuses both channels at the AUT const viewSwitcherChildren: ViewSwitcherSchema = { type: 'view-switcher', views: [], children: CONTENT }; // @ts-expect-error objectui#9256 — `report-viewer` reads neither channel const reportBody: ReportViewerSchema = { type: 'report-viewer', body: CONTENT }; - // @ts-expect-error objectui#9256 — `nl-query` reads neither channel (no zod mirror exists; this face is the only gate) - const nlQueryChildren: NLQuerySchema = { type: 'nl-query', children: CONTENT }; + // (`nl-query` stood here until its node type retired with `@object-ui/plugin-ai`, + // objectstack#25921: `NLQuerySchema` is a `never` tombstone now.) // @ts-expect-error objectui#9256 — `pivot` reads neither channel (no zod mirror exists; this face is the only gate) const pivotBody: PivotTableSchema = { type: 'pivot', body: CONTENT }; // @ts-expect-error objectui#9256 — `chatbot` reads neither channel; `children` is narrowed, `body` is held out @@ -604,11 +603,11 @@ describe('objectui#9256 — the TypeScript face refuses both channels at the AUT expect([ separatorBody, separatorChildren, kbdBody, skeletonChildren, checkboxBody, paginationChildren, dialogChildren, collapsibleChildren, carouselBody, galleryBody, viewSwitcherChildren, reportBody, - nlQueryChildren, pivotBody, chatbotChildren, + pivotBody, chatbotChildren, textBody, textChildren, imageBody, imageChildren, iconBody, iconChildren, tabsBody, tabsChildren, accordionBody, accordionChildren, calendarBody, calendarChildren, listBody, listChildren, timelineBody, timelineChildren, - ]).toHaveLength(31); + ]).toHaveLength(30); }); it('CONTROL — the same nodes WITHOUT a content channel compile (no `@ts-expect-error` here, and `tsc` is the reader)', () => { diff --git a/ui/packages/types/src/__tests__/content-channel-parser-tier-clause-10928.test.ts b/ui/packages/types/src/__tests__/content-channel-parser-tier-clause-10928.test.ts index a7790544677..fc8366431c0 100644 --- a/ui/packages/types/src/__tests__/content-channel-parser-tier-clause-10928.test.ts +++ b/ui/packages/types/src/__tests__/content-channel-parser-tier-clause-10928.test.ts @@ -48,8 +48,9 @@ * ## What is pinned, and what is deliberately not * * The clause as a substring, on the shared builder and on one arm from each of - * the three sources that compose these messages (a literal, the shared - * `neitherContentChannelGuidance`, and `ai.zod.ts`'s local builder), each read + * the sources that compose these messages (a literal and the shared + * `neitherContentChannelGuidance`; the third, `ai.zod.ts`'s local builder, left + * with `@object-ui/plugin-ai`, objectstack#25921), each read * through the arm's refusal with its code and path. Then the population: every * exported member whose refusal is the family-D sentence carries it, so a later * slice that copies an older literal goes red here. The rest of each message is @@ -62,7 +63,6 @@ import * as Z from '../zod/index.zod'; import { neitherContentChannelGuidance } from '../zod/tombstone.zod'; import { AccordionSchema } from '../zod/disclosure.zod'; import { ObjectGridSchema } from '../zod/objectql.zod'; -import { AIFormAssistSchema } from '../zod/ai.zod'; /** The corrected clause, as every family-D / E3 refusal now carries it. */ const CLAUSE = @@ -99,7 +99,6 @@ describe('objectui#10928 — one arm per message source, read through its refusa const ARMS = [ ['a literal (`accordion`)', AccordionSchema, 'accordion'], ['the shared builder (`object-grid`)', ObjectGridSchema, 'object-grid'], - ['ai.zod.ts\'s local builder (`ai-form-assist`)', AIFormAssistSchema, 'ai-form-assist'], ] as const; for (const [source, schema, type] of ARMS) { @@ -123,9 +122,9 @@ describe('objectui#10928 — the population: every family-D refusal carries the } } - it('the walk reaches all three message sources (non-vacuity, by name rather than by count)', () => { + it('the walk reaches both message sources (non-vacuity, by name rather than by count)', () => { const found = new Set(members.map((m) => `${m.name}.${m.key}`)); - for (const name of ['AccordionSchema', 'ObjectGridSchema', 'AIFormAssistSchema']) { + for (const name of ['AccordionSchema', 'ObjectGridSchema']) { for (const key of CHANNELS) expect(found, `${name}.${key}`).toContain(`${name}.${key}`); } }); diff --git a/ui/packages/types/src/__tests__/plugin-ai-retired-25921.test.ts b/ui/packages/types/src/__tests__/plugin-ai-retired-25921.test.ts new file mode 100644 index 00000000000..0b3a6787663 --- /dev/null +++ b/ui/packages/types/src/__tests__/plugin-ai-retired-25921.test.ts @@ -0,0 +1,145 @@ +/** + * ObjectUI + * Copyright (c) 2024-present ObjectStack Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** + * The three `@object-ui/plugin-ai` NODE TYPES are retired with their package, + * and a document naming one is refused BY NAME, with its remedy + * (objectstack#25921, ruling A). + * + * The package registered `ai-form-assist`, `ai-recommendations` and `nl-query`, + * and nothing loaded it: the console registered it neither eagerly nor lazily + * and no workspace package imported it. The literals were declared on both + * faces, so the retirement keeps a `retiredNodeType()` arm for each in + * `AnyComponentSchema` — the shape `cloud-plan-status-retired-12954.test.ts` + * pins, whose suites this file follows — and the published mirror NAMES stay as + * `@deprecated` `never` tombstones until objectstack#25912's sunset rule. + * + * Suite 1 measures the named refusal; suite 2 its firing control; suite 3 that + * an unknown literal still takes the generic path; suite 4 that the published + * zod names accept nothing. + */ + +import { describe, it, expect } from 'vitest'; +import * as ZodFace from '../zod/index.zod'; +import { safeValidateSchema } from '../zod/index.zod'; +import type { AnySchema, AIFormAssistSchema, AIRecommendationsSchema, NLQuerySchema } from '../index'; + +/* -------------------------------------------------------------------------- */ +/* Compile-time pins — read by tsc -p tsconfig.test.json, not by vitest. */ +/* -------------------------------------------------------------------------- */ + +type Assert = T; +type Equal = (() => T extends A ? 1 : 2) extends () => T extends B ? 1 : 2 ? true : false; +type IsNever = [T] extends [never] ? true : false; + +// The TypeScript half: `AnySchema` has no member for any of the three literals. +type _NoFormAssistArm = Assert>, true>>; +type _NoRecommendationsArm = Assert>, true>>; +type _NoNLQueryArm = Assert>, true>>; +// Non-vacuity on the same instrument: a surviving arm IS selectable. +type _KeepsButton = Assert>, false>>; + +// The published names are `never` tombstones: still importable, assignable from nothing. +type _FormAssistIsNever = Assert>; +type _RecommendationsIsNever = Assert>; +type _NLQueryIsNever = Assert>; + +// @ts-expect-error objectstack#25921 — a node literal annotated with a retired name is refused at the authoring site. +const _authored: AIFormAssistSchema = { type: 'ai-form-assist' }; +void _authored; + +/* -------------------------------------------------------------------------- */ +/* Runtime — through `safeValidateSchema`, the tolerant node union. */ +/* -------------------------------------------------------------------------- */ + +interface IssueLike { + readonly path?: readonly PropertyKey[]; + readonly message?: string; + readonly code?: string; + readonly errors?: readonly (readonly IssueLike[])[]; +} + +/** Flatten zod's nested union issues into `{ path, message, code }` rows. */ +function flattenIssues(issues: readonly IssueLike[]): Array<{ path: string; message: string; code: string }> { + return issues.flatMap((issue) => [ + { path: (issue.path ?? []).join('.'), message: issue.message ?? '', code: issue.code ?? '' }, + ...(issue.errors ?? []).flatMap((nested) => flattenIssues(nested)), + ]); +} + +function refusals(schema: unknown) { + const result = safeValidateSchema(schema); + return result.success ? [] : flattenIssues(result.error.issues as unknown as IssueLike[]); +} + +/** One document per retired literal, each in a shape its old arm accepted. */ +const RETIRED_NODES = [ + { type: 'ai-form-assist', suggestions: [], showConfidence: true }, + { type: 'ai-recommendations', recommendations: [], layout: 'grid' }, + { type: 'nl-query', placeholder: 'Ask anything', showHistory: false }, +] as const; + +describe('suite 1 — each retired literal is REFUSED, by name (objectstack#25921)', () => { + it.each(RETIRED_NODES)('refuses `$type` with one `custom` issue at `type`', (node) => { + expect(refusals(node).filter((f) => f.code === 'custom' && f.path === 'type')).toHaveLength(1); + }); + + it.each(RETIRED_NODES)('the `$type` refusal names the retirement and the remedy', (node) => { + const [issue] = refusals(node).filter((f) => f.code === 'custom' && f.path === 'type'); + expect(issue?.message).toContain(`\`${node.type}\` is a RETIRED node type`); + expect(issue?.message).toContain('@object-ui/plugin-ai'); + expect(issue?.message).toContain('Remove the node from the page'); + }); +}); + +describe('suite 2 — the refusal is not a validator that turned strict', () => { + it('a surviving node on the same call is still accepted', () => { + expect(refusals({ type: 'button', label: 'Save' })).toEqual([]); + }); +}); + +describe('suite 3 — control: an unknown literal takes the GENERIC path', () => { + it('a name no arm claims is refused without the remedy text', () => { + const found = refusals({ type: 'ai-no-such-node-25921' }); + const text = found.map((r) => r.message).join('\n'); + expect(text).not.toEqual(''); + expect(found.some((f) => f.code === 'custom')).toBe(false); + expect(text).not.toContain('@object-ui/plugin-ai'); + }); +}); + +describe('suite 4 — the published zod names are `never` tombstones', () => { + const NAMES = [ + 'AIConfigSchema', + 'AIFieldSuggestionSchema', + 'AIFormAssistSchema', + 'AIRecommendationItemSchema', + 'AIRecommendationsSchema', + 'NLQueryResultSchema', + 'NLQuerySchema', + 'AIComponentSchema', + ] as const; + + it.each(NAMES)('`%s` is still exported, and accepts nothing — the message names the retirement', (name) => { + const schema = (ZodFace as Record)[name] as { safeParse: (v: unknown) => { success: boolean; error?: { issues: { message: string }[] } } }; + expect(schema, name).toBeDefined(); + for (const value of [{}, { type: 'ai-form-assist' }, 'x']) { + const result = schema.safeParse(value); + expect(result.success).toBe(false); + expect(result.error?.issues[0]?.message).toContain('objectstack#25921'); + } + }); + + it('the refusal arms themselves are NOT on the barrel (nothing outside this package parses against one)', () => { + for (const name of ['RetiredAIFormAssistNodeSchema', 'RetiredAIRecommendationsNodeSchema', 'RetiredNLQueryNodeSchema']) { + expect(name in ZodFace, name).toBe(false); + } + // Lit control: the same probe answers yes for a name the barrel does carry. + expect('AnyComponentSchema' in ZodFace).toBe(true); + }); +}); diff --git a/ui/packages/types/src/__tests__/silence-clause-parser-tier-closure-10981.test.ts b/ui/packages/types/src/__tests__/silence-clause-parser-tier-closure-10981.test.ts index 4266f4b3394..386329d3209 100644 --- a/ui/packages/types/src/__tests__/silence-clause-parser-tier-closure-10981.test.ts +++ b/ui/packages/types/src/__tests__/silence-clause-parser-tier-closure-10981.test.ts @@ -250,7 +250,7 @@ describe('objectui#10981 — the closure walk over this package\'s `src`', () => for (const f of ['base.ts', 'data-display.ts', 'zod/form.zod.ts', 'zod/objectql.zod.ts']) expect(names).toContain(f); const text = (f: string) => texts.find(([p]) => p === `${SRC}/${f}`)?.[1] ?? ''; expect(text('zod/navigation.zod.ts')).toContain(UNKNOWN_PROP_TAIL); - expect(text('ai.ts')).toContain("only the parser tier's `not-a-container` warning (objectui#9910) noticed it"); + expect(text('views.ts')).toContain("only the parser tier's `not-a-container` warning (objectui#9910) noticed it"); }); it('no file still pairs "no error" with "no warning"', () => { diff --git a/ui/packages/types/src/__tests__/zod-mirror-parity.test.ts b/ui/packages/types/src/__tests__/zod-mirror-parity.test.ts index 89fd88bc6a1..8ae7d1352c2 100644 --- a/ui/packages/types/src/__tests__/zod-mirror-parity.test.ts +++ b/ui/packages/types/src/__tests__/zod-mirror-parity.test.ts @@ -959,7 +959,6 @@ import ts from 'typescript'; import type { z } from 'zod'; import type { WithoutCatchall } from './zod-catchall-type'; -import { AIConfigSchema, AIFieldSuggestionSchema, AIFormAssistSchema, AIRecommendationItemSchema, AIRecommendationsSchema, NLQueryResultSchema, NLQuerySchema } from '../zod/ai.zod.js'; import { AppComponentSchema, AppSchemaRendererNodeSchema, MenuItemSchema as AppMenuItemSchema, NavigationAreaSchema, NavigationItemSchema } from '../zod/app.zod.js'; import { BaseSchema, ComponentConfigSchema, ComponentInputSchema, ComponentMetaSchema, KeyedI18nLabelSchema, SchemaNodeSchema } from '../zod/base.zod.js'; import { CloudWorkspaceTimezoneNoticeSchema } from '../zod/cloud.zod.js'; @@ -980,7 +979,6 @@ import { AlertDialogSchema, ContextMenuSchema, DialogSchema, DrawerSchema, Dropd import { ReportBuilderSchema, ReportComponentSchema, ReportExportConfigSchema, ReportFieldSchema, ReportFilterSchema, ReportGroupBySchema, ReportSectionSchema, ReportViewerSchema } from '../zod/reports.zod.js'; import { DetailSectionNodeSchema, DetailViewFieldSchema, DetailViewSchema, DetailViewSectionSchema, DetailViewTabSchema, FilterUISchema, SortUISchema, ViewSwitcherSchema } from '../zod/views.zod.js'; -import type { AIConfig as Ts_AIConfig, AIFieldSuggestion as Ts_AIFieldSuggestion, AIFormAssistSchema as Ts_AIFormAssistSchema, AIRecommendationItem as Ts_AIRecommendationItem, AIRecommendationsSchema as Ts_AIRecommendationsSchema, NLQueryResult as Ts_NLQueryResult, NLQuerySchema as Ts_NLQuerySchema } from '../ai'; import type { AppComponentSchema as Ts_AppComponentSchema, AppSchemaRendererNodeSchema as Ts_AppSchemaRendererNodeSchema, NavigationArea as Ts_NavigationArea } from '../app'; import type { BaseSchema as Ts_BaseSchema, ComponentConfig as Ts_ComponentConfig, ComponentInput as Ts_ComponentInput, ComponentMeta as Ts_ComponentMeta, KeyedI18nLabel as Ts_KeyedI18nLabel } from '../base'; import type { CloudWorkspaceTimezoneNoticeSchema as Ts_CloudWorkspaceTimezoneNoticeSchema } from '../cloud'; @@ -1821,13 +1819,6 @@ export type assertionNarrowerOperatorIsBlindToAMirroredOnlyKey = /** Mirror VALUES, keyed `#`. Runtime, so the census below can read the keys. */ const MIRRORS = { - 'ai.zod.ts#AIConfigSchema': AIConfigSchema, - 'ai.zod.ts#AIFieldSuggestionSchema': AIFieldSuggestionSchema, - 'ai.zod.ts#AIFormAssistSchema': AIFormAssistSchema, - 'ai.zod.ts#AIRecommendationItemSchema': AIRecommendationItemSchema, - 'ai.zod.ts#AIRecommendationsSchema': AIRecommendationsSchema, - 'ai.zod.ts#NLQueryResultSchema': NLQueryResultSchema, - 'ai.zod.ts#NLQuerySchema': NLQuerySchema, 'app.zod.ts#AppComponentSchema': AppComponentSchema, // objectui#11515 — the zod-only arm's TS twin. 'app.zod.ts#AppSchemaRendererNodeSchema': AppSchemaRendererNodeSchema, @@ -2036,13 +2027,6 @@ const MIRRORS = { /** The declaration each mirror restates. Same keys as `MIRRORS` — pinned below. */ interface Declared { - 'ai.zod.ts#AIConfigSchema': Ts_AIConfig; - 'ai.zod.ts#AIFieldSuggestionSchema': Ts_AIFieldSuggestion; - 'ai.zod.ts#AIFormAssistSchema': Ts_AIFormAssistSchema; - 'ai.zod.ts#AIRecommendationItemSchema': Ts_AIRecommendationItem; - 'ai.zod.ts#AIRecommendationsSchema': Ts_AIRecommendationsSchema; - 'ai.zod.ts#NLQueryResultSchema': Ts_NLQueryResult; - 'ai.zod.ts#NLQuerySchema': Ts_NLQuerySchema; 'app.zod.ts#AppComponentSchema': Ts_AppComponentSchema; 'app.zod.ts#AppSchemaRendererNodeSchema': Ts_AppSchemaRendererNodeSchema; 'app.zod.ts#NavigationAreaSchema': Ts_NavigationArea; @@ -2274,24 +2258,6 @@ export type DeclaredTypeOf< K extends MirrorKey, P extends keyof Declared[K] > = * new drift on a listed mirror fails, and so does a listed key that has been fixed. */ interface KnownDrift { - /** - * RUNTIME SLOT (objectui#6124 shape, settled by objectui#10874): `ai-recommendations` - * registers the raw `AIRecommendations` component, `SchemaRenderer` spreads the - * node's own keys onto its props, and it CALLS both (`onSelect?.(item)`, - * `onDismiss?.(item)`), so the TS side declares the callables and the mirror - * refuses them by name (`handlerKeyRefusal(…, 'runtime-slot')`). The pair was born - * ledgered by objectui#10859 against a TS twin that still read `string`; the entry - * is unchanged in content, and only its reason moved into this class. - */ - 'ai.zod.ts#AIRecommendationsSchema': 'onDismiss' | 'onSelect'; - /** - * RUNTIME SLOT (objectui#6124 shape, settled by objectui#10874): `nl-query` registers - * the raw `NLQueryInput` component, which CALLS `onSubmit` off the spread props - * with the query text (`onSubmitProp?.(queryText)`), so the TS side declares the - * callable and the mirror refuses it by name — same history as - * `AIRecommendationsSchema` above. - */ - 'ai.zod.ts#NLQuerySchema': 'onSubmit'; /** * INTERFACE-TO-PASSTHROUGH (objectui#8347), an artifact of this ratchet's * assignability test and ⛔ not a narrowing a document can hit. The member is a @@ -4912,8 +4878,26 @@ const EXCLUSIONS: Readonly> = { // `data-display.zod.ts#HtmlSchema` left `MIRRORS` with it. 'data-display.zod.ts#RetiredHtmlNodeSchema': 'a RETIRED node type refusal arm (objectui#13449), not a mirror — its only member is the `type` literal it refuses on, and the TS half of that retirement is the ABSENCE of an `html` member in `DataDisplaySchema`', - 'ai.zod.ts#AIComponentSchema': - "a union OVER the mirrors, not an object of its own — its members are checked individually above", + // objectstack#25921 — `@object-ui/plugin-ai` RETIRED. The three node types' + // refusal arms are the same kind as the rows above, pinned in + // `./plugin-ai-retired-25921.test.ts`; their former pairs left `MIRRORS` with + // them, and so did the four support-record pairs, whose TS twins are `never` + // tombstones now. The eight published names stay exported as `z.never()` + // tombstones until objectstack#25912's sunset rule: none mirrors anything. + 'ai.zod.ts#RetiredAIFormAssistNodeSchema': + 'a RETIRED node type refusal arm (objectstack#25921), not a mirror — its only member is the `type` literal it refuses on, and the TS half of that retirement is the ABSENCE of an `ai-form-assist` member in `AnySchema`', + 'ai.zod.ts#RetiredAIRecommendationsNodeSchema': + 'a RETIRED node type refusal arm (objectstack#25921), not a mirror — its only member is the `type` literal it refuses on, and the TS half of that retirement is the ABSENCE of an `ai-recommendations` member in `AnySchema`', + 'ai.zod.ts#RetiredNLQueryNodeSchema': + 'a RETIRED node type refusal arm (objectstack#25921), not a mirror — its only member is the `type` literal it refuses on, and the TS half of that retirement is the ABSENCE of an `nl-query` member in `AnySchema`', + 'ai.zod.ts#AIConfigSchema': 'a RETIRED `z.never()` tombstone (objectstack#25921) — accepts nothing; its TS twin is `never`', + 'ai.zod.ts#AIFieldSuggestionSchema': 'a RETIRED `z.never()` tombstone (objectstack#25921) — accepts nothing; its TS twin is `never`', + 'ai.zod.ts#AIFormAssistSchema': 'a RETIRED `z.never()` tombstone (objectstack#25921) — accepts nothing; its TS twin is `never`', + 'ai.zod.ts#AIRecommendationItemSchema': 'a RETIRED `z.never()` tombstone (objectstack#25921) — accepts nothing; its TS twin is `never`', + 'ai.zod.ts#AIRecommendationsSchema': 'a RETIRED `z.never()` tombstone (objectstack#25921) — accepts nothing; its TS twin is `never`', + 'ai.zod.ts#NLQueryResultSchema': 'a RETIRED `z.never()` tombstone (objectstack#25921) — accepts nothing; its TS twin is `never`', + 'ai.zod.ts#NLQuerySchema': 'a RETIRED `z.never()` tombstone (objectstack#25921) — accepts nothing; its TS twin is `never`', + 'ai.zod.ts#AIComponentSchema': 'a RETIRED `z.never()` tombstone (objectstack#25921) — it was the union over the three arms; `AnyComponentSchema` lists their refusal arms instead', 'product-blocks.zod.ts#ProductBlockComponentSchema': "a union OVER the mirrors (objectui#12197), not an object of its own — its members are checked individually above", // objectui#10872 — the ADR-0080 public-block arms. Each is `BaseSchema` plus a diff --git a/ui/scripts/__tests__/console-vite-alias-closure-4925.test.ts b/ui/scripts/__tests__/console-vite-alias-closure-4925.test.ts index d404e7e06ad..f9f5d5a6478 100644 --- a/ui/scripts/__tests__/console-vite-alias-closure-4925.test.ts +++ b/ui/scripts/__tests__/console-vite-alias-closure-4925.test.ts @@ -64,8 +64,8 @@ import ts from 'typescript'; * `pnpm-workspace.yaml` lists EVERY workspace package (46 at the time of * writing); the console's closure is 34 of them. A naive `table == workspace` * assertion therefore reds on day one over packages the console legitimately - * does not import — `@object-ui/cli`, `@object-ui/plugin-ai`, - * `@object-ui/runner`, `@object-ui/test-support`, the examples. The workspace + * does not import — `@object-ui/cli`, `@object-ui/runner`, + * `@object-ui/test-support`, the examples. The workspace * manifest's role here is to say what a workspace package IS, not what the * console needs; the closure says what it needs. `does not demand an alias for a * workspace package the console never imports` below is a standing negative @@ -95,7 +95,8 @@ import ts from 'typescript'; * floor, and a deletion cannot create a stale entry). Observed exactly that — * 1 failed, 7 passed, the finding reading `@object-ui/plugin-map (reached via * apps/console/src/register-plugins.ts imports '@object-ui/plugin-map')`. - * - NEGATIVE control — `@object-ui/plugin-ai` is a `packages/*` workspace + * - NEGATIVE control — `@object-ui/plugin-ai` (since its retirement, + * objectstack#25921, `@object-ui/cli`) is a `packages/*` workspace * package with no alias entry and no place in the closure. UNMUTATED this * file is green, which is the control: the table is not being held to the * workspace manifest. It is also asserted by name below, and probed the other @@ -501,10 +502,12 @@ describe('objectui#4925 — apps/console workspace alias table vs. its import cl ).toBeGreaterThan(0); // Named, so the control cannot be satisfied by an empty-ish coincidence: - // plugin-ai is shaped exactly like the plugins the console DOES alias. - expect(WORKSPACE.has('@object-ui/plugin-ai')).toBe(true); - expect(closure.packages.has('@object-ui/plugin-ai')).toBe(false); - expect(aliasPackages.has('@object-ui/plugin-ai')).toBe(false); + // `@object-ui/cli` is a published `packages/*` library with an `exports` map, + // the shape the console's aliased packages have. (The name here was + // `@object-ui/plugin-ai` until that package retired, objectstack#25921.) + expect(WORKSPACE.has('@object-ui/cli')).toBe(true); + expect(closure.packages.has('@object-ui/cli')).toBe(false); + expect(aliasPackages.has('@object-ui/cli')).toBe(false); }); it('aliases every closure package at that package\'s own workspace directory', () => { diff --git a/ui/scripts/__tests__/doc-version-claims.test.ts b/ui/scripts/__tests__/doc-version-claims.test.ts index a3d0776902d..219b2980377 100644 --- a/ui/scripts/__tests__/doc-version-claims.test.ts +++ b/ui/scripts/__tests__/doc-version-claims.test.ts @@ -1137,7 +1137,6 @@ const KNOWN_CLAIMS: KnownClaim[] = [ { file: 'packages/layout/README.md', claim: PEER_19, kind: 'restatement', why: PEER_RESTATEMENT_OK }, { file: 'packages/mobile/README.md', claim: PEER_19, kind: 'restatement', why: PEER_RESTATEMENT_OK }, { file: 'packages/permissions/README.md', claim: PEER_19, kind: 'restatement', why: PEER_RESTATEMENT_OK }, - { file: 'packages/plugin-ai/README.md', claim: PEER_19, kind: 'restatement', why: PEER_RESTATEMENT_OK }, { file: 'packages/plugin-designer/README.md', claim: PEER_19, kind: 'restatement', why: PEER_RESTATEMENT_OK }, { file: 'packages/plugin-report/README.md', claim: PEER_19, kind: 'restatement', why: PEER_RESTATEMENT_OK }, { file: 'packages/react/README.md', claim: PEER_19, kind: 'restatement', why: PEER_RESTATEMENT_OK }, diff --git a/ui/scripts/__tests__/percent-storage-callers-11475.test.ts b/ui/scripts/__tests__/percent-storage-callers-11475.test.ts index d8ca331d4f5..20dc1d11a23 100644 --- a/ui/scripts/__tests__/percent-storage-callers-11475.test.ts +++ b/ui/scripts/__tests__/percent-storage-callers-11475.test.ts @@ -81,19 +81,6 @@ const CALLERS: Record = { ], }; -/** - * Files that call a DIFFERENT function spelled `formatPercent`, named so the - * exclusion is a decision rather than a blind spot: `plugin-ai` formats a 0–1 - * model confidence through its own module-local helper in - * `useAiTranslation.ts`, imported from there, not from `@object-ui/fields`. - */ -const OTHER_FORMAT_PERCENT = new Set([ - 'packages/plugin-ai/src/useAiTranslation.ts', - 'packages/plugin-ai/src/AIFormAssist.tsx', - 'packages/plugin-ai/src/AIRecommendations.tsx', - 'packages/plugin-ai/src/NLQueryInput.tsx', -]); - const SKIP_DIRS = new Set(['node_modules', 'dist', '__tests__', '__mocks__', 'coverage']); const SOURCE_FILE = /\.(ts|tsx|mts|cts)$/; const NOT_SOURCE = /\.(test|spec|stories)\.[cm]?tsx?$|\.d\.ts$/; @@ -212,7 +199,6 @@ describe('every caller of the percent scaling states its storage (objectui#11475 const found: Record> = {}; for (const file of files) { - if (OTHER_FORMAT_PERCENT.has(file)) continue; const calls = callsIn(file); if (calls.length > 0) found[file] = calls; } @@ -235,15 +221,4 @@ describe('every caller of the percent scaling states its storage (objectui#11475 ); expect(bare).toEqual([]); }); - - it('the excluded `formatPercent` really is a different function, imported from its own module', () => { - const helper = readFileSync(path.join(repoRoot, 'packages/plugin-ai/src/useAiTranslation.ts'), 'utf8'); - expect(helper).toMatch(/export function formatPercent\(score: number, locale: string\)/); - for (const file of OTHER_FORMAT_PERCENT) { - if (file.endsWith('useAiTranslation.ts')) continue; - const src = readFileSync(path.join(repoRoot, file), 'utf8'); - expect(src, file).toMatch(/import \{[^}]*\bformatPercent\b[^}]*\} from '\.\/useAiTranslation'/); - expect(src, file).not.toMatch(/from '@object-ui\/fields'[^\n]*formatPercent|formatPercent[^\n]*from '@object-ui\/fields'/); - } - }); }); diff --git a/ui/scripts/__tests__/runner-package-test-entry-3746.test.ts b/ui/scripts/__tests__/runner-package-test-entry-3746.test.ts index 8abbc6053ff..1c8c137c6d7 100644 --- a/ui/scripts/__tests__/runner-package-test-entry-3746.test.ts +++ b/ui/scripts/__tests__/runner-package-test-entry-3746.test.ts @@ -72,7 +72,6 @@ const TEST_FILE = /\.test\.[cm]?[jt]sx?$/; * file must not become a reason to touch these packages out of band. */ const BARE_ENTRY_BASELINE = [ - 'packages/plugin-ai', 'packages/plugin-chatbot', 'packages/plugin-designer', 'packages/plugin-editor', diff --git a/ui/scripts/__tests__/select-type-check-scope.test.ts b/ui/scripts/__tests__/select-type-check-scope.test.ts index 003b9a1b622..65c6202c824 100644 --- a/ui/scripts/__tests__/select-type-check-scope.test.ts +++ b/ui/scripts/__tests__/select-type-check-scope.test.ts @@ -212,11 +212,11 @@ describe('select-type-check-scope — the verdict', () => { }); it('names them all the same on an affected set that lacks them (objectui#12272)', () => { - // `...[base]` over `packages/plugin-ai` alone selects that one package, and + // `...[base]` over `packages/plugin-markdown` alone selects that one package, and // its `^build` closure holds none of fields, plugin-dashboard, plugin-grid. - const lacking = run(['packages/plugin-ai/src/index.ts'], () => ['@object-ui/plugin-ai']); + const lacking = run(['packages/plugin-markdown/src/index.tsx'], () => ['@object-ui/plugin-markdown']); expect(lacking.lines.at(-1)).toBe('scope=affected'); - expect(lacking.lines).toContain(' @object-ui/plugin-ai'); + expect(lacking.lines).toContain(' @object-ui/plugin-markdown'); const tail = lacking.lines.slice(lacking.lines.findIndex((l) => l.startsWith('The repo-root programs'))); expect(tail.slice(1, -1)).toEqual(ROOT_PROGRAM_PACKAGES.map((p) => ` ${p}`)); }); From 2a9feba6da4840f17a37debc4cb7756721284d53 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 22:28:25 +0000 Subject: [PATCH 06/13] chore(changeset): retire @object-ui/plugin-ai, minor with the breaking note (WIP) Claude-Session: https://claude.ai/code/session_01VBDBR9oS1MNUcjBVXds5D6 Co-authored-by: Claude --- .changeset/25921-retire-plugin-ai.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .changeset/25921-retire-plugin-ai.md diff --git a/.changeset/25921-retire-plugin-ai.md b/.changeset/25921-retire-plugin-ai.md new file mode 100644 index 00000000000..832047f001a --- /dev/null +++ b/.changeset/25921-retire-plugin-ai.md @@ -0,0 +1,22 @@ +--- +"@object-ui/types": minor +"@object-ui/i18n": minor +"@object-ui/cli": minor +--- + +refactor(ui)!: retire `@object-ui/plugin-ai` and its three node types + +Clause-②: no (narrowing) +Metadata-protocol: narrowed + + + +**BREAKING.** `@object-ui/plugin-ai` is retired and 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. + +What leaves, and what to do: + +- FROM `import { AIFormAssist, AIRecommendations, NLQueryInput } from '@object-ui/plugin-ai'` TO nothing: drop the dependency and the import. A host that still wants the components keeps its own copy and registers it under its own type key. +- FROM a page node `{ "type": "ai-form-assist" }`, `{ "type": "ai-recommendations" }` or `{ "type": "nl-query" }` TO no node: remove it 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 its place whenever the package was not loaded. +- `@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. +- `@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. +- `@object-ui/cli`: `objectui check` no longer lists the three literals as known component types. From a138a39e465fdcfb43c93bf1ccb6d9f632784eac Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 22:29:56 +0000 Subject: [PATCH 07/13] chore(changeset): name each retired export on its own FROM line (WIP) Claude-Session: https://claude.ai/code/session_01VBDBR9oS1MNUcjBVXds5D6 Co-authored-by: Claude --- .changeset/25921-retire-plugin-ai.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.changeset/25921-retire-plugin-ai.md b/.changeset/25921-retire-plugin-ai.md index 832047f001a..1b89314c487 100644 --- a/.changeset/25921-retire-plugin-ai.md +++ b/.changeset/25921-retire-plugin-ai.md @@ -15,8 +15,10 @@ Metadata-protocol: narrowed What leaves, and what to do: -- FROM `import { AIFormAssist, AIRecommendations, NLQueryInput } from '@object-ui/plugin-ai'` TO nothing: drop the dependency and the import. A host that still wants the components keeps its own copy and registers it under its own type key. -- FROM a page node `{ "type": "ai-form-assist" }`, `{ "type": "ai-recommendations" }` or `{ "type": "nl-query" }` TO no node: remove it 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 its place whenever the package was not loaded. +- FROM `AIFormAssist` 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. +- FROM `AIRecommendations` TO nothing: the same fix. +- FROM `NLQueryInput` TO nothing: the same fix. +- 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. - `@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. - `@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. - `@object-ui/cli`: `objectui check` no longer lists the three literals as known component types. From bc1e8c138815952a508b78da6eb2bd205b34a109 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 22:45:07 +0000 Subject: [PATCH 08/13] chore(sdui): re-record the i18n and types registry records (WIP) Claude-Session: https://claude.ai/code/session_01VBDBR9oS1MNUcjBVXds5D6 Co-authored-by: Claude --- scripts/sdui-manifest.sources/i18n.json | 2 +- scripts/sdui-manifest.sources/types.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/sdui-manifest.sources/i18n.json b/scripts/sdui-manifest.sources/i18n.json index 84836d6c007..56b6b0cefea 100644 --- a/scripts/sdui-manifest.sources/i18n.json +++ b/scripts/sdui-manifest.sources/i18n.json @@ -1 +1 @@ -{"package":"ui/packages/i18n","sources":31,"sha256":"4bcb36ff124e98b8405929277c2acd9afdeb73347b814df67cf26a80727996af","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} +{"package":"ui/packages/i18n","sources":31,"sha256":"54ffad1aaf88eb4e1ff370f12c0bef96dae80911bdb8a6b0afeb4a81cca286b1","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} diff --git a/scripts/sdui-manifest.sources/types.json b/scripts/sdui-manifest.sources/types.json index c264a9f0078..9e1e918b9dd 100644 --- a/scripts/sdui-manifest.sources/types.json +++ b/scripts/sdui-manifest.sources/types.json @@ -1 +1 @@ -{"package":"ui/packages/types","sources":81,"sha256":"d7928a68db8d76df5f05933d139f9d3d46e2fd87f74945f69c74e079f1a8eda2","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} +{"package":"ui/packages/types","sources":81,"sha256":"d76d40992fa43e274c2c739a87ec2127077653dff6a13d18663391cd586027ee","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} From b97d2d956bbdee39ff6a66912ac5e189fc6ac3c6 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 22:48:03 +0000 Subject: [PATCH 09/13] test: settle the mirror-parity, handler-key census and markdown-input ledgers after the plugin-ai retirement (WIP) Claude-Session: https://claude.ai/code/session_01VBDBR9oS1MNUcjBVXds5D6 Co-authored-by: Claude --- .../component-docs-retired-handler-keys-7340.test.ts | 3 +++ .../types/src/__tests__/zod-mirror-parity.test.ts | 11 ++++++++--- ui/scripts/markdown-test-inputs.mjs | 8 +++----- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/ui/packages/types/src/__tests__/component-docs-retired-handler-keys-7340.test.ts b/ui/packages/types/src/__tests__/component-docs-retired-handler-keys-7340.test.ts index 71f7c15aafb..84735ff10f4 100644 --- a/ui/packages/types/src/__tests__/component-docs-retired-handler-keys-7340.test.ts +++ b/ui/packages/types/src/__tests__/component-docs-retired-handler-keys-7340.test.ts @@ -390,6 +390,9 @@ describe('the retired population is measured off the shipped tree (objectui#7340 // `deleteBehavior`, not a handler; no shipped interface declares the name // otherwise. 'onDelete', + // objectstack#25921 — its one LIVE declaration, `AIRecommendationsSchema.onDismiss`, + // left with `@object-ui/plugin-ai`; only `?: never` spellings remain. + 'onDismiss', 'onExpandChange', // objectui#7068: the legacy `ActionSchema.onFailure` callback object — no // shipped interface declares an `onFailure` at all any more. `onSuccess` diff --git a/ui/packages/types/src/__tests__/zod-mirror-parity.test.ts b/ui/packages/types/src/__tests__/zod-mirror-parity.test.ts index 8ae7d1352c2..ce9ebb65000 100644 --- a/ui/packages/types/src/__tests__/zod-mirror-parity.test.ts +++ b/ui/packages/types/src/__tests__/zod-mirror-parity.test.ts @@ -154,7 +154,12 @@ * a delta to this number; count the registry. Nothing asserts it against a written * one, so this line is prose and can rot; the pin that cannot is the one * comparing the two halves to each other. - * - **53 entries** in `KnownDrift`, **90 keys** across them — 55 / 93 until + * - **51 entries** in `KnownDrift`, **87 keys** across them — 53 / 90 until + * objectstack#25921 retired `@object-ui/plugin-ai`: its seven pairs left `MIRRORS` + * with it (their TS twins are `never` tombstones, their zod names `z.never()`), and + * with them the two objectui#6124 runtime-slot entries + * `ai.zod.ts#AIRecommendationsSchema` (`onDismiss`, `onSelect`) and + * `ai.zod.ts#NLQuerySchema` (`onSubmit`) — −2 entries, −3 keys. It was 55 / 93 until * objectstack#25923 made every object in `../zod/` `.passthrough()` and taught * `NarrowerThanDeclared` the interface rule (`AcceptedOf`): four * INTERFACE-TO-PASSTHROUGH keys LEFT — two entries whole @@ -912,7 +917,7 @@ * * ## KNOWN_DRIFT is a ratchet, not a waiver * - * 53 of the registered pairs carry TYPE drift TODAY (measured, not assumed). Each is + * 51 of the registered pairs carry TYPE drift TODAY (measured, not assumed). Each is * pinned to its EXACT drifted key set, so the entry fails when new drift appears on * that mirror AND when the recorded drift is fixed — a stale entry cannot rot * quietly. Correcting them is not one change: the pairs below split into DISJOINT @@ -5367,7 +5372,7 @@ const ZOD_DIR = join(dirname(fileURLToPath(import.meta.url)), '..', 'zod'); * MINUEND under it had moved. Nothing failed on any of those days, because nothing * compared the registry to a number. objectui#7433 is that absence, not the digits. */ -const EXPECTED_MIRROR_PAIRS = 204; +const EXPECTED_MIRROR_PAIRS = 197; /** * A ledger this file can size from its own AST. `WiderThanDeclared` joined at diff --git a/ui/scripts/markdown-test-inputs.mjs b/ui/scripts/markdown-test-inputs.mjs index 597fc08b383..cc70fdc99b4 100644 --- a/ui/scripts/markdown-test-inputs.mjs +++ b/ui/scripts/markdown-test-inputs.mjs @@ -253,15 +253,13 @@ export const ADJUDICATED = new Map([ // objectui#10872 batch 4 (its `QUICK_START` path): the first json fence under // the quick-start's "### Add Actions" heading -- the taught `action:button` // node -- which the page pin runs through `objectui validate` beside a - // `page:header`. The scanner also resolves the bare `'README.md'` segment of - // the file's prose against this package and the root; neither is read. (Its - // other read, `@object-ui/plugin-ai`'s README, left with that package, - // objectstack#25921.) + // `page:header`. (Its other read, `@object-ui/plugin-ai`'s README, left with + // that package, objectstack#25921, and with it the bare `'README.md'` segment + // the scanner used to resolve.) [ 'packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts', { reads: ['content/docs/guide/quick-start.md'], - notRead: ['README.md', 'packages/cli/README.md'], }, ], [ From 4bef30fa196aabfe3584a984627509b27276bfab Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 23:02:20 +0000 Subject: [PATCH 10/13] docs: lockstep package count 111 -> 110 after plugin-ai leaves the fixed group Claude-Session: https://claude.ai/code/session_01VBDBR9oS1MNUcjBVXds5D6 Co-authored-by: Claude --- content/docs/protocol/backward-compatibility.mdx | 4 ++-- scripts/publish-smoke-pack.mjs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/content/docs/protocol/backward-compatibility.mdx b/content/docs/protocol/backward-compatibility.mdx index 3ce98359950..29423b59a0c 100644 --- a/content/docs/protocol/backward-compatibility.mdx +++ b/content/docs/protocol/backward-compatibility.mdx @@ -116,7 +116,7 @@ Read the **Breaking?** column, not the version number: in the 17.x minors every 1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `protocol:breaking` label. 2. **Deprecation** — The old behavior is deprecated in a MINOR release (see timeline above). 3. **Migration Guide** — A detailed migration guide is published before the removal lands, in the release notes for the version that carries it. -4. **Release** — In the 17.x minors the breaking change shipped in the next **MINOR** version, carrying a changeset entry marked `**BREAKING**`, and `scripts/check-changeset-no-major.mjs` failed a pull request that declared a `major` bump, because under lockstep one `major` would promote all 111 published packages. During the v18 prerelease it shipped in the next `18.0.0-next.N`, because the guard stood aside in pre mode and a pull request could declare a major bump. Changesets has exited pre mode, so the guard fails a `major` bump again. See [how a breaking stage lands on the v18 line](/docs/releases/v18#how-a-breaking-stage-lands-on-this-line). +4. **Release** — In the 17.x minors the breaking change shipped in the next **MINOR** version, carrying a changeset entry marked `**BREAKING**`, and `scripts/check-changeset-no-major.mjs` failed a pull request that declared a `major` bump, because under lockstep one `major` would promote all 110 published packages. During the v18 prerelease it shipped in the next `18.0.0-next.N`, because the guard stood aside in pre mode and a pull request could declare a major bump. Changesets has exited pre mode, so the guard fails a `major` bump again. See [how a breaking stage lands on the v18 line](/docs/releases/v18#how-a-breaking-stage-lands-on-this-line). --- @@ -196,7 +196,7 @@ The `@objectstack/spec` package provides additional stability guarantees: ### Which surfaces this covers -**All of them.** It was not scoped to an experimental corner or a pre-release channel: all **111** packages published from this repository belong to a single Changesets `fixed` group, so they share one version number and one policy. No published surface was exempt. +**All of them.** It was not scoped to an experimental corner or a pre-release channel: all **110** packages published from this repository belong to a single Changesets `fixed` group, so they share one version number and one policy. No published surface was exempt. The convention was enforced rather than informal — `scripts/check-changeset-no-major.mjs` failed a pull request that introduced a `major` bump, because under lockstep a single `major` on one package would promote the entire stack. During the v18 prerelease Changesets was in pre mode with the tag `next` and the guard stood aside, so a pull request on the v18 line could declare a major bump. Changesets has exited pre mode, and the guard fails a `major` bump again. diff --git a/scripts/publish-smoke-pack.mjs b/scripts/publish-smoke-pack.mjs index bd334a4efd3..63610cb68b1 100644 --- a/scripts/publish-smoke-pack.mjs +++ b/scripts/publish-smoke-pack.mjs @@ -33,7 +33,7 @@ * a future exclusion cannot re-open the hole silently. * * Source of truth: the workspace itself. The Changesets `fixed` group in - * .changeset/config.json enumerates the same 111 names, but it is a DERIVED + * .changeset/config.json enumerates the same 110 names, but it is a DERIVED * declaration validated against the workspace by scripts/check-changeset-fixed.mjs * (which reddens both when a public package is missing from the group and * when a group name no longer exists), and it also holds the private members From db6ddb4d5ec12cee6e9ebe02c7725e1acec8993b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 00:04:43 +0000 Subject: [PATCH 11/13] test,docs: published-package count and license-text floor follow the plugin-ai retirement Claude-Session: https://claude.ai/code/session_01VBDBR9oS1MNUcjBVXds5D6 Co-authored-by: Claude --- ui/QUICK_REFERENCE.md | 2 +- ui/scripts/__tests__/package-files-exist.test.ts | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ui/QUICK_REFERENCE.md b/ui/QUICK_REFERENCE.md index e0a452d165d..808180493d3 100644 --- a/ui/QUICK_REFERENCE.md +++ b/ui/QUICK_REFERENCE.md @@ -87,7 +87,7 @@ pnpm changeset publish # Publish to npm (CI only) | Path | Purpose | | --- | --- | -| `packages/*` | 37 published packages (`@object-ui/*`), plus the private `vscode-extension`, `test-support` (test-only, never released) and `runner` (workspace demo app, never released) | +| `packages/*` | 36 published packages (`@object-ui/*`), plus the private `vscode-extension`, `test-support` (test-only, never released) and `runner` (workspace demo app, never released) | | `apps/console` | Full ObjectUI console app (Vite + React) | | `apps/site` | Public docs site at (fumadocs) | | `examples/*` | Runnable examples and the schema catalog — see [`examples/README.md`](./examples/README.md) | diff --git a/ui/scripts/__tests__/package-files-exist.test.ts b/ui/scripts/__tests__/package-files-exist.test.ts index 308a64caef9..f4b998ddd63 100644 --- a/ui/scripts/__tests__/package-files-exist.test.ts +++ b/ui/scripts/__tests__/package-files-exist.test.ts @@ -439,11 +439,12 @@ const KNOWN_LICENSE_TEXT_MISSING: Record = {}; describe('published packages that declare a license ship its text (objectui#3696)', () => { it('discovers the packages that owe license text (guard cannot pass by finding nothing)', () => { - // Same anti-vacuity floor as the guard above, sitting just under the 39 - // measured on main@3e601773e. A predicate that quietly matches nothing — + // Same anti-vacuity floor as the guard above, sitting just under the 37 + // measured once `@object-ui/plugin-ai` retired (objectstack#25921; 39 on + // main@3e601773e). A predicate that quietly matches nothing — // a renamed `license` field, a lost workspace root, a `private` default // flipped — would otherwise report success over an empty set. - expect(owingPackages.length).toBeGreaterThanOrEqual(38); + expect(owingPackages.length).toBeGreaterThanOrEqual(36); // The two packages objectui#3696 fixed must be in scope BY NAME. If either // drops out of the population, this guard has stopped watching the exact From 20d75f11f7befab915bd41707f660c44c0004327 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 01:14:22 +0000 Subject: [PATCH 12/13] chore(sdui): re-record the types registry record on top of main Claude-Session: https://claude.ai/code/session_01VBDBR9oS1MNUcjBVXds5D6 Co-authored-by: Claude --- scripts/sdui-manifest.sources/types.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/sdui-manifest.sources/types.json b/scripts/sdui-manifest.sources/types.json index ea9118118b3..1fc0e5b22b4 100644 --- a/scripts/sdui-manifest.sources/types.json +++ b/scripts/sdui-manifest.sources/types.json @@ -1 +1 @@ -{"package":"ui/packages/types","sources":81,"sha256":"f8450c19832a17c2f45e0164ec9e6e68cb247efdc00c81998c77e8c42c11da65","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} +{"package":"ui/packages/types","sources":81,"sha256":"844ad7ece1279e74c906b1b6b87e973a30a4b4512b74c7a4c6f24a57eec13c35","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} From bde1e7222c5b859e1cf49d459bf72d1525d919bf Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 07:05:56 +0000 Subject: [PATCH 13/13] test: the files-entry floor follows the plugin-ai retirement Claude-Session: https://claude.ai/code/session_01VBDBR9oS1MNUcjBVXds5D6 Co-authored-by: Claude --- ui/scripts/__tests__/package-files-exist.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/scripts/__tests__/package-files-exist.test.ts b/ui/scripts/__tests__/package-files-exist.test.ts index aef9134049d..5297090f445 100644 --- a/ui/scripts/__tests__/package-files-exist.test.ts +++ b/ui/scripts/__tests__/package-files-exist.test.ts @@ -233,8 +233,10 @@ describe('package.json `files` entries exist on disk (objectui#3663)', () => { // floors sit just under the measured values on main@dae1ac41e (40 packages // declaring `files`, 159 entries) so that losing a workspace root, a broken // glob parser or a moved directory is a failure and not a quiet pass. + // The entry floor came down when `@object-ui/plugin-ai` retired + // (objectstack#25921): 149 entries measured then. expect(packagesWithFiles.length).toBeGreaterThanOrEqual(38); - expect(declared.length).toBeGreaterThanOrEqual(150); + expect(declared.length).toBeGreaterThanOrEqual(145); // The specimen package must be in scope by name: if plugin-tree ever drops // out of the scan, the guard has stopped watching the exact spot that