@ifrankwang/openspec-agents 0.125.0 → 0.127.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp-server/cli.mjs +31 -4
- package/LICENSE +21 -0
- package/README.md +77 -96
- package/assets/agents/openspec-developer.md +2 -2
- package/assets/agents/openspec-reviewer.md +4 -4
- package/assets/skills/api-test/SKILL.md +13 -3
- package/assets/skills/architecture-evolution/SKILL.md +5 -1
- package/assets/skills/environment-cleanup/SKILL.md +4 -2
- package/assets/skills/java-quality-gate/SKILL.md +1 -1
- package/assets/workflows/task-simple.yaml +7 -5
- package/assets/workflows/task.yaml +9 -7
- package/dsh/cordis.patch.yml +6 -6
- package/package.json +8 -1
- package/src/adapters/deepseek-harness/index.ts +7 -0
- package/src/adapters/mcp-common/server.ts +1 -1
- package/src/adapters/plugin-common/index.ts +12 -3
- package/src/core/state.ts +2 -2
- package/src/core/tools/lifecycle.ts +51 -6
- package/src/core/tools/schemas.ts +1 -1
- package/src/core/tools/types.ts +3 -1
- package/src/core/types.ts +3 -1
package/.mcp-server/cli.mjs
CHANGED
|
@@ -35738,6 +35738,15 @@ function assertValidRecovery(recovery) {
|
|
|
35738
35738
|
}
|
|
35739
35739
|
}
|
|
35740
35740
|
}
|
|
35741
|
+
function otherTaskGroupsSettled(state, targetGroupId) {
|
|
35742
|
+
return state.workItems.filter((w) => w.id.startsWith("task:") && w.id !== `task:${targetGroupId}`).every((item) => {
|
|
35743
|
+
if (item.phase === "done" || item.metadata["completed_at"] !== undefined)
|
|
35744
|
+
return true;
|
|
35745
|
+
const noTags = Object.keys(item.tags).length === 0;
|
|
35746
|
+
const childrenSettled = item.children.filter((c) => c.type === "task").every((c) => c.phase === "todo" || isTerminalPhase(c.phase));
|
|
35747
|
+
return noTags && childrenSettled;
|
|
35748
|
+
});
|
|
35749
|
+
}
|
|
35741
35750
|
async function initExecute(params, ctx) {
|
|
35742
35751
|
assertOrchestrator(ctx, "opx_orch_init");
|
|
35743
35752
|
const args = { ...params };
|
|
@@ -35780,6 +35789,19 @@ async function initExecute(params, ctx) {
|
|
|
35780
35789
|
const baseBranch = args.base_branch || await getCurrentBranch(ctx.worktree);
|
|
35781
35790
|
let state = await readStateByChangeId(ctx.worktree, args.change_id);
|
|
35782
35791
|
const wasCurrentGroup = state?.taskGroupId === args.task_group_id;
|
|
35792
|
+
let modeSwitchNote = null;
|
|
35793
|
+
if (state && args.mode !== undefined && args.mode !== (state.mode ?? "full")) {
|
|
35794
|
+
const othersSettled = otherTaskGroupsSettled(state, args.task_group_id);
|
|
35795
|
+
const withinWindow = (args.task_group_id !== state.taskGroupId || args.recovery?.phase === "task_analysis") && othersSettled;
|
|
35796
|
+
if (!withinWindow) {
|
|
35797
|
+
throw new Error(`mode 参数与已固化的流程模式不一致:已固化 mode="${state.mode ?? "full"}"${state.mode === undefined ? "(旧变更未固化,读取时兜底 full)" : ""},传入值:"${args.mode}"。
|
|
35798
|
+
` + `当前场景不允许切换流程模式:切换任务组须其他任务组均已完成或从未激活;重制当前组仅支持 recovery.phase="task_analysis"(其他任务组同样须已完成或从未激活)。
|
|
35799
|
+
` + `请去掉 mode 参数继续沿用固化模式,或满足上述窗口条件后再切换。`);
|
|
35800
|
+
}
|
|
35801
|
+
const prevMode = state.mode;
|
|
35802
|
+
state.mode = args.mode;
|
|
35803
|
+
modeSwitchNote = `流程模式已从 ${prevMode ?? "full"} 切换为 ${args.mode}。`;
|
|
35804
|
+
}
|
|
35783
35805
|
if (!state) {
|
|
35784
35806
|
state = {
|
|
35785
35807
|
changeId: args.change_id,
|
|
@@ -35880,7 +35902,12 @@ async function initExecute(params, ctx) {
|
|
|
35880
35902
|
}
|
|
35881
35903
|
state.taskGroupId = args.task_group_id;
|
|
35882
35904
|
await writeState(ctx.worktree, state);
|
|
35883
|
-
|
|
35905
|
+
const parts = ["编排会话已初始化。"];
|
|
35906
|
+
if (args.recovery)
|
|
35907
|
+
parts.push(`已恢复到 ${args.recovery.phase} 阶段。`);
|
|
35908
|
+
if (modeSwitchNote)
|
|
35909
|
+
parts.push(modeSwitchNote);
|
|
35910
|
+
return parts.join("");
|
|
35884
35911
|
}
|
|
35885
35912
|
async function bindWorktreeRefs(item, worktreePath, branch, baseBranch, opts = {}) {
|
|
35886
35913
|
item.metadata["worktree_path"] = worktreePath;
|
|
@@ -36848,7 +36875,7 @@ var orchInitSchema = {
|
|
|
36848
36875
|
mode: {
|
|
36849
36876
|
type: "string",
|
|
36850
36877
|
enum: ["full", "simple"],
|
|
36851
|
-
description: "流程模式选择:full=完整流程(analyze→implement→三重审查+收尾验证);simple=精简流程(implement→quality_review→done
|
|
36878
|
+
description: "流程模式选择:full=完整流程(analyze→implement→三重审查+收尾验证);simple=精简流程(implement→quality_review→done,缺省)。首次新建编排状态时固化;已开始的变更仅在允许窗口内可更新:切换任务组(其他任务组均已完成或从未激活)或 recovery.phase=task_analysis 重制当前组(其他任务组同样须已完成或从未激活);其余场景传不同 mode 将报错。"
|
|
36852
36879
|
}
|
|
36853
36880
|
},
|
|
36854
36881
|
required: ["change_id", "task_group_id"],
|
|
@@ -37040,7 +37067,7 @@ function jsonSchemaToZod(schema2) {
|
|
|
37040
37067
|
var AGENT_ARG = "_agent";
|
|
37041
37068
|
var TOOL_SPECS = {
|
|
37042
37069
|
opx_orch_init: {
|
|
37043
|
-
description: "初始化编排会话。传入变更 ID 和任务组 ID,工具自动解析 tasks.md 提取全部任务组并解析目标组子任务。可通过 recovery 参数恢复到指定阶段。无 recovery 重复初始化当前任务组时保留其阶段和进度;切换到其它任务组时初始化该组。可选 mode 参数(full/simple,缺省 simple
|
|
37070
|
+
description: "初始化编排会话。传入变更 ID 和任务组 ID,工具自动解析 tasks.md 提取全部任务组并解析目标组子任务。可通过 recovery 参数恢复到指定阶段。无 recovery 重复初始化当前任务组时保留其阶段和进度;切换到其它任务组时初始化该组。可选 mode 参数(full/simple,缺省 simple)选择流程模式:首次新建状态时固化;已开始的变更仅切组(其他任务组均已完成或从未激活)或 recovery.phase=task_analysis 重制当前组(其他任务组同样须已完成或从未激活)时可更新,其余场景传不同 mode 报错。",
|
|
37044
37071
|
schema: orchInitSchema,
|
|
37045
37072
|
execute: (args, ctx) => initExecute(args, ctx)
|
|
37046
37073
|
},
|
|
@@ -37100,7 +37127,7 @@ async function ensureDefaultUnattended(args, ctx) {
|
|
|
37100
37127
|
}
|
|
37101
37128
|
} catch {}
|
|
37102
37129
|
}
|
|
37103
|
-
var PKG_VERSION = "0.
|
|
37130
|
+
var PKG_VERSION = "0.127.0";
|
|
37104
37131
|
function buildMcpServer(worktree, opts = {}) {
|
|
37105
37132
|
const mcp = new McpServer({ name: "openspec-agents", version: PKG_VERSION });
|
|
37106
37133
|
for (const [name, spec] of Object.entries(TOOL_SPECS)) {
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 ifrankwang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,129 +1,110 @@
|
|
|
1
1
|
# openspec-agents
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

|
|
4
|
+

|
|
4
5
|
|
|
5
|
-
>
|
|
6
|
-
> 未来方向:向前扩展「探索 + 方案制定」,覆盖 OpenSpec 更早期环节。
|
|
6
|
+
> 让多个 AI 智能体按规范流程协作,把 OpenSpec 变更规范落地为经过审查的高质量代码。
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
`openspec-agents` 是一组面向 AI 编码工具的插件,把 OpenSpec 的「需求 → 设计 → 任务拆分」成果,自动推进到「实现 → 验证 → 质量审查 → 收尾合并」的完整实施流程。主智能体负责调度,专职智能体分别承担实现、检查与质量把关,每一步都有门禁确认,全程可审计、可恢复。
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## 特性
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
- **多工具支持**:Claude Code、Codex、ZCode、DeepSeek Harness(DSH)、OpenCode 均可原生接入。
|
|
13
|
+
- **规范驱动**:直接消费 OpenSpec 变更规范(proposal / design / tasks / specs),流程与规范一一对应。
|
|
14
|
+
- **智能体团队**:不再是单个 AI 从头写到尾,而是「实现者 + 审查者 + 质量把关者」分工协作,结果更稳。
|
|
15
|
+
- **质量门禁**:实现之后经过工具检查、任务验证与多维度质量审查;发现的问题自动回退修复,也可按规则豁免并留痕。
|
|
16
|
+
- **隔离执行**:每个变更在独立的环境中实施,互不干扰;变更完成后合并并清理。
|
|
17
|
+
- **过程可审计**:进度、结论、豁免与恢复点全程记录,随时可以接着上次的进度继续。
|
|
13
18
|
|
|
14
|
-
|
|
19
|
+
## 快速开始
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
1. 准备环境:**Node.js ≥ 23.6**(或 Bun)与 **git**。
|
|
22
|
+
2. 按下方「安装」选择你正在使用的 AI 工具完成安装。
|
|
23
|
+
3. 重启该工具后,把下面「一句话接入」的提示词发给你的 AI 即可开始。
|
|
17
24
|
|
|
18
|
-
|
|
25
|
+
## 安装
|
|
19
26
|
|
|
20
|
-
|
|
27
|
+
按你使用的 AI 工具选择一种方式:
|
|
21
28
|
|
|
22
|
-
|
|
29
|
+
### Claude Code
|
|
23
30
|
|
|
24
|
-
|
|
31
|
+
```bash
|
|
32
|
+
claude plugin marketplace add https://github.com/ifrankwang/claude-code-plugins
|
|
33
|
+
claude plugin install openspec-agents@ifrankwang
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Codex
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
codex plugin marketplace add https://github.com/ifrankwang/codex-plugins
|
|
40
|
+
codex plugin add openspec-agents@ifrankwang
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### ZCode
|
|
44
|
+
|
|
45
|
+
打开 ZCode 插件页,添加市场 `ifrankwang/zcode-plugins`,然后安装 `openspec-agents`。
|
|
46
|
+
|
|
47
|
+
### DeepSeek Harness(DSH)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
dsh plugin --profile web add @ifrankwang/openspec-agents
|
|
51
|
+
```
|
|
25
52
|
|
|
26
|
-
|
|
53
|
+
安装后重启 `dsh web` 即可。
|
|
27
54
|
|
|
28
|
-
|
|
55
|
+
### OpenCode
|
|
29
56
|
|
|
30
|
-
|
|
57
|
+
```bash
|
|
58
|
+
npm install -D @ifrankwang/openspec-agents
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
然后在 OpenCode 配置中加载 `@ifrankwang/openspec-agents` 插件,重启 OpenCode。
|
|
62
|
+
|
|
63
|
+
## 让 AI 帮你接入(一句话)
|
|
64
|
+
|
|
65
|
+
安装后(甚至还没安装时),直接把这句提示词发给你的 AI 编码工具:
|
|
66
|
+
|
|
67
|
+
> 请帮我安装并启用 openspec-agents 插件,然后基于当前项目的 OpenSpec 变更规范,执行完整的变更实施编排流程。
|
|
31
68
|
|
|
32
|
-
|
|
69
|
+
英文版:
|
|
33
70
|
|
|
34
|
-
|
|
71
|
+
> Please install and enable the openspec-agents plugin for my current AI coding tool, then run the OpenSpec change orchestration workflow on this repository.
|
|
35
72
|
|
|
36
|
-
-
|
|
37
|
-
- 物理权限取各逻辑身份权限并集(维度审查者因此具备编辑与写入能力);审查行为以「只报不改」指令约束兜底——文档/注释等不影响代码运行的问题可直接修改,逻辑类问题只上报,full 模式同样适用。
|
|
38
|
-
- `_agent` 为纯自述、无硬校验:任何调用者可自报任意身份,裁定权与视图路由均信任自述值;冒认只能以自报身份行事,无法越出该身份自身的既有权限面。
|
|
73
|
+
如果已经安装,只需说一句「请运行 openspec-agents 编排流程」即可开工。AI 会负责完成安装、初始化、任务分派与质量把关的全部步骤。
|
|
39
74
|
|
|
40
|
-
##
|
|
75
|
+
## 工作原理(简述)
|
|
41
76
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
- **隔离与安全**:每个 change 使用独立 git worktree、资源隔离命名空间,减少相互干扰。
|
|
47
|
-
- **可观测**:内置进度看板,状态/豁免/并发锁持久化到 `openspec/states/`。
|
|
77
|
+
1. **输入**:项目中的 OpenSpec 变更规范(变更说明、设计、任务清单、验收规格)。
|
|
78
|
+
2. **分派**:主智能体把任务交给专职智能体——实现者负责写代码,审查者负责检查,质量把关者负责多维验证。
|
|
79
|
+
3. **门禁**:每一步的结论通过检查后才进入下一步;不通过的结论自动回到实现环节修复。
|
|
80
|
+
4. **收尾**:变更完成后自动合并分支并清理执行环境,全程留痕可审计。
|
|
48
81
|
|
|
49
|
-
##
|
|
82
|
+
## 适用场景与代价
|
|
50
83
|
|
|
51
84
|
| | |
|
|
52
85
|
|---|---|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
86
|
+
| 适合 | 对代码质量、过程可控性要求高的团队与项目 |
|
|
87
|
+
| 代价 | 比单个 AI 直接修改更慢,API 消耗更高 |
|
|
55
88
|
|
|
56
|
-
|
|
89
|
+
如果你愿意用一点成本换取稳定、可复现、可审计的实施过程,这个项目就是为你准备的。
|
|
57
90
|
|
|
58
|
-
##
|
|
91
|
+
## 开发
|
|
59
92
|
|
|
60
|
-
|
|
93
|
+
```bash
|
|
94
|
+
bun install # 安装依赖
|
|
95
|
+
bun test # 运行测试
|
|
96
|
+
bun run typecheck # 类型检查
|
|
97
|
+
bun run build:plugins # 构建各平台的插件包(发布用)
|
|
98
|
+
```
|
|
61
99
|
|
|
62
|
-
|
|
100
|
+
## 贡献
|
|
63
101
|
|
|
64
|
-
|
|
65
|
-
# 安装依赖
|
|
66
|
-
bun install
|
|
102
|
+
欢迎提交 [Issue](https://github.com/ifrankwang/openspec-agents/issues) 与 Pull Request,也欢迎 Star 支持。
|
|
67
103
|
|
|
68
|
-
|
|
69
|
-
bun test
|
|
70
|
-
bun run typecheck
|
|
71
|
-
```
|
|
104
|
+
## 致谢
|
|
72
105
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
- **OpenCode**:以 npm 插件形式加载,插件注入 agent/skill 与 MCP server 配置(`config.mcp` 的 stdio server,自包含 bundle,`--worktree` 指向当前项目根)。
|
|
76
|
-
```bash
|
|
77
|
-
npm install -D @ifrankwang/openspec-agents
|
|
78
|
-
```
|
|
79
|
-
然后在 OpenCode 配置中加载 `@ifrankwang/openspec-agents` 插件并重启 OpenCode(MCP server 配置在启动时加载):`opx_*` 工具以 `mcp__opx__*` 形态出现(如 `mcp__opx__status`、`mcp__opx__agent_submit`,serverName=opx),身份经 `_agent` 参数承载(缺省视为编排视角)。
|
|
80
|
-
|
|
81
|
-
- **Claude Code**:通过官方插件市场安装。
|
|
82
|
-
```bash
|
|
83
|
-
claude plugin marketplace add https://github.com/ifrankwang/claude-code-plugins
|
|
84
|
-
claude plugin install openspec-agents@ifrankwang
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
- **Codex**:通过官方插件市场安装。
|
|
88
|
-
```bash
|
|
89
|
-
codex plugin marketplace add https://github.com/ifrankwang/codex-plugins
|
|
90
|
-
codex plugin add openspec-agents@ifrankwang
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
- **ZCode**:在 ZCode 插件页添加市场 `ifrankwang/zcode-plugins`,然后安装 `openspec-agents`。
|
|
94
|
-
|
|
95
|
-
- **DeepSeek Harness(DSH)**:通过 DSH 的 bundle 插件机制接入。发布到 npm 后可直接安装:
|
|
96
|
-
```bash
|
|
97
|
-
dsh plugin --profile web add @ifrankwang/openspec-agents
|
|
98
|
-
```
|
|
99
|
-
本地开发时也可使用构建产物:
|
|
100
|
-
```bash
|
|
101
|
-
bun run build:plugins
|
|
102
|
-
dsh plugin --profile web add ./dist/deepseek-harness-plugin
|
|
103
|
-
```
|
|
104
|
-
安装后重启 `dsh web`:
|
|
105
|
-
- `opx_*` 会以 `mcp__opx__*`(如 `mcp__opx__status`、`mcp__opx__agent_submit`) 原生工具出现;
|
|
106
|
-
- `assets/skills` 会作为额外 skill 根被扫描;
|
|
107
|
-
- 每个子代理会注册为 DSH 原生 subagent 工具:`openspec_developer` 与 `openspec_reviewer` 两个物理子代理,编排主代理可直接分派(9 种逻辑身份经 `_agent` 参数承载,同一物理子代理可被多次分派)。
|
|
108
|
-
|
|
109
|
-
## 常用命令
|
|
110
|
-
|
|
111
|
-
| 命令 | 说明 |
|
|
112
|
-
|------|------|
|
|
113
|
-
| `bun test` | 运行所有测试 |
|
|
114
|
-
| `bun run typecheck` | TypeScript 类型检查 |
|
|
115
|
-
| `bun run build:plugins` | 构建 Claude Code / Codex / ZCode / DeepSeek Harness 插件包(CI/内部使用) |
|
|
116
|
-
| `bun run sync` | 同步本地最新开发版本到各 harness 插件缓存,并安装/刷新依赖 |
|
|
117
|
-
|
|
118
|
-
## 项目结构
|
|
106
|
+
本项目基于 [OpenSpec](https://github.com/Fission-AI/OpenSpec)([openspec.sh](https://openspec.sh))构建——感谢 OpenSpec 团队带来的规范驱动开发方法:先写规范、再写代码,让 AI 开发有据可依。本插件专注于补全 OpenSpec 变更的**实施与质量把关**环节,是对 OpenSpec 工作流的编排增强。
|
|
119
107
|
|
|
120
|
-
|
|
121
|
-
src/core/ — Agent 无关内核(状态机、工具执行器、状态持久化)
|
|
122
|
-
src/adapters/ — opencode / claude-code / codex / zcode / deepseek-harness / mcp 适配层
|
|
123
|
-
assets/agents/ — Agent 定义
|
|
124
|
-
assets/skills/ — 内置 skill(含 orchestrator)
|
|
125
|
-
assets/workflows/ — workflow 定义
|
|
126
|
-
tests/ — 测试
|
|
127
|
-
```
|
|
108
|
+
## 许可证
|
|
128
109
|
|
|
129
|
-
|
|
110
|
+
[MIT](LICENSE) © 2025 ifrankwang
|
|
@@ -15,7 +15,7 @@ permission:
|
|
|
15
15
|
|
|
16
16
|
提交前按已加载 skill(含质量维度规范类)完成自检,审查兜底与终局裁定仍由对应 reviewer 承担(自检不替代 reviewer 裁定)。
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
自证/验证启动的服务与基础设施一律隔离启动、用后即停,自证完成即清理,不留残留。隔离对象不限于数据库(mock 服务、消息队列/缓存等中间件、扫描分析平台均按隔离标识隔离)。
|
|
19
19
|
|
|
20
20
|
任务执行与提交约束(Task/Issue 清单处理、环境问题处置、blocker 提交、文档读取范围、工具调用边界)以 opx_status 操作指引与约束区块为准,此处不重复描述。
|
|
21
21
|
|
|
@@ -60,7 +60,7 @@ permission:
|
|
|
60
60
|
- 命名合理性:按 skill 中的通用语言与命名规范——命名体现意图、不与其他场景混淆、与术语表一致防漂移
|
|
61
61
|
- 方案复用与平行实现:按 skill 中的方案复用与平行实现约定——同类能力已有既有实现时(如已有手工限流基础框架仍引入新限流框架且不改动原实现)须优先复用或扩展,确需平行引入(新旧并存)时按上文需用户拍板问题的确认机制确认并存期与技术债,无人值守缺省裁决为方案必须包含原技术实现的迁移方案
|
|
62
62
|
- 技术债识别:按 skill 中的技术债识别约定——方案是否引入可识别技术债(过时技术选型、技术迁移中断、平行引入、临时方案固化、契约不一致),引入的债务须在方案中规划偿还或说明可接受理由
|
|
63
|
-
- 架构演进审查:按 skill
|
|
63
|
+
- 架构演进审查:按 skill 中的架构演进审查约定——方案是否延续项目整体架构方向、是否支持长期迭代演进、是否破坏既有契约(接口/数据/行为兼容性)、重构方案是否贴合项目历史逻辑并保持既有行为语义;破坏兼容或偏离架构脉络须在方案中说明理由与过渡安排;涉及字段/实体承载位置搬迁时,方案须明确数据承接与迁移设计:新承载位置、产出字段写入目标、存量数据迁移、旧位置兼容与清理;缺失按既有契约破坏/重构贴合判例报 issue
|
|
64
64
|
|
|
65
65
|
## 执行边界产出(架构师身份)
|
|
66
66
|
|
|
@@ -56,8 +56,8 @@ Info 级别 issue 的 description/suggestion 中禁止出现阶段/时机相关
|
|
|
56
56
|
| 级别 | task 产出验证 | 服务启动验证 | 测试审查 |
|
|
57
57
|
|------|-------------|-------------|---------|
|
|
58
58
|
| Critical | 核心 task 产出文件缺失 | 服务无法启动(无论是否本次变更引起) | 测试依赖无隔离环境导致 CI 必然失败 |
|
|
59
|
-
| High | 关键 task
|
|
60
|
-
| Medium | task
|
|
59
|
+
| High | 关键 task 产出不完整;行为无实现(仅状态标记未实际生效);流程执行正常但关键数据产出缺失/未落库/落错位置(区别于『仅打状态标记未实际生效』:本行=流程与响应正常,产出物缺失或落错位) | 健康检查不通过;关键外部依赖不可用;新增接口无法调通(不论原因) | 核心业务逻辑无测试覆盖;Mock 了被测对象本身;测试仅断言可见状态/响应而 spec 要求的一并操作行为(入队/落库/下游消费等)无断言,将缺陷固化为期望行为;测试仅验证流程跑通/响应成功,未对 spec 要求的关键数据产出(落位/完整性)断言 |
|
|
60
|
+
| Medium | task 产出存在但质量不达标;关键数据产出部分缺失、值不完整或与 spec 指定承载位置不一致 | 启动有 WARN 但服务可用 | 断言过于宽松;缺少边界值测试;关键数据产出部分缺失、值不完整或与 spec 指定承载位置不一致 |
|
|
61
61
|
| Low | 产出文件位置不符合约定 | 启动日志格式不统一 | 测试命名不规范但不影响运行 |
|
|
62
62
|
| Info | 建议补充额外产出物 | 建议增加启动度量指标 | 建议补充边缘用例 |
|
|
63
63
|
|
|
@@ -138,7 +138,7 @@ Info 级别 issue 的 description/suggestion 中禁止出现阶段/时机相关
|
|
|
138
138
|
- 命名合理性:按 skill 中的通用语言与命名规范——命名体现意图、不与其他场景混淆、与术语表一致防漂移
|
|
139
139
|
- 方案复用与平行实现:识别引入与既有实现功能重叠的平行实现(另起一套逻辑)的情况——同类能力已有既有实现仍另建实现且不改动原实现、无迁移方案则报 issue。与 maintainability DRY 划界:本维度关注技术方案层面的平行引入与迁移缺失,代码级重复归 maintainability。具体复用与迁移要求按 skill 约定
|
|
140
140
|
- 技术债识别:按 skill 中的技术债识别约定——识别实现中的存量与新增技术债(过时技术选型、迁移中断残留、平行实现、临时方案固化、契约不一致),按统一严重级别报 issue
|
|
141
|
-
- 架构演进审查:按 skill 中的架构演进审查约定——实现是否延续项目整体架构方向、是否支持长期迭代演进、是否破坏既有契约(接口签名/数据格式/行为语义兼容性)、重构是否贴合项目历史逻辑并保持既有行为语义,破坏兼容或改变既有语义按统一严重级别报 issue
|
|
141
|
+
- 架构演进审查:按 skill 中的架构演进审查约定——实现是否延续项目整体架构方向、是否支持长期迭代演进、是否破坏既有契约(接口签名/数据格式/行为语义兼容性)、重构是否贴合项目历史逻辑并保持既有行为语义,破坏兼容或改变既有语义按统一严重级别报 issue;涉及字段/实体承载位置搬迁时,方案须明确数据承接与迁移设计:新承载位置、产出字段写入目标、存量数据迁移、旧位置兼容与清理;缺失按既有契约破坏/重构贴合判例报 issue
|
|
142
142
|
|
|
143
143
|
### 可维护性维度
|
|
144
144
|
|
|
@@ -187,7 +187,7 @@ Info 级别 issue 的 description/suggestion 中禁止出现阶段/时机相关
|
|
|
187
187
|
## 职责边界(确定性检查与任务验证)
|
|
188
188
|
|
|
189
189
|
- **确定性工具检查**:工具维度审查者与 simple 合并审查者被允许运行确定性工具检查(代码格式/架构约束/静态分析/单元测试编译/深度扫描),按已加载质量门 skill 的必做清单执行并将输出映射为统一 issue 结构;检查结果纳入审查结论
|
|
190
|
-
- **任务验证**:任务维度审查者与 simple 合并审查者承担任务验证职责,验证 task 产出完整性、服务健康与 API 测试质量;未通过的 task 经 failed_tasks
|
|
190
|
+
- **任务验证**:任务维度审查者与 simple 合并审查者承担任务验证职责,验证 task 产出完整性、服务健康与 API 测试质量;未通过的 task 经 failed_tasks 上报;验证完成后按清理规范做幂等清理,不留环境残留;数据产出核验——对照 spec 数据产出要求核验关键数据产出落位/完整性/搬迁承接/基线对比,识别『流程正常但数据缺失/落错位置』类缺陷
|
|
191
191
|
- **工具改进**:可工具化的质量 pattern 双报业务 issue 与工具改进 issue(规则草案由审查者提供),实施工作由开发者完成,审查者不直接改动规则/配置文件以外的实现
|
|
192
192
|
|
|
193
193
|
## 压制判定
|
|
@@ -98,17 +98,18 @@ API 测试脚本运行前需获取有效认证凭证。常见模式:
|
|
|
98
98
|
## 执行顺序
|
|
99
99
|
|
|
100
100
|
```
|
|
101
|
-
1. **MUST** 启动基础设施(Docker compose 项目名带隔离标识:`docker compose -p <namespace> up -d`,避免与并发 change
|
|
101
|
+
1. **MUST** 启动基础设施(Docker compose 项目名带隔离标识:`docker compose -p <namespace> up -d`,避免与并发 change 的容器冲突;隔离对象不限于数据库——mock 服务、消息队列/缓存等中间件、扫描分析平台等一切为本变更启动的资源均须经隔离标识启动,禁止复用宿主既有实例或共享端口)
|
|
102
102
|
2. **MUST** 准备 SQL 前置数据(写入本 change 的隔离容器内 DB)
|
|
103
103
|
3. **MUST** 启动服务(端口用编排会话建议端口:`--server.port=<建议端口>`,避免并发 change 端口冲突)
|
|
104
104
|
4. API 测试脚本(BASE_URL 使用上述端口)
|
|
105
105
|
5. **MUST** 停止服务并清理隔离环境(`docker compose -p <namespace> down`),随后幂等自检:本 change 隔离标识下无残留容器(`docker compose -p <namespace> ps` 为空)、建议端口无进程监听
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
以上第 1、2、3、5 条为 MUST:隔离(独立 compose
|
|
108
|
+
以上第 1、2、3、5 条为 MUST:隔离(独立 compose 项目名、隔离数据库、隔离 mock 服务/中间件/扫描分析平台、建议端口、down 清理)是并发安全硬约束,不适用本文件开头「项目规范优先」的推荐豁免。
|
|
109
109
|
|
|
110
110
|
- SQL 在前:先准备数据,再启动应用确保应用启动时读取到完整数据
|
|
111
111
|
- 隔离标识 `<namespace>` 与建议端口来自编排会话上下文
|
|
112
|
+
- 隔离对象不限于数据库:除数据库外,mock 服务、消息队列/缓存等中间件、扫描分析平台等其余为本变更启动的环境资源均须按隔离标识隔离;仅隔离数据库不满足并发安全要求
|
|
112
113
|
- 启动服务:按项目构建文件确定的启动方式(mvn / gradle / npm 等),端口用编排会话建议端口
|
|
113
114
|
- API 脚本:BASE_URL 使用上述建议端口,依赖运行中的服务
|
|
114
115
|
- 停止服务:测试完成后停止服务并清理隔离环境
|
|
@@ -122,11 +123,18 @@ API 测试脚本必须覆盖所有新增/变更接口:
|
|
|
122
123
|
| 正常路径 | 按 spec 请求结构传入合法值,验证响应状态码(2xx) 和响应结构 |
|
|
123
124
|
| 关键边界 | 缺必填字段 → 4xx;非法值(空串/超长/类型错误) → 4xx;极值 → 正确响应或合理错误 |
|
|
124
125
|
| 业务闭环 | spec 含列表/详情/状态流转/删除接口时,测试须端到端闭环:创建后用返回标识在列表/详情检索成功、状态变更后可见性反映、删除/停用后不可见 |
|
|
126
|
+
| 关键数据产出 | 涉及数据产出/落位/搬迁的变更,测试须断言关键产出字段已持久化到 spec 指定承载位置且值完整非空;产出为异步/延迟时须等待产出完成后断言;仅凭响应 2xx 与流程跑通不构成验收 |
|
|
125
127
|
|
|
126
128
|
spec 描述为多个行为一并发生的(如状态变更与数据入队),每个行为都须有断言证据;断言范围不限于响应状态码与响应结构,须覆盖变更引发的真实行为(状态落库、消息入队、下游可消费等连带效果)。
|
|
127
129
|
|
|
128
130
|
「业务闭环」与「一并操作行为组」划界:行为组=同一请求内多个行为同时发生的断言;闭环=跨请求链路的可见性验证(创建 → 检索 → 变更 → 删除),二者互补,均须覆盖。
|
|
129
131
|
|
|
132
|
+
「数据产出落位」≠业务闭环(闭环=跨请求可见性:创建→检索→变更→删除;数据产出=产出字段在 spec 指定承载位置的持久化与完整性,含不暴露于响应、须异步等待的字段);也≠一并操作行为组(行为组=同一请求内多个行为同时发生)。三者互补,均须覆盖;数据产出断言不得以「详情接口能查到」替代「字段已持久化到 spec 指定承载位置」。
|
|
133
|
+
|
|
134
|
+
## 数据模型搬迁与数据产出回归
|
|
135
|
+
|
|
136
|
+
字段/实体承载位置变更(如字段从子实体搬到实体根)时,测试须验证:新位置读写正常、旧位置无遗漏依赖/无残留写入、存量数据迁移后可读、关键产出字段非空完整、产出字段新旧承载位置映射正确;回归场景以同一输入比对关键数据产出与变更前基线一致(持久化存储快照/导出/读回结果对比)。
|
|
137
|
+
|
|
130
138
|
## 测试数据
|
|
131
139
|
|
|
132
140
|
http 请求体与 SQL 前置数据均按 spec 业务语义构造:使用真实字段值与业务含义明确的数据(如按 spec 字段语义构造的订单号、状态名、业务名称),禁止无意义占位值(如「测试数据」「aaa」「test1」)。列表/详情检索断言须基于真实业务标识(创建接口返回的 id/编码),使检索结果可预期、可追溯。
|
|
@@ -138,6 +146,7 @@ http 请求体与 SQL 前置数据均按 spec 业务语义构造:使用真实
|
|
|
138
146
|
以下情况不属于 API 变更,可豁免本章约束:
|
|
139
147
|
- 纯配置文件(非接口契约配置)、构建脚本、GitHub Actions、Docker 文件
|
|
140
148
|
- 仅 SQL init 文件改动且无 API 契约影响
|
|
149
|
+
- 涉及数据模型搬迁/存量数据迁移的 SQL 改动不适用「仅 SQL init 文件改动且无 API 契约影响」豁免
|
|
141
150
|
- 纯格式、注释、import 排序等无逻辑变更
|
|
142
151
|
- 仅测试代码变更(无生产代码、接口契约、构建与测试配置改动)
|
|
143
152
|
- 纯文档变更
|
|
@@ -146,7 +155,8 @@ http 请求体与 SQL 前置数据均按 spec 业务语义构造:使用真实
|
|
|
146
155
|
|
|
147
156
|
MUST 遵守以下约束,不得以编译通过或单元测试通过替代 API 测试:
|
|
148
157
|
|
|
149
|
-
- MUST
|
|
158
|
+
- MUST 不得以接口返回成功、状态字段正确或流程执行顺畅作为唯一验收;spec 含一并操作行为组时,须提供每个行为真实发生的断言证据。
|
|
159
|
+
- MUST 数据产出类变更须提供关键产出字段落位与完整性的断言证据(产出为异步/延迟时须等待产出完成后断言)。
|
|
150
160
|
|
|
151
161
|
### 脚本准备
|
|
152
162
|
|
|
@@ -20,7 +20,7 @@ capabilities: ["architecture"]
|
|
|
20
20
|
| 术语/命名与通用语言漂移 | 命名与通用语言约定 |
|
|
21
21
|
| 分层规则合规、依赖方向违规(绕过分层直连) | 架构分层约定 |
|
|
22
22
|
| 方案设计时未复用既有能力/平行引入新实现 | 方案复用与平行实现约定 |
|
|
23
|
-
| 本 skill
|
|
23
|
+
| 本 skill 五类检查与上述职责的相邻边界见各类检查中的划界说明 | 本 skill |
|
|
24
24
|
|
|
25
25
|
## 检查类别
|
|
26
26
|
|
|
@@ -58,3 +58,7 @@ capabilities: ["architecture"]
|
|
|
58
58
|
- 判据:重构改变既有行为语义且未显式声明
|
|
59
59
|
- 处置:重构改变既有行为语义且未显式声明 → 至少 Medium 级别 issue
|
|
60
60
|
- 划界:重构场景下破坏契约归本类;非重构场景的新契约破坏归第 3 类。平行引入新实现(另起一套)归方案复用与平行实现约定
|
|
61
|
+
|
|
62
|
+
### 5. 数据模型搬迁与数据承接
|
|
63
|
+
|
|
64
|
+
涉及字段/实体承载位置搬迁时,须明确数据承接与迁移设计:新承载位置、产出字段写入目标、存量数据迁移、旧位置兼容与清理;缺失或遗漏产出字段承载点按契约破坏/重构贴合判例处理。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: environment-cleanup
|
|
3
|
-
description:
|
|
3
|
+
description: 隔离环境清理通用规范——停止残留服务、清理隔离容器/数据库/mock 服务/中间件、删除隔离扫描分析项目。技术栈无关。
|
|
4
4
|
capabilities: ["cleanup"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -22,8 +22,9 @@ capabilities: ["cleanup"]
|
|
|
22
22
|
|
|
23
23
|
- 隔离容器:按隔离标识确认存在性后停止并移除(无残留容器即通过,不因重复清理报错)
|
|
24
24
|
- 隔离数据库:确认无残留的隔离数据库实例/数据卷后清理;无残留即通过
|
|
25
|
+
- 隔离 mock 服务/中间件:按隔离标识停止并移除对应容器/实例;无残留即通过
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
幂等判据:隔离标识下无残留容器、无残留隔离数据库、无残留隔离 mock 服务/中间件即为清理完成,重复执行不产生副作用。
|
|
27
28
|
|
|
28
29
|
## 删除隔离扫描分析项目
|
|
29
30
|
|
|
@@ -41,4 +42,5 @@ capabilities: ["cleanup"]
|
|
|
41
42
|
|
|
42
43
|
- 无残留的服务进程(本次启动的进程已停止,端口监听已释放)
|
|
43
44
|
- 无残留的隔离容器(隔离标识下 compose ps 为空)
|
|
45
|
+
- 无残留的隔离 mock 服务/中间件(按隔离标识查询不存在或容器已移除)
|
|
44
46
|
- 无残留的隔离扫描分析项目(按隔离标识查询不存在)
|
|
@@ -29,7 +29,7 @@ must_do: ["env", "compile", "format", "architecture", "static_analysis", "unit_t
|
|
|
29
29
|
|
|
30
30
|
在执行工具检查前,先确保工具运行环境就绪。环境检查失败时先按自愈性步骤尝试恢复;不可自愈或自愈失败后,用 `question` 提请用户处理或裁定。用户裁定降级跳过时,在报告中注明降级理由,不阻塞其他检查。
|
|
31
31
|
|
|
32
|
-
使用编排会话提供的隔离标识 `<namespace>`(来自编排会话上下文)为 SonarQube 容器指定独立的 docker compose 项目名,避免多个并发 change
|
|
32
|
+
使用编排会话提供的隔离标识 `<namespace>`(来自编排会话上下文)为 SonarQube 容器指定独立的 docker compose 项目名,避免多个并发 change 的容器互相冲突。环境隔离对象不限于数据库:SonarQube 实例与扫描项目均须按隔离标识隔离(独立 compose 项目名 + 项目 key 后缀,见第 6 节),禁止复用宿主既有实例/项目。
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
35
|
docker info
|
|
@@ -30,7 +30,9 @@ phases:
|
|
|
30
30
|
- "tasks.md 当前任务组任务项 + `[spec:xxx]` 溯源 specs/ 下相关 spec.md(需求细节与验收标准);存在时读取 clarify.md(架构方向结论)"
|
|
31
31
|
- "按 Task 项顺序逐个实现,聚焦当前子任务,不超出执行边界"
|
|
32
32
|
- "实施前识别 spec 描述的一并操作行为组(如状态变更与数据入队这类需同时发生的多个行为);实施后逐项自证每个行为真实生效,禁止以单一状态字段的实现替代其他行为"
|
|
33
|
-
- "
|
|
33
|
+
- "涉及数据模型或流程产出变更(含字段/实体承载位置搬迁、AI 生成等流程产物)时,自证须包含关键数据产出断言:产出字段已持久化到 spec 指定承载位置且值完整非空、搬迁承接与同一输入产出基线一致(按已加载接口测试规范执行);禁止仅以流程跑通/接口成功替代"
|
|
34
|
+
- "自证/验证需启动服务或基础设施时,一律按已加载技术栈 skill 的隔离启动规范以隔离方式启动,隔离标识与建议端口以视图 Worktree 区块为准;隔离对象不限于数据库——为本变更启动的 mock 服务、消息队列/缓存等中间件、扫描分析平台等一切环境资源均须按隔离标识隔离,禁止复用既有实例或共享端口;启动前发现隔离环境有残留时,先按已加载的清理规范幂等清空再启动"
|
|
35
|
+
- "涉及接口契约或数据口径变更(按已加载的接口测试规范判定)时,实施过程中即时更新/新建与变更接口对应的 `.http` 测试脚本(已有脚本更新为匹配变更后契约,尚无脚本则新建,并同步 SQL 前置数据脚本),按已加载接口测试规范的完整执行顺序(含 SQL 前置数据准备、隔离启动基础设施与服务、执行 `.http` 脚本)执行接口测试,确保变更内容真实有效;执行结果纳入自证与 self_check_results;修复被驳回 issue 后再次提交前同样须执行"
|
|
34
36
|
- "自证完成后停止本次启动的服务/基础设施实例,不留残留"
|
|
35
37
|
- "环境/基础设施问题(schema 缺失、DDL 未执行、依赖未安装)应通过 migration/初始化脚本等代码层面解决;仅生产级凭据、真实第三方资源、人工运维才走 blocker/豁免"
|
|
36
38
|
- "issue 指向文件的目录已并入执行边界,修复这些文件(含回归引入问题)不算越界;issue 的 file 字段指向 openspec/changes/ 下文档时,按该 issue 要求修复对应文档同样不算越界(仅限该 issue 指定文件,不得扩大范围);可按 issue 的 suggestion 直接执行修复;reviewer 的 boundary_expansion 已并入边界"
|
|
@@ -53,7 +55,7 @@ phases:
|
|
|
53
55
|
- "需以 verdict=failed + blocker 上报的条件:需求模糊 / design 问题 / 技术阻塞 / 超出执行边界"
|
|
54
56
|
- "禁止以 SonarQube 平台状态修改(SAFE/WONTFIX/False Positive/Confirmed 等)作为豁免手段:平台状态仅作用于当前 SonarQube 项目实例,每次 change 使用独立项目 key,不跨 change 生效;存量问题跨 change 豁免必须经 exempt_issue_ids 申请 → 报源 reviewer 裁定 dismissed 的流程"
|
|
55
57
|
- "禁止以压制手段静默规避确定性质量工具问题:压制必须附明确理由并经豁免申请流程裁定,不得以工具不再报错为由跳过申报"
|
|
56
|
-
- "
|
|
58
|
+
- "禁止为腾出端口或资源而停止、复用或干扰非本变更启动的进程(含用户或其他会话运行的服务实例);验证所需环境(数据库、mock 服务、消息队列/缓存等中间件、扫描分析平台等)一律按隔离标识隔离启动,禁止共享或占用宿主既有实例"
|
|
57
59
|
- name: review
|
|
58
60
|
steps:
|
|
59
61
|
- id: quality_review
|
|
@@ -66,10 +68,10 @@ phases:
|
|
|
66
68
|
instructions:
|
|
67
69
|
- "依据 opx_status 提供的变更检测结果分流:无代码/配置变更且本层无待复核/待裁定项 → 直接提交 passed,不运行全量工具检查,并以一条 step 名首段为 no_change 的 validation_steps 声明本轮整体豁免必做清单(completed=false + 结构化 skip_reason 注明判定依据);无变更但有本层待复核/待裁定项 → 仅复核/裁定待处理项后提交,不跑全量工具扫描;有变更 → 加载质量门类 skill 获取工具清单与映射规则,按已加载质量门 skill 必做清单逐项执行全部确定性工具检查(代码格式/架构约束/静态分析/单元测试编译/深度扫描——按必做清单中的对应项逐项执行并逐项申报),将工具输出映射为统一 issue 结构后提交"
|
|
68
70
|
- "「有变更」分支先审查上方「本次变更证据」区块的变更文件清单与区间 diff 内容后自行判断:若确认本次变更仅为注释/文档性且无逻辑影响(非注释掉代码、非结构性/格式化变更、非文件重命名),可自行决定跳过全量工具检查直接提交 passed(validation_steps 以 step=no_change 申报整体豁免必做清单);无法确认或含逻辑/结构变更,必须顺序运行全部确定性工具检查;裁量仅免除全量工具扫描,不免除复核/裁定——本层 blocking issue 仍须处理至终态后提交"
|
|
69
|
-
- "逐项验证:①task 产出完整性 ②启动服务并检查健康 ③独立执行全量 API 测试并审查质量(含 API 测试自闭环链路:创建后可检索、状态变更后可见、删除后不可见;测试数据贴近业务语义,禁止无意义占位值) ④审查测试代码质量(断言放水/Mock 过度/覆盖不足)⑤业务行为真实性:对照 spec 逐项核验每个业务行为的实现与断言证据(含一并操作的行为组、连带效果、下游消费),识别『仅打状态标记未实际生效』类缺陷"
|
|
71
|
+
- "逐项验证:①task 产出完整性 ②启动服务并检查健康 ③独立执行全量 API 测试并审查质量(含 API 测试自闭环链路:创建后可检索、状态变更后可见、删除后不可见;测试数据贴近业务语义,禁止无意义占位值) ④审查测试代码质量(断言放水/Mock 过度/覆盖不足)⑤业务行为真实性:对照 spec 逐项核验每个业务行为的实现与断言证据(含一并操作的行为组、连带效果、下游消费),识别『仅打状态标记未实际生效』类缺陷 ⑥关键数据产出核验:对照 spec 数据产出要求,核验实现与测试是否覆盖产出数据落位(spec 指定承载位置)、完整非空、搬迁承接与同一输入基线一致,识别『流程正常但数据缺失/落错位置』类缺陷"
|
|
70
72
|
- "验证完成后(含 verdict=failed 报 issue 回退 implement 的轮次)按已加载的清理规范执行幂等清理:停止残留的服务实例、清理本 change 的隔离环境、删除残留的隔离扫描项目;无残留即通过,不因重复执行报错;清理结果纳入 validation_steps 申报(未执行须以 completed=false + 结构化 skip_reason 说明)"
|
|
71
|
-
- "本次变更仅含测试代码(判定:视图「变更范围」文件清单全部命中已加载技术栈 skill 定义的测试代码路径,且无生产代码、接口契约、构建与测试配置等非测试文件)时,豁免②启动服务并检查健康与③独立执行全量 API 测试,仅验证①④;豁免时 validation_steps 中②③置 completed=false,并以结构化 skip_reason 申报(格式:{\"item\":\"<对应验证项>\",\"category\":\"<降级类别>\",\"adjudication\":\"user_response|unattended_auto|env_unavailable\",\"note\":\"<说明>\"}),note 注明「本次变更仅含测试代码,豁免启动服务与 API
|
|
72
|
-
- "读取 design.md 的 API 定义(请求/响应结构、数据模型)与 specs/ 下相关 spec.md
|
|
73
|
+
- "本次变更仅含测试代码(判定:视图「变更范围」文件清单全部命中已加载技术栈 skill 定义的测试代码路径,且无生产代码、接口契约、构建与测试配置等非测试文件)时,豁免②启动服务并检查健康与③独立执行全量 API 测试,仅验证①④;豁免时 validation_steps 中②③置 completed=false,并以结构化 skip_reason 申报(格式:{\"item\":\"<对应验证项>\",\"category\":\"<降级类别>\",\"adjudication\":\"user_response|unattended_auto|env_unavailable\",\"note\":\"<说明>\"}),note 注明「本次变更仅含测试代码,豁免启动服务与 API 测试」及判定依据;⑤⑥的实现核验在纯测试代码变更场景无对象,其断言证据核验并入④测试代码质量"
|
|
74
|
+
- "读取 design.md 的 API 定义(请求/响应结构、数据模型)与 specs/ 下相关 spec.md(需求细节、验收标准与数据产出要求),用于准备测试数据与对照 API 合约"
|
|
73
75
|
- "加载匹配维度 skill,以本轮 diff/变更文件为锚点结合 skill 规范与领域知识做拓展审查(skill 仅为最低基准);顺带发现的非本轮缺陷按对应维度严重级别标准报 issue,禁止静默丢弃"
|
|
74
76
|
- "审查新 issue 前先查看视图「待复核」区块中的既有 issue(仅本审查者报源),避免语义重复"
|
|
75
77
|
- "报 issue 时必须显式声明归因维度 dimension(style/architecture/performance/security/maintainability):本 reviewer 不归属任一质量维度,dimension 即该 issue 归属的目标质量维度"
|
|
@@ -61,7 +61,9 @@ phases:
|
|
|
61
61
|
- "tasks.md 当前任务组任务项 + `[spec:xxx]` 溯源 specs/ 下相关 spec.md(需求细节与验收标准);存在时读取 clarify.md(架构方向结论)"
|
|
62
62
|
- "按 Task 项顺序逐个实现,聚焦当前子任务,不超出执行边界"
|
|
63
63
|
- "实施前识别 spec 描述的一并操作行为组(如状态变更与数据入队这类需同时发生的多个行为);实施后逐项自证每个行为真实生效,禁止以单一状态字段的实现替代其他行为"
|
|
64
|
-
- "
|
|
64
|
+
- "涉及数据模型或流程产出变更(含字段/实体承载位置搬迁、AI 生成等流程产物)时,自证须包含关键数据产出断言:产出字段已持久化到 spec 指定承载位置且值完整非空、搬迁承接与同一输入产出基线一致(按已加载接口测试规范执行);禁止仅以流程跑通/接口成功替代"
|
|
65
|
+
- "自证/验证需启动服务或基础设施时,一律按已加载技术栈 skill 的隔离启动规范以隔离方式启动,隔离标识与建议端口以视图 Worktree 区块为准;隔离对象不限于数据库——为本变更启动的 mock 服务、消息队列/缓存等中间件、扫描分析平台等一切环境资源均须按隔离标识隔离,禁止复用既有实例或共享端口;启动前发现隔离环境有残留时,先按已加载的清理规范幂等清空再启动"
|
|
66
|
+
- "涉及接口契约或数据口径变更(按已加载的接口测试规范判定)时,实施过程中即时更新/新建与变更接口对应的 `.http` 测试脚本(已有脚本更新为匹配变更后契约,尚无脚本则新建,并同步 SQL 前置数据脚本),按已加载接口测试规范的完整执行顺序(含 SQL 前置数据准备、隔离启动基础设施与服务、执行 `.http` 脚本)执行接口测试,确保变更内容真实有效;执行结果纳入自证与 self_check_results;修复被驳回 issue 后再次提交前同样须执行"
|
|
65
67
|
- "自证完成后停止本次启动的服务/基础设施实例,不留残留"
|
|
66
68
|
- "环境/基础设施问题(schema 缺失、DDL 未执行、依赖未安装)应通过 migration/初始化脚本等代码层面解决;仅生产级凭据、真实第三方资源、人工运维才走 blocker/豁免"
|
|
67
69
|
- "issue 指向文件的目录已并入执行边界,修复这些文件(含回归引入问题)不算越界;issue 的 file 字段指向 openspec/changes/ 下文档时,按该 issue 要求修复对应文档同样不算越界(仅限该 issue 指定文件,不得扩大范围);可按 issue 的 suggestion 直接执行修复;reviewer 的 boundary_expansion 已并入边界"
|
|
@@ -72,7 +74,7 @@ phases:
|
|
|
72
74
|
- "提交前完成自检并经 self_check_results 汇总"
|
|
73
75
|
- "提交前按已加载的维度规范类 skill(style/architecture/performance/security/maintainability 能力类别)对本次变更文件逐项自检(聚焦本轮变更文件,不扩散全量代码),自检结论与工具检查结果一并汇总进 self_check_results;修复被驳回 issue 后再次提交前同样须按维度规范类 skill 对变更文件自检后再提交"
|
|
74
76
|
- "自检须覆盖已加载的维度规范类 skill 的全部检查点,self_check_results 按维度逐项列结论(通过/遗留及理由),禁止仅汇总工具检查结果"
|
|
75
|
-
- "修复完成的 issue 经 fixed_issue_ids 上报后进入待复核(review
|
|
77
|
+
- "修复完成的 issue 经 fixed_issue_ids 上报后进入待复核(review)状态,终态由报源 reviewer 复核裁定;不可修 issue 经 exempt_issue_ids 申请豁免(申请后进入待裁定(review)状态,由报源 reviewer 在后续审查轮次裁定,被驳回的 issue 回待修复);Info 级 issue 不阻塞提交,无需申请豁免;全部 blocking issue 均已上报修复或申请豁免后才可 passed 提交推进"
|
|
76
78
|
- "确定性质量工具检查出的问题不得以压制手段(压制注解/注释抑制/规则排除配置/平台状态标注/构建跳过开关等)静默规避:确需压制时,必须在压制处代码注释说明清楚理由(commit message 可补充),并经 exempt_issue_ids 申请豁免由报源 reviewer 裁定;不得以 fixed_issue_ids 冒充修复;被驳回后须移除压制并真实修复"
|
|
77
79
|
- "遇外部依赖、凭证、真实输入或必须 stub/降级/跳过验收才能继续时,以 verdict=failed 提交 blocker(含 source_role、task_id、category、description、evidence、attempted_actions、options)"
|
|
78
80
|
- "提交 opx_agent_submit:passed 时存在未完成或被驳回(open/rejected)的子任务必须全部列入 completed_task_ids(数字 id 如 1、2,或任务编号如 1.1),全部子任务已验证时可省略;不可修 issue 申请豁免(exempt_issue_ids)"
|
|
@@ -86,7 +88,7 @@ phases:
|
|
|
86
88
|
- "需以 verdict=failed + blocker 上报的条件:需求模糊 / design 问题 / 技术阻塞 / 超出执行边界"
|
|
87
89
|
- "禁止以 SonarQube 平台状态修改(SAFE/WONTFIX/False Positive/Confirmed 等)作为豁免手段:平台状态仅作用于当前 SonarQube 项目实例,每次 change 使用独立项目 key,不跨 change 生效;存量问题跨 change 豁免必须经 exempt_issue_ids 申请 → 报源 reviewer 裁定 dismissed 的流程"
|
|
88
90
|
- "禁止以压制手段静默规避确定性质量工具问题:压制必须附明确理由并经豁免申请流程裁定,不得以工具不再报错为由跳过申报"
|
|
89
|
-
- "
|
|
91
|
+
- "禁止为腾出端口或资源而停止、复用或干扰非本变更启动的进程(含用户或其他会话运行的服务实例);验证所需环境(数据库、mock 服务、消息队列/缓存等中间件、扫描分析平台等)一律按隔离标识隔离启动,禁止共享或占用宿主既有实例"
|
|
90
92
|
- name: review
|
|
91
93
|
steps:
|
|
92
94
|
- id: verify_tool
|
|
@@ -123,10 +125,10 @@ phases:
|
|
|
123
125
|
on_pass: verify_quality
|
|
124
126
|
on_fail: implement
|
|
125
127
|
instructions:
|
|
126
|
-
- "逐项验证:①task 产出完整性 ②启动服务并检查健康 ③独立执行全量 API 测试并审查质量(含 API 测试自闭环链路:创建后可检索、状态变更后可见、删除后不可见;测试数据贴近业务语义,禁止无意义占位值) ④审查测试代码质量(断言放水/Mock 过度/覆盖不足)⑤业务行为真实性:对照 spec 逐项核验每个业务行为的实现与断言证据(含一并操作的行为组、连带效果、下游消费),识别『仅打状态标记未实际生效』类缺陷"
|
|
128
|
+
- "逐项验证:①task 产出完整性 ②启动服务并检查健康 ③独立执行全量 API 测试并审查质量(含 API 测试自闭环链路:创建后可检索、状态变更后可见、删除后不可见;测试数据贴近业务语义,禁止无意义占位值) ④审查测试代码质量(断言放水/Mock 过度/覆盖不足)⑤业务行为真实性:对照 spec 逐项核验每个业务行为的实现与断言证据(含一并操作的行为组、连带效果、下游消费),识别『仅打状态标记未实际生效』类缺陷 ⑥关键数据产出核验:对照 spec 数据产出要求,核验实现与测试是否覆盖产出数据落位(spec 指定承载位置)、完整非空、搬迁承接与同一输入基线一致,识别『流程正常但数据缺失/落错位置』类缺陷"
|
|
127
129
|
- "验证完成后(含 verdict=failed 轮次)按已加载的清理规范执行幂等清理:停止残留的服务实例、清理本 change 的隔离环境、删除残留的隔离扫描项目;无残留即通过,清理结果纳入 validation_steps 申报"
|
|
128
|
-
- "本次变更仅含测试代码(判定:视图「变更范围」文件清单全部命中已加载技术栈 skill 定义的测试代码路径,且无生产代码、接口契约、构建与测试配置等非测试文件)时,豁免②启动服务并检查健康与③独立执行全量 API 测试,仅验证①④;豁免时 validation_steps 中②③置 completed=false,并以结构化 skip_reason 申报(格式:{\"item\":\"<对应验证项>\",\"category\":\"<降级类别>\",\"adjudication\":\"user_response|unattended_auto|env_unavailable\",\"note\":\"<说明>\"}),note 注明「本次变更仅含测试代码,豁免启动服务与 API
|
|
129
|
-
- "读取 design.md 的 API 定义(请求/响应结构、数据模型)与 specs/ 下相关 spec.md
|
|
130
|
+
- "本次变更仅含测试代码(判定:视图「变更范围」文件清单全部命中已加载技术栈 skill 定义的测试代码路径,且无生产代码、接口契约、构建与测试配置等非测试文件)时,豁免②启动服务并检查健康与③独立执行全量 API 测试,仅验证①④;豁免时 validation_steps 中②③置 completed=false,并以结构化 skip_reason 申报(格式:{\"item\":\"<对应验证项>\",\"category\":\"<降级类别>\",\"adjudication\":\"user_response|unattended_auto|env_unavailable\",\"note\":\"<说明>\"}),note 注明「本次变更仅含测试代码,豁免启动服务与 API 测试」及判定依据;⑤⑥的实现核验在纯测试代码变更场景无对象,其断言证据核验并入④测试代码质量"
|
|
131
|
+
- "读取 design.md 的 API 定义(请求/响应结构、数据模型)与 specs/ 下相关 spec.md(需求细节、验收标准与数据产出要求),用于准备测试数据与对照 API 合约"
|
|
130
132
|
- "有 task 未通过时必须 verdict=failed 并经 failed_tasks 上报(task_id + reason);passed 时不允许提供 failed_tasks"
|
|
131
133
|
- "当发现修复需要调整 openspec/changes/ 下文档时,所报 issue 的 file 字段必须指向目标文档"
|
|
132
134
|
- "报 issue 时必须显式声明归因维度 dimension(style/architecture/performance/security/maintainability):本 reviewer 不归属任一质量维度,dimension 即该 issue 归属的目标质量维度"
|
|
@@ -173,7 +175,7 @@ phases:
|
|
|
173
175
|
on_fail: verify_cleanup
|
|
174
176
|
instructions:
|
|
175
177
|
- "将基准分支({{base_branch}})最新代码合并进当前工作分支(git merge 基准分支),解决合并冲突;修复范围仅限合并冲突涉及文件与回归失败定位文件(改动仅用于解决合并引入的问题,可超出现有执行边界,禁止借机扩大范围)"
|
|
176
|
-
- "
|
|
178
|
+
- "合并后回归复验:按已加载的开发实践规范执行构建验证;按已加载的接口测试规范启动服务并执行接口测试(含关键数据产出/搬迁回归断言);全部通过才算通过,不分失败来源(含基准分支新代码引入的失败)"
|
|
177
179
|
- "按已加载的清理规范执行幂等清理:停止残留的服务实例、清理本 change 的隔离环境、删除残留的扫描分析项目;无对应 skill 规范时跳过并在申报中注明"
|
|
178
180
|
- "清理后确认工作分支 git status 干净(含未跟踪文件)"
|
|
179
181
|
- "逐项申报 validation_steps(completed + 证据;未执行项以 completed=false + 结构化 skip_reason 申报:{\"item\":\"<必做项>\",\"category\":\"<降级类别>\",\"adjudication\":\"user_response|unattended_auto|env_unavailable\",\"note\":\"<说明>\"})"
|
package/dsh/cordis.patch.yml
CHANGED
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
|
|
54
54
|
提交前按已加载 skill(含质量维度规范类)完成自检,审查兜底与终局裁定仍由对应 reviewer 承担(自检不替代 reviewer 裁定)。
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
自证/验证启动的服务与基础设施一律隔离启动、用后即停,自证完成即清理,不留残留。隔离对象不限于数据库(mock 服务、消息队列/缓存等中间件、扫描分析平台均按隔离标识隔离)。
|
|
57
57
|
|
|
58
58
|
任务执行与提交约束(Task/Issue 清单处理、环境问题处置、blocker 提交、文档读取范围、工具调用边界)以 opx_status 操作指引与约束区块为准,此处不重复描述。
|
|
59
59
|
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
- 命名合理性:按 skill 中的通用语言与命名规范——命名体现意图、不与其他场景混淆、与术语表一致防漂移
|
|
99
99
|
- 方案复用与平行实现:按 skill 中的方案复用与平行实现约定——同类能力已有既有实现时(如已有手工限流基础框架仍引入新限流框架且不改动原实现)须优先复用或扩展,确需平行引入(新旧并存)时按上文需用户拍板问题的确认机制确认并存期与技术债,无人值守缺省裁决为方案必须包含原技术实现的迁移方案
|
|
100
100
|
- 技术债识别:按 skill 中的技术债识别约定——方案是否引入可识别技术债(过时技术选型、技术迁移中断、平行引入、临时方案固化、契约不一致),引入的债务须在方案中规划偿还或说明可接受理由
|
|
101
|
-
- 架构演进审查:按 skill
|
|
101
|
+
- 架构演进审查:按 skill 中的架构演进审查约定——方案是否延续项目整体架构方向、是否支持长期迭代演进、是否破坏既有契约(接口/数据/行为兼容性)、重构方案是否贴合项目历史逻辑并保持既有行为语义;破坏兼容或偏离架构脉络须在方案中说明理由与过渡安排;涉及字段/实体承载位置搬迁时,方案须明确数据承接与迁移设计:新承载位置、产出字段写入目标、存量数据迁移、旧位置兼容与清理;缺失按既有契约破坏/重构贴合判例报 issue
|
|
102
102
|
|
|
103
103
|
## 执行边界产出(架构师身份)
|
|
104
104
|
|
|
@@ -185,8 +185,8 @@
|
|
|
185
185
|
| 级别 | task 产出验证 | 服务启动验证 | 测试审查 |
|
|
186
186
|
|------|-------------|-------------|---------|
|
|
187
187
|
| Critical | 核心 task 产出文件缺失 | 服务无法启动(无论是否本次变更引起) | 测试依赖无隔离环境导致 CI 必然失败 |
|
|
188
|
-
| High | 关键 task
|
|
189
|
-
| Medium | task
|
|
188
|
+
| High | 关键 task 产出不完整;行为无实现(仅状态标记未实际生效);流程执行正常但关键数据产出缺失/未落库/落错位置(区别于『仅打状态标记未实际生效』:本行=流程与响应正常,产出物缺失或落错位) | 健康检查不通过;关键外部依赖不可用;新增接口无法调通(不论原因) | 核心业务逻辑无测试覆盖;Mock 了被测对象本身;测试仅断言可见状态/响应而 spec 要求的一并操作行为(入队/落库/下游消费等)无断言,将缺陷固化为期望行为;测试仅验证流程跑通/响应成功,未对 spec 要求的关键数据产出(落位/完整性)断言 |
|
|
189
|
+
| Medium | task 产出存在但质量不达标;关键数据产出部分缺失、值不完整或与 spec 指定承载位置不一致 | 启动有 WARN 但服务可用 | 断言过于宽松;缺少边界值测试;关键数据产出部分缺失、值不完整或与 spec 指定承载位置不一致 |
|
|
190
190
|
| Low | 产出文件位置不符合约定 | 启动日志格式不统一 | 测试命名不规范但不影响运行 |
|
|
191
191
|
| Info | 建议补充额外产出物 | 建议增加启动度量指标 | 建议补充边缘用例 |
|
|
192
192
|
|
|
@@ -267,7 +267,7 @@
|
|
|
267
267
|
- 命名合理性:按 skill 中的通用语言与命名规范——命名体现意图、不与其他场景混淆、与术语表一致防漂移
|
|
268
268
|
- 方案复用与平行实现:识别引入与既有实现功能重叠的平行实现(另起一套逻辑)的情况——同类能力已有既有实现仍另建实现且不改动原实现、无迁移方案则报 issue。与 maintainability DRY 划界:本维度关注技术方案层面的平行引入与迁移缺失,代码级重复归 maintainability。具体复用与迁移要求按 skill 约定
|
|
269
269
|
- 技术债识别:按 skill 中的技术债识别约定——识别实现中的存量与新增技术债(过时技术选型、迁移中断残留、平行实现、临时方案固化、契约不一致),按统一严重级别报 issue
|
|
270
|
-
- 架构演进审查:按 skill 中的架构演进审查约定——实现是否延续项目整体架构方向、是否支持长期迭代演进、是否破坏既有契约(接口签名/数据格式/行为语义兼容性)、重构是否贴合项目历史逻辑并保持既有行为语义,破坏兼容或改变既有语义按统一严重级别报 issue
|
|
270
|
+
- 架构演进审查:按 skill 中的架构演进审查约定——实现是否延续项目整体架构方向、是否支持长期迭代演进、是否破坏既有契约(接口签名/数据格式/行为语义兼容性)、重构是否贴合项目历史逻辑并保持既有行为语义,破坏兼容或改变既有语义按统一严重级别报 issue;涉及字段/实体承载位置搬迁时,方案须明确数据承接与迁移设计:新承载位置、产出字段写入目标、存量数据迁移、旧位置兼容与清理;缺失按既有契约破坏/重构贴合判例报 issue
|
|
271
271
|
|
|
272
272
|
### 可维护性维度
|
|
273
273
|
|
|
@@ -316,7 +316,7 @@
|
|
|
316
316
|
## 职责边界(确定性检查与任务验证)
|
|
317
317
|
|
|
318
318
|
- **确定性工具检查**:工具维度审查者与 simple 合并审查者被允许运行确定性工具检查(代码格式/架构约束/静态分析/单元测试编译/深度扫描),按已加载质量门 skill 的必做清单执行并将输出映射为统一 issue 结构;检查结果纳入审查结论
|
|
319
|
-
- **任务验证**:任务维度审查者与 simple 合并审查者承担任务验证职责,验证 task 产出完整性、服务健康与 API 测试质量;未通过的 task 经 failed_tasks
|
|
319
|
+
- **任务验证**:任务维度审查者与 simple 合并审查者承担任务验证职责,验证 task 产出完整性、服务健康与 API 测试质量;未通过的 task 经 failed_tasks 上报;验证完成后按清理规范做幂等清理,不留环境残留;数据产出核验——对照 spec 数据产出要求核验关键数据产出落位/完整性/搬迁承接/基线对比,识别『流程正常但数据缺失/落错位置』类缺陷
|
|
320
320
|
- **工具改进**:可工具化的质量 pattern 双报业务 issue 与工具改进 issue(规则草案由审查者提供),实施工作由开发者完成,审查者不直接改动规则/配置文件以外的实现
|
|
321
321
|
|
|
322
322
|
## 压制判定
|
package/package.json
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ifrankwang/openspec-agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.127.0",
|
|
4
|
+
"description": "OpenSpec change orchestration plugin for AI coding agents (Claude Code, Codex, ZCode, DeepSeek Harness, OpenCode)",
|
|
5
|
+
"author": "ifrankwang",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/ifrankwang/openspec-agents#readme",
|
|
8
|
+
"bugs": {
|
|
9
|
+
"url": "https://github.com/ifrankwang/openspec-agents/issues"
|
|
10
|
+
},
|
|
4
11
|
"type": "module",
|
|
5
12
|
"main": "./src/core/index.ts",
|
|
6
13
|
"exports": {
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
buildAgents,
|
|
26
26
|
buildSkills,
|
|
27
27
|
copyWorkflows,
|
|
28
|
+
copyLicense,
|
|
28
29
|
readPkgVersion,
|
|
29
30
|
type PluginPackageResult,
|
|
30
31
|
} from "../plugin-common/index.ts"
|
|
@@ -32,6 +33,9 @@ import {
|
|
|
32
33
|
/** DSH bundle 的 npm 包名(与项目发布包一致,便于 `dsh plugin add` 与 sync 识别)。 */
|
|
33
34
|
export const DSH_PLUGIN_NAME = "@ifrankwang/openspec-agents"
|
|
34
35
|
|
|
36
|
+
/** DSH bundle 包的作者署名(与各插件包 plugin.json 的 author 保持一致)。 */
|
|
37
|
+
export const PLUGIN_AUTHOR_STRING = "ifrankwang"
|
|
38
|
+
|
|
35
39
|
/** 默认插件包输出目录(dist/ 已 gitignore,生成物不入库)。 */
|
|
36
40
|
export const DEEP_SEEK_HARNESS_PLUGIN_DIR = resolve("dist", "deepseek-harness-plugin")
|
|
37
41
|
/** 简写别名,便于与 `dsh` 缩写保持一致。 */
|
|
@@ -174,6 +178,7 @@ export function buildDeepSeekHarnessPlugin(outDir: string = DEEP_SEEK_HARNESS_PL
|
|
|
174
178
|
const skills = buildSkills(join(outDir, "skills"))
|
|
175
179
|
bundleMcpServer(outDir)
|
|
176
180
|
copyWorkflows(outDir)
|
|
181
|
+
copyLicense(outDir)
|
|
177
182
|
|
|
178
183
|
writeFileSync(
|
|
179
184
|
join(outDir, "package.json"),
|
|
@@ -184,6 +189,8 @@ export function buildDeepSeekHarnessPlugin(outDir: string = DEEP_SEEK_HARNESS_PL
|
|
|
184
189
|
description: PLUGIN_DESCRIPTION,
|
|
185
190
|
type: "module",
|
|
186
191
|
private: true,
|
|
192
|
+
author: PLUGIN_AUTHOR_STRING,
|
|
193
|
+
license: "MIT",
|
|
187
194
|
dsh: {
|
|
188
195
|
bundle: {
|
|
189
196
|
patch: `./${MANIFEST_PATCH_FILENAME}`,
|
|
@@ -37,7 +37,7 @@ interface ToolSpec {
|
|
|
37
37
|
const TOOL_SPECS: Record<string, ToolSpec> = {
|
|
38
38
|
opx_orch_init: {
|
|
39
39
|
description:
|
|
40
|
-
"初始化编排会话。传入变更 ID 和任务组 ID,工具自动解析 tasks.md 提取全部任务组并解析目标组子任务。可通过 recovery 参数恢复到指定阶段。无 recovery 重复初始化当前任务组时保留其阶段和进度;切换到其它任务组时初始化该组。可选 mode 参数(full/simple,缺省 simple
|
|
40
|
+
"初始化编排会话。传入变更 ID 和任务组 ID,工具自动解析 tasks.md 提取全部任务组并解析目标组子任务。可通过 recovery 参数恢复到指定阶段。无 recovery 重复初始化当前任务组时保留其阶段和进度;切换到其它任务组时初始化该组。可选 mode 参数(full/simple,缺省 simple)选择流程模式:首次新建状态时固化;已开始的变更仅切组(其他任务组均已完成或从未激活)或 recovery.phase=task_analysis 重制当前组(其他任务组同样须已完成或从未激活)时可更新,其余场景传不同 mode 报错。",
|
|
41
41
|
schema: orchInitSchema,
|
|
42
42
|
execute: (args, ctx) => initExecute(args as any, ctx),
|
|
43
43
|
},
|
|
@@ -18,9 +18,9 @@ import { parseAgentMd, resolve, EXCLUDED_AGENTS } from "../agent-md.ts"
|
|
|
18
18
|
|
|
19
19
|
export const PLUGIN_NAME = "openspec-agents"
|
|
20
20
|
export const PLUGIN_DESCRIPTION =
|
|
21
|
-
"OpenSpec change
|
|
22
|
-
/** plugin.json author
|
|
23
|
-
export const PLUGIN_AUTHOR = "
|
|
21
|
+
"OpenSpec change orchestration for AI coding agents: turn OpenSpec change specs into reviewed, high-quality code."
|
|
22
|
+
/** plugin.json author(各插件包统一署名项目作者)。 */
|
|
23
|
+
export const PLUGIN_AUTHOR = "ifrankwang"
|
|
24
24
|
|
|
25
25
|
export function readPkgVersion(): string {
|
|
26
26
|
return (JSON.parse(readFileSync(resolve("package.json"), "utf-8")) as { version: string }).version
|
|
@@ -98,6 +98,14 @@ export function copyWorkflows(targetDir: string): void {
|
|
|
98
98
|
cpSync(resolve("assets", "workflows"), join(targetDir, "assets", "workflows"), { recursive: true })
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/** 复制 MIT 协议文件到插件包根目录(随插件与 marketplace 一起分发)。 */
|
|
102
|
+
export function copyLicense(targetDir: string): void {
|
|
103
|
+
const license = resolve("LICENSE")
|
|
104
|
+
if (existsSync(license)) {
|
|
105
|
+
cpSync(license, join(targetDir, "LICENSE"))
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
101
109
|
export interface PluginPackageParams {
|
|
102
110
|
/** 输出目录(如 dist/zcode-plugin/) */
|
|
103
111
|
outDir: string
|
|
@@ -129,6 +137,7 @@ export function buildPluginPackage({ outDir, manifestDirName }: PluginPackagePar
|
|
|
129
137
|
// workflow 定义随包分发:bundle 内 TASK_WORKFLOW_PATH 按部署深度逐级上溯探测
|
|
130
138
|
// assets/workflows/task.yaml,插件根(dist/cache 形态)上溯 1 级即命中
|
|
131
139
|
copyWorkflows(outDir)
|
|
140
|
+
copyLicense(outDir)
|
|
132
141
|
|
|
133
142
|
writeFileSync(
|
|
134
143
|
join(outDir, manifestDirName, "plugin.json"),
|
package/src/core/state.ts
CHANGED
|
@@ -271,8 +271,8 @@ export async function readStateByChangeId(worktree: string, changeId: string): P
|
|
|
271
271
|
createdAt: legacy.createdAt,
|
|
272
272
|
updatedAt: legacy.updatedAt,
|
|
273
273
|
unattended: legacy.unattended,
|
|
274
|
-
//
|
|
275
|
-
//
|
|
274
|
+
// 透传固化模式:读取不改动 mode(缺 mode 的旧变更保持缺省,消费端读时兜底 full,不在此写回);
|
|
275
|
+
// init 在允许窗口(切组/重制当前组,其他任务组均终态或从未激活)内可显式更新 state.mode
|
|
276
276
|
mode: legacy.mode,
|
|
277
277
|
}
|
|
278
278
|
if (needsUpgrade) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "path"
|
|
2
|
-
import type { TaskItem, TaskStatus, WorkflowMode } from "../types.ts"
|
|
2
|
+
import type { OrchestrateState, TaskItem, TaskStatus, WorkflowMode } from "../types.ts"
|
|
3
3
|
import { BUILD_PHASE_TARGETS, REVIEW_LAYERS, REVIEW_VERIFY_STEPS } from "../types.ts"
|
|
4
4
|
import { agentToReviewLayer } from "../constants.ts"
|
|
5
5
|
import { runGit, runGitChecked, getCurrentBranch, getMergeBase, isWorktreeClean, markTaskGroupCheckboxesComplete, mergeBranchToTarget, discoverDiskWorktrees, detectMainRepoPollution, detectChanges, type DetectChangesResult } from "../git.ts"
|
|
@@ -278,6 +278,25 @@ function assertValidRecovery(recovery: InitParams["recovery"]): void {
|
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
+
/**
|
|
282
|
+
* 判断目标组以外的全部任务组(workItems 中 `task:` 前缀、非目标组)是否"终态或从未激活",
|
|
283
|
+
* 即 mode 切换窗口(W1 切组 / W2 重制当前组)的共同前置条件:
|
|
284
|
+
* - 终态:item.phase === "done" 或 metadata.completed_at 已设置;
|
|
285
|
+
* - 从未激活:无执行痕迹——tags 为空且所有 task children 的 phase 均为 todo 或终态(isTerminalPhase)。
|
|
286
|
+
*/
|
|
287
|
+
function otherTaskGroupsSettled(state: OrchestrateState, targetGroupId: string): boolean {
|
|
288
|
+
return state.workItems
|
|
289
|
+
.filter((w) => w.id.startsWith("task:") && w.id !== `task:${targetGroupId}`)
|
|
290
|
+
.every((item) => {
|
|
291
|
+
if (item.phase === "done" || item.metadata["completed_at"] !== undefined) return true
|
|
292
|
+
const noTags = Object.keys(item.tags).length === 0
|
|
293
|
+
const childrenSettled = item.children
|
|
294
|
+
.filter((c) => c.type === "task")
|
|
295
|
+
.every((c) => c.phase === "todo" || isTerminalPhase(c.phase))
|
|
296
|
+
return noTags && childrenSettled
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
|
|
281
300
|
export async function initExecute(params: InitParams, ctx: ToolContext): Promise<string> {
|
|
282
301
|
assertOrchestrator(ctx, "opx_orch_init")
|
|
283
302
|
|
|
@@ -326,6 +345,31 @@ export async function initExecute(params: InitParams, ctx: ToolContext): Promise
|
|
|
326
345
|
let state = await readStateByChangeId(ctx.worktree, args.change_id)
|
|
327
346
|
const wasCurrentGroup = state?.taskGroupId === args.task_group_id
|
|
328
347
|
|
|
348
|
+
// state 已存在时的 mode 变更窗口校验/更新(位于 for 循环前——循环内非活跃组新建 item 与
|
|
349
|
+
// applyRecoveryState 均读 state.mode;错误早于任何状态变更抛出,不落盘):
|
|
350
|
+
// - 无变更意图(不传 mode 或与生效值一致——旧 state 缺 mode 时生效值兜底 full)→ 放行,保持既有行为;
|
|
351
|
+
// - W1 切组:task_group_id ≠ state.taskGroupId 且其他任务组均终态或从未激活;
|
|
352
|
+
// - W2 重制当前组:recovery.phase=task_analysis 且其他任务组均终态或从未激活
|
|
353
|
+
// (reopenIssues 仅支持 dev_impl,与 W2 天然互斥,不构成冲突路径);
|
|
354
|
+
// - 窗口外传不同 mode → 报错。
|
|
355
|
+
let modeSwitchNote: string | null = null
|
|
356
|
+
if (state && args.mode !== undefined && args.mode !== (state.mode ?? "full")) {
|
|
357
|
+
const othersSettled = otherTaskGroupsSettled(state, args.task_group_id)
|
|
358
|
+
const withinWindow =
|
|
359
|
+
((args.task_group_id !== state.taskGroupId || args.recovery?.phase === "task_analysis") && othersSettled)
|
|
360
|
+
if (!withinWindow) {
|
|
361
|
+
throw new Error(
|
|
362
|
+
`mode 参数与已固化的流程模式不一致:已固化 mode="${state.mode ?? "full"}"${state.mode === undefined ? "(旧变更未固化,读取时兜底 full)" : ""},传入值:"${args.mode}"。\n` +
|
|
363
|
+
`当前场景不允许切换流程模式:切换任务组须其他任务组均已完成或从未激活;重制当前组仅支持 recovery.phase="task_analysis"(其他任务组同样须已完成或从未激活)。\n` +
|
|
364
|
+
`请去掉 mode 参数继续沿用固化模式,或满足上述窗口条件后再切换。`
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
const prevMode = state.mode
|
|
368
|
+
state.mode = args.mode
|
|
369
|
+
// 旧变更未固化 mode 时读取兜底 full,切换说明按 full 表述
|
|
370
|
+
modeSwitchNote = `流程模式已从 ${prevMode ?? "full"} 切换为 ${args.mode}。`
|
|
371
|
+
}
|
|
372
|
+
|
|
329
373
|
if (!state) {
|
|
330
374
|
state = {
|
|
331
375
|
changeId: args.change_id,
|
|
@@ -335,8 +379,8 @@ export async function initExecute(params: InitParams, ctx: ToolContext): Promise
|
|
|
335
379
|
workItems: [],
|
|
336
380
|
createdAt: new Date().toISOString(),
|
|
337
381
|
updatedAt: new Date().toISOString(),
|
|
338
|
-
// 新建 state 时固化模式:取 init 参数 mode(缺省 simple);state
|
|
339
|
-
//
|
|
382
|
+
// 新建 state 时固化模式:取 init 参数 mode(缺省 simple);state 已存在时的 mode 变更
|
|
383
|
+
// 由上方窗口校验统一处理(W1 切组 / W2 重制当前组),旧 state 缺 mode 由消费端读时兜底 full,不写回。
|
|
340
384
|
mode: args.mode ?? "simple",
|
|
341
385
|
}
|
|
342
386
|
} else {
|
|
@@ -443,9 +487,10 @@ export async function initExecute(params: InitParams, ctx: ToolContext): Promise
|
|
|
443
487
|
state.taskGroupId = args.task_group_id
|
|
444
488
|
await writeState(ctx.worktree, state)
|
|
445
489
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
490
|
+
const parts = ["编排会话已初始化。"]
|
|
491
|
+
if (args.recovery) parts.push(`已恢复到 ${args.recovery.phase} 阶段。`)
|
|
492
|
+
if (modeSwitchNote) parts.push(modeSwitchNote)
|
|
493
|
+
return parts.join("")
|
|
449
494
|
}
|
|
450
495
|
|
|
451
496
|
async function bindWorktreeRefs(
|
|
@@ -217,7 +217,7 @@ export const orchInitSchema: JSONSchema = {
|
|
|
217
217
|
type: "string",
|
|
218
218
|
enum: ["full", "simple"],
|
|
219
219
|
description:
|
|
220
|
-
"流程模式选择:full=完整流程(analyze→implement→三重审查+收尾验证);simple=精简流程(implement→quality_review→done
|
|
220
|
+
"流程模式选择:full=完整流程(analyze→implement→三重审查+收尾验证);simple=精简流程(implement→quality_review→done,缺省)。首次新建编排状态时固化;已开始的变更仅在允许窗口内可更新:切换任务组(其他任务组均已完成或从未激活)或 recovery.phase=task_analysis 重制当前组(其他任务组同样须已完成或从未激活);其余场景传不同 mode 将报错。",
|
|
221
221
|
},
|
|
222
222
|
},
|
|
223
223
|
required: ["change_id", "task_group_id"],
|
package/src/core/tools/types.ts
CHANGED
|
@@ -28,7 +28,9 @@ export interface InitParams {
|
|
|
28
28
|
* 恢复后 currentStep 落在第一个未全部通过的 verify step,可能早于被重置的 step。 */
|
|
29
29
|
reset_steps?: ReviewVerifyStep[]
|
|
30
30
|
}
|
|
31
|
-
/** 流程模式(full/simple
|
|
31
|
+
/** 流程模式(full/simple):首次新建编排状态时固化;已开始的变更仅在切组(其他任务组均已完成
|
|
32
|
+
* 或从未激活)或 recovery.phase=task_analysis 重制当前组(其他任务组同样须已完成或从未激活)
|
|
33
|
+
* 时允许更新,窗口外传不同 mode 报错。 */
|
|
32
34
|
mode?: WorkflowMode
|
|
33
35
|
}
|
|
34
36
|
|
package/src/core/types.ts
CHANGED
|
@@ -136,6 +136,8 @@ export interface OrchestrateState {
|
|
|
136
136
|
updatedAt: string
|
|
137
137
|
unattended?: boolean
|
|
138
138
|
/** 变更开始时固化的流程模式(经 opx_orch_init 的 mode 参数写入,缺省 simple)。
|
|
139
|
-
* 缺失即旧变更,一律按 full 处理(读时兜底,不写回);已存在的 state
|
|
139
|
+
* 缺失即旧变更,一律按 full 处理(读时兜底,不写回);已存在的 state 仅在 init 允许窗口
|
|
140
|
+
* (切组且其他任务组均已完成或从未激活,或 recovery.phase=task_analysis 重制当前组)内
|
|
141
|
+
* 可被 mode 参数更新。 */
|
|
140
142
|
mode?: WorkflowMode
|
|
141
143
|
}
|