本文固定 Graft Runtime、Module、Provider、Agent、Runner、Task Runtime 与 realtime 运行面在组合时必须遵守的三项原则:
- 每个运行时资源都必须有可追踪的创建者、owner 和 disposer。
- capability 的可见性必须受稳定边界控制,不能默认成为全局可见服务。
- 每个组合单元必须声明依赖、暴露 capability 和清理责任。
本文不把 Graft 改造成动态插件平台。当前 compile-time module registry、显式 DI、Task/Submission authority 和 Provider 生命周期仍是唯一基线。
适用对象:
server/internal/app的 Runtime 与 core resources;server/modules/*的 Module、collector、event consumer、cron job 和 realtime stream;- 后续 compile-time Provider、独立 Agent、Runner 与 Integration;
- Task Runtime 的 worker、Stage executor 和 operation-scoped external resource;
- WebSocket/SSE gateway、topic subscription 与 observer。
不适用对象:
- Codex 或仓库
.agents/skills/**;它们是开发工具,不是 Graft 产品运行时 Skill; - 临时纯函数、请求内短期值和 PostgreSQL 事务本身;它们继续由调用栈或
context.Context直接拥有; - runtime filesystem scan、动态 module/Provider discovery、第三方 marketplace、HMR 或热卸载。
| 概念 | 含义 | 例子 |
|---|---|---|
| runtime resource | 需要显式释放、停止、注销或等待退出的运行时实体 | goroutine、ticker、event subscription、WebSocket、Docker client、worker pool |
| creator | 实际创建资源的代码边界 | Module Boot、Task Runtime Start、gateway handler |
| owner | 对资源存活、失败、观测和最终清理负责的唯一 authority | Module、Task Runtime、Agent process、realtime subscription |
| disposer | 可重复调用且尽最大努力的释放操作 | Stop(ctx)、Close(ctx)、unsubscribe closure、Shutdown(ctx) |
| capability | 一个受 typed contract 约束的业务或平台能力 | moduleapi.TaskService、Provider-neutral port |
| composition unit | 能声明依赖、暴露 capability 与清理责任的稳定单元 | Module、Provider、Agent、Runner、Task Runtime |
owner 不等于调用者:调用方可以消费 capability,但不得接管其资源生命周期或持久化事实。一个资源只能有一个 owner;共享资源必须由其 owner 提供受控使用接口,而不是转移关闭权。
Application Runtime
|
+-- compile-time Module registry
| +-- Module lifecycle scope
| +-- module capability / route / job declarations
| +-- module-owned collectors, subscriptions and clients
|
+-- Task Runtime
| +-- persisted Submission / Task / Stage facts
| +-- worker and execution scopes
|
+-- Provider / Runner / Agent boundaries
| +-- compile-time Provider lifecycle
| +-- independent Agent process lifecycle
|
+-- Core runtime services
config | logger | event | realtime | permission | service container
这不是 Agent -> Task -> Plugin 的通用父子 Context 树:Agent 是独立进程与身份边界,Task 是持久化执行事实,Module 是编译期应用能力。只有真实的取消、资源从属和释放顺序才建立 scope 关系。
Register只声明 capability、route、permission、job、event consumer 和配置定义;不得启动长期资源。Boot启动 Module-owned runtime resource,必须使用 Runtime 注入的 lifecycle context 或由 owner 从它派生的 context。Shutdown以 Runtime 注入的有界 shutdown context 反向停止资源;不得用context.Background()绕过关闭语义。- durable event Dispatcher 是 Runtime-owned core resource,不属于任一 Module;它可以使用独立于
runCtx的 process-lifetime parent,以便在请求取消或监听器异常后 drain 已接收事件,但必须由 Runtime 的有界 shutdown context 显式停止。该独立 parent 不是逃逸关闭责任的理由。 - 独立 Agent、Provider、Runner 与 Task Runtime 必须有同等明确的
Start、Stop或Closeowner;它们不得自建第二套启动、任务、配置或恢复机制。 - 启动失败时 creator 必须清理本次已创建资源;Runtime 继续清理此前成功启动的 owner。关闭失败必须记录并聚合,不能阻止后续 cleanup。
每个长期资源必须能从代码和日志回答:
- 创建者、owner 与所属 composition unit;
- 父 lifecycle context 或 operation identity;
- 开始、停止、失败和 cleanup 结果;
- 停止 timeout、未完成工作和恢复语义;
- 是否有持久化事实,以及其 canonical owner。
对于 goroutine、timer、subscription、observer、WebSocket 和外部 client,创建点必须保留或注册 idempotent disposer。调用方不得只保留裸 channel、callback 或全局 map 引用而失去注销路径。
- 同步 in-process EventBus 只是本地 observer,不是 durable event、Task queue 或外部 message broker;当前
MemoryBus选择 application-lifetime owner,不提供注销或关闭接口。订阅 handler 必须同步、短时且不得隐藏创建后台资源;需要长期运行的工作必须回到 owning Module/Runtime 的显式Boot/Shutdown。 - durable event consumer 在
Register声明、由 dispatcher 统一启动和关闭;consumer 不得私自启动 delivery worker。 - realtime subscription 和 topic observer 必须返回一次性可调用的取消函数;stream owner 在最后订阅者离开、Module Shutdown 或 gateway 关闭时注销 observer 并等待相关 goroutine。
- cron registry 只登记声明;scheduler owner 负责实际调度器的启动、停止和 job cleanup。业务模块不得直接引入独立 scheduler。
- Module 内部实现、repository、ORM client、storeent、Agent 私有 transport、Provider SDK 和 runtime resource 默认不可跨边界访问。
- 跨模块 capability 只能进入
server/internal/moduleapi/**;平台 typed contract 只能进入server/internal/contract/**;外部 provider-neutral 能力仅在经过 boundary review 后进入server/internal/ports/**。 - 容器只能解析已显式注册的稳定 capability。容器不是业务依赖的万能入口,也不授权任意 Module 持有全部 core service。
- HTTP、MCP、realtime 与 web 只消费其已有契约和授权范围,不因为某个 capability 已注册就默认可访问。
composition unit 的声明必须区分:
| 声明 | owner | 当前落点 |
|---|---|---|
| module identity、module dependencies、builder | compile-time ModuleSpec | server/internal/module 与 generated registry |
| required / exposed cross-module capability | composition unit + moduleapi owner |
Builder / Register 的显式 typed registration |
| configuration owner 与 immutable/startup policy | owning core/module/provider | config governance 与 definition registry |
| route、permission、menu、event/job/realtime topic | owning module/core boundary | Register phase 的对应 registry |
| runtime resource 与 disposer | 创建它的 owner | Boot/Start/handler 内的 owner state |
Module dependency 解决启动排序;capability dependency 解决实际协作。两者不得互相替代:没有 capability contract 时不能仅靠依赖顺序直接 import 对方内部实现;没有 module dependency 时也不能靠一次成功的 Resolve 隐藏启动顺序要求。
Phase 3 的最小声明载体是 server/internal/module.Spec:
RequiredCapabilities与ExposedCapabilities使用module.TypedCapability[T]()生成 typed contract key;key 由 Go 类型派生,不接受字符串 service name。ConfigurationOwner只标识配置定义的 canonical owner,不触发读取、重载或拓扑变更。Resources记录 owner 已创建的长期资源及其既有 disposer 名称;它是可审计 metadata,不是通用 Scope 或自动 cleanup registry。RuntimeMetadata只读投影这些声明,供诊断与 conformance 使用;实际 capability 注册/解析继续沿用显式 container 与moduleapicontract。
Provider、Agent、Runner 与 Task Runtime 可以复用同一声明语义,但不得把它们投影成 Module、共享 Task 状态或新增全局 registry。局部健康只允许使用 Ready、Degraded、Unavailable 三态,故障仍由 capability owner 解释,不能改写 Runtime、Task/Submission 或 Agent ledger 的事实 authority。
Graft 不引入全局动态 Context hierarchy。只在下列真实资源边界创建窄 scope:
| Scope | owner | 负责 | 不负责 |
|---|---|---|---|
| Module lifecycle scope | Module / Runtime | module background resource、disposer、shutdown deadline | Task persisted state、其它 Module 私有资源 |
| Task execution scope | Task Runtime | Stage cancellation、executor resource、operation log | Module lifecycle、Task state mutation authority |
| Realtime subscription scope | gateway / stream owner | ticket、topic、observer、connection/stream cleanup | module business state |
| Agent process scope | Agent executable | identity、transport、heartbeat、process shutdown | server-side Task/Submission truth |
| Provider operation scope | Provider / Runner | external client/session、operation cleanup、degraded result | second task state machine |
新增 scope 之前必须证明:至少两个资源需要共同取消/释放,且现有 Module、Task、subscription 或 Agent owner 无法清晰表达从属关系。否则保持局部显式 context.Context 和 disposer。
当前状态分类:
| 对象 | 允许的变化 | 禁止的变化 |
|---|---|---|
| Module / Provider | process start/stop、配置变更后的受控 process restart | runtime load/unload、HMR、动态依赖重算 |
| TaskPlan / Stage executor binding | 新 Task 使用新版本定义 | 修改在途 Task 的冻结计划 |
| Agent | 独立进程 restart、证书/配置 rotate 后 reconnect | server 内热替换为 Module |
| RuntimeTarget | 持久化 reconcile 与受控状态转换 | 以内存 reload 覆盖 binding/identity 事实 |
| System Config cache | 受控 invalidation/reload | 改变 Module 拓扑或权限/路由 owner |
| realtime connection | ticket-scoped open/close | 脱离 gateway 的永久全局订阅 |
单个 Provider 或 capability 失败应返回 capability-local Unavailable 或 Degraded 语义;不得把故障伪装为应用全局可用,也不得以自动 plugin reload 掩盖外部副作用或泄漏。
实施必须按以下顺序推进:
- 盘点和修复现有资源 owner、disposer 与 shutdown 证据。
- 统一 Module、Provider、Agent、Runner、Task Runtime 的 lifecycle 清单和可观测性。
- 在重复需求出现后,为 Module resource 或 subscription 增加窄 scope/disposer helper。
- 补充 required/exposed capability 声明和 capability-local health。
- 只有运行时隔离、持久化状态迁移、失败回滚和运维控制都已证明需要时,才重新评估动态 enable/disable;HMR 不在当前路线中。
验收标准:
- 每个新增长期资源有 creator、owner、disposer 和关闭测试;
- 跨 Module capability 不泄露私有实现,也不默认扩散为全局可见服务;
- composition unit 的依赖、公开 capability、配置 owner、生命周期与 cleanup 可从一处代码/契约追踪;
- Task、Agent、Runner、durable event 和 realtime 仍保持各自既定 authority;
- 不增加动态 plugin loader、第二套 DI、第二套 scheduler 或第二套 Task state machine。