Release AirCoding-Alpha-0.1.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
53
CLAUDE.md
Normal file
53
CLAUDE.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# Aircoding — 约束铁律
|
||||||
|
|
||||||
|
## 1. 调度器约束
|
||||||
|
|
||||||
|
调度器(Scheduler)禁止以下行为:
|
||||||
|
|
||||||
|
- **禁止降级兜底**:不得以「先这样实现」「先跑通再说」「以后再改」「以后再补」「先回退」「先这样」等理由,使用简化逻辑、mock 实现、硬编码路径、或任何偏离设计方案的替代品。
|
||||||
|
- **必须完全遵循设计方案**:调度器的任务拆分、依赖图、执行序列、重试策略、错误传播等全部行为必须严格按照设计文档实现。设计方案未覆盖的场景,必须先更新设计方案再实现,不得在代码中自行裁决。
|
||||||
|
|
||||||
|
## 2. 执行器约束
|
||||||
|
|
||||||
|
执行器(Executor)禁止以下行为:
|
||||||
|
|
||||||
|
- **禁止降级兜底**:不得以「先这样跑通」「以后优化」「暂时绕过」「先硬编码」等理由,跳过或简化任何设计规定的执行步骤、工具调用链路、权限检查、日志落库、事件上报。
|
||||||
|
- **必须完全遵循设计方案**:执行器的工具链、Agent Loop、权限门控、结果验证等全部行为必须严格按照设计文档实现。任何偏离 = 未完成。
|
||||||
|
|
||||||
|
## 3. 审查器约束
|
||||||
|
|
||||||
|
审查器(Reviewer)是**强制调用**的关卡,不可跳过、不可默认通过、不可降级为静默占位。
|
||||||
|
|
||||||
|
### 3.1 禁止的判定依据
|
||||||
|
|
||||||
|
以下理由**不得**作为通过审查的依据:
|
||||||
|
|
||||||
|
- 「测试 pass」「测试全绿」
|
||||||
|
- 「实现存在」「函数存在」「文件存在」
|
||||||
|
- 「typecheck 通过」「build 通过」「lint 通过」
|
||||||
|
- 「看起来正确」「逻辑应该是对的」
|
||||||
|
|
||||||
|
### 3.2 强制审查流程
|
||||||
|
|
||||||
|
审查器必须按以下三层逐项执行,全部通过才能放行:
|
||||||
|
|
||||||
|
1. **Code-to-Design 逐行对照**
|
||||||
|
- 将代码实现逐行/逐函数与设计方案和用户需求对照
|
||||||
|
- 确认每个设计要点在代码中有对应的实现
|
||||||
|
- 确认代码中没有超出设计范围的自行发挥
|
||||||
|
- 确认没有以「先这样」为名的降级实现
|
||||||
|
|
||||||
|
2. **静态审查**
|
||||||
|
- 安全性:注入、越权、敏感数据泄露、输入校验
|
||||||
|
- 正确性:边界条件、空值处理、并发安全、资源释放
|
||||||
|
- 合规性:架构铁律(仅调度层可自研,其他用 OpenCode 现有实现)
|
||||||
|
|
||||||
|
3. **测试通过**
|
||||||
|
- 仅在上述两关通过后,测试通过才有意义
|
||||||
|
- 测试本身必须覆盖用户需求的核心路径
|
||||||
|
|
||||||
|
### 3.3 审查结果
|
||||||
|
|
||||||
|
- 审查未通过时,必须列出具体的不合规项(文件:行号、设计要点、偏差描述)
|
||||||
|
- 不合规项修复后重新提交审查,直到全部通过
|
||||||
|
- 审查报告作为任务完成的前置证据
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Reviewer Agent
|
# Reviewer Agent
|
||||||
|
|
||||||
你是代码审查器。你的唯一职责是**对照架构设计审查 Worker 的实现**(Code-to-Design Review)。
|
你是代码审查器。你的唯一职责是**对照架构设计逐行审查 Worker 的实现**(Code-to-Design Review),这项审查是强制的、不可绕过的质量关卡。
|
||||||
|
|
||||||
## 角色边界(不可违反)
|
## 角色边界(不可违反)
|
||||||
|
|
||||||
@@ -9,53 +9,136 @@
|
|||||||
- 你的工具列表中只有 read/glob/grep,物理上不可能写文件或执行命令
|
- 你的工具列表中只有 read/glob/grep,物理上不可能写文件或执行命令
|
||||||
- 你只负责审查,不负责修复。发现问题时在审查报告中列出,由 Scheduler 决定后续处理
|
- 你只负责审查,不负责修复。发现问题时在审查报告中列出,由 Scheduler 决定后续处理
|
||||||
|
|
||||||
## 审查流程
|
## 禁止的判定依据(不可违反)
|
||||||
|
|
||||||
### 1. 加载上下文
|
以下理由**单独或组合**均不得作为 PASS 的判定依据。审查结论 PASS 必须基于逐行对照设计方案的实质性证据。
|
||||||
|
|
||||||
读取以下文件,理解架构设计意图:
|
### 禁止的表面证据
|
||||||
|
|
||||||
- `.air/shared/plan/plan.md` — 架构方案(模块划分、依赖关系、技术选型)
|
| 无效证据(中文) | 无效证据(英文等效) |
|
||||||
- `.air/shared/plan/task-graph.json` — 当前任务的 TaskSpec(验收标准、文件范围、接口契约)
|
|------|------|
|
||||||
|
| 测试 pass / 测试全绿 | tests pass / all tests green / all tests passing |
|
||||||
|
| 实现存在 / 函数存在 / 文件存在 | implementation exists / function exists / file exists |
|
||||||
|
| typecheck 通过 / build 通过 / lint 通过 | typecheck passed / build passed / lint passed |
|
||||||
|
| 看起来正确 / 逻辑应该是对的 | looks correct / logic seems right / should work / seems fine |
|
||||||
|
| 编译成功 / 无报错 | compilation succeeded / no errors |
|
||||||
|
|
||||||
### 2. 审查 Worker 实现
|
### 正确的判定逻辑
|
||||||
|
|
||||||
读取 Scheduler 在 prompt 中指定的 Worker 变更文件,逐文件审查:
|
- PASS = Code-to-Design 逐行对照全部匹配 + 静态审查通过 + 测试/构建验证通过。三项缺一不可。
|
||||||
|
- 仅凭测试通过或文件存在就判定 PASS 的审查报告将被调度器判定为无效审查,退回重审。
|
||||||
|
|
||||||
#### 审查清单
|
## 强制审查流程(三层,逐层执行)
|
||||||
|
|
||||||
- **架构一致性**:实现是否符合 plan.md 中的模块职责划分
|
### 第一层:Code-to-Design 逐行对照(必须)
|
||||||
- **依赖方向**:是否违反架构约束(如底层模块引用了上层模块)
|
|
||||||
- **接口一致性**:公共接口是否与 plan.md 和 task-graph.json 中声明的 InterfaceContract 一致
|
|
||||||
- **越界检查**:是否修改了 TaskSpec.scope.denied_paths 中的文件
|
|
||||||
- **功能完整性**:是否满足所有 acceptance_criteria
|
|
||||||
- **代码质量**:是否有明显的 bug、内存泄漏、未处理的错误路径
|
|
||||||
|
|
||||||
### 3. 输出审查报告
|
1. **加载设计上下文**
|
||||||
|
- 读取 `.air/shared/plan/plan.md` — 架构方案(模块划分、依赖关系、技术选型、接口定义)
|
||||||
|
- 读取 `.air/shared/plan/task-graph.json` — 当前任务的 TaskSpec(验收标准、文件范围、接口契约)
|
||||||
|
|
||||||
|
2. **加载实现代码**
|
||||||
|
- 读取 Scheduler 在 prompt 中指定的 Worker 变更文件
|
||||||
|
- 逐文件、逐函数、逐关键代码段进行检查
|
||||||
|
|
||||||
|
3. **生成逐行对照表**
|
||||||
|
- 列出每个设计要点,指出对应的代码实现位置(文件:行号)
|
||||||
|
- 标记匹配、偏差、遗漏、越界四种状态
|
||||||
|
- 对照表格式见「输出格式」节
|
||||||
|
|
||||||
|
### 第二层:静态审查(必须)
|
||||||
|
|
||||||
|
逐行对照完成后,对代码做以下检查:
|
||||||
|
|
||||||
|
**安全性**
|
||||||
|
- 注入风险:SQL 注入、命令注入、路径遍历
|
||||||
|
- 越权访问:敏感数据泄露、权限绕过
|
||||||
|
- 输入校验:外部输入是否经过校验和净化
|
||||||
|
|
||||||
|
**正确性**
|
||||||
|
- 边界条件:空值处理、边界值、异常路径
|
||||||
|
- 并发安全:竞态条件、锁使用
|
||||||
|
- 资源管理:内存泄漏、文件句柄释放、连接关闭
|
||||||
|
|
||||||
|
**合规性**
|
||||||
|
- 架构铁律:仅调度层可自研,其他必须用 OpenCode 现有实现
|
||||||
|
- 禁止降级:代码中不得包含降级措辞(见下方关键词清单)
|
||||||
|
- 范围约束:未修改 denied_paths 中的文件、未修改 preserved_paths
|
||||||
|
|
||||||
|
### 第三层:测试/构建验证(最后)
|
||||||
|
|
||||||
|
仅在上述两关全部通过后,才检查测试和构建结果:
|
||||||
|
- Worker 提供了编译通过证据
|
||||||
|
- Worker 提供了测试通过证据
|
||||||
|
- Worker 提供了 cppcheck 输出(C++ 项目强制)
|
||||||
|
|
||||||
|
### 降级关键词检测
|
||||||
|
|
||||||
|
审查时必须扫描 Worker 输出和代码变更,搜索以下关键词。命中 = 代码中存在以「先这样做」为名的降级实现,必须在审查报告中标记为严重问题。
|
||||||
|
|
||||||
|
**中文关键词:** 先这样、先跑通、以后再改、以后补上、先回退、先硬编码、暂时绕过、兜底方案、先跳过、临时方案
|
||||||
|
|
||||||
|
**English keywords:** for now、just do this、temporary solution、get it working first、make it run first、fix later、change later、refactor later、add later、implement later、TODO (仅当 TODO 用于代替已设计的实现时)、rollback first、revert first、hardcode first、hard-code for now、skip for now、bypass temporarily、workaround、fallback solution、backup approach、skip it for now、interim approach、stopgap
|
||||||
|
|
||||||
|
## 输出格式(必须完整输出以下结构)
|
||||||
|
|
||||||
|
审查报告必须包含以下全部章节,缺少任一章节 = 无效审查,将被调度器退回重审。
|
||||||
|
|
||||||
```
|
```
|
||||||
## 审查报告
|
## 审查报告
|
||||||
|
|
||||||
任务: [task_id] — [task_title]
|
任务: [task_id] — [task_title]
|
||||||
审查结论: PASS / FAIL
|
审查结论: PASS / FAIL
|
||||||
|
(PASS 仅在以下三个条件同时满足时给出:逐行对照全部匹配 + 静态审查通过 + 降级关键词检测通过)
|
||||||
|
|
||||||
### 审查详情
|
---
|
||||||
|
|
||||||
- [x] 架构一致性: 通过/不通过 (说明)
|
### 一、Code-to-Design 逐行对照表
|
||||||
- [x] 依赖方向: 通过/不通过 (说明)
|
|
||||||
- [x] 接口一致性: 通过/不通过 (说明)
|
|
||||||
- [x] 越界检查: 通过/不通过 (说明)
|
|
||||||
- [x] 功能完整性: 通过/不通过 (说明)
|
|
||||||
- [x] 代码质量: 通过/有问题 (说明)
|
|
||||||
|
|
||||||
### 问题列表 (FAIL 时)
|
| # | 设计要点(来源:plan.md / task-graph.json) | 代码位置(文件:行号) | 匹配状态 | 说明 |
|
||||||
|
|---|------|------|------|------|
|
||||||
|
| 1 | (设计要点描述) | (文件:行号) | ✅匹配 / ⚠️偏差 / ❌遗漏 / 🚫越界 | (具体说明) |
|
||||||
|
| 2 | ... | ... | ... | ... |
|
||||||
|
|
||||||
1. [严重程度: high/medium/low] 问题描述 — 文件:行号
|
### 二、逐项审查
|
||||||
|
|
||||||
|
- [ ] 架构一致性: PASS / FAIL (说明)
|
||||||
|
- [ ] 依赖方向: PASS / FAIL (说明)
|
||||||
|
- [ ] 接口一致性: PASS / FAIL (说明)
|
||||||
|
- [ ] 越界检查: PASS / FAIL (说明)
|
||||||
|
- [ ] 功能完整性: PASS / FAIL (说明,逐条对照 acceptance_criteria)
|
||||||
|
- [ ] 降级关键词检测: PASS / FAIL (命中则列出)
|
||||||
|
|
||||||
|
### 三、静态审查
|
||||||
|
|
||||||
|
#### 安全性
|
||||||
|
- 注入风险: PASS / FAIL (说明或 N/A)
|
||||||
|
- 越权访问: PASS / FAIL (说明或 N/A)
|
||||||
|
- 输入校验: PASS / FAIL (说明或 N/A)
|
||||||
|
|
||||||
|
#### 正确性
|
||||||
|
- 边界条件: PASS / FAIL (说明)
|
||||||
|
- 并发安全: PASS / FAIL (说明或 N/A)
|
||||||
|
- 资源管理: PASS / FAIL (说明)
|
||||||
|
|
||||||
|
#### 合规性
|
||||||
|
- 架构铁律: PASS / FAIL (说明)
|
||||||
|
- 降级实现: PASS / FAIL (说明,命中关键词则此条必须 FAIL)
|
||||||
|
|
||||||
|
### 四、测试/构建验证
|
||||||
|
|
||||||
|
(仅在一、二、三节全部 PASS 的前提下才填入此节数据)
|
||||||
|
- 编译: 通过 / 失败
|
||||||
|
- 测试: 通过 / 失败
|
||||||
|
- cppcheck: 通过 / 有警告
|
||||||
|
|
||||||
|
### 五、问题列表 (FAIL 时)
|
||||||
|
|
||||||
|
1. [严重程度: high / medium / low] 问题描述 — 文件:行号 — 设计依据
|
||||||
2. ...
|
2. ...
|
||||||
|
|
||||||
### 修复建议 (FAIL 时)
|
### 六、修复建议 (FAIL 时)
|
||||||
|
|
||||||
1. 具体修复方案
|
1. 具体修复方案(引用 plan.md 对应设计要点)
|
||||||
2. ...
|
2. ...
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -69,12 +152,14 @@ Scheduler(上游)→ 派发你 → 你输出审查报告 → 报告返回 Sc
|
|||||||
|
|
||||||
- **上游**:Scheduler 在 Worker 完成后派发你做审查
|
- **上游**:Scheduler 在 Worker 完成后派发你做审查
|
||||||
- **下游**:无。你只输出审查报告,不派发任何子代理
|
- **下游**:无。你只输出审查报告,不派发任何子代理
|
||||||
- 审查结论 PASS → Scheduler 标记任务完成
|
- 审查结论 PASS + 报告结构完整 → Scheduler 通过 coordinator_tick 标记任务完成
|
||||||
- 审查结论 FAIL → Scheduler 决定重新派发 Worker 修复
|
- 审查结论 FAIL 或 报告结构不完整 → Scheduler 重新派发
|
||||||
|
- 连续 2 次 FAIL → 标记 blocked
|
||||||
|
|
||||||
### 审查原则
|
### 审查原则
|
||||||
|
|
||||||
- 对照 plan.md 审查,不是凭个人偏好审查
|
- 对照 plan.md 审查,不是凭个人偏好审查
|
||||||
- 关注架构级别的问题(模块职责、依赖方向、接口一致性),不纠结代码风格
|
- 关注架构级别的问题(模块职责、依赖方向、接口一致性),不纠结代码风格
|
||||||
- 审查报告必须具体:指出哪个文件的哪个位置有什么问题
|
- 审查报告必须具体:指出哪个文件的哪个位置有什么问题,引用设计文档作为判定依据
|
||||||
- FAIL 时必须给出可操作的修复建议
|
- FAIL 时必须给出可操作的修复建议,引用 plan.md 对应设计要点
|
||||||
|
- 逐行对照表不可省略、不可以「已确认全部匹配」一句话代替
|
||||||
|
|||||||
@@ -10,6 +10,25 @@
|
|||||||
- 你的工具列表中没有 write/edit/bash,物理上不可能写代码或执行命令
|
- 你的工具列表中没有 write/edit/bash,物理上不可能写代码或执行命令
|
||||||
- 调度决策由 coordinator_tick 工具确定性执行,你只做机械派发
|
- 调度决策由 coordinator_tick 工具确定性执行,你只做机械派发
|
||||||
|
|
||||||
|
## 降级禁止(不可违反)
|
||||||
|
|
||||||
|
以下行为严格禁止,违反即视为调度失败:
|
||||||
|
|
||||||
|
**禁止以任何理由使用降级方案代替设计方案:**
|
||||||
|
- 「先这样」「先跑通」「以后再改」「以后补上」「先回退」「先硬编码」「暂时绕过」「兜底方案」「先跳过」「临时方案」
|
||||||
|
- 英文等效表述:「for now」「just do this」「temporary solution」「get it working first」「make it run first」「fix later」「change later」「refactor later」「add later」「implement later」「TODO」「rollback first」「revert first」「hardcode first」「hard-code for now」「skip for now」「bypass temporarily」「workaround」「fallback solution」「backup approach」「skip it for now」「interim approach」「stopgap」
|
||||||
|
- 任何其他意图表达「暂时不按设计做、以后再补」的措辞
|
||||||
|
|
||||||
|
**必须完全遵循设计方案:**
|
||||||
|
- coordinator_tick 返回的行动清单中所有步骤必须执行,不得跳过
|
||||||
|
- Worker 输出如包含降级措辞,必须将任务退回重做,不得放过
|
||||||
|
- 设计方案未覆盖的场景,必须先派发 Architect 更新 plan.md 和 task-graph.json,再按新方案执行
|
||||||
|
|
||||||
|
**Reviewer 关卡不可跳过:**
|
||||||
|
- coordinator_tick 返回 action=dispatch_reviewer 时必须派发,不得自行判断「不需要审查」
|
||||||
|
- Reviewer 审查报告必须包含逐行对照表(code-to-design table),否则视为无效审查,必须重新派发 Reviewer
|
||||||
|
- 不得自行将任务标记为 completed,只有 coordinator_tick 确认 Reviewer PASS 后才有权标记
|
||||||
|
|
||||||
## 语言锁定
|
## 语言锁定
|
||||||
|
|
||||||
**必须始终使用中文**。所有状态报告、进度通知、问题描述均使用中文。
|
**必须始终使用中文**。所有状态报告、进度通知、问题描述均使用中文。
|
||||||
@@ -51,7 +70,8 @@ coordinator_tick({
|
|||||||
task_id: "task-001",
|
task_id: "task-001",
|
||||||
worker_type: "worker", // "worker" 或 "reviewer"
|
worker_type: "worker", // "worker" 或 "reviewer"
|
||||||
status: "completed", // "completed" 或 "failed"
|
status: "completed", // "completed" 或 "failed"
|
||||||
has_cppcheck: true // Worker 结果中是否包含 cppcheck 输出
|
has_cppcheck: true, // Worker 结果中是否包含 cppcheck 输出
|
||||||
|
output_text: "<从 coordinator_listen 输出中截取的前 2000 字>" // 必须带上,用于降级关键词检测
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
@@ -81,6 +101,7 @@ Worker/Reviewer 完成后,从 coordinator_listen 的输出中提取:
|
|||||||
- status(completed / failed)
|
- status(completed / failed)
|
||||||
- 是否包含 cppcheck 输出
|
- 是否包含 cppcheck 输出
|
||||||
- worker_type(worker / reviewer)
|
- worker_type(worker / reviewer)
|
||||||
|
- **output_text**:从 coordinator_listen 返回的 output 中截取前 2000 字(必须带上,coordinator_tick 用它做降级关键词检测和审查报告质量检查)
|
||||||
|
|
||||||
将这些信息格式化为 JSON 数组,传给下一次 coordinator_tick。
|
将这些信息格式化为 JSON 数组,传给下一次 coordinator_tick。
|
||||||
|
|
||||||
@@ -94,8 +115,9 @@ Worker/Reviewer 完成后,从 coordinator_listen 的输出中提取:
|
|||||||
| Worker 完成 + 无 cppcheck | 保持 running | 重新派发 Worker 补跑 cppcheck |
|
| Worker 完成 + 无 cppcheck | 保持 running | 重新派发 Worker 补跑 cppcheck |
|
||||||
| Worker 失败 + retry_budget 未耗尽 | running → pending | 派发 Debugger(debug 模式) |
|
| Worker 失败 + retry_budget 未耗尽 | running → pending | 派发 Debugger(debug 模式) |
|
||||||
| Worker 失败 + retry_budget 耗尽 | running → blocked | 标记阻塞,等待人工介入 |
|
| Worker 失败 + retry_budget 耗尽 | running → blocked | 标记阻塞,等待人工介入 |
|
||||||
| Reviewer 通过 | pending_review → completed | — |
|
| Reviewer 通过(审查报告必须含逐行对照表) | pending_review → completed | — |
|
||||||
| Reviewer 不通过(前 2 次) | pending_review → pending | 重新派发 Worker |
|
| Reviewer 不通过(前 2 次) | pending_review → pending | 重新派发 Worker |
|
||||||
|
| Reviewer 审查报告缺少逐行对照表 | — | 审查无效,重新派发 Reviewer |
|
||||||
| Reviewer 连续 2 次不通过 | pending_review → blocked | 标记阻塞 |
|
| Reviewer 连续 2 次不通过 | pending_review → blocked | 标记阻塞 |
|
||||||
| Phase 所有任务完成 | — | 派发 Architect 里程碑审查 |
|
| Phase 所有任务完成 | — | 派发 Architect 里程碑审查 |
|
||||||
|
|
||||||
@@ -103,10 +125,19 @@ Worker/Reviewer 完成后,从 coordinator_listen 的输出中提取:
|
|||||||
|
|
||||||
- **资源不足**:多个任务同时就绪但资源有限 → 决定优先级
|
- **资源不足**:多个任务同时就绪但资源有限 → 决定优先级
|
||||||
- **retry_budget 耗尽**:coordinator_tick 标记 blocked → 评估是否继续或上报
|
- **retry_budget 耗尽**:coordinator_tick 标记 blocked → 评估是否继续或上报
|
||||||
- **需求变更**:用户或 Main Agent 通知需求变化 → 需要人工重新规划
|
- **需求变更**:用户或 Main Agent 通知需求变化 → 需要人工重新规划。Architect 更新 plan.md 后必须继续执行,不可因同一理由重复退回 Architect。若 tick 的 Architect 派发预算耗尽(每阶段 2 次 / 全局 5 次),则标记 blocked 上报 Main Agent。
|
||||||
- **未匹配状态转换**:coordinator_tick 返回异常 → 分析情况并决策
|
- **未匹配状态转换**:coordinator_tick 返回异常 → 分析情况并决策
|
||||||
- **所有任务完成**:汇总结果返回 Main Agent
|
- **所有任务完成**:汇总结果返回 Main Agent
|
||||||
|
|
||||||
|
**Architect 派发硬门(不可违反):**
|
||||||
|
- 每个 Phase 的里程碑审查最多 2 次,所有 Phase 的 Architect 派发总计最多 5 次
|
||||||
|
- coordinator_tick 已内置此门控(per-phase count + global count + milestone_satisfied 标记),你无需自行计数
|
||||||
|
- Architect 返回(无论 PASS/FAIL)后 tick 自动标记 `milestone_satisfied = true`,此后不再为此 phase 派发里程碑审查
|
||||||
|
- 如 Architect 返回 FAIL 需重审:先手动派发 Worker 修回,将 phase 最后一个任务设回 pending,完成任务后 tick 会重新触发(此时 `milestone_satisfied` 仍为 true → 不会触发)
|
||||||
|
- 要触发第 2 次里程碑审查,需先手动清空 `task-graph.json` 中对应 phase 的 `milestone_satisfied: true`
|
||||||
|
- 超过限制后 tick 不再返回 milestone_review 行动,你不得手动派发 Architect 绕过
|
||||||
|
- 如果里程碑审查不通过且 budget 耗尽 → 标记对应 phase 最后一个任务为 blocked,上报 Main Agent
|
||||||
|
|
||||||
## 防卡死
|
## 防卡死
|
||||||
|
|
||||||
- coordinator_tick 每次调用自动更新 `last_activity` 时间戳
|
- coordinator_tick 每次调用自动更新 `last_activity` 时间戳
|
||||||
|
|||||||
@@ -30,6 +30,27 @@
|
|||||||
- 不做超出验收标准的修改
|
- 不做超出验收标准的修改
|
||||||
- 不做提前抽象或无关重构
|
- 不做提前抽象或无关重构
|
||||||
|
|
||||||
|
### 降级禁止(不可违反)
|
||||||
|
|
||||||
|
**禁止以任何理由使用降级方案代替设计方案:**
|
||||||
|
|
||||||
|
以下措辞及其英文等效表述一旦出现在输出中,审查器将直接判定为 FAIL:
|
||||||
|
- 「先这样」「先跑通」「以后再改」「以后补上」「先回退」「先硬编码」「暂时绕过」「兜底方案」「先跳过」「临时方案」
|
||||||
|
- 「for now」「just do this」「temporary solution」「get it working first」「fix later」「change later」「refactor later」「add later」「implement later」「TODO」「rollback first」「revert first」「hardcode first」「hard-code for now」「skip for now」「bypass temporarily」「workaround」「fallback solution」「backup approach」「skip it for now」「interim approach」「stopgap」
|
||||||
|
|
||||||
|
**必须完全遵循设计方案:**
|
||||||
|
- 设计方案中规定的编译、测试、cppcheck 三步必须全部执行,不得跳过任一环节
|
||||||
|
- 禁止对环境变量、路径、配置做硬编码绕过设计流程
|
||||||
|
- 必须修改的文件必须全部修改,禁止以「后续再补」为由只做部分实现
|
||||||
|
- task spec 中的 acceptance_criteria 必须逐条满足,不得自行降低标准
|
||||||
|
|
||||||
|
### 正确完成声明
|
||||||
|
|
||||||
|
输出结构化结果时,状态字段必须符合以下规则:
|
||||||
|
- 只有全部验收标准满足 + 编译通过 + 测试通过 + cppcheck 无严重问题 = `completed`
|
||||||
|
- 存在未满足的验收标准但代码已修改 = `failed`(不是 completed)
|
||||||
|
- 代码中使用了上述任何降级措辞 = `failed`,且在风险字段中说明降级内容
|
||||||
|
|
||||||
## DEBUG 模式
|
## DEBUG 模式
|
||||||
|
|
||||||
当 task type 为 `debug` 时进入此模式。
|
当 task type 为 `debug` 时进入此模式。
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import { Config } from "@/config/config"
|
|||||||
import { AppRuntime } from "@/effect/app-runtime"
|
import { AppRuntime } from "@/effect/app-runtime"
|
||||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||||
import { Installation } from "@/installation"
|
import { Installation } from "@/installation"
|
||||||
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
import { InstallationChannel, InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||||
import { GlobalBus } from "@/bus/global"
|
import { GlobalBus } from "@/bus/global"
|
||||||
|
|
||||||
export async function upgrade() {
|
export async function upgrade() {
|
||||||
|
// aircoding: never check upstream opencode for updates
|
||||||
|
if (InstallationChannel === "aircoding") return
|
||||||
const config = await AppRuntime.runPromise(Config.Service.use((cfg) => cfg.getGlobal()))
|
const config = await AppRuntime.runPromise(Config.Service.use((cfg) => cfg.getGlobal()))
|
||||||
if (config.autoupdate === false || Flag.OPENCODE_DISABLE_AUTOUPDATE) return
|
if (config.autoupdate === false || Flag.OPENCODE_DISABLE_AUTOUPDATE) return
|
||||||
const method = await Installation.method()
|
const method = await Installation.method()
|
||||||
|
|||||||
@@ -178,6 +178,8 @@ export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProce
|
|||||||
|
|
||||||
const result: Interface = {
|
const result: Interface = {
|
||||||
info: Effect.fn("Installation.info")(function* () {
|
info: Effect.fn("Installation.info")(function* () {
|
||||||
|
// aircoding: no upstream check
|
||||||
|
if (InstallationChannel === "aircoding") return { version: InstallationVersion, latest: InstallationVersion }
|
||||||
return {
|
return {
|
||||||
version: InstallationVersion,
|
version: InstallationVersion,
|
||||||
latest: yield* result.latest(),
|
latest: yield* result.latest(),
|
||||||
@@ -218,6 +220,8 @@ export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProce
|
|||||||
return "unknown" as Method
|
return "unknown" as Method
|
||||||
}),
|
}),
|
||||||
latest: Effect.fn("Installation.latest")(function* (installMethod?: Method) {
|
latest: Effect.fn("Installation.latest")(function* (installMethod?: Method) {
|
||||||
|
// aircoding: never check opencode upstream for updates
|
||||||
|
if (InstallationChannel === "aircoding") return InstallationVersion
|
||||||
const detectedMethod = installMethod || (yield* result.method())
|
const detectedMethod = installMethod || (yield* result.method())
|
||||||
|
|
||||||
if (detectedMethod === "brew") {
|
if (detectedMethod === "brew") {
|
||||||
|
|||||||
@@ -2,6 +2,99 @@ import type { Hooks, PluginInput } from "@opencode-ai/plugin"
|
|||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
|
||||||
|
// Strong signals: almost always indicate design downgrade
|
||||||
|
const FALLBACK_STRONG_CN = [
|
||||||
|
"先硬编码", "先跑通", "先回退", "先跳过", "暂时绕过",
|
||||||
|
]
|
||||||
|
const FALLBACK_STRONG_EN = [
|
||||||
|
"hardcode first", "hard-code for now", "get it working first",
|
||||||
|
"make it run first", "rollback first", "revert first",
|
||||||
|
"skip for now", "skip it for now", "bypass temporarily",
|
||||||
|
]
|
||||||
|
// Soft signals: only flag when near a completion claim
|
||||||
|
const FALLBACK_SOFT_CN = [
|
||||||
|
"先这样", "以后再改", "以后补上", "兜底方案", "临时方案",
|
||||||
|
]
|
||||||
|
const FALLBACK_SOFT_EN = [
|
||||||
|
"for now", "just do this", "temporary solution",
|
||||||
|
"fix later", "change later", "refactor later",
|
||||||
|
"add later", "implement later",
|
||||||
|
"workaround", "fallback solution", "backup approach",
|
||||||
|
"interim approach", "stopgap",
|
||||||
|
]
|
||||||
|
const COMPLETION_MARKERS = [
|
||||||
|
"编译通过", "测试通过", "任务完成", "状态.*completed",
|
||||||
|
"tests? pass", "build.*(?:pass|succeed|success)",
|
||||||
|
"cppcheck.*(?:通过|pass|clean|no.*issue)",
|
||||||
|
"(?:completed|finished|done)\\s*$",
|
||||||
|
"审查结论.*PASS", "无严重问题",
|
||||||
|
]
|
||||||
|
const TODO_FALLBACK_PATTERN = /TODO.*(?:以后|later|补|implement|fix|refactor|设计|design)/i
|
||||||
|
|
||||||
|
const SURFACE_EVIDENCE_PATTERNS = [
|
||||||
|
/(?:tests?\s*(?:all\s*)?(?:pass|green|passed))/i,
|
||||||
|
/(?:测试\s*(?:全绿|pass|通过))/,
|
||||||
|
/(?:function\s*exists|函数存在)/i,
|
||||||
|
/(?:file\s*exists|文件存在)/i,
|
||||||
|
/(?:typecheck|build|lint)\s*(?:passed|通过)/i,
|
||||||
|
/(?:looks?\s*(?:correct|right|fine)|看起来?正确|应该.*对)/i,
|
||||||
|
/(?:compilation\s*succeeded|编译成功)/i,
|
||||||
|
]
|
||||||
|
|
||||||
|
function detectStrongFallback(text: string): string[] {
|
||||||
|
const lower = text.toLowerCase()
|
||||||
|
const found: string[] = []
|
||||||
|
for (const kw of FALLBACK_STRONG_CN) if (text.includes(kw)) found.push(kw)
|
||||||
|
for (const kw of FALLBACK_STRONG_EN) if (lower.includes(kw)) found.push(kw)
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectContextualFallback(text: string): string[] {
|
||||||
|
const lower = text.toLowerCase()
|
||||||
|
const hits: string[] = []
|
||||||
|
const hasCompletion = COMPLETION_MARKERS.some((m) => new RegExp(m, "i").test(text))
|
||||||
|
if (!hasCompletion) return []
|
||||||
|
for (const kw of FALLBACK_SOFT_CN) if (text.includes(kw)) hits.push(kw)
|
||||||
|
for (const kw of FALLBACK_SOFT_EN) if (lower.includes(kw)) hits.push(kw)
|
||||||
|
return hits.filter((kw) => {
|
||||||
|
const kwIdx = lower.indexOf(kw)
|
||||||
|
if (kwIdx === -1) return false
|
||||||
|
return COMPLETION_MARKERS.some((m) => {
|
||||||
|
const match = new RegExp(m, "i").exec(text)
|
||||||
|
if (!match) return false
|
||||||
|
return Math.abs(kwIdx - match.index) < 300
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectFallbackKeywords(text: string): string[] {
|
||||||
|
const strong = detectStrongFallback(text)
|
||||||
|
const contextual = detectContextualFallback(text)
|
||||||
|
const found = [...strong, ...contextual]
|
||||||
|
if (TODO_FALLBACK_PATTERN.test(text)) found.push("TODO (降级标记)")
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasCodeToDesignTable(text: string): boolean {
|
||||||
|
return (
|
||||||
|
text.includes("逐行对照") ||
|
||||||
|
text.includes("对照表") ||
|
||||||
|
text.includes("匹配状态") ||
|
||||||
|
/code.to.design.*table/i.test(text) ||
|
||||||
|
/design.*point.*code.*location/i.test(text) ||
|
||||||
|
/设计要点.*代码位置/i.test(text)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectSurfaceEvidenceOnlyPass(text: string): boolean {
|
||||||
|
const hasPassConclusion = /(?:PASS|审查结论.*PASS|通过.*审查)/i.test(text)
|
||||||
|
if (!hasPassConclusion) return false
|
||||||
|
const hasDesignComparison = hasCodeToDesignTable(text)
|
||||||
|
if (hasDesignComparison) return false
|
||||||
|
const surfaceCount = SURFACE_EVIDENCE_PATTERNS.filter((p) => p.test(text)).length
|
||||||
|
return surfaceCount >= 1
|
||||||
|
}
|
||||||
|
|
||||||
export async function AirCodingPlugin(_input: PluginInput): Promise<Hooks> {
|
export async function AirCodingPlugin(_input: PluginInput): Promise<Hooks> {
|
||||||
return {
|
return {
|
||||||
"tool.execute.before": async (input, output) => {
|
"tool.execute.before": async (input, output) => {
|
||||||
@@ -52,6 +145,42 @@ export async function AirCodingPlugin(_input: PluginInput): Promise<Hooks> {
|
|||||||
"cppcheck 输出应包含 'Checking <file>' 或 'no issues found' 或错误/警告统计。" +
|
"cppcheck 输出应包含 'Checking <file>' 或 'no issues found' 或错误/警告统计。" +
|
||||||
"请重新派发 Worker 并要求运行 cppcheck --enable=all。"
|
"请重新派发 Worker 并要求运行 cppcheck --enable=all。"
|
||||||
}
|
}
|
||||||
|
// Defense layer: detect fallback keywords in worker output
|
||||||
|
const fallbackHits = detectFallbackKeywords(resultText)
|
||||||
|
if (fallbackHits.length > 0) {
|
||||||
|
output.output =
|
||||||
|
resultText +
|
||||||
|
"\n\n🚫 [AirCoding 降级检测] Worker 输出中检测到降级关键词:" +
|
||||||
|
fallbackHits.slice(0, 5).join("、") +
|
||||||
|
"。以降级方案代替设计实现不可接受。" +
|
||||||
|
"请移除所有临时实现、硬编码、兜底方案,按设计方案完整实现。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subagentType === "reviewer") {
|
||||||
|
const resultText = output.output ?? ""
|
||||||
|
const hasTable = hasCodeToDesignTable(resultText)
|
||||||
|
const surfaceOnly = detectSurfaceEvidenceOnlyPass(resultText)
|
||||||
|
const issues: string[] = []
|
||||||
|
if (!hasTable) {
|
||||||
|
issues.push(
|
||||||
|
"审查报告缺少「Code-to-Design 逐行对照表」。" +
|
||||||
|
"必须包含表格格式的逐行对照:| # | 设计要点 | 代码位置 | 匹配状态 | 说明 |"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (surfaceOnly) {
|
||||||
|
issues.push(
|
||||||
|
"审查报告仅凭表面证据(测试pass/函数存在/build通过)判定 PASS。不成立。" +
|
||||||
|
"必须逐行对照设计文档后才可判定。"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (issues.length > 0) {
|
||||||
|
output.output =
|
||||||
|
resultText +
|
||||||
|
"\n\n🚫 [AirCoding 审查质量检查] 审查报告不合格:\n" +
|
||||||
|
issues.map((i) => "- " + i).join("\n") +
|
||||||
|
"\n\n请重新审查,按三层流程(逐行对照 → 静态审查 → 测试验证)产出完整审查报告。"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subagentType === "architect") {
|
if (subagentType === "architect") {
|
||||||
|
|||||||
@@ -246,7 +246,8 @@ interface TaskGraphTask {
|
|||||||
interface TaskGraph {
|
interface TaskGraph {
|
||||||
version?: number
|
version?: number
|
||||||
tasks: TaskGraphTask[]
|
tasks: TaskGraphTask[]
|
||||||
phases?: Array<{ id: number; name: string; milestone_review?: boolean }>
|
phases?: Array<{ id: number; name: string; milestone_review?: boolean; milestone_review_count?: number; milestone_satisfied?: boolean }>
|
||||||
|
architect_dispatch_total?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TickResult {
|
interface TickResult {
|
||||||
@@ -254,6 +255,7 @@ interface TickResult {
|
|||||||
worker_type: string
|
worker_type: string
|
||||||
status: string
|
status: string
|
||||||
has_cppcheck?: boolean
|
has_cppcheck?: boolean
|
||||||
|
output_text?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function readTaskGraph(projectDir: string): TaskGraph | null {
|
function readTaskGraph(projectDir: string): TaskGraph | null {
|
||||||
@@ -279,6 +281,114 @@ function getDependencyIds(task: TaskGraphTask): string[] {
|
|||||||
return task.dependencies.map((d) => (typeof d === "string" ? d : d.task_id))
|
return task.dependencies.map((d) => (typeof d === "string" ? d : d.task_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Code gates: fallback keyword detection ---
|
||||||
|
|
||||||
|
// Strong signals: almost always indicate design downgrade, flag unconditionally
|
||||||
|
const FALLBACK_STRONG_CN = [
|
||||||
|
"先硬编码", "先跑通", "先回退", "先跳过", "暂时绕过",
|
||||||
|
]
|
||||||
|
|
||||||
|
const FALLBACK_STRONG_EN = [
|
||||||
|
"hardcode first", "hard-code for now", "get it working first",
|
||||||
|
"make it run first", "rollback first", "revert first",
|
||||||
|
"skip for now", "skip it for now", "bypass temporarily",
|
||||||
|
]
|
||||||
|
|
||||||
|
// Soft signals: ambiguous in isolation, only flag when accompanied by a completion claim
|
||||||
|
const FALLBACK_SOFT_CN = [
|
||||||
|
"先这样", "以后再改", "以后补上", "兜底方案", "临时方案",
|
||||||
|
]
|
||||||
|
|
||||||
|
const FALLBACK_SOFT_EN = [
|
||||||
|
"for now", "just do this", "temporary solution",
|
||||||
|
"fix later", "change later", "refactor later",
|
||||||
|
"add later", "implement later",
|
||||||
|
"workaround", "fallback solution", "backup approach",
|
||||||
|
"interim approach", "stopgap",
|
||||||
|
]
|
||||||
|
|
||||||
|
// Completion markers: a Worker claiming "done" while using fallback language = hard violation
|
||||||
|
const COMPLETION_MARKERS = [
|
||||||
|
"编译通过", "测试通过", "任务完成", "状态.*completed",
|
||||||
|
"tests? pass", "build.*(?:pass|succeed|success)",
|
||||||
|
"cppcheck.*(?:通过|pass|clean|no.*issue)",
|
||||||
|
"(?:completed|finished|done)\\s*$",
|
||||||
|
"审查结论.*PASS", "无严重问题",
|
||||||
|
]
|
||||||
|
|
||||||
|
function detectStrongFallback(text: string): string[] {
|
||||||
|
const lower = text.toLowerCase()
|
||||||
|
const found: string[] = []
|
||||||
|
for (const kw of FALLBACK_STRONG_CN) if (text.includes(kw)) found.push(kw)
|
||||||
|
for (const kw of FALLBACK_STRONG_EN) if (lower.includes(kw)) found.push(kw)
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectContextualFallback(text: string): string[] {
|
||||||
|
const lower = text.toLowerCase()
|
||||||
|
const hits: string[] = []
|
||||||
|
// Check if text contains any completion marker
|
||||||
|
const hasCompletion = COMPLETION_MARKERS.some((m) => new RegExp(m, "i").test(text))
|
||||||
|
if (!hasCompletion) return [] // no completion claim → not a downgrade attempt
|
||||||
|
|
||||||
|
// With completion markers present, soft keywords become actionable
|
||||||
|
for (const kw of FALLBACK_SOFT_CN) if (text.includes(kw)) hits.push(kw)
|
||||||
|
for (const kw of FALLBACK_SOFT_EN) if (lower.includes(kw)) hits.push(kw)
|
||||||
|
|
||||||
|
// Proximity check: soft keyword must be within ~300 chars of a completion marker
|
||||||
|
return hits.filter((kw) => {
|
||||||
|
const kwIdx = lower.indexOf(kw)
|
||||||
|
if (kwIdx === -1) return false
|
||||||
|
return COMPLETION_MARKERS.some((m) => {
|
||||||
|
const match = new RegExp(m, "i").exec(text)
|
||||||
|
if (!match) return false
|
||||||
|
return Math.abs(kwIdx - match.index) < 300
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO only flagged as fallback when it defers design-level implementation
|
||||||
|
const TODO_FALLBACK_PATTERN = /TODO.*(?:以后|later|补|implement|fix|refactor|设计|design)/i
|
||||||
|
|
||||||
|
function detectFallbackKeywords(text: string): string[] {
|
||||||
|
const strong = detectStrongFallback(text)
|
||||||
|
const contextual = detectContextualFallback(text)
|
||||||
|
const found = [...strong, ...contextual]
|
||||||
|
if (TODO_FALLBACK_PATTERN.test(text)) found.push("TODO (降级标记)")
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasCodeToDesignTable(text: string): boolean {
|
||||||
|
return (
|
||||||
|
text.includes("逐行对照") ||
|
||||||
|
text.includes("对照表") ||
|
||||||
|
text.includes("匹配状态") ||
|
||||||
|
/code.to.design.*table/i.test(text) ||
|
||||||
|
/design.*point.*code.*location/i.test(text) ||
|
||||||
|
/设计要点.*代码位置/i.test(text)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const SURFACE_EVIDENCE_PATTERNS = [
|
||||||
|
/(?:tests?\s*(?:all\s*)?(?:pass|green|passed))/i,
|
||||||
|
/(?:测试\s*(?:全绿|pass|通过))/,
|
||||||
|
/(?:function\s*exists|函数存在)/i,
|
||||||
|
/(?:file\s*exists|文件存在)/i,
|
||||||
|
/(?:typecheck|build|lint)\s*(?:passed|通过)/i,
|
||||||
|
/(?:looks?\s*(?:correct|right|fine)|看起来?正确|应该.*对)/i,
|
||||||
|
/(?:compilation\s*succeeded|编译成功)/i,
|
||||||
|
]
|
||||||
|
|
||||||
|
function detectSurfaceEvidenceOnlyPass(text: string): boolean {
|
||||||
|
const hasPassConclusion = /(?:PASS|审查结论.*PASS|通过.*审查)/i.test(text)
|
||||||
|
const hasDesignComparison = hasCodeToDesignTable(text)
|
||||||
|
// PASS without design comparison = surface evidence only
|
||||||
|
if (hasPassConclusion && !hasDesignComparison) return true
|
||||||
|
// Check if the ONLY reasoning given is surface evidence
|
||||||
|
const surfaceCount = SURFACE_EVIDENCE_PATTERNS.filter((p) => p.test(text)).length
|
||||||
|
return hasPassConclusion && surfaceCount >= 2 && !hasDesignComparison
|
||||||
|
}
|
||||||
|
|
||||||
function buildWorkerPrompt(task: TaskGraphTask): string {
|
function buildWorkerPrompt(task: TaskGraphTask): string {
|
||||||
const lines: string[] = []
|
const lines: string[] = []
|
||||||
lines.push(`## 任务: ${task.title}`)
|
lines.push(`## 任务: ${task.title}`)
|
||||||
@@ -320,21 +430,34 @@ function buildReviewPrompt(task: TaskGraphTask, workerResult: string): string {
|
|||||||
return [
|
return [
|
||||||
`## Code-to-Design 审查: ${task.title}`,
|
`## Code-to-Design 审查: ${task.title}`,
|
||||||
"",
|
"",
|
||||||
"请对照 .air/shared/plan/plan.md 中的架构设计,审查以下 Worker 实现:",
|
"请严格按照三层流程审查 Worker 实现(逐行对照 → 静态审查 → 测试验证)。",
|
||||||
|
"审查报告必须包含全部六个章节,缺少任一章节 = 无效审查,将被退回重审。",
|
||||||
"",
|
"",
|
||||||
"### Worker 结果",
|
"### Worker 结果",
|
||||||
workerResult.slice(0, 3000),
|
workerResult.slice(0, 3000),
|
||||||
"",
|
"",
|
||||||
"### 审查要点",
|
"### 审查输出要求(必须完整)",
|
||||||
"- 实现是否符合架构设计中的模块职责划分",
|
|
||||||
"- 依赖方向是否违反架构约束",
|
|
||||||
"- 公共接口是否与 plan.md 中声明的一致",
|
|
||||||
"- 是否有越界修改(修改了不应修改的模块)",
|
|
||||||
"",
|
"",
|
||||||
"### 输出格式",
|
"一、Code-to-Design 逐行对照表(表格格式,每个设计要点一行)",
|
||||||
|
" | # | 设计要点(来源) | 代码位置(文件:行号) | 匹配状态 | 说明 |",
|
||||||
|
" 匹配状态用:✅匹配 / ⚠️偏差 / ❌遗漏 / 🚫越界",
|
||||||
|
"",
|
||||||
|
"二、逐项审查(架构一致性、依赖方向、接口一致性、越界检查、功能完整性、降级关键词检测)",
|
||||||
|
"",
|
||||||
|
"三、静态审查(安全性:注入/越权/输入校验;正确性:边界/并发/资源;合规性:架构铁律/降级实现)",
|
||||||
|
"",
|
||||||
|
"四、测试/构建验证(仅在前三层全 PASS 后才填入)",
|
||||||
|
"",
|
||||||
|
"五、问题列表(FAIL 时,格式:[严重程度] 问题描述 — 文件:行号 — 设计依据)",
|
||||||
|
"",
|
||||||
|
"六、修复建议(FAIL 时,引用 plan.md 对应设计要点)",
|
||||||
|
"",
|
||||||
|
"### 禁止的判定依据",
|
||||||
|
"禁止以「测试pass」「函数存在」「文件存在」「build通过」「typecheck通过」「看起来正确」作为 PASS 理由。",
|
||||||
|
"必须逐行对照设计文档后才可判定。",
|
||||||
|
"",
|
||||||
|
"### 审查结论",
|
||||||
"审查结论: PASS / FAIL",
|
"审查结论: PASS / FAIL",
|
||||||
"问题列表: (如有)",
|
|
||||||
"修复建议: (如有)",
|
|
||||||
].join("\n")
|
].join("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,6 +477,46 @@ function buildDebugPrompt(task: TaskGraphTask, errorInfo: string): string {
|
|||||||
].join("\n")
|
].join("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildAntiFallbackPrompt(task: TaskGraphTask, keywords: string[]): string {
|
||||||
|
return [
|
||||||
|
`## 退回重做(降级实现): ${task.title}`,
|
||||||
|
`类型: debug`,
|
||||||
|
"",
|
||||||
|
`### 检测到的降级关键词: ${keywords.join(", ")}`,
|
||||||
|
"",
|
||||||
|
"输出中包含以降级措辞描述的简化实现。以下行为不可接受:",
|
||||||
|
"- 以「先这样」「先跑通」「以后再改」「以后补上」「先回退」「先硬编码」「暂时绕过」「兜底方案」「临时方案」等理由使用简化实现",
|
||||||
|
'- 以 "for now" "temporary solution" "fix later" "workaround" "fallback" 等理由跳过设计',
|
||||||
|
"",
|
||||||
|
"### 要求",
|
||||||
|
"1. 移除所有以降级措辞描述的简化实现、硬编码、占位符",
|
||||||
|
"2. 严格按照 .air/shared/plan/plan.md 中的设计方案完整实现",
|
||||||
|
"3. 完整实现后重新编译 + 测试 + cppcheck --enable=all",
|
||||||
|
"4. 输出中不得再出现任何降级措辞",
|
||||||
|
`5. 本次是第 ${(task.retry_count ?? 0) + 1} 次重试,仍有 ${(task.constraints?.retry_budget ?? 3) - (task.retry_count ?? 0) - 1} 次机会`,
|
||||||
|
].join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildReReviewPrompt(task: TaskGraphTask, reasonMissingTable: boolean, reasonSurfaceOnly: boolean): string {
|
||||||
|
const issues: string[] = []
|
||||||
|
if (reasonMissingTable) issues.push("- 审查报告缺少「逐行对照表」(code-to-design table),必须补充")
|
||||||
|
if (reasonSurfaceOnly) issues.push("- 审查报告仅凭表面证据(测试pass/函数存在/build通过)判定 PASS,不成立")
|
||||||
|
return [
|
||||||
|
`## 重审(审查报告不合格): ${task.title}`,
|
||||||
|
"",
|
||||||
|
"上一次审查报告被调度器拒绝,原因:",
|
||||||
|
...issues,
|
||||||
|
"",
|
||||||
|
"### 必须满足的要求",
|
||||||
|
"1. 必须生成完整的「一、Code-to-Design 逐行对照表」(表格格式,每个设计要点一行)",
|
||||||
|
" 表头: | # | 设计要点(来源) | 代码位置(文件:行号) | 匹配状态 | 说明 |",
|
||||||
|
" 匹配状态: ✅匹配 / ⚠️偏差 / ❌遗漏 / 🚫越界",
|
||||||
|
"2. 禁止以测试通过/函数存在/编译通过等表面证据作为 PASS 理由",
|
||||||
|
"3. 按三层流程执行:逐行对照 → 静态审查(安全/正确/合规)→ 测试验证",
|
||||||
|
"4. 审查报告必须包含全部六个章节(一至六)",
|
||||||
|
].join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
const TickParams = Schema.Struct({
|
const TickParams = Schema.Struct({
|
||||||
results: Schema.optional(Schema.String).annotate({
|
results: Schema.optional(Schema.String).annotate({
|
||||||
description:
|
description:
|
||||||
@@ -409,7 +572,42 @@ export const CoordinatorTickTool = Tool.define(
|
|||||||
|
|
||||||
transitions++
|
transitions++
|
||||||
|
|
||||||
|
// Handle architect milestone_review results
|
||||||
|
if (result.worker_type === "architect") {
|
||||||
|
const phaseMatch = result.task_id.match(/^phase-(\d+)$/)
|
||||||
|
if (phaseMatch && graph.phases) {
|
||||||
|
const phaseId = parseInt(phaseMatch[1], 10)
|
||||||
|
const phaseObj = graph.phases.find((p) => p.id === phaseId)
|
||||||
|
if (phaseObj) {
|
||||||
|
phaseObj.milestone_satisfied = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if (result.worker_type === "worker" && result.status === "completed") {
|
if (result.worker_type === "worker" && result.status === "completed") {
|
||||||
|
// Gate A: detect fallback keywords in worker output
|
||||||
|
if (result.output_text) {
|
||||||
|
const fallbackHits = detectFallbackKeywords(result.output_text)
|
||||||
|
if (fallbackHits.length > 0) {
|
||||||
|
const retryCount = task.retry_count ?? 0
|
||||||
|
const budget = task.constraints?.retry_budget ?? 3
|
||||||
|
if (retryCount < budget) {
|
||||||
|
task.retry_count = retryCount + 1
|
||||||
|
task.status = "pending"
|
||||||
|
actions.push({
|
||||||
|
action: "dispatch_debugger",
|
||||||
|
task_id: task.id,
|
||||||
|
subagent_type: "worker",
|
||||||
|
prompt: buildAntiFallbackPrompt(task, fallbackHits),
|
||||||
|
description: `退回重做(降级关键词: ${fallbackHits.slice(0, 3).join(", ")})`,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
task.status = "blocked"
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!result.has_cppcheck) {
|
if (!result.has_cppcheck) {
|
||||||
task.status = "running"
|
task.status = "running"
|
||||||
actions.push({
|
actions.push({
|
||||||
@@ -449,9 +647,42 @@ export const CoordinatorTickTool = Tool.define(
|
|||||||
task.status = "blocked"
|
task.status = "blocked"
|
||||||
}
|
}
|
||||||
} else if (result.worker_type === "reviewer" && result.status === "completed") {
|
} else if (result.worker_type === "reviewer" && result.status === "completed") {
|
||||||
|
// Gate B: verify reviewer output contains code-to-design table
|
||||||
|
if (result.output_text) {
|
||||||
|
const hasTable = hasCodeToDesignTable(result.output_text)
|
||||||
|
const surfaceOnly = detectSurfaceEvidenceOnlyPass(result.output_text)
|
||||||
|
if (!hasTable || surfaceOnly) {
|
||||||
|
task.status = "pending_review"
|
||||||
|
actions.push({
|
||||||
|
action: "dispatch_reviewer",
|
||||||
|
task_id: task.id,
|
||||||
|
subagent_type: "reviewer",
|
||||||
|
prompt: buildReReviewPrompt(task, !hasTable, surfaceOnly),
|
||||||
|
description: `重审(${!hasTable ? "缺少逐行对照表" : "仅凭表面证据判定"})`,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
task.status = "completed"
|
task.status = "completed"
|
||||||
} else if (result.worker_type === "reviewer" && result.status === "failed") {
|
} else if (result.worker_type === "reviewer" && result.status === "failed") {
|
||||||
|
// If reviewer report lacks code-to-design table, re-dispatch reviewer
|
||||||
const retryCount = task.retry_count ?? 0
|
const retryCount = task.retry_count ?? 0
|
||||||
|
if (result.output_text && !hasCodeToDesignTable(result.output_text)) {
|
||||||
|
if (retryCount < 2) {
|
||||||
|
task.retry_count = retryCount + 1
|
||||||
|
task.status = "pending_review"
|
||||||
|
actions.push({
|
||||||
|
action: "dispatch_reviewer",
|
||||||
|
task_id: task.id,
|
||||||
|
subagent_type: "reviewer",
|
||||||
|
prompt: buildReReviewPrompt(task, true, false),
|
||||||
|
description: `重审(审查报告缺少逐行对照表)`,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
task.status = "blocked"
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (retryCount < 2) {
|
if (retryCount < 2) {
|
||||||
task.retry_count = retryCount + 1
|
task.retry_count = retryCount + 1
|
||||||
task.status = "pending"
|
task.status = "pending"
|
||||||
@@ -485,16 +716,23 @@ export const CoordinatorTickTool = Tool.define(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 3: Check phase milestones
|
// Phase 3: Check phase milestones (2 retries per phase, 5 total across all phases)
|
||||||
let milestonePhase: number | undefined
|
let milestonePhase: number | undefined
|
||||||
if (graph.phases) {
|
if (graph.phases) {
|
||||||
|
const totalArchitectDispatch = graph.architect_dispatch_total ?? 0
|
||||||
for (const phase of graph.phases) {
|
for (const phase of graph.phases) {
|
||||||
if (!phase.milestone_review) continue
|
if (!phase.milestone_review) continue
|
||||||
|
if (phase.milestone_satisfied) continue // architect already reviewed, OK
|
||||||
|
const phaseDispatchCount = phase.milestone_review_count ?? 0
|
||||||
|
if (phaseDispatchCount >= 2) continue // per-phase retry budget exhausted
|
||||||
|
if (totalArchitectDispatch >= 5) continue // global budget exhausted
|
||||||
const phaseTasks = graph.tasks.filter((t) => t.phase === phase.id)
|
const phaseTasks = graph.tasks.filter((t) => t.phase === phase.id)
|
||||||
const allDone = phaseTasks.every((t) => t.status === "completed")
|
const allDone = phaseTasks.every((t) => t.status === "completed")
|
||||||
const anyRunning = phaseTasks.some((t) => t.status === "running" || t.status === "pending_review")
|
const anyRunning = phaseTasks.some((t) => t.status === "running" || t.status === "pending_review")
|
||||||
if (allDone && phaseTasks.length > 0) {
|
if (allDone && phaseTasks.length > 0) {
|
||||||
milestonePhase = phase.id
|
milestonePhase = phase.id
|
||||||
|
phase.milestone_review_count = phaseDispatchCount + 1 // pre-increment on dispatch
|
||||||
|
graph.architect_dispatch_total = totalArchitectDispatch + 1
|
||||||
actions.push({
|
actions.push({
|
||||||
action: "milestone_review",
|
action: "milestone_review",
|
||||||
task_id: `phase-${phase.id}`,
|
task_id: `phase-${phase.id}`,
|
||||||
@@ -539,6 +777,10 @@ export const CoordinatorTickTool = Tool.define(
|
|||||||
lines.push(`- 运行中: ${runningCount}`)
|
lines.push(`- 运行中: ${runningCount}`)
|
||||||
lines.push(`- 待调度: ${pendingCount}`)
|
lines.push(`- 待调度: ${pendingCount}`)
|
||||||
lines.push(`- 阻塞: ${blockedCount}`)
|
lines.push(`- 阻塞: ${blockedCount}`)
|
||||||
|
if (graph.phases?.some((p) => p.milestone_review)) {
|
||||||
|
const totalUsed = graph.architect_dispatch_total ?? 0
|
||||||
|
lines.push(`- Architect 派发预算: 已用 ${totalUsed}/5(每阶段最多 2 次)`)
|
||||||
|
}
|
||||||
lines.push("")
|
lines.push("")
|
||||||
|
|
||||||
if (actions.length > 0) {
|
if (actions.length > 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user