Skip to content

Commit d42e06b

Browse files
docs(adr-0026): caption the section 3.1 example as the proposed shape, and spell the design doc's UI plugin type ui (#19862)
Fixes #16140 Clause-②: no This PR executes ruling `5793373857` (summon 28, class-1 item 2, letter B, director seat, 2026-09-23T10:41Z) and nothing else. It touches two files: `docs/adr/0026-client-ui-plugin-distribution.md` and `docs/design/plugin-distribution.md`. ## What changed - **ADR-0026, section 3.1.** One dated note sits under the example, after the existing 2026-09-07 note. It says the example is the **proposed** shape. It names what the current `ManifestSchema` (`packages/spec/src/kernel/manifest.zod.ts#ManifestSchema`) refuses in it: `runtime: "ui"` (the `PluginRuntimeSchema` enum is `node` / `sandbox` / `worker`), `engines.objectui` and `engines.react`, the top-level `ui` block, and `permissions.data` and `permissions.navigation`. It also says `name` is omitted. - The caption's last sentence makes the ruling's reading of the general rule visible: an example validates against the current schema, or carries a caption that names the keys it proposes beyond it. The rule's recorded text lives in ruling `5563454219`. It is not restated or rewritten here. - The example, the 2026-09-07 note and all of the proposal text are unchanged. The ADR diff is +11 / -0. - **`docs/design/plugin-distribution.md`.** Its two `type: ui-plugin` spellings, in section 4.2 and in worked example C of section 10, now read `type: ui`. One note under section 4.2 records the original spelling. It copies the shape PR #19769 (`c118524061`) used on the ADR. The rest of the prose is unchanged. This file's diff is +8 / -2. ## Measurement: every claim in the caption, taken before it was written The instrument is a throwaway probe and is not committed. It takes the section 3.1 jsonc block from the committed file (`git show REV:docs/adr/0026-client-ui-plugin-distribution.md`) and strips the `//` comments that sit outside strings. It then parses the result with `ManifestSchema`. It ran on one tree through two resolution paths, and they agree line for line: - `packages/spec/src`, through `tsx`; - the built `packages/spec/dist/kernel/index.mjs`, which is the `./kernel` import entry in the package's `exports`. The spec source is `origin/main` `8cbc3c0084`. This branch changes no file under `packages/`. The whole example as written fails against `ManifestSchema` (`success=false`) with five issues: ```text path=["name"] code=invalid_type expected=string path=["permissions"] code=invalid_union union-branch[0] code=invalid_type expected=array (the legacy string[] form) union-branch[1] code=unrecognized_keys keys=["data","navigation"] path=["engines"] code=unrecognized_keys keys=["objectui","react"] path=["runtime"] code=invalid_value values=["node","sandbox","worker"] path=[] code=unrecognized_keys keys=["ui"] ``` **Firing control, on the same instrument.** Remove `runtime`, `engines`, `ui` and `permissions` from the example and add a `name`, and it parses with `success=true`. So those five issues are the whole refusal. No other part of the example (`id`, `version`, `type`, `integrity`) is refused, and no refusal hides behind another. **Reading against the ruling's list.** The two sets are the same. Every item the ruling names is refused, and no refusal falls outside the ruling's list. One detail differs in kind. `runtime` is a declared key, and it is refused with `invalid_value` because the value `"ui"` is outside the enum, not with `unrecognized_keys`. So the caption writes `runtime: "ui"` and names the enum, as the ruling does. It does not call `runtime` an undeclared key. **`runtime`'s enum at source.** `PluginRuntimeSchema = z.enum(['node', 'sandbox', 'worker'])` in `packages/spec/src/kernel/manifest.zod.ts`. The probe reads `PluginRuntimeSchema.options` as `["node","sandbox","worker"]`. The example parses the same way at `8cbc3c0084` and at this branch's head `e59f601c56`. None of the example's bytes changed. The design doc's note, measured with the same instrument: ```text ManifestSchema.pick({type}) {type:"ui-plugin"} -> ["type"] invalid_value ManifestSchema.pick({type}) {type:"ui"} -> success=true PluginSchema {name:"x", type:"ui-plugin"} -> ["type"] invalid_value CORE_PLUGIN_TYPES = ["ui","driver","server","app","theme","agent","objectql"] ``` ## Why this closes the card rather than saying `Part of` The card carries two rulings: - `5563454219` (batch 61, option A) was executed by PR #19769, which the maintainer merged as `c118524061`. - `5793373857` (letter B) answers the one residue that round released with (`5787494422` / `5792259933`). All three of its items are in this diff: the caption, the reading of the rule, and the design doc. After this lands, the ruling leaves nothing else open on the card. #15638, the runtime tolerance arm in `plugin-hono-server`, is a separate card. It remains open and is not addressed here. The card is `pm:dispatched`, not `needs-user-decision`, so a merge cannot close a card that is still waiting for a decision. The merge is also a human act (tier H), so the card closes only when the maintainer lands the caption. ## Changeset This PR has no changeset. Measured: - 70 non-private `package.json` manifests carry 217 `files[]` entries, and none of them reaches `docs/`. The repo-root `package.json` is private. - Strings unique to the two examples, `com.acme.signature-field` and `fancy gauge`, have 0 hits across the built `packages/spec/dist` and `packages/spec/json-schema`. The positive control `PLUGIN_UI_REQUIRED_KEY_MISSING` hits 83 files there. So the repo's route is the `skip-changeset` label. AGENTS.md (Post-Task Checklist, step 3) reserves that label for a diff that publishes nothing from any released package, and ruling `5563454219` named the same route for the first round. This PR neither adds nor edits a `.changeset/*.md`, so the route-0 exclusion does not apply. The dispatch forbids this seat any label write, so the label is **not** applied here. `Check Changeset` stays red until a seat applies it. It is not a required context. ## Gates (at `e59f601c56`) `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived 19 commands for this change set: 2 paths against merge base `8cbc3c008`. All 19 ran, and each exited 0. `pnpm --filter @objectstack/lint run check:doc-formula-expressions` first stopped at `PREREQUISITE NOT MET` (exit 3, nothing measured). The gate printed its fix, `pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint`. That is 4 packages, and it ran under the shared verify lock. After it, the gate exited 0. The `--ran` reconciliation reads: "19 derived famil(ies) accounted for — 19 run, 0 NOT-MEASURED (a DERIVED zero — all 19 recorded an exit code and none of them is 3)". Selected verdict lines: - `check-adr-links`: 690 relative link destination(s) under docs/adr/ resolve. - `check-adr-symbol-anchors`: 2123 anchors across 140 records resolve. The caption's new anchor `packages/spec/src/kernel/manifest.zod.ts#ManifestSchema` is one of them. It is extracted from the ADR at head (2 anchors in the file, against 1 at base) and resolves as a `declaration`. - `check:doc-authoring`: no growth. - `check:nul-bytes`: OK, no raw ASCII control bytes. This PR touches no package source, so it owes no package test or typecheck. The spec, formula, sdui-parser and lint builds served two things only: the doc-formula gate and the dist leg of the probe. `dispatch-gates` names some families as outside its 19 commands: the artifact rosters, the wide-population families, the path-scheduled `Test Core` job and the type-check lanes. CI runs those, and it runs repo-wide scans such as `pnpm lint`. ## Acceptance notes - **Placement.** The ruling says "under the section 3.1 example", and the example already carries one dated note (2026-09-07). The caption goes directly after that note. Both dated notes now sit under the example in date order, with no prose between them. - **The design doc still spells `runtime: 'ui'` / `runtime: ui`**, in its section 4.2 heading and in worked example C. That is ADR-0026's proposed tier, and the current `PluginRuntimeSchema` refuses it. It is left alone for two reasons. The ruling limited this file's treatment to its two `type: ui-plugin` spellings. And the section 4.2 heading points at ADR-0026 section 3.1, which now carries the caption. Noted, not filed. - The two closed-set pins are untouched: `packages/core/src/plugin-type-closed-set.test.ts` and `packages/rest/src/plugin-type-closed-set.pin.test.ts`. ## Governed surface `docs/adr/**` is tier H on the governed-surface register. This PR stays **draft**. It lands only by the maintainer's hand, or by an authorized approval from `GOVERNED_APPROVERS`. This seat does not flip it to ready, queue it or arm auto-merge. ## 维护者速读(草稿) **改了什么**:ADR-0026 §3.1 的 UI 插件清单示例下面加了一条带日期的说明,写明这是「提议中的形状」。说明点名了当前 `ManifestSchema` 不接受的部分:`runtime: "ui"`(当前只有 `node` / `sandbox` / `worker`)、`engines.objectui/react`、顶层 `ui` 块、`permissions.data/navigation`,还说明示例缺少必填的 `name`。示例本身和提议正文一字未改。另外,`docs/design/plugin-distribution.md` 里的两处 `type: ui-plugin` 改成了 `type: ui`,并加了一条注记说明原来的拼写,做法和上一轮改 ADR 时相同。 **为什么改**:执行裁决 `5793373857`(字母 B)。上一轮把 `type` 改对以后,整个示例仍会被 schema 拒收 5 处。裁决的选择是加一行说明,而不是改写示例。 **风险与代价(含回滚)**:纯文档改动,不发布任何包,对运行时零影响。说明里的每一条都先用 schema 实测过,结果与裁决列出的完全一致。回滚方法就是 revert 本 PR。 **席位意见**: **你要做的**:审阅后合并。ADR 属于治理面,需要您本人合并。另外,`Check Changeset` 要等有人挂上 `skip-changeset` 标签才会变绿;它不是必需检查,不会挡住合并。 --- _Generated by [Claude Code](https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 55cd8d4 commit d42e06b

2 files changed

Lines changed: 19 additions & 2 deletions

File tree

‎docs/adr/0026-client-ui-plugin-distribution.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,17 @@ A UI plugin is an `.osplugin` with:
104104
> `ManifestSchema` and `PluginSchema` refuse `ui-plugin`; the example above and
105105
> §3.7 use `ui`.
106106
107+
> **Note (2026-09-23, #16140) — the example above is the proposed shape.** It
108+
> is kept as this ADR proposes it, and the current `ManifestSchema`
109+
> (`packages/spec/src/kernel/manifest.zod.ts#ManifestSchema`) refuses it as
110+
> written. What it proposes beyond that schema: `runtime: "ui"` (the
111+
> `PluginRuntimeSchema` enum is `node` / `sandbox` / `worker`);
112+
> `engines.objectui` and `engines.react`; the top-level `ui` block; and
113+
> `permissions.data` and `permissions.navigation`. It also omits `name`, which
114+
> `ManifestSchema` requires. The rule for pasteable ADR examples is read this
115+
> way: an example validates against the current schema, or carries a caption
116+
> that names the keys it proposes beyond it.
117+
107118
`extends` reuses the existing `capabilities.extensionPoints`/`extensions` seam;
108119
`ui.shared` lists singletons the host injects (never bundled — same externalize
109120
rule as ADR-0025 §3.3, applied to browser deps).

‎docs/design/plugin-distribution.md‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,17 @@ Compiled manifest adds four blocks to the existing `ObjectStackManifest`:
124124

125125
### 4.2 UI plugin (`runtime: 'ui'` — ADR-0026 §3.1)
126126

127-
Same `.osplugin` envelope, but `type: ui-plugin`, `dist` is a **browser ESM
127+
Same `.osplugin` envelope, but `type: ui`, `dist` is a **browser ESM
128128
bundle** default-exporting a `register(host)` function, and the manifest carries a
129129
`ui` block (`entry`, `shared` singletons, `extends` extension points) plus
130130
client-scoped `permissions` (data read/write via SDK, network → CSP, navigation).
131131

132+
> **Note (2026-09-23, #16140).** This document originally spelled the UI plugin
133+
> package type `ui-plugin`. The closed set of plugin types (`CORE_PLUGIN_TYPES`,
134+
> `packages/spec/src/kernel/plugin.zod.ts`) adopted `ui`, and both
135+
> `ManifestSchema` and `PluginSchema` refuse `ui-plugin`; this section and
136+
> worked example C in §10 use `ui`.
137+
132138
### 4.3 Packaging strategies (default: bundled)
133139

134140
- **Bundled (default).** `tsup --noExternal` bundles third-party JS into `dist`;
@@ -274,7 +280,7 @@ the **App**; the connector rides inside it. Stripe API key → KV secret-ref at
274280
install.
275281

276282
**C. Community "fancy gauge" widget (UI, U1 default).**
277-
`type: ui-plugin`, `runtime: ui`, unverified publisher → **U1 iframe** only, CSP
283+
`type: ui`, `runtime: ui`, unverified publisher → **U1 iframe** only, CSP
278284
`connect-src` empty, data via scoped SDK (RLS-gated). Bundled into whatever App's
279285
dashboard uses it, or operator-enabled org-wide.
280286

0 commit comments

Comments
 (0)