Uh oh!
There was an error while loading. Please reload this page.
feat(core): init 阶段取服务的声明契约改为强制 — check:init-service-contract 门禁 (#4471) - #4631
Conversation
…ate (#4471) ADR-0116's ordering contract (dependencies / optionalDependencies / requiresServices / providesServices) was complete but voluntary: a plugin resolving getService('X') during init() while declaring nothing was invisible to every check, failing only under unlucky composition orders — the #4085 and #4420 failure class, the latter at data-consistency cost. - scripts/check-init-service-contract.mjs: AST scan of every plugin unit (classes and object literals) under packages/. Walks the init() call graph transitively (same-class methods, same-file functions — the #4420 call sat in a private helper), skips deferred callbacks (hooks) and start(), and errors on any init-reachable getService of a workspace-provided service not covered by a declaration. 12-case --self-test pins the #4420 pre-fix shape caught, all declared shapes passing, and the start()/hook shapes exempt. - Wired as check:init-service-contract in root package.json and lint.yml. - Declares the 11 previously undeclared init-time consumers the gate found (metadata, rest, two cli serve plugins, analytics, datasource-admin, job, knowledge, queue, settings, storage) via optionalDependencies on their providers — declared tolerance in the plugin, never a checker-side ledger. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012C2cd7tL8QDoZ2QKN3djJ5
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 11 package(s): 53 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#4471
问题
ADR-0116 /
packages/core/src/plugin-order.ts的顺序契约机制是完备的(dependencies硬依赖、optionalDependencies有则排前、requiresServices+assertInitServiceRequirements具名报错),但所有声明都是自愿的:一个插件在init()里getService('X')却什么都不声明,没有任何一层会指出来——只会在某个组合顺序下静默拿不到服务,而 miss 又通常藏在 best-effort 的 try/catch + warn 里。#4085 和 #4420 都是这么发生的,后者的代价是数据一致性级别(每次重启丢光在途审批)。方案(issue 期望的方向一:静态
check:*门禁)scripts/check-init-service-contract.mjs— 用 TypeScript AST 扫描packages/下所有插件单元(class 形态与对象字面量形态都覆盖),规则:getService('X'),且 X 由某个 workspace 插件的providesServices声明提供,消费方必须在dependencies/optionalDependencies里提到该 provider,或在requiresServices里提到该服务——否则报错(确定性地在 CI 挂掉,而不是概率性地在某次 boot 挂掉)。getService('manifest')在 init 调的私有方法registerRunObject里、还包着 try/catch——只匹配 init 正文的检查恰好漏掉真正出过事的形态。扫描从 init() 出发,传递跟随同类方法调用与同文件函数调用。start()阶段的消费(apps/setup/studio/account的低风险模式,issue 明确说不必按同一把尺子量)、无 workspace provider 的服务(无从声明顺序)。optionalDependencies(有则排前、缺则按声明降级,即 fix(automation,approvals): 审批决策不能在流程原地不动的情况下"成功" (#4420) #4460 的参考写法),checker 里没有任何豁免清单。--self-test(CI 中先于审计运行):pin 住 [automation/approvals] 进程重启后审批决策静默失效:挂起 flow run 仍只存内存(#1518 标记 COMPLETED 但 17.0.0-rc.1 未生效),approve 落库却永不推进且零报错 #4420 修复前形态被抓、已声明的各形态全部通过、start()/hook 形态不误报、对象字面量插件也在覆盖内、递归 helper 终止等。接入:根
package.json的check:init-service-contract+lint.yml新步骤;plugin-order.ts头注释补充"声明不是自愿的"一段,指向该门禁。门禁在现有代码上的初次收获(修复前全树 11 处未声明,全部在本 PR 补上声明)
首次运行即抓出 11 处未声明的 init 期消费——正是 issue 说的"少数漏网的那个最贵":
@objectstack/metadataMetadataPluginmanifestoptionalDependencies: [objectql]@objectstack/restrest-api-pluginmanifest(注册 sys_import_job)@objectstack/cliserve.ts 两个内联插件http.server/http-serveroptionalDependencies: [com.objectstack.server.hono]data(auto-bridge 探测)optionalDependencies: [objectql]manifest(Setup 导航)manifest+clusteroptionalDependencies: [objectql, service.cluster]objectql(RLS 复查)optionalDependencies: [objectql]manifest(注册 sys 表)每一处都保持原有的"缺 provider 时按设计降级"语义不变——变化只是:两者同时组合时,内核现在确定性地把 provider 排在前面,不再靠注册顺序的运气。
验证
node scripts/check-init-service-contract.mjs --self-test→✓ self-test: 12 cases(含 [automation/approvals] 进程重启后审批决策静默失效:挂起 flow run 仍只存内存(#1518 标记 COMPLETED 但 17.0.0-rc.1 未生效),approve 落库却永不推进且零报错 #4420 修复前形态被抓的 pin)✓ init-service contract: 41 declared / 1 self-provided / 3 without a workspace provider (73 plugin unit(s) scanned)pnpm turbo typecheck --filtercore/metadata/rest/cli/services → 64 tasks successfulpnpm turbo test --filtercore/metadata/rest/services → 37 tasks successful(rest 554 tests 全过);cli → 54 tasks successful范围外
issue 里"另外值得单独定一条"的日志级别约定(best-effort 降级导致"看起来正常、实则不持久"时不应是 warn)未在本 PR 实现,已另开 issue 跟踪(见 PR 评论关联)。
🤖 Generated with Claude Code
https://claude.ai/code/session_012C2cd7tL8QDoZ2QKN3djJ5
Generated by Claude Code