Repository navigation
docs(adr-0026): spell the UI plugin package type ui, note the original ui-plugin - #19769
Conversation
…nal `ui-plugin` The section 3.1 manifest example and the section 3.7 prose spelled the package type `ui-plugin`, a value both ManifestSchema and PluginSchema refuse. The closed set (CORE_PLUGIN_TYPES) adopted `ui`. Both occurrences now read `ui`, and one note under the example records the proposal's original spelling. Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewVERDICT: PASS — nothing blocking. Head reviewed: Reading moment: this record was written at 2026-09-23T01:51Z. A contract-review record binds ONE head and one reading moment.
Reviewed-by: isolated at-tier subagent dispatched by Tier — measured by the SEAT from the reviewer's transcript
What was proven
The residue — a fact check, reproduced, and ⛔ not a verdict on the rulingThe reviewer reproduced the round's residue exactly: against the built CI at this head38 check runs, all Generated by Claude Code |
…, and spell the design doc's UI plugin type `ui` (objectstack-ai#19862) Fixes objectstack-ai#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 objectstack-ai#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 objectstack-ai#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. objectstack-ai#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>
Part of #16140
Clause-②: no
This PR executes ruling 5563454219 on
docs/adr/0026-client-ui-plugin-distribution.mdand nothing else. The ruling came from the director seat in decision batch #61 (option A); the maintainer's reply, verbatim, was 「同意」.What changed
:80):"type": "ui-plugin"is now"type": "ui".:170on base,:176after the note):type: ui-pluginis nowtype: ui.ui-pluginwas this proposal's original spelling and that the closed set (CORE_PLUGIN_TYPES) adoptedui. The ADR's argument, itsProposedstatus line and every other line are unchanged.The diff is 1 file, +8 / -2. Two things are deliberately untouched:
packages/core/src/plugin-type-closed-set.test.tsandpackages/rest/src/plugin-type-closed-set.pin.test.ts;plugin-hono-serverstill accepts the legacyui-plugintype thatPluginSchemarefuses — an unreachable arm under ADR-0049 #15638, the runtime tolerance arm inplugin-hono-server. It remains open and is not addressed here.Premise check (base
ed4b655e5b)**Status**: Proposed.ui-pluginoccurs exactly twice in the file: at:80, inside the jsonc example, and at:170, in prose.CORE_PLUGIN_TYPES(packages/spec/src/kernel/plugin.zod.ts:90) isui, driver, server, app, theme, agent, objectql. There is noui-plugin.Two of the card's readings have drifted since it was filed. Neither affects the ruling:
ManifestSchema.typeenum is now atmanifest.zod.ts:430, andManifestSchemais now astrictObject, which refuses unknown keys.PluginSchema.safeParse({type:'ui'})is no longersuccess=true:type: 'ui'now requiresstaticPathandslug(PLUGIN_UI_REQUIRED_KEY_MISSING). The reading on thetypekey itself still holds on both schemas.Proof: the example against the built schema
The instrument is a throwaway probe, not committed. It takes the section 3.1 jsonc block from the committed file at each revision (
git show REV:docs/adr/...) and strips the//comments that sit outside strings. It then parses the result withManifestSchemaandPluginSchemafrom a freshpnpm --filter @objectstack/spec buildof this branch, loaded through the package'sexportsmap (packages/spec/dist/kernel/index.js, notsrc/). "Before" is baseed4b655e5band "after" ise968910ad5.The
typekey, on both schemas. The before leg is the firing control: the same instrument refuses it.The example's keys whose values the current schema already accepts (
id,version,type,integrity), parsed withManifestSchema.pickof those keys:The whole example, as pasted, against
ManifestSchema:Acceptance notes
This edit alone does not meet the ruling's general rule. The rule says any example under
docs/adr/**that an author could paste must validate against the current schema. After this edit,ManifestSchemastill refuses the section 3.1 example on five counts, and none of them istype:runtime: "ui":PluginRuntimeSchemaaccepts onlynode,sandboxorworker. The example itself annotates this value "new tier".engines.objectuiandengines.react:PluginEnginesSchemadeclares onlyplatformandprotocol.uiblock is not aManifestSchemakey.permissions.dataandpermissions.navigation: the structured branch declares onlyservices,hooks,networkandfs.nameis required byManifestSchema, and the example never had it.The first four are schema extensions the proposal makes and that were never adopted. Correcting them would change what the ADR proposes, which the ruling does not authorise, so this PR leaves them alone and reports them to the seat as an open question.
PluginSchemais not the example's schema, because the example is an.ospluginpackage manifest.PluginSchemais a non-strictz.objectand strips the manifest's keys. On the after leg it trades thetyperefusal forPLUGIN_UI_REQUIRED_KEY_MISSINGonstaticPathandslug, which are runtime-plugin keys that a package manifest does not carry. This is recorded for completeness and is not a defect in the example.The same spelling survives in prose at
docs/design/plugin-distribution.md:127and:277(type: ui-plugin). That file is outside this card's one-file write surface, so it is reported to the seat and not changed here.Changeset
There is no changeset.
skip-changesetis the right label, as the ruling says. Measured:package.jsonmanifests scanned; 0 of theirfiles[]entries reachdocs/adr/.com.acme.signature-field) has 0 hits across the built@objectstack/specdist/andjson-schema/. The positive control,PLUGIN_UI_REQUIRED_KEY_MISSING, has 16.This PR does not write the label.
Gates (at
e968910ad5)node scripts/pm/dispatch-gates.mjs --commandsderived 19 commands for this change set. All 19 were run, and each exited 0.check:doc-formula-expressionsfirst stopped atPREREQUISITE NOT MET(exit 3, nothing measured); after building@objectstack/formulaand@objectstack/lintit exited 0.The
dispatch-gates --ranreconciliation 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/ resolvecheck-adr-symbol-anchors: 2122 anchors across 140 records resolvecheck-adr-anchors: OKcheck:doc-authoring: no growthcheck-nul-bytes: OK, no raw ASCII control bytesNo package source is touched, so no package build, test or typecheck is owed. The
@objectstack/specbuild served the proof only. Repo-wide scans such aspnpm lintrun in CI.Governed surface
docs/adr/**is tier H on the governed-surface register. This PR stays draft. It lands only by a human merge throughGOVERNED_APPROVERS.维护者速读(草稿)
改了什么:ADR-0026 §3.1 manifest 示例和 §3.7 正文里的包类型
ui-plugin改成了ui;示例下方加了一条注记,说明ui-plugin是本提案最初的拼写,闭集CORE_PLUGIN_TYPES后来采用的是ui。只改这一个文件,+8/-2。为什么改:执行第 61 批裁决的选项 A(您回复「同意」)。原来作者照抄示例时,
type: "ui-plugin"会同时被ManifestSchema和PluginSchema拒收。风险与代价(含回滚):纯文档,不发布任何包,运行时零影响;回滚就是 revert 本 PR。有一点需要您知道:改完后
type已经能通过,但整个示例仍会被ManifestSchema拒收 5 处,即runtime: "ui"、engines.objectui/react、顶层ui块、permissions.data/navigation,以及缺少必填的name。前四处是提案自己提出、至今未被采纳的 schema 扩展。因此光靠本 PR 还满足不了裁决附带的通则「可照抄的示例必须能通过当前 schema」,这一点已作为待决问题交给席位。席位意见:
你要做的:审阅后合并(这是治理面,需要您本人或
GOVERNED_APPROVERS批准)。另外请就上面剩下的 5 处拒收定一个方向。Generated by Claude Code
Generated by Claude Code