diff --git a/docs/proposals/2026-10-14-svc-plugin-compose-deploy.md b/docs/proposals/2026-10-14-svc-plugin-compose-deploy.md new file mode 100644 index 0000000..f26f0b4 --- /dev/null +++ b/docs/proposals/2026-10-14-svc-plugin-compose-deploy.md @@ -0,0 +1,80 @@ +# svc 插件组合部署(--with)方案 r1 + +状态:**待评审**。日期:2026-10-14。负责人:weichen。 +任务卡:Dev `.task/feature-svc-plugin-compose.md`。 +需求来源:部署时用交互或参数选择把哪些插件与基座编译成同一个二进制服务,并在服务中激活这些插件。 + +## 1. 范围 + +做: + +- 每次 `svc.sh` 的 agent deploy 可以选择零个或多个已登记插件。 +- 被选中的插件与 `--from` 指定的 Dev 基座编译成一个仍名为 `agent-system` 的二进制。 +- 部署后校验这些插件已注册且命令面处于启用;失败走现有 release 回退。 +- 插件清单来自 Dev 仓 `plugins/registry.json`,不自动发现。 + +不做: + +- 不做 sidecar,不把插件部署成独立服务。 +- 不让插件自带基座 pin。基座就是本次 `--from` 的 Dev 提交;对不上就构建失败并报原因。 +- 第一版最多一个带静态工作台(`web`)的插件。 +- 不删除旧的 `--from rdm`。它仍用 RDM 仓自己的 `pins/` 构建,与 `--with` 互斥。 + +## 2. 步骤 0 试验结论(不留代码) + +2026-10-14 在 `/tmp/svc-compose-spike` 复制当前 Dev 的 backend 模块,空白导入 +`agent-system-rdm/plugins/rdm`(指向 `~/.cache/agent-system-svc/rdm-src`), +`go mod edit -require/-replace` 后执行 `CGO_ENABLED=0 go build -mod=mod`。 + +- 构建成功。产物约 40MB,`version` 仍是基座的 `agent-system 0.1.0-dev`。 +- `go version -m` 显示主模块 `agent-system-backend`,依赖 `agent-system-rdm v0.0.0`。 +- 二进制内可见 `rdm.project.create` 与 `rdm.ping`。 +- 聚焦依赖(`golang.org/x/*`、`github.com/jackc/*`、`modernc.org/sqlite`、`gopkg.in/yaml`)版本无变化。 +- 新增模块只有 `agent-system-rdm`、`github.com/kr/text`、`github.com/rogpeppe/go-internal`。 +- 默认 `-mod=readonly` 会拒绝更新 `go.mod`/`go.sum`,组合构建必须使用 `-mod=mod`。 +- 依赖模块自己的 `replace` 不生效,插件按主模块(Dev 基座)解析 `agent-system-backend`。这正是“基座以 Dev 提交为准”所要的结果。 + +试验目录是一次性的,不进入任何仓库。 + +## 3. 行为 + +### 3.1 选择 + +- `--with rdm,foo` 选择插件;`--with rdm@` 钉住单个插件提交;`--with none` 明确不带插件。 +- 标准输入是终端且没有 `--with` 时,按登记表给出编号勾选。默认勾选上次 `svc.env` 的 `plugins=`;第一次默认不选。回车接受默认,`none` 清空。 +- 非交互且没有 `--with`:沿用上次选择,没有则不带插件。已有脚本和 CI 的行为不变。 +- `--dry-run` 打印基座提交和每个插件的提交,不构建。 +- `--from rdm` 与 `--with`(除 `none`)互斥。`--from release` 不能再注入插件。 + +### 3.2 构建 + +1. 物化 Dev 提交的隔离 worktree,并完成前端构建,使嵌入资源就绪。`--from local` 不改主检出的 `go.mod`:把 backend 模块复制到 `runtime/.worktrees/` 再构建。 +2. 每个插件使用独立源仓缓存(`~/.cache/agent-system-svc/plugin-`,`repoEnv` 可覆盖克隆地址)并物化 worktree。 +3. 在这份一次性 backend 副本里生成 `cmd/agent-system/zz_plugins_gen.go`(空白导入 `entryPackage`),并用 `go mod edit` 增加 `require` 与 `replace`。 +4. `go build -mod=mod` 产出 `agent-system`。基座的 `version`、身份和 `user` 等子命令保持不变。 +5. 测试可设 `SVC_COMPOSE_STUB=1`,跳过真实 `go build`,只记录插件选择。这不是生产路径。 + +### 3.3 激活与记录 + +- `GET /api/plugins` 与 `GET /api/registry` 的 `compiled` 列表必须包含每个所选插件。 +- `GET /api/commands` 中该插件 `commandPrefix` 下至少有一条 `enabled` 命令。 +- 插件在而命令全部禁用时,用与设置播种相同的临时管理员 key 调用 `POST /api/plugins/{id}/enabled`,再复查。失败则 `svc_rollback_deploy`。 +- `svc.env` 写 `plugins=id@sha,id@sha`。`source` 形如 `dev@+rdm@`。release 目录的 `release.json` 带同一 `plugins` 字段。 +- `update` 把基座升到远端最新,并原样带上已记录的 `--with`,不再询问。`recover` 只拉起已有二进制,不重新选择。 +- 插件 id 集合变化(含从无到有、从有到无)与源种类变化一样,需要 `--allow-source-switch`。只改提交不需要。 + +### 3.4 工作台 + +登记表里 `web` 非空的插件,沿用现有伴生 static 服务(`web`)。多于一个带 `web` 的插件直接拒绝。 + +## 4. 跨仓 + +- Backend:`extension.InProc` 增加可选 `HostOptions` 与 `CLI`。布尔默认按“任一为 true”合并,不覆盖显式 serve 旗标。`GET /api/registry` 增加 `compiled`。 +- RDM:`plugins/rdm` 在 `InProc` 上声明 `HostOptions`(含关闭上传自动索引、会话卡镜像、执行原语)和 `-bootstrap-admin` 钩子。RDM 自己的 `main.go` 继续可用,旧 `--from rdm` 不依赖这次钩子。 +- Dev:`scripts/svc.sh`、`plugins/registry.json`、`docs/svc.md`、`help plugins`、`tests/test_svc_plugins.py`,并推进 backend 与 documents 的 gitlink。 + +## 5. 验收 + +- 登记、选择、换源门禁、dry-run、stub 部署与激活走 `tests/test_svc_plugins.py`。 +- Backend 的合并与 CLI 钩子走 `go test ./pkg/extension/`。 +- 真实 `go build` 组合(Dev 提交 + RDM 提交)在步骤 0 已证实可编译。带网络的完整 `svc deploy --with rdm` 与 `make test-lifecycle` 不在本增量的 stub 套件里,未在本机再跑时标 NOT_RUN。 diff --git a/docs/proposals/INDEX.md b/docs/proposals/INDEX.md index 2eef8aa..9c782a7 100644 --- a/docs/proposals/INDEX.md +++ b/docs/proposals/INDEX.md @@ -4,6 +4,8 @@ 本索引负责导航与差异提示,规划正文、批准范围和验收条件仍以链接的主记录及其批准版本为准。 合入授权不批准新方向、不调整里程碑范围、不启动新的业务任务。 +待评审(收录不是批准):[svc 插件组合部署(--with)方案 r1](2026-10-14-svc-plugin-compose-deploy.md)。 + ## 先看哪一份 - [IAM I1 权限边界与公开扩展合同 r1](2026-10-13-iam-i1-permission-contract.md):