fix(plugin-hono-server): drop the retired ui-plugin arm from UI auto-discovery - #20086
Conversation
…discovery The auto-discovery block matched `type === 'ui' || type === 'ui-plugin'` under a "Support legacy" comment. `ui-plugin` is outside the closed plugin-type set and both kernels' `use()` already refuse it through the shared plugin contract, so the disjunct was reachable through neither kernel's `use()`. Delete it and its comment. Pin C in ui-plugin-auto-discovery.pin.test.ts replaces its placeholder: C1 reads the contract's refusal of a declared `ui-plugin` on both kernels (envelope parity), and C2 proves the arm is gone behaviourally: an admitted `ui` entry flipped to `ui-plugin` before start() mounts nothing, with an in-case firing control. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TnPAC1UsTGfHPXVUCL6iLn
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 7 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 36089109896 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Merge-queue red on this PR's group: not this PR's; no action taken; the queue has rebuilt it on
|
Fixes #15638
Clause-②: no
What
This PR deletes the
plugin.type === 'ui-plugin'disjunct and its "Support legacy 'ui-plugin' and new 'ui' type" comment from the UI auto-discovery block inHonoServerPlugin.start()(packages/plugins/plugin-hono-server/src/hono-plugin.ts:521-522on66960564). The condition is nowplugin.type === 'ui' && plugin.staticPath.This is item 1 of the maintainer ruling B+ (5557089123), carried out on the director seat's execution terms (5644357902). Item 2, the boot seam refusing the retired spelling, was delivered by #16049's enforcement. This PR reads that refusal and does not re-implement it. There is no hint specific to the retired spelling: the generic closed-set refusal is the message.
This PR fills pin C of #16050 (the
it.todoinui-plugin-auto-discovery.pin.test.ts). #16050 stays open here; the seat handles it at landing.Reachability probe (the precondition, run before any edit)
A temporary probe (deleted, never committed) ran against the unmodified tree at
66960564. It drove the realObjectKernel.use()andLiteKernel.use(), then the realHonoServerPlugin.init()andstart(), and recorded the route registrations:type: 'ui-plugin', withstaticPathandslugFailed to load plugin: NAME - PLUGIN_CONTRACT_VIOLATION: ... at 'type'; not storedcode: PLUGIN_CONTRACT_VIOLATION; not storedtype: 'ui'/probe-consoleand/probe-console/*, two registrations each)type: 'driver'[][]uientry flipped toui-pluginbeforestart()Static census on the same tree:
The arm iterates
ctx.getKernel().plugins, and that map has exactly two writers, bothregisterPluginByName:ObjectKernel.use()(kernel.ts:253), afterloadPlugin,validatePluginContractandassertPluginContract;LiteKernel.use()(lite-kernel.ts:75), afterassertPluginContract.No other
plugins.set(orregisterPluginByName(call exists outside tests.The CLI's config-object path wraps a bundle that has no
initinAppPlugin.AppPlugin'stype = 'app'is a class field and is never copied from the bundle, and the wrapped object still enters throughkernel.use().typevalues handed tokernel.use()in the repo:standard,server,driver,objectqlandapp.type: 'plugin'hits aremanifest.register(...)package manifests, not kernel plugins.ui-pluginas a type value. The three non-test, non-doc mentions are package names in comments.type.Reading. No path admits a DECLARED
ui-pluginto the map on either kernel. The disjunct was unreachable through either kernel'suse(), which is the pin file's own reachability standard.The one residual is the last table row. The contract validates at
use()and stores the object by reference, so an object admitted asuithat later rewrites its owntypedid reach the arm. That is a bypass of the contract, not a path through it: the same bypass works for every key the contract checks, and nothing in the repo produces it. The changeset names it, C2 pins it, and it is flagged for the contract reviewer below.Clause-②
Declared from the delivered diff:
no.packages/spec/src/**is touched.ui-pluginwas already refused atuse()on both kernels before this diff.start()reads it, so I read it as outside the conformance limb. Reviewer: please confirm or overturn that reading.No
**BREAKING**banner and no ADR-0087 marker are carried.Pin C
These cases are in
packages/plugins/plugin-hono-server/src/ui-plugin-auto-discovery.pin.test.ts, using the file's own harness:ui-pluginfixture, otherwise a completeuifixture, is refused:LiteKernel.use(), withcode: PLUGIN_CONTRACT_VIOLATIONat'type';ObjectKernel.use(), with exactly that text behind the loader'sFailed to load plugin: NAME -prefix. This is the F1/F2 parity shape.uientry flipped toui-pluginbeforestart()mounts[]. The same case carries a firing control: same kernel, same fixture, no flip, 4 routes.observe()gains an optionalbeforeStarthook that receives the kernel's own admitted entry. C2 is its only caller.Evidence (at
7363f0f4)pnpm --filter @objectstack/plugin-hono-server test: 26 files, 314 tests passed.pnpm --filter @objectstack/plugin-hono-server typecheck: exit 0, andcheck:test-typecheckreports OK.tsconfig.test.json --listFilesOnlyincludes the pin file.scripts/ablation-replace.mjsin wrap mode:(plugin.type === 'ui' || plugin.type === 'ui-plugin') && plugin.staticPath. The anchor went from 1 hit to 0 and the blob from201c5866toca844899.expected [ '/console-fixture', …(3) ] to deeply equal []. The failure went the expected way (to red).201c5866, equal to HEAD, andgit diff HEADis empty.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives 60 commands, identical to the dispatch list. All 60 ran and exit 0.--rananswers "60 derived, 60 run, 0 NOT-MEASURED, 0 UNRUN (a DERIVED zero)".check:dual-build-cjs-loads,check:lean-entry-closureandcheck:type-check-debtfirst exited 3 (PREREQUISITE NOT MET). Afterturbo run build --filter='./packages/*' --filter='./packages/*/*'(72/72 tasks), all three exit 0.check-changeset-fixed,check:authz-resolver,check:error-code-casingandcheck:filter-alias-parity.pnpm lint, the full union: exit 0.Acceptance notes
use(): theplugin.slug || plugin.name.split('/').pop()derivation and the&& plugin.staticPathconjunct. That is already recorded on [Decision] The declared plugin contract does not govern what the runtime accepts:LiteKernelnever runsPluginSchema, andisDefaultsurvives the kernel that does #16721. This ruling does not cover them, so they are left untouched.packages/core/src/plugin-contract.ts: the parse output is discarded and the object is stored by reference). It applies to every contract key, not onlytype, and it is not a defect of this card.default/isDefaultredirect is still unpinned, as the pin file's header already says.packages/core/src/plugin-loader.tsandpackages/spec/**are not touched.CHANGELOG.mdandpackage.jsonare left to the Version Packages PR.Generated by Claude Code