Skip to content

feat(core,runtime): plugin ordering is a declared, kernel-enforced contract (ADR-0116, #4131) - #4163

Merged
os-zhuang merged 2 commits into
mainfrom
claude/plugin-ordering-convention-t9ylrz
Jul 30, 2026
Merged

feat(core,runtime): plugin ordering is a declared, kernel-enforced contract (ADR-0116, #4131)#4163
os-zhuang merged 2 commits into
mainfrom
claude/plugin-ordering-convention-t9ylrz

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #4131#4085 的结构性根因)。按 issue 中倾向的方案落地:方案 1 的排序语义 + 方案 2 的校验部分,并配 ADR(ADR-0116)。

内核契约(@objectstack/core

Plugin 接口新增三个字段,两个内核(ObjectKernel / LiteKernel)通过同一份实现(新的 plugin-order.ts,同时消灭了两份逐字节相同的 resolveDependencies 拷贝)执行:

声明 语义
optionalDependencies order-if-present:已组装则与 dependencies 一样拓扑提前(真实边,参与环检测);未组装则静默跳过,不报错
requiresServices init() 同步硬取的服务。两段校验:① Phase 1 之前 —— 所需服务的唯一已声明 provider 排在自己之后 ⇒ 指名报错(双方插件名 + slot + 修法),未产生任何 init 副作用;② 每个 init 执行前的权威复查 —— 此刻缺失即原地指名报错(正是旧裸崩 Service not found 的位置,零误报)
providesServices init() 无条件注册的服务,驱动上面校验与诊断。字段文档明确:条件注册(如 option-gated 的 protocol)不得声明

没有声明的插件也拿到诊断:Phase 1 期间的 getService miss 现在会附上「哪个插件正在 init、该服务由哪个已组装 provider 声明、该怎么声明依赖」。Service '<name>' not found 前缀与 factory 路径的 is async - use await 逐字保留(有消费方做字符串匹配)。

刻意不做:从服务声明自动排序(D4)——排序只来自 dependencies/optionalDependencies,服务声明只驱动校验与报错;声明完备度建立起来之后再议。

首批声明方

  • AppPluginoptionalDependencies = ['com.objectstack.engine.objectql'] + requiresServices = ['manifest'](empty-env 无操作路径上清空,engine-less 内核照常启动)。objectql 刻意requiresServices —— init 里对它的每次取用都在 try/catch 后面,属于 degrade 而非硬需求。
  • ObjectQLPluginprovidesServices = ['objectql','data','manifest','lifecycle'](排除 option-gated 的 protocol/objects/analytics)。
  • MetadataPluginprovidesServices = ['metadata']

回归测试 app-plugin.ordering.test.ts真实内核摆出 #4085 的原始组合(AppPlugin 注册在 engine 之前):现在任何 slot 都能正确启动;真正缺 manifest provider 的组合在 init 副作用之前指名失败。

其它

  • ADR-0116docs/adr/0116-plugin-ordering-declared-contract.md):完整决策记录,含「一次是意外,两次是缺少被强制的契约」的历史与 rejected 方案 3(document + lint)。
  • serve.ts / standalone-stack.ts 的两处排序注释更新为「已声明、已强制」的现状(fix(cli,runtime): a named artifact that is missing is a broken instruction, not an empty boot — and the ordering claims say what the kernel resolves (#4110 follow-up, #4131) #4138 已修的注释保持不动)。
  • spec:contracts/plugin-validator.ts 接口 + kernel/plugin-validator.zod.ts schema 增加同名字段;content/docs/protocol/kernel/lifecycle.mdx 增加 Plugin Ordering Contract 一节;生成物 gate 全绿(check:docs/check:api-surface/check:authorable-surface 等 8 项 PASS)。
  • changeset:core/spec/runtime minor,objectql/metadata patch;纯增量,未声明的插件语义逐字不变。

验证

  • 全仓 pnpm test 通过(turbo 71 任务全绿,exit 0);重点包单独跑过:core 437+19 新、objectql 1208、metadata 279、runtime 935+3 新、cli 881、spec 272 文件。
  • 全仓 pnpm build 71/71 成功。

🤖 Generated with Claude Code

https://claude.ai/code/session_014DQuJBNpwStpJvo3owBw2B


Generated by Claude Code

…ntract (ADR-0116, #4131)

The kernel resolves init/start order from the plugin dependency graph, so
kernel.use() registration order proves nothing — yet a plugin that needs a
service at init when its provider is composed, while also booting without
the provider, had no way to declare that. AppPlugin was the standing
example (#4085: manifest grabbed in init before ObjectQLPlugin registered
it, correct only by slot convention).

- Plugin contract gains optionalDependencies (order-if-present),
  requiresServices (validated pre-Phase-1 and immediately before each
  init, with named structural errors), and providesServices (powers the
  validation and diagnostics).
- One shared topological sort in plugin-order.ts replaces the two
  byte-identical copies in ObjectKernel and ObjectKernelBase.
- A getService miss during Phase 1 now names the initializing plugin and
  the composed provider, so even undeclared plugins fail diagnosably.
- AppPlugin declares the engine soft dependency + the manifest init
  requirement (cleared on the empty-env no-op path); ObjectQLPlugin and
  MetadataPlugin declare what their inits register.
- ADR-0116 records the decision; lifecycle.mdx documents the contract;
  spec contract + validator schema carry the new fields; regression test
  boots a real kernel in the exact #4085 composition.

Closes #4131

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DQuJBNpwStpJvo3owBw2B
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 30, 2026 1:52pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Jul 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 6 package(s): @objectstack/cli, @objectstack/core, @objectstack/metadata, @objectstack/objectql, @objectstack/runtime, @objectstack/spec.

126 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/actions-as-tools.mdx (via @objectstack/core)
  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/knowledge-rag.mdx (via @objectstack/core)
  • content/docs/ai/natural-language-queries.mdx (via @objectstack/core)
  • content/docs/ai/skills-reference.mdx (via packages/cli, @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/cli, packages/runtime, @objectstack/spec)
  • content/docs/api/data-flow.mdx (via @objectstack/cli)
  • content/docs/api/environment-routing.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/api/wire-format.mdx (via @objectstack/runtime)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/cli, @objectstack/runtime, packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/core, @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/metadata, @objectstack/objectql, packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/core, packages/runtime, packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via packages/objectql, @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/backup-restore.mdx (via @objectstack/cli)
  • content/docs/deployment/cli.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/deployment/index.mdx (via @objectstack/runtime)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/core, @objectstack/objectql)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/runtime)
  • content/docs/deployment/self-hosting.mdx (via @objectstack/cli)
  • content/docs/deployment/single-project-mode.mdx (via @objectstack/runtime)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/deployment/vercel.mdx (via @objectstack/objectql, @objectstack/runtime)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/cli, @objectstack/runtime, @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via packages/metadata, @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/core, @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/cli)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx (via @objectstack/core)
  • content/docs/kernel/runtime-services/index.mdx (via packages/cli, packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/core, @objectstack/metadata, @objectstack/objectql, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/core, @objectstack/objectql, @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/cli, @objectstack/core, @objectstack/objectql, @objectstack/runtime)
  • content/docs/permissions/authorization.mdx (via packages/core, packages/runtime, @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/anatomy.mdx (via @objectstack/core)
  • content/docs/plugins/development.mdx (via @objectstack/core, @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/core, @objectstack/objectql, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/cli, @objectstack/core, @objectstack/metadata, @objectstack/objectql, @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/core, @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/core, @objectstack/objectql, @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/core, @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/kernel/metadata-service.mdx (via @objectstack/metadata)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/cli, @objectstack/core, @objectstack/spec)
  • content/docs/protocol/kernel/realtime-protocol.mdx (via @objectstack/cli)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via packages/objectql, @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/cli, @objectstack/core, @objectstack/objectql, @objectstack/runtime, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/core, @objectstack/metadata, @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v15.mdx (via @objectstack/core)
  • content/docs/releases/v16.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Plugin ordering is an unenforced convention: AppPlugin needs objectql/manifest at init but declares nothing (the structural cause of #4085)

2 participants