Pin the extension roadmap and downstream spike
ci / test (push) Failing after 1m54s

Why: Downstream products need a published extension surface, contract checks, and a sample plugin that does not modify the base.

Evidence: Boundary lint passed. examples/downstream-rdm go test passed. Submodule pointers record the core, documents, backend, ui, and frontend commits.

Impact: Merge order is Core, Documents, UI, and Frontend, then Backend, then this PR. Do not squash the submodule PRs. make test runs the boundary lint first. Version tags and the remote downstream repository are not created.

Refs: .task/feature-base-extension-roadmap.md
This commit was merged in pull request #132.
This commit is contained in:
2026-10-01 10:47:13 +00:00
21 changed files with 203 additions and 6 deletions
+27
View File
@@ -0,0 +1,27 @@
# feature/base-extension-roadmap 任务卡
任务主记录:https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/132 ;状态:进行中;owner:weichen
## 基线与授权
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= f8fefa0f888e8f5fd5207c9e97099e59935ff69b
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-base-extension-roadmap(worktree);分支:feature/base-extension-roadmap
- 授权与交付终点:在本 worktree 落地路线图提案与基座实现,并在各子仓与父仓本地提交。不推送、不打版本 tag、不新建下游远程仓。
## 目标与非目标
- 目标:公开扩展面(`pkg/extension`)、插件契约 v2 四种形态、生命周期治理、质量回路与工程基座接线;下游薄切片只 import 公开 SDK。
- 非目标:打 `v0.2.0-experimental`、发布 npm、改 Go module path、把目标系统(两树一网)做进基座、容器编排、旧资产导入。
## 同步记录
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|---|---|---|---|
| 启动 | f8fefa0f888e8f5fd5207c9e97099e59935ff69b | f8fefa0f888e8f5fd5207c9e97099e59935ff69b | 2026-10-01T10:10:35Z |
| 同步前验收 | 26dfea7ed62575097f14fedf1b7713a77dedb66f | 6d496139b45bbe0d32818e56ed6c5fbba649d777 | 2026-10-01T10:39:24Z |
## 结果与验证(完成后填写)
- 结果:documents 三份提案与开发指南;core manifest v2;backend 声明/sidecar/进程内/Host API/迁移 v13/质量回路/审计与命令对拍;ui SDK 与 frontend ESM 加载;examples 与 `make plugin-new`、`make test-boundaries`。
- 验证:`cd modules/core && go test ./...`;`cd modules/backend && go test ./...`;`bash scripts/lint-boundaries.sh`;`cd examples/downstream-rdm && go test .`;UI `tsc`+vitest 71;frontend vitest。
- 影响与剩余项:apiVersion `2.0-experimental`。版本 tag 与 npm 发布命令留在兼容提案,由用户确认后执行。下游远程仓由用户另建。Playwright 对拍文件在 `modules/frontend/e2e/`,未安装 `@playwright/test`,不进 vitest。合入顺序:Core #14、Documents #31、UI #25、Frontend #31,然后 Backend #41,最后 Dev #132。子仓不要 squash,否则 Backend / Dev 的 gitlink 会指向不存在的提交。
+8 -1
View File
@@ -96,7 +96,14 @@ test-ui: contracts
test-agentcmd:
cd deps/agent-command && go vet ./... && go test -count=1 ./...
test: test-core test-backend test-frontend test-ui test-agentcmd
test-boundaries:
bash scripts/lint-boundaries.sh
plugin-new:
@test -n "$(NAME)" || (echo "用法:make plugin-new NAME=<id>"; exit 2)
bash scripts/plugin-new.sh "$(NAME)"
test: test-boundaries test-core test-backend test-frontend test-ui test-agentcmd
# 运行演示(mock 模式):单二进制 + 示例插件 + 嵌入式存储,浏览器 http://127.0.0.1:8787
# 环境服务管理(scripts/svc.sh):deploy = 停旧 → 构建 → 启动;dev/prod 相互隔离。
+9
View File
@@ -0,0 +1,9 @@
# 下游薄切片
本目录模拟独立仓库 `Agent-System-Plugin-RDM`。它只 import `agent-system-backend/pkg/extension`,不改基座。
把本目录搬到新仓库时,由 owner 创建 Git 仓库。`replace` 改成基座检出路径或已发布版本。需要拉起完整二进制时再空白导入 `agent-system-backend/pkg/extension/link`,并复制基座 `go.mod` 的 replace(下游模块不会继承它们)。
```
go test .
```
+7
View File
@@ -0,0 +1,7 @@
module example.com/agent-system-plugin-rdm
go 1.27
require agent-system-backend v0.0.0
replace agent-system-backend => ../../modules/backend
+12
View File
@@ -0,0 +1,12 @@
package rdm
import "agent-system-backend/pkg/extension"
func init() {
extension.RegisterCompiled(extension.InProc{
ID: "rdm",
Register: func(reg extension.Registry) error {
return reg.AddSkill("rdm-note", "# 研发记忆\n\n下游切片技能,不修改基座。\n")
},
})
}
+19
View File
@@ -0,0 +1,19 @@
package rdm
import (
"testing"
"agent-system-backend/pkg/extension"
)
func TestCompiledSkillRegistration(t *testing.T) {
extension.ResetCompiled()
extension.RegisterCompiled(extension.InProc{ID: "rdm"})
got := extension.Compiled()
if len(got) != 1 || got[0].ID != "rdm" {
t.Fatalf("%+v", got)
}
if extension.SurfaceVersion == "" {
t.Fatal("surface version required")
}
}
+8
View File
@@ -0,0 +1,8 @@
export function mount(el, bridge) {
const btn = document.createElement('button')
btn.type = 'button'
btn.textContent = '走命令'
btn.addEventListener('click', () => bridge.execCommand('builtin.help', {}))
el.replaceChildren(btn)
}
export function unmount() {}
@@ -0,0 +1 @@
{ "id": "feishu", "kind": "feishu", "title": "飞书" }
@@ -0,0 +1 @@
{ "id": "notes", "title": "笔记", "description": "数据集声明,检索引擎在基座" }
+5
View File
@@ -0,0 +1,5 @@
export function mount(el, bridge) {
el.textContent = '声明示例页面'
el.dataset.bridge = bridge ? 'ready' : ''
}
export function unmount() {}
@@ -0,0 +1 @@
{ "id": "home", "title": "声明示例", "module": "./page.mjs" }
+13
View File
@@ -0,0 +1,13 @@
{
"id": "decl-demo",
"name": "声明示例",
"version": "0.1.0",
"apiVersion": "2.0-experimental",
"requires": { "network": ["api.example.com"] },
"provides": {
"skills": ["welcome"],
"pages": ["home"],
"channels": ["feishu"],
"knowledge": ["notes"]
}
}
@@ -0,0 +1,3 @@
# welcome
声明式技能示例。描述任务步骤,不携带可执行代码。
+17
View File
@@ -0,0 +1,17 @@
// sidecar 示例:只通过 Host API 客户端说话,不导入 internal。
package main
import (
"context"
"log"
"os"
"agent-system-backend/pkg/extension"
)
func main() {
c := extension.NewHostClient(os.Getenv("AGENT_HOST_API"), os.Getenv("AGENT_HOST_TOKEN"), os.Getenv("AGENT_PLUGIN_ID"), "local")
if err := c.Audit(context.Background(), "sidecar.ready", os.Getenv("AGENT_PLUGIN_ID"), []byte(`{}`)); err != nil {
log.Fatal(err)
}
}
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
# 依赖边界:公开扩展 SDK 与示例不得导入后端 internal。
# pkg/extension/link 是唯一例外:下游空白导入它才能把 extension.Main 接到产品组合根。
set -euo pipefail
root=$(cd "$(dirname "$0")/.." && pwd)
fail=0
check() {
local path="$1"
if [ -d "$path" ] && grep -R -n --include='*.go' --include='*.ts' --include='*.tsx' 'agent-system-backend/internal' "$path" >/dev/null; then
echo "boundary: $path imports agent-system-backend/internal" >&2
grep -R -n --include='*.go' --include='*.ts' --include='*.tsx' 'agent-system-backend/internal' "$path" >&2
fail=1
fi
}
sdk="$root/modules/backend/pkg/extension"
if [ -d "$sdk" ]; then
hits=$(grep -R -n --include='*.go' 'agent-system-backend/internal' "$sdk" | grep -v '/link/' || true)
if [ -n "$hits" ]; then
echo "boundary: pkg/extension imports agent-system-backend/internal" >&2
echo "$hits" >&2
fail=1
fi
fi
check "$root/examples"
if [ "$fail" -ne 0 ]; then
exit 1
fi
echo "boundaries ok"
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# 生成声明式插件骨架:make plugin-new NAME=<id>
set -euo pipefail
name="${1:-}"
if [ -z "$name" ]; then
echo "usage: plugin-new.sh <id>" >&2
exit 2
fi
root=$(cd "$(dirname "$0")/.." && pwd)
dest="$root/examples/plugins/$name"
if [ -e "$dest" ]; then
echo "exists: $dest" >&2
exit 1
fi
mkdir -p "$dest/tools" "$dest/skills" "$dest/pages"
cat > "$dest/plugin.json" <<EOF
{
"id": "$name",
"name": "$name",
"version": "0.1.0",
"apiVersion": "2.0-experimental",
"requires": { "network": [] },
"provides": { "skills": ["welcome"], "pages": ["home"] }
}
EOF
printf '# %s\n\n描述这个技能做什么、何时使用。\n' "$name" > "$dest/skills/welcome.md"
cat > "$dest/pages/home.json" <<EOF
{ "id": "home", "title": "$name", "module": "./page.mjs" }
EOF
cat > "$dest/page.mjs" <<'EOF'
export function mount(el, bridge) {
el.textContent = '插件页面'
el.dataset.bridge = bridge ? 'ready' : ''
}
export function unmount() {}
EOF
echo "wrote $dest"