Initial publish v0.1.0: standalone workflow core (corpus + examples + guards)
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
# 兼容矩阵(COMPATIBILITY)
|
||||
|
||||
Core 与 octopus 实例(dogfood)之间的兼容性契约。随各 Increment 更新;
|
||||
自首个 semver 发布起,本表挂接 changelog(发布仓 `CHANGELOG.md` 的
|
||||
条目以本表的行作为兼容性依据)。
|
||||
|
||||
## core 版本 × octopus 版本
|
||||
|
||||
| core 版本 | 对应 octopus 侧状态 | 兼容说明 |
|
||||
| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 0.x(Increment 1–4:骨架 + G0–G4 全量迁移) | octopus 实例 `.octopus/` 为消费镜像;core 与 dogfood 内容逐字/改写对齐 | core 是 SSOT 权威副本;dogfood 经 `script/core-sync.sh` 单向同步。schema 尚未迁入 core。 |
|
||||
| 0.x(Increment 5:schema $id 迁移四件套) | dogfood `.octopus/schemas/*.json` 的 `$id` **保持旧命名空间不动**(活系统仍引用);core 侧 `$id` 已迁至 `https://eightarms.net/octopus-workflow/schemas/` | 双轨期:core schema 与 dogfood schema 是**不同 $id 的两份文件**(manifest 登记为 `rewritten`,不逐字同步)。活归档 run 的 index.json 携带旧 $id,读取侧经别名表解析。 |
|
||||
| 1.0.0(首个 semver 发布,规划中) | 发布仓创建;live 迁移(dogfood 侧切新命名空间)完成 | `id-aliases.json` 置 `frozen: true`——$id 一经发布不可变(v3 §G)。别名保留 ≥2 个 major 版本。 |
|
||||
|
||||
## builtin overlay 语料兼容
|
||||
|
||||
- 自 Increment 6b 起,builtin overlay(`builtin-data.gen.ts`)内嵌
|
||||
**中立版语料**(源权威 = `core/`);旧版内嵌本组织实例表述(含组织
|
||||
工单号活链接与实例 token)。overlay 仅在实例磁盘无 `<instance-root>/`
|
||||
目录时生效——已初始化实例(含本组织 dogfood)运行时行为不变。
|
||||
|
||||
## $id 行(schema 命名空间兼容)
|
||||
|
||||
- 旧命名空间 `$id`(历史形态 `https://eightarms.net/{legacy-owner}/octopus/.octopus/schemas/*.schema.json`)
|
||||
经 `core/schemas/id-aliases.json` 解析到新命名空间
|
||||
(`https://eightarms.net/octopus-workflow/schemas/*.schema.json`)。
|
||||
- **别名保留期 ≥2 个 major 版本**:自 frozen 起至少两个 major 版本内,
|
||||
旧 `$id` 仍被接受(`runs-index.schema.json` 的 `artifacts[].schema`
|
||||
字段声明该历史接受集)。
|
||||
- **twin token 归一记录**:scaffold-template 侧 schema `$id` 的 owner
|
||||
token(旧命名空间 owner → Octopus)差异经 `normalizeSchemaOwnerToken`
|
||||
(`script/check-scaffold-parity.ts`)归一化比较,双向等价;Increment 5
|
||||
已将残留旧 owner token 的 6 份模板 schema 统一为 Octopus token(纯
|
||||
一致性清理,归一化语义不变)。
|
||||
|
||||
## 后续 semver changelog 挂钩
|
||||
|
||||
- 首个 semver 发布(1.0.0)起,任何影响本表行的变更($id 变更、别名
|
||||
增删、兼容语义调整)必须在发布仓 changelog 中登记,并同步更新本表。
|
||||
- 别名表 `frozen: true` 后新增别名视为 minor 变更;删除别名须满足
|
||||
「≥2 个 major 版本保留期」并视为 major 变更。
|
||||
@@ -0,0 +1,431 @@
|
||||
{
|
||||
"version": 1,
|
||||
"mappings": [
|
||||
{
|
||||
"core": "core/rules/claim-discipline.md",
|
||||
"dogfood": ".octopus/rules/claim-discipline.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/stash-discipline.md",
|
||||
"dogfood": ".octopus/rules/stash-discipline.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/code-graph.md",
|
||||
"dogfood": ".octopus/rules/code-graph.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/compact.md",
|
||||
"dogfood": ".octopus/rules/compact.md",
|
||||
"batch": "G1",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/workflow-routing.md",
|
||||
"dogfood": ".octopus/rules/workflow-routing.md",
|
||||
"batch": "G1",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/auto-approve.md",
|
||||
"dogfood": ".octopus/rules/auto-approve.md",
|
||||
"batch": "G1",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/two-tier-artifacts.md",
|
||||
"dogfood": ".octopus/rules/two-tier-artifacts.md",
|
||||
"batch": "G1",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/ticket-lifecycle.md",
|
||||
"dogfood": ".octopus/rules/ticket-lifecycle.md",
|
||||
"batch": "G1",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/testing.md",
|
||||
"dogfood": ".octopus/rules/testing.md",
|
||||
"batch": "G1",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/artifact-addressing.md",
|
||||
"dogfood": null,
|
||||
"batch": "G2",
|
||||
"sync": "core-only"
|
||||
},
|
||||
{
|
||||
"core": "core/adapters/gitea/patterns.md",
|
||||
"dogfood": ".octopus/skills/_shared/gitea-write-patterns.md",
|
||||
"batch": "G2",
|
||||
"sync": "split"
|
||||
},
|
||||
{
|
||||
"core": "core/adapters/gitea/reading.md",
|
||||
"dogfood": ".octopus/skills/_shared/gitea-read-patterns.md",
|
||||
"batch": "G2",
|
||||
"sync": "split"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/issue-checklist-sync.md",
|
||||
"dogfood": ".octopus/rules/issue-checklist-sync.md",
|
||||
"batch": "G3",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/issue-cross-linking.md",
|
||||
"dogfood": ".octopus/rules/issue-cross-linking.md",
|
||||
"batch": "G3",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/artifact-index-guards.md",
|
||||
"dogfood": ".octopus/rules/artifact-index-guards.md",
|
||||
"batch": "G3",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/type-checking.md",
|
||||
"dogfood": ".octopus/rules/type-checking.md",
|
||||
"batch": "G3",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/session-scope-guard.md",
|
||||
"dogfood": ".octopus/rules/session-scope-guard.md",
|
||||
"batch": "G3",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/analyze-dag/",
|
||||
"dogfood": ".octopus/skills/analyze-dag/",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/browser-debug/",
|
||||
"dogfood": ".octopus/skills/browser-debug/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/codegraph-setup/",
|
||||
"dogfood": ".octopus/skills/codegraph-setup/",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/frontend/",
|
||||
"dogfood": ".octopus/skills/frontend/",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/gitea-rest/",
|
||||
"dogfood": ".octopus/skills/gitea-rest/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/headless-session-ops/",
|
||||
"dogfood": ".octopus/skills/headless-session-ops/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/image-interpret/",
|
||||
"dogfood": ".octopus/skills/image-interpret/",
|
||||
"batch": "G4",
|
||||
"sync": "verbatimDir"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/implement/",
|
||||
"dogfood": ".octopus/skills/implement/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/land-batch/",
|
||||
"dogfood": ".octopus/skills/land-batch/",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/project-kickoff/",
|
||||
"dogfood": ".octopus/skills/project-kickoff/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/prototype/",
|
||||
"dogfood": ".octopus/skills/prototype/",
|
||||
"batch": "G4",
|
||||
"sync": "verbatimDir"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/release/",
|
||||
"dogfood": ".octopus/skills/release/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/retrospective/",
|
||||
"dogfood": ".octopus/skills/retrospective/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/review-artifact/",
|
||||
"dogfood": ".octopus/skills/review-artifact/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/review-code/",
|
||||
"dogfood": ".octopus/skills/review-code/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/verify/",
|
||||
"dogfood": ".octopus/skills/verify/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/writing-skills/",
|
||||
"dogfood": ".octopus/skills/writing-skills/",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/compaction-round-boundary.md",
|
||||
"dogfood": ".octopus/skills/_shared/compaction-round-boundary.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/large-prompts.md",
|
||||
"dogfood": ".octopus/skills/_shared/large-prompts.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/review-final-report-template.md",
|
||||
"dogfood": ".octopus/skills/_shared/review-final-report-template.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/review-orchestrator-rules.md",
|
||||
"dogfood": ".octopus/skills/_shared/review-orchestrator-rules.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/review-pipeline-phases.md",
|
||||
"dogfood": ".octopus/skills/_shared/review-pipeline-phases.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/review-reviewer-prompt.md",
|
||||
"dogfood": ".octopus/skills/_shared/review-reviewer-prompt.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/review-revision-prompt.md",
|
||||
"dogfood": ".octopus/skills/_shared/review-revision-prompt.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/synthesis_task.md",
|
||||
"dogfood": ".octopus/skills/_shared/synthesis_task.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/worker-report-persistence.md",
|
||||
"dogfood": ".octopus/skills/_shared/worker-report-persistence.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/roles/coordinator.yaml",
|
||||
"dogfood": ".octopus/skills/_shared/roles/coordinator.yaml",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/roles/producer.yaml",
|
||||
"dogfood": ".octopus/skills/_shared/roles/producer.yaml",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/roles/reviewer.yaml",
|
||||
"dogfood": ".octopus/skills/_shared/roles/reviewer.yaml",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/roles/tool.yaml",
|
||||
"dogfood": ".octopus/skills/_shared/roles/tool.yaml",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/roles/verifier.yaml",
|
||||
"dogfood": ".octopus/skills/_shared/roles/verifier.yaml",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/audit-process.md",
|
||||
"dogfood": ".octopus/checklists/audit-process.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/bugfix.md",
|
||||
"dogfood": ".octopus/checklists/bugfix.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/code-review.md",
|
||||
"dogfood": ".octopus/checklists/code-review.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/dag-single-gate.md",
|
||||
"dogfood": ".octopus/checklists/dag-single-gate.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/frontend.md",
|
||||
"dogfood": ".octopus/checklists/frontend.md",
|
||||
"batch": "G4",
|
||||
"sync": "verbatim"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/implementation.md",
|
||||
"dogfood": ".octopus/checklists/implementation.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/pipeline-gate.md",
|
||||
"dogfood": ".octopus/checklists/pipeline-gate.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/port.md",
|
||||
"dogfood": ".octopus/checklists/port.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/prototype.md",
|
||||
"dogfood": ".octopus/checklists/prototype.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/refactoring.md",
|
||||
"dogfood": ".octopus/checklists/refactoring.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/release.md",
|
||||
"dogfood": ".octopus/checklists/release.md",
|
||||
"batch": "G4",
|
||||
"sync": "verbatim"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/retrospective.md",
|
||||
"dogfood": ".octopus/checklists/retrospective.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/verification.md",
|
||||
"dogfood": ".octopus/checklists/verification.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/templates/dag.md",
|
||||
"dogfood": ".octopus/templates/dag.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/templates/runs-layout.md",
|
||||
"dogfood": ".octopus/templates/runs-layout.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/port-analysis.schema.json",
|
||||
"dogfood": ".octopus/schemas/port-analysis.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/review-status.schema.json",
|
||||
"dogfood": ".octopus/schemas/review-status.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/reviewer-output.schema.json",
|
||||
"dogfood": ".octopus/schemas/reviewer-output.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/runs-bundle.schema.json",
|
||||
"dogfood": ".octopus/schemas/runs-bundle.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/runs-index.schema.json",
|
||||
"dogfood": ".octopus/schemas/runs-index.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/runs-meta.schema.json",
|
||||
"dogfood": ".octopus/schemas/runs-meta.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/synthesis.schema.json",
|
||||
"dogfood": ".octopus/schemas/synthesis.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/workflow-routing.schema.json",
|
||||
"dogfood": ".octopus/schemas/workflow-routing.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
# Core 迁移映射表
|
||||
|
||||
编号保真(不变量 C-2):迁移仅改位置,不改编号。映射登记的运行时事实源
|
||||
是 `core/CORE-MANIFEST.json`;本表是批次级视图。
|
||||
|
||||
| 源路径 | Core 路径 | 批次 | 状态 |
|
||||
| --------------------------------------------------------- | ----------------------------------------------------- | ---- | ---------------------------------------------------------------------------------------- |
|
||||
| `.octopus/rules/claim-discipline.md` | `core/rules/claim-discipline.md` | G0 | 已迁移(逐字复制) |
|
||||
| `.octopus/rules/stash-discipline.md` | `core/rules/stash-discipline.md` | G0 | 已迁移(逐字复制) |
|
||||
| `.octopus/rules/code-graph.md` | `core/rules/code-graph.md` | G0 | 已迁移(逐字复制) |
|
||||
| `.octopus/rules/compact.md` | `core/rules/compact.md` | G1 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/workflow-routing.md` | `core/rules/workflow-routing.md` | G1 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/auto-approve.md` | `core/rules/auto-approve.md` | G1 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/two-tier-artifacts.md` | `core/rules/two-tier-artifacts.md` | G1 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/ticket-lifecycle.md` | `core/rules/ticket-lifecycle.md` | G1 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/testing.md` | `core/rules/testing.md` | G1 | 已迁移(接口中立化改写) |
|
||||
| (Core 原生,无 dogfood 源) | `core/rules/artifact-addressing.md` | G2 | 新增(Core 原生契约) |
|
||||
| `.octopus/skills/_shared/gitea-write-patterns.md` | `core/adapters/gitea/patterns.md` | G2 | 已拆分(adapter 拆分上提) |
|
||||
| `.octopus/skills/_shared/gitea-read-patterns.md` | `core/adapters/gitea/reading.md` | G2 | 已拆分(adapter 拆分上提) |
|
||||
| `.octopus/rules/issue-checklist-sync.md` | `core/rules/issue-checklist-sync.md` | G3 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/issue-cross-linking.md` | `core/rules/issue-cross-linking.md` | G3 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/artifact-index-guards.md` | `core/rules/artifact-index-guards.md` | G3 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/type-checking.md` | `core/rules/type-checking.md` | G3 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/session-scope-guard.md` | `core/rules/session-scope-guard.md` | G3 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/skills/analyze-dag/` | `core/skills/analyze-dag/` | G4 | 已迁移(整目录逐字) |
|
||||
| `.octopus/skills/browser-debug/` | `core/skills/browser-debug/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/codegraph-setup/` | `core/skills/codegraph-setup/` | G4 | 已迁移(整目录逐字) |
|
||||
| `.octopus/skills/frontend/` | `core/skills/frontend/` | G4 | 已迁移(整目录逐字) |
|
||||
| `.octopus/skills/gitea-rest/` | `core/skills/gitea-rest/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/headless-session-ops/` | `core/skills/headless-session-ops/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/image-interpret/` | `core/skills/image-interpret/` | G4 | 已迁移(整目录逐字) |
|
||||
| `.octopus/skills/implement/` | `core/skills/implement/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/land-batch/` | `core/skills/land-batch/` | G4 | 已迁移(整目录逐字) |
|
||||
| `.octopus/skills/project-kickoff/` | `core/skills/project-kickoff/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/prototype/` | `core/skills/prototype/` | G4 | 已迁移(整目录逐字) |
|
||||
| `.octopus/skills/release/` | `core/skills/release/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/retrospective/` | `core/skills/retrospective/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/review-artifact/` | `core/skills/review-artifact/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/review-code/` | `core/skills/review-code/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/verify/` | `core/skills/verify/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/writing-skills/` | `core/skills/writing-skills/` | G4 | 已迁移(整目录逐字) |
|
||||
| `.octopus/skills/_shared/compaction-round-boundary.md` | `core/skills/_shared/compaction-round-boundary.md` | G4 | 已迁移(逐字,protected 纪律载体;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/_shared/large-prompts.md` | `core/skills/_shared/large-prompts.md` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/review-final-report-template.md` | `core/skills/_shared/review-final-report-template.md` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/review-orchestrator-rules.md` | `core/skills/_shared/review-orchestrator-rules.md` | G4 | 已迁移(逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/_shared/review-pipeline-phases.md` | `core/skills/_shared/review-pipeline-phases.md` | G4 | 已迁移(逐字,protected 纪律载体;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/_shared/review-reviewer-prompt.md` | `core/skills/_shared/review-reviewer-prompt.md` | G4 | 已迁移(逐字,protected 纪律载体;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/_shared/review-revision-prompt.md` | `core/skills/_shared/review-revision-prompt.md` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/synthesis_task.md` | `core/skills/_shared/synthesis_task.md` | G4 | 已迁移(逐字,protected 纪律载体;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/_shared/worker-report-persistence.md` | `core/skills/_shared/worker-report-persistence.md` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/roles/coordinator.yaml` | `core/skills/_shared/roles/coordinator.yaml` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/roles/producer.yaml` | `core/skills/_shared/roles/producer.yaml` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/roles/reviewer.yaml` | `core/skills/_shared/roles/reviewer.yaml` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/roles/tool.yaml` | `core/skills/_shared/roles/tool.yaml` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/roles/verifier.yaml` | `core/skills/_shared/roles/verifier.yaml` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/checklists/*.md`(13 份) | `core/checklists/` | G4 | 已迁移(逐字;audit-process/bugfix/pipeline-gate/port/prototype/verification;6a 改写为 rewritten) |
|
||||
| `.octopus/templates/*.md`(2 份) | `core/templates/` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/schemas/port-analysis.schema.json` | `core/schemas/port-analysis.schema.json` | G5 | 已迁移($id 迁至公共命名空间) |
|
||||
| `.octopus/schemas/review-status.schema.json` | `core/schemas/review-status.schema.json` | G5 | 已迁移($id 迁至公共命名空间) |
|
||||
| `.octopus/schemas/reviewer-output.schema.json` | `core/schemas/reviewer-output.schema.json` | G5 | 已迁移($id 迁至公共命名空间) |
|
||||
| `.octopus/schemas/runs-bundle.schema.json` | `core/schemas/runs-bundle.schema.json` | G5 | 已迁移($id 迁至公共命名空间) |
|
||||
| `.octopus/schemas/runs-index.schema.json` | `core/schemas/runs-index.schema.json` | G5 | 已迁移($id 迁至公共命名空间;artifacts[].schema 声明历史接受集) |
|
||||
| `.octopus/schemas/runs-meta.schema.json` | `core/schemas/runs-meta.schema.json` | G5 | 已迁移($id 迁至公共命名空间) |
|
||||
| `.octopus/schemas/synthesis.schema.json` | `core/schemas/synthesis.schema.json` | G5 | 已迁移($id 迁至公共命名空间) |
|
||||
| `.octopus/schemas/workflow-routing.schema.json` | `core/schemas/workflow-routing.schema.json` | G5 | 已迁移($id 迁至公共命名空间) |
|
||||
| (待登记) | (待登记) | G5+ | 未开始 |
|
||||
|
||||
## G5 twin 归一记录
|
||||
|
||||
scaffold-template 侧(`packages/octopus/src/cli/cmd/scaffold-template/schemas/`)
|
||||
schema `$id` 的 owner token 归一(旧命名空间 owner token → Octopus,与既有 2 份多数对齐;
|
||||
`normalizeSchemaOwnerToken` 归一化下双向等价,纯一致性清理):
|
||||
|
||||
- `review-status.schema.json`
|
||||
- `reviewer-output.schema.json`
|
||||
- `runs-bundle.schema.json`
|
||||
- `runs-index.schema.json`
|
||||
- `synthesis.schema.json`
|
||||
- `workflow-routing.schema.json`
|
||||
|
||||
## 保护注记(protected 纪律载体)
|
||||
|
||||
以下四个 `_shared` 文件是多轮评审管线的纪律载体(round-boundary 压缩、
|
||||
评审提示词、综合任务),G4 仅逐字迁移不改内容;其改写留给 Increment 6
|
||||
(deferHard 标记已在 manifest 登记):
|
||||
|
||||
- `review-pipeline-phases.md`
|
||||
- `review-reviewer-prompt.md`
|
||||
- `synthesis_task.md`
|
||||
- `compaction-round-boundary.md`
|
||||
|
||||
## G6(Increment 6a:发布单元达标面)
|
||||
|
||||
- **deferHard 27 件 → rewritten**:10 个 verbatimDir 技能目录 + 17 个
|
||||
verbatim 文件(_shared md/yaml + 清单/模板)全部按 G1 风格中立化改写
|
||||
(机制/结构/frontmatter 保留;实例术语以占位符表述并指向
|
||||
core/adapters/TERMINOLOGY.md;技能内 .ts 脚本未动)。gitea-rest SKILL.md
|
||||
标注为 gitea adapter reference implementation(frontmatter 原样)。
|
||||
manifest:sync → rewritten(目录行保持目录路径)、batch → G6、deferHard
|
||||
键全删。
|
||||
- **守卫小扩展**:parity/cohesion/dangling/core-sync 支持 rewritten 目录行
|
||||
(存在性 = 目录存在;C-1 扫描 = 递归目录内 .md/.yaml);cohesion 默认
|
||||
扫描面纳入 .yaml;core-sync 对 rewritten 目录行报 managed-by 跳过。
|
||||
- **去链接化契约执行**:core/** 全树 #NNNN 活引用 → [org-internal #NNNN]
|
||||
注记(457 处、48 文件);delink:core 幂等;verbatim 镜像内 60 处递延
|
||||
至 6b。
|
||||
- **examples/ 建立**:4 篇组织沉积教学案例(sprint-mode 吞吐事故、
|
||||
preflight 证据闭环、WIP cap 与 TD 登记、评审轮次预算),只引用 Core
|
||||
不被引用。
|
||||
- **P1 终验门**:check:core-p1(98 文本文件全树扫描;豁免 id-aliases.json
|
||||
与 runs-index 历史命名空间声明;verbatim 镜像 2 处命中列 6b 递延)。
|
||||
- 最终分类:71 mappings = 15 verbatim + 7 verbatimDir + 46 rewritten +
|
||||
2 split + 1 core-only。
|
||||
|
||||
## G6b(Increment 6b:字节锁解锁 + EMBED 重指)
|
||||
|
||||
- **字节锁镜像翻转 rewritten**:v3 去链接化契约规定 dogfood 侧保留活链接
|
||||
→ 不能三侧同批 delink。18 个携带 refs/P1 命中的 verbatim/verbatimDir
|
||||
镜像(13 个 verbatim 文件行 + 5 个 verbatimDir 目录行)翻转为
|
||||
sync = "rewritten"、batch = "G6b";core 侧运行 delink(60 处
|
||||
#NNNN → [org-internal #NNNN])+ 两处旧 owner legacy-token 手工中立化
|
||||
(checklists/code-review.md 5.8 行、checklists/refactoring.md 4.7 行,
|
||||
改为不指名组织的 token 漂移表述);dogfood 侧不动(parity 的 rewritten
|
||||
语义 = 双侧存在 + core 模式扫描,无字节比较)。翻转账本的 md 链接改写
|
||||
为 core/ 与 <instance-root>/ 引用形态(analyze-dag/frontend/
|
||||
writing-skills 共 21 处路径去实例化)。
|
||||
- **EMBED 重指 core/**:generate-builtin.ts 的 EMBED_SOURCES 源权威从
|
||||
`<instance-root>/` 改为 core/(audit §G 承诺)。CLOSURE_PATTERN / META_SKILL_
|
||||
PREFIXES / closure 路径解析同步重指;service 键名(rules/auto-approve.md
|
||||
等)保持不变,运行时读取不受影响。builtin overlay 仅在无 <instance-root>/
|
||||
磁盘目录时生效——本组织恒有 → dogfood 运行时不变;shipped 默认语料自此
|
||||
为中立版(内嵌 [org-internal #NNNN] 注记、无组织实例 token)。
|
||||
- **终验归零**:delink-core --check 0 replaceable / 0 deferred;
|
||||
check-core-p1 0 hits / 0 deferred(6a 递延清单全部清偿)。
|
||||
- 最终分类:71 mappings = 2 verbatim + 2 verbatimDir + 64 rewritten +
|
||||
2 split + 1 core-only。
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
# octopus Core(工作流标准公共层)
|
||||
|
||||
`core/` 是工作流体系标准化后的**公共 Core**:自包含的规则、技能、
|
||||
清单、模板、schema 与适配器层。`.octopus/` 下的对应文件是**消费副本**
|
||||
(dogfood 实例),`examples/` 仅承载叙事性示例。
|
||||
|
||||
## 不变量 C-1:Core 不引用实例
|
||||
|
||||
- Core 内的工件**不得引用** `examples/` 或任何实例专有路径;`core/adapters/**`
|
||||
例外——adapter 目录承载后端绑定的 API 形态(工具名、端点),但仍
|
||||
**禁止实例机密与实例专有地址**(内网 IP、实例主机名、实例配置路径)。
|
||||
- 引用方向是**单向的**:`examples/` 与实例层可以引用 Core,Core 永远不
|
||||
回头引用它们。
|
||||
- 机械化校验:`script/check-core-cohesion.ts` 的 C-1 扫描——HARD 模式
|
||||
(实例机密/专有地址)对全部 core 生效;SOFT 模式(实例路径/工具名)
|
||||
对 `core/adapters/**` 豁免、其余 core 文件生效(见「增量采用」)。
|
||||
|
||||
## 不变量 C-2:迁移保编号
|
||||
|
||||
- 清单迁移**仅改位置、不改编号**:任何规则 / 技能 / 清单迁入 Core 时,
|
||||
其标识符(issue 编号、TD 编号、检查项编号)原样保留。
|
||||
- 编号是跨实例的稳定契约;重编号会切断历史追溯链。
|
||||
- 映射登记:`core/CORE-MANIFEST.json`(core 路径 ↔ dogfood 路径 ↔ 批次),
|
||||
编号保真映射表骨架见 `core/MIGRATION.md`。
|
||||
|
||||
## SSOT 契约(单一事实源)
|
||||
|
||||
- **Core = 全量权威副本**:规则、技能、\_shared、清单、模板已全部迁入,
|
||||
所有修改先落在 `core/`。**builtin overlay 源权威 = core/**(Increment 6b
|
||||
起,`packages/octopus/script/generate-builtin.ts` 的 EMBED_SOURCES 从
|
||||
core/ 读取——shipped 默认语料为中立版;仅当实例磁盘无
|
||||
`<instance-root>/` 目录时 overlay 才生效)。
|
||||
- **`.octopus/` = 消费镜像**:dogfood 实例通过 `script/core-sync.sh` 从
|
||||
Core 单向同步(verbatim / verbatimDir 自动逐文件同步;rewritten /
|
||||
split 按术语表落地),永不反向。
|
||||
- **`examples/` 仅叙事**:只引用 Core,不承载事实源内容。
|
||||
- 机械化守护:
|
||||
- `script/core-sync.sh` — 单向同步(`--check` 只检不写,CI 用);
|
||||
- `script/check-core-parity.ts` — 逐对比较 manifest 登记的文件内容,
|
||||
不一致即报漂移;
|
||||
- `script/check-core-cohesion.ts` — manifest 路径存在性 + C-1 扫描;
|
||||
- `script/check-dangling-refs.ts` — 双侧路径存在性 + core 内部引用
|
||||
完整性(intra-core 悬空检出;`--scope-report` 输出覆盖摘要);
|
||||
- `script/check-scaffold-parity.ts` — scaffold-template twin 逐字节
|
||||
校验(schema $id owner token 归一化比较);
|
||||
- `script/delink-core.ts` — 去链接化契约执行(#NNNN 活引用 → [org-internal #NNNN]
|
||||
注记;幂等;--check 零剩余才过;Inc 6b 字节锁镜像翻转 rewritten 后无递延);
|
||||
- `script/check-core-p1.ts` — P1 零残留终验(私网 IP / 实例主机名 / 个人
|
||||
身份 / 旧 owner token 全树 grep;豁免 id-aliases.json 与历史命名空间
|
||||
URL 声明;Inc 6b 起无字节锁递延);
|
||||
- `script/check-schema-ids.ts` — core schemas $id 命名空间/文件名匹配
|
||||
- id-aliases 一对一/存在性 + 历史接受集声明在位(完整 guards 表见
|
||||
文末「机械化守护(guards)」)。
|
||||
|
||||
## 发布单元达标(Increment 6a + 6b)
|
||||
|
||||
- **去链接化契约已执行**:core/** 全树 #NNNN 活引用已注记化
|
||||
([org-internal #NNNN]),delink:core 幂等可重跑,--check 为零剩余门。
|
||||
Inc 6b 已解锁 6a 递延的 60 处字节锁镜像引用(翻转 rewritten 后 core 侧
|
||||
全量注记化,0 递延)。
|
||||
- **P1 零残留终验门**:check:core-p1 扫 core/ 全部文本文件(md/yaml/
|
||||
json/ts),命中私网 IP、实例主机名、个人身份、旧 owner token 即 FAIL;
|
||||
id-aliases.json(SCH-F401)与 runs-index 历史命名空间声明豁免。
|
||||
Inc 6b 已清偿 6a 递延的 2 处旧 owner token 命中(0 递延)。
|
||||
- **examples/ 方向性**:组织沉积教学案例(叙事),只引用 Core、不被 Core
|
||||
引用(C-1 单向);不入 manifest、不进 cohesion 扫描面。
|
||||
|
||||
## 目录语义
|
||||
|
||||
| 目录 | 语义 |
|
||||
| ---------------------- | ------------------------------------------------------- |
|
||||
| `core/rules/` | L1 强制规则的 Core 源(14 份已全部迁入,G0–G3) |
|
||||
| `core/skills/` | 技能整目录的 Core 源(17 个已全部迁入,G4 verbatimDir) |
|
||||
| `core/skills/_shared/` | 技能共享工件(角色 yaml、评审管线文件等,14 份,G4) |
|
||||
| `core/checklists/` | 清单(13 份已全部迁入,G4) |
|
||||
| `core/templates/` | 模板(dag.md、runs 布局,2 份已全部迁入,G4) |
|
||||
| `core/schemas/` | JSON Schema(8 份已迁入,G5;$id 已迁至公共命名空间) |
|
||||
| `core/adapters/` | 实例适配层(后端参考实现;SOFT 扫描豁免、HARD 仍生效) |
|
||||
|
||||
## 增量采用
|
||||
|
||||
Core 的采用以 `core/CORE-MANIFEST.json` 登记为准——**登记了才算采用**。
|
||||
G0–G4 批次已完成全量迁移(14 规则、17 技能整目录、\_shared 14 份、
|
||||
13 清单、2 模板);后续批次按 `core/MIGRATION.md` 的映射表推进。
|
||||
|
||||
### C-1 扫描的增量形态(设计决策)
|
||||
|
||||
模式分两级(Increment 3):
|
||||
|
||||
- **HARD**(全 core 生效,含 adapters):实例主机名、内网 IP 段、实例
|
||||
配置路径——实例机密与实例专有地址任何 core 文件不得出现。
|
||||
- **SOFT**(`core/adapters/**` 豁免):`.octopus/`、`packages/octopus`、
|
||||
MCP 工具名、工作树路径等实例绑定内容——adapter 目录是 Gitea 参考
|
||||
实现,允许承载;其余 core 文件不得出现。
|
||||
|
||||
G0 三份规则是从实例规则**逐字复制**的,规则正文里出现实例路径字样属
|
||||
**叙述性引用**,且都在行内代码/围栏内(扫描先剥离,零命中)。
|
||||
脚本以 `--strict` 开关预留更严形态:`--strict` 时额外对 verbatim 文件
|
||||
按全模式集扫描(剥离语义同前)。默认扫描面 = core 下全部 `.md` 文件
|
||||
(含 adapters,adapters 只查 HARD)。扫描匹配前先剥离行内代码 span
|
||||
(反引号内文字)与围栏代码块——反引号内的是定义性提及(如本文件对
|
||||
C-1 模式列表的描述),不是活引用。
|
||||
|
||||
### manifest 语义(sync 字段)
|
||||
|
||||
| sync 值 | 语义 | parity 校验 | core-sync 行为 |
|
||||
| ------------- | --------------------------------------------------------------------- | ------------------------------------------------- | ------------------------------------ |
|
||||
| `verbatim` | 逐字复制 | 字节比较 | Core → dogfood 自动 `cp` |
|
||||
| `verbatimDir` | 整目录逐字复制(目录级行,core 路径以 `/` 结尾) | 递归文件集一致(排除 `.gitkeep`)+ 逐文件字节比较 | Core → dogfood 逐文件同步 |
|
||||
| `rewritten` | 接口中立化改写 | 双侧存在 + core 侧 HARD+SOFT 扫描 | `managed-by: rewrite (no auto-sync)` |
|
||||
| `split` | adapter 拆分上提(页名/寻址语义已上提 Core 契约,API 形态留 adapter) | 双侧存在 + core 侧 HARD 扫描(SOFT 豁免) | `managed-by: split (no auto-sync)` |
|
||||
| `core-only` | Core 原生契约,无 dogfood 对应(`dogfood: null`) | core 存在 + HARD+SOFT 扫描,无 dogfood 检查 | `managed-by: core-only` |
|
||||
|
||||
`deferHard: true`(历史字段,**Increment 6a 已全数清零**):曾标记暂含实例
|
||||
表述的行;6a 已将全部 27 行中立化改写为 rewritten 并删除该键。现
|
||||
manifest 中不存在 deferHard 行,cohesion `--strict` 的 deferred 计数为 0。
|
||||
@@ -0,0 +1,36 @@
|
||||
# 术语映射表(Core ↔ dogfood 实例)
|
||||
|
||||
> 本表是 Core 改写与 dogfood 实例对照的**唯一事实源**(v3 §E):Core 侧
|
||||
> 工件使用下表左列的中立术语,右列是本组织 dogfood 实例(Gitea 后端)的
|
||||
> 实际表述。任何 Core 工件需要落到本实例时,按本表做机械替换;反向
|
||||
> 改写(实例 → Core)同样以本表为准。新增映射必须先改本表,再改工件。
|
||||
|
||||
| Core 中立术语 | dogfood/Gitea 实例表述 |
|
||||
| --- | --- |
|
||||
| 工单后端(ticket backend) | Gitea |
|
||||
| Tier-2 工件库 | Gitea wiki |
|
||||
| 工单评论 API(ticket backend 评论接口) | `gitea_issue_comment__*` |
|
||||
| wiki 读写 API(Tier-2 工件库读写接口) | `gitea_wiki__*` |
|
||||
| 看板移动 API(工单看板列迁移接口) | `gitea_column__move_issue` |
|
||||
| 项目看板 API(工单面板管理接口) | `gitea_project__*` |
|
||||
| 路由解析工具(Step 0 路由门依赖) | `route_resolver` |
|
||||
| 会话工作树路径(并发分支工作区根) | `<worktrees-root>` |
|
||||
| 测试命令·变更集(本地前置检查) | `bun run test:changed` |
|
||||
| 测试命令·全量分片(交付前置 / nightly) | `bun run test:parallel` |
|
||||
| 类型检查命令 | `bun typecheck` |
|
||||
| 产品代码路径(harness 实现所在包) | `packages/octopus/src/...` |
|
||||
| 私化工单引用(组织内部工单号) | `#NNNN` 活链接(Core 侧用 `[org-internal #NNNN]` 注记) |
|
||||
| 实例规则目录(dogfood 消费副本所在) | `.octopus/`(如 `.octopus/rules/`) |
|
||||
| 运行工作区(Tier-1 本地工件) | `.octopus/runs/{slug}/` |
|
||||
| 实例配置文件(会话/管线配置) | `octopus.jsonc` |
|
||||
|
||||
## 备注
|
||||
|
||||
- **私化工单引用**:Core 侧不得产出指向具体实例的活链接 `#NNNN`(会被
|
||||
实例渲染为真实工单);一律写 `[org-internal #NNNN]` 注记格式,落地
|
||||
实例时按本表还原为 `#NNNN`。
|
||||
- **命令占位**:Core 规则正文中的 `<test-cmd:changed>`、
|
||||
`<test-cmd:parallel>` 等占位符按本表「测试命令」两行绑定到实例命令。
|
||||
- **裸词 Gitea**:仅当作为 adapter 名称出现时允许(如「Gitea adapter」);
|
||||
- 表内反引号包裹的实例表述仅供对照,不构成 Core 对实例的引用
|
||||
(C-1 扫描剥离行内代码后本表零命中)。
|
||||
@@ -0,0 +1,215 @@
|
||||
# Gitea Adapter — 写模式(Write Patterns)
|
||||
|
||||
> Gitea adapter 参考实现(Increment 3,自 dogfood 源
|
||||
> `_shared/gitea-write-patterns.md` 拆分上提)。本目录承载后端绑定的
|
||||
> API 形态(MCP 工具名、REST 端点、curl 形态);页名规范与寻址语义
|
||||
> 是后端中立契约,见 `core/rules/artifact-addressing.md`(本文不重复)。
|
||||
> 实例基址由实例配置提供(下文 `<gitea-base-url>`),见
|
||||
> `core/adapters/TERMINOLOGY.md`。
|
||||
|
||||
Owner/repo 固定为 `Octopus/octopus`。所有 wiki 页名遵循 Core 契约
|
||||
`{slug}/{type}-{seq:02d}-{title}`(例外页全枚举见 artifact-addressing.md)。
|
||||
|
||||
## Wiki URL 构造 — html_url 规则
|
||||
|
||||
**黄金规则:绝不手工拼接 wiki URL。** `gitea_wiki__create_page` /
|
||||
`gitea_wiki__get_page` / `gitea_wiki__list_pages` 响应中的 `html_url`
|
||||
字段是唯一权威链接,发布时捕获并原样复用。`page_name` → `html_url`
|
||||
的变换不可推导(`/`→`%2F`、含斜杠页名带 `.-` 尾缀、CJK 百分号编码),
|
||||
必须读 API。
|
||||
|
||||
**两种标识符勿混淆**:
|
||||
|
||||
| 标识符 | 是什么 | 用途 |
|
||||
| ----------- | ------------------------------------ | ---------------------------------------- |
|
||||
| `page_name` | 原始页标识;字面 `/`、无主机、无编码 | wiki MCP 工具的 `page_name`/`title` 参数 |
|
||||
| `html_url` | 后端生成的完整可点击 URL | markdown 链接、`target_url`、PR 正文 |
|
||||
|
||||
**去向**:
|
||||
|
||||
- **工件索引位置列** — 一个单元格同时存两者:
|
||||
``[`{page_name}`]({html_url})``。链接文本供读侧调
|
||||
`gitea_wiki__get_page`,href 供人点击(见 Pattern 10)。
|
||||
- **commit-status `target_url`** — 终报页的 `html_url`(见 Pattern 8)。
|
||||
- **页内交叉链接** — 用 `html_url`。
|
||||
|
||||
无 API 响应可用时(静态源串)用 `<gitea-base-url>`,且仅此一处来源。
|
||||
|
||||
## Pattern 1: create-wiki-page
|
||||
|
||||
```
|
||||
gitea_wiki__create_page(owner="Octopus", repo="octopus",
|
||||
title="{slug}/{type}-{seq:02d}-{title}",
|
||||
content="{内容}",
|
||||
message="{可选 commit message}")
|
||||
```
|
||||
|
||||
**发布→验证(强制)**:发布后立刻回读确认存在且内容一致:
|
||||
|
||||
```
|
||||
gitea_wiki__get_page(owner="Octopus", repo="octopus", page_name="{同 title}")
|
||||
```
|
||||
|
||||
404 / 内容不一致 → 修复后重发。页名冲突(409)→ 该页已存在,改用
|
||||
Pattern 2 update,绝不另发新页。响应的 `html_url` 立即捕获复用。
|
||||
|
||||
## Pattern 2: update-wiki-page
|
||||
|
||||
```
|
||||
gitea_wiki__update_page(owner="Octopus", repo="octopus",
|
||||
page_name="{页名}",
|
||||
content="{新内容}",
|
||||
message="{commit message}")
|
||||
```
|
||||
|
||||
更新后再回读验证;409 冲突 → 拉最新内容手工合并后重试。
|
||||
|
||||
## Pattern 3: create-issue
|
||||
|
||||
```
|
||||
gitea_issue__create(owner="Octopus", repo="octopus",
|
||||
title="{标题}", body="{正文}", labels=["{label}"])
|
||||
```
|
||||
|
||||
### 工单交叉链接(强制)
|
||||
|
||||
父子工单组必须双向链接:父工单 task list 引用 `#<number>`;子工单正文
|
||||
带 `## 父级 / Parent` 节引用父 `#<number>`。
|
||||
|
||||
### 衍生工单创建
|
||||
|
||||
- 技术债(verify Phase 5.5):`TD-NNN` 经分配台账取号后升票,`## Parent`
|
||||
指回登记册源工单。
|
||||
- 基线失败(Phase 5.55):label `baseline-failure` + `BF-NNN`(族伞签,
|
||||
按失败签名去重)。
|
||||
- 不稳定测试(Phase 5.56):label `flaky-test` + `FT-NNN`(同上)。
|
||||
|
||||
## Pattern 4: update-issue
|
||||
|
||||
```
|
||||
gitea_issue__update(owner="Octopus", repo="octopus",
|
||||
index={issue_number}, body="{正文}", state="{open|closed}", ...)
|
||||
```
|
||||
|
||||
原位更新正文(checklist 勾选、live 状态表维护);关闭工单即触发
|
||||
归档动作(见 artifact-addressing.md §4.3 + Pattern 10)。
|
||||
|
||||
## Pattern 5: add-issue-comment
|
||||
|
||||
```
|
||||
gitea_issue_comment__create(owner="Octopus", repo="octopus",
|
||||
index={issue_number}, body="{评论正文}")
|
||||
```
|
||||
|
||||
首次评论后捕获返回的 `comment_id`——后续对同一逻辑评论的更新必须走
|
||||
Pattern 6 原位 edit,绝不再 create。用于:评审综合(Synthesis)、
|
||||
状态备注、TD 登记、claim 认领。
|
||||
|
||||
## Pattern 6: edit-issue-comment
|
||||
|
||||
```
|
||||
gitea_issue_comment__edit(owner="Octopus", repo="octopus",
|
||||
comment_id={comment_id}, body="{新正文}")
|
||||
```
|
||||
|
||||
单评论聚合不变量(工件索引、当前状态表等)的执行手段。
|
||||
|
||||
## Pattern 7: move-issue-to-column
|
||||
|
||||
```
|
||||
gitea_column__move_issue(owner="Octopus", repo="octopus",
|
||||
project_id={project_id}, column_id={column_id}, index={issue_number})
|
||||
```
|
||||
|
||||
看板列迁移(Todo → In Progress → Review → Done)。
|
||||
|
||||
## Pattern 7.5: move-issue-to-pipeline-stage
|
||||
|
||||
管线阶段板列(Pipeline Stages board column)承载阶段迁移——阶段转移
|
||||
落到板列,**不落** `## 当前状态` 行(该表只承载 PR / 评审 / CI 行与
|
||||
非阶段阻塞项)。列序列按管线阶段定义;移动用 Pattern 7 同款
|
||||
`gitea_column__move_issue`,column 由 `gitea_project__list` /
|
||||
`gitea_column__list` 发现。
|
||||
|
||||
## Pattern 8: post-commit-status(REST 回退)
|
||||
|
||||
MCP 工具缺席时用 REST 直发 commit status(评审综合的 Tier-2 落点):
|
||||
|
||||
```bash
|
||||
curl -X POST "<gitea-base-url>/api/v1/repos/Octopus/octopus/statuses/{sha}" \
|
||||
-H "Authorization: token <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"context": "pipeline/review-{stage}",
|
||||
"state": "{success|failure|pending|error}",
|
||||
"target_url": "{html_url}",
|
||||
"description": "{≤140 chars 摘要}"
|
||||
}'
|
||||
```
|
||||
|
||||
context 公式:`pipeline/review-{stage}`(`code` / `review-dag` /
|
||||
`audit-process`)。token 从实例配置读取(此处 `<token>` 占位)。
|
||||
merge 前读回验证:`GET /commits/{PR_SHA}/status`。
|
||||
|
||||
## Pattern 9: create-iteration-board
|
||||
|
||||
```
|
||||
gitea_project__create(owner="Octopus", repo="octopus",
|
||||
title="{slug} — Iteration {N}", description="…")
|
||||
gitea_column__create(owner="Octopus", repo="octopus",
|
||||
project_id={project_id}, title="Todo")
|
||||
# … In Progress / Review / Done 同款
|
||||
```
|
||||
|
||||
DAG 聚合 agent 在单门 PASS 后建板;工单正文模板带 `## Node Reference`
|
||||
(指向 `{epic-slug}/dag`)、`## Acceptance Criteria`、`## Parent`。
|
||||
|
||||
## Pattern 10: artifact-index(工单 ↔ 工件索引)
|
||||
|
||||
技能发布工件后,在源工单维护 **`## 工件索引` 评论**——单一原位编辑的
|
||||
索引(反向链接 + compaction 恢复主路径;语义不变量见
|
||||
`core/rules/artifact-addressing.md` §4):
|
||||
|
||||
```
|
||||
# 1. 找源工单(PR body / commit 的 Closes #N,或 DAG 父映射);无则跳过
|
||||
# 2. 评论已存在?
|
||||
gitea_issue_comment__list(owner="Octopus", repo="octopus", index={issue_number})
|
||||
# → 扫 body 以 "## 工件索引" 开头的评论(遗留前缀 "## Pipeline 工件追踪表"
|
||||
# 原位升级,不重复发)
|
||||
# 3a. 不存在 → gitea_issue_comment__create 初始化
|
||||
# 3b. 存在 → gitea_issue_comment__edit 原位编辑(复用 comment_id)
|
||||
```
|
||||
|
||||
**索引表模板**(每工件一行;技能只增改自己的行,绝不删他技的行):
|
||||
|
||||
## 工件索引
|
||||
|
||||
slug: `{slug}` — source issue #{N}
|
||||
|
||||
| 工件 | 类型 | 版本 | 位置 | 重读 |
|
||||
|------|------|------|------|------|
|
||||
| DAG | 任务图 | v1 (frozen) | [`{epic-slug}/dag`]({html_url}) | CORE |
|
||||
|
||||
**位置列填充规则**:单元格 = markdown 链接 ``[`{page_name}`]({html_url})``;
|
||||
链接文本(page_name,字面 `/`)供读侧 `gitea_wiki__get_page`;href
|
||||
(html_url)供人点击,必须取自 API 响应,严禁拼接。
|
||||
|
||||
**重读优先级**:`CORE` = compaction 后必读(重读集 = 全部 CORE 行);
|
||||
`ON-Demand` → `ON-DEMAND` = 按需;`ARCHIVE` = 已归档不读。
|
||||
|
||||
**归档动作(archive-at-close)**:工单关闭时由关闭方 agent 原位 edit
|
||||
本评论——表格上方加归档横幅(`> **状态**: ✅ 已归档 — issue #{N} 关闭于
|
||||
{date}`)+ 全部行 重读 置 `ARCHIVE`;不删行、不改位置列、不发第二条
|
||||
评论。主路径 verify Phase 5.6;跳过 verify 的路由由关闭 agent 补执行。
|
||||
|
||||
**各技能行映射**:
|
||||
|
||||
| 技能 | 工件 ID | 位置 |
|
||||
| ------------------------- | ---------------------------- | ------------------------------------------------------------ |
|
||||
| `analyze-dag` | `DAG` | `{epic-slug}/dag` |
|
||||
| `review-artifact` | `REVIEW-{stage}` | `{slug}/reviews/{stage}/final/report` |
|
||||
| `review-code` | `REVIEW-code` | `{slug}/reviews/code/final/report` |
|
||||
| `review-code`(DAG task) | `REVIEW-code-task-{node-id}` | `{epic-slug}/reviews/code/final/report-task-{node-id}` |
|
||||
| `verify` | `VERIFY-{N}` | `{slug}/05-verify-iteration-{N}` |
|
||||
| `verify`(milestone) | `VERIFY-M-{M-id}` | `{epic-slug}/05-verify-milestone-{M-id}`(重读 `ON-DEMAND`) |
|
||||
| `verify`(DAG task) | `VERIFY-TASK-{node-id}` | `{epic-slug}/05-verify-task-{node-id}`(重读 `ON-DEMAND`) |
|
||||
@@ -0,0 +1,123 @@
|
||||
# Gitea Adapter — 读模式(Read Patterns)
|
||||
|
||||
> Gitea adapter 参考实现(Increment 3,自 dogfood 源
|
||||
> `_shared/gitea-read-patterns.md` 拆分上提)。页名规范与寻址语义见
|
||||
> `core/rules/artifact-addressing.md`(后端中立契约,本文不重复)。
|
||||
> 实例基址 `<gitea-base-url>` 由实例配置提供,见 TERMINOLOGY.md。
|
||||
|
||||
Owner/repo 固定 `Octopus/octopus`。compaction 恢复从 Pattern 6
|
||||
(读工件索引)开始——源工单上的工件索引是上下文恢复的规范入口,
|
||||
硬编码页名路径仅作回退。
|
||||
|
||||
## Pattern 1: read-wiki-page
|
||||
|
||||
```
|
||||
gitea_wiki__get_page(owner="Octopus", repo="octopus", page_name="{slug}/{page_title}")
|
||||
```
|
||||
|
||||
常用页名(全量文法 + 例外枚举见 `core/rules/artifact-addressing.md` §2):
|
||||
|
||||
| 工件 | page_name |
|
||||
| -------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| 验证报告 | `{slug}/05-verify-iteration-{N}` |
|
||||
| DAG 工件(单门) | `{epic-slug}/dag`;AC 子页 `{epic-slug}/dag-nodes/{node-id}`;覆盖子页 `{epic-slug}/dag-coverage` |
|
||||
| 评审终报(review-dag) | `{epic-slug}/reviews/review-dag/final/report` |
|
||||
| 评审终报(code) | `{slug}/reviews/code/final/report` |
|
||||
| 评审终报(code, DAG task) | `{epic-slug}/reviews/code/final/report-task-{node-id}` |
|
||||
| 验证(DAG milestone) | `{epic-slug}/05-verify-milestone-{M-id}` |
|
||||
| 验证(DAG task) | `{epic-slug}/05-verify-task-{node-id}` |
|
||||
| DAG 共享契约 | `{epic-slug}/shared/{file}` |
|
||||
| bugfix 附件 | `{slug}/repro-notes` · `{slug}/test-report` · `{slug}/bugfix-report` · `{slug}/ABORT` |
|
||||
| 原型/实现笔记 | `{slug}/prototype-debt` · `{slug}/spike-report` · `{slug}/impl-notes` |
|
||||
| 设计修订 | `{slug}/03-design-amendments` |
|
||||
| 审计工件 | `audit/{date}/{page}`,如 `audit/{date}/final/report` |
|
||||
| 移植工件 | `port-{name}/source-analysis/{file}` · `port-{name}/self-check` |
|
||||
| 回顾报告 | `_retrospectives/{cycle-name}` |
|
||||
| 回顾归档 | `_archive/{slug}/` |
|
||||
| 技能评估 | `_evals/{skill-name}/{page}` |
|
||||
| 浏览器调试证据 | `{slug}/verify/evidence/{name}`(临时截图留在工作区截图目录) |
|
||||
|
||||
历史只读页名(旧管线产物仍可读):`{slug}/01-stakeholder-interview`、
|
||||
`{slug}/02-requirements-index`、`{slug}/02-req-{seq:02d}-{title}`、
|
||||
`{slug}/02-03-req-design`、`{slug}/03-design-index`、
|
||||
`{slug}/03-design-{seq:02d}-{title}`、`{slug}/03-adr-{NNNN}-{title}`、
|
||||
`{slug}/04-plan-index`、`{slug}/04-plan-{seq:02d}-{title}`、
|
||||
`{slug}/roadmap/{page}`、`{slug}/shared/{file}`、
|
||||
`{slug}/reviews/{roadmap|design-space|plan}/final/report`。
|
||||
|
||||
**本地回退(wiki MCP 工具不可用)**:读本地 wiki 克隆(实例路径见
|
||||
TERMINOLOGY.md)。wiki 文件名 URL 编码(`/`→`%2F`,`.md` 前可能带
|
||||
`.-`),用 glob 而非固定路径解析:
|
||||
|
||||
```
|
||||
glob(pattern="*{slug}*{page-title}*.md", path="<local-wiki-clone>/{repo}-wiki/")
|
||||
```
|
||||
|
||||
## Pattern 2: read-issue
|
||||
|
||||
```
|
||||
gitea_issue__get(owner="Octopus", repo="octopus", index={issue_number})
|
||||
```
|
||||
|
||||
读工单详情、正文、元数据(label、assignee、milestone、state)。
|
||||
|
||||
## Pattern 3: read-issue-comments
|
||||
|
||||
```
|
||||
gitea_issue_comment__list(owner="Octopus", repo="octopus", index={issue_number})
|
||||
```
|
||||
|
||||
读工单全部评论(评审结果、状态备注、讨论线程)。
|
||||
|
||||
## Pattern 4: read-project-column-issues
|
||||
|
||||
```
|
||||
gitea_column__list_issues(owner="Octopus", repo="octopus",
|
||||
project_id={project_id}, column_id={column_id})
|
||||
```
|
||||
|
||||
看板列工单队列。project/column 发现:`gitea_project__list` +
|
||||
`gitea_column__list`。
|
||||
|
||||
## Pattern 5: read-review-status(CLI)
|
||||
|
||||
```
|
||||
octopus review status [--stage <stage>] [--sha <sha>] [--json]
|
||||
```
|
||||
|
||||
读评审收敛状态(commit status)。stage 值:`code`、`review-dag`、
|
||||
`audit-process`;省略 `--stage` 列出该提交全部状态。历史 stage 值
|
||||
(`design-space` 等)仅可查历史。
|
||||
|
||||
**stage-id → 评审目录映射**(Tier-1 本地 `reviews/{stage}/` 段):
|
||||
`review-code` → `code`;`review-dag` → `review-dag`;
|
||||
`audit-process` → `audit-process`。只有 code 评审用短名,不得发明其他。
|
||||
|
||||
收敛判定:context `pipeline/review-{stage}` 的 status 存在且
|
||||
state = `success`(= PASS 或 WARN,无未决 BLOCKER)。退出码 0 = 全部
|
||||
success,1 = 有 failure/error。
|
||||
|
||||
## Pattern 6: read-artifact-index(compaction 恢复主路径)
|
||||
|
||||
压缩后或新会话从工单种子恢复时,读工件索引评论(写侧 Pattern 10)
|
||||
而非猜测页名路径:
|
||||
|
||||
```
|
||||
gitea_issue__get(owner="Octopus", repo="octopus", index={issue_number})
|
||||
gitea_issue_comment__list(owner="Octopus", repo="octopus", index={issue_number})
|
||||
# 找 body 以 "## 工件索引"(遗留 "## Pipeline 工件追踪表")开头的评论
|
||||
gitea_wiki__get_page(owner="Octopus", repo="octopus", page_name=page_name)
|
||||
```
|
||||
|
||||
**位置列提取规则**:单元格形如 ``[`{page_name}`]({html_url})`` ——
|
||||
取**链接文本**(首个 `[` 与 `]` 之间、剥反引号)作为 page_name,不是
|
||||
href(含 `%2F`/`.-`,API 不认)。裸 page_name 单元格(无链接包裹)也
|
||||
接受:整格即 page_name。形如 `#comment-{id}` 的值改用
|
||||
`gitea_issue_comment__list` 读,不得喂给 `gitea_wiki__get_page`。
|
||||
|
||||
**重读规则**:重读集 = 全部 `CORE` 行;`ON-DEMAND` 行仅当前任务需要
|
||||
时读;绝不读 `ARCHIVE` 行。索引缺失(历史工单)→ 回退 issue body +
|
||||
`## 当前状态` + 硬编码页名路径。
|
||||
|
||||
**已归档索引**:无 `CORE` 行,恢复时重读集为空,按需直读目标工件
|
||||
(位置列 URL 仍有效);归档只改重读优先级,不改可访问性。
|
||||
@@ -0,0 +1,211 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# 流程审计检查清单(Process Audit Checklist)
|
||||
|
||||
> 用于审计 `<instance-root>/` 流程基础设施本身的完整性、一致性与合规性。
|
||||
> 审计对象是这套 SDLC「工厂」本身(skills / checklists / templates / schemas),
|
||||
> 而非某个具体项目对流程的遵循情况(后者归 `retrospective`)。
|
||||
>
|
||||
> 依据:内部一致性规则、ISO 19011:2018(审核指南)、IEEE 1028-2008(软件评审与审计)、
|
||||
> 以及本仓库 `AGENTS.md` 工程约定。
|
||||
|
||||
> **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TRC)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TRC=Traceability vs 本清单的 TRC=Traceability 审计维度)。跨清单引用时必须带清单限定(如 audit-process.md TRC 10.3),不得使用裸代码。
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. 审计对象是 `<instance-root>/` 目录下的 `skills/`、`checklists/`、`templates/`、`schemas/`;
|
||||
2. 每个审计维度由一名独立审计员(Explorer,只读)负责,逐项判定 PASS / FAIL / NA(NA=逐项"不适用"判定标记,不属于 findings JSON 的维度 verdict 枚举 PASS/WARN/FAIL/UNRESOLVED——UNRESOLVED 专指审计员崩溃/超时状态,勿混用);
|
||||
3. 对 FAIL 项必须给出:具体文件位置、引用证据、可执行的修复建议;
|
||||
4. 审计员必须保持**独立性**(ISO 19011:2018):仅依据文件事实判定,不臆测意图;
|
||||
5. 任一维度存在 FAIL 项须修订后重新审计,直至收敛。
|
||||
|
||||
**Known exceptions to INV 1.3** (inline self-check checklists — skills whose
|
||||
checklist obligations are embedded in the SKILL.md body by design):
|
||||
- `core/skills/writing-skills/SKILL.md` §"Authoring checklist" — methodology
|
||||
skill, inline RED/GREEN/REFACTOR self-check is its normative form.
|
||||
- `core/skills/codegraph-setup/SKILL.md` §"Verification checklist" —
|
||||
setup/installation skill, inline verification checklist is its normative form.
|
||||
- `core/skills/project-kickoff/SKILL.md` §"4. Workflow" step 4 (**Post-kickoff verification**) —
|
||||
setup/lifecycle skill; its self-check is the `octopus kickoff --check-only`
|
||||
tool's 5 readiness items, which is the tool-based equivalent of an inline
|
||||
checklist (analogous to codegraph-setup's CLI verification), not a
|
||||
`core/checklists/` artifact checklist.
|
||||
- `core/skills/analyze-dag/SKILL.md` — pipeline Producer (DAG route entry
|
||||
skill). Its producer-side self-check obligations are folded inline into the
|
||||
SKILL.md body (Topology Constraints §, Page-Size Budget, Requirement
|
||||
Registry, Exec-Resource Configuration) as the mechanically-checkable
|
||||
destination of the folded plan-checklist rows; the **gate-side** coverage is
|
||||
`core/checklists/dag-single-gate.md` (TOPO/REQMAP/RELEASE), not a
|
||||
producer self-check checklist. A thin `analyze-dag.md` wrapper would add no
|
||||
value over the in-skill rows + the single-gate checklist.
|
||||
- `core/skills/land-batch/SKILL.md` — merge-pr stage skill (batch PR
|
||||
landing). Its self-check obligations are inline by design: the fail-closed
|
||||
`## Preconditions (all mandatory, fail-closed)` block and the
|
||||
"Pre-validate locally (never enter CI red)" rung are the normative gates,
|
||||
backstopped by CI merge-gate mechanical checks — same doctrine as
|
||||
project-kickoff's tool-based self-check; a thin wrapper checklist would
|
||||
add no value.
|
||||
- Tool/utility & integration skills — `browser-debug`,
|
||||
`gitea-rest`, `image-interpret`, `headless-session-ops` — are
|
||||
non-artifact-producing (they drive tools / sessions / MCP servers, not SDLC
|
||||
pipeline artifacts), so INV 1.3's "产物型 skill" clause does not apply to
|
||||
them; their operational obligations are inline normative statements (e.g.
|
||||
`browser-debug`'s session-cleanup / screenshot-naming iron rules) by the same
|
||||
doctrine as the listed exemptions above.
|
||||
These are exempted per `audit/2026-08-11/round1/revision-summary` (writing-skills,
|
||||
codegraph-setup), `audit/2026-08-13/round1/revision-summary` (project-kickoff),
|
||||
`audit/2026-08-17/round1/revision-summary` (analyze-dag), and
|
||||
`audit/2026-09-01/round1/revision-summary` (land-batch);
|
||||
creating thin wrapper checklist files would add no value.
|
||||
|
||||
---
|
||||
|
||||
## 1. 清单完整性(INV — Inventory Completeness)
|
||||
|
||||
> 流水线阶段齐全、无孤儿文件。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 1.1 | SDLC 流水线各阶段均有对应 skill(analyze-dag→review-dag 单门→implement→review-code→verify→release→retrospective;legacy roadmap/requirements/design/plan 已归档 [org-internal #3072] phase 3,见 `<instance-root>/archive/`) | ☐ | ☐ | |
|
||||
| 1.2 | 每个 `skills/<name>/` 目录恰好包含一个 `SKILL.md`(例外:`_shared/` 目录不含 `SKILL.md`,为共享引用文档目录) | ☐ | ☐ | |
|
||||
| 1.3 | 每个产物型 skill 都有配套 `checklists/<name>.md`(无清单的自检要求即为缺口) | ☐ | ☐ | |
|
||||
| 1.4 | `checklists/` 中无孤儿清单(存在清单但无任何 skill 引用) | ☐ | ☐ | |
|
||||
| 1.5 | `templates/` 中无孤儿模板(存在模板但无任何 skill 引用) | ☐ | ☐ | |
|
||||
| 1.6 | `schemas/` 中无孤儿 schema(存在 schema 但无任何 skill 引用) | ☐ | ☐ | |
|
||||
| 1.7 | 每个 review-* skill 的维度数量与其引用清单的章节数量一致,或存在文档化的合并说明(每节恰好被一个维度覆盖) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 2. 交叉引用完整性(XREF — Cross-Reference Integrity)
|
||||
|
||||
> skill ↔ checklist ↔ template ↔ schema 的引用路径不得断裂。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 2.1 | skill 中引用的每个 `core/checklists/*.md` 路径真实存在 | ☐ | ☐ | |
|
||||
| 2.2 | skill 中引用的每个 `core/templates/*.md` 路径真实存在 | ☐ | ☐ | |
|
||||
| 2.3 | skill 中引用的每个 `core/schemas/*.json` 路径真实存在 | ☐ | ☐ | |
|
||||
| 2.4 | skill 中引用的 wiki `{slug}/...` 路径结构与其它 skill 一致;引用模式见 `_shared/gitea-read-patterns.md` / `_shared/gitea-write-patterns.md` | ☐ | ☐ | |
|
||||
| 2.5 | 清单中引用的标准编号(IEEE/ISO)在 skill References 中亦有呼应 | ☐ | ☐ | |
|
||||
| 2.6 | skill 之间相互引用的前序/后继阶段名称真实存在(无断链) | ☐ | ☐ | |
|
||||
| 2.7 | 引用的检查项编号(如 `ARCH 1.1–1.7`)在目标清单中确实存在 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 元数据合规性(FM — Frontmatter Conformance)
|
||||
|
||||
> 每个 SKILL.md 的 frontmatter 必须符合 octopus skill 规范。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 3.1 | `name` 存在、小写连字符、≤64 字符,且与所在目录名一致 | ☐ | ☐ | |
|
||||
| 3.2 | `description` 存在且非空(缺失会被加载器过滤、永不触发) | ☐ | ☐ | |
|
||||
| 3.3 | `description` 同时说明「做什么」与「何时触发」 | ☐ | ☐ | |
|
||||
| 3.4 | `description` 使用第三人称("Use when...",而非 "I help...") | ☐ | ☐ | |
|
||||
| 3.5 | 需要静默于相邻话题的 skill 使用了 "Use ONLY when..." 或 "Use ONLY after..." 限定 | ☐ | ☐ | |
|
||||
| 3.6 | `triggers`(如有)为关键词/文件名,前置了用户可能说出的字面词 | ☐ | ☐ | |
|
||||
| 3.7 | frontmatter 无未知顶层字段,YAML 可解析 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 命名约定一致性(NAM — Naming Convention)
|
||||
|
||||
> 跨流程基础设施的命名必须统一。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 4.1 | 清单文件名使用产物名词形式(如 `implementation.md`、`code-review.md`),skill 名使用动词形式或标准 SDLC 产物名(如 `implement`、`review-code`;`frontend`、`writing-skills` 等按产物/领域命名的 skill,以及按工具命名的集成类 skill(`gitea-rest`、`codegraph-setup`)除外),共享清单以产物名命名(如 `verification.md`、`port.md`;已归档的 legacy 清单见 `<instance-root>/archive/checklists/`) | ☐ | ☐ | |
|
||||
| 4.2 | 维度代码(如 ARCH / SEC / TRC)在 skill 与清单间拼写一致 | ☐ | ☐ | |
|
||||
| 4.3 | 角色名称与 core/skills/_shared/roles/*.yaml 的 name 字段一致(Producer/Reviewer/Verifier/Tool/Coordinator)。旧角色名(Developer/Analyst/Architect 等)由 role.ts 中的 ROLE_ALIASES 安全映射,不再需要独立 YAML。不得使用已废弃的角色名(如 'Organizer')。`escalation` 字段除可指向上述注册角色外,亦可指向有效的 subagent_type(如 'Builder',其作为运行时可加载的构建型子代理类型) | ☐ | ☐ | |
|
||||
| 4.4 | 产物路径片段(wiki `{slug}/...`)命名跨 skill 统一;引用模式见 `_shared/gitea-read-patterns.md` / `_shared/gitea-write-patterns.md`。审计类产物使用日期 slug(如 wiki `audit/{date}/`)属允许例外 | ☐ | ☐ | |
|
||||
| 4.5 | 严重度等级(BLOCKER/MAJOR/MINOR/INFO)跨 skill 与 schema 一致 | ☐ | ☐ | 允许例外:`port-analysis.schema.json` 使用三值契约(BLOCKER/MAJOR/MINOR,永不产出 INFO),已在 schema description 中自证为对四值集的文档化例外 |
|
||||
| 4.6 | 裁决值(PASS/WARN/FAIL)跨 skill 与 schema 一致 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 流水线衔接(FLOW — Pipeline Cohesion)
|
||||
|
||||
> 阶段之间的前置条件与产物链必须闭合,无断裂。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 5.1 | 每个 skill 的 Preconditions 引用的前序产物,确由某个上游 skill 产出 | ☐ | ☐ | |
|
||||
| 5.2 | 每个 skill 的产物,确被某个下游 skill 作为输入消费(终点除外:verify、release、retrospective,以及发布/副作用类 skill 如 `review-artifact` (target: process),其产出由人类或外部系统消费) | ☐ | ☐ | |
|
||||
| 5.3 | review-* skill 的输入路径与其对应生产 skill 的输出路径精确匹配(代码评审输入为项目源码树中 git diff 标识的文件;其余评审输入为 wiki `{slug}/` 下的文档,读取模式见 `_shared/gitea-read-patterns.md`;process 审计目标为第三输入类——本地 `<instance-root>/` 语料,无上游生产 skill,自产自审) | ☐ | ☐ | |
|
||||
| 5.4 | 收敛/审批关卡(如「Do NOT advance without approval」)在阶段切换处存在 | ☐ | ☐ | |
|
||||
| 5.5 | 阶段顺序无环(不存在 A 依赖 B 同时 B 依赖 A) | ☐ | ☐ | |
|
||||
| 5.6 | 每个 skill 声明的角色与模型分配在同类 skill 间一致 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 6. Schema 契约(SCH — Schema Contract)
|
||||
|
||||
> schema 被正确引用,且 skill 描述的字段与 schema 定义对齐。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 6.1 | skill 中要求写入的 JSON 产物均指明了对应的 `core/schemas/*.json` | ☐ | ☐ | 允许例外:(a) `precondition-gate.jsonl`(review-code/verify)为自版本化、机器可再生的 JSONL 记录,有意无 backing schema;(b) browser-debug 证据包(`browser/{session-id}/manifest.json` 等)由冻结共享契约 `browser-evidence-4486/shared/pack-manifest-v1` 等校验(写入方 `<harness-package>/src/browser/evidence-pack.ts`),不在 `core/schemas/` 下 |
|
||||
| 6.2 | skill 文中描述的字段名与 schema 的 `required`/`properties` 一致 | ☐ | ☐ | |
|
||||
| 6.3 | skill 描述的枚举值(verdict/severity)与 schema `enum` 一致 | ☐ | ☐ | |
|
||||
| 6.4 | 每个 schema 自身合法(`$schema`、`$id`、`type` 齐备) | ☐ | ☐ | |
|
||||
| 6.5 | 多个 skill 共用同一 schema 时语义一致(无相互冲突的字段约定) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 7. 重复与漂移(DUP — Duplication & Drift)
|
||||
|
||||
> 单一事实来源;术语不漂移。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 7.1 | 同一概念在不同 skill 中术语一致(无同义词漂移) | ☐ | ☐ | |
|
||||
| 7.2 | 检查项/规则无跨文件的实质性重复(应集中于清单而非散落于 skill) | ☐ | ☐ | |
|
||||
| 7.3 | 收敛准则(max_rounds、停止条件)在各 review-* skill 间一致或有理由不一致 | ☐ | ☐ | |
|
||||
| 7.4 | 标准编号引用一致(同一标准不出现多种写法) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 8. 审计原则合规(STD — Audit Standards / ISO 19011:2018 · IEEE 1028-2008)
|
||||
|
||||
> 体现独立性、客观证据、分级判定、可重复。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 8.1 | 审计员独立性:review-* skill 明确审计员只读、不得越权(ISO 19011:2018 §4) | ☐ | ☐ | |
|
||||
| 8.2 | 客观证据:findings 要求 location + evidence(IEEE 1028-2008 证据留痕) | ☐ | ☐ | |
|
||||
| 8.3 | 分级判定:严重度与裁决规则量化(pass_rate 阈值明确,非主观) | ☐ | ☐ | |
|
||||
| 8.4 | 可重复:审计员 prompt 标准化、温度低(确定性输出) | ☐ | ☐ | |
|
||||
| 8.5 | 防范范围收窄:明令禁止「只看重点/从简」类弱化措辞(IEEE 1028-2008 完整性) | ☐ | ☐ | |
|
||||
| 8.6 | 留痕:审计产物落盘到结构化文件,而非仅存于对话 | ☐ | ☐ | |
|
||||
| 8.7 | 闭环:存在重审循环与收敛/审批终止条件(ISO 19011:2018 跟踪与关闭) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 9. 工程约定符合度(AGT — AGENTS.md Conformance)
|
||||
|
||||
> 流程基础设施所描述/示例的工程做法须符合本仓库 `AGENTS.md` 及其**委托的约定源**:
|
||||
> `AGENTS.md` 将工程约定委托给 L1 `core/rules/*`(注入主会话)与 L2 wiki `rules/*`
|
||||
> (按需读取),故本维各项的「基线」是 `AGENTS.md` 委托到的实际约定文档(下各条注明)。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 9.1 | skill 示例命令与测试约定一致(基线:`core/rules/testing.md` — `bun run test:parallel`、从 package 目录运行、勿从 repo 根运行) | ☐ | ☐ | |
|
||||
| 9.2 | skill 示例的类型检查命令与 `core/rules/type-checking.md` 一致(package 目录 `bun typecheck`,非直接 `tsc`;repo 根整仓为 `bun turbo typecheck`) | ☐ | ☐ | |
|
||||
| 9.3 | skill 引用的 DB/迁移流程与 L2 wiki `rules/database`(Drizzle、`bun run db generate`)一致 | ☐ | ☐ | |
|
||||
| 9.4 | skill 描述的模块形态与 L2 wiki `rules/style-guide`(无 `export namespace`、自再导出)一致 | ☐ | ☐ | |
|
||||
| 9.5 | skill 描述的 Effect 用法与 L2 wiki `rules/effect-rules` 一致(如适用) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 10. 可追溯性(TRC — Traceability)
|
||||
|
||||
> 从需求到验证全链路可追溯;审计自身亦可追溯。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 10.1 | 存在贯穿全流水线的追溯机制(需求 ID → 设计 → 实现 → 验证) | ☐ | ☐ | |
|
||||
| 10.2 | 每个 review-* skill 都包含 Traceability 维度或等价检查 | ☐ | ☐ | |
|
||||
| 10.3 | 审计 finding 的 ID 规则唯一且可定位到具体检查项 | ☐ | ☐ | |
|
||||
| 10.4 | 审计历史(轮次、裁决、blocker/major 数)被记录于 status 产物 | ☐ | ☐ | |
|
||||
| 10.5 | 模型/角色分配变更可在 skill 中追溯(同类 skill 横向可比) | ☐ | ☐ | |
|
||||
@@ -0,0 +1,85 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Bugfix 检查清单
|
||||
|
||||
> 开发者在修复 Bug 时自检使用。确保修复的是根因而非症状,且不引入新问题。
|
||||
|
||||
> **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TST/BEH/GATE/REG)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TST=Test Quality、verification.md 的 TST=Test Suite Completeness、port.md 的 TST=Test Porting)。跨清单引用时必须带清单限定(如 code-review.md TST 5.8),不得使用裸代码。
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. Phase 1-4 每阶段结束时检查对应项;
|
||||
2. Phase 5 完成后检查全部项;
|
||||
3. 所有项通过后方可提交。
|
||||
|
||||
---
|
||||
|
||||
## 1. 复现确认(REPRO — Reproduction)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------- | ---- | ------ | ---- |
|
||||
| 1.1 | Bug 可以稳定复现(有明确的复现步骤) | ☐ | ☐ | |
|
||||
| 1.2 | 实际行为与 Bug 报告一致 | ☐ | ☐ | |
|
||||
| 1.3 | 如果无法复现,已向用户反馈原因并等待补充信息 | ☐ | ☐ | |
|
||||
| 1.4 | 已捕获环境快照(`ps aux`、`lsof -p PID`、日志文件大小、进程树 — pipeline 模式强制,standalone 建议) | ☐ | ☐ | |
|
||||
| 1.5 | 复现置信度评级 1–5(≥3 方可进入 pipeline 设计阶段;<3 → abort) | ☐ | ☐ | |
|
||||
| 1.6 | 已检索 Gitea wiki `{slug}/repro-notes` / `{slug}/bugfix-report` 中是否存在同类症状的先前调查报告,复用已知根因而非重新推导(via `wiki 读写 API(见 TERMINOLOGY)`) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 2. 根因分析(ROOT — Root Cause)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------ | ---- | ------ | ---- |
|
||||
| 2.1 | 根因已定位到具体代码行(非模块级别) | ☐ | ☐ | |
|
||||
| 2.2 | 根因是底层逻辑缺陷,非表面症状 | ☐ | ☐ | |
|
||||
| 2.3 | 如果根因来自数据/环境/配置,已注明具体差异 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 回归测试(TEST — Regression Test)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 3.1 | 已编写针对性的回归测试 | ☐ | ☐ | |
|
||||
| 3.2 | 回归测试在修复前 FAIL(确认覆盖了 Bug) | ☐ | ☐ | |
|
||||
| 3.3 | 回归测试在修复后 PASS | ☐ | ☐ | |
|
||||
| 3.4 | 如果无法编写自动化测试,已标注 `[flaky]` 并说明原因 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 修复质量(FIX — Fix Quality)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------- | ---- | ------ | ---- |
|
||||
| 4.1 | 修复是最小变更——只改了必须改的部分 | ☐ | ☐ | |
|
||||
| 4.2 | 未混入重构、风格调整或不相关的"顺手改" | ☐ | ☐ | |
|
||||
| 4.3 | 修复的是根因而非症状 | ☐ | ☐ | |
|
||||
| 4.4 | 同一模块的已有测试全部通过(修复未引入退化) | ☐ | ☐ | |
|
||||
| 4.5 | Commit 消息使用常规提交前缀(`fix:` / `refactor:` / `docs:`)并位于 `[{chunk-id}][{iteration}]` 之后 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 全面回归(REG — Full Regression)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------- | ---- | ------ | ---- |
|
||||
| 5.1 | `bun run test:parallel` 全部通过(零失败) | ☐ | ☐ | |
|
||||
| 5.2 | `bun typecheck` 无错误 | ☐ | ☐ | |
|
||||
| 5.3 | `bun lint` 无错误 | ☐ | ☐ | |
|
||||
| 5.4 | 如果有集成/端到端测试,已运行并全部通过 | ☐ | ☐ | |
|
||||
| 5.5 | 移交评审前已按 `core/checklists/code-review.md` 的 A11Y(§8)与 PERF(§4)节自检本次变更,并对全部改动文件跑 `bunx prettier --check`(pre-commit 钩子只扫提交时的 staged 文件,CR-COMMIT 把提交推迟到评审收敛后,移交前的工作区漂移没有任何钩子拦截——出处:sticky-diff-error 复盘 action item 2) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 6. 闭环(CLOSURE — Fix Linked to Commit)
|
||||
|
||||
> 防止"幽灵 artifact":bugfix 报告已写出但代码修复从未提交/合并,导致 bug
|
||||
> 复发并重复调查。参见 `core/skills/retrospective/SKILL.md` 了解该规则的
|
||||
> 复盘出处。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------- | ---- | ------ | ---- |
|
||||
| 6.1 | standalone bugfix 的 `{slug}/bugfix-report` 必须在同一周期内有**已合并提交**背书;若仅调查未应用修复,必须显式标注 "investigation only — no fix applied" | ☐ | ☐ | |
|
||||
| 6.2 | 若本次修复取代了先前的同类 `{slug}/bugfix-report`,已在该先前报告顶部标注 SUPERSEDED 并指向新 PR | ☐ | ☐ | |
|
||||
@@ -0,0 +1,198 @@
|
||||
# 代码评审检查清单
|
||||
|
||||
> 用于评审代码的正确性、设计一致性、安全性和可维护性。
|
||||
> 依据 IEEE 1028-2008 和行业最佳实践。
|
||||
|
||||
> **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TST/BEH/GATE/REG)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TST=Test Quality、verification.md 的 TST=Test Suite Completeness、port.md 的 TST=Test Porting)。跨清单引用时必须带清单限定(如 code-review.md TST 5.8),不得使用裸代码。
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. 阅读待评审的全部代码文件;
|
||||
2. 同时阅读设计文档中的相关章节作为参考基线;
|
||||
3. 逐项检查,判定为"通过"或"不通过";
|
||||
4. 对"不通过"项注明具体文件名、行号和问题描述;
|
||||
5. 任一检查维度中若存在"不通过"项,须修订后重新评审;
|
||||
6. 所有维度均通过后方可合并或发布。
|
||||
7. 第 2 轮及以后的评审中,任何仅属外观性的 MINOR 发现(命名、注释、死代码、导入顺序、风格等不影响正确性或可读性的问题)必须满足其一:已修复并关闭,或在 synthesis 中以 `WAIVED-{id}` 条目显式豁免并注明原因。`WAIVED-{id}` 约定的规范定义见 `.octopus/skills/_shared/review-revision-prompt.md`(本清单的豁免标记形如 `WAIVED-COR-R2-001`)。
|
||||
8. **CR-COMMIT 门禁**:代码评审收敛(所有维度 PASS)后,必须将所有评审修订提交到工作分支,再进入 verify 阶段。未提交的评审修订不得通过 verify 放行。验证阶段(verify)启动前必须确认 `git status` 无未提交修改。
|
||||
9. **standalone-bugfix 模式的预先存在模式豁免**:在 standalone-bugfix 模式下,修复的范围应以最小化外科手术为原则。若 COR/STY/DOC 维度的发现指向的是**修复前已存在的模式**(并非本次变更引入),且该模式与文件中已有代码保持一致,则 `WAIVED` 或 `ACCEPTED_RISK` 是有效的处理方式。具体适用场景:
|
||||
- COR 1.9/1.10/1.13:错误处理策略在变更前已采用同等模式(如 `console.warn` 而非面向用户的错误提示、无超时策略等),本次变更未使其劣化
|
||||
- STY 6.8:文件/模块长度超过 200 行属于变更前已有的结构,拆分为独立重构任务(非本次 bugfix 范畴)
|
||||
- DOC 9.5:注释中的默认值初始化(如 `lastTriggerScrollTop = -1`)为防御性编程,在观察者建立前即被覆盖,无行为影响
|
||||
每次豁免必须在 synthesis 中以带编号的 `WAIVED-{id}` 或 `ACCEPTED_RISK` 条目记录原因。此豁免不影响其他维度的评审标准。
|
||||
|
||||
---
|
||||
|
||||
## 1. 正确性、错误处理与兼容性检查(COR — Correctness, Error Handling & Compatibility)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | ------------------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 1.1 | 逻辑分支完整,无遗漏的 if/else、switch case 或漏处理的枚举值 | ☐ | ☐ | |
|
||||
| 1.2 | 边界条件正确处理(空值、空集合、零值、负值、最大值/最小值) | ☐ | ☐ | |
|
||||
| 1.3 | null/undefined 在使用前已检查,无空引用风险 | ☐ | ☐ | |
|
||||
| 1.4 | 异步操作正确使用 await 或 Promise 链,无竞态条件 | ☐ | ☐ | |
|
||||
| 1.5 | 循环有正确的终止条件,无无限循环风险 | ☐ | ☐ | |
|
||||
| 1.6 | 类型转换安全(如字符串转数字、JSON 解析),有失败处理 | ☐ | ☐ | |
|
||||
| 1.7 | 无逻辑死区(unreachable code)或死代码(dead code) | ☐ | ☐ | |
|
||||
| 1.8 | 所有可能失败的操作(I/O、网络、解析、数据库)有错误处理 | ☐ | ☐ | |
|
||||
| 1.9 | 错误信息对用户友好(不暴露内部堆栈、路径或 SQL) | ☐ | ☐ | |
|
||||
| 1.10 | 外部服务调用有超时、重试和熔断策略(与设计文档一致) | ☐ | ☐ | |
|
||||
| 1.11 | 事务边界明确,异常时回滚,无部分提交 | ☐ | ☐ | |
|
||||
| 1.12 | 错误码和 HTTP 状态码语义正确(不把 500 当 400 用) | ☐ | ☐ | |
|
||||
| 1.13 | 异常被捕获且传播到合适的层级,无被吞掉的异常 | ☐ | ☐ | |
|
||||
| 1.14 | 异步错误(Promise rejection、EventEmitter error)有处理器 | ☐ | ☐ | |
|
||||
| 1.15 | 公共 API 的签名、参数和返回值未做不兼容变更(或已标注 breaking) | ☐ | ☐ | |
|
||||
| 1.16 | 配置项的新增/删除/改名有迁移路径或向后兼容处理 | ☐ | ☐ | |
|
||||
| 1.17 | 客户端(前端/移动端/SDK)与后端接口版本兼容 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计一致性与依赖检查(DGN — Design Compliance & Dependencies)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | ------------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 2.1 | 代码实现了设计文档中该组件的全部指定接口和方法签名 | ☐ | ☐ | |
|
||||
| 2.2 | 组件职责与设计文档中声明的职责一致,无越界逻辑 | ☐ | ☐ | |
|
||||
| 2.3 | 组件依赖关系与设计文档的依赖图一致,无反向依赖 | ☐ | ☐ | |
|
||||
| 2.4 | 数据模型(字段、类型、关系)与设计文档的数据设计一致 | ☐ | ☐ | |
|
||||
| 2.5 | 接口输入输出的 Schema 与设计文档的接口设计一致 | ☐ | ☐ | |
|
||||
| 2.6 | 代码未引入设计文档中未提及的新依赖或新服务 | ☐ | ☐ | |
|
||||
| 2.7 | 架构模式(工厂、策略、仓储等)的使用方式与设计决策一致 | ☐ | ☐ | |
|
||||
| 2.8 | 若代码偏离设计,有明确的 ADR 或注释说明理由 | ☐ | ☐ | |
|
||||
| 2.9 | 已用 `graph usages`/`impact` 核验改动符号的所有调用方与传递影响均已处理,无遗漏的破坏性变更(评审时用代码图核对,而非 grep 逐文件追踪) | ☐ | ☐ | |
|
||||
| 2.10 | 实现文件结构与迭代计划中的架构描述一致(文件数、模块划分、依赖方向)。若偏离(如单文件合并替代多文件架构),迭代计划已更新或偏离理由于设计文档中标明 | ☐ | ☐ | |
|
||||
| 2.11 | 新增依赖有明确的技术理由(不在审查时追问"为什么需要它") | ☐ | ☐ | |
|
||||
| 2.12 | 依赖版本已锁定(package-lock.json / bun.lock / 等同文件已更新) | ☐ | ☐ | |
|
||||
| 2.13 | 无已知漏洞的依赖版本(依据 CVE 数据库或 `npm audit` 等同检查) | ☐ | ☐ | |
|
||||
| 2.14 | 无引入未使用的依赖 | ☐ | ☐ | |
|
||||
| 2.15 | 许可证兼容,无 GPL/AGPL 等强传染性许可证引入到非 GPL 项目 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 安全性检查(SEC — Security)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | ----------------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 3.1 | 用户输入在执行 SQL/命令/HTML 前已参数化或转义 | ☐ | ☐ | |
|
||||
| 3.2 | 身份认证逻辑无绕过路径,会话令牌安全生成和验证 | ☐ | ☐ | |
|
||||
| 3.3 | 授权检查在关键操作前执行,无 IDOR(越权访问)风险 | ☐ | ☐ | |
|
||||
| 3.4 | 敏感数据(密码、令牌、密钥、PII)不在日志、错误消息或响应中泄露 | ☐ | ☐ | |
|
||||
| 3.5 | 加密算法为业界推荐标准(无 MD5/SHA1/DES 用于安全目的),密钥管理合规 | ☐ | ☐ | |
|
||||
| 3.6 | 输入校验在服务端执行(不依赖客户端校验) | ☐ | ☐ | |
|
||||
| 3.7 | 无硬编码的凭据、密钥、内网地址或环境特定值 | ☐ | ☐ | |
|
||||
| 3.7.1 | 暂存区/提交无符号链接,无机器本地绝对路径引用(`git diff --cached --diff-filter=T` 为空)| ☐ | ☐ | |
|
||||
| 3.8 | 安全相关配置项(CORS、CSP、速率限制)符合设计文档安全章节 | ☐ | ☐ | |
|
||||
| 3.9 | SAST 结论已核对:PR 存在时引用同 SHA 的 CI SAST 结果(`sast.yml`,无新增 HIGH/CRITICAL 发现);无 PR 或同 SHA 无扫描结果时标注 `UNVERIFIABLE-LOCAL`,留待 verify Phase 2.5 处理。评审员为只读权限,不自行运行扫描工具 | ☐ | ☐ | |
|
||||
| 3.10 | 针对 CI SAST 报告的 HIGH 发现:True Positive 已修复或路由 Developer;False Positive 已标注排除理由(引用 `sast.yml` 产物,不重复推导) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 性能检查(PERF — Performance)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | ------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 4.1 | 循环内无同步 I/O 或数据库查询(N+1 问题) | ☐ | ☐ | |
|
||||
| 4.2 | 算法复杂度合理(无 O(n²) 或更高在主路径中,除非设计明确接受) | ☐ | ☐ | |
|
||||
| 4.3 | 资源(连接、文件句柄、缓冲区)在使用后释放,无泄漏风险 | ☐ | ☐ | |
|
||||
| 4.4 | 查询使用了索引,EXPLAIN 计划与设计文档索引设计一致 | ☐ | ☐ | |
|
||||
| 4.5 | 适度使用缓存和批处理,无过早优化但也无非受控的重复计算 | ☐ | ☐ | |
|
||||
| 4.6 | 异步操作非阻塞,长耗时操作用队列或后台任务处理 | ☐ | ☐ | |
|
||||
| 4.7 | 前端:列表子项有稳定 key,事件处理器引用稳定,无不必要的重渲染 | ☐ | ☐ | |
|
||||
| 4.8 | 前端:大列表使用虚拟滚动或分页,非首屏组件使用代码分割(lazy) | ☐ | ☐ | |
|
||||
| 4.9 | 前端:图片有懒加载(`loading="lazy"`)和尺寸占位,无布局偏移 | ☐ | ☐ | |
|
||||
| 4.10 | 前端:`useMemo`/`useCallback`/`computed`/`derived` 使用合理,无不必要的计算 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 测试质量检查(TST — Test Quality)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 5.1 | 新增代码有对应的测试(单元或集成),覆盖主要路径和分支 | ☐ | ☐ | |
|
||||
| 5.2 | 测试覆盖了边界条件(空值、极值、异常路径) | ☐ | ☐ | |
|
||||
| 5.3 | 测试断言验证了具体行为,非仅"不抛异常"或"返回非空" | ☐ | ☐ | |
|
||||
| 5.4 | 测试相互独立,可任意顺序运行,无共享可变状态 | ☐ | ☐ | |
|
||||
| 5.5 | Mock/Stub 的使用合理,模拟的外部行为与真实行为一致 | ☐ | ☐ | |
|
||||
| 5.6 | 测试命名清晰表达了测试场景和预期结果 | ☐ | ☐ | |
|
||||
| 5.7 | 本次变更涉及的测试已通过(`test:changed`,由 mechanical-green gate 机器判定,评审员引用 gate 结果即可)。全量套件(`test:parallel`)归 verify,不是代码评审门禁([org-internal #2598] 检查分层) | ☐ | ☐ | |
|
||||
| 5.8 | 评审发现的"dummy fixture / harness 默认值漂移"类问题,不能仅凭 `WAIVED` 处置:若漂移值是 harness 对**生产 CLI/配置默认值**的镜像(如 harness 旧 owner 默认 vs CLI 新 owner 默认这类 token 漂移),WAIVED 会把真实漂移放行到 post-merge([org-internal #3169] TST-F002 → 后续 [org-internal #3172] 才修)。处置前确认该值是否被生产路径读取;被读取则必须 FIX 或显式登记 TD | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 6. 风格与约定检查(STY — Style & Convention)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 6.1 | 命名遵循项目约定(函数名、变量名、文件名、目录结构) | ☐ | ☐ | |
|
||||
| 6.2 | 缩进、空格、引号、分号、行长度与项目格式化配置一致 | ☐ | ☐ | |
|
||||
| 6.3 | 函数/方法长度合理(通常 ≤ 50 行),单一职责 | ☐ | ☐ | |
|
||||
| 6.4 | 导入语句有序分组(第三方、内部、相对),无未使用的导入 | ☐ | ☐ | |
|
||||
| 6.5 | 无注释掉的代码块(应删除或用版本控制追溯) | ☐ | ☐ | |
|
||||
| 6.6 | 类型声明充分,无不必要的 `any` 或隐式类型 | ☐ | ☐ | |
|
||||
| 6.7 | Lint 零错误已由机器判定(mechanical-green gate 与 CI 的 `bun oxlint --deny-warnings`);评审员引用 gate/CI 结果即可,只复核 lint 覆盖不到的约定项(6.1–6.6、6.8),不重复人工推导 | ☐ | ☐ | |
|
||||
| 6.8 | 文件/模块长度 ≤ 200 行(超出需拆分)。模块过长降低可维护性和 review 效率 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 7. 数据库与数据检查(DBT — Database & Data)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 7.1 | 数据库迁移可安全回滚(down migration 正确且与 up 对称) | ☐ | ☐ | |
|
||||
| 7.2 | 查询使用参数化,无拼接 SQL 字符串 | ☐ | ☐ | |
|
||||
| 7.3 | 索引使用与设计文档的索引设计对应,EXPLAIN 计划合理 | ☐ | ☐ | |
|
||||
| 7.4 | 事务范围最小化(不在事务内执行外部调用或长计算) | ☐ | ☐ | |
|
||||
| 7.5 | 数据库连接生命周期正确,连接池配置合理 | ☐ | ☐ | |
|
||||
| 7.6 | 无大规模数据迁移导致锁表风险,大表变更方案已说明 | ☐ | ☐ | |
|
||||
| 7.7 | 数据库 Schema 变更对已有数据向后兼容(新字段允许 NULL 或有默认值) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 8. 可访问性与浏览器兼容性检查(A11Y — Accessibility & Browser Compatibility)
|
||||
|
||||
> 适用于所有面向用户的前端代码。确保 UI 可被各类用户(包括使用辅助技术者)正常使用。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | ----------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 8.1 | 交互元素使用原生语义 HTML(`<button>`/`<a>`/`<input>` 而非 `<div>` 模拟) | ☐ | ☐ | |
|
||||
| 8.2 | 图片有有意义的 `alt` 文本;纯装饰性图片使用 `alt=""` 或 CSS background | ☐ | ☐ | |
|
||||
| 8.3 | 表单控件有关联的 `<label>` 元素(非仅 placeholder) | ☐ | ☐ | |
|
||||
| 8.4 | 所有交互元素可通过键盘访问和操作(Tab 聚焦,Enter/Space 激活,方向键导航) | ☐ | ☐ | |
|
||||
| 8.5 | 模态框/弹窗:打开时焦点移入首元素,关闭时焦点回退触发按钮,ESC 可关闭 | ☐ | ☐ | |
|
||||
| 8.6 | 动态内容更新有 `aria-live` 通知(如搜索结果显示数、表单错误提示) | ☐ | ☐ | |
|
||||
| 8.7 | 颜色对比度合规:正文 ≥ 4.5:1,大文本 ≥ 3:1,非仅靠颜色传达信息 | ☐ | ☐ | |
|
||||
| 8.8 | 页面标题层级(`h1`→`h2`→`h3`)有逻辑层次,无跳级 | ☐ | ☐ | |
|
||||
| 8.9 | `aria-label`/`aria-labelledby` 为仅图标按钮、导航区提供了屏幕阅读器标签 | ☐ | ☐ | |
|
||||
| 8.10 | 焦点指示器可见(`:focus-visible`),无 `outline: none` 但未提供替代样式 | ☐ | ☐ | |
|
||||
| 8.11 | a11y 自动化扫描(`a11y.yml` / axe-core)结果已引用:PR 命中触发路径时以同 SHA CI 结论为准(无新增违规项),评审员不重复运行;语义性条目(8.1–8.10)仍由评审判断 | ☐ | ☐ | |
|
||||
| 8.12 | 前端:目标浏览器(Chrome/Firefox/Safari/Edge 最近 2 个主版本)下功能正常 | ☐ | ☐ | |
|
||||
| 8.13 | 前端:CSS 特性(Grid/Flexbox/Custom Properties)在目标浏览器均有支持或降级方案 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 9. 文档检查(DOC — Documentation)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 9.1 | 复杂算法、非常规优化或非直觉的逻辑有解释性注释 | ☐ | ☐ | |
|
||||
| 9.2 | 对外 API 的接口文档已同步更新(参数、返回值、错误码) | ☐ | ☐ | |
|
||||
| 9.3 | 面向用户的错误消息清晰、可操作(不应是"系统错误,请重试") | ☐ | ☐ | |
|
||||
| 9.4 | README / runbook / 运维文档如有必要已更新(如新增配置项) | ☐ | ☐ | |
|
||||
| 9.5 | 注释与代码一致(代码已改但注释未改视为文档错误) | ☐ | ☐ | |
|
||||
| 9.6 | 废弃的 API/配置有 `@deprecated` 标记和替代方案说明 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 10. 可追溯性完整性检查(TRC — Traceability Completeness)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | ------------------------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 10.1 | 每次代码修改的 commit/PR 描述包含对应的工作项 ID | ☐ | ☐ | |
|
||||
| 10.2 | 每个验收标准的实现均有对应的自动化测试用例 | ☐ | ☐ | |
|
||||
| 10.3 | 无验收标准遗漏测试或手动验证路径(全部覆盖) | ☐ | ☐ | |
|
||||
| 10.4 | 代码修改范围未超出工作项定义(无"顺便修"的无关变更) | ☐ | ☐ | |
|
||||
| 10.5 | 新增的需求引用(REQ-F-{NNN} / REQ-NF-{NNN})在代码中有对应实现 | ☐ | ☐ | |
|
||||
| 10.6 | 已删除/弃用的代码有明确的移除原因和替代方案说明 | ☐ | ☐ | |
|
||||
| 10.7 | 测试报告能按工作项 ID 筛选(测试与工作项可交叉引用) | ☐ | ☐ | |
|
||||
@@ -0,0 +1,59 @@
|
||||
# 单门三维审查 checklist(review-dag)
|
||||
|
||||
> 单门 `review-dag` 替代 `review-design-space` + `review-iteration-plan` 双门(spec-02 §1 D-02)。
|
||||
> 门内分层三维:**TOPO**(拓扑)、**REQMAP**(需求映射)、**RELEASE**(滚动放行)。
|
||||
> 省的是编排开销,不省修订循环——三维各审独立风险面。
|
||||
>
|
||||
> **权威源**:本文件判据逐行复制 `dag-pipeline/spec-04` §1 三张表
|
||||
> (含 `NFR:` 前缀 / 里程碑边 / 里程碑节点豁免与 `estimated_sessions` 判据)。
|
||||
> 若本文件与 spec-04 §1 不一致,以 spec-04 §1 为唯一权威源。
|
||||
> (`spec-0N` 前身为 `dag-pipeline/03-design-0N-*`,2026-08-21 [org-internal #3072] phase 3
|
||||
> 升格迁移,旧名仅保留墓碑占位以维持旧 URL 可达。)
|
||||
|
||||
## TOPO — 拓扑维
|
||||
|
||||
| 检查项 | 判据 |
|
||||
|---|---|
|
||||
| 环检测 | 边方向图无环;有环 = BLOCKER |
|
||||
| 依赖正确性 | 每条边 `from→to` 方向正确(契约生产方先于消费方);缺边/错边 = MAJOR |
|
||||
| 大小均匀性 | 节点工作量分布均匀,无"巨型节点";巨型节点 = 单个 task 节点 `size_attrs.estimated_sessions ≥ 2` → MAJOR(需拆分;INFO 豁免不适用) |
|
||||
| **粒度下限** | 每个 task 节点预估实现时长由 `size_attrs.estimated_hours`/`estimated_sessions` 承载(冻结字段,见 spec-02 §2.1);判据一律以 `estimated_sessions` 为准(两字段一致性不变量见 spec-02 §2.1,完整分档见 spec-03 §3 规则 3):字段不一致(`|estimated_hours − 8 × estimated_sessions| > 2`)= MAJOR(需 `analyze-dag` 重填);`estimated_sessions < 0.25`(对应约 `estimated_hours < 2`)= 低于下限(MAJOR,≥3 处 = BLOCKER);`1 < estimated_sessions < 2` = 超出上限(MINOR,提示拆分;连续集成性工作不可拆分则 INFO);`estimated_sessions ≥ 2` = 巨型节点 → MAJOR(需拆分,见「大小均匀性」行,INFO 豁免不适用)(防工单元数据成本爆炸) |
|
||||
| 里程碑位置 | 每个 `cross_session_in ≥ 2` 汇聚点已焊入里程碑(spec-03 规则 2);缺失 = BLOCKER |
|
||||
| 可执行性 | 每个 task 节点可被单个 session 独立实现(EXE 折叠项;里程碑节点无实现工作、由 verify 承担,不参与本判据) |
|
||||
| 页尺寸自检信号 | 读 `{epic-slug}/dag` 页首 `> 页尺寸自检: 超限` 标志(spec-02 §2.6)→ 以 **INFO** finding 记录于 synthesis(`summary` = 页尺寸超限、子页已下沉 `{subpages}`),供 retrospective 统计与试点负责人核查;**不改变任何门判据、不触发重派生**(非 PASS/FAIL 判据) |
|
||||
|
||||
## REQMAP — 需求映射维
|
||||
|
||||
| 检查项 | 判据 |
|
||||
|---|---|
|
||||
| 需求覆盖 | **每需求至少一节点**:需求登记表每条 `REQ-F-{NNN}` 被 ≥1 节点的 `req_refs` 引用(无"有需求无节点"遗漏) |
|
||||
| 节点溯源 | **每 task 节点至少一需求**:每 task 节点 `req_refs` 非空且引用有效编号(无"有节点无需求"过度分解) |
|
||||
| 验收标准可证伪 | 每 task 节点 `acceptance_criteria` 可证伪且映射 `test_id`(CLR 折叠项:无"视情况而定";`NFR:` 前缀条目不参与本判据——其验证由 per-task review-code + 里程碑 verify 承担,见 spec-02 §2.9) |
|
||||
| **AC 路径覆盖** | 每 task 节点验收标准覆盖正常路径、错误路径、边界场景三类(`.octopus/archive/checklists/requirements-analysis.md` TST 8.3/8.7 等价);缺错误/边界路径 = MAJOR(`NFR:` 前缀条目不参与本判据——其验证由 per-task review-code + 里程碑 verify 承担,见 spec-02 §2.9) |
|
||||
| 契约↔节点一致性 | 每条 task 间跨 session 边的 `contract_ref` 与两端节点验收标准一致(里程碑边无 `contract_ref`、不参与本判据,见 spec-02 §2.1 / spec-03 §2) |
|
||||
|
||||
> **REQMAP 维仅审 task 节点**:milestone 节点无 `req_refs`/`acceptance_criteria`(只有 DoD,spec-03 §3 规则 2(b)),由 spec-05 里程碑 DoD 承担,非评审对象(spec-08 §2 里程碑行 `评审深度` 列 `—`)——故「每 task 节点」均不含里程碑行,含里程碑的 DAG 不会在 REQMAP 维产生误报。
|
||||
|
||||
> **AC 下沉子页的读取路径**:当节点 AC 细目因 `{epic-slug}/dag` 页尺寸预算超限下沉到 `{epic-slug}/dag-nodes/{node-id}` 子页时(spec-02 §2.6),reviewer 按 `{epic-slug}/dag` 页内 `{node-id} → {epic-slug}/dag-nodes/{node-id}` 指针读取子页核对「验收标准可证伪」「AC 路径覆盖」判据(子页 AC 与页内指针同源,均为冻结副本 `{epic-slug}/dag` 的一部分)。
|
||||
|
||||
## RELEASE — 滚动放行维
|
||||
|
||||
| 检查项 | 判据 |
|
||||
|---|---|
|
||||
| 就绪规则 | 节点 `ready` 当且仅当其所有跨 session 入边源节点均处于各自类型的终结态(task 源节点 `done`、里程碑源节点 `green`;同 session 边不阻塞就绪) |
|
||||
| 依赖批放行 | 按依赖层分批放行:一层内互不依赖的节点同批 `ready`(最大化并行,见 spec-05 里程碑) |
|
||||
| 契约冻结范围 | 仅 task 间的跨 session 边进入 `frozen`;同 session 边与里程碑边不冻结(spec-02 §1 D-05;里程碑边 `contract_state` 不适用,见 spec-02 §2.1 / spec-03 §2) |
|
||||
| 放行风险前移 | 高风险节点(breaking 契约 / 大 fan-out)前移至早期批次(RISK 折叠项)。**操作化锚点**:breaking 契约节点不得晚于其所在依赖层内其它非 breaking 节点的最早可用批次(同层内先于或等于);**大 fan-out** = 单节点跨 session 出边数 ≥ 3(取 `cross_session_edge_count` 阈值表 D3 档起点,spec-06 §2),此类节点同样适用「不晚于同层最早批次」规则——reviewer 按拓扑层序对批次划分做机械核对 |
|
||||
| 估算合理性 | 各 **task** 节点 `size_attrs.estimated_hours`/`estimated_sessions`(冻结字段)已填写、取值在 TOPO「粒度下限」「大小均匀性」界内(两字段一致性不变量见 spec-02 §2.1),并支撑批次划分——同批并行节点由跨 session 边拓扑可达性决定(「依赖批放行」行);容量/并发上限属 Orchestrator 执行期资源配置、单门不审(§2.1 PAR 4.4 丢弃行,EST 折叠项)——reviewer 对照冻结 node schema 逐 **task** 节点核验该字段存在且取值合理(里程碑节点无实现工作、不填,见 spec-02 §2.1 / spec-03 §3) |
|
||||
|
||||
## 门收敛规则
|
||||
|
||||
| 项 | 规则 |
|
||||
|---|---|
|
||||
| 轮次上限 | 由评审深度派生(spec-06):D1≤2,D2≤3,D3≤3,D4≤4 |
|
||||
| PASS | 三维均 0 BLOCKER 且 0 MAJOR |
|
||||
| WARN | 0 BLOCKER;MAJOR 在修订轮内关闭;MINOR/INFO 允许(收敛为 PASS) |
|
||||
| FAIL | 任一维存在 BLOCKER |
|
||||
| INFO 处理 | INFO 不阻塞,记录供 retrospective 统计与试点负责人核查(review-dag 因 `never_trim: true` 结构性不可裁剪——gate-trim 元进程已退役,该字段自足于 `dag:` 块,见 spec-07 §5) |
|
||||
| 部分重审 | **round 3 起**(round 2 仍全维重审——三维共享 DAG 形状/字段副作用面,先以一轮全维重审建立干净基线,再于 round 3 起缩窄;与共享 review-artifact skill「round 2 起部分重审」的差异是有意的,因本门仅 3 维且互为副作用面)轮间只重审上一轮的 FAIL / WARN(含未关闭 BLOCKER 或 MAJOR 的维)/ UNRESOLVED 维(不省修订循环);维度需重审 = 该维 verdict 为 FAIL / WARN(含未关闭 BLOCKER 或 MAJOR)/ UNRESOLVED,MINOR/INFO 维视为通过不复跑。任一维修订改变**共享 DAG 形状(节点/边集合)或共享字段(`size_attrs`、AC 文本、契约内容)**时,以该字段为判据输入的相关维随轮重验(三维互为副作用面) |
|
||||
| 输出 | 单门 synthesis(复用 `synthesis.schema.json`,`dimensions` 键为 TOPO/REQMAP/RELEASE)+ commit status `pipeline/review-dag` |
|
||||
@@ -0,0 +1,115 @@
|
||||
# 前端实现自检清单
|
||||
|
||||
> 开发者在编写前端代码前后自检使用。确保组件结构合理、样式一致、状态完整、
|
||||
> 可访问且可测试。分为"实现前"(PRE)和"实现后"(POST)两部分。
|
||||
> 全部通过后方可提交代码评审。
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. **PRE** 项在开始写组件前检查;
|
||||
2. **POST** 项在完成编码和所有验证命令后检查;
|
||||
3. 对"不通过"项必须在代码评审前修复;
|
||||
4. 无法满足的项标记 `[N/A: <原因>]`。
|
||||
|
||||
---
|
||||
|
||||
## 实现前(PRE — Pre-Implementation)
|
||||
|
||||
### 1. 上下文完备性
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ----- | ------------------------------------------------------------------ | ---- | ------ | --- | ---- |
|
||||
| PRE-1 | 已确定项目框架(React / Vue / Svelte / SolidJS / Angular) | ☐ | ☐ | ☐ | |
|
||||
| PRE-2 | 已确定样式方案(Tailwind / CSS Modules / styled-components / ...) | ☐ | ☐ | ☐ | |
|
||||
| PRE-3 | 已读取至少 3 个同模块的现有文件(UI 工作同类组件),理解命名、结构和样式模式(共享 brownfield 规则,规范出处:`.octopus/skills/review-code/SKILL.md` §"Greenfield vs. Brownfield") | ☐ | ☐ | ☐ | |
|
||||
| PRE-4 | 已确认路由模式(file-based / config-based)和新组件路由位置 | ☐ | ☐ | ☐ | |
|
||||
| PRE-5 | 已确认项目使用的 UI 基础库(Kobalte / Radix / Headless UI / ...) | ☐ | ☐ | ☐ | |
|
||||
| PRE-6 | 若项目有设计系统(token / theme / spacing),已确认取值方式 | ☐ | ☐ | ☐ | |
|
||||
| PRE-7 | Pipeline 模式:已读取设计文档的组件设计、接口设计、NFR 章节(DAG 路由:设计输入解析自冻结 DAG 副本 `{epic-slug}/dag` 节点规格 + 跨 session 边契约,见 `implementation.md` 使用说明第 5 条) | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 2. 组件边界
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ----- | --------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| PRE-8 | 组件职责单一——一个组件只做一件事 | ☐ | ☐ | ☐ | |
|
||||
| PRE-9 | Props 类型已列出(TypeScript 接口 / PropTypes / defineProps) | ☐ | ☐ | ☐ | |
|
||||
| PRE-10| 所有需要的 UI 状态已识别:loading / empty / error / success / edge | ☐ | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 实现后(POST — Post-Implementation)
|
||||
|
||||
### 3. 组件结构
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ----------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-1 | 组件命名清晰、遵循项目约定(PascalCase / kebab-case) | ☐ | ☐ | ☐ | |
|
||||
| POST-2 | 所有 Props 有完整类型,无 `any` 类型 | ☐ | ☐ | ☐ | |
|
||||
| POST-3 | 组件文件结构符合项目约定(单文件 / 目录+index / co-located) | ☐ | ☐ | ☐ | |
|
||||
| POST-4 | 无巨型组件(> 200 行)——必要时已拆分为子组件 | ☐ | ☐ | ☐ | |
|
||||
| POST-5 | 事件处理器和回调遵循项目命名规范(`on*` / `handle*`) | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 4. 样式与设计系统
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | -------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-6 | 使用项目统一的样式方案,未引入新的样式库 | ☐ | ☐ | ☐ | |
|
||||
| POST-7 | 若使用 Tailwind:无冗余类名堆积,复杂样式已提取为 `@apply` 或组件 | ☐ | ☐ | ☐ | |
|
||||
| POST-8 | 响应式断点已处理(移动端/平板/桌面),无横向溢出 | ☐ | ☐ | ☐ | |
|
||||
| POST-9 | 若项目有暗色模式:组件在亮/暗主题下均可正常显示 | ☐ | ☐ | ☐ | |
|
||||
| POST-10| 使用项目设计 token(颜色/间距/字体),无硬编码魔法数值 | ☐ | ☐ | ☐ | |
|
||||
| POST-11| 动画/过渡遵循项目约定(`motion` / CSS transition / ...),无突兀跳动 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 5. 状态覆盖
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ----------------------------------------------------------------| ---- | ------ | --- | ---- |
|
||||
| POST-12| **Loading 状态**:数据加载时有骨架屏/加载指示器,布局不跳动 | ☐ | ☐ | ☐ | |
|
||||
| POST-13| **Empty 状态**:无数据时有友好提示和操作引导(非空白页) | ☐ | ☐ | ☐ | |
|
||||
| POST-14| **Error 状态**:请求失败时显示错误信息和重试/恢复操作 | ☐ | ☐ | ☐ | |
|
||||
| POST-15| **Edge cases**:超长文本截断、特殊字符、空数组、`null`/`undefined` 值均已处理 | ☐ | ☐ | ☐ | |
|
||||
| POST-16| 数据更新后 UI 正确反映最新状态(无过期数据残留) | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 6. 可访问性(a11y)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ------------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-17| 使用语义化 HTML 元素(`<button>` 而非 `<div onclick>`) | ☐ | ☐ | ☐ | |
|
||||
| POST-18| 图片/图标有有意义的 `alt` 文本(纯装饰性图片使用 `alt=""`) | ☐ | ☐ | ☐ | |
|
||||
| POST-19| 表单控件有关联的 `<label>`(非仅 placeholder) | ☐ | ☐ | ☐ | |
|
||||
| POST-20| 所有交互元素可通过键盘访问(Tab 导航,Enter/Space 激活) | ☐ | ☐ | ☐ | |
|
||||
| POST-21| 弹窗/模态框:打开时焦点移入,关闭时焦点回退,ESC 可关闭 | ☐ | ☐ | ☐ | |
|
||||
| POST-22| 动态内容更新(加载完成/错误提示/列表变化)有适当的 `aria-live` 通知 | ☐ | ☐ | ☐ | |
|
||||
| POST-23| 色彩对比度:正文 ≥ 4.5:1,大文本 ≥ 3:1,非仅靠颜色传达信息 | ☐ | ☐ | ☐ | |
|
||||
| POST-24| 页面有逻辑的标题层级(`h1` → `h2` → `h3`),无跳级 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 7. 性能
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | -------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-25| 无不必要的重渲染——列表子项 key 稳定、事件处理器引用稳定 | ☐ | ☐ | ☐ | |
|
||||
| POST-26| 大列表使用虚拟滚动或分页(非一次性渲染全部) | ☐ | ☐ | ☐ | |
|
||||
| POST-27| 图片使用懒加载(`loading="lazy"`),有合适的 `width`/`height` 防止布局偏移 | ☐ | ☐ | ☐ | |
|
||||
| POST-28| 非首屏组件考虑代码分割(`lazy()` / `defineAsyncComponent`) | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 8. 代码质量
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ----------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-29| `bun typecheck`(或项目等效命令)通过,无类型错误 | ☐ | ☐ | ☐ | |
|
||||
| POST-30| `bun lint`(或项目等效命令)通过,无错误或警告 | ☐ | ☐ | ☐ | |
|
||||
| POST-31| 无注释掉的代码块或 `console.log` 调试语句 | ☐ | ☐ | ☐ | |
|
||||
| POST-32| 无硬编码的 API 地址、密钥或环境特定值——使用环境变量或配置 | ☐ | ☐ | ☐ | |
|
||||
| POST-33| 没有因 UI 改动导致的不相关组件样式错乱 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 9. 前端测试
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ------------------------------------------------------------------ | ---- | ------ | --- | ---- |
|
||||
| POST-34| 新组件有基础渲染测试("does it render without crashing") | ☐ | ☐ | ☐ | |
|
||||
| POST-35| 关键交互有行为测试(点击/输入/提交触发预期回调或状态变化) | ☐ | ☐ | ☐ | |
|
||||
| POST-36| 至少覆盖 loading / error / empty 其中一种边界状态的测试 | ☐ | ☐ | ☐ | |
|
||||
| POST-37| 若项目使用 Storybook:新组件有至少一个 story | ☐ | ☐ | ☐ | |
|
||||
| POST-38| `bun run test:parallel`(或项目等效命令)全部通过 | ☐ | ☐ | ☐ | |
|
||||
@@ -0,0 +1,153 @@
|
||||
# 实现自检清单
|
||||
|
||||
> 开发者在编写代码前后自检使用。确保代码忠实实现设计、可测试且符合项目规范。
|
||||
> 分为"实现前"(PRE)和"实现后"(POST)两部分。全部通过后方可提交代码评审。
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. **PRE** 项在开始写代码前检查;
|
||||
2. **POST** 项在完成编码和所有验证命令后检查;
|
||||
3. 对"不通过"项必须在代码评审前修复;
|
||||
4. 无法满足的项标记 `[N/A: <原因>]`;
|
||||
5. **DAG 路由产物解析([org-internal #3072] phase 3 后唯一管线模式)**:DAG 运行不存在
|
||||
legacy `{slug}/04-plan-*` / `{slug}/03-design-*` 页面,PRE-1~PRE-7、
|
||||
PRE-11、PRE-12、PRE-19、POST-15.1 引用的产物按 `implement`/`verify`
|
||||
SKILL 的 DAG-route read map 解析:工作项定义 → 冻结 DAG 副本
|
||||
`{epic-slug}/dag` 节点规格 + 节点工单正文;验收条件与声明的 `test_id`
|
||||
→ 节点 `acceptance_criteria`(含 `{epic-slug}/dag-nodes/{node-id}`
|
||||
下沉子页);组件/接口/数据设计、REQ→组件追溯 → 节点规格 + 跨 session
|
||||
边契约(`{epic-slug}/shared/{file}`);「在 plan 中标注」(PRE-19)与
|
||||
迭代计划页标注(POST-15.1)→ 节点 `acceptance_criteria` 或节点工单
|
||||
正文显式标注。standalone 模式(bugfix/refactor/port)以请求本身为规格,
|
||||
上述项标记 `[N/A: standalone 无 legacy 产物]`。
|
||||
|
||||
---
|
||||
|
||||
## 实现前(PRE — Pre-Implementation)
|
||||
|
||||
### 1. 上下文完备性
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ----- | -------------------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| PRE-1 | 已读取工作项定义(wiki page `{slug}/04-plan-04-iteration-assignment`) | ☐ | ☐ | ☐ | |
|
||||
|
||||
| PRE-2 | 已读取本迭代验收条件(wiki page `{slug}/04-plan-05-acceptance-criteria`) | ☐ | ☐ | ☐ | |
|
||||
|
||||
| PRE-2.1 | 已从 plan/05 验收条件表提取本工作项每条声明的 test_id(测试用例 ID 列),作为 Phase 3 Red→Green 测试优先顺序的依据;MANUAL/BENCH 类型已识别 | ☐ | ☐ | ☐ | |
|
||||
|
||||
| PRE-3 | 已读取可追溯矩阵中的 REQ→组件映射(wiki page `{slug}/03-design-08-traceability`) | ☐ | ☐ | ☐ | |
|
||||
|
||||
| PRE-4 | 已读取涉及组件的设计文档(wiki page `{slug}/03-design-03-component-design`) | ☐ | ☐ | ☐ | |
|
||||
| PRE-5 | 若涉及 API,已读取接口设计(wiki page `{slug}/03-design-04-interface-design`) | ☐ | ☐ | ☐ | |
|
||||
| PRE-6 | 若涉及数据模型,已读取数据设计(wiki page `{slug}/03-design-05-data-design`) | ☐ | ☐ | ☐ | |
|
||||
| PRE-7 | 若涉及非功能需求,已读取对应设计章节(wiki page `{slug}/03-design-06-non-functional-design`) | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 2. 代码图调研(Code Graph)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ------------------------------------------------------------------------------------------ | ---- | ------ | --- | ---- |
|
||||
| PRE-CG1 | 会话已确认代码图就绪(`codegraph_status` 非空,否则 `codegraph init -i`) | ☐ | ☐ | ☐ | |
|
||||
| PRE-CG2 | 已用 `codegraph_explore`/`codegraph_search` 定位待改符号的定义与依赖,而非 grep+read 全文拼凑 | ☐ | ☐ | ☐ | |
|
||||
| PRE-CG3 | 已用 `codegraph_callers` 查清待改符号的所有调用方,确认改动不遗漏调用点 | ☐ | ☐ | ☐ | |
|
||||
| PRE-CG4 | 已用 `codegraph_explore`/`codegraph_callers` 评估改动的传递影响范围,回归风险已知 | ☐ | ☐ | ☐ | |
|
||||
| PRE-CG5 | 精读实现时使用 `read(filePath, symbol: ...)` 只取目标符号,未整文件读取大文件 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 3. 工作项边界
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ---------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| PRE-8 | 工作项范围清晰,不超过 3 个文件变更(单次 Worker 会话上下文窗口限制) | ☐ | ☐ | ☐ | |
|
||||
| PRE-9 | 所有需要创建/修改的文件在设计文档中有对应组件或接口 | ☐ | ☐ | ☐ | |
|
||||
| PRE-10 | 没有设计文档未提及的新组件、新表或新外部依赖需要引入 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 4. 设计与计划门控(Design & Plan Gate)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | -------------------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| PRE-11 | 设计文档(wiki pages `{slug}/03-design-**`)已存在且包含本工作项涉及的全部组件设计 | ☐ | ☐ | ☐ | |
|
||||
| PRE-12 | 迭代计划(plan/)已存在且本工作项有明确的工作项 ID 和验收条件 | ☐ | ☐ | ☐ | |
|
||||
| PRE-13 | 评审门已收敛:DAG 路由下为 review-dag 单门收敛(`octopus review status --stage review-dag` 的 state 为 `success`,与 `pipeline-gate.md` DAG 路由变体一致);standalone 模式(bugfix/refactor/port)无上游评审门,标记 `[N/A: standalone 无上游评审]`(legacy design-space / iteration-plan 双门已随 [org-internal #3072] phase 3 归档) | ☐ | ☐ | ☐ | |
|
||||
| PRE-14 | 合并前基准刷新:当前分支已 rebase 到目标分支(`git fetch origin && git rebase origin/main`),无合并冲突。若 rebase 引入新变更,重新运行 `bun typecheck && bun run test:parallel` 后再提交 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 5. UI 组件设计完整性(仅前端/UI 工作项)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | -------------------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| PRE-15 | 设计文档覆盖了组件的全部四种状态(Loading / Empty / Error / Success),每种状态有明确的渲染内容和触发条件(参见 `.octopus/archive/templates/design.md` §11.1,legacy 设计模板 [org-internal #3072] phase 3) | ☐ | ☐ | ☐ | |
|
||||
| PRE-16 | 设计文档覆盖了组件所需的全部交互行为:列表导航、焦点管理、键盘快捷键、展开/折叠、实时过滤(参见 `.octopus/archive/templates/design.md` §11.2,legacy 设计模板 [org-internal #3072] phase 3) | ☐ | ☐ | ☐ | |
|
||||
| PRE-17 | 设计文档覆盖了组件的全部可访问性要求:ARIA role/label、键盘可达、焦点环、对比度、色彩独立性(参见 `.octopus/archive/templates/design.md` §11.3,legacy 设计模板 [org-internal #3072] phase 3) | ☐ | ☐ | ☐ | |
|
||||
| PRE-18 | 设计文档已逐组件声明测试策略类型(render / source-verification / E2E / manual)。已知测试基础设施限制(Kobalte portal + happydom、路由上下文缺失等)已有对应替代方案标记(参见 `.octopus/archive/templates/design.md` §11.4,legacy 设计模板 [org-internal #3072] phase 3) | ☐ | ☐ | ☐ | |
|
||||
| PRE-19 | 超过 3 个 MANUAL 类型的 AC 已标记为设计风险,并在 plan 中标注更高级测试基础设施依赖 | ☐ | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 实现后(POST — Post-Implementation)
|
||||
|
||||
### 4. 设计一致性
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ------------------------------------------------------ | ---- | ------ | --- | ---- |
|
||||
| POST-1 | 组件接口签名(方法名、参数、返回值)与设计文档一致 | ☐ | ☐ | ☐ | |
|
||||
| POST-2 | 数据模型字段名、类型、约束与数据设计一致 | ☐ | ☐ | ☐ | |
|
||||
| POST-3 | API 端点、方法、请求/响应格式、状态码与接口设计一致 | ☐ | ☐ | ☐ | |
|
||||
| POST-4 | 组件依赖关系与设计的依赖图一致,无反向依赖或新增依赖 | ☐ | ☐ | ☐ | |
|
||||
| POST-5 | 未引入设计文档未提及的新依赖(npm 包、外部服务) | ☐ | ☐ | ☐ | |
|
||||
| POST-6 | 若不得已偏离设计,有明确的注释标注原因和设计修正建议(含 ADR/amend 引用 — 不可仅写"偏离设计") | ☐ | ☐ | ☐ | |
|
||||
| POST-6.1 | 已用设计文档中的决策树/状态机/真值表,代入至少 2 组具体输入手工 trace 每条分支,确认代码输出与设计预期一致(尤其条件取反、`===` vs `!==`、状态翻转等易错点) | ☐ | ☐ | ☐ | |
|
||||
| POST-6.2 | 因 API 不兼容、上游限制或测试基础设施不足而延迟的项,已在代码中用 `[OPEN: <short-id>]` 标注(含延迟原因、影响范围、建议解决时机)。verify Phase 5.5 会为每个 `[OPEN]` 项在源票 `## TD 登记` 评论登记一行(registry-first,`ticket-lifecycle.md`;排期后才升格独立票)。禁止仅标注 `[OPEN]` 而不登记 | ☐ | ☐ | ☐ | |
|
||||
| POST-6.3 | 邻近配额([org-internal #3002] G3):本迭代触碰的区域(包/模块)若在源票 `## TD 登记` 中有适用行,已带走 ≥1 项一并处置(修复或带理由显式再延迟);无适用行时在报告记录 `0 applicable` | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 5. 代码质量
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------- | ----------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-7 | `bun typecheck` 通过,无类型错误 | ☐ | ☐ | ☐ | |
|
||||
| POST-8 | `bun lint` 通过,无 lint 错误或警告 | ☐ | ☐ | ☐ | |
|
||||
| POST-8.1 | 删除代码后(清理死代码、测试文件、重构移除),重新运行 `bun lint` 并确认无 unused-import / unused-variable 警告(常见遗留:删除测试代码后遗漏的 import) | ☐ | ☐ | ☐ | |
|
||||
| POST-8.2 | 删除 `.ts`/`.tsx` 文件后,验证 `bun typecheck` 无"找不到模块"或孤立类型引用错误 | ☐ | ☐ | ☐ | |
|
||||
| POST-9 | 函数/方法长度合理(≤ 50 行),单一职责 | ☐ | ☐ | ☐ | |
|
||||
| POST-10 | 错误处理路径完备(I/O、网络、解析、数据库操作) | ☐ | ☐ | ☐ | |
|
||||
| POST-10.1 | I/O 操作有超时守卫(如 `Effect.timeout`),避免无限挂起 | ☐ | ☐ | ☐ | |
|
||||
| POST-10.2 | 子进程调用有显式退出码检查(`exitCode !== 0` 显式 fail) | ☐ | ☐ | ☐ | |
|
||||
| POST-10.3 | `Effect.orDie`/`orDieWith` 仅用于 unrecoverable 场景;recoverable 错误用 `catchAll`/`recoverWith` | ☐ | ☐ | ☐ | |
|
||||
| POST-10.4 | 多写操作(INSERT + UPDATE)用 `Database.transaction` 包裹保证原子性 | ☐ | ☐ | ☐ | |
|
||||
| POST-11 | 无硬编码凭据、密钥、内网地址或环境特定值 | ☐ | ☐ | ☐ | |
|
||||
| POST-11.1 | 提交前无法跟踪链接检查(单一事实来源:`code-review.md` SEC 3.7.1——`git diff --cached --diff-filter=T` 为空;判据以该条为准)| ☐ | ☐ | ☐ | |
|
||||
| POST-11.2 | 提交前已确认无进程/流程副产品混入暂存区(如 `.claim` 空文件、claim carrier、临时 pid/日志)——`git status` 逐条核对,`git add -A` 前先看 untracked 清单([org-internal #3169] 教训:`.claim` 空文件随 iter-0 混入,round-1 九维评审要求 `git rm`) | ☐ | ☐ | ☐ | |
|
||||
| POST-12 | 无注释掉的代码块或 `console.log` 调试语句 | ☐ | ☐ | ☐ | |
|
||||
| POST-13 | 无 `as any` 类型断言绕过类型检查(生产代码必须保有完整类型安全) | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 6. 测试
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------- | ---------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-14 | `bun run test:parallel` 全部通过,无失败用例 | ☐ | ☐ | ☐ | |
|
||||
| POST-15 | 新增代码有测试覆盖,覆盖主要路径和关键分支 | ☐ | ☐ | ☐ | |
|
||||
| POST-15.1 | 测试与实现位于同一迭代;若分拆到下一迭代,必须在迭代计划(wiki page `{slug}/04-plan-04-iteration-assignment`)中显式标注并给出理由。实现提交时不得处于"零测试覆盖"状态 | ☐ | ☐ | ☐ | |
|
||||
| POST-15.2 | 新增组件源文件(`.tsx`/组件 `.ts`)提交时,同一 commit 必须附带至少一个冒烟测试(render 测试,或 Kobalte portal 类组件的源码验证测试——见 AGENTS.md「Testing Kobalte components with happydom」)。禁止整批源文件无任何测试落地、将全部测试统一延后到未来 chunk | ☐ | ☐ | ☐ | |
|
||||
| POST-15.3 | plan/05 中声明的每个 test_id 已落地为真实测试(文件路径::测试名与声明一致) | ☐ | ☐ | ☐ | |
|
||||
| POST-16 | 测试验证了验收条件中的具体行为 | ☐ | ☐ | ☐ | |
|
||||
| POST-17 | 测试覆盖了边界条件(空值、异常输入、权限边界) | ☐ | ☐ | ☐ | |
|
||||
| POST-18 | 测试相互独立,可任意顺序运行 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 7. 文档与日志
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------- | ---------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-19 | 复杂逻辑有简洁解释(若真正非直觉) | ☐ | ☐ | ☐ | |
|
||||
| POST-20 | 对外 API 的错误消息用户可读且可操作 | ☐ | ☐ | ☐ | |
|
||||
| POST-21 | 关键操作有结构化日志(含上下文如 userId、requestId) | ☐ | ☐ | ☐ | |
|
||||
| POST-22 | JSDoc 与实际函数签名一致,参数(含新增参数)已完整文档化 | ☐ | ☐ | ☐ | |
|
||||
| POST-23 | 带副作用的新代码路径(日志、事件发布)已守卫所有控制流(恢复、空值、错误路径),防止幽灵触发 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 8. 过程与规范一致性
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------- | -------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-24 | AI artifact header 使用 `@ai-artifact:` 多行格式(示例见 AGENTS.md)。不得使用其他格式如 `// AI-GENERATED ARTIFACT` | ☐ | ☐ | ☐ | |
|
||||
| POST-25 | 评审修订后,若修改了被测试覆盖的代码,已同步更新对应的测试断言(source-verification 或 render 测试) | ☐ | ☐ | ☐ | |
|
||||
| POST-26 | 测试断言中不得包含 `@opencode-ai` 字符串字面量(namespace gate 扫描字符串字面量,会将其误判为命名空间残留);安全写法见 `.octopus/rules/testing.md` § "Namespace gate and test assertions" | ☐ | ☐ | ☐ | |
|
||||
| POST-31 | commit 消息使用了正确的 conventional 类型:性能优化用 `perf`(非 `feat`),内部重构用 `refactor`,新功能用 `feat`,bug 修复用 `fix`。类型选择直接影响 auto-changelog 和 release notes 准确性 | ☐ | ☐ | ☐ | |
|
||||
| POST-32 | 若工作项依赖关键 upstream 库(如 `marked`、`effect`、`@kobalte/core`),已在本地运行基础 smoke test 确认 API 签名未变(upstream 可能在 minor 版本变更返回类型或参数) | ☐ | ☐ | ☐ | |
|
||||
@@ -0,0 +1,129 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Pipeline Gate Checklist
|
||||
|
||||
跨阶段门控清单。implement、review-code、verify 的 precondition 应引用本清单。
|
||||
每个阶段启动前必须逐项确认。
|
||||
|
||||
## DAG 路由变体(DAG-routed Epic/Feature 及其 Kind/Task 子工单)
|
||||
|
||||
DAG 路由(`dag.route` / `dag.task_route`)**不适用**下方 legacy 的
|
||||
Upstream Artifact Existence 与 Upstream Review Convergence 检查
|
||||
(各 skill 的 DAG 分支——implement §"DAG-mode input path"、review-code
|
||||
§"DAG Task Mode"、verify §"DAG branch"——以下列判据替代):
|
||||
|
||||
- [ ] 冻结 DAG 副本存在:wiki page `{epic-slug}/dag` 存在且首行为
|
||||
`> DAG 工件状态: frozen`(节点 spec、跨 session 边契约、
|
||||
`size_attrs` 均自该副本解析)
|
||||
- [ ] 单门收敛:`octopus review status --stage review-dag` state 为
|
||||
`success`(替代 design-space + iteration-plan 双门;任务工单
|
||||
不重跑单门,只核对其已收敛)
|
||||
- [ ] (仅任务工单)票 body `## 父级 / Parent` 指向 DAG 父级
|
||||
(`Kind/Epic` 或 `Kind/Feature`,[org-internal #3061] 阶段 2),
|
||||
Step 0 解析到 `dag.task_route`(entry `implement`),不继承
|
||||
`dag.route`
|
||||
- [ ] 跨节点依赖由 DAG 拓扑承担:节点 ready 以其全部跨 session 上游
|
||||
处于终态为准(同 session 边不阻塞);fan-in ≥ 2 里程碑节点由
|
||||
verify 里程碑承担 DoD,不建工单、不走本清单
|
||||
|
||||
abort 恢复:单门未收敛 → 聚合 agent 重跑 review-artifact skill(target:
|
||||
review-dag)直至收敛;DAG 副本缺失或未冻结 → 回到 `analyze-dag`
|
||||
(分解 → 单门 → 冻结)后再重试。
|
||||
|
||||
## Upstream Artifact Existence(live — standalone modes)
|
||||
|
||||
- [ ] standalone bugfix:repro notes wiki page `{slug}/repro-notes` 存在且含环境快照(via `wiki 读写 API(见 TERMINOLOGY)`)
|
||||
- [ ] 验收条件可解析:来自请求/工单 body 或节点 AC(等价的验收条件文档,via `wiki 读写 API(见 TERMINOLOGY)` / `工单 API(见 TERMINOLOGY)get`)
|
||||
|
||||
> Legacy([org-internal #3072] phase 3,2026-08-21 归档):requirements/design/plan baseline
|
||||
> 页(`{slug}/02-requirements-index` / `{slug}/03-design-index` /
|
||||
> `{slug}/04-plan-index` / `{slug}/04-plan-05-acceptance-criteria`)与
|
||||
> design-space / iteration-plan 双门收敛检查随 legacy 管线归档
|
||||
> (`<instance-root>/archive/`);历史页面仍可读,DAG 路由由上方 DAG 变体接管。
|
||||
|
||||
## Upstream Review Convergence
|
||||
|
||||
- [ ] (代码评审阶段)code review 已收敛 — run `octopus review status --stage code` and verify state is `success`
|
||||
|
||||
## Upstream Dependency Check
|
||||
|
||||
- [ ] 当前节点的所有跨 session 上游依赖节点处于终态(task `done` / milestone `green`,以冻结 DAG 副本 / `## DAG 状态` 表为准)
|
||||
- [ ] 如果有依赖节点未到终态 → abort,列出阻塞的节点
|
||||
|
||||
## Deferred Items Closure
|
||||
|
||||
- [ ] 上一迭代的 Carried Items 全部有明确的 Target Iteration 且已在当前迭代处理或重新延期
|
||||
- [ ] 上一迭代的 Carried Risks 状态已更新(open/closed)
|
||||
- [ ] verify 阶段:所有 UNVERIFIABLE 项有明确的 reactivation plan 或被标记为 accepted tech debt(有记录)
|
||||
|
||||
## Tech Debt Review
|
||||
|
||||
- [ ] tech debt 可经 Gitea issue 查询(`工单 API(见 TERMINOLOGY)list(labels="tech-debt")`,如果项目有任何已完成的 chunk/迭代)
|
||||
- [ ] 回读 open `tech-debt` issues,检查每项的 Reactivation Trigger 是否已满足
|
||||
- [ ] 到期项(trigger 已满足)已纳入当前迭代工作项或显式延期(更新 trigger)
|
||||
- [ ] 如果有到期项未处理 → warn 并列出,建议纳入当前迭代
|
||||
- [ ] 本迭代全部 tech-debt 项均已创建为 `tech-debt` labeled issue;遗漏的项已在 verify 阶段补建或在报告中 flag
|
||||
|
||||
## PR 准入(pr-admission)
|
||||
|
||||
> 纪律:`core/rules/ticket-lifecycle.md` PR 准入节(TD-678 / [org-internal #3881] / [org-internal #4425])。
|
||||
|
||||
- [ ] PR 标题符合 `[slug][iter-N] type(scope): 描述` 格式
|
||||
- [ ] PR 正文含变更清单 + 自测结果 + 关联合规(关联 issue 引用,如 `Closes #N`;数字后须接 ASCII 标点或行尾——紧跟全角标点会破坏 Gitea 自动关闭,PR [org-internal #3882] 实例)
|
||||
- [ ] 交付推送前本地验证已绿:`<harness-package>` 下 `bun run test:changed` 全绿 + `bun typecheck` 0 error(WIP 备份推送不受此门约束——分支裸推零 CI,交付信号是完成回报而非推送)
|
||||
- [ ] 分支已推送 origin;PR **未**由本会话自行创建(由编排按容量串行开启,一次一张、双绿并入再开下一张)
|
||||
- [ ] 交付报告已发(branch= 分支名 / 改动文件清单 / 自测结果 / verify 与 risk 回执)
|
||||
- [ ] 若编排不可达(fail-open)自开了 PR:PR 正文已标注 `uncoordinated`
|
||||
|
||||
## Issue Checklist Sync
|
||||
|
||||
> 跨阶段门控。source issue 的 checklist 与 `## 当前状态` live-status 表
|
||||
> 必须在每次对外可见的状态跃迁后就地同步(PR 创建 / 评审收敛 / CI
|
||||
> 状态跃迁),不得等到技能退出边界。规则:`core/rules/issue-checklist-sync.md`。
|
||||
|
||||
- [ ] 若存在 source issue:其 checklist 已按同步点表渐进勾选(DAG 冻结、iteration commit、PR 创建、review PASS、CI 跃迁、verify 终扫),无陈旧 `- [ ]` 项
|
||||
- [ ] 若为 incident / standalone-bugfix 流程:issue 含 `## 当前状态` live-status 小节,且 PR / review / CI 行已随跃迁更新([org-internal #1689])
|
||||
- [ ] 过程性 AC(如"连续 N 次绿")的进度注记已更新(带 run 编号)
|
||||
- [ ] 遗留项(未勾选)均有 `(Deferred: ...)` 或 `(Pending: ...)` 注记
|
||||
- [ ] verify PASS 时 `## 当前状态` 小节已折叠进 checklist 注记并移除
|
||||
|
||||
## Bugfix Pipeline Pre-Design Falsification Gate
|
||||
|
||||
> Bugfix 流程特有。必须在进入修复实现前逐项确认。
|
||||
> 单一事实来源:`core/checklists/bugfix.md` §1 REPRO(条目 1.4-1.6)——
|
||||
> 环境快照、复现置信度 ≥3/5、根因假设未被证伪、不满足即 abort 的判据
|
||||
> 以该清单为准,此处不重复列举;逐项核对 bugfix.md §1 后方可进入修复实现。
|
||||
|
||||
## Failure Protocol
|
||||
|
||||
如果任何检查项不满足:
|
||||
1. **abort** 当前阶段,不继续执行
|
||||
2. 列出所有不满足的检查项
|
||||
3. 指出需要完成的上游工作
|
||||
4. 告知用户在 upstream 工作完成前无法继续
|
||||
|
||||
## Recovery Protocol
|
||||
|
||||
abort 后,根据缺失项运行对应的 skill 修复上游工作,然后再重试当前阶段:
|
||||
|
||||
| abort 原因 | 恢复动作(运行的 skill) | 修复后重试 |
|
||||
|-----------|--------------------------|-----------|
|
||||
| 冻结 DAG 副本缺失或未冻结 | `analyze-dag` — 分解 → `review-artifact` (target: review-dag) 单门 → 冻结 | implement / review-code / verify |
|
||||
| review-dag 单门未收敛(`octopus review status --stage review-dag` non-`success`) | `review-artifact` (target: review-dag) — 重新运行单门评审直到收敛(聚合 agent) | implement / review-code / verify |
|
||||
| 上游依赖节点未到终态(task 非 `done` / milestone 非 `green`) | 切换到该依赖节点的工作流,从其当前阶段继续推进,直到其 verify 通过 | implement / verify |
|
||||
| carried items 未闭环(上一迭代 Carried Items 未处理或无延期记录) | 在当前迭代处理该 carried item,或正式延期到下一迭代并更新其 Target Iteration | verify |
|
||||
| 验收条件不可解析(节点 AC 缺失或工单 body 无验收条件) | 在 DAG 副本上补齐节点 AC(修订走 oversize 信号流程),或在工单 body 补验收条件 | verify |
|
||||
| tech debt 到期项未处理(open `tech-debt` issue) | 将到期项纳入当前迭代工作项,或在该 tech-debt issue 中更新 Reactivation Trigger 并说明延期理由 | implement / verify |
|
||||
|
||||
### 恢复流程
|
||||
|
||||
1. abort 时,列出所有不满足的检查项
|
||||
2. 对照上表,确定每个缺失项对应的恢复 skill
|
||||
3. 如果有多个缺失项,按流水线顺序修复(DAG 冻结 → review-dag 单门 → 实现 → code review → verify)
|
||||
4. 每个恢复 skill 完成后,重新检查对应的 precondition
|
||||
5. 所有 precondition 满足后,重试原本 abort 的阶段
|
||||
|
||||
### 注意
|
||||
|
||||
- 恢复上游工作时,不要丢弃已完成的下游工作(如已有代码实现),而是将其作为修复后的验证输入
|
||||
- 如果上游修复导致下游已完成的工件失效,需要重新运行受影响阶段
|
||||
- 上游依赖 chunk 未完成时,优先推进依赖 chunk 而非绕过
|
||||
@@ -0,0 +1,159 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Port 检查清单
|
||||
|
||||
> 跨项目端口移植自检。Developer 在 Phase A1.7/A1.8(目标分析+能力边界)、
|
||||
> Phase 5(实现)、Phase 6(测试)、Phase 7(报告)各过一遍。
|
||||
|
||||
> **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TST/BEH/GATE/REG)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TST=Test Quality、verification.md 的 TST=Test Suite Completeness、port.md 的 TST=Test Porting)。跨清单引用时必须带清单限定(如 code-review.md TST 5.8),不得使用裸代码。
|
||||
|
||||
---
|
||||
|
||||
## 0. 自检门控(GATE — Self-Check Gate)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 0.1 | 全部 12 个章节均已逐项检查并标记 ☑ 或 ☐ | ☐ | ☐ | |
|
||||
| 0.2 | 所有 ☐ 项均有修复计划或推迟路径(含 reactivation path) | ☐ | ☐ | |
|
||||
| 0.3 | 已完成清单已输出为 wiki `port-{name}/self-check`(via `wiki 读写 API(见 TERMINOLOGY)`) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 0.5 源分析评审(SRV — Source Analysis Review)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 0.5.1 | 源函数清单与源文件交叉验证:无遗漏函数/符号(**须用自动化符号 diff,非主观判断**) | ☐ | ☐ | |
|
||||
| 0.5.2 | 公共 API 表与源路由/方法定义一致(输入/输出/错误码准确) | ☐ | ☐ | |
|
||||
| 0.5.3 | FID 清单覆盖所有源测试用例,file:line 引用正确 | ☐ | ☐ | |
|
||||
| 0.5.4 | 依赖列表与源项目包管理文件一致 | ☐ | ☐ | |
|
||||
| 0.5.5 | Pipeline 模式:10 维度评审全部收敛(0 BLOCKER, 0 MAJOR) | ☐ | ☐ | |
|
||||
| 0.5.6 | SRC-CMP 符号差集已记录:codegraph/grep 导出源符号集 vs 清单,差集为空或差集项均有 BLOCKER/DEFER 记录 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 0.7 目标接收面分析(TGT — Target Surface Analysis)
|
||||
|
||||
> 在 Phase A2(概念映射)之前完成。回答"目标项目准备好了吗?"。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 0.7.1 | 目标项目包结构已映射(每个包的角色已记录) | ☐ | ☐ | |
|
||||
| 0.7.2 | 目标已有能力已识别(与源功能重叠的模块/路由/Provider/Schema) | ☐ | ☐ | |
|
||||
| 0.7.3 | 自动化结构差异已完成(文件/依赖/导出符号/路由/Provider/Schema/CLI/主题/配置/Env/构建) | ☐ | ☐ | |
|
||||
| 0.7.4 | 集成点已识别(每个集成点标注变更类型和受影响的目标文件) | ☐ | ☐ | |
|
||||
| 0.7.5 | 目标就绪评估已完成(是否需要重构/新包/Migration/配置变更,阻塞项已标注) | ☐ | ☐ | |
|
||||
| 0.7.6 | `port-{name}/source-analysis/11-target-surface`(wiki)已输出 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 0.8 能力边界定义(CAP — Capability Boundary)
|
||||
|
||||
> 在 Phase A2(概念映射)之前完成。回答"这个能力的完整边界是什么?"。
|
||||
> **GATE:13 个维度必须全部填写,否则不得进入 Phase A2。**
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 0.8.1 | 维度 1 — 源代码文件:每个文件已列出(含目标位置和状态) | ☐ | ☐ | |
|
||||
| 0.8.2 | 维度 2 — 类型定义/接口:所有共享类型/品牌类型/Schema 已列出 | ☐ | ☐ | |
|
||||
| 0.8.3 | 维度 3 — 数据库 Schema/Migration:表/列/Migration 已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.4 | 维度 4 — 配置条目:Config Key/Setting/默认值已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.5 | 维度 5 — 环境变量:Env Var/VITE_* 已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.6 | 维度 6 — CLI 标志/命令:CLI 命令/标志/选项已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.7 | 维度 7 — 主题/样式文件:CSS/Theme JSON/Tailwind/Token 已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.8 | 维度 8 — 路由定义:新路由/修改重定向/路由守卫已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.9 | 维度 9 — Provider/Context 层级:新 Provider/插入点/Context Key 已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.10 | 维度 10 — 构建配置变更:vite/tsconfig/webpack/tailwind 已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.11 | 维度 11 — Package.json 依赖:新依赖/版本变更/Workspace 依赖已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.12 | 维度 12 — 测试文件:单元测试/集成测试/测试夹具/测试助手已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.13 | 维度 13 — 共享包变更:SDK/UI/Core 等跨包依赖变更已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.14 | 交叉验证:源函数清单中每个函数/符号出现在维度 1 或维度 2 中 | ☐ | ☐ | |
|
||||
| 0.8.15 | 交叉验证:结构差异中每个 Gap 在能力边界中有对应条目 | ☐ | ☐ | |
|
||||
| 0.8.16 | 所有 N/A 维度含一行理由 | ☐ | ☐ | |
|
||||
| 0.8.17 | 所有 ☐ 项含推迟路径和 reactivation trigger | ☐ | ☐ | |
|
||||
| 0.8.18 | `port-{name}/source-analysis/12-capability-boundary`(wiki)已输出 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 1. 源项目理解(SRC — Source Understanding)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------ | ---- | ------ | ---- |
|
||||
| 1.1 | 源功能代码已逐文件阅读 | ☐ | ☐ | |
|
||||
| 1.2 | 源测试已全部阅读(测试是权威行为规范) | ☐ | ☐ | |
|
||||
| 1.3 | 源项目依赖已全部列清(库、服务、基础设施) | ☐ | ☐ | |
|
||||
| 1.4 | 源项目的公共 API 已文档化 | ☐ | ☐ | |
|
||||
| 1.5 | 源函数清单已生成(每个公开/私有函数/符号均有记录,含 Ported? 列) | ☐ | ☐ | |
|
||||
| 1.6 | 源测试用例已全部提取为 FID 清单(`port-{name}/source-analysis/fid-raw`,wiki) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 2. 概念映射(MAP — Concept Mapping)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 2.1 | 每个源概念在目标项目中都有对应(或标记 [GAP]) | ☐ | ☐ | |
|
||||
| 2.2 | 映射优先使用目标项目的现有模式和库(不引入新依赖) | ☐ | ☐ | |
|
||||
| 2.3 | 模式冲突时(如回调 vs async/await)以目标项目模式为准 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 差距分析(GAP — Gap Analysis)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 3.1 | 所有 [GAP] 都有替代方案和决策记录;推迟项含 reactivation path | ☐ | ☐ | |
|
||||
| 3.2 | 差距不应通过引入新基础设施解决(除非无替代方案) | ☐ | ☐ | |
|
||||
| 3.3 | 导致行为变更的差距标记为 FIDELITY DEVIATION 并已获批准 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 适配设计(ADAPT — Adaptation Design)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------ | ---- | ------ | ---- |
|
||||
| 4.1 | 文件在目标项目中的位置已规划 | ☐ | ☐ | |
|
||||
| 4.2 | 接口适配已记录(命名、类型、错误处理风格) | ☐ | ☐ | |
|
||||
| 4.3 | 依赖替代方案已内联到 Phase 3 的 Gap 决策 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 实现忠实度(FID — Fidelity)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------- | ---- | ------ | ---- |
|
||||
| 5.1 | 移植代码遵循目标项目约定(命名、模式、风格) | ☐ | ☐ | |
|
||||
| 5.2 | 未"改进"源逻辑(行为一致优先) | ☐ | ☐ | |
|
||||
| 5.3 | 未引入新的第三方依赖 | ☐ | ☐ | |
|
||||
| 5.4 | 源项目注释已同步移植 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 6. 测试移植(TST — Test Porting)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 6.1 | 源项目的所有测试(含边界/异常/错误路径)均已移植 | ☐ | ☐ | |
|
||||
| 6.2 | 移植的测试全部通过 | ☐ | ☐ | |
|
||||
| 6.3 | 目标项目已有测试无回归 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 7. 行为忠实度验证(BEH — Behavioral Fidelity)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------- | ---- | ------ | ---- |
|
||||
| 7.1 | 源项目的每个公共行为在目标项目中可复现 | ☐ | ☐ | |
|
||||
| 7.2 | 忠实度偏差已有文档记录和批准 | ☐ | ☐ | |
|
||||
| 7.3 | 推迟的功能(不能移植的部分)有后续计划 | ☐ | ☐ | |
|
||||
| 7.4 | 反向覆盖:源函数清单 Ported? 列无残留 ☐(残留项须有 DEFER + reactivation trigger) | ☐ | ☐ | |
|
||||
| 7.5 | 符号级完整性:源/目标导出符号 diff 的 hard gap 均有 DEFER 记录(B5 SRC-CMP) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 8. 最终验证(FINAL — Final Validation)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------ | ---- | ------ | ---- |
|
||||
| 8.1 | `bun run test:parallel` 全部通过(移植测试 + 已有测试) | ☐ | ☐ | |
|
||||
| 8.2 | `bun typecheck` 无错误 | ☐ | ☐ | |
|
||||
| 8.3 | `bun lint` 无错误 | ☐ | ☐ | |
|
||||
@@ -0,0 +1,59 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Prototype / Spike 自检清单
|
||||
|
||||
> 开发者在执行 prototype skill 时自检使用。确保 disposition 决策明确、契约完整、
|
||||
> 产出物可审计。分为 THROWAWAY 与 EVOLUTIONARY 两轨。
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. **Phase 0** 项在写任何代码前检查;
|
||||
2. **THROWAWAY** 或 **EVOLUTIONARY** 项根据 disposition 选择执行;
|
||||
3. 全部通过后方可声明完成;无法满足的项标记 `[N/A: <原因>]`。
|
||||
|
||||
---
|
||||
|
||||
## Phase 0 — Disposition 决策(强制)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
|---|--------|------|--------|-----|------|
|
||||
| P0-1 | 已用一句话明确回答"这段代码会被提升为生产代码(EVOLUTIONARY)还是学习后丢弃(THROWAWAY)?" | ☐ | ☐ | ☐ | |
|
||||
| P0-2 | 已从用户原话中引用证据(`@evidence`)支持 disposition 决策 | ☐ | ☐ | ☐ | |
|
||||
| P0-3 | 若用户措辞为条件式("if it works...")或模糊,已向用户提问澄清而非猜测 | ☐ | ☐ | ☐ | |
|
||||
| P0-4 | disposition 与 evidence 已记录在产出物头部或对应 artifact 中 | ☐ | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## THROWAWAY 轨(Spike)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
|---|--------|------|--------|-----|------|
|
||||
| T-1 | 已声明 time-box(小时/天),并在到期时停止 | ☐ | ☐ | ☐ | |
|
||||
| T-2 | 产出物包含 spike 代码 + learning report(wiki `{slug}/spike-report`,via `wiki 读写 API(见 TERMINOLOGY)`);写入模式见 `_shared/gitea-write-patterns.md` | ☐ | ☐ | ☐ | |
|
||||
| T-3 | spike 代码已标记 `@ai-artifact: spike`,且位于 `spike/` 或 scratch worktree | ☐ | ☐ | ☐ | |
|
||||
| T-4 | learning report 包含:验证了什么、什么失败、go/pivot/stop 决策 | ☐ | ☐ | ☐ | |
|
||||
| T-5 | spike 代码不可被生产代码 import;report 接受后已删除或隔离 | ☐ | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## EVOLUTIONARY 轨(高保真原型)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
|---|--------|------|--------|-----|------|
|
||||
| E-1 | 已创建 debt register(wiki `{slug}/prototype-debt`,via `wiki 读写 API(见 TERMINOLOGY)`),每项 shortcut 有 owner + promotion criterion | ☐ | ☐ | ☐ | |
|
||||
| E-2 | 第 1 天质量底线:typecheck 通过、无未授权的 `any`、committed code 无 `console.log` | ☐ | ☐ | ☐ | |
|
||||
| E-3 | 明确延迟的质量项(测试覆盖、错误状态、可观测性、性能预算)已列入 debt register | ☐ | ☐ | ☐ | |
|
||||
| E-4 | promotion 前已通过 `review-code`(`mode: "prototype-promotion"`),debt register 作为必需输入 | ☐ | ☐ | ☐ | |
|
||||
| E-5 | debt register 为空 OR 每项剩余条目有 reviewer 书面 waiver | ☐ | ☐ | ☐ | |
|
||||
| E-6 | waived 条目已由 `verify` Phase 5.5 创建为 `tech-debt` labeled issue(无平行 tracker) | ☐ | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 反模式(不得出现)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
|---|--------|------|--------|-----|------|
|
||||
| A-1 | 未以"prototype 最终总要重写"为由,在 EVOLUTIONARY 场景下 freeze + delete 原型 | ☐ | ☐ | ☐ | |
|
||||
| A-2 | 未以"spike 只是玩玩"为由,跳过 learning report | ☐ | ☐ | ☐ | |
|
||||
| A-3 | 未以"先跑起来再说"为由,让 EVOLUTIONARY 原型在无 debt register 的情况下进入 review | ☐ | ☐ | ☐ | |
|
||||
@@ -0,0 +1,64 @@
|
||||
# Refactor 检查清单
|
||||
|
||||
> 重构自检。Developer 在 Phase 4(每步)和 Phase 5(最终验证)各过一遍。
|
||||
|
||||
> **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TST/BEH/GATE/REG)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TST=Test Quality、verification.md 的 TST=Test Suite Completeness、port.md 的 TST=Test Porting)。跨清单引用时必须带清单限定(如 code-review.md TST 5.8),不得使用裸代码。
|
||||
|
||||
---
|
||||
|
||||
## 1. 基线检查(BASE — Baseline)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------- | ---- | ------ | ---- |
|
||||
| 1.1 | 重构范围内的代码有测试覆盖 | ☐ | ☐ | |
|
||||
| 1.2 | 全量测试在重构前全部通过 | ☐ | ☐ | |
|
||||
| 1.3 | 测试覆盖率已捕获(行/分支) | ☐ | ☐ | |
|
||||
| 1.4 | 工作区干净(无未提交变更) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 2. 范围控制(SCOPE — Scope)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 2.1 | 重构目标明确(哪种变换,为什么) | ☐ | ☐ | |
|
||||
| 2.2 | 步骤分解 ≤ 10 步 | ☐ | ☐ | |
|
||||
| 2.3 | 不存在范围蔓延(未在同一重构中混入新功能或 Bug 修复) | ☐ | ☐ | |
|
||||
| 2.4 | 若重构涉及身份/owner token 改名(org、账号、邮箱、域名前缀):已先产出"不改清单"——OS 账号、个人邮箱、历史记录归属、dummy fixture 等非组织身份引用逐类明确保留,再执行机械替换([org-internal #3169] 教训:D1 缺此边界 → round-1 15 MAJOR) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 步骤执行(STEP — Per-step Verification)
|
||||
|
||||
> 每一步重构后检查:
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 3.1 | 每步为单一概念变换(非 rename + extract 一步完成) | ☐ | ☐ | |
|
||||
| 3.2 | 每步后 `bun run test:parallel` 全部通过 | ☐ | ☐ | |
|
||||
| 3.3 | 若测试失败,已立即回退而非原地修复 | ☐ | ☐ | |
|
||||
| 3.4 | 每步有独立 commit(便于 revert 或 cherry-pick) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 最终验证(FINAL — Final Validation)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 4.1 | `bun run test:parallel` 全部通过(与重构前相同数量或更多) | ☐ | ☐ | |
|
||||
| 4.2 | `bun typecheck` 无错误 | ☐ | ☐ | |
|
||||
| 4.3 | `bun lint` 无错误 | ☐ | ☐ | |
|
||||
| 4.4 | 测试覆盖率未下降(±1%) | ☐ | ☐ | |
|
||||
| 4.5 | 无新增或修改的测试(重构不应改变测试中的断言逻辑) | ☐ | ☐ | |
|
||||
| 4.6 | 若重构改变了对外可见字符串(stage 名称、tool description、错误消息等),对应的 snapshot 已通过 `bun test --update-snapshots` 更新 | ☐ | ☐ | |
|
||||
| 4.7 | 若重构变更了身份/owner token:残留扫描守卫(如 `script/verify-urls.ts`)已做**语义正反验证**——搜索模式指向**旧** token(旧 owner 前缀的 URL 形态),且已知新 token(新 owner 前缀的 URL 形态)与真实旧残留各跑一次,确认守卫对"合规新 URL"不误报、对"真实残留"不漏报([org-internal #3169] 教训:D5 模式被翻转 → 守卫对新 URL 误报、对旧残留失明) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 无行为变更(BEH — No Behavior Change)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------------- | ---- | ------ | ---- |
|
||||
| 5.1 | 对外接口签名未变化(或已有 @deprecated 说明) | ☐ | ☐ | |
|
||||
| 5.2 | 公共 API 行为一致(相同输入 → 相同输出) | ☐ | ☐ | |
|
||||
| 5.3 | 未引入新的运行时错误/异常路径 | ☐ | ☐ | |
|
||||
@@ -0,0 +1,76 @@
|
||||
# Release 检查清单
|
||||
|
||||
> 发版前自检。Release Manager 在 Phase 1 和 Phase 6 完整过一遍。
|
||||
|
||||
> **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TST/BEH/GATE/REG)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TST=Test Quality、verification.md 的 TST=Test Suite Completeness、port.md 的 TST=Test Porting)。跨清单引用时必须带清单限定(如 code-review.md TST 5.8),不得使用裸代码。
|
||||
|
||||
---
|
||||
|
||||
## 1. 前置闸门(GATE — Pre-release Gates)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------- | ---- | ------ | ---- |
|
||||
| 1.1 | 工作区干净(`git status` 无未提交变更) | ☐ | ☐ | |
|
||||
| 1.2 | 在正确的发布分支上 | ☐ | ☐ | |
|
||||
| 1.3 | 构建通过 | ☐ | ☐ | |
|
||||
| 1.4 | Typecheck + Lint 通过 | ☐ | ☐ | |
|
||||
| 1.5 | 测试全部通过 | ☐ | ☐ | |
|
||||
| 1.6 | 依赖审计已运行,无新增 HIGH/CRITICAL CVE | ☐ | ☐ | |
|
||||
| 1.7 | 无未跟踪的敏感文件(.npmrc 含 token、.env、credentials、私钥等) | ☐ | ☐ | |
|
||||
|
||||
> **注意 1.6**:若使用了无法访问公有 registry 的私有仓库(如自建 Gitea),`bun audit` / `npm audit` 可能报 404。此时无法获取 CVE 数据属于已知盲区,须在 release report 中明确标注「dependency audit: N/A (custom registry)」。
|
||||
|
||||
---
|
||||
|
||||
## 2. 版本号(VER — Version)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------- | ---- | ------ | ---- |
|
||||
| 2.1 | 已找到上一个 tag(或确认这是首个 tag) | ☐ | ☐ | |
|
||||
| 2.2 | 所有 commit 已按类型分类(BREAKING/feat/fix/other) | ☐ | ☐ | |
|
||||
| 2.3 | 版本号遵循 semver(MAJOR.MINOR.PATCH) | ☐ | ☐ | |
|
||||
| 2.4 | 变更类型与 commit 内容一致 | ☐ | ☐ | |
|
||||
| 2.5 | 版本号大于上一个 tag | ☐ | ☐ | |
|
||||
| 2.6 | 版本号已写入所有版本文件 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 变更日志(LOG — Changelog)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------- | ---- | ------ | ---- |
|
||||
| 3.1 | 自上一个 tag 以来的所有非 chore 提交均已收录 | ☐ | ☐ | |
|
||||
| 3.2 | 条目分组正确(Added/Changed/Fixed/Breaking) | ☐ | ☐ | |
|
||||
| 3.3 | Breaking change 有迁移说明 | ☐ | ☐ | |
|
||||
| 3.4 | 每个条目标注了对应的 commit hash | ☐ | ☐ | |
|
||||
| 3.5 | CHANGELOG.md 已更新(prepend 新版本段) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 标签(TAG — Git Tag)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------- | ---- | ------ | ---- |
|
||||
| 4.1 | commit message 含版本号 | ☐ | ☐ | |
|
||||
| 4.2 | tag 已创建且指向正确 commit | ☐ | ☐ | |
|
||||
| 4.3 | tag 命名遵循项目约定(默认 v{version}) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 回滚计划(ROLL — Rollback Plan)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------- | ---- | ------ | ---- |
|
||||
| 5.1 | Git 回退步骤已文档化 | ☐ | ☐ | |
|
||||
| 5.2 | 若有数据库迁移,down migration 存在且已测试 | ☐ | ☐ | |
|
||||
| 5.3 | 回滚触发条件已明确(延迟/错误率/严重 Bug) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 6. 冒烟测试(SMOKE — Post-release Smoke)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------- | ---- | ------ | ---- |
|
||||
| 6.1 | Tagged commit 可构建 | ☐ | ☐ | |
|
||||
| 6.2 | 测试全部通过 | ☐ | ☐ | |
|
||||
| 6.3 | 回到了原始分支 | ☐ | ☐ | |
|
||||
@@ -0,0 +1,64 @@
|
||||
# Retrospective 检查清单
|
||||
|
||||
> 迭代复盘自检。Retrospective Lead 在 Phase 7 报告前检查。
|
||||
|
||||
---
|
||||
|
||||
## 1. 数据收集(DATA — Data Quality)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------------- | ---- | ------ | ---- |
|
||||
| 1.1 | 分析范围已明确(tag range / time range) | ☐ | ☐ | |
|
||||
| 1.2 | Commit 按类型统计已运行(BREAKING/feat/fix/...) | ☐ | ☐ | |
|
||||
| 1.3 | Revert 数和无前缀 commit 数已统计 | ☐ | ☐ | |
|
||||
| 1.4 | 文件改动 Top 10 已列出(按 +/− 排序) | ☐ | ☐ | |
|
||||
| 1.5 | 测试结果已获取(通过/失败/跳过数) | ☐ | ☐ | |
|
||||
| 1.6 | 测试文件改动情况已检查(无测试改动的源码改动) | ☐ | ☐ | |
|
||||
| 1.7 | Typecheck + Lint 当前状态已检查 | ☐ | ☐ | |
|
||||
| 1.8 | 依赖审计已运行,变更已记录 | ☐ | ☐ | |
|
||||
| 1.9 | 流程基础设施已检查(pre-commit/CI/PR/review/doc/artifact) | ☐ | ☐ | |
|
||||
| 1.10 | 已检查是否存在被删除的未合并 workflow/* 分支(废弃工作/资源浪费) | ☐ | ☐ | |
|
||||
| 1.11 | 已检查 merged workflow/* 分支与关联 worktree 在合并后已清理(本地分支删除 + worktree 移除) | ☐ | ☐ | |
|
||||
| 1.12 | 周期时长和团队规模已记录(压缩周期 = 质量风险,单点故障) | ☐ | ☐ | |
|
||||
| 1.13 | 范围估算验证:triage/scope 声明前已统计各标签下的 issue 数量(如 `Status/Blocked`、`Kind/*`、`Priority/*`),并与之前声明对比,偏差 >20% 标注为估算偏差 | ☐ | ☐ | |
|
||||
| 1.14 | 门缺陷逃逸分析已运行(§2.8 escape-rate 单指标,[org-internal #3061]):各 gate 的 clean runs / escapes / escape_rate 已统计,UNDER-POWERED(≥0.3)门已标注并回馈 §2.9 pre-flight(gate-trim 元进程已退役,[org-internal #3072] phase 3) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 2. 分析质量(ANAL — Analysis Quality)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 2.1 | "What went wrong" 每一项都有数据证据支撑 | ☐ | ☐ | |
|
||||
| 2.2 | 根因追溯到具体环节(测试缺失/模块过于庞大/提交不规范)| ☐ | ☐ | |
|
||||
| 2.3 | "What went well" 有可复用的模式描述 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 行动项(ACT — Action Items)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 3.1 | 行动项数 ≤ 5 | ☐ | ☐ | |
|
||||
| 3.2 | 每个行动项有具体文件路径(修改哪个 template/checklist/SKILL/config) | ☐ | ☐ | |
|
||||
| 3.3 | 每个行动项有 Owner 角色 | ☐ | ☐ | |
|
||||
| 3.4 | 行动项可度量(如何判断已执行) | ☐ | ☐ | |
|
||||
| 3.5 | 行动项不是"更努力"、"更仔细"等空泛措辞 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 闭环(LOOP — Feedback Loop)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 4.1 | 所有素材文件已更新(行动项中引用的文件) | ☐ | ☐ | |
|
||||
| 4.2 | 更新的素材变更已在 report 中记录 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 无指责原则(SAFE — Blameless)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------- | ---- | ------ | ---- |
|
||||
| 5.1 | 报告不含任何个人指责 | ☐ | ☐ | |
|
||||
| 5.2 | 所有问题归因于流程/工具/信息不足,非个人能力 | ☐ | ☐ | |
|
||||
@@ -0,0 +1,146 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# 迭代验证检查清单
|
||||
|
||||
> 迭代级质量门禁。在所有工作项通过代码评审后执行。
|
||||
> 确保集成正确、非功能达标、无回归。
|
||||
|
||||
> **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TST/BEH/GATE/REG)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TST=Test Quality、verification.md 的 TST=Test Suite Completeness、port.md 的 TST=Test Porting)。跨清单引用时必须带清单限定(如 code-review.md TST 5.8),不得使用裸代码。
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. 在当前迭代所有工作项通过代码评审后执行;
|
||||
2. 逐项检查,判定为"通过"或"不通过";
|
||||
3. "不通过"项注明具体指标和差距;
|
||||
4. 全部通过后迭代正式 Done;
|
||||
5. DoD 行判定词汇 PASS/FAIL/UNVERIFIABLE 为 verify 阶段专用:UNVERIFIABLE=
|
||||
"无自动化验证手段(缺测试/缺工具/缺基线)",与评审维度的 verdict 枚举
|
||||
PASS/WARN/FAIL/UNRESOLVED 有意区分(WARN=评审软通过,UNRESOLVED=评审员
|
||||
崩溃/超时),勿混用。
|
||||
6. **DAG 路由产物解析**:DAG 运行不存在 legacy `{slug}/04-plan-*` /
|
||||
`{slug}/03-design-*` 页面,条目 1.5 引用的验收条件页
|
||||
`{slug}/04-plan-05-acceptance-criteria` 按 DAG-route read map 解析:
|
||||
→ 节点 `acceptance_criteria`(`{epic-slug}/dag`,含
|
||||
`{epic-slug}/dag-nodes/{node-id}` 下沉子页)或节点工单正文显式标注;
|
||||
standalone 模式以请求本身为规格。
|
||||
|
||||
---
|
||||
|
||||
## 1. DoD 矩阵完整性检查(DOD — Definition of Done Matrix)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 1.1 | DoD 矩阵中每一条验收条件有明确判定(PASS/FAIL/UNVERIFIABLE) | ☐ | ☐ | |
|
||||
| 1.2 | 每个 PASS 判定有可验证的证据(测试名称、命令输出、测量值) | ☐ | ☐ | |
|
||||
| 1.3 | 每个 FAIL 判定注明了具体差距(期望值 vs 实测值) | ☐ | ☐ | |
|
||||
| 1.4 | UNVERIFIABLE 项标记了原因(缺测试/缺工具/缺基线)并标注了重新激活路径(目标 chunk+iteration)。不可仅标注"未来处理"(wishlist) | ☐ | ☐ | |
|
||||
| 1.5 | DoD 矩阵覆盖本迭代的全部需求(与 wiki page `{slug}/04-plan-05-acceptance-criteria` 对照) | ☐ | ☐ | |
|
||||
| 1.6 | DoD 表中声明的每个测试用例 ID 在代码库中存在、本轮已运行且通过;缺失或未通过项记为 FAIL 并注明差距(期望断言 vs 实际)。`MANUAL`/`BENCH` 条目须附人工记录或基准输出 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 2. 测试完备性检查(TST — Test Suite Completeness)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------- | ---- | ------ | ---- |
|
||||
| 2.1 | `bun run test:parallel` 全部通过(或项目等同命令)。单次执行:同一棵树只跑一次,结果同时用于 DoD 判定与回归分类(5.1),仅当代码在验证中途变更时重跑 | ☐ | ☐ | |
|
||||
| 2.2 | 单元测试 + 集成测试均已运行(若项目有分离命令) | ☐ | ☐ | |
|
||||
| 2.3 | 所有测试输出已捕获,失败用例有详细记录 | ☐ | ☐ | |
|
||||
| 2.4 | 对于使用 `git worktree add/remove` 的模块,`test:parallel` 可能因 git 内部文件锁定而挂起。接受隔离/串行测试结果(`bun test <file> --timeout 120000`)并通过,并注明 TST-GIT-CONTENTION | ☐ | ☐ | |
|
||||
| 2.5 | 已统计本轮 flaky test(间歇性失败/跳过)数量并与基线对比;新增 flaky test 需追溯根因并路由回 Developer;本轮未修复的 flaky test 已在 Phase 5.56 登记为 `flaky-test` labeled issue(FT-NNN) | ☐ | ☐ | |
|
||||
| 2.6 | E2E 浏览器可用性已检查(`npx playwright install --dry-run` 或等效命令)。若浏览器未安装,E2E 相关 DoD 条目预标记为 `⚠️ UNVERIFIABLE — Playwright browsers not installed`,并在 reactivation trigger 中注明安装命令。禁止在无浏览器环境中对 E2E 条目标记 PASS | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 构建与类型检查(BLD — Build & Typecheck)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------- | ---- | ------ | ---- |
|
||||
| 3.1 | `bun typecheck` 无错误(或项目等同命令) | ☐ | ☐ | |
|
||||
| 3.2 | `bun lint` 无错误(警告可记录但非阻断) | ☐ | ☐ | |
|
||||
| 3.3 | 构建产物可正常生成(如项目有 build 步骤) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 非功能需求验证(NFR — Non-Functional Requirements)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 4.1 | 本迭代覆盖的每个性能指标已测量(P50/P95/P99、吞吐量) | ☐ | ☐ | |
|
||||
| 4.2 | 性能指标与设计阈值和基线(前次迭代)双重对比 | ☐ | ☐ | |
|
||||
| 4.3 | 性能超出阈值的指标有差距分析和 profiling 热点 | ☐ | ☐ | |
|
||||
| 4.4 | 安全扫描已执行,无新高危漏洞引入 | ☐ | ☐ | |
|
||||
| 4.5 | 如无自动性能/安全测试,已标注为 UNVERIFIABLE 并建议具体工具 | ☐ | ☐ | |
|
||||
| 4.6 | 前端:Lighthouse / Core Web Vitals 性能评分未下降(FCP/LCP/TBT/CLS 均在阈值内) | ☐ | ☐ | |
|
||||
| 4.7 | 前端:bundle 体积分析已运行,无预期外增长(新增 chunk > 50KB 需说明理由) | ☐ | ☐ | |
|
||||
| 4.8 | 前端:axe-core / Lighthouse a11y 扫描无新增违规项 | ☐ | ☐ | |
|
||||
| 4.9 | 前端:可访问性(键盘导航、屏幕阅读器、对比度)已验证通过 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 回归检查(REG — Regression)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------------- | ---- | ------ | ---- |
|
||||
| 5.1 | 之前迭代通过的测试全部继续通过(零回归)——复用 2.1 同一次全量运行的结果判定,不重复执行 | ☐ | ☐ | |
|
||||
| 5.2 | 任何回归有失败单元测试名和可能原因分析 | ☐ | ☐ | |
|
||||
| 5.3 | 如无之前迭代的测试基线,首次迭代此项标记为 N/A | ☐ | ☐ | |
|
||||
| 5.4 | 用引用缺失/空 `{file:...}` 令牌的可选配置启动 TUI/CLI,确认启动不崩溃。回归测试位于 `<harness-package>/test/config/config-content-substitution.test.ts` | ☐ | ☐ | |
|
||||
| 5.5 | 每个失败用例已在 base 分支/最近绿色 commit 上单独复跑并分类:base 上也失败 → 预先存在(BF,Phase 5.55 登记);base 通过本分支失败 → 回归(阻塞并路由 Developer)。禁止把预先存在失败当作"别人的问题"丢弃 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 6. 报告完整性检查(RPT — Report Completeness)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 6.1 | 验证报告写入 wiki `{slug}/05-verify-iteration-{N}`(via `wiki 读写 API(见 TERMINOLOGY)`) | ☐ | ☐ | |
|
||||
| 6.2 | 报告包含 DoD 矩阵汇总(PASS/FAIL/UNVERIFIABLE 计数) | ☐ | ☐ | |
|
||||
| 6.3 | 报告包含 NFR 验证结果 | ☐ | ☐ | |
|
||||
| 6.4 | 报告包含回归检查状态 | ☐ | ☐ | |
|
||||
| 6.5 | 报告包含 FAIL 项的处理路径(路由回 Developer 或标记为已知) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 7. 上下文卫生验证(CTX — Context Hygiene)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | ----------------------------------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 7.1 | 迭代执行中每个 stage 边界执行了压缩,或确认上下文未超限无需压缩 | ☐ | ☐ | |
|
||||
| 7.2 | 每次压缩后重读了 wiki `{slug}/` 页面恢复当前 slug/stage(非仅依赖压缩摘要);读取模式见 `_shared/gitea-read-patterns.md`(deprecated: `.artifacts/{slug}/`) | ☐ | ☐ | |
|
||||
| 7.3 | 多轮 review 的 round 边界压缩遵循(round ≥ 2 时 compact + 重读 synthesis comment 与 commit status(`octopus review status --stage ...`)) | ☐ | ☐ | |
|
||||
| 7.4 | 未在 stage 中途(tool-call 循环 / sub-agent 派发过程中)执行压缩 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 8. 技术债务登记检查(TD-LOCAL — Tech Debt Local Registration)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 8.1 | 本迭代全部 UNVERIFIABLE / ACCEPTED_RISK / `[OPEN]` 项均已登记为源票 `## TD 登记` 评论中的行(registry-first,`ticket-lifecycle.md`;`filing.sprint-mode: true` 期间禁止升格独立票) | ☐ | ☐ | |
|
||||
| 8.2 | 每行含 `TD-NNN`、type、Severity、origin、一句话摘要与可客观验证的 Reactivation Trigger;TD-NNN 为注册期分配——经 `script/td-alloc.sh` 从号段台账(TD allocation ledger,常设 tracker)取号,任何分配必须先落台账 td-alloc 评论([org-internal #3322] 互斥) | ☐ | ☐ | |
|
||||
| 8.3 | 两处登记的 `TD-NNN` 标识一致,且每项都有可客观验证的 Reactivation Trigger | ☐ | ☐ | |
|
||||
| 8.4 | 邻近配额复核([org-internal #3002] G3):报告含本迭代触碰区域的登记册带走情况(consumed/applicable,或 `0 applicable`);与本 run 的债务配额记录一致(legacy roadmap 状态追踪表已归档,[org-internal #3072] phase 3) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 9. 基线失败登记检查(BF-LOCAL — Baseline-Failure Registration)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 9.1 | 本迭代全部预先存在失败(Phase 2.6 分类为 pre-existing)均已创建为 Gitea issue(label `baseline-failure`,via `工单 API(见 TERMINOLOGY)list(labels="baseline-failure")` 可查) | ☐ | ☐ | |
|
||||
| 9.2 | 每个 baseline-failure issue 含 `BF-NNN`(标题)、Test 标识、Severity(Priority label)、`## Parent` 交叉链接、可在 base commit 复现的 Reproduction 步骤 | ☐ | ☐ | |
|
||||
| 9.3 | 已对存量 `baseline-failure` issue 去重(按 failure signature 匹配——错误/断言形状 + 受影响表面,同一根因家族可跨多个测试,与 verify Phase 5.55 及 `core/rules/ticket-lifecycle.md` BF 家族伞口径一致),未重复登记已知失败;若本迭代修复了既有 BF,已关闭对应 issue 并引用修复 commit(编号/去重规则见 `core/rules/testing.md`) | ☐ | ☐ | |
|
||||
| 9.4 | 验证报告 "Baseline Failures (Pre-existing)" 段落记录了 `BF-NNN → #NNNN` 映射(无则记 "0 baseline failures");未登记任何失败时本节为空方为通过 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 10. Flaky Test 登记检查(FT-LOCAL — Flaky-Test Registration)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | --------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 10.1 | 本迭代全部未修复 flaky test 均已创建为 Gitea issue(label `flaky-test`,via `工单 API(见 TERMINOLOGY)list(labels="flaky-test")` 可查) | ☐ | ☐ | |
|
||||
| 10.2 | 每个 flaky-test issue 含 `FT-NNN`(标题)、Test 标识、Severity(Priority label)、Failure rate、`## Parent` 交叉链接、复现步骤(多次重跑) | ☐ | ☐ | |
|
||||
| 10.3 | 已对存量 `flaky-test` issue 去重(按 failure signature 匹配——错误/断言形状 + 受影响表面,与 verify Phase 5.56 及 `core/rules/ticket-lifecycle.md` FT 家族伞口径一致);未重复登记已知 flaky;已稳定的 flaky 已关闭并引用修复 commit(编号/去重规则见 `core/rules/testing.md`) | ☐ | ☐ | |
|
||||
| 10.4 | 验证报告 "Flaky Tests (Intermittent)" 段落记录了 `FT-NNN → #NNNN` 映射(无则记 "0 flaky tests") | ☐ | ☐ | |
|
||||
@@ -0,0 +1,116 @@
|
||||
# 工件寻址契约(Core,后端中立)
|
||||
|
||||
> Core 原生契约(Increment 3,无 dogfood 对应源文件)。术语对照见
|
||||
> `core/adapters/TERMINOLOGY.md`;本文所有实例路径以占位符表述
|
||||
> (`<runs-root>` = 实例运行目录根),落地实例时按术语表绑定。
|
||||
|
||||
## 1. ref 语法
|
||||
|
||||
所有 Tier-2 工件的规范引用格式:
|
||||
|
||||
```
|
||||
{backend}:{kind}/{owner}/{repo}/{id}#{anchor}
|
||||
```
|
||||
|
||||
- `{backend}` — 工单后端标识(如 `gitea`);由 adapter 层注册。
|
||||
- `{kind}` — 工件类别,枚举见下表;新类别扩展时须同步各 adapter 的
|
||||
resolve 实现并在本表登记。
|
||||
- `{owner}/{repo}` — 仓库坐标。
|
||||
- `{id}` — 工件标识:issue/PR 用数字,wiki 页用页名(可含 `/`),
|
||||
评论用 `{issue-number}c{comment-id}`,commit-status 用 `{sha}@{context}`。
|
||||
- `#{anchor}` — 可选锚点(页内段落、行号等),解析时透传不解释。
|
||||
|
||||
| kind | 含义 | id 形态 |
|
||||
| --------------- | --------------- | ---------------------- |
|
||||
| `issue` | 工单 | 数字 |
|
||||
| `issue-comment` | 工单评论 | `{issue}c{comment-id}` |
|
||||
| `wiki-page` | Tier-2 工件库页 | 页名(含 `/`) |
|
||||
| `commit-status` | 提交状态 | `{sha}@{context}` |
|
||||
| `pr` | 合并请求 | 数字 |
|
||||
|
||||
## 2. 页名文法(Core 契约)
|
||||
|
||||
Tier-2 工件库的**常规页名**:
|
||||
|
||||
```
|
||||
{slug}/{type}-{seq:02d}-{title}
|
||||
```
|
||||
|
||||
- `{slug}` 限 `[a-z0-9-]`;`{seq}` 两位零填充;`{title}` 限
|
||||
`[a-z0-9-]`(CJK 标题按 adapter 层编码规则处理)。
|
||||
|
||||
### 2.1 例外页(全枚举)
|
||||
|
||||
以下页名不受常规文法约束(勘自实例的全部真实约定):
|
||||
|
||||
| 例外页 | 形态 | 说明 |
|
||||
| -------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------- |
|
||||
| 评审轮次页 | `{slug}/reviews/{stage}/round{N}/{page}` | `page` ∈ `task-{ROLE}`、`revision-summary` |
|
||||
| 评审终报 | `{slug}/reviews/{stage}/final/report` | 含 DAG task 变体 `…/final/report-task-{node-id}` |
|
||||
| 验证报告 | `{slug}/05-verify-iteration-{N}` | 常规前缀 + 无 title 段的变体 |
|
||||
| 验证报告(里程碑) | `{epic-slug}/05-verify-milestone-{M-id}` | 同上 |
|
||||
| 验证报告(任务节点) | `{epic-slug}/05-verify-task-{node-id}` | 同上 |
|
||||
| DAG 工件 | `{epic-slug}/dag`、`{epic-slug}/dag-nodes/{node-id}`、`{epic-slug}/dag-coverage` | 单门 DAG 管线工件 |
|
||||
| DAG 共享契约 | `{epic-slug}/shared/{file}` | 跨会话契约 |
|
||||
| bugfix 附件 | `{slug}/repro-notes`、`{slug}/test-report`、`{slug}/bugfix-report`、`{slug}/ABORT` | 单段固定名 |
|
||||
| 原型/笔记 | `{slug}/prototype-debt`、`{slug}/spike-report`、`{slug}/impl-notes` | 单段固定名 |
|
||||
| 设计修订 | `{slug}/03-design-amendments` | 常规前缀 + 固定名 |
|
||||
| 浏览器证据 | `{slug}/verify/evidence/{name}` | 验证证据页 |
|
||||
| 审计轮次页 | `audit/{date}/round{N}/{page}` | 日期 slug 例外;`page` ∈ `synthesis`、`task-{ROLE}` |
|
||||
| 审计终报 | `audit/{date}/final/report` | 日期 slug 例外 |
|
||||
| 回顾报告 | `_retrospectives/{cycle-name}` | 跨 slug 命名空间例外 |
|
||||
| 技能评估 | `_evals/{skill-name}/{page}` | 评估命名空间例外 |
|
||||
| 移植工件 | `port-{name}/source-analysis/{file}`、`port-{name}/self-check` | 移植命名空间例外 |
|
||||
| 回顾归档 | `_archive/{slug}/…` | 归档命名空间例外 |
|
||||
|
||||
> 历史只读页名(旧管线产物,仍可读取):`{slug}/01-stakeholder-interview`、
|
||||
> `{slug}/02-requirements-index`、`{slug}/02-req-{seq:02d}-{title}`、
|
||||
> `{slug}/02-03-req-design`、`{slug}/03-design-{seq:02d}-{title}`、
|
||||
> `{slug}/03-adr-{NNNN}-{title}`、`{slug}/04-plan-index`、
|
||||
> `{slug}/04-plan-{seq:02d}-{title}`、`{slug}/roadmap/{page}`。
|
||||
|
||||
### 2.2 kind ↔ type 映射
|
||||
|
||||
| 工件类别(kind 语境) | 页名 `{type}` 段 |
|
||||
| --------------------- | ---------------------------------------------------------------- |
|
||||
| 验证报告 | `05-verify-iteration` / `05-verify-milestone` / `05-verify-task` |
|
||||
| 设计文档 | `03-design` / `03-adr` / `03-design-amendments` |
|
||||
| 计划文档 | `04-plan` |
|
||||
| 需求文档 | `02-req` / `02-requirements-index` |
|
||||
| 评审工件 | `reviews`(目录段,非前缀) |
|
||||
| DAG 工件 | `dag` / `dag-nodes` / `dag-coverage` |
|
||||
| 审计工件 | `audit`(日期前缀命名空间) |
|
||||
|
||||
## 3. Tier-1 ↔ Tier-2 映射
|
||||
|
||||
wiki 页 `{slug}/…` 与实例运行目录 `<runs-root>/{slug}/…`(Tier-1 本地
|
||||
结构化工件,见 Two-Tier 规则)按 slug 一一对应:
|
||||
|
||||
- 页 `{slug}/reviews/{stage}/round{N}/findings-{DIM}.json` ↔ 本地
|
||||
`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIM}.json`;
|
||||
- 页面正文承载 Tier-2 决策记录;原始发现、工作草稿留在 Tier-1 本地。
|
||||
|
||||
**文件名 URL 编码规则**:页名映射为本地文件名时 `/` → `%2F`
|
||||
(逆向解码同理);`.-` 尾缀是实例后端生成的 slug 产物,解码时剥除。
|
||||
|
||||
## 4. 双向链接不变量
|
||||
|
||||
1. **工件 ↔ 工单引用成对**:工件发布到工件库后,源工单侧必须有反向
|
||||
索引(工件索引评论);工单侧索引的每一行必须指向真实存在的工件。
|
||||
2. **单评论聚合**:一个源工单有且仅有一条工件索引评论,各技能只
|
||||
原位增改自己的行,绝不发第二条。
|
||||
3. **归档补全**:工单关闭时,关闭方 agent 原位编辑索引评论——加归档
|
||||
横幅、全部行重读优先级置 `ARCHIVE`;不删行、不改位置列。
|
||||
|
||||
## 5. adapter 义务
|
||||
|
||||
每个工单后端 adapter 必须实现三个操作:
|
||||
|
||||
- **parse** — 解析 ref 字符串为 `{backend, kind, owner, repo, id, anchor}`;
|
||||
- **resolve** — 把解析结果解析为该后端可调用的 API 形态(端点、工具名、
|
||||
参数),API 调用形态归 adapter 层,Core 不约束;
|
||||
- **validate** — 校验页名符合第 2 节文法(含例外枚举)。
|
||||
|
||||
页名规范是**后端中立契约**;仓里的共用解析器
|
||||
`script/resolve-artifact-ref.ts` 提供 parse 与页名/文件名编解码、
|
||||
Tier-1 路径预测的中立实现,adapter 可复用。
|
||||
@@ -0,0 +1,38 @@
|
||||
> Core 中立版(Increment 4 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Artifact-Index Guards (mandatory)
|
||||
|
||||
> Check-mechanized (injection-budget batch-3, [org-internal #3699]): guards
|
||||
> 1-3's checkable invariants run in the repo's artifact-index-check script
|
||||
> (`<artifact-index-check-cmd> <ticket>`, see the term mapping; exit 4 =
|
||||
> violation, `--json` summary; tolerant of wild archive-marker dialects).
|
||||
> This file is the normative definition, NOT in any `agents_md` injection
|
||||
> whitelist since 2026-08-29 — do not re-add; the checks run at write/close
|
||||
> time regardless. Full spec: Pattern 10 in the shared write-patterns doc —
|
||||
> this rule is its on-demand distill.
|
||||
|
||||
### 1. Legacy-prefix scan (never create a duplicate)
|
||||
|
||||
Before creating a `## 工件索引` comment, scan the issue's existing comments
|
||||
for the legacy prefix `## Pipeline 工件追踪表`. If found, UPGRADE it
|
||||
in place (edit, not create) and rename. NEVER create a second index comment
|
||||
(DEFECT-1 / [org-internal #1924]). Mechanized: reports the comment_id to edit.
|
||||
|
||||
### 2. Never-duplicate invariant (single-comment)
|
||||
|
||||
Exactly ONE index comment per source issue, maintained by in-place
|
||||
comment-edit via the ticket-backend comment API (reuse the same
|
||||
`comment_id`). Each skill adds/updates ONLY its own rows, never deletes
|
||||
another skill's rows. Mechanized: >1 prefixed comment (either prefix) →
|
||||
exit 4.
|
||||
|
||||
### 3. Archive-at-close (issue close → archive, by ANY closing agent)
|
||||
|
||||
When a source issue is CLOSED — by ANY agent, verify ran or not — the closing
|
||||
agent edits the index comment in place: (a) add the canonical archive banner
|
||||
(Pattern 10) ABOVE the table with the actual close date; (b) set EVERY row's
|
||||
重读 column to `ARCHIVE`. Do NOT delete rows or change 位置-column URLs;
|
||||
reversible in-place edit. Mechanized: closed-ticket marker + 重读
|
||||
completeness → exit 4. Sequence/actor notes: the issue-checklist-sync rule
|
||||
§ 归档动作; primary path `verify` Phase 5.6 (Verifier); verify-skipping routes
|
||||
(audit-only, documentation, standalone/bugfix) — the closing agent does it.
|
||||
@@ -0,0 +1,109 @@
|
||||
> Core 中立版(Increment 2 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Auto-approve stages
|
||||
|
||||
The `auto_approve` block in the instance config controls which pipeline gates
|
||||
the agent passes without asking the user. Stage identifiers are matched
|
||||
against the stage registry (harness 内建的阶段注册模块). Unknown or retired
|
||||
identifiers fall back to manual confirmation (fail-closed) with a one-time
|
||||
`auto_approve.unknown_stage` warning — copy ids verbatim from the table
|
||||
below.
|
||||
|
||||
## Scope boundary: `auto_approve` vs `permission`
|
||||
|
||||
Two **independent mechanisms** — confusing them is the most common
|
||||
"auto-approve isn't working" report:
|
||||
|
||||
- **`auto_approve`** gates **only the `question` tool** (pipeline decision
|
||||
questions; the harness 内建的自动批准判定模块); no effect on tool
|
||||
execution.
|
||||
- **`permission`** gates **tool / file-system operations** (`bash`, `read`,
|
||||
`write`, `external_directory`, …) and **never consults `auto_approve`**.
|
||||
`external_directory` defaults to ask, so outside-project prompts are
|
||||
expected even with every stage enabled.
|
||||
|
||||
**`auto_approve` does NOT suppress permission prompts — intentionally so.**
|
||||
To stop a permission prompt, change the matching `permission` rule or use the
|
||||
UI's "auto-accept permissions" toggle (browser-local, not the project
|
||||
`auto_approve` config).
|
||||
|
||||
| Stage | Gate | Risk |
|
||||
| --------------- | ---------------------------- | -------------------------------------------- |
|
||||
| `review-dag` | DAG single-gate review | Medium — sets direction |
|
||||
| `review-code` | Code review | Medium — gates what reaches main |
|
||||
| `verify` | Iteration verification | Medium — runs tests / DoD matrix |
|
||||
| `audit-process` | Process infrastructure audit | Medium — read-only but gates process changes |
|
||||
| `merge-pr` | Create PR + merge to main | High — irreversible landing |
|
||||
|
||||
> Risk values mirror the stage registry's `BUILTIN_STAGES[].risk` — the
|
||||
> registry is the runtime authority; if this table and the registry disagree,
|
||||
> fix this table. `merge-pr` is excluded from default/preset `stages` arrays
|
||||
> (not structurally blocked; explicitly adding it is not recommended).
|
||||
> Legacy stage retirement (12 → 5): the phase-3 retirement ruling doc
|
||||
> (Tier-2 reference, reachable via the ticket backend).
|
||||
|
||||
Config shape: `"auto_approve": { "stages": ["review-code", "verify"], "stageless": false }` — `stages` empty (`[]`) = all manual; `stageless: true` also auto-approves questions called without a stage. Absent block defaults to `{ stages: ["review-code", "verify", "audit-process"], stageless: false }`; the removed `enabled` field migrates on load (mapping: Tier-2 reference page `rules/auto-approve-detail`). Presets (`safe`/`full`/`off`/`show`): same reference page — `full` never includes `merge-pr`.
|
||||
|
||||
### Stage matching behavior
|
||||
|
||||
- Question has a stage → auto-approved iff that stage is in `stages`.
|
||||
- Stageless question → auto-approved iff `stageless: true`; otherwise prompts.
|
||||
- `destructive: true` → ALWAYS prompts, regardless of config — the safety net
|
||||
for irreversible actions; business-decision questions are never covered by
|
||||
a stage whitelist (§"Mandatory destructive list").
|
||||
|
||||
Check logic: the harness 内建的问询与自动批准判定模块
|
||||
(resolveAutoApproveDecision).
|
||||
|
||||
### Live-gate degradation chain ([org-internal #3531])
|
||||
|
||||
An auto-approved question is NOT consumed instantly: L1 — a live human
|
||||
watching the session (SSE declaration / fresh presence ping) answers within
|
||||
`question_live_gate.timeout_sec` (default 600) and overrides the default;
|
||||
L2 — blocking questions with a `#<ticket>` title and a ticket-backend token
|
||||
fall back to a ticket comment answered as `**answer <question-id>** <label>`
|
||||
(default 3600s); L3 — auto default (first option, annotated
|
||||
`live-gate … → auto default`). `timeout_sec: 0` disables the whole gate
|
||||
(legacy instant default); `l2_timeout_sec: 0` disables only L2.
|
||||
`nonblocking: true` questions never reach L2/L3 — no live human → parked in
|
||||
the pending-answer queue, answered later from the console. Every final
|
||||
decision publishes a `question.degradation.routed` event.
|
||||
**`destructive: true` is untouched by the chain**: `decide()` rejects it
|
||||
before the gate runs (iron law).
|
||||
|
||||
### Mandatory destructive list (业务决策类问询强制清单)
|
||||
|
||||
Origin: [org-internal #3223] — a stage whitelist auto-answered two
|
||||
irreversible production data changes because nothing marked them
|
||||
`destructive`. A stage whitelist grants "execution gates may proceed
|
||||
uninterrupted"; it must NEVER grant "business decisions may be
|
||||
self-answered". `stage` names the pipeline phase, `destructive` names the
|
||||
business risk.
|
||||
|
||||
**Iron Law: a question whose recommended/executed action falls in ANY category
|
||||
below MUST be called with `destructive: true`. The auto-approve stage whitelist
|
||||
is never a substitute for human confirmation of these categories.**
|
||||
|
||||
1. **Merge / release landing** — merging a PR to main, cutting a tag or
|
||||
release, deploying/promoting to production (server-side auto-merge is a
|
||||
human-configured policy robot, never agent self-answer latitude; kill
|
||||
switch: the auto-merge-low-risk runbook, Tier-2 reference via the ticket
|
||||
backend).
|
||||
2. **Production database writes** — upsert/update/delete against production
|
||||
data stores, production data migrations, live-data backfills.
|
||||
3. **Billing / plan changes** — plan or tier migration, pricing/concurrency
|
||||
changes, anything touching billing state.
|
||||
4. **Org / tenant structure changes** — org or workspace migration, tenant
|
||||
topology changes, moving resources between orgs.
|
||||
5. **Deletion of durable resources** — deleting data, branches, worktrees,
|
||||
deployments, or any resource outside `/tmp` and session-scratch space.
|
||||
6. **Persistent config writes** — config changes that outlive the session
|
||||
(writing to the instance config file, the instance process-config area,
|
||||
deploy/systemd units, or other on-disk configuration consumed by other
|
||||
sessions/services).
|
||||
|
||||
This list is the floor, not the ceiling: any other irreversible
|
||||
business-visible decision also carries `destructive`. When in doubt, mark it —
|
||||
a redundant prompt costs one round-trip; an auto-approved production write can
|
||||
cost the data. Mirrored in the question tool's self-description for paths
|
||||
without L1 injection.
|
||||
@@ -0,0 +1,56 @@
|
||||
## Claim Discipline (mandatory)
|
||||
|
||||
Every status assertion about work — "tests pass", "build succeeds", "bug fixed",
|
||||
"work complete", "no regressions", "fully resolves" — is a CLAIM that requires
|
||||
FRESH evidence produced this turn. Confidence is not evidence; a prior run is
|
||||
not evidence; the change "looking right" is not evidence. Baseline testing
|
||||
showed agents asserting "tests pass, no regressions introduced" for changes
|
||||
whose tests were never run — this rule exists to stop that.
|
||||
|
||||
**Iron Law: NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE.**
|
||||
|
||||
Before asserting any status, run the gate:
|
||||
|
||||
1. **IDENTIFY** — what exact command/output proves this claim?
|
||||
2. **RUN** — execute it fresh, this turn (do not recall a prior result).
|
||||
3. **READ** — the full output and exit code, not just the tail.
|
||||
4. **VERIFY** — does the output actually confirm the claim?
|
||||
5. **ONLY THEN** make the claim, and cite the evidence (`file:line`, test name,
|
||||
exit code, the command run).
|
||||
|
||||
**Red-flag language** — reword or remove unless you attach evidence: "should
|
||||
work", "probably", "seems to", "I'm confident", "fully tested", "comprehensive",
|
||||
"no regressions", "fully resolves", "works in all cases", "regardless of why",
|
||||
"exactly", and any expression of satisfaction ("Done!", "Great!", "Perfect!")
|
||||
uttered before verification has run.
|
||||
|
||||
**If you cannot run the proving command** (hypothetical/analysis task with no
|
||||
codebase, blocked environment): say so — "not verified; would require running X"
|
||||
— and downgrade the claim from assertion to hypothesis. An honest "unverified"
|
||||
beats a confident unevidenced claim.
|
||||
|
||||
This applies everywhere: commit messages, PR bodies, status reports, review
|
||||
conclusions, handoff summaries, and verbal confirmations to the user.
|
||||
|
||||
## Tool-failure honesty (never fabricate results)
|
||||
|
||||
The Iron Law above governs claims about work; this section governs the
|
||||
**source** of those claims. When a tool you need is unavailable, returns an
|
||||
error, returns empty, or you cannot otherwise access a required resource, you
|
||||
MUST report the failure honestly. NEVER fabricate, invent, or hallucinate a
|
||||
plausible-looking result to fill the gap.
|
||||
|
||||
- An honest "I could not call `gitea_wiki__get_page`, so I cannot review
|
||||
dimension Y" is always correct. An honest failure beats a confident fiction.
|
||||
- Producing a complete, specific-looking structured result — review JSON with
|
||||
findings, severity levels, and file references, all invented — without a
|
||||
successful tool call that supplies the evidence is a **critical defect**,
|
||||
more dangerous than aborting: the output looks credible but is entirely
|
||||
fictional and silently poisons every downstream decision.
|
||||
- This covers every tool-gated output: review findings, test results, file
|
||||
contents, API responses, search hits, metrics. If the tool did not return
|
||||
it, you do not know it — say so.
|
||||
|
||||
Origin: [org-internal #2115] root cause 5 / [org-internal #2118] — deepseek-v4-flash reviewers fabricated
|
||||
full review JSON when the `gitea_wiki__*` tools were unavailable to subagents.
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
## Code Graph First (mandatory)
|
||||
|
||||
Before any cross-file understanding or change, query the code graph first. Do
|
||||
NOT grep + read whole files to reconstruct relationships.
|
||||
|
||||
- **Session start**: run `codegraph_status` to confirm the graph is built; if
|
||||
empty, run `codegraph init -i` from the project root once. The graph persists
|
||||
and is reused for the whole session.
|
||||
- **Trust but verify stats**: `codegraph_status` can return a stale or partial
|
||||
snapshot. If counts look low, run `codegraph_status` again — codegraph
|
||||
auto-syncs on file changes (2s debounce via native OS file watcher).
|
||||
Do not run structural queries against a graph you suspect is incomplete.
|
||||
- **Structural questions** ("who calls / what depends on / where defined / what
|
||||
breaks if I change this") MUST use `codegraph_explore` (primary, for flows and
|
||||
surveys), `codegraph_search` (locate symbols), `codegraph_callers` (every call
|
||||
site).
|
||||
Do not piece relationships together from grep + read output.
|
||||
- **Reading an implementation**: use `read(filePath, symbol: "<name>")` to fetch
|
||||
only the target symbol's line range. Do not read an entire large file to see
|
||||
one function.
|
||||
- **grep is the fallback, not the default**: use grep only for fuzzy text the
|
||||
graph cannot answer (comments, string literals, config text). Use
|
||||
`codegraph_node` with `symbolsOnly: true` for a cheap structural overview instead of Glob.
|
||||
- **Delegate exploration**: prefer running graph + read inside an Explorer
|
||||
subagent and returning only `file:line` results and conclusions, to keep the
|
||||
main context lean.
|
||||
|
||||
## Per-worktree indexes (mandatory for worktree sessions)
|
||||
|
||||
The main checkout's `.codegraph/` indexes ONLY that tree. A git worktree lives
|
||||
outside the repo path, so nearest-`.codegraph/` resolution finds nothing under
|
||||
it — graph queries in a workflow worktree silently degrade to grep+read
|
||||
(the [org-internal #2561] / TD-006 gap).
|
||||
|
||||
- `script/session-worktree.sh create` and `script/scratch-worktree.sh create`
|
||||
build a per-worktree index automatically: `codegraph init -i <path>` runs in
|
||||
the background (best-effort — missing CLI, existing index, or init failure
|
||||
never blocks worktree creation). Set `CODEGRAPH_SKIP=1` to disable
|
||||
(selftests / CI).
|
||||
- Worktrees created BEFORE this hook landed have no index: run
|
||||
`codegraph init -i` once from inside them.
|
||||
- First query after creating a worktree may hit a still-building index; if
|
||||
results look empty, check `codegraph status` inside the worktree, then retry.
|
||||
- Kickoff readiness (`checkCodegraph`) checks `<root>/.codegraph/codegraph.db`
|
||||
per root, and `codegraph serve --mcp` resolves from the session's cwd — both
|
||||
pick the worktree's own index once it exists.
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
> Core 中立版(Increment 2 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Compaction Discipline (mandatory)
|
||||
|
||||
Context compaction keeps long pipelines inside the model's context window.
|
||||
Two mechanisms exist:
|
||||
|
||||
1. **Stage-boundary compaction** (main session / orchestrator) —
|
||||
capacity-driven [org-internal #2601]: compact at a clean stage boundary
|
||||
ONLY when a capacity trigger holds (below), not merely because the
|
||||
boundary was reached.
|
||||
2. **Round-boundary compaction** (multi-round reviews) — compact at every
|
||||
review round ≥ 2, after the round's state is persisted.
|
||||
|
||||
### Stage-boundary compaction (capacity-driven, [org-internal #2601])
|
||||
|
||||
The main session orchestrates the whole pipeline while delegating stages to
|
||||
subagents, but still accumulates their result summaries, planning reasoning,
|
||||
and todo state — over a full pipeline this can exhaust the context window.
|
||||
|
||||
**Rule — compact at clean boundaries only when capacity demands it.** Every
|
||||
stage persists its complete output to durable artifacts (Tier-2 artifact
|
||||
pages under `{slug}/…`, Tier-1 run workspace files) on completion. At each
|
||||
CLEAN boundary (artifacts written, next stage not yet dispatched), evaluate
|
||||
the triggers:
|
||||
|
||||
1. **Capacity trigger** — the harness's compaction recommendation has fired,
|
||||
context is near the threshold, or the agent observes context-pressure
|
||||
symptoms (truncation, degraded recall of earlier stage outputs).
|
||||
2. **Projection trigger** — the stages REMAINING on this route (skip list in
|
||||
the routing table) will not fit alongside the context already
|
||||
accumulated, based on per-stage growth observed so far.
|
||||
|
||||
A trigger holding → compact at the NEAREST clean boundary. No trigger → KEEP
|
||||
the context: preserved working context is worth more than the tokens it costs.
|
||||
|
||||
**Mixed capacity threshold — min(water_level, absolute cap)
|
||||
[org-internal #3321] / TD-434]**: the capacity threshold is
|
||||
`min(context × water_level, compaction.max_context_tokens)` — whichever is
|
||||
LOWER (`max_context_tokens` default 100_000; `0` restores pure water_level
|
||||
semantics [org-internal #3342]). On large windows the 100k cap binds first.
|
||||
Measurement basis: the provider's REAL reported usage (input tokens), not
|
||||
local estimates; the rendered guidance and the Trigger A/B overflow paths
|
||||
resolve the same normalized values (harness 内建的压缩引导模块, see the
|
||||
instance term mapping).
|
||||
|
||||
**Sub-session posture [org-internal #3321]**: Explorers compact by returning
|
||||
promptly, persisting only what their skill protocol REQUIRES (review findings
|
||||
JSON to the run workspace reviews directory). Workers persist outputs to
|
||||
Tier 1 BEFORE finishing. The parent context is where the mixed threshold
|
||||
applies. Tier 1 boundary: two-tier-artifacts rule.
|
||||
|
||||
**Short runs target zero intra-run compaction** — an implement →
|
||||
review-code → verify run fits one window; clean boundaries do not compact
|
||||
without a trigger. (Rationale + retired legacy stages: Tier-2 reference page
|
||||
`rules/compact-detail`, reachable via the ticket backend.)
|
||||
|
||||
Clean boundaries where triggers are evaluated (live skills):
|
||||
|
||||
- After `analyze-dag` completes (before the `review-dag` single gate)
|
||||
- After each `review-*` converges (before the next production stage)
|
||||
- After `implement` / `verify` completes (before its review or the next stage)
|
||||
|
||||
**Never compact mid-stage** — not mid tool-call cycle, not mid subagent
|
||||
dispatch. Compact only at clean boundaries where artifacts are already
|
||||
persisted.
|
||||
|
||||
**Re-orient after compacting** (recovery contract): compaction discards the
|
||||
in-conversation record of the current slug and stage. Before resuming,
|
||||
re-read the persisted recovery set — the Tier 1 compact-recovery entry
|
||||
`index.json` in the run workspace, the issue's `## 工件索引` CORE rows, the
|
||||
frozen DAG copy (`{epic-slug}/dag`, `{epic-slug}/dag-nodes/{node-id}`) for
|
||||
DAG-routed runs, the latest review pages, or the DoD matrix
|
||||
(`{slug}/05-verify-*`) — to recover the current slug, stage, and outstanding
|
||||
items. Do not rely on the compacted summary alone.
|
||||
|
||||
**Identity verification precedes re-orientation [org-internal #2459].**
|
||||
Every compacted summary opens with a harness-injected `### Session Identity`
|
||||
card (agent, role/mode/ticket, CORE artifact rows, role re-read set),
|
||||
rendered from role-yaml `compact.preserve` lists and issue CORE rows by the
|
||||
harness 内建的压缩引导模块 (renderIdentityInjection; preserve lines keyed
|
||||
through identityPreserveLines). Role yaml preserve lists are
|
||||
harness-enforced — recovery is always via the persisted artifact, never
|
||||
conversation memory. Verify the card against the durable record
|
||||
(title/purpose, issue assignee + claim, orchestrator id); on mismatch stand
|
||||
by and report, never act on the summary's identity claims.
|
||||
|
||||
### Round-boundary compaction
|
||||
|
||||
Multi-round review skills (`review-artifact`, `review-code`) compact at every
|
||||
round boundary ≥ 2. Canonical text: shared compaction-round-boundary doc
|
||||
(injected by both review skills); read it for the exact orchestrator
|
||||
procedure — do not restate.
|
||||
|
||||
### The `min_turns_since_compaction` guard applies everywhere
|
||||
|
||||
The `compact` tool rejects calls made too soon after the previous one
|
||||
(`compaction.agent.min_turns_since_compaction`, default **3** assistant
|
||||
turns); no boundary type is exempt. Beyond `insufficient_turns` the tool has
|
||||
more guard outcomes (`min_interval`, `no_model`, `circuit_breaker_open`,
|
||||
`disabled` — rejected; `compaction_in_progress`, `deduplicated` — deferred;
|
||||
full table: Tier-2 reference page `rules/compact-detail`, reachable via the
|
||||
ticket backend). All are accept-and-continue: never retry past a guard,
|
||||
never work around the dedup/breaker state. A boundary whose compaction was
|
||||
rejected or deferred simply keeps its context; the persisted artifacts still
|
||||
bound what accumulates.
|
||||
|
||||
### Role-level re-read set
|
||||
|
||||
Role YAML `compact.preserve` lists are harness-enforced: after any compaction
|
||||
the session agent's preserve lines are injected at the summary head; recovery
|
||||
is always via the persisted artifact, never conversation memory.
|
||||
@@ -0,0 +1,159 @@
|
||||
> Core 中立版(Increment 4 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Issue Checklist Sync (mandatory, progressive)
|
||||
|
||||
The source issue's checklist is the **stakeholder-facing progress tracker**.
|
||||
It MUST be kept in sync with actual work state at every pipeline milestone —
|
||||
not just at close time. Stakeholders should be able to open the issue at any
|
||||
point and see exactly what is done, in progress, and deferred.
|
||||
|
||||
### Sync points (progressive)
|
||||
|
||||
| When | Who | What |
|
||||
|------|-----|------|
|
||||
| After the DAG artifact is frozen (`review-dag` PASS + freeze confirmation per `analyze-dag/SKILL.md` §Output & Lifecycle step 2, [org-internal #3379]) | Aggregator agent | Convert the source Epic's draft acceptance criteria into a formal checklist; annotate requirement-covered items `_(DAG frozen: {epic-slug}/dag v1)_` |
|
||||
| After each iteration commit (`implement` Phase 4.5) | Developer | Check off items delivered by this iteration |
|
||||
| After a PR is created | Developer | Append a `## 当前状态` live-status section (or update it) with PR number + mergeable; in an Epic task list, append the PR reference to the corresponding row |
|
||||
| After code review converges (`review-code` PASS, Phase E) | Orchestrator | Check off review-related items; annotate `_(reviewed: round N PASS)_`; update the `## 当前状态` review row; label the PR `ready-to-merge` — keep-mergeable automation takes over (hand-written keep-mergeable commits are retired) |
|
||||
| On CI key state transitions (first green / failure / cumulative goal reached) | Verifier or Developer | Update the process-type AC (e.g. "N consecutive green runs") progress note in the issue; record the run number |
|
||||
| At `verify` Phase 5.6 (final sweep before PASS) / issue close | Closing agent (Verifier on the primary path; otherwise the last role that closes the issue) | Final sweep — every remaining item gets annotated; **archive-at-close**: fold `## 当前状态`, then mark the `## 工件索引` comment archived per the artifact-index-guards rule guard #3 (see「归档动作」below) |
|
||||
|
||||
> Historical ([org-internal #3072] phase 3): the requirements/design/plan exit sync points
|
||||
> were archived with their skills (detail: Tier-2 reference page
|
||||
> `rules/issue-checklist-sync-detail`, reachable via the ticket backend).
|
||||
|
||||
Between sync points the checklist stays as-is. **PR / review / CI
|
||||
transitions** MUST also be visible via the live status note below — the
|
||||
checklist is not the only carrier.
|
||||
|
||||
### Live status note (`## 当前状态`)
|
||||
|
||||
For flows with externally visible transitions between skill exits —
|
||||
standalone bugfix, incident response, long-running reviews — maintain a short
|
||||
live status table near the top of the issue body, updated in place via the
|
||||
ticket-backend issue-update API at the transition moment (closes the
|
||||
stale-issue gap [org-internal #1668]/[org-internal #1689]]).
|
||||
|
||||
All roles use the literal heading `## 当前状态` (no suffix). If an issue carries
|
||||
a legacy variant (e.g. `## 当前状态 (live)` from [org-internal #1668]), normalize it to
|
||||
`## 当前状态` at the first transition touched. Example table + legacy-variant
|
||||
list: Tier-2 reference page `rules/issue-checklist-sync-detail`, reachable
|
||||
via the ticket backend.
|
||||
|
||||
Rules:
|
||||
|
||||
- One row per externally visible transition. Skill-exit sync points are
|
||||
surfaced by checklist annotations alone — do NOT add live-status rows for
|
||||
them. PR / review / CI transitions carry BOTH a checklist annotation AND
|
||||
a live-status row.
|
||||
- **Stage rows live on the board, not here**: pipeline stage transitions are
|
||||
represented by the Pipeline Stages board column (shared write-patterns
|
||||
Pattern 7.5), NOT by `## 当前状态` rows. Keep only the
|
||||
status-sync-automated rows (PR / 代码评审 / CI) and non-stage
|
||||
blockers/deferrals.
|
||||
- Update in place (same section heading `## 当前状态`), never append a second copy.
|
||||
- Optional for quiet pipeline flows; **MANDATORY for incident-response and
|
||||
standalone-bugfix flows** (long windows without skill exits).
|
||||
- At verify PASS / issue close, fold the table's content into checklist
|
||||
annotations and remove the section (see the「归档动作」section below).
|
||||
|
||||
### Automated sync ([org-internal #1833], timer-polled since [org-internal #2813])
|
||||
|
||||
PR / review / CI status-row transitions are automated by the `status-sync`
|
||||
poller (deployed as a host timer, every 5 min; event sources + latency table
|
||||
on the Tier-2 reference page `rules/issue-checklist-sync-detail`, reachable
|
||||
via the ticket backend). Roles do NOT hand-write those three row types. A PR
|
||||
body may carry several close refs (batch PRs, [org-internal #3731]
|
||||
`land-batch`): rows fan out to EVERY referenced issue
|
||||
(`parseClosesFixesWide` — [org-internal #3867], merged 2026-08-31) — Chinese 关闭/修复 refs
|
||||
drive rows too, even though the ticket backend never auto-closes those
|
||||
keywords. The remaining sync points (checklist annotations at skill exits,
|
||||
`## 当前状态` creation, deferral/pending marking, section fold-at-close) are
|
||||
still manual (content judgment, not event detection). Fallback for poller
|
||||
outages / manual replay: the `status-sync` manual dispatch
|
||||
(`issue`/`row`/`value`) — the row-write engine is a no-op when the section
|
||||
or row is absent, never creates or corrupts them; poller failures file a
|
||||
deduplicated `[status-sync-cron]` issue.
|
||||
|
||||
### Standalone bugfix / incident flows
|
||||
|
||||
Pipeline mode has natural sync points at skill exits; standalone bugfix and
|
||||
incident flows do NOT — the only boundaries are commit → PR → review → CI →
|
||||
merge, and EACH is a sync point with the same annotations as the progressive
|
||||
table above (full table: Tier-2 reference page
|
||||
`rules/issue-checklist-sync-detail`, reachable via the ticket backend): fix
|
||||
commit → tick fix-delivered ACs `_(commit {sha}: file)_`; PR → open/update
|
||||
`## 当前状态`; review convergence → tick review ACs + review row; CI
|
||||
transitions → process-AC progress with run number; verify PASS / close →
|
||||
final sweep, fold `## 当前状态`, archive per「归档动作」.
|
||||
|
||||
### DAG-routed tickets — node checklist variant
|
||||
|
||||
For tickets routed through the DAG pipeline (see workflow-routing rule
|
||||
§"DAG 路由 (v2)"), the task-node checklist has a fixed shape (spec-08
|
||||
§1/§2/§3):
|
||||
|
||||
- **Task tickets (nodes) use a fixed 5-row checklist** + an optional 6th row:
|
||||
验收标准 / 依赖就绪 / PR / 评审(review-code) / CI — **fixed 5 rows, fixed
|
||||
order, no free additions**. The `verify里程碑` row exists ONLY when the node
|
||||
fans into a milestone.
|
||||
- **Row 6 close-time semantics**: if at ticket close the milestone is not yet
|
||||
green, keep `- [ ]` + `_(Pending: M-01 未 green)_` — mapped to **Pending**, not
|
||||
STALE, so it satisfies the Iron Law (no stale items at close). After milestone
|
||||
verify PASS the aggregator agent flips it to `- [x]` in place.
|
||||
- **Epic aggregator `## DAG 状态` vs `## 当前状态`**: `## DAG 状态` is the
|
||||
node-status aggregator table (one row per DAG node — never carries ARCHIVE);
|
||||
`## 当前状态` carries only PR / 评审 / CI rows (no stage rows).
|
||||
- **Archive rule unchanged**: the archive marker stays ONLY on the `## 工件索引`
|
||||
comment (single-comment invariant). The body aggregator table is kept as a
|
||||
historical snapshot at close and does NOT carry ARCHIVE.
|
||||
|
||||
### Kind/MVP tickets — exemption ([org-internal #3061])
|
||||
|
||||
`Kind/MVP` tickets run the interactive no-pipeline mode: no stage exits →
|
||||
**nothing to sync**, no `## 当前状态` section (no PR/review/CI rows until
|
||||
graduation). The body itself is the living document: `## 决策日志` is
|
||||
append-only (superseded decisions struck through, never edited); `## Debt
|
||||
Register` rows update in place. On graduation (relabel `Kind/Feature`) normal
|
||||
sync rules resume from the DAG route; the decision log stays as backfill input.
|
||||
|
||||
### Annotation format
|
||||
|
||||
- **Done** → `- [x]` + parenthetical evidence: `_(PR #NNN: file/component)_`
|
||||
or `_(commit {sha}: file)_` if no PR yet.
|
||||
- **Deferred** → keep `- [ ]` + `_(Deferred: TD-XXX <reason>)_`.
|
||||
- **Pending** → keep `- [ ]` + `_(Pending: <blocker>)_`.
|
||||
|
||||
### How to sync (each point)
|
||||
|
||||
Find the source issue (`Closes #N` / `Fixes #N` in the PR body or commit
|
||||
messages, `## 父级 / Parent` / roadmap chunk mapping, or the issue the user
|
||||
referenced; none exists → skip). Fetch the body, map each `- [ ]` item
|
||||
**relevant to this sync point** to evidence (`- [x]` + `_(PR #NNN: …)_` /
|
||||
`_(commit {sha}: file)_`), deferral, or pending — leave out-of-scope items
|
||||
untouched for a later point — and update in place, preserving all
|
||||
non-checklist content. PR / review / CI transitions ALSO go into the
|
||||
`## 当前状态` live-status section.
|
||||
|
||||
### 归档动作(issue 关闭 = 归档)
|
||||
|
||||
**生命周期 = 工单生命周期**([org-internal #1929]):文档生命周期与源 issue 绑定 —— verify PASS → issue 关闭 = 归档。Tier-2 工件库无独立生命周期。
|
||||
|
||||
归档是 **显式动作**,由关闭 issue 的 agent 执行:主路径为 Verifier 在 `verify` Phase 5.6;路径跳过 verify 时(audit-only、文档类、standalone/bugfix)由关闭 issue 的 role 代为执行。无论哪个 actor,均遵循 artifact-index-guards 规则 guard #3(机制以共享 write-patterns Pattern 10 为准)。动作序列:
|
||||
|
||||
1. **Checklist 终扫**(Phase 5.6 既有步骤 / closing agent 补执行):每个 `- [ ]` 项都被注解(done / deferred / pending)。
|
||||
2. **Fold `## 当前状态`**(Phase 5.6 既有步骤 / closing agent 补执行):表格内容折入 checklist 注解后移除该节。
|
||||
3. **标记 `## 工件索引` 归档**:按共享 write-patterns Pattern 10 Archive-at-close 原位 edit(机制以 Pattern 10 为准)。横幅日期以实际关闭日为准。
|
||||
4. **关闭 issue**:PR body 的 `Closes #N` 在 merge 时自动关闭;无 PR 的流程由 **关闭 issue 的 agent** 执行工单后端的 issue-close API 完成闭环。
|
||||
|
||||
约束([org-internal #1928] 设计约束;机制细节以共享 write-patterns Pattern 10 Archive-at-close 为准,此处不重复):
|
||||
|
||||
- **L 级大件(>~300 行)留 Tier-2 工件库不删除** —— 页面与位置列 URL 保持不动(稳定 URL 供历史与共享契约引用)。
|
||||
- **索引/状态/归档判定全部收敛到 issue** —— Tier-2 工件库无独立生命周期;归档标记只落在 `## 工件索引` 评论。
|
||||
- 归档标记可逆(原位 edit):PR 被拒 / issue 重开时恢复行级 重读 即可。
|
||||
|
||||
### Iron Law
|
||||
|
||||
NO ISSUE MAY BE CLOSED WITH STALE CHECKLIST ITEMS. If a checklist item
|
||||
cannot be mapped to evidence or deferral at verify time, the iteration is
|
||||
NOT done.
|
||||
@@ -0,0 +1,72 @@
|
||||
> Core 中立版(Increment 4 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Issue Cross-Linking (mandatory)
|
||||
|
||||
Traceability flows in two directions. Both MUST be maintained so a
|
||||
reviewer can start at any node — an issue or a Tier-2 artifact-library page —
|
||||
and navigate to every related artifact without asking.
|
||||
|
||||
### Issue ↔ issue (parent/child groups)
|
||||
|
||||
When creating a **group** of related issues — an Epic with phase/P-level
|
||||
sub-tasks, a roadmap decomposed into chunk tickets, a bug with a
|
||||
regression-test follow-up, or any parent/child breakdown — the issues MUST be
|
||||
cross-linked:
|
||||
|
||||
- **Parent issue**: after creating all children, update the parent body so its
|
||||
task list references each child by `#<number>` (e.g. `- [ ] [org-internal #1556] — [P0] ...`).
|
||||
The ticket backend renders these as clickable links and auto-generates a
|
||||
back-reference on each child's timeline.
|
||||
- **Child issue**: its body MUST contain a `## 父级 / Parent` section referencing
|
||||
the parent by `#<number>`.
|
||||
- Never create a group of issues that is only one-way linked or not linked at
|
||||
all. The parent task list is the single navigation hub.
|
||||
|
||||
### Issue ↔ Tier-2 artifact (pipeline traceability)
|
||||
|
||||
When a production skill (`analyze-dag`, publishing the `{epic-slug}/dag`
|
||||
artifact; historically the pre-[org-internal #3072] producer skills) publishes
|
||||
an artifact to the Tier-2 artifact library, the link to the source issue is
|
||||
only **half-built** — the library's index page links forward to the issue,
|
||||
but the issue has no link back to the library. A stakeholder opening the
|
||||
issue sees no path to the pipeline artifacts — the traceability chain is
|
||||
broken.
|
||||
|
||||
After publishing, the skill MUST complete the reverse link on the source issue:
|
||||
|
||||
- **Post / update the "## 工件索引" comment** (工件索引协议, per the shared
|
||||
write-patterns doc Pattern 10): a single in-place-edited comment (via the
|
||||
ticket-backend comment-create, then comment-edit API; NEVER post a
|
||||
duplicate; legacy prefix "## Pipeline 工件追踪表" is upgraded in place). One
|
||||
row per artifact: `{工件 | 类型 | 版本 | 位置 | 重读}` where **重读** ∈
|
||||
{`CORE`, `ON-DEMAND`, `ARCHIVE`}.
|
||||
- **The CORE rows ARE the compaction re-read set** — after `compact`, recovery
|
||||
reads the 工件索引 comment and fetches every CORE row's 位置 (shared
|
||||
read-patterns doc Pattern 6), instead of hard-coded library paths. Skills'
|
||||
re-read instructions therefore point at the index.
|
||||
- **Archive-at-close(归档动作, [org-internal #1929]/[org-internal #1928])** — issue 关闭即归档;Tier-2 工件库无独立
|
||||
生命周期。`ARCHIVE` is not just a per-row re-read priority: at issue close the
|
||||
whole index is marked archived by the closing agent (Verifier at `verify`
|
||||
Phase 5.6 on the primary path; otherwise the Orchestrator / last-role that
|
||||
closes the issue) via in-place edit. The mechanism (banner + every row's
|
||||
重读 → ARCHIVE, single-comment invariant, rows/位置 URLs never deleted,
|
||||
L-level library artifacts keep stable URLs) is defined once in the shared
|
||||
write-patterns doc Pattern 10 Archive-at-close — reference it, do not
|
||||
restate. Sequence and actor: the issue-checklist-sync rule § 归档动作.
|
||||
- **Sync the issue's checklist** per the issue-checklist-sync rule for the
|
||||
matching exit point (live: the DAG-freeze sync point after `review-dag`
|
||||
PASS; the legacy requirements-exit / design-exit / plan-exit points were
|
||||
retired with their skills, [org-internal #3072] phase 3).
|
||||
- Skip if no source issue exists (no `Closes #N` / `Fixes #N` / roadmap chunk
|
||||
mapping ties this slug to an issue).
|
||||
|
||||
**Size/S & Size/M exception ([org-internal #1832]) — RETIRED (2026-08-21, [org-internal #3072]
|
||||
phase 3)**: retired with the legacy producer skills and the `sizing:` block.
|
||||
Historical indexes pointing at in-issue artifacts (`位置 = #comment-{id}`)
|
||||
remain valid, readable locations; new artifacts publish where their live
|
||||
skill/route puts them (DAG: library page `{epic-slug}/dag` etc.; MVP: ticket
|
||||
body).
|
||||
|
||||
Authoritative implementation patterns: shared write-patterns doc Pattern 3
|
||||
(issue↔issue) and Pattern 10 (issue↔artifact index) under the skills'
|
||||
shared-artifacts directory.
|
||||
@@ -0,0 +1,115 @@
|
||||
> Core 中立版(Increment 4 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Session Scope Guard (mandatory)
|
||||
|
||||
> Enforcement layer: precheck-mechanized (injection-budget batch-2,
|
||||
> [org-internal #3667]) — pre-flight sources 1-3 below live in the repo's
|
||||
> session-conflicts script, source 4 in the claim script's compare-and-swap.
|
||||
> This file is the normative definition, NOT part of any `agents_md`
|
||||
> injection whitelist since 2026-08-29 — do not re-add; the checks run at
|
||||
> claim time regardless.
|
||||
|
||||
Standalone and ticket-seeded sessions run without a pipeline around them —
|
||||
no plan, no reviewer keeping them inside their work item. When such a
|
||||
session hits a test failure NOT caused by its own change ([org-internal
|
||||
[org-internal #1803]], via [org-internal #1753]/[[org-internal #1744]]), it tends to fix the foreign
|
||||
failure in place (drifting off-task), and sessions seeded for the same issue
|
||||
collide on the same shared flaky test.
|
||||
|
||||
**Iron Law: if a test fails and it was NOT caused by your change, you MUST
|
||||
classify → file → continue. Do NOT fix it in place — unless that test IS your
|
||||
task.**
|
||||
|
||||
### The classify → file → continue protocol
|
||||
|
||||
1. **Classify** — rerun the failing test on the base commit / last-green:
|
||||
fails there too → **baseline failure (BF)**; intermittent across runs →
|
||||
**flaky test (FT)**; fails only with your change → **your regression** —
|
||||
fix it now.
|
||||
2. **File** — register every BF/FT as an issue in the ticket backend labeled
|
||||
`baseline-failure` or `flaky-test` with a `BF-NNN` / `FT-NNN` id, per the
|
||||
testing rule. Deduplicate against an existing BF/FT issue (comment your
|
||||
run evidence instead of filing a twin).
|
||||
3. **Continue** — return to your assigned task; cite the BF/FT issue number
|
||||
in your report / PR body so the next session sees it is owned.
|
||||
|
||||
A foreign failure does not block your task, but the task is NOT done until
|
||||
the failure is filed — an un-filed BF/FT is a process gap. Filing is the
|
||||
whole job: **the fix belongs to whoever owns that test**, not to you.
|
||||
|
||||
### One task, one owner — check before you start
|
||||
|
||||
Before starting work on an issue or code area, you MUST verify no other
|
||||
session already owns it. The pre-flight check spans four sources — (1) live
|
||||
sessions referencing `#N`, (2) a workflow branch already covering #N
|
||||
(worktrees, local branches, remote `workflow/*` refs), (3) an open PR
|
||||
referencing `#N`, (4) assignee + claim comment naming another owner. A hit
|
||||
on ANY source → the ticket is already owned: ABORT (or, for a DEAD claim,
|
||||
take over via the mechanized channel below; otherwise coordinate on the
|
||||
issue), never silently start a second owner.
|
||||
|
||||
**Mechanized ([org-internal #3667])**: the repo's session-conflicts script
|
||||
(`<session-conflicts-cmd> <ticket>`) scans sources 1-3 in one read-only pass
|
||||
(exit 4 = conflict, matching the claim script; source 1 fail-opens visibly
|
||||
when the app API is down — the backend hard guard [org-internal #1989]
|
||||
backstops); source 4 IS the claim script's compare-and-swap. Dead-claim
|
||||
takeover is mechanized in the claim script (see below); manual recipes:
|
||||
the headless-session-ops skill's ticket-recipe reference §0.
|
||||
|
||||
**Dead-claim takeover ([org-internal #3668]).** A claim whose session is
|
||||
gone (404 on the app API) and whose branch was never pushed is a DEAD claim
|
||||
— the [org-internal #3419] shape. Do NOT hand-edit the dead claim comment
|
||||
(the retired admin workaround — unauditable); run the claim script's
|
||||
takeover subcommand (`<claim-takeover-cmd> <ticket> <branch> --session-id
|
||||
<id>`) instead. Evidence rule (the claim script's header is normative): a
|
||||
competing claim is dead iff its named branch is ABSENT on the remote AND
|
||||
(its session 404s OR the comment is at least `CLAIM_TAKEOVER_STALE_DAYS`
|
||||
old — age >= the threshold, default 3d). Evidence holds → one atomic
|
||||
takeover: assignee re-set + a `**claim**` comment carrying
|
||||
`supersedes-claim #<id>` markers and the inline evidence (auditable chain,
|
||||
nothing edited) + branch push, with post-posting race re-verification.
|
||||
Evidence fails (session alive, branch pushed = completed claim, or fresh
|
||||
claim with unknown liveness) → exit 10, zero mutation — coordinate on the
|
||||
issue or salvage the worktree instead. A valid claim is never superseded.
|
||||
|
||||
**Claim-first (atomic 3-step claim).** The FIRST action when starting
|
||||
ticket-driven work is to claim the ticket — one atomic action of THREE
|
||||
steps, valid ONLY when all complete: (1) set the issue assignee to the
|
||||
working account, (2) post a claim comment naming the workflow branch and
|
||||
session id, (3) push the workflow branch to remote (even an empty commit —
|
||||
a purely local branch is invisible to other sessions, [org-internal
|
||||
[org-internal #2280]/[org-internal #2282]]). Use the repo's claim wrappers (staged-transaction claim
|
||||
script; exit codes and CAS semantics in its header; non-zero = NOT claimed)
|
||||
and the provision wrapper (adds the worktree + Tier-1 runs scaffold). The
|
||||
manual 3-step is the fallback where the scripts are unavailable, same
|
||||
re-verification.
|
||||
|
||||
**Merge-time equivalence check.** Before merging, scan the remote main
|
||||
branch's recent commits for `#<ticket>` — surfacing an equivalent
|
||||
implementation already on main *before* the merge rejection.
|
||||
|
||||
**Merge-time review gate ([org-internal #2345]).** `pipeline/review-code` is
|
||||
a required status: merge needs it green OR the route-based exemption
|
||||
(Kind/* close routes skip review-code — posted by the repo's
|
||||
review-gate-exemption script). Read the commit-status endpoint for the PR
|
||||
SHA: `success` + `review not required (route:` description → route
|
||||
exemption, proceed; `success` otherwise → proceed; missing / `error` /
|
||||
`pending` / other → BLOCK — a missing status never approves.
|
||||
|
||||
**Epic ownership table.** Multi-chunk parallel Epics keep a chunk →
|
||||
session/account ownership table on the parent issue — consult it and claim
|
||||
your chunk before starting.
|
||||
|
||||
Two sessions owning the same issue or code area is never faster — it is the
|
||||
exact collision this rule prevents. If a collision HAS happened, follow the
|
||||
failure-path playbook at the ownership-collision runbook (Tier-2 reference
|
||||
page, reachable via the ticket backend).
|
||||
|
||||
### Scope
|
||||
|
||||
Applies semantically to **every session** — pipeline subagents (Producer /
|
||||
Verifier / Reviewer / Tool) and standalone/ticket-seeded sessions alike;
|
||||
for subagents, "your task" is the delegated prompt's scope, and classify →
|
||||
file → continue applies unchanged. Filing rides the testing rule;
|
||||
launch-time owner checks live in the headless-session-ops skill (which
|
||||
points here). This file stays the normative definition — read on demand.
|
||||
@@ -0,0 +1,113 @@
|
||||
## Stash Discipline (mandatory)
|
||||
|
||||
> Enforcement layer: **L0 tool-enforced** (bash guard + husky hooks) — this
|
||||
> file is reference/documentation, NOT part of any `agents_md` per-turn
|
||||
> injection whitelist since the 2026-08-27 injection-budget batch-1. Do not
|
||||
> re-add it to a whitelist; the guard enforces regardless.
|
||||
|
||||
In the octopus worktree model, multiple parallel sessions share a single git
|
||||
repository. The git **stash stack is repository-global** (`stash@{0}`,
|
||||
`stash@{1}`, …) and is NOT isolated per worktree. When two sessions in
|
||||
different worktrees run `git stash` / `git stash pop`, they silently index
|
||||
each other's entries and one session's `pop` returns the other session's
|
||||
content — or `drop`/`clear` wipes uncommitted work belonging to a session in
|
||||
a different worktree.
|
||||
|
||||
This failure mode has already destroyed completed work in production ([org-internal #1655] /
|
||||
[org-internal #1629] — a finished 25-test implementation overwritten on the shared stack).
|
||||
|
||||
**Iron Law: NEVER use `git stash` (or `git stash pop` / `push` / `drop` /
|
||||
`clear` / `apply`) inside an octopus worktree.**
|
||||
|
||||
The bash tool enforces this at L0 via a safety-floor pattern that blocks
|
||||
`git stash` invocations. Use one of the following instead — they are
|
||||
worktree-local and cannot be clobbered by a parallel session:
|
||||
|
||||
### Approved alternatives
|
||||
|
||||
1. **WIP commit on the current workflow branch** (preferred for
|
||||
mid-iteration checkpoints):
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "wip: <slug> — <one-line state>"
|
||||
# … later, before the real commit:
|
||||
git reset --soft HEAD~1 # un-commit but keep the work staged
|
||||
```
|
||||
|
||||
The per-iteration commit model already expects these to be cleaned up by
|
||||
amend / interactive rebase before merge, so a `wip:` commit is never a
|
||||
long-term smell.
|
||||
|
||||
2. **Throwaway side branch** (preferred when you must switch branches and
|
||||
cannot commit yet):
|
||||
|
||||
```bash
|
||||
git switch -c wip/<slug>-<topic>
|
||||
git add -A && git commit -m "wip: <slug> — <one-line state>"
|
||||
git switch - # back to the workflow branch
|
||||
# … later:
|
||||
git cherry-pick wip/<slug>-<topic> # or merge / reset as needed
|
||||
git branch -D wip/<slug>-<topic>
|
||||
```
|
||||
|
||||
3. **`git diff > /tmp/octopus/<slug>.patch`** (last resort, when even a WIP
|
||||
commit is impossible — e.g. the index is in a conflicted state you do not
|
||||
want to disturb):
|
||||
|
||||
```bash
|
||||
git diff --binary > /tmp/octopus/<slug>.patch
|
||||
git apply /tmp/octopus/<slug>.patch # restore later
|
||||
```
|
||||
|
||||
Keep the patch under `/tmp/octopus/` so it survives the session but is
|
||||
never accidentally committed (see wiki page `rules/temp-files`, L2
|
||||
on-demand).
|
||||
|
||||
### What about switching away from a dirty tree?
|
||||
|
||||
If you must `git checkout <base>` to reproduce a failure but the worktree is
|
||||
dirty, do **not** reach for stash. Instead:
|
||||
|
||||
- WIP-commit first (option 1), or
|
||||
- Use the lifecycle-managed scratch-worktree script ([org-internal #2790]) to materialise
|
||||
the base commit in a *separate* worktree and run the failing test there:
|
||||
|
||||
```bash
|
||||
bash script/scratch-worktree.sh create <slug>-base <base>
|
||||
bash script/scratch-worktree.sh remove <slug>-base
|
||||
```
|
||||
|
||||
This is the pattern the `verify` skill uses for the regression-vs-baseline
|
||||
classification check. The script registers the worktree, symlinks
|
||||
`node_modules` from the source checkout when `bun.lock` matches, and the
|
||||
`sweep` backstop reclaims worktrees abandoned by dying sessions — bare
|
||||
`git worktree add /tmp/octopus/...` has no such recovery ([org-internal #2790]).
|
||||
|
||||
### Exceptions
|
||||
|
||||
- Running `git stash list` / `git stash show` to *inspect* the stack (no
|
||||
mutation) is allowed — it cannot lose work. The safety-floor pattern only
|
||||
blocks mutating subcommands (`push`, `pop`, `apply`, `drop`, `clear`,
|
||||
`create`, `store`, `branch`, and bare `git stash`).
|
||||
- The TUI's **prompt stash** (`prompt.stash` keybind, `usePromptStash`) is
|
||||
unrelated — it stashes *input text* in memory, not git refs, and is not
|
||||
affected by this rule.
|
||||
|
||||
### Enforcement
|
||||
|
||||
- **L0 (tool-enforced)**: `packages/octopus/src/tool/bash.ts` adds
|
||||
`/\bgit\s+stash\b(?:\s+(?:push|pop|apply|drop|clear|create|store|branch)|\s*$)/`
|
||||
to `SAFETY_FLOOR_PATTERNS`. Safety-floor items cannot be unblocked via
|
||||
configuration; bypass requires `--no-security-config`.
|
||||
- **L1 (this file)**: reference documentation only — removed from every
|
||||
`agents_md` injection whitelist (2026-08-27, see header); the L0 guard
|
||||
enforces regardless.
|
||||
- **L2 (skills)**: any skill that suggests `git stash` is a defect — file it
|
||||
under Kind/Enhancement with the `retrospective` label (`verify` SKILL.md
|
||||
already uses `script/scratch-worktree.sh` instead).
|
||||
|
||||
If a parallel-session stash collision happens again despite this rule, treat
|
||||
it as a Sev-2 process incident: stop work, recover via
|
||||
`git fsck --unreachable` / `git log -g --walk-reflogs stash`, open a follow-up
|
||||
referencing [org-internal #1655].
|
||||
@@ -0,0 +1,145 @@
|
||||
> Core 中立版(Increment 2 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Testing
|
||||
|
||||
- Avoid mocks as much as possible
|
||||
- Test actual implementation, do not duplicate logic into tests
|
||||
- Tests cannot run from repo root (guard: `do-not-run-tests-from-root`); run from package dirs.
|
||||
- **Local pre-check: run `<test-cmd:changed>`** from the owning package after editing source. It selects only tests affected by your uncommitted + branch changes (path-mapping ∪ CodeGraph direct dependents) and runs them in a single process — seconds, machine stays responsive. Use `--print` to see the selection without running, `--all` to scan beyond the home package.
|
||||
- **Before a delivery push: `<test-cmd:changed>` green + `<typecheck-cmd>` clean** — same selector as CI's PR side (local parity, [org-internal #4425]). The full suite (`<test-cmd:parallel>`) stays with nightly / `run-full-tests` / manual pre-merge runs. 实例命令绑定见 core/adapters/TERMINOLOGY.md。
|
||||
- Never use the bare single-process test runner for the full suite; `<test-cmd:parallel>` shards it and process-isolates known-corrupting files. Parallelism is controlled by `TEST_SHARDS` (default 3; raise it on beefy CI runners, e.g. `TEST_SHARDS=7`).
|
||||
|
||||
### Pre-existing test failures must be filed, not silently logged
|
||||
|
||||
When the full suite surfaces a failure that is **NOT caused by the current
|
||||
change** (it reproduces on the base branch / last-green commit too), it is a
|
||||
**pre-existing / baseline failure**. Do NOT silently drop it as "someone else's
|
||||
problem" and do NOT bury it in a report body.
|
||||
|
||||
- Classify it: run the failing test on the base commit (see `verify` Phase 2.6).
|
||||
Fails there too → pre-existing (BF); passes there → regression you must fix.
|
||||
- De-duplicate BEFORE filing (retro [org-internal #2696]): search existing
|
||||
`baseline-failure` tickets — open AND closed — by test/file name to surface
|
||||
candidates, then match by **failure signature** (error signature + affected
|
||||
surface — one root-cause family may span several tests; see the
|
||||
ticket-lifecycle rule §"BF / FT family umbrellas"). On a signature hit,
|
||||
attach run evidence to the family umbrella; do NOT create a new ticket.
|
||||
- File it: the Verifier registers every un-tracked pre-existing failure as a
|
||||
ticket-backend issue labeled `baseline-failure` with a `BF-NNN` id (`verify`
|
||||
Phase 5.55). The number is repo-wide sequential: highest existing NNN across
|
||||
ALL `baseline-failure` tickets (open AND closed) + 1 — duplicate ids
|
||||
forbidden, including against closed tickets.
|
||||
- A pre-existing failure does not block the iteration that *discovered* it, but
|
||||
the iteration is NOT Done until it is filed — an un-filed baseline failure is
|
||||
a process gap.
|
||||
|
||||
### Flaky tests must be filed, not silently tolerated
|
||||
|
||||
When a test passes and fails intermittently across runs (non-deterministic),
|
||||
it is a **flaky test**. Do NOT silently re-run until it passes and move on.
|
||||
|
||||
- Detect: re-run the suite (or use the project's flaky detector). Record every
|
||||
test that shows intermittent behavior — name, failure rate, and run count.
|
||||
- De-duplicate BEFORE filing (retro [org-internal #2696]): same
|
||||
signature-based procedure as baseline failures above, against existing
|
||||
`flaky-test` tickets.
|
||||
- File it: the Verifier registers every un-tracked flaky test as a
|
||||
ticket-backend issue labeled `flaky-test` with an `FT-NNN` id (`verify`
|
||||
Phase 5.56). `FT-NNN` allocation follows the same repo-wide sequential rule
|
||||
as `BF-NNN`, across ALL `flaky-test` tickets (open AND closed).
|
||||
- A flaky test does not block the iteration verdict, but the iteration is NOT
|
||||
Done until every unfixed flaky test is filed — an un-filed flaky test is a
|
||||
process gap.
|
||||
|
||||
**Filing is the whole job.** If the failure was NOT caused by your change, do
|
||||
NOT fix it in place — classify, file, and return to your task per the
|
||||
session-scope-guard rule. A repository-wide flaky test is a shared magnet —
|
||||
fixing it in place drifts off-task and collides with its owner
|
||||
([org-internal #1803]).
|
||||
|
||||
### Testing Kobalte components with happydom
|
||||
|
||||
Kobalte's portal/context-based components (Collapsible, DropdownMenu, ContextMenu,
|
||||
FormControlContext) do not work reliably under happydom's DOM environment. For
|
||||
these components, use **source-verification tests** — assert that the correct
|
||||
JSX elements, props, and Kobalte primitives are present in the component's
|
||||
source rather than attempting full DOM rendering.
|
||||
|
||||
When implementing a component that uses these Kobalte primitives, prefer
|
||||
source-verification over render tests. The same applies to SolidJS SVG
|
||||
`<filter>`/`<mask>`/`<linearGradient>` elements, which happydom also cannot
|
||||
render reliably.
|
||||
|
||||
### Test file naming convention (one file per component)
|
||||
|
||||
A single component MUST NOT carry both a `.test.ts` and a `.test.tsx` test
|
||||
file — the two-file split duplicated ~3300 lines of overlapping assertions
|
||||
across one UI package [org-internal #1712]. Use exactly one file per
|
||||
component, chosen by test style:
|
||||
|
||||
| Test style | Extension | When to use |
|
||||
| -------------------------------- | -------------- | -------------------------------------------------------------- |
|
||||
| Source-verification | `.test.ts` | Kobalte portal/context components, SVG `<filter>`/`<mask>`/`<linearGradient>` components, and any component happydom cannot render reliably. Asserts against the component source. |
|
||||
| DOM render / interaction | `.test.tsx` | Pure Solid components that render cleanly under happydom. Uses a testing-library harness. |
|
||||
|
||||
Rules:
|
||||
|
||||
- One file per component. If a component needs both source-verification and
|
||||
DOM-render assertions, keep them in a single file matching the dominant
|
||||
style; do not split into a `.test.ts`/`.test.tsx` pair.
|
||||
- When merging an existing dual pair, carry over any assertion unique to the
|
||||
deleted file into the surviving file before deletion.
|
||||
- The app package's `.sv.test.*` suffix is a separate convention for
|
||||
storybook/storyshots source-verification and is unaffected.
|
||||
|
||||
### Source-verification assertion boundaries
|
||||
|
||||
Source-verification tests (per the Kobalte/happydom carve-out above) must assert
|
||||
**structural contracts**, not implementation literals.
|
||||
|
||||
- **Assert**: props wiring, event handler attachment, i18n key presence, ARIA
|
||||
role/attributes present in JSX, namespace correctness, export shape.
|
||||
- **Do NOT assert**: exact source-string literals (`event.preventDefault()`,
|
||||
`'block: "nearest"'`, `event.movementX === 0`), branch operators counted via
|
||||
regex, or any expression that changes when a constant is renamed.
|
||||
- **Do NOT write tautologies** — `source.includes("false") || source.includes("!")`
|
||||
is true for nearly all TSX.
|
||||
- **One source-verification file per component** (see naming convention above).
|
||||
When two test files read the same source, merge into one.
|
||||
- **Prefer behavioral tests** — when source-verification and a behavioral/
|
||||
interaction test cover the same contract, prefer the behavioral test;
|
||||
collapse the SV duplicate.
|
||||
|
||||
### Namespace gate and test assertions
|
||||
|
||||
The namespace gate (pre-commit hook + the check-namespace script) scans ALL
|
||||
string literals in `.ts`/`.tsx`/`.json` files under the product packages for a
|
||||
reserved vendor namespace. Do NOT write test assertions containing that
|
||||
namespace literal — the gate cannot distinguish a test's namespace-absence
|
||||
check from an actual namespace violation, and will fail the commit.
|
||||
|
||||
Instead, use patterns that avoid the literal:
|
||||
```ts
|
||||
const NAMESPACE = "vendor-prefix"
|
||||
expect(source).not.toContain(`@${NAMESPACE}`)
|
||||
```
|
||||
|
||||
### Single test root (per package)
|
||||
|
||||
Each package MUST have exactly ONE test root — no parallel tracks
|
||||
(`src/**/__tests__/`, a `tests/` dir alongside colocated `src/`, or a `test/`
|
||||
alongside `tests/`); a second root drifts and breaks test discovery
|
||||
([org-internal #1711], extended to all packages by [org-internal #1821]).
|
||||
|
||||
Rules:
|
||||
|
||||
- New tests: create them at the package's single root from the start.
|
||||
- Do NOT create `src/**/__tests__/` directories in any package — parallel
|
||||
tracks drift (residuals eliminated by [org-internal #1821]).
|
||||
- For `test/`-root packages: mirror the `src/` path (`test/<src-relative>.test.ts`);
|
||||
type-level tests live alongside the behavioral suites in the config area.
|
||||
- For colocated-root packages: keep tests next to the source file; one test
|
||||
file per component [org-internal #1712].
|
||||
|
||||
Per-package root tables live in the instance layer (they name concrete
|
||||
package paths); the invariant itself — ONE root per package — is universal.
|
||||
@@ -0,0 +1,98 @@
|
||||
> Core 中立版(Increment 2 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Ticket Lifecycle — registry-first filing, claim-to-promote ([org-internal #3002] G1)
|
||||
|
||||
Bugs worth fixing start life as a **registry row**, not an independent ticket.
|
||||
This extends the two-tier-artifacts philosophy to the ticket queue: a
|
||||
"discovery" (no consumer yet, lifecycle undetermined) is Tier-1-shaped — a
|
||||
cheap row in a ledger — and is **promoted** to a Tier-2 independent ticket
|
||||
only when someone schedules or picks it up. (Filing one ticket per discovery
|
||||
is how the 2026-08 backlog formed.)
|
||||
|
||||
### The registry (登记册)
|
||||
|
||||
- **Where**: a `## TD 登记` comment on the **source ticket** (the ticket whose
|
||||
verify/review produced the candidates; a ticket-backend issue comment).
|
||||
No source ticket (standalone iteration) → the owning Epic's tracker
|
||||
ticket; no Epic either → the standing TD 冷存档 tracker ticket (G2's
|
||||
fallback archive target).
|
||||
- **Row format** (one line per item, checkbox-closeable):
|
||||
|
||||
```markdown
|
||||
- [ ] TD-{NNN} · {type: UNVERIFIABLE|ACCEPTED_RISK|DEFERRED|INFO} ·
|
||||
{severity: HIGH|MEDIUM|LOW} · {origin chunk-id/iter-N} — {one-line summary}
|
||||
· trigger: {objectively verifiable reactivation condition}
|
||||
```
|
||||
|
||||
- **TD-NNN** is allocated at REGISTRATION via the ledger mutex
|
||||
[org-internal #3322]: the 配额账本脚本 (`td-alloc` 形态, `bash <alloc-script>
|
||||
<count> <source-slug> [--ticket N]`) takes a contiguous range from the TD
|
||||
allocation ledger (a standing tracker issue) — append-only `**td-alloc**`
|
||||
comments, earliest-comment-id wins on collision. **ANY allocation — manual
|
||||
included — must land a td-alloc comment on the ledger BEFORE the registry
|
||||
row is written** (script, tool API, or web UI). Hand-computed `max + 1`
|
||||
without a ledger comment is FORBIDDEN (parallel-session races,
|
||||
[org-internal #3322]). The id is **reserved for life**: promotion reuses
|
||||
it, never renumbers. Run the alloc script's `--check` (reconciliation)
|
||||
when the last check predates this batch, and in retrospectives.
|
||||
- **De-duplication** queries the TD index (same-source rows) plus open
|
||||
promoted issues (cross-source); index matching replaces title-keyword
|
||||
matching (keyword dedup once produced 18 fragments from one source).
|
||||
- **Query-failure ruling ([org-internal #3383]: fail-open, visible)**: if the
|
||||
dedup query itself errors (backend API failure — NOT "queried and found no
|
||||
match"), retry once; on second failure file anyway, marking the comment
|
||||
`dedup-unverified` for retro/reconciliation sweep. Fail-closed rejected:
|
||||
known dedup failures are match failures it cannot prevent, and a backend
|
||||
hiccup would block a verify PASS gate.
|
||||
|
||||
### Promotion (认领升票)
|
||||
|
||||
A row is promoted to an independent ticket when **scheduled or picked up**
|
||||
(assigned, pulled into a wave/sprint, or chosen):
|
||||
|
||||
1. Create the ticket (ticket backend 立案) with the row's reserved `TD-NNN`
|
||||
(full body per the verify skill's promotion template).
|
||||
2. Tick the registry row (`- [x]`) and append `→ #NNNN` to it.
|
||||
3. The new ticket's `## Parent` points back at the registry's source ticket.
|
||||
|
||||
Unpromoted rows are not second-class: the registry comment IS a visible
|
||||
ticket-backend comment, rows close individually, and the TD index dedups
|
||||
across sessions — the old per-item model's three guarantees at ~1 ticket per
|
||||
source.
|
||||
|
||||
### PR 准入(pr-admission,TD-678)
|
||||
|
||||
会话交付物 = 分支 + 报告;开 PR 属编排职权,按容量串行一次一张、双绿并入
|
||||
再开下一张([org-internal #3864] 管合并出口、本节管 PR 入口)。交付推送前置
|
||||
本地验证(`<test-cmd:changed>` + `<typecheck-cmd>` 绿;实例命令绑定见
|
||||
core/adapters/TERMINOLOGY.md)。编排不可达可自开,PR 正文标
|
||||
`uncoordinated`。细则与门控项:pipeline-gate 清单 PR 准入节。
|
||||
|
||||
### BF / FT family umbrellas
|
||||
|
||||
Baseline failures and flaky tests file **per root-cause family**, not per
|
||||
occurrence: evidence (run, commit, failing output, failure rate) attaches as
|
||||
comments on the family's **umbrella ticket** (ticket-backend issue), keyed by
|
||||
failure signature (error signature + affected surface, not just
|
||||
`file::test-name` — one root-cause family may span several tests; precedents
|
||||
[org-internal #2585], [org-internal #2925]). Filing a new ticket when the
|
||||
signature already matches an existing family (open or closed) is forbidden —
|
||||
attach evidence instead. A family umbrella is closed when its signature no
|
||||
longer reproduces.
|
||||
|
||||
### TTL semantics (feeds G2's issue-reaper)
|
||||
|
||||
- An **unpromoted registry row** stale for 30 days (no promotion, no
|
||||
evidence comment) is reaper-eligible: 7-day notice, then archived into the
|
||||
tracker's archive section.
|
||||
- A **promoted ticket** follows the normal issue lifecycle (its own priority,
|
||||
assignee, milestone).
|
||||
|
||||
### Compatibility (Iron Law preserved)
|
||||
|
||||
- `_(Deferred: TD-XXX <reason>)_` annotations keep their exact format; the
|
||||
`TD-XXX` now resolves to a registry row (or promoted ticket) instead of an
|
||||
issue number.
|
||||
- Archive-at-close, `## 工件索引` guards, and issue-checklist-sync are
|
||||
untouched: the registry comment is an ordinary ticket-backend comment, not
|
||||
a Tier-2 artifact page.
|
||||
@@ -0,0 +1,122 @@
|
||||
> Core 中立版(Increment 2 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Two-Tier Artifact Storage (mandatory)
|
||||
|
||||
Agent-internal handoff (subagents sharing one worktree) and human-visible
|
||||
traceability have different consumers, lifecycles, and costs. The harness
|
||||
separates them into two tiers so internal handoff stops paying the Tier-2
|
||||
tax (HTTP latency, write conflicts, auth, backend health) while
|
||||
human-visible decisions stay in the system of record.
|
||||
|
||||
**Iron Law: an artifact's tier is decided by its CONSUMER and LIFECYCLE, not by
|
||||
its format.** Format (json / md / artifact page / comment) follows the tier
|
||||
decision, never drives it.
|
||||
|
||||
### Tier 1 — local structured (transient, intra-workflow)
|
||||
|
||||
- **Consumer**: subagents within one worktree (Producer / Reviewer /
|
||||
Synthesizer / Verifier) — never a human gate, never cross-worktree.
|
||||
- **Lifecycle**: bounded to one run; one worktree; archived-at-close.
|
||||
- **Visibility**: agent-internal; not human-gated.
|
||||
- **Storage**: the run workspace `runs/{slug}/` — schema-validated local
|
||||
files; active runs gitignored, archive bundle committed at close.
|
||||
- **Examples**: design detail dumps, plan item lists, raw review dimension
|
||||
findings, synthesis prompts, intermediate analysis.
|
||||
|
||||
### Tier 2 — ticket backend / git (stable, cross-worktree, human-visible)
|
||||
|
||||
- **Consumer**: humans (gate decisions), cross-worktree sessions, traceability.
|
||||
- **Lifecycle**: outlives a run; cross-worktree stable; the system of
|
||||
record.
|
||||
- **Visibility**: human-visible; authoritative.
|
||||
- **Storage**: the ticket backend (issues, PRs, artifact pages, commit
|
||||
status) + git-committed artifacts.
|
||||
- **Examples**: frozen inter-chunk contracts, issue status + checklist, PRs,
|
||||
review verdict + synthesis (the human gate), `## 工件索引` comment, commit
|
||||
status, the workflow branch.
|
||||
|
||||
### Decision rule (the 判据)
|
||||
|
||||
An artifact belongs to **Tier 1** if and only if ALL of:
|
||||
|
||||
1. consumed only within one worktree / one run, AND
|
||||
2. not a human gate decision (no human reads it to approve/reject), AND
|
||||
3. not referenced cross-worktree (e.g. a later chunk's frozen contract), AND
|
||||
4. regenerable-or-archivable at close without losing a decision record.
|
||||
|
||||
If ANY condition fails → **Tier 2**. When in doubt, Tier 2: **the Tier-2
|
||||
default载体 is decided by the ticket backend adapter** (参考实现见
|
||||
core/adapters/ 的 adapter 层;本组织实例为 Gitea adapter, 术语对照见
|
||||
core/adapters/TERMINOLOGY.md) — Tier 2 is the default system of record;
|
||||
Tier 1 is an optimization an artifact opts INTO, not out of.
|
||||
|
||||
### Common classifications
|
||||
|
||||
Per-artifact table (review findings, prompt files, working drafts, draft/frozen
|
||||
contracts, issue checklist, 工件索引, `index.json` …): Tier-2 reference page
|
||||
`rules/two-tier-artifacts-detail` (reachable via the ticket backend); the
|
||||
decision rule above classifies anything unlisted.
|
||||
|
||||
### Two indexes, parallel not replacement
|
||||
|
||||
- **Tier 1 `index.json`** (`runs/{slug}/index.json`) — the agent's
|
||||
compact-recovery entry; Tier 1 artifacts only.
|
||||
- **Tier 2 `## 工件索引` comment** — the human-visible traceability hub:
|
||||
artifact-page/issue locations + 重读 priority.
|
||||
|
||||
Both are kept by the producing role but serve different consumers. Migrating a
|
||||
skill's output to Tier 1 does NOT remove its Tier 2 row — it repoints 位置 at
|
||||
the distillation.
|
||||
|
||||
### Archive-at-close
|
||||
|
||||
Closed by the **Verifier** at verify PASS (or the Orchestrator when no
|
||||
verify). The archive step is the ONLY point active-run Tier 1 content enters
|
||||
git history:
|
||||
|
||||
1. Generate the bundle [org-internal #3604]:
|
||||
the run-bundle generator script with `--slug {slug} [--ticket N] [--pr N]`
|
||||
— flips meta to "archived", hashes the workspace into the `sha256_16`
|
||||
manifest, writes `archive/{ticket}-{slug}.json`, validates.
|
||||
2. Commit the bundle on the workflow branch (rides into main via `--no-ff`).
|
||||
Direct pushes to main stay blocked by branch protection [org-internal
|
||||
[org-internal #3604]]; do not bypass.
|
||||
3. **归档前置校验** [org-internal #2727]: the generator validates by default
|
||||
(`--no-validate` must not reach a merge); red exit = fix before close.
|
||||
The L0 merge gate rejects non-compliant bundles at merge anyway.
|
||||
|
||||
merge-gate 另做 **presence(存在性)检查**:PR body/commit 引用 `Closes #N`
|
||||
而 merge 树归档目录无 `meta.ticket.number=N` 的 bundle 时拦截(区别于
|
||||
schema 包的结构合规检查)。预期无 run 的关闭(文档/audit-only/MVP 晋级
|
||||
前/revert/ad-hoc/legacy reopen)贴 `gate-exempt/archive-presence`
|
||||
label——label 事件自动重跑 gate。归档前 PR 呈红是设计行为(fail-closed
|
||||
until archived)。**禁止以豁免 label 替代确实存在过的 run 的 bundle
|
||||
归档**。归档目录的 `.gitkeep` 是该检查的目录存在性不变式,不得删除。
|
||||
|
||||
4. Remove the `{active-slug}/` workspace. Direct `rm -rf` is L0-blocked —
|
||||
sanctioned exit: the post-merge worktree sync script with `--prune-runs`
|
||||
(syncs to the merge commit, prunes the run dir, clears stale-ahead false
|
||||
positives), then the session-worktree removal script.
|
||||
5. Mark the Tier 2 `## 工件索引` archived per the issue-checklist-sync rule
|
||||
§ 归档动作.
|
||||
|
||||
Tier 2 remains the decision record; the Tier 1 git archive is the
|
||||
reproducibility trace — enough to reconstruct what a run produced.
|
||||
|
||||
### Compact interaction
|
||||
|
||||
`compact` never deletes local files: `index.json` survives compaction and is
|
||||
the designated re-read entry — after `compact`, read `runs/{slug}/index.json`,
|
||||
not conversation memory.
|
||||
|
||||
### Gate furlough([org-internal #3607])
|
||||
|
||||
强制门的唯一退出通道是 gate 台账(furlough ledger)的 furlough 条目
|
||||
(测量证据 + reopen 条件);无台账条目的减门/跳过改动一律拒绝,台账
|
||||
本身是 Tier 2 决策工件。
|
||||
|
||||
### Scope
|
||||
|
||||
This rule + the runs layout template + schemas are the foundation; the two
|
||||
tiers coexist — unmigrated artifacts keep writing to the ticket backend per
|
||||
their existing body.
|
||||
@@ -0,0 +1,11 @@
|
||||
> Core 中立版(Increment 4 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Type Checking
|
||||
|
||||
- Always run `<typecheck-cmd>` from package directories, never invoke the
|
||||
underlying compiler driver directly.
|
||||
- Monorepo-wide typecheck from the repo root (all packages in one pass — used
|
||||
by CI and the review-code mechanical-green gate's precondition check): the
|
||||
repo-root turbo-pipeline invocation (see the term mapping). This is the
|
||||
repo-root equivalent of the per-package `<typecheck-cmd>`, not a divergence
|
||||
from it.
|
||||
@@ -0,0 +1,137 @@
|
||||
> Core 中立版(Increment 2 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Workflow Routing — type-driven process selection (mandatory)
|
||||
|
||||
A ticket's `Kind/*` label determines its process route. The main-session
|
||||
Process Assessment Gate MUST resolve the route BEFORE matching a skill, so a
|
||||
labeled ticket never re-derives its path from scratch. Routing is **additive**:
|
||||
it can only skip stages a ticket doesn't need — it can never weaken a
|
||||
mandatory gate or skip review/verify.
|
||||
|
||||
**Authoritative table**: the workflow-routing table in the instance config
|
||||
area (`label → { entry, skip, keep_gates }`). The table is the single source
|
||||
of truth; this rule only says _when and how_ to consult it.
|
||||
|
||||
### When to route (Step 0 — runs before the gate's "Articulate intent")
|
||||
|
||||
On every substantive request that is tied to a ticket, run Step 0:
|
||||
|
||||
1. **Identify the working ticket**. Priority order: explicit `Closes #N` /
|
||||
`Fixes #N` / `Resolves #N` in the PR or commit → `## 父级 / Parent` link
|
||||
or chunk mapping → the issue the user pasted or referenced by number.
|
||||
None of these → no ticket, skip to degradation.
|
||||
2. **Resolve the route with the 路由解析工具 FIRST** — pass the full
|
||||
`Kind/*` label when known; omit `kind` when not yet read (the tool returns
|
||||
the default route plus a read-ticket-then-re-invoke instruction). Agents
|
||||
with the route-gate option enabled enforce this mechanically
|
||||
[org-internal #3328]: other tool calls are dispatch-rejected until the
|
||||
路由解析工具 runs. Do not hand-read the routing table when the tool is
|
||||
available.
|
||||
3. **Read the ticket's `Kind/*` label** from the issue body or via the issue
|
||||
tools (unblocked once the route gate is satisfied). Pick the FIRST
|
||||
`Kind/*` label if several exist; on a non-default route, re-invoke the
|
||||
路由解析工具 with the full label.
|
||||
4. **Apply the route**:
|
||||
- `entry.skill` non-null → invoke that skill (with `entry.mode` if given)
|
||||
instead of natural-language matching. This satisfies gate Step 3.
|
||||
- `entry.skill` null → normal natural-language match, still honoring
|
||||
`skip` / `keep_gates` downstream. Never drop a gate the route lists in
|
||||
`keep_gates`; never add back a stage the route lists in `skip`.
|
||||
|
||||
### Degradation — label missing, unknown, or wrong ([org-internal #1828] AC)
|
||||
|
||||
- **No ticket / no `Kind/*` label / label not in the table** → `default`
|
||||
route (natural-language matching). State this in one line ("no Kind/\*
|
||||
label → default route") so the choice is auditable; never block or ask
|
||||
the user just because a label is absent.
|
||||
- **Label looks wrong for the work** → do NOT silently override the label.
|
||||
Apply the labeled route, flag the mismatch in your first response, and
|
||||
recommend the correct label — the creator owns label correctness; routing
|
||||
acts only on what is written.
|
||||
- **Routing table file missing/unreadable** → fall back to natural-language
|
||||
matching session-wide and note it once — a broken table never blocks work.
|
||||
|
||||
### Interaction with the rest of the pipeline
|
||||
|
||||
- Routing chooses the **entry** and the **skip set** only. L0/L1 enforcement
|
||||
(worktree/branch discipline, commit-msg format, stash ban,
|
||||
claim-discipline, issue-checklist-sync, issue-cross-linking) applies on
|
||||
every route; a `Kind/Documentation` ticket still needs a worktree and a
|
||||
PR.
|
||||
- `keep_gates` from the route and the skill's own mandatory gates intersect:
|
||||
a gate is mandatory if EITHER source requires it. Skipping is valid only
|
||||
when BOTH the route and the skill agree it's skippable for this ticket type.
|
||||
|
||||
### Runtime growth signals (post-sizing)
|
||||
|
||||
Two live growth mechanisms (narrative: Tier-2 reference page
|
||||
`rules/workflow-routing-detail`, reachable via the ticket backend; retired
|
||||
Size/* history: the instance rules-history archive). Canonical key:
|
||||
`dag.size_derivation.oversize_signals` (TD/DUP-F009).
|
||||
|
||||
- **DAG-routed tickets** (DAG Epic, DAG-routed `Kind/Feature`, their
|
||||
`Kind/Task` children — no `Size/*` tier): runtime growth fires the
|
||||
oversize signals → response is **re-derivation + review-dag re-run**.
|
||||
- **Bug tickets (big-bug relabel rule, [org-internal #3061])**: a `Kind/Bug`
|
||||
hitting a design-level trigger (design-decision root cause, shared-contract /
|
||||
public-API change, data migration) has outgrown the bug type — mandatory
|
||||
action is **relabel `Kind/Feature`** (Step 0 reroutes into the DAG
|
||||
pipeline; repro + root-cause notes carry over as node input). Scale-only
|
||||
triggers stay in bugfix with batched iterations.
|
||||
|
||||
Both follow "act on what's written, surface the doubt": when reality
|
||||
disagrees with the label, pause, reclassify, and make the change visible —
|
||||
never quietly override.
|
||||
|
||||
### DAG 路由 (v2) — default (转正)
|
||||
|
||||
The DAG ticket pipeline (`analyze-dag` → task DAG → single gate `review-dag`)
|
||||
is the **default** route for `Kind/Epic` and `Kind/Feature`
|
||||
([org-internal #3061] Phase 2 / TD-388). Both resolve via the routes table
|
||||
directly — **no trigger/marker check** (route rollback = revert PR).
|
||||
Authoritative definition: the `dag:` block in the workflow-routing table;
|
||||
narrative + history: Tier-2 reference page `rules/workflow-routing-detail`.
|
||||
|
||||
1. **`Kind/Epic` or `Kind/Feature` → `dag.route`**: entry `analyze-dag`,
|
||||
single gate `review-dag` (replaces review-design-space +
|
||||
review-iteration-plan), `keep_gates` `[review-dag, verify]`.
|
||||
2. **`Kind/Task` under a DAG parent → `dag.task_route`** (entry `implement`,
|
||||
`implement → review-code → verify`), NOT `dag.route`. All three skills
|
||||
carry a DAG branch; `pipeline/review-dag` success replaces the
|
||||
design-space + iteration-plan convergence preconditions at review-code
|
||||
and verify.
|
||||
|
||||
**Routing stickiness**: once an Epic is routed to DAG (draft DAG artifact
|
||||
exists or `## DAG 状态` table created), all subsequent Step 0 for that Epic
|
||||
stays on the DAG route — stickiness starts at first routing, NOT at
|
||||
single-gate PASS. Symmetrically, an Epic already decomposed via legacy
|
||||
roadmap stays legacy through close (no new legacy decomposition).
|
||||
|
||||
**Ticket creation after single-gate PASS** (switch values authoritative in
|
||||
the routing table `dag.ticket_creation`; analyze-dag itself does NOT create
|
||||
tickets): on `review-dag` PASS the **aggregator agent** creates one
|
||||
`Kind/Task` ticket per `type: task` node (body: node `acceptance_criteria`
|
||||
or dag subpage link + `test_id` suite; `## 父级 / Parent` → DAG parent so
|
||||
Step 0 resolves `dag.task_route`); `type: milestone` nodes get NO ticket
|
||||
(DoD carried by `dag.verify_milestone`); the parent body aggregates
|
||||
`## DAG 状态` + `## 当前状态` (single writer: the aggregator). Each task
|
||||
ticket then flows implement → review-code (`mode: "dag-task"`) → verify
|
||||
(task mode).
|
||||
|
||||
### Retired sections (index)
|
||||
|
||||
Retired mechanisms (Sizing, JIT decomposition, marker disambiguation,
|
||||
legacy-route sunset, publish-target-by-tier, gate-trim) live in the instance
|
||||
rules archive; rulings in the phase-3 retirement ruling doc (Tier-2
|
||||
reference, reachable via the ticket backend). Nothing trims gates —
|
||||
`never_trim` is structural.
|
||||
|
||||
### 立案降档 — filing/sprint-mode([org-internal #3002] G5 / [org-internal #3007])
|
||||
|
||||
Sprint 期间债务侧显式降档,不靠纪律。开关:routing table `filing.sprint-mode`
|
||||
(默认 `false`)。`true`(冲刺期)时 verify Phase 5.5 registry-only 不分严重度
|
||||
(登记行照写、零丢失;独立工单创建冻结至开关回 `false`,已登记行按
|
||||
ticket-lifecycle 规则正常认领升票;期间 HIGH/MEDIUM 行不被 reaper 触碰,
|
||||
[org-internal #3004])。BF/FT(5.55/5.56)不受影响。谁翻转:冲刺负责人(人),
|
||||
在冲刺跟踪单记一行;开关值即 routing table 当前值,verify 每次运行时读取。
|
||||
动机与细节:Tier-2 参考页(经 ticket backend)`rules/workflow-routing-detail`。
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"namespace": "https://eightarms.net/octopus-workflow/schemas/",
|
||||
"frozen": false,
|
||||
"note": "发布仓创建时置 frozen: true——$id 一经发布不可变(v3 §G);别名保留 ≥2 个 major 版本",
|
||||
"aliases": [
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/port-analysis.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/port-analysis.schema.json"
|
||||
},
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/review-status.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/review-status.schema.json"
|
||||
},
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/reviewer-output.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/reviewer-output.schema.json"
|
||||
},
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/runs-bundle.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/runs-bundle.schema.json"
|
||||
},
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/runs-index.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/runs-index.schema.json"
|
||||
},
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/runs-meta.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/runs-meta.schema.json"
|
||||
},
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/synthesis.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/synthesis.schema.json"
|
||||
},
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/workflow-routing.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/workflow-routing.schema.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/port-analysis.schema.json",
|
||||
"title": "Port Analysis Reviewer Output",
|
||||
"description": "Schema for per-dimension port-analysis reviewer findings. Used by the large-port-pipeline source analysis review (Phase B1.5). The port source-analysis pipeline uses a deliberate three-value severity contract 鈥?BLOCKER / MAJOR / MINOR 鈥?and never emits INFO (matching implement/reference/large-port-pipeline.md and source-analysis.md, which name only those three). This is a documented exception to the canonical four-value set (BLOCKER / MAJOR / MINOR / INFO) in reviewer-output.schema.json and synthesis.schema.json.",
|
||||
"type": "object",
|
||||
"required": ["dimension", "findings"],
|
||||
"properties": {
|
||||
"dimension": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"SRC-CMP",
|
||||
"SRC-API",
|
||||
"SRC-DATA",
|
||||
"SRC-BIZ",
|
||||
"SRC-ERR",
|
||||
"SRC-DEP",
|
||||
"SRC-TST",
|
||||
"SRC-MAP",
|
||||
"TGT-SURF",
|
||||
"CAP-BOUND"
|
||||
],
|
||||
"description": "Audit dimension code"
|
||||
},
|
||||
"findings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["id", "severity", "description"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^(SRC-CMP|SRC-API|SRC-DATA|SRC-BIZ|SRC-ERR|SRC-DEP|SRC-TST|SRC-MAP|TGT-SURF|CAP-BOUND)-\\d{3}$",
|
||||
"description": "Finding ID in {DIM_CODE}-{NNN} format"
|
||||
},
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"enum": ["BLOCKER", "MAJOR", "MINOR"]
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Human-readable finding description"
|
||||
},
|
||||
"source_evidence": {
|
||||
"type": "string",
|
||||
"description": "Source file:line reference citing the evidence"
|
||||
},
|
||||
"recommendation": {
|
||||
"type": "string",
|
||||
"description": "Concrete fix suggestion"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/review-status.schema.json",
|
||||
"title": "Review Status",
|
||||
"description": "Schema for tracking the lifecycle state of a review session. This file lives at runs/{slug}/reviews/{stage}/status.json: initialized by the Orchestrator at Phase A (step 0.5) with the full required set, appended by the Synthesizer at Phase B (history[] + current_round), and finalized with `converged` by the Orchestrator at Phase E (see skills/_shared/review-pipeline-phases.md). Reader: the retrospective token-telemetry probe (retrospective/scripts/token-telemetry.ts) walks runs/**/reviews/*/status.json (canonical) and the legacy .artifacts/**/reviews/*/status.json, reading `history[]` (code-level legacy alias `rounds[]`, both handled) and `current_round`. For CLOSED runs (active workspace removed at archive-at-close), the probe reconstructs per-stage round counts from reviews/{stage}/round{N}/ artifact paths in the committed archive bundle runs/archive/{slug}.json ([org-internal #2591]). Since TD/TRC-F006 ([org-internal #2688]) the archive bundle can also embed an explicit digest of this history[] (top-level review_history: {stage, rounds, final_verdict, per_round[]}) so round counts and verdicts stay verifiable offline, without path reconstruction.",
|
||||
"type": "object",
|
||||
"required": ["slug", "started_at", "current_round", "max_rounds", "converged", "history"],
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string"
|
||||
},
|
||||
"started_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"current_round": {
|
||||
"type": "integer"
|
||||
},
|
||||
"max_rounds": {
|
||||
"type": "integer",
|
||||
"default": 3,
|
||||
"description": "Per-skill bound: `review-artifact` skill (process target) = 3 (the roadmap/design/plan targets were archived 2026-08-21, [org-internal #3072] phase 3); `review-code` = 2 default / 3 high-risk (`workflow-routing.yaml` `review.max_rounds`, [org-internal #3567]). `review-dag` (the review-artifact skill's DAG single-gate target) binds per depth from `dag_metrics.review_depth`: D1 鈮?, D2 鈮?, D3 鈮?, D4 鈮? 鈥?D4 reaches round 4, exceeding the shared `review-artifact` default, so the DAG per-depth cap takes precedence (see review-artifact/SKILL.md review-dag Phase D MAX_ROUNDS). Set explicitly by the skill; default 3 is a fallback only."
|
||||
},
|
||||
"converged": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"history": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["round", "overall_verdict", "blockers", "majors"],
|
||||
"properties": {
|
||||
"round": {
|
||||
"type": "integer"
|
||||
},
|
||||
"overall_verdict": {
|
||||
"type": "string",
|
||||
"enum": ["PASS", "WARN", "FAIL"]
|
||||
},
|
||||
"blockers": {
|
||||
"type": "integer"
|
||||
},
|
||||
"majors": {
|
||||
"type": "integer"
|
||||
},
|
||||
"minors": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Count of MINOR findings for this round"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/reviewer-output.schema.json",
|
||||
"title": "Reviewer Output",
|
||||
"description": "Schema for a single reviewer's output for one review dimension. Also the Tier 1 review-findings schema ([org-internal #1988]): migrated skills (review-code) write each dimension's finding as a JSON file at runs/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json validated by this schema; the index.json row for that file carries type=review-findings, schema=this $id. When a dimension has multiple reviewers (dim_split > 1, e.g. D3 TOPO:2 / D4 TOPO:2), each writes its OWN slot file findings-{DIMENSION_CODE}-r{k}.json with reviewer_slot/reviewer_count set. See templates/runs-layout.md and rules/two-tier-artifacts.md.",
|
||||
"type": "object",
|
||||
"required": ["dimension", "dimension_label", "verdict", "quantitative_summary", "findings", "reviewed_files"],
|
||||
"properties": {
|
||||
"dimension": {
|
||||
"type": "string",
|
||||
"description": "Dimension code (e.g. COR, SEC, PERF, TRC)"
|
||||
},
|
||||
"dimension_label": {
|
||||
"type": "string",
|
||||
"description": "Human-readable label"
|
||||
},
|
||||
"reviewer_slot": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"description": "1-based slot of this reviewer WITHIN its dimension, when dim_split assigns >1 reviewer to the dimension (e.g. review-dag D3 TOPO:2, D4 TOPO:2 鈥?dim_split per `workflow-routing.yaml` dag.size_derivation.review_depth). Multi-reviewer dimensions write one file per slot: findings-{DIMENSION_CODE}-r{k}.json. Omitted for single-reviewer dimensions (file stays findings-{DIMENSION_CODE}.json). Preserves per-reviewer attribution for the retrospective overlap analysis ([org-internal #2996] step 1a)."
|
||||
},
|
||||
"reviewer_count": {
|
||||
"type": "integer",
|
||||
"minimum": 2,
|
||||
"description": "Total reviewers assigned to this dimension in this round (set together with reviewer_slot)."
|
||||
},
|
||||
"verdict": {
|
||||
"type": "string",
|
||||
"enum": ["PASS", "WARN", "FAIL", "UNRESOLVED"],
|
||||
"description": "PASS: pass_rate = 1.0 (no failed checks). WARN: pass_rate >= 0.7 and no BLOCKER findings. FAIL: any BLOCKER finding OR pass_rate < 0.7."
|
||||
},
|
||||
"quantitative_summary": {
|
||||
"type": "object",
|
||||
"required": ["total_checks", "passed", "failed", "pass_rate"],
|
||||
"properties": {
|
||||
"total_checks": { "type": "integer", "minimum": 1 },
|
||||
"passed": { "type": "integer", "minimum": 0 },
|
||||
"failed": { "type": "integer", "minimum": 0 },
|
||||
"pass_rate": { "type": "number", "minimum": 0, "maximum": 1 },
|
||||
"na_count": { "type": "integer", "minimum": 0, "description": "Count of not-applicable items" },
|
||||
"blocker_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Count of BLOCKER findings (consumed by synthesis_task.md aggregation)"
|
||||
},
|
||||
"major_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Count of MAJOR findings (consumed by synthesis_task.md aggregation)"
|
||||
},
|
||||
"minor_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Count of MINOR findings (consumed by synthesis_task.md aggregation)"
|
||||
},
|
||||
"info_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Count of INFO findings (consumed by synthesis_task.md aggregation)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"findings": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["id", "severity", "checklist_ref", "location", "summary", "detail", "evidence", "suggestion"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Z0-9]+-F\\d{3,}$",
|
||||
"description": "Unique finding ID within the dimension file, form {OWN_DIMENSION_CODE}-F{NNN}, e.g. COR-F001. In a grouped dispatch the id uses the finding's OWN dimension code (e.g. TST-F001 inside the COR+TST+DBT group), never the joined group code. NOTE: uniqueness of the id VALUE is NOT enforced by findings[] uniqueItems (JSON Schema uniqueItems compares whole objects by deep equality); the synthesizer MUST post-check that id values are unique across all findings-{DIM}*.json files of the round (wired as step 1b of _shared/synthesis_task.md). Multi-slot dimensions (reviewer_slot set): prefix the slot in the id, e.g. TOPOR1-F001 / TOPOR2-F001, so ids stay unique across a dimension's slot files while still matching this pattern."
|
||||
},
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"enum": ["BLOCKER", "MAJOR", "MINOR", "INFO"]
|
||||
},
|
||||
"checklist_ref": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Z0-9]{2,6} \\d+(\\.\\d+)+(\\s*\\([^\\)]*\\))?(\\s*/\\s*[A-Z0-9]{2,6} \\d+(\\.\\d+)+(\\s*\\([^\\)]*\\))?)*$",
|
||||
"description": "Checklist item reference, e.g. COR 1.1, INV 1.3, SEC 3.7.1, STD 8.4 鈥?dimension/section code followed by item number(s)."
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "File:line or file:section where the issue occurs"
|
||||
},
|
||||
"summary": {
|
||||
"type": "string",
|
||||
"description": "One-line description"
|
||||
},
|
||||
"detail": {
|
||||
"type": "string",
|
||||
"description": "Full explanation of the finding"
|
||||
},
|
||||
"evidence": {
|
||||
"type": "string",
|
||||
"description": "Quote or excerpt from the document that supports this finding"
|
||||
},
|
||||
"suggestion": {
|
||||
"type": "string",
|
||||
"description": "Actionable recommendation to resolve the finding"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"reviewed_files": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "List of files actually reviewed"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/runs-bundle.schema.json",
|
||||
"title": "Tier 1 Run Archive Bundle",
|
||||
"description": "Composite archive bundle committed at close: runs/archive/{slug}.json = {meta, index}. Dual-profile by schema_version: >= 2 strict (closed core field set + free-form context bag), 1 or absent legacy (structural typing only; historical drift normalized by repair-run-bundles.ts, see [org-internal #2588]). Validated by validate-run-bundles.ts; CI wiring: .gitea/workflows/octopus-schemas.yml. Authoritative layout: templates/runs-layout.md. Boundary rule: rules/two-tier-artifacts.md.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["meta", "index"],
|
||||
"properties": {
|
||||
"meta": true,
|
||||
"index": true,
|
||||
"working_notes": {
|
||||
"type": "string",
|
||||
"description": "Final content of runs/{slug}/working-notes.md ([org-internal #2600]), inlined at archive time when the file exists. The one Tier 1 artifact whose CONTENT (not just digest) rides in the bundle 鈥?the run's narrative memory (decisions, dead ends, reviewer feedback), consumed by later retro probes and the notes-injection degradation path ([org-internal #2601]). Absent when the run kept no working notes."
|
||||
},
|
||||
"review_history": {
|
||||
"type": "array",
|
||||
"description": "Offline-verifiability digest of the run's review rounds (TD/TRC-F006, [org-internal #2688]): one entry per review stage the run executed, each derived from that stage's live reviews/{stage}/status.json history[] at archive time. Lets closed-run probes reconstruct round counts and verdicts from the bundle alone, without the (removed) live workspace or path reconstruction ([org-internal #2591]). Absent when the run kept no review rounds.",
|
||||
"items": { "$ref": "#/$defs/reviewHistoryEntry" }
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"meta": {
|
||||
"properties": { "schema_version": { "const": 2 } },
|
||||
"required": ["schema_version"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": { "properties": { "meta": { "$ref": "#/$defs/metaStrict" } } },
|
||||
"else": { "properties": { "meta": { "$ref": "#/$defs/metaLegacy" } } }
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"index": {
|
||||
"properties": { "schema_version": { "const": 2 } },
|
||||
"required": ["schema_version"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": { "properties": { "index": { "$ref": "#/$defs/indexStrict" } } },
|
||||
"else": { "properties": { "index": { "$ref": "#/$defs/indexLegacy" } } }
|
||||
}
|
||||
],
|
||||
"$defs": {
|
||||
"reviewHistoryEntry": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["stage", "rounds", "final_verdict"],
|
||||
"description": "Per-stage summary of one review stage's rounds. Shapes mirror review-status.schema.json (stage dir name; history[] item fields).",
|
||||
"properties": {
|
||||
"stage": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Review stage identifier matching the reviews/{stage}/ directory (e.g. code, review-dag, audit-process 鈥?the live values; design-space is a retired historical value)."
|
||||
},
|
||||
"rounds": { "type": "integer", "minimum": 1, "description": "Number of executed rounds." },
|
||||
"final_verdict": { "type": "string", "enum": ["PASS", "WARN", "FAIL"] },
|
||||
"per_round": {
|
||||
"type": "array",
|
||||
"description": "One entry per executed round, mirroring the live status.json history[] items.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["round", "overall_verdict", "blockers", "majors"],
|
||||
"properties": {
|
||||
"round": { "type": "integer", "minimum": 1 },
|
||||
"overall_verdict": { "type": "string", "enum": ["PASS", "WARN", "FAIL"] },
|
||||
"blockers": { "type": "integer", "minimum": 0 },
|
||||
"majors": { "type": "integer", "minimum": 0 },
|
||||
"minors": { "type": "integer", "minimum": 0, "description": "Count of MINOR findings for this round." }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ticket": {
|
||||
"oneOf": [
|
||||
{ "type": "null" },
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["owner", "repo", "number"],
|
||||
"properties": {
|
||||
"owner": { "type": "string", "minLength": 1 },
|
||||
"repo": { "type": "string", "minLength": 1 },
|
||||
"number": { "type": "integer", "minimum": 1 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "Source issue reference, or null for an ad-hoc run (no ticket)."
|
||||
},
|
||||
"parentEpic": {
|
||||
"oneOf": [
|
||||
{ "type": "null" },
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["number"],
|
||||
"properties": { "number": { "type": "integer", "minimum": 1 } }
|
||||
}
|
||||
],
|
||||
"description": "Parent Epic issue number, or null if the run is not under an Epic."
|
||||
},
|
||||
"metaLegacy": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"required": ["slug", "state", "created_at"],
|
||||
"description": "Legacy profile (schema_version 1 or absent): historical bundles AND current live-workspace runs 鈥?runs-meta.schema.json / runs-index.schema.json pin schema_version 1 today. Core fields type-checked when present; extra context properties (route/pr/node/kind/...) tolerated as-is. The v2 strict profile (below) is the migration target: it becomes the profile for new runs once the live-workspace schemas are bumped to emit schema_version 2 with the context bag.",
|
||||
"properties": {
|
||||
"schema_version": { "type": "integer", "minimum": 1 },
|
||||
"slug": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
||||
"branch": { "type": "string" },
|
||||
"state": { "type": "string", "enum": ["active", "archived"] },
|
||||
"created_at": { "type": "string", "anyOf": [{ "format": "date-time" }, { "format": "date" }] },
|
||||
"updated_at": { "type": "string", "anyOf": [{ "format": "date-time" }, { "format": "date" }] },
|
||||
"closed_at": { "type": ["string", "null"], "anyOf": [{ "format": "date-time" }, { "format": "date" }] },
|
||||
"close_commit_sha": {
|
||||
"type": ["string", "null"],
|
||||
"pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$",
|
||||
"description": "Full git SHA, or null when unresolvable (legacy runs whose landing commit is unknown)."
|
||||
},
|
||||
"ticket": { "$ref": "#/$defs/ticket" },
|
||||
"parent_epic": { "$ref": "#/$defs/parentEpic" },
|
||||
"worktree": { "type": ["string", "null"] }
|
||||
}
|
||||
},
|
||||
"metaStrict": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema_version", "slug", "state", "created_at", "updated_at", "ticket"],
|
||||
"description": "Strict profile (schema_version 2): closed core field set. Run-specific context (route/pr/node/task_node/...) goes in the context bag 鈥?the ONLY place for non-core fields.",
|
||||
"allOf": [
|
||||
{
|
||||
"if": { "properties": { "state": { "const": "archived" } }, "required": ["state"] },
|
||||
"then": {
|
||||
"properties": {
|
||||
"closed_at": { "type": "string", "format": "date-time" },
|
||||
"close_commit_sha": { "type": "string", "pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$" }
|
||||
},
|
||||
"required": ["closed_at", "close_commit_sha"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"schema_version": { "type": "integer", "const": 2 },
|
||||
"slug": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
||||
"branch": { "type": "string", "minLength": 1 },
|
||||
"worktree": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Absolute path of the worktree hosting this run, or null for ad-hoc runs without a worktree."
|
||||
},
|
||||
"state": { "type": "string", "enum": ["active", "archived"] },
|
||||
"created_at": { "type": "string", "format": "date-time" },
|
||||
"updated_at": { "type": "string", "format": "date-time" },
|
||||
"closed_at": { "type": ["string", "null"], "format": "date-time" },
|
||||
"close_commit_sha": { "type": ["string", "null"], "pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$" },
|
||||
"ticket": { "$ref": "#/$defs/ticket" },
|
||||
"parent_epic": { "$ref": "#/$defs/parentEpic" },
|
||||
"context": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"description": "Free-form run context (route, pr, node, task_node, mode, kind, ...). In v2 every non-core field MUST live here, not at meta top level."
|
||||
}
|
||||
}
|
||||
},
|
||||
"artifactLegacy": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"required": ["path"],
|
||||
"properties": {
|
||||
"path": { "type": "string", "minLength": 1 },
|
||||
"sha": {
|
||||
"type": ["string", "null"],
|
||||
"pattern": "^[0-9a-f]{12,64}$",
|
||||
"description": "Legacy digests may be truncated (12/16-hex; source files no longer exist, cannot be re-computed). v2 requires full 64-hex."
|
||||
}
|
||||
}
|
||||
},
|
||||
"artifactStrict": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["path", "type", "tier"],
|
||||
"properties": {
|
||||
"path": { "type": "string", "minLength": 1 },
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"meta",
|
||||
"index",
|
||||
"design",
|
||||
"plan",
|
||||
"review-findings",
|
||||
"review-status",
|
||||
"review-synthesis",
|
||||
"synthesis-prompt",
|
||||
"precondition-gate",
|
||||
"working-notes",
|
||||
"analysis",
|
||||
"browser-evidence",
|
||||
"other"
|
||||
]
|
||||
},
|
||||
"schema": { "type": ["string", "null"], "format": "uri" },
|
||||
"tier": { "type": "integer", "const": 1 },
|
||||
"stage": {
|
||||
"type": ["string", "null"],
|
||||
"enum": [
|
||||
"requirements",
|
||||
"design",
|
||||
"plan-iterations",
|
||||
"roadmap",
|
||||
"implement",
|
||||
"review-roadmap",
|
||||
"review-design-space",
|
||||
"review-iteration-plan",
|
||||
"review-dag",
|
||||
"review-code",
|
||||
"verify",
|
||||
"audit-process",
|
||||
"merge-pr",
|
||||
null
|
||||
],
|
||||
"description": "Producing pipeline stage. Live stage ids (per schemas/workflow-routing.schema.json): review-dag, review-code, verify, audit-process, merge-pr. Retired/phase names retained only so historical archived bundles keep validating."
|
||||
},
|
||||
"produced_by": {
|
||||
"type": ["string", "null"],
|
||||
"enum": [
|
||||
"Producer",
|
||||
"Reviewer",
|
||||
"Verifier",
|
||||
"Tool",
|
||||
"Coordinator",
|
||||
"Analyst",
|
||||
"Architect",
|
||||
"Planner",
|
||||
"Developer",
|
||||
"Synthesizer",
|
||||
"Orchestrator",
|
||||
"Remediator",
|
||||
"Auditor",
|
||||
null
|
||||
]
|
||||
},
|
||||
"sha": { "type": ["string", "null"], "pattern": "^[0-9a-f]{64}$" },
|
||||
"tier2_ref": { "type": ["string", "null"] }
|
||||
}
|
||||
},
|
||||
"indexLegacy": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"required": ["artifacts"],
|
||||
"properties": {
|
||||
"schema_version": { "type": "integer", "minimum": 1 },
|
||||
"slug": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
||||
"artifacts": { "type": "array", "items": { "$ref": "#/$defs/artifactLegacy" } }
|
||||
}
|
||||
},
|
||||
"indexStrict": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema_version", "slug", "artifacts"],
|
||||
"properties": {
|
||||
"schema_version": { "type": "integer", "const": 2 },
|
||||
"slug": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
||||
"artifacts": { "type": "array", "items": { "$ref": "#/$defs/artifactStrict" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/runs-index.schema.json",
|
||||
"title": "Tier 1 Run Artifact Index",
|
||||
"description": "Schema for runs/{slug}/index.json 鈥?the artifact manifest for a Tier 1 local structured run. This is the compact-recovery entry point after compaction (compact never deletes local files). Parallel to, not a replacement for, the Tier 2 '## 宸ヤ欢绱㈠紩' issue comment. Authoritative layout: templates/runs-layout.md. Boundary rule: rules/two-tier-artifacts.md.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema_version", "slug", "artifacts"],
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"description": "Optional JSON Schema $self reference for editor validation."
|
||||
},
|
||||
"schema_version": {
|
||||
"type": "integer",
|
||||
"const": 1,
|
||||
"description": "Index schema version."
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9][a-z0-9-]*$",
|
||||
"description": "Run slug; must match meta.json.slug."
|
||||
},
|
||||
"artifacts": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/artifact" },
|
||||
"description": "One entry per Tier 1 artifact in this run. Tier 2 artifacts (Gitea issue comments, wiki pages, commit status, PRs) are NEVER listed here 鈥?this index is Tier 1 only."
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"artifact": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["path", "type", "tier"],
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Artifact path relative to {active-slug}/. Use forward slashes."
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"meta",
|
||||
"index",
|
||||
"design",
|
||||
"plan",
|
||||
"review-findings",
|
||||
"review-status",
|
||||
"review-synthesis",
|
||||
"synthesis-prompt",
|
||||
"precondition-gate",
|
||||
"working-notes",
|
||||
"analysis",
|
||||
"browser-evidence",
|
||||
"other"
|
||||
],
|
||||
"description": "Artifact category. 'meta'/'index' for the manifest files themselves; stage categories for produced artifacts. 'review-synthesis' marks a synthesis result artifact (round verdict) as distinct from 'synthesis-prompt' (the Phase B task prompt) and 'review-status' (the lifecycle file). 'precondition-gate' marks the review-code mechanical-green gate log. 'working-notes' is the cross-stage narrative memory file ([org-internal #2600]) 鈥?one file per run, appended by every role at close, registered once (stage/produced_by null: multi-stage, multi-role). 'browser-evidence' marks a browser evidence pack manifest ([org-internal #4497] N-03, browser/{session-id}/manifest.json 鈥?shared/pack-manifest-v1)."
|
||||
},
|
||||
"schema": {
|
||||
"type": ["string", "null"],
|
||||
"format": "uri",
|
||||
"description": "$id of the validating JSON Schema, or null for schema-less artifacts (e.g. a prompt .md passed via prompt_file). Accepts $ids from the current namespace https://eightarms.net/octopus-workflow/schemas/*.schema.json as well as legacy $ids from the historical namespace https://eightarms.net/fourbroad/octopus/ (archived live runs' index.json carry the old values); legacy values resolve via schemas/id-aliases.json."
|
||||
},
|
||||
"tier": {
|
||||
"type": "integer",
|
||||
"const": 1,
|
||||
"description": "Tier marker. This index lists only Tier 1 artifacts, so the value is always 1."
|
||||
},
|
||||
"stage": {
|
||||
"type": ["string", "null"],
|
||||
"enum": [
|
||||
"requirements",
|
||||
"design",
|
||||
"plan-iterations",
|
||||
"roadmap",
|
||||
"implement",
|
||||
"review-roadmap",
|
||||
"review-design-space",
|
||||
"review-iteration-plan",
|
||||
"review-dag",
|
||||
"review-code",
|
||||
"verify",
|
||||
"audit-process",
|
||||
"merge-pr",
|
||||
null
|
||||
],
|
||||
"description": "Producing pipeline stage, or null for meta/index manifest entries. Live stage ids (per schemas/workflow-routing.schema.json): review-dag, review-code, verify, audit-process, merge-pr. The production-phase / retired names (requirements, design, plan-iterations, roadmap, implement, review-roadmap, review-design-space, review-iteration-plan) are retained ONLY so historical archived rows keep validating; new artifacts MUST use the live ids."
|
||||
},
|
||||
"produced_by": {
|
||||
"type": ["string", "null"],
|
||||
"enum": [
|
||||
"Producer",
|
||||
"Reviewer",
|
||||
"Verifier",
|
||||
"Tool",
|
||||
"Coordinator",
|
||||
"Analyst",
|
||||
"Architect",
|
||||
"Planner",
|
||||
"Developer",
|
||||
"Synthesizer",
|
||||
"Orchestrator",
|
||||
"Remediator",
|
||||
"Auditor",
|
||||
null
|
||||
],
|
||||
"description": "Role that produced the artifact, or null if not role-attributed. Canonical role names are Producer / Reviewer / Verifier / Tool / Coordinator (the `name` field of skills/_shared/roles/*.yaml). The remaining values are fine-grained sub-roles mapped to canonical roles via ROLE_ALIASES in role config: Analyst / Architect / Planner / Developer / Synthesizer / Orchestrator / Remediator map to `Producer`; Auditor maps to `Reviewer`. They are retained so historical index rows validate and so the audit target's revision role (Remediator) and reviewer sub-role (Auditor) can be attributed if ever written to a Tier 1 artifact."
|
||||
},
|
||||
"sha": {
|
||||
"type": ["string", "null"],
|
||||
"pattern": "^[0-9a-f]{64}$",
|
||||
"description": "sha256 content digest, or null if not yet computed."
|
||||
},
|
||||
"tier2_ref": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Cross-link to a Tier 2 mirror of this artifact (issue comment URL, wiki page path, commit status context) when one exists; null when the artifact is Tier 1 only."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/runs-meta.schema.json",
|
||||
"title": "Tier 1 Run Metadata",
|
||||
"description": "Schema for runs/{slug}/meta.json 鈥?run metadata for a Tier 1 local structured run. Authoritative layout: templates/runs-layout.md. Boundary rule: rules/two-tier-artifacts.md. State transitions active -> archived at close (archive-at-close).",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"slug",
|
||||
"state",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"ticket",
|
||||
"parent_epic",
|
||||
"closed_at",
|
||||
"close_commit_sha"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"if": { "properties": { "state": { "const": "archived" } }, "required": ["state"] },
|
||||
"then": {
|
||||
"properties": {
|
||||
"closed_at": { "type": "string", "format": "date-time" },
|
||||
"close_commit_sha": { "type": "string", "pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$" }
|
||||
},
|
||||
"required": ["closed_at", "close_commit_sha"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"description": "Optional JSON Schema $self reference for editor validation."
|
||||
},
|
||||
"schema_version": {
|
||||
"type": "integer",
|
||||
"const": 1,
|
||||
"description": "Layout schema version. Bumped on breaking changes to this shape."
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9][a-z0-9-]*$",
|
||||
"description": "Run slug. Matches the workflow branch tail and the Gitea wiki namespace."
|
||||
},
|
||||
"ticket": {
|
||||
"oneOf": [
|
||||
{ "type": "null" },
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["owner", "repo", "number"],
|
||||
"properties": {
|
||||
"owner": { "type": "string", "minLength": 1 },
|
||||
"repo": { "type": "string", "minLength": 1 },
|
||||
"number": { "type": "integer", "minimum": 1 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "Source issue reference, or null for an ad-hoc run (no ticket)."
|
||||
},
|
||||
"branch": {
|
||||
"type": "string",
|
||||
"description": "Workflow branch, e.g. workflow/enhancement/1968-two-tier-artifacts. Omitted for ad-hoc runs without a branch (both runs-bundle profiles tolerate omission)."
|
||||
},
|
||||
"worktree": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Absolute path of the worktree hosting this run, or null for ad-hoc runs without a worktree (aligned with runs-bundle.schema.json metaStrict)."
|
||||
},
|
||||
"parent_epic": {
|
||||
"oneOf": [
|
||||
{ "type": "null" },
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["number"],
|
||||
"properties": {
|
||||
"number": { "type": "integer", "minimum": 1 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "Parent Epic issue number, or null if the run is not under an Epic."
|
||||
},
|
||||
"state": {
|
||||
"type": "string",
|
||||
"enum": ["active", "archived"],
|
||||
"description": "active: run in progress (workspace gitignored). archived: closed; bundle committed under runs/archive/{slug}.json."
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "RFC 3339 timestamp 鈥?run creation."
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "RFC 3339 timestamp 鈥?last metadata mutation."
|
||||
},
|
||||
"closed_at": {
|
||||
"type": ["string", "null"],
|
||||
"format": "date-time",
|
||||
"description": "RFC 3339 timestamp set at archive-at-close, or null while active."
|
||||
},
|
||||
"close_commit_sha": {
|
||||
"type": ["string", "null"],
|
||||
"pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$",
|
||||
"description": "git SHA of the commit that landed archive/{slug}.json; null while active. Must be non-null when state=archived (enforced by the if/then invariant)."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/synthesis.schema.json",
|
||||
"title": "Synthesis Output",
|
||||
"description": "Schema for the orchestrator's synthesis of all reviewer outputs for a single round.",
|
||||
"type": "object",
|
||||
"required": ["round", "overall_verdict", "dimensions", "aggregated_stats", "top_findings"],
|
||||
"properties": {
|
||||
"round": { "type": "integer", "minimum": 1 },
|
||||
"overall_verdict": {
|
||||
"type": "string",
|
||||
"enum": ["PASS", "WARN", "FAIL"],
|
||||
"description": "PASS: all dimensions PASS. WARN: no BLOCKER, no dimension FAIL. FAIL: otherwise."
|
||||
},
|
||||
"dimensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"required": ["verdict", "pass_rate", "blocker_count", "major_count", "minor_count", "info_count"],
|
||||
"properties": {
|
||||
"verdict": { "type": "string", "enum": ["PASS", "WARN", "FAIL", "UNRESOLVED"] },
|
||||
"pass_rate": { "type": "number" },
|
||||
"blocker_count": { "type": "integer" },
|
||||
"major_count": { "type": "integer" },
|
||||
"minor_count": { "type": "integer" },
|
||||
"info_count": { "type": "integer" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"aggregated_stats": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"total_dimensions",
|
||||
"passed_dimensions",
|
||||
"warn_dimensions",
|
||||
"failed_dimensions",
|
||||
"total_findings",
|
||||
"blockers",
|
||||
"majors",
|
||||
"minors",
|
||||
"infos"
|
||||
],
|
||||
"properties": {
|
||||
"total_dimensions": { "type": "integer" },
|
||||
"passed_dimensions": { "type": "integer" },
|
||||
"warn_dimensions": { "type": "integer" },
|
||||
"failed_dimensions": { "type": "integer" },
|
||||
"total_findings": { "type": "integer" },
|
||||
"blockers": { "type": "integer" },
|
||||
"majors": { "type": "integer" },
|
||||
"minors": { "type": "integer" },
|
||||
"infos": { "type": "integer" },
|
||||
"unresolved_dimensions": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Count of dimensions marked UNRESOLVED (auditor crash/timeout) and excluded from aggregation"
|
||||
}
|
||||
}
|
||||
},
|
||||
"top_findings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["id", "dimension", "severity", "checklist_ref", "summary"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Z0-9]+-F\\d{3,}$",
|
||||
"description": "The source reviewer finding id (from the Tier 1 findings file; slot-prefixed for multi-slot dimensions), so each synthesized headline traces back to its raw finding."
|
||||
},
|
||||
"dimension": { "type": "string" },
|
||||
"severity": { "type": "string", "enum": ["BLOCKER", "MAJOR", "MINOR", "INFO"] },
|
||||
"checklist_ref": { "type": "string" },
|
||||
"summary": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/workflow-routing.schema.json",
|
||||
"title": "Workflow Routing Table",
|
||||
"description": "Schema for workflow-routing.yaml 鈥?type-driven (Kind/*) process selection. Referenced by rules/workflow-routing.md and the auto-approve rule. When this schema and the rules markdown disagree, the YAML (validated by this schema) is authoritative.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["version", "default", "routes"],
|
||||
"properties": {
|
||||
"$schema": { "type": "string" },
|
||||
"version": { "type": "integer", "const": 1 },
|
||||
"default": { "$ref": "#/$defs/route" },
|
||||
"routes": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#/$defs/route" },
|
||||
"description": "Keys are Kind/* label names, e.g. Kind/Bug, Kind/Feature."
|
||||
},
|
||||
"preflight": { "$ref": "#/$defs/preflightPolicy" },
|
||||
"dag": { "$ref": "#/$defs/dag" },
|
||||
"filing": { "$ref": "#/$defs/filing" },
|
||||
"review": { "$ref": "#/$defs/review" }
|
||||
},
|
||||
"$defs": {
|
||||
"stage": {
|
||||
"type": "string",
|
||||
"description": "A pipeline stage identifier 鈥?the 5 live gates (post-[org-internal #3072] phase 3, 2026-08-21): review-dag, review-code, verify, audit-process, merge-pr. Production-phase names (requirements, design, roadmap, implement) and the 7 retired stage ids (roadmap-exit, review-roadmap, requirements-exit, design-exit, review-design-space, review-iteration-plan, plan-iterations) are no longer valid anywhere 鈥?the sizing block that consumed the phase names was retired the same ruling.",
|
||||
"enum": ["review-dag", "review-code", "verify", "audit-process", "merge-pr"]
|
||||
},
|
||||
"entry": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"skill": {
|
||||
"type": ["string", "null"],
|
||||
"enum": ["implement", "review-artifact", "review-code", "verify", "analyze-dag", null],
|
||||
"description": "Skill name to enter, or null for natural-language routing. The legacy roadmap / requirements-elicitation / design / plan-iterations entries were removed when those skills were archived ([org-internal #3072] phase 3, 2026-08-21 - the instance archive)."
|
||||
},
|
||||
"mode": {
|
||||
"type": ["string", "null"],
|
||||
"enum": ["bugfix", "refactor", "port", null],
|
||||
"description": "Skill mode, or null."
|
||||
}
|
||||
}
|
||||
},
|
||||
"stageList": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/stage" }
|
||||
},
|
||||
"route": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"entry": { "$ref": "#/$defs/entry" },
|
||||
"skip": { "$ref": "#/$defs/stageList" },
|
||||
"keep_gates": { "$ref": "#/$defs/stageList" },
|
||||
"preflight": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/preflightItem" },
|
||||
"description": "Evidence-based producer pre-flight checklist ([org-internal #2599]) 鈥?landed by a human from retrospective PRE-FLIGHT PROPOSAL rows. Absent/empty = no injection. Cap is governed by preflight.max_items (policy), not the schema."
|
||||
},
|
||||
"notes": { "type": "string" },
|
||||
"effort": {
|
||||
"type": "string",
|
||||
"enum": ["low", "high", "max"],
|
||||
"description": "Reasoning-effort hint for this route (llm-latency phase 2): maps onto the active model's variant table when it exposes one (GLM-5.2+ low/high/max); fail-open otherwise. Reserve `low` for genuinely lightweight routes (direct-edit, no pipeline) 鈥?reasoning dominates GLM-5.3 generation latency."
|
||||
}
|
||||
}
|
||||
},
|
||||
"preflightPolicy": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"description": "Thresholds for the retro 鈫?routes.{Kind}.preflight evidence loop ([org-internal #2599]). Authoritative values live here; the retrospective consults this block, it does not restate the numbers.",
|
||||
"properties": {
|
||||
"max_items": { "type": "integer", "minimum": 1 },
|
||||
"min_sample": { "type": "integer", "minimum": 0 },
|
||||
"consecutive_recur": { "type": "integer", "minimum": 1 },
|
||||
"aging_consecutive_clean": { "type": "integer", "minimum": 1 }
|
||||
}
|
||||
},
|
||||
"preflightItem": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["id", "item", "evidence"],
|
||||
"properties": {
|
||||
"id": { "type": "string", "description": "Stable slug, e.g. cor-unhandled-null." },
|
||||
"item": { "type": "string", "description": "One checklist line the Producer verifies before implementing." },
|
||||
"evidence": {
|
||||
"type": "string",
|
||||
"description": "Retro reference justifying the item, e.g. 'retro [org-internal #2599]: COR 脳 unhandled-null, 5/8 first-round FAILs on Kind/Bug'."
|
||||
},
|
||||
"added_cycle": {
|
||||
"type": "string",
|
||||
"description": "Cycle id when landed, e.g. 2026-08. Drop-oldest beyond preflight.max_items sorts on this."
|
||||
}
|
||||
}
|
||||
},
|
||||
"filing": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"description": "Filing policy (绔嬫/鐧昏绛栫暐). sprint-mode ([org-internal #3007]): when true, verify Phase 5.5 promotes registry rows to tickets in sprint mode only. work-ticket-wip-cap ([org-internal #3005] G3): open non-TD work tickets beyond this count require scheduling evidence, else they stay registry rows.",
|
||||
"properties": {
|
||||
"sprint-mode": {
|
||||
"type": "boolean",
|
||||
"description": "Sprint-mode filing downgrade switch ([org-internal #3007])."
|
||||
},
|
||||
"work-ticket-wip-cap": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "WIP cap on open non-TD work tickets (Kind/* without assignee) before new filings require scheduling evidence ([org-internal #3005] G3)."
|
||||
}
|
||||
}
|
||||
},
|
||||
"review": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"description": "Review round budget (process-diet P0b, [org-internal #3567]/TD-564): a tail cut, not the norm 鈥?at budget without convergence, STOP; residual findings flow to the TD registry. High risk = a BLOCKER survived a round or the diff touches a frozen cross-session contract.",
|
||||
"required": ["max_rounds"],
|
||||
"properties": {
|
||||
"max_rounds": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["default", "high_risk"],
|
||||
"properties": {
|
||||
"default": { "type": "integer" },
|
||||
"high_risk": { "type": "integer" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dag": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["route", "task_route", "ticket_creation", "verify_milestone", "size_derivation", "review_depth"],
|
||||
"properties": {
|
||||
"route": { "$ref": "#/$defs/dagRoute" },
|
||||
"task_route": { "$ref": "#/$defs/dagTaskRoute" },
|
||||
"ticket_creation": { "$ref": "#/$defs/dagTicketCreation" },
|
||||
"verify_milestone": { "$ref": "#/$defs/dagVerifyMilestone" },
|
||||
"size_derivation": { "$ref": "#/$defs/dagSizeDerivation" },
|
||||
"review_depth": { "$ref": "#/$defs/dagReviewDepth" }
|
||||
}
|
||||
},
|
||||
"dagRoute": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["entry", "skip", "keep_gates", "single_gate"],
|
||||
"properties": {
|
||||
"entry": { "$ref": "#/$defs/entry" },
|
||||
"skip": { "$ref": "#/$defs/stageList" },
|
||||
"keep_gates": { "$ref": "#/$defs/stageList" },
|
||||
"single_gate": { "$ref": "#/$defs/dagSingleGate" },
|
||||
"preflight": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/preflightItem" },
|
||||
"description": "Evidence-based pre-flight checklist for the DAG route ([org-internal #2905] 鏂规 3) 鈥?same item shape as routes.{Kind}.preflight ([org-internal #2599]). Landed by a human from retrospective PRE-FLIGHT PROPOSAL rows, including the 'REQ 脳 late-discovery' root cause (requirements found only after the DAG froze: DAG oversize signals, demo-period feedback). Consumed by analyze-dag (the route's entry skill) before decomposition. Absent/empty = no injection. Optional 鈥?deliberately NOT in dagRoute.required (spec-02 搂2.8 frozen required array unchanged). Cap governed by preflight.max_items (policy), not the schema."
|
||||
}
|
||||
}
|
||||
},
|
||||
"dagTaskRoute": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["entry", "skip", "keep_gates", "inherit"],
|
||||
"properties": {
|
||||
"entry": { "$ref": "#/$defs/entry" },
|
||||
"skip": { "$ref": "#/$defs/stageList" },
|
||||
"keep_gates": { "$ref": "#/$defs/stageList" },
|
||||
"inherit": { "type": "string", "enum": ["parent_dag"] }
|
||||
}
|
||||
},
|
||||
"dagSingleGate": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["id", "dimensions", "replaces", "depth_from", "never_trim"],
|
||||
"properties": {
|
||||
"id": { "type": "string", "enum": ["review-dag"] },
|
||||
"dimensions": {
|
||||
"type": "array",
|
||||
"items": { "type": "string", "enum": ["TOPO", "REQMAP", "RELEASE"] },
|
||||
"uniqueItems": true,
|
||||
"minItems": 3,
|
||||
"maxItems": 3
|
||||
},
|
||||
"replaces": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Historical documentation of which legacy gates the single gate replaced ([org-internal #3072] phase 3 retired them) 鈥?free-form strings, not the live stage vocabulary."
|
||||
},
|
||||
"depth_from": { "type": "string", "enum": ["size_derivation"] },
|
||||
"never_trim": { "type": "boolean" }
|
||||
}
|
||||
},
|
||||
"dagTicketCreation": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["per_node", "milestone_no_ticket", "inherit_parent", "parent_aggregator"],
|
||||
"properties": {
|
||||
"per_node": { "type": "boolean" },
|
||||
"milestone_no_ticket": { "type": "boolean" },
|
||||
"inherit_parent": {
|
||||
"type": "array",
|
||||
"items": { "type": "string", "enum": ["Kind/Epic", "Kind/Feature"] },
|
||||
"uniqueItems": true,
|
||||
"minItems": 1,
|
||||
"description": "Ticket kinds whose DAG children reference them as ## 鐖剁骇 / Parent ([org-internal #3061] Phase 2 / TD-388: Kind/Feature added alongside Kind/Epic)."
|
||||
},
|
||||
"parent_aggregator": { "type": "boolean" }
|
||||
}
|
||||
},
|
||||
"dagVerifyMilestone": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["weld_at_convergence", "milestone_skill", "integration_trigger"],
|
||||
"properties": {
|
||||
"weld_at_convergence": { "type": "boolean" },
|
||||
"milestone_skill": { "type": "string", "enum": ["verify"] },
|
||||
"integration_trigger": { "type": "string", "enum": ["all_incoming_done_and_green"] }
|
||||
}
|
||||
},
|
||||
"dagSizeDerivation": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["enabled", "thresholds", "size_labels", "oversize_signals"],
|
||||
"properties": {
|
||||
"enabled": { "type": "boolean" },
|
||||
"formula": { "type": "string" },
|
||||
"thresholds": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"node_count": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"d1": { "type": "integer" },
|
||||
"d2": { "type": "integer" },
|
||||
"d3": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"cross_session_edge_count": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"d1": { "type": "integer" },
|
||||
"d3": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"contract_change_surface": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"none": { "type": "string" },
|
||||
"additive": { "type": "string" },
|
||||
"breaking": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"size_labels": { "type": "string" },
|
||||
"oversize_signals": { "type": "array", "items": { "$ref": "#/$defs/dagOversizeSignal" } }
|
||||
}
|
||||
},
|
||||
"dagOversizeSignal": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["id", "trigger"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"node_split",
|
||||
"new_cross_session_edge",
|
||||
"draft_contract_breaking",
|
||||
"frozen_contract_breaking",
|
||||
"node_scope_drift"
|
||||
]
|
||||
},
|
||||
"trigger": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"dagReviewDepth": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["D1", "D2", "D3", "D4"],
|
||||
"properties": {
|
||||
"D1": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["reviewers", "rounds", "dim_split"],
|
||||
"properties": {
|
||||
"reviewers": { "type": "integer" },
|
||||
"rounds": { "type": "integer" },
|
||||
"dim_split": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"D2": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["reviewers", "rounds", "dim_split"],
|
||||
"properties": {
|
||||
"reviewers": { "type": "integer" },
|
||||
"rounds": { "type": "integer" },
|
||||
"dim_split": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"D3": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["reviewers", "rounds", "dim_split"],
|
||||
"properties": {
|
||||
"reviewers": { "type": "integer" },
|
||||
"rounds": { "type": "integer" },
|
||||
"dim_split": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"D4": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["reviewers", "rounds", "dim_split"],
|
||||
"properties": {
|
||||
"reviewers": { "type": "integer" },
|
||||
"rounds": { "type": "integer" },
|
||||
"dim_split": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Shared Rule — Round-Boundary Context Compaction
|
||||
|
||||
> **Canonical source of truth.** This file is the single authoritative copy of
|
||||
> the round-boundary compaction rule. It is injected **directly** (via
|
||||
> `<!-- inject -->`) by two skills:
|
||||
>
|
||||
> - `review-code/SKILL.md`
|
||||
> - `review-artifact/SKILL.md`
|
||||
>
|
||||
> Both inject tags are resolved by `injectShared`
|
||||
> (`<harness-package>/src/tool/skill.ts`) at skill-load time. Do not place this
|
||||
> inject inside a file that is *itself* injected (a nested inject) —
|
||||
> `injectShared` is single-pass and does not resolve nested inject tags; they
|
||||
> survive as literal `<!-- inject -->` comments and the procedure is silently
|
||||
> lost.
|
||||
>
|
||||
> **When editing:** update this file only. Every consumer picks up the change
|
||||
> automatically at load time — do not copy the text into any skill.
|
||||
>
|
||||
> This is a reference document, not a skill (no `SKILL.md` frontmatter), so the
|
||||
> skill loader does not register it. It exists purely so maintainers have one
|
||||
> place to edit.
|
||||
|
||||
## Why a canonical copy exists
|
||||
|
||||
Every multi-round review skill (`review-artifact`, `review-code`) needs the model to
|
||||
read this rule inline at the point it dispatches rounds — a cross-file
|
||||
reference would not be loaded into the model's context. So the text is
|
||||
injected directly into both skills at load time. The injection is intentional;
|
||||
this file exists to keep the two consumers on identical wording without a
|
||||
copy-paste mirror.
|
||||
|
||||
## Canonical text
|
||||
|
||||
The block below is what each consumer receives at the inject site. The guard
|
||||
referenced is the agent-initiated `compact` tool's minimum-turns guard,
|
||||
configured at `compaction.agent.min_turns_since_compaction` (default **3**).
|
||||
|
||||
> **Context compaction (round ≥ 2)**: The Orchestrator is the only long-lived
|
||||
> context across rounds; per-round dimension outputs, synthesis, and
|
||||
> convergence tracking must not accumulate unbounded. Once Phase B has posted
|
||||
> the commit status (all round state persisted in the synthesis comment + commit status)
|
||||
> and before dispatching the next
|
||||
> round in Phase A, the Orchestrator MUST run `compact` to summarize the
|
||||
> conversation through the current round, then re-read
|
||||
> the latest synthesis comment via `工单评论 API(见 TERMINOLOGY)` to restore
|
||||
> `current_round` and convergence tracking. The
|
||||
> Synthesizer's return summary (verdict, per-dimension results, re-audit
|
||||
> targets) is carried in the compacted conversation; if it was lost, re-read
|
||||
> the synthesis comment's fenced JSON block as fallback before proceeding.
|
||||
> Skip on round 1
|
||||
> (single-pass reviews never loop). Each round then starts from a bounded
|
||||
> context anchored on file state, not conversation memory.
|
||||
>
|
||||
> This round-boundary compaction is mandatory, but it is NOT exempt from the
|
||||
> `compact` tool's `min_turns_since_compaction` guard
|
||||
> (`compaction.agent.min_turns_since_compaction`, default 3): no such exemption
|
||||
> exists in the tool implementation. If the tool rejects with
|
||||
> `insufficient_turns`, accept the rejection and continue into the next round
|
||||
> without compacting — do NOT retry or work around the guard. A round with too
|
||||
> few turns since the last compaction simply keeps its context; the synthesis
|
||||
> comment + commit status still bound what accumulates.
|
||||
|
||||
## Canonical note line
|
||||
|
||||
There is no `*(Shared rule …)*` italic note line in any consumer — the inject
|
||||
directive resolves to this file's full content (this header included) at load
|
||||
time. Older revisions of this document described a two-step "copy the
|
||||
canonical text into each mirror" sync procedure; that procedure no longer
|
||||
exists and must not be reintroduced.
|
||||
@@ -0,0 +1,35 @@
|
||||
# Shared Rule — Large Prompts via `prompt_file`
|
||||
|
||||
> **Canonical source of truth.** This file is the single authoritative copy of
|
||||
> the large-prompts-via-prompt_file rule. The following skills reference this
|
||||
> rule via an `<!-- inject -->` comment and MUST NOT maintain their own copies:
|
||||
>
|
||||
> - `review-code/SKILL.md`
|
||||
> - `implement/SKILL.md`
|
||||
> - `frontend/SKILL.md`
|
||||
>
|
||||
> (The `roadmap`, `requirements-elicitation`, `design`, and `plan-iterations`
|
||||
> referencing skills were archived 2026-08-21, [org-internal #3072] phase 3 — see
|
||||
> `.octopus/archive/skills/`; their copies of this directive moved with them.)
|
||||
>
|
||||
> **When editing:** update this file only. Each skill references this file via a
|
||||
> one-line `<!-- inject -->` directive — there are no verbatim mirrors to sync.
|
||||
>
|
||||
> This is a reference document, not a skill (no `SKILL.md` frontmatter), so the
|
||||
> skill loader does not register it.
|
||||
|
||||
## Why a canonical copy exists
|
||||
|
||||
The `task-tool-discipline` rule in `AGENTS.md` requires large prompts to be
|
||||
written to a file and passed via `prompt_file`. This guidance applies to every
|
||||
skill that dispatches sub-agents with potentially large prompts. A single
|
||||
canonical source prevents drift and ensures the rule, file path convention
|
||||
(`/tmp/octopus/<slug>.md`), and justification stay consistent.
|
||||
|
||||
## Canonical text
|
||||
|
||||
> **Large Prompts via `prompt_file`**
|
||||
>
|
||||
> Follow the `task-tool-discipline` rule in `AGENTS.md`: any prompt expected to
|
||||
> exceed ~1 KB MUST be written to a file first and passed via the `task` tool's
|
||||
> `prompt_file` parameter (e.g. `/tmp/octopus/<slug>.md`), never inlined.
|
||||
@@ -0,0 +1,75 @@
|
||||
# Shared Final Report Template
|
||||
|
||||
> Canonical final-report markdown template shared across review skills. Read
|
||||
> on-demand by the Orchestrator when writing the final report in Phase E.
|
||||
|
||||
```markdown
|
||||
# {REVIEW_TYPE} Final Report
|
||||
|
||||
- **Project**: {slug}
|
||||
- **Rounds completed**: {N}
|
||||
- **Final verdict**: {PASS | WARN | FAIL}
|
||||
- **Converged**: {true | false — reason}
|
||||
|
||||
## Dimension Summary
|
||||
|
||||
| Dimension | Final Verdict | Pass Rate | R1→RN |
|
||||
| --------- | ------------- | --------- | ----- |
|
||||
| ... | | | |
|
||||
|
||||
## Outstanding Findings
|
||||
|
||||
### Accepted Risks (for tech-debt issue creation in verify Phase 5.5)
|
||||
|
||||
> verify Phase 5.5 会将下表每项 Accepted Risk 创建为 Gitea issue
|
||||
> (label `tech-debt`,标题含 `TD-NNN`)。issue 集即技术债登记表——
|
||||
> 不再有 `tech-debt.md` 文件或 `{slug}/tech-debt` wiki 页。
|
||||
|
||||
| Finding ID | Severity | Description | Recommended Reactivation Trigger |
|
||||
|-----------|----------|-------------|--------------------------------|
|
||||
| {ID} | HIGH/MEDIUM/LOW | {描述} | {可客观判断的条件} |
|
||||
|
||||
> Note: the Severity column uses the **TD-registry severity scale**
|
||||
> (HIGH/MEDIUM/LOW → Priority/High|Medium|Low labels, feeding verify Phase
|
||||
> 5.5's `## TD 登记` rows and promotion labels), NOT the review-finding
|
||||
> severity scale (BLOCKER/MAJOR/MINOR/INFO) — reviewers assign the TD
|
||||
> severity directly when accepting a risk.
|
||||
|
||||
### Other Open Items
|
||||
|
||||
...
|
||||
|
||||
## Review History
|
||||
|
||||
| Round | Verdict | Blockers | Majors | Minors |
|
||||
| ----- | ------- | -------- | ------ | ------ |
|
||||
| ... | | | | |
|
||||
|
||||
## Round-1 Findings Summary
|
||||
|
||||
> Mandatory for `review-code` (Phase E step 2.55): one row per round-1
|
||||
> finding, produced by the round-1 Synthesizer as
|
||||
> `.octopus/runs/{slug}/reviews/{stage}/round1/findings-summary.md`
|
||||
> (`_shared/review-pipeline-phases.md` Phase B step 3 "Round-1 findings
|
||||
> digest") and copied verbatim by the Orchestrator — the Orchestrator does
|
||||
> NOT read the findings JSONs — before they are deleted at archive-at-close.
|
||||
> This table is the durable record retro M7 reads. Other targets MAY include
|
||||
> it when round-1 findings data exists.
|
||||
|
||||
| Dim | Finding ID | Severity | Summary |
|
||||
| --- | ---------- | -------- | ------- |
|
||||
| ... | | | |
|
||||
```
|
||||
|
||||
## Placeholders
|
||||
|
||||
- `{REVIEW_TYPE}` — the value the skill's Phase Bindings declares, used
|
||||
verbatim in the title (`# {REVIEW_TYPE} Final Report`): "Code Review"
|
||||
(review-code — human label; its commit-status context is separately fixed
|
||||
to `pipeline/review-code`), or the machine ids `audit-process` /
|
||||
`review-dag` (review-artifact targets — same id feeds the
|
||||
`pipeline/{REVIEW_TYPE}` commit-status context; see review-artifact Phase
|
||||
Bindings). (The design-space/plan targets were archived 2026-08-21,
|
||||
[org-internal #3072] phase 3.)
|
||||
- `{DIMENSION_COUNT}` — 10 (code review; 9 in standalone-bugfix mode), 3 (review-dag single gate), or 10 (process audit); used in
|
||||
the skill's SKILL.md reference text, not inside this template body
|
||||
@@ -0,0 +1,168 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Shared Orchestrator Rules (Mandatory)
|
||||
|
||||
> Canonical Orchestrator behavior rules shared across review skills. Read
|
||||
> on-demand by the Orchestrator. These rules constrain the Orchestrator's behavior;
|
||||
> violating any of them invalidates the review.
|
||||
|
||||
1. **No scope narrowing**: The Orchestrator MUST NOT add qualifiers to the
|
||||
reviewer prompt that discourage thoroughness. Forbidden phrases include:
|
||||
"Focus on the most important issues", "Prioritize critical findings",
|
||||
"Limit to top N items", "Be concise", or any wording that suggests the
|
||||
reviewer should self-censor.
|
||||
|
||||
2. **Standardized prompts only**: Every reviewer receives the prompt from the
|
||||
template field-for-field. The Orchestrator MUST NOT modify the template
|
||||
structure, add constraints, or remove required sections.
|
||||
|
||||
3. **Quantitative verdicts only**: A PASS verdict REQUIRES pass_rate = 1.0
|
||||
with quantitative_summary populated. The Orchestrator MUST NOT promote a
|
||||
WARN to PASS based on subjective judgment. If pass_rate < 1.0, the
|
||||
dimension is at best WARN; if pass_rate < 0.7 or any BLOCKER finding exists,
|
||||
the dimension MUST be FAIL. INFO findings do NOT reduce `pass_rate` and do
|
||||
NOT affect the dimension verdict — a dimension whose only findings are INFO
|
||||
is PASS. INFO is dispositioned (`TD-candidate` / `DISCARDED`) at synthesis
|
||||
and never enters the revision loop (see
|
||||
`_shared/review-pipeline-phases.md` Phase D, "Severity handling — INFO
|
||||
disposition").
|
||||
|
||||
4. **Synthesis is aggregation, not reinterpretation**: The Orchestrator MUST NOT
|
||||
soften, rephrase, or downgrade reviewer findings during synthesis.
|
||||
If a reviewer writes "BLOCKER", it stays "BLOCKER" in synthesis.
|
||||
|
||||
5. **Anonymous findings preservation**: If two reviewers independently flag
|
||||
the same issue from different dimensions, preserve both entries. Do not
|
||||
deduplicate by discarding one — cross-dimension agreement is itself a
|
||||
signal.
|
||||
|
||||
6. **File-based communication**: Do not inline large text blocks in prompts.
|
||||
Use wiki page paths. Reviewer output goes to the skill's configured findings
|
||||
sink (see `_shared/review-pipeline-phases.md` "Output model — two findings
|
||||
sinks"): either a Tier 1 local file
|
||||
`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`
|
||||
(validated by `reviewer-output.schema.json`) for migrated skills like
|
||||
`review-code`, or an issue comment via `工单评论 API(见 TERMINOLOGY)` (Pattern
|
||||
5) for unmigrated skills. Tier boundary: `core/rules/two-tier-artifacts.md`.
|
||||
Either way, reviewer output does NOT travel as agent reply text.
|
||||
|
||||
7. **Auditor independence**: Reviewers are read-only Explorers. Only the
|
||||
{REVISION_ROLE} (a separate Worker, run in a different phase) may edit files.
|
||||
The Orchestrator MUST NOT let a reviewer both judge and fix.
|
||||
|
||||
8. **Subagent dispatch contract** (binding — omitting `subagent_type` is the
|
||||
#1 review-skill dispatch failure): every `task` tool call MUST resolve an
|
||||
agent type. The skill prose uses role names ("Orchestrator", "Architect",
|
||||
"Reviewer", "Developer", "Analyst", "Planner", "Verifier", "Remediator", "Synthesizer") — those are
|
||||
`role` values, NOT agent types. Map them to the `task` tool parameters as
|
||||
follows:
|
||||
|
||||
| Skill role | `role` param | `subagent_type` param |
|
||||
| ----------------- | -------------- | --------------------- |
|
||||
| Reviewer | `Reviewer` | `explorer` |
|
||||
| Orchestrator | `Orchestrator` | `worker` |
|
||||
| Architect | `Architect` | `worker` |
|
||||
| Developer / Tester| `Developer` | `worker` |
|
||||
| Analyst | `Analyst` | `worker` |
|
||||
| Planner | `Planner` | `worker` |
|
||||
| Verifier | `Verifier` | `worker` |
|
||||
| Remediator | `Remediator` | `worker` |
|
||||
| Synthesizer | `Synthesizer` | `worker` |
|
||||
|
||||
The `agent` type is declared in `roles/*.yaml` for the five roles that
|
||||
have a role YAML — `producer.yaml` (worker), `reviewer.yaml` (explorer),
|
||||
`tool.yaml` (explorer), `verifier.yaml` (worker), and `coordinator.yaml`
|
||||
(worker; the pool coordinator session — not a task-dispatch role, so it
|
||||
does not appear in the table above). The task tool derives
|
||||
`subagent_type` from `role` automatically when `subagent_type` is omitted
|
||||
AND the role's `agent` field is defined. All other roles in the table above
|
||||
(Orchestrator, Architect, Developer, Analyst, Planner, Remediator,
|
||||
Synthesizer) have NO role YAML and therefore MUST pass `subagent_type`
|
||||
explicitly — never pass only `role` + `description` and assume a default.
|
||||
|
||||
Concrete example (dispatching a reviewer):
|
||||
|
||||
```
|
||||
task(
|
||||
description: "Review CMP dimension",
|
||||
role: "Reviewer",
|
||||
subagent_type: "explorer", // optional here (derivable from role), shown for clarity
|
||||
permissions: [{ permission: "read", pattern: "*", action: "allow" }, ...],
|
||||
temperature: 0.1,
|
||||
prompt: <filled reviewer template>
|
||||
)
|
||||
```
|
||||
|
||||
Forbidden: a `task` call that names the role in prose ("Spawn the
|
||||
Architect") but omits BOTH `subagent_type` and a defined-`agent` `role` —
|
||||
it fails with `TaskMissingSubagentType`.
|
||||
|
||||
## Default Explorer Permissions
|
||||
|
||||
All review and audit skills that spawn Explorer sub-agents use the same
|
||||
read-only BASE permission set. The canonical base template is:
|
||||
|
||||
```
|
||||
permissions: [
|
||||
{ permission: "read", pattern: "*", action: "allow" },
|
||||
{ permission: "glob", pattern: "*", action: "allow" },
|
||||
{ permission: "grep", pattern: "*", action: "allow" }
|
||||
]
|
||||
```
|
||||
|
||||
Each skill references this base template via `core/skills/_shared/review-orchestrator-rules.md`
|
||||
rather than inlining the JSON array. This centralization ensures the base
|
||||
permission surface stays consistent across all review/audit skills.
|
||||
|
||||
**Tier-1 local exception** ([org-internal #1988]): migrated skills that write raw per-dimension
|
||||
findings to `<runs-root>/**` (e.g. `review-code`) add ONE scoped `edit` allow
|
||||
to the base set — `{ permission: "edit", pattern: "<runs-root>/**", action:
|
||||
"allow" }` — so the reviewer can write its OWN finding file. This is a
|
||||
documented exception to the read-only base, not a divergence: it does NOT grant
|
||||
edit on the code under review, so rule #7 (auditor independence) holds. See
|
||||
`_shared/review-pipeline-phases.md` Phase A step 4 and `review-code/SKILL.md`
|
||||
Phase A step 3 for the inline extended array.
|
||||
|
||||
Residual risk — accepted trade-off (TD/STD-F004, [org-internal #2688]): the grant is scoped
|
||||
to `<runs-root>/**`, wider than the reviewer's own findings file. Per-file
|
||||
narrowing was considered and rejected: findings paths are round- and
|
||||
dimension-dependent (`{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`),
|
||||
which makes per-file permission patterns brittle at spawn time, and `runs/**`
|
||||
holds only transient Tier 1 artifacts (digest-summarized at archive). The
|
||||
residual exposure — a misbehaving reviewer could overwrite a sibling stage's
|
||||
Tier 1 files — is recorded here as accepted; auditor independence is
|
||||
unaffected (code under review lives outside `runs/`).
|
||||
|
||||
### No bash for Explorer sub-agents (deliberate boundary, [org-internal #2115] RC4 / [org-internal #2126])
|
||||
|
||||
Explorer sub-agents (Reviewer / auditor / Tool roles) have **no bash**. The
|
||||
`explorer` agent is built deny-by-default at
|
||||
`<harness-package>/src/agent/agent.ts` (`"*": "deny"` + explicit `read`/`glob`/
|
||||
`grep` allows + `bash: "deny"`), so the `permissions` array above is read-only
|
||||
by construction — a reviewer cannot run commands, mutate state, or escape its
|
||||
read surface even if a prompt tried to grant bash. This is a deliberate
|
||||
security boundary that upholds rule #7 (auditor independence): a reviewer must
|
||||
never alter the artifact under review or its environment.
|
||||
|
||||
**Reviewer guidance**: do everything with `read`, `glob`, `grep`, and the
|
||||
`gitea-rest` skill (curl against `/api/v1`; `gitea-mcp` is retired). If a
|
||||
review dimension seems to require running a command
|
||||
(executing a build, running a test, computing a metric), that is the
|
||||
Synthesizer / Verifier's job (Worker sub-agents, which DO have bash via the
|
||||
`worker` agent defaults) — not the Reviewer's. Report the need in findings
|
||||
instead of attempting bash.
|
||||
|
||||
> **[org-internal #2126] Task 2 conclusion — keep + document.** The `deny **` pushed per-tool
|
||||
> at `<harness-package>/src/session/prompt.ts` is the **session tool-enable /
|
||||
> disable map** (it disables `todowrite` / `task` / `experimental.primary_tools`
|
||||
> for subagents and allows forwarded MCP tools). It is NOT the source of the
|
||||
> Explorer bash deny — that comes from the `explorer` agent config above, which
|
||||
> is the correct place. Relaxing it would break auditor independence; worker-
|
||||
> class sub-agents already have bash wherever the pipeline needs to run
|
||||
> commands. Decision: **keep the deny, document it here** (this section).
|
||||
|
||||
## Placeholder
|
||||
|
||||
- `{REVISION_ROLE}` — "Developer" (code review), "Analyst" (review-dag single
|
||||
gate), or "Remediator" (process audit). The Worker role that revises
|
||||
artifacts between rounds. (The Architect/Planner revision roles belonged to
|
||||
the archived design-space / iteration-plan targets, [org-internal #3072] phase 3.)
|
||||
@@ -0,0 +1,493 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Review Pipeline Phases (Canonical)
|
||||
|
||||
This document is the **single source of truth** for the shared Phase A→E
|
||||
review/audit pipeline flow. Individual review and audit skills bind their own
|
||||
parameters and reference this doc rather than duplicating the shared mechanics.
|
||||
|
||||
**Terminology**: "reviewer" and "auditor" are interchangeable — `audit-process`
|
||||
legitimately uses "auditor"/"Re-audit" terminology. All generic descriptions
|
||||
use `{REVIEWER_OR_AUDITOR}`.
|
||||
|
||||
**Output model — two findings sinks** ([org-internal #1988] Tier 1 migration): raw per-dimension
|
||||
reviewer findings have ONE of two sinks, chosen per skill. The **synthesis**
|
||||
(human gate) and **commit status** are ALWAYS Tier 2 (Gitea) regardless of the
|
||||
findings sink.
|
||||
|
||||
| Sink | Skills | Reviewer writes findings to | Synthesizer reads findings from |
|
||||
|---|---|---|---|
|
||||
| `tier1-local` (migrated) | `review-code`, `review-artifact` (live targets: review-dag, audit-process; the roadmap/design-space/plan targets were archived 2026-08-21, [org-internal #3072] phase 3) | `<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json` (validated by `core/schemas/reviewer-output.schema.json`) | the local finding files (no Gitea call) |
|
||||
| `tier2-comment` (fallback) | _(none — all targets migrated)_ | Gitea PR/issue comment via `工单评论 API(见 TERMINOLOGY)` (Pattern 5) | `工单评论 API(见 TERMINOLOGY)` (Pattern 3) |
|
||||
|
||||
Migration complete — every review/audit skill now outputs `tier1-local`; the
|
||||
`tier2-comment` row remains as the documented fallback (boundary rule:
|
||||
`core/rules/two-tier-artifacts.md`; a skill without `<runs-root>/`
|
||||
write access falls back to comment-based output unchanged). A `tier1-local`
|
||||
reviewer additionally
|
||||
appends an `index.json` row per finding file (Tier 1 manifest, see
|
||||
`core/templates/runs-layout.md`).
|
||||
|
||||
**Tier 2 outputs (always)**: synthesis is posted as a **Gitea PR/issue comment**
|
||||
with a fenced ` ```json ` block — except for the `audit-process` target, which
|
||||
has no source issue and posts the synthesis as a **wiki page**
|
||||
`audit/{date}/round{N}/synthesis` instead (see `review-artifact/SKILL.md` process target
|
||||
Output Artifacts); status tracking uses **REST commit status**
|
||||
(`pipeline/{REVIEW_TYPE}` context). The full Gitea output pattern is documented
|
||||
in `_shared/gitea-write-patterns.md`.
|
||||
|
||||
---
|
||||
|
||||
## Phase A — Parallel {REVIEWER_OR_AUDITOR_NAME}
|
||||
|
||||
**Orchestrator actions**:
|
||||
|
||||
0. **Move the source issue to the `review` column** on the Pipeline Stages
|
||||
board per `_shared/gitea-write-patterns.md` Pattern 7.5. Skip if no
|
||||
source issue exists. The review stage is a single board column covering
|
||||
all review targets (DAG single gate / code review / audit-process).
|
||||
|
||||
0.5. **Initialize the review-status file** (round 1 only). The Orchestrator
|
||||
creates `<runs-root>/{slug}/reviews/{stage}/status.json` before dispatching
|
||||
round 1, with the full schema-required field set
|
||||
(`core/schemas/review-status.schema.json`): `slug`, `started_at`
|
||||
(RFC 3339 timestamp), `current_round: 1`, `max_rounds` (per-skill binding —
|
||||
`review-artifact` = 3, `review-code` = 2 default / 3 high-risk —
|
||||
`<instance-root>/workflow-routing.yaml` `review.max_rounds`), `converged: false`,
|
||||
`history: []`. This
|
||||
step is skipped on rounds ≥ 2 — the file already exists and the Synthesizer
|
||||
appends to it in Phase B. Initialization is the Orchestrator's sole
|
||||
responsibility; the Synthesizer never creates the file. The Orchestrator
|
||||
also appends an `index.json` row for the file (`type` = `review-status`,
|
||||
`stage` = `{REVIEW_TYPE}`, `produced_by` = `Orchestrator`, `schema` = the
|
||||
review-status `$id`).
|
||||
|
||||
1. **Tool-availability probe** (defense-in-depth, [org-internal #2115] RC1 / [org-internal #2126]). Before
|
||||
dispatching any {REVIEWER_OR_AUDITOR}, verify reviewers will be able to read
|
||||
wiki artifacts (requirements / design / plan / prior-review reports). A
|
||||
reviewer that cannot read its inputs aborts or hallucinates — catch this ONCE
|
||||
in the Orchestrator instead of letting {DIMENSION_COUNT} parallel subagents
|
||||
each rediscover the gap:
|
||||
|
||||
- **Primary path**: confirm `gitea_wiki__*` tools are registered (the
|
||||
Orchestrator checks its own tool catalog; these tools are forwarded to
|
||||
reviewer subagents when registered — [org-internal #2116] raised the tool cap 50→100 so
|
||||
they are present in normal runs). If available → continue to step 2.
|
||||
- **Fallback path** (when `gitea_wiki__*` is NOT registered, e.g. a
|
||||
cross-repo scenario [org-internal #2115]): verify the **local wiki clone** at
|
||||
`<wiki-clone-root>/{repo}-wiki/` is readable —
|
||||
`glob(pattern="*.md", path="<wiki-clone-root>/{repo}-wiki/")`. If the clone
|
||||
is readable → continue to step 2; reviewers use the local clone (see
|
||||
`_shared/gitea-read-patterns.md` Pattern 1 fallback note and the "Tool
|
||||
Availability & Path Resolution" section of this skill).
|
||||
- **Both unavailable → FAIL FAST**: post a `failure` commit status (state
|
||||
`"failure"`, context `pipeline/{REVIEW_TYPE}`, description naming the
|
||||
missing capability) and abort — do NOT enter parallel dispatch. Report the
|
||||
gap so the environment can be fixed (register the wiki MCP server, or
|
||||
populate `<wiki-clone-root>/{repo}-wiki/`).
|
||||
|
||||
This probe is belt-and-suspenders: RC1 (tool cap, [org-internal #2116]) already fixed the
|
||||
primary cause of missing `gitea_wiki__*` in subagents. It exists so a future
|
||||
environment regression fails gracefully with one clear message instead of N
|
||||
silent subagent aborts.
|
||||
|
||||
2. Post an initial pending commit status via REST:
|
||||
|
||||
```
|
||||
POST /api/v1/repos/Octopus/octopus/statuses/{SHA}
|
||||
state: "pending"
|
||||
context: "pipeline/{REVIEW_TYPE}"
|
||||
description: "Round {N}: {DIMENSION_COUNT} reviewers dispatched"
|
||||
```
|
||||
|
||||
(See `_shared/gitea-write-patterns.md` Pattern 8.)
|
||||
|
||||
3. Spawn **all {DIMENSION_COUNT} {REVIEWER_OR_AUDITOR}s in parallel**. Each
|
||||
receives:
|
||||
- The standardized prompt with dimension-specific values filled in.
|
||||
- Agent type: Explorer.
|
||||
- Permission: Explorer read-only (see
|
||||
`core/skills/_shared/review-orchestrator-rules.md` — Default
|
||||
Explorer Permissions).
|
||||
- Temperature: 0.1 (deterministic output).
|
||||
|
||||
4. Wait for all {REVIEWER_OR_AUDITOR}s to complete. Each reviewer writes its
|
||||
dimension result to the skill's configured findings sink:
|
||||
- `tier1-local` (e.g. `review-code`, `review-artifact`, `audit-process`): write the JSON to
|
||||
`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`
|
||||
(validated by `core/schemas/reviewer-output.schema.json`). The reviewer
|
||||
is granted scoped write to `<runs-root>/**` (read-only everywhere else —
|
||||
this emits the reviewer's OWN output, it does not touch the artifact under
|
||||
review, so it does not violate rule #7 auditor independence).
|
||||
- `tier2-comment` (fallback): post an issue comment via
|
||||
`工单评论 API(见 TERMINOLOGY)` (Pattern 5, `_shared/gitea-write-patterns.md`)
|
||||
with a fenced ` ```json ` block on the target PR (pipeline mode) or issue
|
||||
(standalone mode). The comment is labeled `**{DIMENSION_CODE} review — round {N}**`.
|
||||
After all reviewers complete, for `tier1-local` the Orchestrator appends one
|
||||
`index.json` row per finding file (type `review-findings`, stage
|
||||
`{REVIEW_TYPE}`, produced_by `Reviewer`, schema = the reviewer-output `$id`).
|
||||
|
||||
5. If a {REVIEWER_OR_AUDITOR} fails (crash / timeout), retry once. If still
|
||||
failed, mark that dimension as `UNRESOLVED` in the synthesis and continue.
|
||||
Record it for deferred retry at the start of the next round.
|
||||
|
||||
---
|
||||
|
||||
## Phase B — Synthesis (Delegated)
|
||||
|
||||
> **Context optimization**: Phase B is delegated to a Synthesizer (Worker) to
|
||||
> prevent the Orchestrator's context from accumulating N dimension finding
|
||||
> Read results (up to N × 50KB per round). The Synthesizer reads all dimension
|
||||
> findings in its own context, posts the synthesis comment + commit status,
|
||||
> and returns a compact summary (≤ 2KB). The Orchestrator never reads
|
||||
> dimension findings directly.
|
||||
|
||||
**Orchestrator actions**:
|
||||
|
||||
1. Fill the synthesis task template
|
||||
(`core/skills/_shared/synthesis_task.md`) with:
|
||||
- Round number. A round-1 dispatch carries the template's step-8
|
||||
"Round-1 only — findings digest" duty (the Synthesizer writes
|
||||
`round1/findings-summary.md`, consumed verbatim at Phase E — see the
|
||||
digest bullet in step 3 below); verify it is present in the filled
|
||||
template.
|
||||
- Target PR number (pipeline mode) or issue number (standalone mode).
|
||||
- List of UNRESOLVED dimensions (reviewers that crashed/timed out).
|
||||
- Commit SHA and review type.
|
||||
- For `tier1-local` skills (all review/audit): write the filled template to
|
||||
`<runs-root>/{slug}/reviews/{stage}/round{N}/task-synthesizer.md` (Tier 1
|
||||
synthesis prompt; append an `index.json` row, type `synthesis-prompt`). The
|
||||
`/tmp/octopus/` embryo is NOT used — Tier 1 owns the prompt.
|
||||
- For `tier2-comment` skills: write the filled template to
|
||||
`/tmp/octopus/synthesis_task_{slug}_round{N}.md` for `prompt_file` use; the
|
||||
authoritative copy is published as a Gitea wiki page at
|
||||
`{slug}/reviews/{stage}/round{N}/task-synthesizer`.
|
||||
|
||||
2. Dispatch the **Synthesizer** (Worker) via the `task` tool:
|
||||
- `role`: `Synthesizer`
|
||||
- `subagent_type`: `worker`
|
||||
- `prompt_file`: the synthesis-prompt path from step 1 (Tier 1 local path for
|
||||
`tier1-local` skills; `/tmp/octopus/...` for `tier2-comment` skills)
|
||||
- `permissions`: read + write + edit + bash + glob + grep (all allow)
|
||||
- `temperature`: 0.1
|
||||
- Foreground (the Orchestrator waits for completion).
|
||||
|
||||
3. The Synthesizer:
|
||||
- Reads all dimension findings from the skill's configured sink:
|
||||
- `tier1-local`: read the finding files at
|
||||
`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-*.json` (in its
|
||||
OWN context, not the Orchestrator's — no Gitea call).
|
||||
- `tier2-comment`: read all dimension comments via `工单评论 API(见 TERMINOLOGY)`
|
||||
(Pattern 3, `_shared/gitea-read-patterns.md`) on the target PR/issue.
|
||||
- **Post-check finding-id uniqueness (MANDATORY)**: verifies that `id`
|
||||
values are unique across ALL findings files of the round (including
|
||||
every `-r{k}` slot file) — the schema's `uniqueItems` does NOT enforce
|
||||
this. On a duplicate id, keeps both findings, notes the collision in
|
||||
the digest and marks the affected dimension WARN (per step 1b of
|
||||
`_shared/synthesis_task.md`).
|
||||
- Computes synthesis following `core/schemas/synthesis.schema.json`.
|
||||
- Posts synthesis as a PR/issue comment via `工单评论 API(见 TERMINOLOGY)`
|
||||
(Pattern 5, `_shared/gitea-write-patterns.md`) — markdown summary + fenced
|
||||
` ```json ` block matching `core/schemas/synthesis.schema.json`. (This
|
||||
is the Tier 2 human-gate output — it stays on Gitea for both sinks.) For
|
||||
the `audit-process` target (no source issue), post the synthesis as a wiki
|
||||
page `audit/{date}/round{N}/synthesis` instead of a PR/issue comment.
|
||||
- Posts a commit status via REST with context
|
||||
`pipeline/{REVIEW_TYPE}` (Pattern 8, `_shared/gitea-write-patterns.md`):
|
||||
`state: "success"` for PASS/WARN, `"failure"` for FAIL.
|
||||
- Appends one `history[]` entry for this round to the run's review-status
|
||||
file `<runs-root>/{slug}/reviews/{stage}/status.json` (validated by
|
||||
`core/schemas/review-status.schema.json`) — `round`,
|
||||
`overall_verdict`, `blockers`, `majors`, `minors` — the durable per-round
|
||||
audit history (defined by `core/schemas/review-status.schema.json`
|
||||
`history[]`; mirrors the audit-process checklist TRC 10.4), and updates
|
||||
the file's `current_round` to this round number. (The Orchestrator initializes `status.json` before
|
||||
round 1 — see Phase A step 0.5 — with the full required field set `slug` /
|
||||
`started_at` / `current_round` / `max_rounds` / `converged` / `history`.
|
||||
The Synthesizer only appends `history[]` and updates `current_round`; it
|
||||
never re-creates the file.)
|
||||
- **Round-1 findings digest (round 1 only)**: for skills whose final report
|
||||
carries a Round-1 Findings Summary (`review-code` Phase E step 2.55 and
|
||||
any target adopting the template section), the round-1 Synthesizer is the
|
||||
PRODUCER of that table: it writes
|
||||
`<runs-root>/{slug}/reviews/{stage}/round1/findings-summary.md` — the
|
||||
rendered `## Round-1 Findings Summary` table (columns
|
||||
`| Dim | Finding ID | Severity | Summary |`, one row per round-1 finding;
|
||||
Summary = one-line 摘由/root cause) — distilled from the round-1 findings
|
||||
it just read in its own context (append an `index.json` row, type
|
||||
`review-synthesis` — Synthesizer-produced synthesis artifacts; the
|
||||
round-verdict return summary `synthesis-return.md` keeps the generic
|
||||
worker-report type `other` per
|
||||
`_shared/worker-report-persistence.md`). This duty is wired into the
|
||||
Synthesizer's operative prompt as step 8 of
|
||||
`_shared/synthesis_task.md` ("Round 1 only — findings digest"). The
|
||||
Orchestrator at Phase E copies this file
|
||||
verbatim into the final report; it does NOT read the findings JSONs
|
||||
(step 4 below) — the digest file is the wired producer→consumer path.
|
||||
- Returns a compact summary (≤ 2KB) with verdict, per-dimension results,
|
||||
and re-audit targets.
|
||||
|
||||
4. The Orchestrator receives the summary and proceeds to Phase C or Phase E
|
||||
based on `overall_verdict`. The Orchestrator does NOT read dimension
|
||||
comments or the synthesis comment directly — all decision-relevant data
|
||||
is in the Synthesizer's return summary.
|
||||
|
||||
5. Present the Synthesizer's return summary to the user in chat.
|
||||
|
||||
---
|
||||
|
||||
## Phase C — {REVISION_ROLE} Revision
|
||||
|
||||
If `overall_verdict` is PASS, skip to Phase E.
|
||||
|
||||
Otherwise:
|
||||
|
||||
**Orchestrator actions**:
|
||||
|
||||
1. Prepare the {REVISION_ROLE}'s input as a structured task, published as a
|
||||
Gitea wiki page at `{slug}/reviews/{stage}/round{N}/task-{ROLE}` (Pattern 1,
|
||||
`_shared/gitea-write-patterns.md`). For the `audit-process` target, the
|
||||
process target's Phase Bindings override the wiki namespace to
|
||||
`audit/{date}/` — publish to `audit/{date}/round{N}/task-{ROLE}` instead
|
||||
(see `review-artifact/SKILL.md` process target).
|
||||
|
||||
This must contain:
|
||||
- Paths to all files that need revision.
|
||||
- Reference to the synthesis comment on the PR/issue (posted in Phase B).
|
||||
- References to the dimensions that have open BLOCKER, MAJOR, or MINOR
|
||||
findings, via the skill's findings sink:
|
||||
- `tier1-local`: paths to the Tier 1 finding files
|
||||
(`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`).
|
||||
- `tier2-comment`: references to the individual dimension issue comments.
|
||||
Dimensions whose only findings are INFO are excluded — INFO is
|
||||
dispositioned at synthesis and never revised (see the "Severity handling —
|
||||
INFO disposition" rule in Phase D).
|
||||
- The {REVISION_ROLE} MUST NOT receive the full text of all findings inline —
|
||||
use finding-file path / comment references only to avoid context explosion.
|
||||
- Convergence status: blockers/majors remaining vs. previous round.
|
||||
- **Revision survival ([org-internal #2601]) — primary path**: the task tool's completion
|
||||
output includes a `task_id`; passing it back resumes the SAME
|
||||
{REVISION_ROLE} subagent session instead of creating a fresh one. Round 1:
|
||||
persist the returned `task_id` with the round's working-notes entry (it
|
||||
must survive round-boundary compaction). Round ≥ 2: dispatch with
|
||||
`task_id` set to the previous revision round's — the reviser continues
|
||||
in-session with its prior context. {REVIEWER_OR_AUDITOR}s are NEVER
|
||||
resumed — per-round freshness is their independence.
|
||||
- **Revision survival — fallback** (task_id lost to compaction before it
|
||||
was persisted): for round ≥ 2, include the current
|
||||
`<runs-root>/{slug}/working-notes.md` content (run narrative memory:
|
||||
prior decisions, dead ends, earlier reviewer feedback) and a reference to
|
||||
the previous round's revision summary. State explicitly that this revises
|
||||
work a prior round already produced — re-derive from upstream artifacts
|
||||
ONLY when a finding requires it.
|
||||
|
||||
2. Spawn the {REVISION_ROLE} (Worker) with revision instructions from
|
||||
`core/skills/_shared/review-revision-prompt.md`. For round ≥ 2, pass
|
||||
the previous reviser `task_id` to resume the same session (see Revision
|
||||
survival above); reviewer/auditor dispatches are always fresh.
|
||||
|
||||
---
|
||||
|
||||
## Phase D — {REVIEWER_OR_AUDIT_LOOP} Loop
|
||||
|
||||
After the {REVISION_ROLE} completes revision:
|
||||
|
||||
1. Increment the round counter, and update `current_round` in
|
||||
`<runs-root>/{slug}/reviews/{stage}/status.json` to match. Post the round
|
||||
transition as a progress update to the commit status (Pattern 8):
|
||||
```
|
||||
state: "pending"
|
||||
context: "pipeline/{REVIEW_TYPE}"
|
||||
description: "Round {N+1}: re-reviewing after {REVISION_ROLE} revision"
|
||||
```
|
||||
2. Go back to Phase A (spawn a new round against the revised files).
|
||||
|
||||
**Convergence criteria** (stop the loop when ANY of these is true):
|
||||
|
||||
| Condition | Action |
|
||||
| -------------------------------------- | ---------------------------------------------- |
|
||||
| `overall_verdict` = PASS | Stop. Proceed to Phase E. |
|
||||
| `current_round` > `{MAX_ROUNDS}` | Stop. Proceed to Phase E with final status. |
|
||||
| No improvement in 2 consecutive rounds | Stop. (Same blocker count AND same major count |
|
||||
| (compared to 2 rounds ago) | without reduction). Proceed to Phase E. |
|
||||
| User explicitly approves current state | Stop. Proceed to Phase E. |
|
||||
|
||||
**`{MAX_ROUNDS}` binding** (per skill):
|
||||
- `review-artifact` skill (process target): **3**.
|
||||
- `review-artifact` skill (`review-dag` single-gate target): **per depth** from
|
||||
`dag_metrics.review_depth` — D1 ≤2, D2 ≤3, D3 ≤3, D4 ≤4 (D4 reaches round 4,
|
||||
exceeding the shared `review-artifact` default; the per-depth cap takes precedence —
|
||||
see `review-artifact/SKILL.md` review-dag Phase D and
|
||||
`core/schemas/review-status.schema.json`).
|
||||
- `review-code` skill: **2** default / **3** high-risk (a BLOCKER survived a
|
||||
round, or the diff touches a frozen cross-session contract) —
|
||||
`<instance-root>/workflow-routing.yaml` `review.max_rounds` ([org-internal #3567], process-diet
|
||||
P0b). At budget without convergence: STOP, register the remaining
|
||||
BLOCKER/MAJOR findings as TD registry rows, verdict FAIL if any BLOCKER
|
||||
remains else PASS — the debt is carried by those TD registry rows
|
||||
(see `review-code/SKILL.md` Phase D). This
|
||||
supersedes the 2026-08-25 [org-internal #3380] calibration (p50=2, p95=5, n=66): the runs
|
||||
past round 3 are the marathon tail this budget exists to cut; their residue
|
||||
flows to the TD registry instead of reviewer rounds. Ledger:
|
||||
`docs/workflow-refactor/thresholds-ledger.md`.
|
||||
|
||||
Rationale for `review-artifact` = 3 (evaluated in [org-internal #1969], replacing the prior blanket
|
||||
default of 5): (1) the Round-3 Escalation Guardrail below already detects
|
||||
non-convergence at the start of round 3; (2) the INFO-disposition rule below
|
||||
removes spurious WARN rounds previously caused by non-blocking INFO findings;
|
||||
(3) the "no improvement in 2 consecutive rounds" stop condition naturally
|
||||
fires by round 3; (4) the marginal value of rounds 4-5 is low once the
|
||||
guardrail has fired and they burn reviewer budget. (`review-code` no longer
|
||||
reaches round 4 — its round budget is 2/3, see the binding above.)
|
||||
|
||||
**Provisional, not empirically validated**: the four safeguards make `review-artifact`=3
|
||||
defensible, but it has no fresh run-data yet. Treat it as a hypothesis to
|
||||
confirm via the retrospective signal ([org-internal #1834]; gate-trim's landing machinery was
|
||||
retired, [org-internal #3072] phase 3 — the check is observational, not a trim proposal): if
|
||||
live `review-artifact` runs frequently hit round 3 still improving yet are
|
||||
forced to stop at WARN/FAIL, bump it back to 4.
|
||||
|
||||
**Severity handling — INFO disposition** (non-blocking, non-revising): INFO
|
||||
findings are observations/recommendations beyond the checklist. They NEVER
|
||||
block convergence and NEVER drive a reviser round.
|
||||
- A dimension whose ONLY open findings are INFO has effective verdict **PASS**
|
||||
— INFO does not reduce `pass_rate` and does not cause WARN/FAIL (see
|
||||
orchestrator rule #3). Such a dimension does NOT count against
|
||||
`overall_verdict`.
|
||||
- At synthesis, the Synthesizer dispositions EVERY INFO finding as one of:
|
||||
- `TD-candidate` — actionable observation worth a tech-debt item; record a
|
||||
suggested REGISTRY ROW (one-line summary + reactivation trigger,
|
||||
`core/rules/ticket-lifecycle.md`). Actual `TD-NNN` registration is
|
||||
deferred to the `verify` skill's Phase 5.5 registry filing — a
|
||||
TD-candidate does NOT open an independent ticket at review time
|
||||
(consistent with how `review-code` Accepted Risks flow to verify).
|
||||
- `DISCARDED` — not worth tracking; record a one-line reason.
|
||||
Dispositions are listed in the synthesis comment (markdown). INFO counts
|
||||
carry in `info_count` / `infos` for reporting only.
|
||||
- Phase C reviser task (step 1 above) references ONLY dimensions with open
|
||||
BLOCKER/MAJOR/MINOR findings. INFO findings are NEVER passed to the
|
||||
{REVISION_ROLE}.
|
||||
|
||||
**Round-3 Escalation Guardrail** (applies to the `review-artifact` skill's
|
||||
live targets — the process audit target AND the review-dag single-gate
|
||||
target; the roadmap / design / plan targets were archived 2026-08-21,
|
||||
[org-internal #3072] phase 3). `review-code` does NOT use this guardrail; it uses its round
|
||||
budget ([org-internal #3567], see the `{MAX_ROUNDS}` binding above).
|
||||
|
||||
At the start of round 3, if the combined blocker+major count is still >50% of
|
||||
round 1's count, STOP the loop and surface to the user:
|
||||
|
||||
> Review not converging: round 1 had {N1} blockers+majors, round 2 still has
|
||||
> {N2} ({pct}% of round 1). This suggests the artifact under review needs a
|
||||
> substantive rewrite, not incremental patching. Options:
|
||||
> (a) Return to upstream — for the `process` audit target, escalate to the
|
||||
> octopus maintainer for a process-level redesign (the legacy producer skills
|
||||
> `roadmap` / `requirements-elicitation` / `design` / `plan-iterations` were
|
||||
> archived 2026-08-21, [org-internal #3072] phase 3 — `<instance-root>/archive/`), then restart this
|
||||
> review at round 1.
|
||||
> (b) Continue iterating — accept that more rounds may be needed (capped by
|
||||
> `{MAX_ROUNDS}`).
|
||||
|
||||
Choosing (a) returns control to the upstream skill (or maintainer); the
|
||||
review's commit status is marked with description `"escalated"` and
|
||||
`converged: false`.
|
||||
|
||||
**Round-4 user-choice escalation point (shared)**: a multi-round review skill
|
||||
MUST offer the user an explicit choice before entering round 4 — (a) stop the
|
||||
review and escalate to a human, or (b) continue into round 4 — via the
|
||||
`question` tool. The guard means "escalate with a choice", NOT "force-stop".
|
||||
The choice is UNCONDITIONAL at the round-3→4 transition — instance skills may
|
||||
narrow when an escalation *variant* (e.g. a design-level decision) fires, but
|
||||
never narrow the choice gate itself. The call MUST be optionized ([org-internal #3378]):
|
||||
two options — `Stop & escalate to a human` (first, when recommended) /
|
||||
`Continue into round 4` — with `stage` set to the running skill's stage so
|
||||
auto-approval applies in autonomous mode; free-text asking is a spec
|
||||
violation.
|
||||
Instance: review-dag D4 option-b (`review-artifact/SKILL.md` §"Round-3
|
||||
Escalation Guardrail interaction") — the sole live instance, its D4 round cap
|
||||
(≤4) being the only budget that reaches round 4 (`review-code` is capped 2/3,
|
||||
[org-internal #3567]).
|
||||
Rationale (retro 2026-08-21 / [org-internal #3050]): unattended late-round loops drift into
|
||||
review marathons — the choice forces the design-level stall to surface to a
|
||||
human at round 4 instead of round 6.
|
||||
|
||||
**Efficiency optimization**: From round 2 onward ([org-internal #2598]), only spawn
|
||||
{REVIEWER_OR_AUDITOR}s for dimensions that had FAIL, WARN, or UNRESOLVED in
|
||||
the previous round. Every 2 rounds, run a full {DIMENSION_COUNT}-reviewer
|
||||
sweep to catch regressions. From round 2 onward reviewer input is also
|
||||
delta-scoped: the file list handed to each reviewer covers only the files
|
||||
changed since the previous round plus their convention-comparison neighbors,
|
||||
not the round-1 full set.
|
||||
|
||||
---
|
||||
|
||||
## Phase E — Final Report
|
||||
|
||||
**Orchestrator actions**:
|
||||
|
||||
1. Write the final report as a Gitea wiki page at
|
||||
`{slug}/reviews/{stage}/final/report` (Pattern 1,
|
||||
`_shared/gitea-write-patterns.md`), using the template at
|
||||
`core/skills/_shared/review-final-report-template.md`. For the
|
||||
`audit-process` target, write to `audit/{date}/final/report` instead (wiki
|
||||
namespace override `audit/{date}/`). Also set the lifecycle `converged` flag
|
||||
in `<runs-root>/{slug}/reviews/{stage}/status.json`: `true` when the final
|
||||
verdict is PASS (or WARN with no unresolved BLOCKERs), else `false`. This is
|
||||
the ONLY place `converged` is written — the Orchestrator at Phase E; the
|
||||
Synthesizer never sets it.
|
||||
|
||||
2. Update the `## 工件索引` comment on the source issue per
|
||||
`_shared/gitea-write-patterns.md` Pattern 10: add/update the
|
||||
`REVIEW-{stage}` row (位置 = the final report page, 重读 = CORE —
|
||||
review conclusions are part of the compaction re-read set).
|
||||
Skip if no source issue exists (Pattern 10 step 1).
|
||||
|
||||
2.5. **Inline-ization (skills that bind it)** — when the review target is a
|
||||
PR and the skill's Phase E binds the inline-comment step (currently
|
||||
`review-code` only, as its Phase E step 2.56), execute it now: post the
|
||||
final round's findings as PR inline comments via
|
||||
`<harness-package>/script/post-inline-comments.ts` (consumer of frozen
|
||||
contract `shared/review-inline-v1`): noise findings (INFO / DISCARDED /
|
||||
`WAIVED-{id}`) are never inlined (contract behavior 2), multi-round
|
||||
posts delete the identity's previous inline comments first (behavior 5),
|
||||
stale lines degrade to the file's first diff hunk (behavior 4; caveat:
|
||||
this server build returns HTTP 500 rather than the contract's 422 for
|
||||
stale lines — TD-611 — so stale items currently surface under `failed`
|
||||
with `fallback` empty), and partial failures are accounted — never
|
||||
blocking the verdict (behavior 6) — in the synthesis comment and final
|
||||
report's `inline: X posted / Y file-level / Z fallback / W failed` line.
|
||||
Skills whose target has no PR (e.g. `audit-process`) skip this step. The
|
||||
skill-specific binding (runner invocation, findings paths, identity,
|
||||
cleanup) is normative in the binding skill's own Phase E section.
|
||||
|
||||
3. Post final commit status via REST:
|
||||
|
||||
```
|
||||
POST /api/v1/repos/Octopus/octopus/statuses/{SHA}
|
||||
state: "success"|"failure"
|
||||
context: "pipeline/{REVIEW_TYPE}"
|
||||
description: "Final: {PASS|WARN|FAIL} after {N} rounds — converged: {true|false}"
|
||||
target_url: "{wiki page link}"
|
||||
```
|
||||
|
||||
3.5. **Gitea approval timing (mandatory when the PR needs a human approval
|
||||
gate)**: if the flow requests approval from a human reviewer on Gitea,
|
||||
register the reviewer FIRST via `gitea_pull__add_reviewer`, wait for the
|
||||
`REQUEST_REVIEW` registration to be recorded, and only THEN ask the human
|
||||
to approve. Approving before the reviewer is registered produces
|
||||
`official: false` on the approval — the gate treats the PR as unapproved
|
||||
and the approval must be re-done. ([org-internal #1931]: audit R2→R3 re-loop + re-approve,
|
||||
~30min wasted on exactly this ordering.)
|
||||
|
||||
4. Present the final report to the user and obtain approval via the `question`
|
||||
tool, passing `stage: "{STAGE}"`.
|
||||
|
||||
The `question` tool auto-approves per `core/rules/auto-approve.md` (stage `"{STAGE}"`).
|
||||
|
||||
Do NOT advance without approval (auto or explicit).
|
||||
|
||||
---
|
||||
|
||||
## Orchestrator Rules (Mandatory)
|
||||
|
||||
See `core/skills/_shared/review-orchestrator-rules.md` for the mandatory
|
||||
Orchestrator behavior rules. Substitute `{REVISION_ROLE}`: {REVISION_ROLE}.
|
||||
@@ -0,0 +1,135 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Shared Reviewer Prompt Template
|
||||
|
||||
> Canonical template for spawning parallel Explorer reviewers. Read on-demand
|
||||
> by the Orchestrator when dispatching reviewers. The Orchestrator substitutes
|
||||
> skill-level constants (fixed per skill, documented in the skill's SKILL.md)
|
||||
> and per-dimension values (filled from the skill's dimension mapping table).
|
||||
|
||||
The prompt text below is sent to each reviewer. Substitute all `{PLACEHOLDER}`
|
||||
values before dispatch.
|
||||
|
||||
```
|
||||
You are a {REVIEWER_ROLE}. Your responsibility is exactly the dimension(s)
|
||||
assigned to you — one dimension in the standard dispatch, or the named group
|
||||
of dimensions in a risk-tier grouped dispatch (review-code S/M tiers). Do NOT
|
||||
expand or narrow your scope beyond the assignment. Examine every file
|
||||
assigned to you and flag every issue you find, regardless of perceived
|
||||
importance.
|
||||
|
||||
{BASELINE_SECTION}
|
||||
|
||||
## Your Dimension(s)
|
||||
|
||||
Code: {DIMENSION_CODE}
|
||||
Label: {DIMENSION_NAME}
|
||||
|
||||
## Checklist Items You Must Verify
|
||||
|
||||
{CHECKLIST_ITEMS}
|
||||
|
||||
{FILE_SECTIONS}
|
||||
|
||||
## Instructions
|
||||
|
||||
{READ_FIRST_STEPS}
|
||||
4. For each checklist item, determine whether the {ARTIFACT_NOUN} satisfies it.
|
||||
Every checklist item must receive a PASS or FAIL verdict.
|
||||
Items that are genuinely not applicable must be marked NA with a reason.
|
||||
5. For every FAIL, produce a finding with: a unique id (pattern {FINDING_ID_PREFIX}-F{NNN},
|
||||
e.g. {FINDING_ID_EXAMPLE}), severity, checklist ref, {FINDING_DETAILS}, and a concrete
|
||||
suggestion for how to fix it. {FINDING_ID_PREFIX} is the finding's OWN
|
||||
dimension code — equal to {DIMENSION_CODE} in the standard single-dimension
|
||||
dispatch; for a grouped dispatch each finding's id uses its own dimension
|
||||
code (e.g. COR-F001, TST-F001), NOT the joined group code
|
||||
(never `COR+TST+DBT-F001`). When your dispatch is multi-slot
|
||||
(reviewer_count > 1), use the slot-prefixed form {DIM}R{k}-F{NNN} with your
|
||||
1-based slot k (e.g. TOPOR1-F001) so parallel slot files never collide on id.
|
||||
{CONTRADICTION_RULE}
|
||||
7. Assign severity strictly:
|
||||
{SEVERITY_DEFINITIONS}
|
||||
8. Count every PASS and FAIL. Compute pass_rate = passed / total_checks.
|
||||
Verdict rules:
|
||||
- PASS: pass_rate = 1.0 (no failed checks)
|
||||
- WARN: pass_rate >= 0.7 and no BLOCKER findings
|
||||
- FAIL: any BLOCKER finding OR pass_rate < 0.7
|
||||
9. Place the computed counts in a `quantitative_summary` object with fields
|
||||
`total_checks`, `passed`, `failed`, `pass_rate`, `na_count` (if any items were marked NA),
|
||||
and the four severity counts `blocker_count`, `major_count`, `minor_count`,
|
||||
`info_count` (count the BLOCKER/MAJOR/MINOR/INFO findings you produced —
|
||||
the Synthesizer consumes these for aggregation).
|
||||
10. Write findings to `{OUTPUT_FILE}`
|
||||
using the JSON Schema defined at `core/schemas/reviewer-output.schema.json`.
|
||||
Populate `reviewed_files` with the list of file paths you actually read
|
||||
during review.
|
||||
|
||||
## What You Must NOT Do
|
||||
|
||||
- Do NOT fabricate results. If a tool you need (e.g. `gitea_wiki__get_page`,
|
||||
`read`, `codegraph_*`) is unavailable, errors, or returns empty, report the
|
||||
failure honestly — emit a finding stating the dimension could not be
|
||||
reviewed due to the missing tool. NEVER invent findings, severities, or
|
||||
file references you did not derive from a successful tool call. Fabricated
|
||||
review output is a critical defect (per `core/rules/claim-discipline.md`
|
||||
§ "Tool-failure honesty").
|
||||
- Do NOT narrow your scope because "this seems minor".
|
||||
- Do NOT skip checklist items because "{LOOKS_FINE_PHRASE}".
|
||||
- Do NOT produce a PASS verdict to save effort. Base verdicts on data.
|
||||
- Do NOT add findings for dimensions you are not assigned to.
|
||||
- Do NOT read files outside your assigned list.
|
||||
- Do NOT edit any file EXCEPT your own findings file at `{OUTPUT_FILE}`. You are read-only for all other files.
|
||||
```
|
||||
|
||||
## Placeholders
|
||||
|
||||
### Skill-level constants (fixed per skill — see the skill's SKILL.md)
|
||||
|
||||
- `{REVIEWER_ROLE}` — reviewer identity (e.g. "code reviewer", "DAG reviewer", "process auditor")
|
||||
- `{BASELINE_SECTION}` — the full baseline section heading + body (differs structurally
|
||||
per skill: single design baseline vs. dual requirements+design baselines)
|
||||
- `{READ_FIRST_STEPS}` — instruction steps 1–3 (what to read first; includes the `1.` `2.`
|
||||
`3.` prefixes)
|
||||
- `{ARTIFACT_NOUN}` — "code" or "artifact" (used in step 4)
|
||||
- `{FINDING_DETAILS}` — the location/evidence phrase in step 5 (e.g. "file path and line
|
||||
number, a summary, detailed explanation, quoted code evidence")
|
||||
- `{FINDING_ID_EXAMPLE}` — example finding id (e.g. COR-F001, CMP-F001)
|
||||
- `{FINDING_ID_PREFIX}` — the id prefix for findings: the finding's own
|
||||
dimension code (equal to `{DIMENSION_CODE}` in the standard dispatch; the
|
||||
own-code of each finding inside a group in a grouped dispatch; see step 5's
|
||||
per-finding rule and the multi-slot `{DIM}R{k}-F{NNN}` form)
|
||||
- `{CONTRADICTION_RULE}` — step 6 in full (includes the `6.` prefix); the rule for when
|
||||
an artifact contradicts its baseline
|
||||
- `{SEVERITY_DEFINITIONS}` — the four severity bullets (BLOCKER/MAJOR/MINOR/INFO) with
|
||||
leading indentation matching step 7's sub-items
|
||||
- `{LOOKS_FINE_PHRASE}` — "the code looks fine" / "the artifacts look fine"
|
||||
|
||||
### Per-dimension values (filled by the Orchestrator from the dimension mapping table)
|
||||
|
||||
- `{DIMENSION_CODE}` — the dimension code (e.g. COR, ARCH). For a grouped
|
||||
dispatch (review-code S/M risk tiers, where one reviewer covers several
|
||||
dimensions), fill with the joined group, e.g. `COR+TST+DBT` — the reviewer
|
||||
then emits one findings file for the group with per-finding ids prefixed by
|
||||
each finding's own dimension code.
|
||||
- `{DIMENSION_NAME}` — the dimension label (e.g. Correctness, Architecture);
|
||||
for a grouped dispatch, the joined labels of the group.
|
||||
- `{CHECKLIST_ITEMS}` — the inline checklist items text for this dimension (read from
|
||||
the checklist file; inlined so the reviewer does not need to open the checklist)
|
||||
- `{FILE_SECTIONS}` — the file sections block (skill-specific section headers + the
|
||||
dimension's assigned file paths; include the "(none)" note where applicable)
|
||||
- `{OUTPUT_FILE}` — the output target for this reviewer's findings. The skill
|
||||
picks the findings sink (see `_shared/review-pipeline-phases.md` "Output model
|
||||
— two findings sinks"):
|
||||
- Tier 1 local (migrated skills, e.g. `review-code`): the absolute path inside
|
||||
the worktree, e.g.
|
||||
`<worktrees-root>/<repo>-<slug>/<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`
|
||||
— a JSON file validated by `core/schemas/reviewer-output.schema.json`.
|
||||
The reviewer is granted scoped write to `<runs-root>/**` for this.
|
||||
When the dispatch assigns MORE THAN ONE reviewer to this dimension
|
||||
(dim_split > 1), each reviewer writes its OWN slot file
|
||||
`findings-{DIMENSION_CODE}-r{k}.json` (k = its 1-based slot) with
|
||||
`reviewer_slot` / `reviewer_count` set — parallel reviewers never share
|
||||
one file.
|
||||
- Tier 2 comment (unmigrated fallback): post an issue comment via
|
||||
`工单评论 API(见 TERMINOLOGY)` (Pattern 5) with a fenced ` ```json ` block,
|
||||
labeled `{DIMENSION_CODE} review — round {N}` (multi-slot reviewers append
|
||||
` · r{k}` to the label).
|
||||
@@ -0,0 +1,57 @@
|
||||
# Shared Revision Prompt Template
|
||||
|
||||
> Canonical prompt for spawning the {ROLE} (Worker) that revises artifacts
|
||||
> between review rounds. Read on-demand by the Orchestrator in Phase C. The
|
||||
> Orchestrator substitutes skill-level constants before dispatch.
|
||||
|
||||
```
|
||||
Read `{TASK_FILE}` for your input files. {REVISION_SCOPE}
|
||||
|
||||
{RESOLUTION_RULE}
|
||||
|
||||
Your task file references ONLY dimensions with open BLOCKER, MAJOR, or MINOR
|
||||
findings. INFO findings are NEVER in your scope — they are dispositioned
|
||||
(`TD-candidate` or `DISCARDED`) at synthesis and excluded from revision (see
|
||||
`.octopus/skills/_shared/review-pipeline-phases.md` Phase D, "Severity
|
||||
handling — INFO disposition"). Do NOT act on or chase INFO observations.
|
||||
|
||||
From round 2 onward, every remaining purely cosmetic MINOR finding
|
||||
({COSMETIC_MINOR_EXAMPLES} — per the severity definition above) MUST be explicitly
|
||||
dispositioned: either fix it and mark it resolved, or waive it by recording
|
||||
`WAIVED-{finding-id}` (e.g. `{WAIVED_EXAMPLE}`) with a one-line reason and setting
|
||||
the finding's status to `WAIVED`. Cosmetic MINORs MUST NOT carry forward as open
|
||||
items beyond round 2 — an unresolved cosmetic MINOR is itself a process violation.
|
||||
This is the canonical definition of the `WAIVED-{id}` convention referenced in
|
||||
`{CHECKLIST_REF}`.
|
||||
|
||||
After revising, report a summary of changes made:
|
||||
- Files modified: {FILES_MODIFIED_LINE}
|
||||
- Findings addressed: {counts by severity}
|
||||
- Findings deferred with justification: {list}
|
||||
- Findings waived as cosmetic MINOR (round 2+): {WAIVED-{id} → reason}
|
||||
|
||||
Persist that summary to `{RETURN_FILE}` as your LAST action, BEFORE
|
||||
returning it — worker-report persistence ([org-internal #2847], see
|
||||
`.octopus/skills/_shared/worker-report-persistence.md`). The persisted copy
|
||||
is the report of record; if your completion notification is lost, the
|
||||
Orchestrator recovers the revision outcome from this file.
|
||||
```
|
||||
|
||||
## Placeholders
|
||||
|
||||
### Skill-level constants (fixed per skill — see the skill's SKILL.md)
|
||||
|
||||
- `{ROLE}` — "Developer" (code review), "Analyst" (review-dag single gate), or "Remediator" (process audit)
|
||||
- `{TASK_FILE}` — `task-developer`, `task-architect`, `task-analyst`, `task-planner`, or `task-remediator` (wiki page name under `{slug}/reviews/{stage}/round{N}/`)
|
||||
- `{REVISION_SCOPE}` — the opening paragraph: what to revise and the
|
||||
minimal/surgical constraint
|
||||
- `{RESOLUTION_RULE}` — the design-deviation / resolution-comment rule
|
||||
- `{COSMETIC_MINOR_EXAMPLES}` — the parenthetical examples of cosmetic MINORs
|
||||
- `{WAIVED_EXAMPLE}` — example waived id (e.g. `WAIVED-COR-R2-001`)
|
||||
- `{CHECKLIST_REF}` — the checklist file(s) the WAIVED convention is defined in
|
||||
- `{FILES_MODIFIED_LINE}` — `{list}` or `{list by artifact type}`
|
||||
- `{RETURN_FILE}` — the Tier 1 return-persistence target for this dispatch,
|
||||
following the skill's canonical stage path (e.g. review-code:
|
||||
`.octopus/runs/{slug}/reviews/code/round{N}/revision-return.md`); default
|
||||
per convention: `.octopus/runs/{slug}/workers/{chunk-id}-worker-{seq}.md`,
|
||||
lightweight fallback `/tmp/octopus/{chunk-id}-worker-{seq}.md`
|
||||
@@ -0,0 +1,86 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
name: Coordinator
|
||||
purpose: >
|
||||
Pool dispatch coordinator (billing-v2 [org-internal #4041] §7, REQ-F-012): a wake-driven,
|
||||
STATELESS decision-maker session living in the pool pod alongside workers.
|
||||
List eligible issues (Gitea API, scoped token) → produce DAG-aware dispatch
|
||||
proposals → commit each dispatch through the N-16 assignment API
|
||||
(POST /api/pools/:repo/assignments). The coordinator decides; the
|
||||
control-plane dumbwaiters make the commit atomic. It never holds wallet
|
||||
internals, never bypasses the central ledger, and never merges (E-4).
|
||||
|
||||
# Layer 5 — Executable
|
||||
agent: worker
|
||||
model: null
|
||||
agents_md:
|
||||
- workflow-routing
|
||||
- claim-discipline
|
||||
- code-graph
|
||||
- compact
|
||||
|
||||
# Stateless iron rule (§7.1): the coordinator's ENTIRE dispatch state is the
|
||||
# work_order_assignments table. After any wake (first start, re-wake after
|
||||
# idle, or takeover after the previous coordinator died) the first action is
|
||||
# ALWAYS: GET /api/pools/:repo/assignments?state=assigned — rebuild the
|
||||
# in-flight view from the table, never from memory or notes. Dying costs
|
||||
# nothing; the pool reconciler migrates the role to the earliest surviving
|
||||
# member by convention (no election).
|
||||
#
|
||||
# Decision procedure (AC-1) — full text: docs/runbooks/pool-coordinator.md.
|
||||
# 1. recover — read the assignment table (state=assigned) + org
|
||||
# notifications (pools.assignment.expired = re-dispatch inbox)
|
||||
# 2. list — eligible issues via Gitea API (scoped token): labeled
|
||||
# Kind/* and unassigned/without active assignment row
|
||||
# 3. propose — DAG-aware: respect blockedBy edges (epic DAG / node deps),
|
||||
# sibling load (least-loaded member first), and the N>1
|
||||
# self-assign constraints below; fast path (AC-1 简单场景):
|
||||
# exactly one eligible issue and one available member →
|
||||
# deterministic pick, no elaborate deliberation needed
|
||||
# 4. commit — one POST per dispatch; proposal payload carries the audit
|
||||
# trace (§audit contract below); 409/403 envelopes are
|
||||
# terminal outcomes, not retried blindly
|
||||
#
|
||||
# Self-assign constraints (AC-2, §7.2 N>1) — the ledger is identity-blind, so
|
||||
# the constraints live HERE and are enforced by convention + audited via the
|
||||
# trace: least-loaded ordering puts self LAST (self-assign only when no other
|
||||
# member is available); the coordinator holds at most 1 active assignment at
|
||||
# a time, same cap as any worker; self-dispatch still goes through the same
|
||||
# POST (铁律 — never "start working because I am the coordinator"). N=1:
|
||||
# self-dispatch is the only form and is NOT a special branch.
|
||||
#
|
||||
# Audit contract (AC-4/AC-6): every POST carries proposal =
|
||||
# { version: 1,
|
||||
# decision: { coordinator, self_position: "last"|"n/a", held_active,
|
||||
# cap: 1, fast_path: boolean, reason },
|
||||
# eligible: [{ issue, blockedBy: [issue,...] }...],
|
||||
# chosen: { issue, member } }
|
||||
# The trace lands verbatim on the assignment row (jsonb proposal_payload),
|
||||
# is queryable via GET /api/pools/:repo/assignments, and is exported in the
|
||||
# assignmentAuditRecord shape (runs archive / pool-drill.sh). LLM decisions
|
||||
# are not replayable — the recorded decision + the deterministic commit is
|
||||
# the audit story.
|
||||
#
|
||||
# Security posture (AC-5, see docs/security/threat-model.md §自治批处理):
|
||||
# the coordinator authenticates with a workspace-level API token
|
||||
# (TD-687/[org-internal #3898] owner-minted, jti-revocable, role claim org_role). TODAY'S
|
||||
# mint shape hardcodes org_role=viewer — read-capable only (assignment table
|
||||
# + pool surfaces); the editor-capable mint needed for dispatch submission is
|
||||
# an explicit deployment-time decision on the [org-internal #3898] channel (not landed with
|
||||
# N-19). Either way the token can NOT mint tokens, touch wallet internals, or
|
||||
# merge PRs — the merge gate stays human (E-4).
|
||||
#
|
||||
# Worker-report persistence ([org-internal #2847] — see
|
||||
# core/skills/_shared/worker-report-persistence.md): a Coordinator
|
||||
# sub-agent persists its FINAL RETURN REPORT to disk as its LAST action,
|
||||
# before returning — <runs-root>/{slug}/workers/{chunk-id}-worker-{seq}.md
|
||||
# (Tier 1 preferred; else /tmp/octopus/{chunk-id}-worker-{seq}.md), and
|
||||
# appends its working-notes entry at stage exit per
|
||||
# core/templates/runs-layout.md. The persisted copy is the report of
|
||||
# record; re-read persisted artifacts after any compaction, never rely on
|
||||
# conversation memory.
|
||||
compact:
|
||||
preserve:
|
||||
- "docs/runbooks/pool-coordinator.md — the decision procedure is file-driven, never memory-driven; re-read after any compaction"
|
||||
- "the recovered in-flight view (GET /api/pools/:repo/assignments?state=assigned) — the coordinator holds NO dispatch state in memory; re-read the table first after every wake/compaction"
|
||||
- "the audit trace schema (decision.self_position / decision.held_active / decision.cap / eligible[].blockedBy) — drift here breaks AC-6 auditability"
|
||||
- "self-assign constraints: self LAST in least-loaded order, active-assignment cap 1, self-dispatch goes through the same POST — never a special branch (§7.2 铁律)"
|
||||
@@ -0,0 +1,41 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
name: Producer
|
||||
purpose: >
|
||||
Write production artifacts — requirements, design, plans, code, fixes,
|
||||
refactors, releases, retrospectives. The specific task, its checklist, and
|
||||
the current pipeline stage are injected by the calling skill.
|
||||
|
||||
# Layer 5 — Executable
|
||||
agent: worker
|
||||
model: null
|
||||
agents_md:
|
||||
- code-graph
|
||||
- testing
|
||||
- type-checking
|
||||
- claim-discipline
|
||||
- compact
|
||||
|
||||
# Worker-report persistence ([org-internal #2847], retro-2696 AI#2 — see
|
||||
# core/skills/_shared/worker-report-persistence.md): every Producer
|
||||
# sub-agent persists its FINAL RETURN REPORT to disk as its LAST action,
|
||||
# before returning — <runs-root>/{slug}/workers/{chunk-id}-worker-{seq}.md
|
||||
# (Tier 1 preferred; skill-canonical stage path when the skill defines one),
|
||||
# else /tmp/octopus/{chunk-id}-worker-{seq}.md. The persisted copy is the
|
||||
# report of record; the task notification is a convenience copy.
|
||||
#
|
||||
# Working-notes contract ([org-internal #2600] — see core/templates/runs-layout.md §
|
||||
# working-notes.md): at stage exit the Producer appends ONE entry (≤30 lines:
|
||||
# decisions and why, dead ends, reviewer feedback that changed the work, hints
|
||||
# for the next stage) to <runs-root>/{slug}/working-notes.md, and registers
|
||||
# its index.json row (type working-notes) once if absent.
|
||||
#
|
||||
# Role-level compaction contract (harness-enforced — see core/rules/compact.md
|
||||
# § "Role-level re-read set"). After any compaction, a Producer sub-agent re-reads
|
||||
# these persisted artifacts before continuing; it never relies on conversation
|
||||
# memory for them.
|
||||
compact:
|
||||
preserve:
|
||||
- "<runs-root>/{slug}/working-notes.md (re-read FIRST, before formal artifacts — carries prior stages' tacit context: decisions, dead ends, reviewer feedback)"
|
||||
- "the work item / node spec being implemented (issue body or the frozen DAG copy {epic-slug}/dag + {epic-slug}/dag-nodes/{node-id} subpages)"
|
||||
- "the node's design baseline: node spec + cross-session edge contracts in {epic-slug}/dag (design detail is folded into node AC + contracts)"
|
||||
- "the acceptance criteria for the current node (node acceptance_criteria in {epic-slug}/dag, or the task ticket body)"
|
||||
@@ -0,0 +1,30 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
name: Reviewer
|
||||
purpose: >
|
||||
Review one dimension of an artifact against its checklist, read-only.
|
||||
Find issues that would block merge. One reviewer per dimension, all
|
||||
running in parallel.
|
||||
|
||||
# Layer 5 — Executable
|
||||
agent: explorer
|
||||
model: null
|
||||
agents_md:
|
||||
- code-graph
|
||||
- claim-discipline
|
||||
- ticket-lifecycle
|
||||
- compact
|
||||
|
||||
# Working-notes contract ([org-internal #2600] — see core/templates/runs-layout.md §
|
||||
# working-notes.md): the Reviewer side appends ONE distilled entry via the
|
||||
# Synthesizer at review convergence — never per dimension dispatch (9
|
||||
# dimension dispatches = 1 entry) — to <runs-root>/{slug}/working-notes.md.
|
||||
#
|
||||
# Role-level compaction contract (harness-enforced — see core/rules/compact.md
|
||||
# §"Role-level re-read set"). A Reviewer is read-only and short-lived (one
|
||||
# dimension per dispatch); after any compaction it re-reads its checklist and
|
||||
# the artifact under review, never conversation memory.
|
||||
compact:
|
||||
preserve:
|
||||
- "<runs-root>/{slug}/working-notes.md (re-read FIRST — prior stages' tacit context; also the file the Synthesizer's convergence entry appends to)"
|
||||
- "the dimension checklist being applied (core/checklists/*.md)"
|
||||
- "the artifact under review ({slug}/ wiki page or PR diff)"
|
||||
@@ -0,0 +1,26 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
name: Tool
|
||||
purpose: >
|
||||
Specialized tool agent for image interpretation and other vision/utility
|
||||
tasks that require a specific model.
|
||||
|
||||
# Layer 5 — Executable
|
||||
agent: explorer
|
||||
model: opencode/claude-haiku-4-5
|
||||
agents_md:
|
||||
- code-graph
|
||||
- claim-discipline
|
||||
- compact
|
||||
|
||||
# Working-notes contract ([org-internal #2600] — see core/templates/runs-layout.md §
|
||||
# working-notes.md): Tool agents append to <runs-root>/{slug}/working-notes.md
|
||||
# ONLY when the task produced a load-bearing finding (e.g. image evidence that
|
||||
# changed a decision) — not for routine single-shot output.
|
||||
#
|
||||
# Role-level compaction contract (harness-enforced — see core/rules/compact.md
|
||||
# §"Role-level re-read set"). Tool agents are single-shot; after any compaction
|
||||
# they re-read the task prompt's input artifact, never conversation memory.
|
||||
compact:
|
||||
preserve:
|
||||
- "<runs-root>/{slug}/working-notes.md (re-read FIRST — prior stages' tacit context)"
|
||||
- "the input artifact named in the task prompt (image path, file, or URL)"
|
||||
@@ -0,0 +1,40 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
name: Verifier
|
||||
purpose: >
|
||||
Run the iteration's DoD matrix, integration tests, NFR validation, and
|
||||
regression checks. The iteration is not Done until all DoD items pass.
|
||||
|
||||
# Layer 5 — Executable
|
||||
agent: worker
|
||||
model: null
|
||||
agents_md:
|
||||
- code-graph
|
||||
- testing
|
||||
- ticket-lifecycle
|
||||
- type-checking
|
||||
- claim-discipline
|
||||
- compact
|
||||
|
||||
# Worker-report persistence ([org-internal #2847], retro-2696 AI#2 — see
|
||||
# core/skills/_shared/worker-report-persistence.md): the Verifier
|
||||
# persists its FINAL RETURN REPORT (DoD verdict + evidence pointers) to disk
|
||||
# as its LAST action, before returning —
|
||||
# <runs-root>/{slug}/workers/{chunk-id}-worker-{seq}.md (Tier 1 preferred),
|
||||
# else /tmp/octopus/{chunk-id}-worker-{seq}.md. The persisted copy is the
|
||||
# report of record; the task notification is a convenience copy.
|
||||
#
|
||||
# Working-notes contract ([org-internal #2600] — see core/templates/runs-layout.md §
|
||||
# working-notes.md): the Verifier appends ONE entry at verify exit (what the
|
||||
# DoD sweep caught, flaky/deferral context the next run should know). The
|
||||
# Phase 5.5 archive step additionally inlines the final working-notes.md
|
||||
# content into the archive bundle's working_notes field.
|
||||
#
|
||||
# Role-level compaction contract (harness-enforced — see core/rules/compact.md
|
||||
# §"Role-level re-read set"). After any compaction, a Verifier re-reads these
|
||||
# persisted artifacts before continuing; it never relies on conversation memory.
|
||||
compact:
|
||||
preserve:
|
||||
- "<runs-root>/{slug}/working-notes.md (re-read FIRST, before formal artifacts — carries prior stages' tacit context: decisions, dead ends, reviewer feedback)"
|
||||
- "the DoD matrix for the current iteration ({slug}/05-verify-*)"
|
||||
- "the acceptance criteria (node acceptance_criteria in the frozen DAG copy {epic-slug}/dag, or the task ticket body)"
|
||||
- "the final code-review report ({slug}/reviews/code/final/report)"
|
||||
@@ -0,0 +1,236 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Synthesis Task — Round {ROUND_NUMBER}
|
||||
|
||||
> Shared template filled by the Orchestrator and dispatched to the Synthesizer
|
||||
> (Worker) during Phase B of all review and audit skills. The Orchestrator
|
||||
> writes this template to the skill's configured prompt location (Tier 1 local
|
||||
> `<runs-root>/{slug}/reviews/{stage}/round{N}/task-synthesizer.md` for
|
||||
> `tier1-local` skills like `review-code`; `/tmp/octopus/synthesis_task_{slug}_round{N}.md`
|
||||
> + a Gitea wiki page for `tier2-comment` skills) for `prompt_file` dispatch.
|
||||
>
|
||||
> The Synthesizer reads all dimension findings from the skill's findings sink
|
||||
> (local files or issue comments), posts the synthesis to the pipeline PR /
|
||||
> source issue comment — for audit-process runs, the wiki page
|
||||
> `audit/{date}/round{N}/synthesis` — with a fenced JSON block (Tier 2 human
|
||||
> gate — always Gitea), updates the commit status, and returns a compact
|
||||
> summary (≤ 2KB).
|
||||
|
||||
## Input
|
||||
|
||||
- **Target**: PR #{PR_NUMBER} (pipeline mode) or issue #{ISSUE_NUMBER} (standalone mode)
|
||||
— for audit-process runs, the wiki page `audit/{date}/round{N}/synthesis`.
|
||||
Post the synthesis to this target.
|
||||
- **Findings sink**: `{FINDINGS_SINK}` — `tier1-local` or `tier2-comment`
|
||||
(set by the skill; see `_shared/review-pipeline-phases.md`).
|
||||
- **Dimension findings** — already produced by individual reviewers:
|
||||
- `tier1-local`: read the finding files at
|
||||
`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`,
|
||||
each a JSON document matching `core/schemas/reviewer-output.schema.json`.
|
||||
Read every `findings-*.json` in that round directory. A dimension with
|
||||
multiple reviewer slots (dim_split > 1) contributes several files
|
||||
(`findings-{DIM}-r1.json`, `-r2.json`, …) — aggregate them as ONE
|
||||
dimension's findings (union of findings; worst verdict wins).
|
||||
- `tier2-comment`: read the dimension issue comments via
|
||||
`工单评论 API(见 TERMINOLOGY)` (Pattern 3, `_shared/gitea-read-patterns.md`).
|
||||
Each comment body contains a `**{DIMENSION_CODE} review — round {N}**` header
|
||||
followed by a fenced ` ```json ` block matching
|
||||
`core/schemas/reviewer-output.schema.json`.
|
||||
- **Dimensions marked UNRESOLVED** (reviewer crashed/timed out, no finding exists):
|
||||
`{UNRESOLVED_DIMENSIONS}`
|
||||
Exclude these from aggregation.
|
||||
- **Synthesis schema**: `core/schemas/synthesis.schema.json`
|
||||
- **Commit SHA**: `{SHA}` (the HEAD commit being reviewed).
|
||||
- **Review type**: `{REVIEW_TYPE}` — one of `review-code`, `review-dag`,
|
||||
`audit-process` (the `review-design-space` / `review-iteration-plan` /
|
||||
`review-roadmap` targets were archived 2026-08-21, [org-internal #3072] phase 3 — their
|
||||
historical synthesis data remains readable).
|
||||
|
||||
## Your Job
|
||||
|
||||
1. Read all dimension findings from the configured sink. For each dimension,
|
||||
extract:
|
||||
- `verdict` (PASS / WARN / FAIL / UNRESOLVED)
|
||||
- `pass_rate`
|
||||
- `blocker_count`, `major_count`, `minor_count`, `info_count`
|
||||
- `findings` array (each finding has: id, severity, checklist_ref, summary)
|
||||
|
||||
1b. **Finding-id uniqueness post-check (MANDATORY)**: after reading ALL
|
||||
findings files of the round (including every `-r{k}` slot file), verify
|
||||
that `id` values are unique across the whole round. The reviewer-output
|
||||
schema's `uniqueItems` compares whole objects and does NOT enforce id
|
||||
uniqueness — this step is the enforcement. On a duplicate id: do NOT
|
||||
silently drop or renumber it; keep both findings in the raw files, note
|
||||
the collision in the synthesis digest, and mark the affected dimension
|
||||
WARN with the collision listed (the dispatch/prompt defect that produced
|
||||
it — e.g. a missing slot prefix — is the fix target).
|
||||
|
||||
2. Compute `aggregated_stats`:
|
||||
- `total_dimensions`: count of all dispatched dimensions (including UNRESOLVED)
|
||||
- `passed_dimensions`, `warn_dimensions`, `failed_dimensions`
|
||||
- `total_findings`, `blockers`, `majors`, `minors`, `infos`
|
||||
- `unresolved_dimensions`: count of UNRESOLVED
|
||||
|
||||
3. Collect `top_findings` — top 10 ranked by severity:
|
||||
- Order: BLOCKER > MAJOR > MINOR > INFO
|
||||
- Within each severity, order by dimension code alphabetically
|
||||
- Each entry: `{ id, dimension, severity, checklist_ref, summary }` — `id` is
|
||||
the source reviewer finding id (Tier 1 findings file; slot-prefixed for
|
||||
multi-slot dimensions), so each headline traces back to its raw finding
|
||||
|
||||
4. Compute `overall_verdict`:
|
||||
- **PASS**: all available dimensions have verdict PASS.
|
||||
- **WARN**: no FAIL dimensions AND no BLOCKER findings.
|
||||
- **FAIL**: any FAIL dimension OR any BLOCKER finding.
|
||||
|
||||
5. Post the synthesis as a **PR comment** (pipeline mode) or **issue comment**
|
||||
(standalone mode) via `工单评论 API(见 TERMINOLOGY)`:
|
||||
- **Exception — `audit-process` target**: process audits have no source
|
||||
issue, so post the synthesis as a **wiki page** `audit/{date}/round{N}/synthesis`
|
||||
via `wiki 读写 API(见 TERMINOLOGY)` instead of a PR/issue comment (see
|
||||
`_shared/review-pipeline-phases.md` Phase B step 3 and review-artifact/SKILL.md
|
||||
process target Output Artifacts). The commit-status `target_url` then
|
||||
carries that wiki page's `html_url`.
|
||||
- The comment body MUST contain a **markdown summary** (dimension table,
|
||||
top findings, verdict) followed by a **fenced ` ```json ` block** with
|
||||
the structured synthesis data.
|
||||
- Format:
|
||||
|
||||
```
|
||||
## Synthesis — Round {N}
|
||||
|
||||
**Verdict**: {PASS|WARN|FAIL}
|
||||
**Target**: PR #{N} | Issue #{N} (audit-process: wiki `audit/{date}/round{N}/synthesis`)
|
||||
**Dimensions**: {passed} passed, {warned} warned, {failed} failed, {unresolved} unresolved
|
||||
**Findings**: {B} BLOCKERs, {M} MAJORs, {m} MINORs, {I} INFOs
|
||||
|
||||
### Dimension Summary
|
||||
| Dimension | Verdict | Pass Rate | B | M | m | I |
|
||||
|-----------|---------|-----------|---|---|---|---|
|
||||
| ... | ... | ... | | | | |
|
||||
|
||||
### Top Findings
|
||||
1. [{DIM_CODE}] [{SEVERITY}] {checklist_ref}: {summary}
|
||||
2. ...
|
||||
|
||||
```json
|
||||
{
|
||||
"round": N,
|
||||
"overall_verdict": "PASS|WARN|FAIL",
|
||||
"dimensions": {
|
||||
"DIM_CODE": { "verdict": "PASS|WARN|FAIL|UNRESOLVED", "pass_rate": N, "blocker_count": N, "major_count": N, "minor_count": N, "info_count": N }
|
||||
},
|
||||
"aggregated_stats": {
|
||||
"total_dimensions": N,
|
||||
"passed_dimensions": N,
|
||||
"warn_dimensions": N,
|
||||
"failed_dimensions": N,
|
||||
"total_findings": N,
|
||||
"blockers": N,
|
||||
"majors": N,
|
||||
"minors": N,
|
||||
"infos": N,
|
||||
"unresolved_dimensions": N
|
||||
},
|
||||
"top_findings": [
|
||||
{ "id": "CODE-F001", "dimension": "CODE", "severity": "BLOCKER|MAJOR|MINOR|INFO", "checklist_ref": "X.Y", "summary": "..." }
|
||||
]
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
6. Post a **commit status** via REST:
|
||||
|
||||
```
|
||||
POST /api/v1/repos/Octopus/octopus/statuses/{SHA}
|
||||
Authorization: token {GITEA_TOKEN}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"state": "success"|"failure",
|
||||
"context": "pipeline/{REVIEW_TYPE}",
|
||||
"description": "Round {N}: {PASS|WARN|FAIL} — {passed}/{total} dims passed, {B}B {M}M",
|
||||
"target_url": "{link to synthesis comment}"
|
||||
}
|
||||
```
|
||||
|
||||
- `state` is `"success"` for PASS or WARN, `"failure"` for FAIL (any BLOCKER).
|
||||
- `context` uses the review type: `pipeline/review-code`,
|
||||
`pipeline/review-dag`, `pipeline/audit-process` (slug `audit`).
|
||||
|
||||
(See `_shared/gitea-write-patterns.md` Pattern 8 for the full commit-status recipe.)
|
||||
|
||||
7. Append one `history[]` entry for this round to the run's review-status file
|
||||
`<runs-root>/{slug}/reviews/{stage}/status.json` (validated by
|
||||
`core/schemas/review-status.schema.json`): `round`,
|
||||
`overall_verdict`, `blockers`, `majors`, `minors`, and update the file's
|
||||
top-level `current_round` to this round number. This is the durable
|
||||
per-round audit history (defined by `core/schemas/review-status.schema.json`
|
||||
`history[]`; mirrors the audit-process checklist TRC 10.4). Initialization is the **Orchestrator**'s
|
||||
job (Phase A step 0.5 of `_shared/review-pipeline-phases.md`): the file is
|
||||
created before round 1 with the full schema-required field set `slug`,
|
||||
`started_at`, `current_round`, `max_rounds`, `converged`, `history` — do NOT
|
||||
create it here; the Synthesizer only appends `history[]` and updates
|
||||
`current_round`.
|
||||
|
||||
8. **Round 1 only — findings digest** (rounds ≥ 2: skip this step): write the
|
||||
rendered `## Round-1 Findings Summary` table (columns
|
||||
`| Dim | Finding ID | Severity | Summary |`, one row per round-1 finding;
|
||||
Summary = one-line 摘由/root cause) to
|
||||
`<runs-root>/{slug}/reviews/{stage}/round1/findings-summary.md`,
|
||||
distilled from the round-1 findings you just read in your own context, and
|
||||
append the `index.json` row (type `review-synthesis`, stage
|
||||
`{REVIEW_TYPE}`, produced_by `Synthesizer`). The Orchestrator copies this
|
||||
file verbatim into the final report's Round-1 Findings Summary section at
|
||||
Phase E (mandatory for `review-code` step 2.55; other targets MAY include
|
||||
it — `_shared/review-final-report-template.md`) and does NOT read the
|
||||
findings files itself (`_shared/review-pipeline-phases.md` Phase B step 3
|
||||
"Round-1 findings digest").
|
||||
|
||||
9. Persist your return summary (the exact text from the Return Format
|
||||
below) to `<runs-root>/{slug}/reviews/{stage}/round{N}/synthesis-return.md`
|
||||
(Tier 1) as your LAST action, BEFORE returning it — worker-report
|
||||
persistence ([org-internal #2847], `_shared/worker-report-persistence.md`). The persisted
|
||||
copy is the report of record; if your completion notification is lost, the
|
||||
Orchestrator recovers the round verdict from this file.
|
||||
|
||||
## Governance (Binding)
|
||||
|
||||
- **Rule 4 — Aggregation, not reinterpretation**: You MUST NOT soften, rephrase,
|
||||
or downgrade reviewer findings. If a reviewer writes "BLOCKER", it stays
|
||||
"BLOCKER" in synthesis.
|
||||
- **Rule 5 — Anonymous findings preservation**: If two reviewers independently
|
||||
flag the same issue from different dimensions, preserve both entries. Do not
|
||||
deduplicate by discarding one — cross-dimension agreement is itself a signal.
|
||||
- **Schema fidelity**: The fenced JSON block MUST validate against
|
||||
`synthesis.schema.json`. Read the schema file first if unsure of field names.
|
||||
|
||||
## Return Format (Binding — ≤ 2KB)
|
||||
|
||||
Return ONLY this summary as your final message. Do NOT include the full
|
||||
synthesis JSON — it is already posted to the pipeline PR / source issue
|
||||
comment (for audit-process runs, the wiki page `audit/{date}/round{N}/synthesis`).
|
||||
Persist this summary verbatim to
|
||||
`<runs-root>/{slug}/reviews/{stage}/round{N}/synthesis-return.md` before
|
||||
returning (step 9 above — worker-report persistence [org-internal #2847]).
|
||||
|
||||
```
|
||||
## Synthesis Complete
|
||||
|
||||
**Round**: {N}
|
||||
**Verdict**: {PASS|WARN|FAIL}
|
||||
**Pass Rate**: {avg_pass_rate}
|
||||
**Dimensions**: {passed} passed, {warned} warn, {failed} failed, {unresolved} unresolved
|
||||
**Findings**: {B} BLOCKER, {M} MAJOR, {m} MINOR, {I} INFO
|
||||
|
||||
### Failed/Warn Dimensions (re-audit targets for next round):
|
||||
- {DIM_CODE}: verdict={FAIL|WARN}, findings={count} ({finding_ids})
|
||||
- ...
|
||||
|
||||
### Top Findings (for revision task):
|
||||
1. [{DIM_CODE}] [{SEVERITY}] {checklist_ref}: {summary}
|
||||
2. ...
|
||||
|
||||
### Artifacts Posted:
|
||||
- Synthesis comment on {PR_OR_ISSUE} #{N} (audit-process: wiki `audit/{date}/round{N}/synthesis`)
|
||||
- Commit status: pipeline/{REVIEW_TYPE} → {success|failure}
|
||||
```
|
||||
@@ -0,0 +1,98 @@
|
||||
# Worker Report Persistence (mandatory)
|
||||
|
||||
> Canonical convention for every worker sub-agent dispatch (roles whose
|
||||
> `agent:` is `worker` — Producer sub-roles, Verifier, Synthesizer, …),
|
||||
> foreground or background (`task background=true`). Origin: retro WWW#5 /
|
||||
> [org-internal #2696] action item #2 ([org-internal #2847]) — at least one confirmed case where a
|
||||
> background worker's completion report reached the orchestrating session
|
||||
> only via the task-tool notification; the notification was lost and the
|
||||
> report evaporated, leaving nothing but reverse-engineering from artifacts.
|
||||
|
||||
## The problem
|
||||
|
||||
A worker's final return report (status, verdict, judgment, evidence
|
||||
pointers, open items) travels to the orchestrating session through a single
|
||||
channel: the task completion notification. If that notification is lost —
|
||||
stream break, orchestrator interrupted before receipt, session restart —
|
||||
the report is gone even though the worker finished its work. Conclusions
|
||||
that exist only in a notification are not durable.
|
||||
|
||||
## The rule (Iron Law)
|
||||
|
||||
**Every worker MUST persist its final return report to disk as its LAST
|
||||
action, BEFORE returning.** The persisted copy is the report of record; the
|
||||
in-conversation return is a convenience copy. No worker return may exist
|
||||
only in the task notification.
|
||||
|
||||
## Where to persist (path selection, in order)
|
||||
|
||||
1. **Tier 1 run workspace (preferred when it exists)** — if
|
||||
`.octopus/runs/{slug}/` exists (see `.octopus/templates/runs-layout.md`),
|
||||
write the report to:
|
||||
- Default: `.octopus/runs/{slug}/workers/{chunk-id}-worker-{seq}.md`
|
||||
- Skill-canonical override: when the dispatching skill already owns a
|
||||
stage directory for this worker, the skill's canonical path replaces
|
||||
the generic name (e.g. the review-code Synthesizer writes
|
||||
`reviews/{stage}/round{N}/synthesis-return.md` next to its task file).
|
||||
- Register one `index.json` row: `type: "other"`, `schema: null`
|
||||
(markdown), `stage`: producing stage, `produced_by`: role name.
|
||||
2. **Lightweight fallback (no run workspace)** — standalone / ad-hoc
|
||||
sessions with no `.octopus/runs/{slug}/`: write to
|
||||
`/tmp/octopus/{chunk-id}-worker-{seq}.md`. Survives session death and
|
||||
compaction; not expected to survive machine restarts — acceptable for
|
||||
the notification-loss window it covers.
|
||||
|
||||
### Naming
|
||||
|
||||
- `{chunk-id}` — the owning work identifier stated in the dispatch prompt
|
||||
(ticket number, `WI-N`, chunk id, e.g. `2847`, `WI-3`).
|
||||
- `{seq}` — 1-based dispatch sequence of THIS worker within that chunk-id
|
||||
(a chunk dispatched a second time → `seq` 2). For review loops the round
|
||||
number in the skill-canonical path plays this role.
|
||||
- The dispatching Orchestrator MUST state both values (or the canonical
|
||||
path) in the dispatch prompt — see "Dispatch duty" below.
|
||||
|
||||
## What to persist
|
||||
|
||||
The **verbatim final return report** — the exact text the worker returns
|
||||
via the task result: status (`done` / `blocked`), verdict if any, key
|
||||
results, evidence pointers (commit sha, test names, `file:line`), and open
|
||||
items. Markdown, no schema. Keep it identical to the returned summary so
|
||||
recovery reads exactly what the orchestrator would have received.
|
||||
|
||||
## Orchestrator recovery path (notification missing)
|
||||
|
||||
When a dispatched worker's completion notification never arrives —
|
||||
background task result lost, or the orchestrating session was interrupted
|
||||
and resumed — the orchestrating session recovers the worker's conclusion
|
||||
from disk:
|
||||
|
||||
1. Identify the dispatch's `{chunk-id}` (from the dispatch record /
|
||||
working-notes / task log).
|
||||
2. Read, in order, the newest match by `{seq}` / mtime:
|
||||
- `.octopus/runs/{slug}/workers/{chunk-id}-worker-*.md`
|
||||
- the skill-canonical return file when the dispatch named one (e.g.
|
||||
`reviews/{stage}/round{N}/synthesis-return.md`)
|
||||
- `/tmp/octopus/{chunk-id}-worker-*.md`
|
||||
3. The newest persisted report IS the worker's conclusion of record —
|
||||
resume the pipeline from it. Do NOT re-dispatch a worker whose persisted
|
||||
report shows `done`; re-dispatch (or resume via `task_id`) only on
|
||||
`blocked` or a missing file.
|
||||
|
||||
## Dispatch duty (high-frequency dispatch points)
|
||||
|
||||
Every dispatch prompt for a worker MUST carry: (a) the persist instruction
|
||||
("persist your final report to `{path}` before returning, per
|
||||
`_shared/worker-report-persistence.md`"), and (b) the concrete target path
|
||||
with `{chunk-id}`/`{seq}` or the skill-canonical location filled in. The
|
||||
high-frequency templates carry it inline:
|
||||
|
||||
- `implement/SKILL.md` — Agent Role responsibilities + Phase 5 (Report)
|
||||
persist step (applies to every mode's report phase).
|
||||
- `review-code/SKILL.md` Phase B / Phase C dispatch steps, via the shared
|
||||
templates `_shared/synthesis_task.md` and
|
||||
`_shared/review-revision-prompt.md`.
|
||||
|
||||
Reviewer (Explorer) dispatches are covered structurally: their findings
|
||||
JSON is already a Tier 1 persisted artifact, and the Synthesizer's
|
||||
persisted return is the round's recoverable conclusion.
|
||||
@@ -0,0 +1,503 @@
|
||||
---
|
||||
name: analyze-dag
|
||||
description: >
|
||||
Use ONLY when an Epic is routed to the DAG ticket pipeline (Kind/Epic or
|
||||
Kind/Feature — both resolve to analyze-dag via the routes table directly,
|
||||
[org-internal #3072] phase 3 / [org-internal #3061] Phase 2). The
|
||||
Analyst decomposes the Epic into a single task-DAG artifact — merging
|
||||
roadmap+requirements+design+plan into one graph (nodes = acceptance
|
||||
criteria, edges = contracts, topology = plan).
|
||||
triggers:
|
||||
- analyze-dag
|
||||
- "dag pipeline"
|
||||
- "DAG 管线"
|
||||
- "任务 DAG"
|
||||
- "task DAG"
|
||||
- "DAG decomposition"
|
||||
- "DAG 分解"
|
||||
# `stage` is intentionally omitted: analyze-dag is the DAG route's entry skill,
|
||||
# replacing roadmap+requirements+design+plan — it has no single stage identifier
|
||||
# in the stage registry (see `<instance-root>/workflow-routing.yaml` dag.route and
|
||||
# core/schemas/workflow-routing.schema.json stage enum). Phase-boundary
|
||||
# signalling for the DAG route is the review-dag single gate and verify
|
||||
# milestone, not a StageDone event.
|
||||
role: Producer
|
||||
---
|
||||
|
||||
# Analyze DAG — Task-DAG Decomposition (DAG route entry skill)
|
||||
|
||||
## Purpose
|
||||
|
||||
analyze-dag is the **entry skill** of the DAG ticket pipeline
|
||||
(`dag.route.entry.skill`, spec-07 §2). For a DAG-routed Epic
|
||||
(`Kind/Epic`, or `Kind/Feature` — both resolve here via the routes table
|
||||
directly, [org-internal #3072] phase 3 / [org-internal #3061] Phase 2 / TD-388 —
|
||||
including BACKFILL mode for MVP graduates and big-bug relabels), it
|
||||
**replaces** `roadmap` + `requirements-elicitation` + `design` +
|
||||
`plan-iterations` and produces **ONE artifact**: the task DAG, published as
|
||||
wiki page `{epic-slug}/dag` (Tier 2 — human-visible, cross-worktree stable).
|
||||
|
||||
The task DAG merges three views into a single graph:
|
||||
|
||||
| View | Where it lives in the DAG |
|
||||
| ---- | ------------------------- |
|
||||
| requirements | node `acceptance_criteria` (each node is its own spec) |
|
||||
| design | edge `contract_ref` (inter-node interface contract) |
|
||||
| plan | topology (edge direction = dependency / execution order) |
|
||||
|
||||
`{epic-slug}` = the slug of the routed Epic — **not** this design's own slug
|
||||
`dag-pipeline`.
|
||||
|
||||
## Agent Role
|
||||
|
||||
The **Analyst** (Producer) owns and produces the task DAG. The Analyst
|
||||
decomposes the Epic into nodes (acceptance criteria), edges (contracts), and
|
||||
a topological order (the plan), then publishes the single graph.
|
||||
|
||||
## Route Disqualifiers — 适用性预检(分解前必查)
|
||||
|
||||
分解前,Analyst 必须对照下表预检。任一命中 → **不分解**,将具体未决问题
|
||||
列回 Epic 并 bounce(在不稳定需求上分解会产出一个即刻腐烂的冻结图;
|
||||
"先出个小 DAG" 不是降级选项——不进管线,先稳定需求):
|
||||
|
||||
| Disqualifier | 判定 | Bounce 动作 |
|
||||
| --- | --- | --- |
|
||||
| 需求未稳 | scope item 含糊、互相矛盾、或只能以 "视情况" 回答;关键干系人问题仍开放 | 在 Epic 上列出澄清问题清单;问题解决后重入 analyze-dag |
|
||||
| 设计主导 | 主要风险是一个有 ≥2 个可行选项的架构/平台决策,而非任务分解;需要先做一次探索 | 将开放问题转 `prototype`(THROWAWAY spike)或设计探索;决策记录后重入 |
|
||||
| 外部契约未决 | scope 依赖未决的外部契约(第三方 schema、上游 API 未定) | 按 External-System Rule(见下文)解析或围栏;不可拆的残余隔离后再重入 |
|
||||
|
||||
**反向拦截(Kind/MVP 降级建议,[org-internal #3061])**:一个 `Kind/Feature` 工单在分解预检中
|
||||
暴露"概念未稳"(scope 仍在探索、成功标准未定、决策日志会推翻节点划分)时,
|
||||
正确出口不是硬分解,而是建议改标签 `Kind/MVP` 先行交互式验证——概念稳定后
|
||||
经 backfill 模式(见下文)带着决策日志重入。
|
||||
|
||||
### Phase 0 需求澄清(disqualifier 的结构化扩展,[org-internal #3061])
|
||||
|
||||
预检不止于"能否分解",还须证明"需求已澄清到可分解"。两项强制检查:
|
||||
|
||||
**1. Scope item 可证伪性预检**:逐条检查 Epic 的 scope item。每条必须能改写为
|
||||
一个可观察的通过条件(给定输入/操作 → 可观测的结果);写不出来的条目就是
|
||||
澄清问题,列入 bounce 清单。禁止把不可证伪条目直接塞进 node AC。
|
||||
|
||||
**2. 横向完备性三态清单**:对下表每个维度做三态判定——**已覆盖**(scope 有
|
||||
对应条目)/ **不适用**(一句话理由)/ **待澄清**(列具体问题)。任一维度停在
|
||||
"待澄清" → bounce(澄清问题清单随 bounce 一起给出,不让用户猜缺什么):
|
||||
|
||||
| 维度 | 检查 |
|
||||
| --- | --- |
|
||||
| 外部系统 | 沿用 External-System Rule(见下文):每个外部交互有明确协议与失败语义 |
|
||||
| 错误路径 | 关键流程的失败分支有定义(不是只写 happy path) |
|
||||
| 数据生命周期 | 数据的创建/保留/删除策略已明确 |
|
||||
| 权限/安全 | 访问边界与敏感数据处理已明确(或显式声明"无安全面") |
|
||||
| NFR | 性能/容量/兼容性约束已枚举(见下) |
|
||||
| 边界条件 | 值域/并发/时序等边界有约定(或声明"无特殊边界") |
|
||||
|
||||
**NFR 输入来源明确化**:NFR 不允许"分解时脑补"。Epic 必须自带 NFR 输入——
|
||||
scope item 中带 `NFR:` 前缀的条目,或一行显式声明"无 NFR 约束"。两者皆缺 →
|
||||
作为澄清问题 bounce,而不是默认无约束。node schema 的 `NFR:` 条目只能来自
|
||||
该枚举,不得新增臆造。
|
||||
|
||||
## Node Schema (spec-03 §1)
|
||||
|
||||
```yaml
|
||||
node:
|
||||
id: "N-01" # task node N-{nn}; milestone node M-{nn}
|
||||
title: "契约骨架" # one line
|
||||
type: task # task | milestone
|
||||
acceptance_criteria: # "requirements" view — one falsifiable AC per line
|
||||
- "AC-1: <可证伪的验收条件, 映射 test_id>"
|
||||
- "NFR: <产品 NFR 条目>" # NFR:-prefixed entry — enumerated storage, no REQ-F number
|
||||
req_refs: # functional-requirement references
|
||||
- "REQ-F-001"
|
||||
status: pending # pending | ready | in_progress | done | blocked | green(仅里程碑)
|
||||
owner_session: null # assigned session id, null when unassigned
|
||||
size_attrs: # decomposition-time measured attributes (spec-06)
|
||||
cross_session_in: 1
|
||||
cross_session_out: 1
|
||||
contract_change: additive # none | additive | breaking (out-edge aggregation)
|
||||
estimated_hours: 8 # task nodes only; milestone nodes omitted
|
||||
estimated_sessions: 1 # task nodes only; milestone nodes omitted
|
||||
```
|
||||
|
||||
Field semantics:
|
||||
|
||||
- `acceptance_criteria` — the node **is** its spec. Each AC is falsifiable and
|
||||
maps a `test_id` ∈ `{ file-path::test-name | MANUAL | BENCH:<script> }`
|
||||
(the plan-iterations §6 SDD→TDD handshake; no other values). Entries MAY be
|
||||
`NFR:`-prefixed (product NFRs — written into node AC as enumerated storage;
|
||||
they carry no `REQ-F` number and are not functional ACs).
|
||||
- `req_refs` — functional-requirement ids this node covers, derived from the
|
||||
requirement registry (see below). Drives the REQMAP "every requirement ≥1
|
||||
node / every node ≥1 requirement" mechanical check.
|
||||
- `status` — drives rolling release (spec-04 RELEASE). `ready` = all
|
||||
cross-session in-edge source nodes reached their terminal state (task `done`,
|
||||
milestone `green`; same-session edges do not block). `green` is **milestone
|
||||
only** (a task node's terminal state is `done`).
|
||||
- `size_attrs` — decomposition-time measured inputs; the DAG-level `dag_metrics`
|
||||
aggregates them. `estimated_hours` / `estimated_sessions` are **required only
|
||||
for `type: task` nodes** (milestone nodes have no implementation work and
|
||||
omit them). The two fields are two units of the same estimate; the
|
||||
consistency invariant is `|estimated_hours − 8 × estimated_sessions| ≤ 2`,
|
||||
and **granularity is always judged by `estimated_sessions`** (1 session ≈ 8h).
|
||||
|
||||
## Edge Schema (spec-03 §2)
|
||||
|
||||
```yaml
|
||||
edge:
|
||||
from: "N-01" # source node id
|
||||
to: "N-02" # target node id
|
||||
contract_ref: "shared/types-v1" # contract pointer (wiki {epic-slug}/shared/ or schema $id)
|
||||
cross_session: true # from/to owned by different sessions?
|
||||
contract_state: draft # draft | frozen (only meaningful for task-to-task cross-session edges)
|
||||
change_type: additive # none | additive | breaking (only cross-session edges)
|
||||
```
|
||||
|
||||
Field semantics:
|
||||
|
||||
- `contract_ref` — the "design" view: where the contract lives. Cross-session
|
||||
task edges point at wiki `{epic-slug}/shared/` or a schema `$id`; same-session
|
||||
contracts may be inlined.
|
||||
- `cross_session` — contract freezing applies **only** to `true` edges (spec-02 §1 D-05).
|
||||
Same-session edges are `cross_session: false` (zero freeze cost).
|
||||
- `contract_state` — `draft` (initial, freely revisable) → `frozen` (after the
|
||||
single-gate PASS). Only meaningful for task-to-task cross-session edges.
|
||||
- `change_type` — the contract change surface, feeding `contract_change_surface`.
|
||||
|
||||
**Milestone edge exception (frozen, spec-03 §2 / spec-02 §2.1):**
|
||||
a milestone is a session-less integration point, so every edge connected to a
|
||||
milestone (in + out) is `cross_session: true` but carries **NO `contract_ref`,
|
||||
NO `change_type`**, and `contract_state` is **not applicable** (never frozen).
|
||||
Milestone edges do not participate in `contract_change_surface` aggregation
|
||||
(treated as `none`).
|
||||
|
||||
## Topology Constraints (spec-03 §3 — must satisfy, or TOPO fails)
|
||||
|
||||
1. **Acyclicity** — the directed graph must have no cycle. A cycle = BLOCKER.
|
||||
2. **Milestone weld rule** — any node with `cross_session_in ≥ 2` (≥2
|
||||
cross-session in-edges converging on it) MUST have a milestone `M` welded at
|
||||
the convergence point, such that:
|
||||
- (a) all cross-session in-edges terminate at `M` (M **is** the convergence
|
||||
point — fan-in edges end at M, no separate original convergence task node);
|
||||
- (b) M has no implementation work (`type: milestone`, no
|
||||
`acceptance_criteria`, only a DoD);
|
||||
- (c) M's DoD = integration verification of the converged scope (spec-05);
|
||||
- (d) M is the **必经 (must-pass) node** for all downstream consumers —
|
||||
downstream nodes may not become `ready` before M is `green`.
|
||||
A convergence point with no downstream consumer is a sink milestone (in-edges
|
||||
only).
|
||||
3. **Granularity floor** — every `type: task` node's implementation estimate is
|
||||
carried by `size_attrs.estimated_hours` / `estimated_sessions` (milestones
|
||||
omit both). Judged by `estimated_sessions` (1 session ≈ 8h). The SINGLE
|
||||
SOURCE of the judging criteria — floor/ceiling bounds, the severity
|
||||
mapping (field-inconsistency, below-floor, above-ceiling, giant node), and
|
||||
the folded checklist rows — is `core/checklists/dag-single-gate.md`
|
||||
§TOPO: recalibrate there, not here. (One-line summary: sessions far below
|
||||
the floor → merge candidates; far above → split/giant; hours-sessions
|
||||
mismatch → field inconsistency.)
|
||||
|
||||
## Requirement Registry (需求登记表, spec-02 §2.9)
|
||||
|
||||
Maintained at the DAG root. Each row:
|
||||
|
||||
```yaml
|
||||
- id: "REQ-F-001" # derived from the Epic scope-item number
|
||||
title: "<需求标题>"
|
||||
source: "<来源 scope item>"
|
||||
refs_by: ["N-01", "N-02"] # node ids that reference this requirement
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- **Only functional requirements** (Epic scope items) get `REQ-F-{NNN}` numbers
|
||||
and rows in the registry.
|
||||
- **Product NFRs do NOT get `REQ-F` numbers** and are NOT registered as rows —
|
||||
they are written into node `acceptance_criteria` as `NFR:`-prefixed entries
|
||||
(enumerated storage), so "every NFR ≥1 node" is mechanically checkable via
|
||||
node-AC enumeration.
|
||||
- `refs_by` is derived from the REQMAP "every requirement ≥1 node" criterion
|
||||
(missing coverage = a requirement with no node).
|
||||
|
||||
## Breadth Coverage — 类目覆盖矩阵 + 排除账本 ([org-internal #2905] 方案 1)
|
||||
|
||||
The four-stage fold that produced the single DAG artifact kept requirement
|
||||
**depth** (REQMAP three-path coverage + falsifiable AC) but dropped legacy
|
||||
elicitation's **breadth** scanning ("还有什么别的需求?"-style category
|
||||
sweep). The registry above only registers what the proposer wrote — a
|
||||
requirement nobody wrote does not exist in the system, and REQMAP's
|
||||
bidirectional mapping checks *in-table* consistency, never `表 ↔ 现实`
|
||||
completeness. The coverage matrix is the mechanical, enumerable part of the
|
||||
compensation (three layers: artifact = matrix, graph = probe nodes, data =
|
||||
`dag.route.preflight` learning loop; this section is the artifact layer).
|
||||
|
||||
analyze-dag MUST emit both sections in `{epic-slug}/dag` (template
|
||||
`core/templates/dag.md` §2.1/§2.2):
|
||||
|
||||
1. **类目覆盖矩阵 (coverage matrix)** — the fixed category set (≤10
|
||||
categories, extracted from legacy
|
||||
`<instance-root>/archive/checklists/requirements-analysis.md`
|
||||
CMP/SAF dimensions) × state ∈ {已覆盖, 明确排除, 待确认}. The category
|
||||
list is FIXED in the template — do not invent or drop categories per
|
||||
Epic; state is per-Epic.
|
||||
- **已覆盖** — the category has registered demand (registry `REQ-F-{NNN}`
|
||||
row or a node `NFR:` entry); evidence cell cites the id(s).
|
||||
- **明确排除** — out of scope, with a ledger row (below). An exclusion
|
||||
without a ledger row is NOT valid.
|
||||
- **待确认** — intermediate state; MUST carry (向{确认人}确认{什么}) so it
|
||||
is distinguishable from 漏了. 待确认 rows SHOULD be driven to a terminal
|
||||
state (已覆盖/明确排除) before the draft is submitted to review-dag.
|
||||
2. **排除账本 (exclusion ledger)** — one `E-n` row per exclusion:
|
||||
条目 — out of scope, 理由, 确认人. Exclusions are reversible (delete the
|
||||
row + flip the matrix state in the same revision).
|
||||
|
||||
**明确排除 ≠ 漏了 (distinguishability)**: excluded = matrix row 明确排除 +
|
||||
ledger `E-n` row with reason and confirmer; 漏了 = blank state, or an "N/A"
|
||||
without a ledger row. The single gate does NOT gain criteria for this —
|
||||
REQMAP's three tables are frozen (spec-04 §1) and `表 ↔ 现实`
|
||||
completeness is a **production-side obligation** of analyze-dag; reviewers
|
||||
may still raise findings on the matrix as ordinary artifact content.
|
||||
|
||||
**Relation to the Product-Epic External-System Rule**: that rule is the
|
||||
external-integration category applied to product Epics — the matrix
|
||||
generalizes its shape from 1 category to N. For a product-type Epic the
|
||||
外部系统集成 category remains mandatory-covered (task node or cross-session
|
||||
contract edge, see the rule below). Process-infrastructure Epics will
|
||||
typically 明确排除 most product-facing categories — that is legitimate;
|
||||
the ledger records why.
|
||||
|
||||
**Page-size budget**: the matrix + ledger participate in the ≤800-line
|
||||
budget (spec-02 §2.6). If the page exceeds, sink category DETAIL to
|
||||
sub-page `{epic-slug}/dag-coverage` using the same mechanics as AC sinking
|
||||
(in-page pointer, return link above the status line, version stamp rides the
|
||||
frozen copy) — purely organizational, NOT an oversize signal, no
|
||||
re-derivation.
|
||||
|
||||
### Breadth probe nodes (P2, [org-internal #2905] 方案 2)
|
||||
|
||||
When a 待确认 category is cheapest to resolve empirically, a **probe node**
|
||||
is a legal task node at the granularity floor:
|
||||
|
||||
```yaml
|
||||
- id: "N-07"
|
||||
title: "探查:{类目} 存量/可行性核对"
|
||||
type: task
|
||||
acceptance_criteria:
|
||||
- "AC-1: 产出 {类目} 结论(进/不进登记表)— MANUAL"
|
||||
req_refs: ["REQ-F-00n"] # 指向被其澄清的需求;探查本身不派生新 REQ-F
|
||||
status: pending
|
||||
size_attrs:
|
||||
cross_session_in: 0
|
||||
cross_session_out: 0
|
||||
contract_change: none
|
||||
estimated_hours: 2
|
||||
estimated_sessions: 0.25 # 粒度下限(Topology Constraints 3)——合法,不得更低
|
||||
```
|
||||
|
||||
- `MANUAL` is a legal `test_id` value — a probe's deliverable is a human
|
||||
verdict (matrix flip), not an automated test.
|
||||
- A probe node anchors to an **existing** requirement it clarifies
|
||||
(`req_refs` non-empty — REQMAP 节点溯源 requires it). A 待确认 category
|
||||
with NO anchor requirement is resolved by confirmation (向确认人问询),
|
||||
not by a probe node.
|
||||
- The probe's conclusion flips the category: → 已覆盖 (new registry row +
|
||||
covering nodes) or → 明确排除 (new ledger row). A conclusion landing
|
||||
BEFORE freeze is ordinary draft revision (no signal).
|
||||
- **登记表单独加行 ≡ node_split**(显式化):post-freeze 新增登记表行(新需求)
|
||||
按 `node_split` oversize 信号处理——触发重派生 + 无条件重跑 review-dag、修订
|
||||
冻结副本(新增需求必然改变节点集合或 `req_refs`;需要新节点时全额按
|
||||
node_split 建单传播)。
|
||||
|
||||
### Data layer — read `dag.route.preflight` BEFORE decomposition ([org-internal #2905] 方案 3)
|
||||
|
||||
Before starting decomposition, analyze-dag MUST read
|
||||
`<instance-root>/workflow-routing.yaml` `dag.route.preflight` and, when the list is
|
||||
non-empty, verify each item as a producer self-check (the same injection
|
||||
contract `implement` applies for `routes.{Kind}.preflight`, [org-internal #2599] — landed
|
||||
items carry their retro evidence in `evidence`):
|
||||
|
||||
1. Read the `dag:` block's `route.preflight` array (live yaml, repo root).
|
||||
2. For each item: run its `item` self-check against the Epic's scope /
|
||||
breadth matrix before writing the DAG (items are typically recurring
|
||||
late-discovery categories, e.g. the retro-derived
|
||||
`REQ × late-discovery` class — requirements that surfaced only after
|
||||
previous DAGs froze).
|
||||
3. An item that fires becomes a 待确认 matrix row (or a probe node, above)
|
||||
— never silently dropped.
|
||||
4. Absent/empty array → no injection, proceed (current state: the list is
|
||||
empty until a retro cycle lands evidence).
|
||||
|
||||
## dag_metrics (spec-03 §4 / spec-06)
|
||||
|
||||
Stored at the DAG root / Epic aggregator. Keys are frozen (spec-02 §2.6):
|
||||
|
||||
```yaml
|
||||
dag_metrics:
|
||||
node_count: 6 # task + milestone nodes total
|
||||
cross_session_edge_count: 7 # total cross_session:true edges (milestone edges counted too)
|
||||
contract_change_surface: additive # none | additive | breaking
|
||||
review_depth: D4 # derived tier D1–D4
|
||||
```
|
||||
|
||||
- `contract_change_surface` — **worst-value aggregation** of `change_type` across
|
||||
cross-session **task edges only** (`breaking > additive > none`). Milestone
|
||||
edges are excluded (treated as `none` — no contract, no `change_type`).
|
||||
- `review_depth` — derived (decomposition **is** the sizing, 分解即定档):
|
||||
|
||||
```
|
||||
review_depth = max(
|
||||
depth_by(node_count),
|
||||
depth_by(cross_session_edge_count),
|
||||
depth_by(contract_change_surface)
|
||||
)
|
||||
```
|
||||
|
||||
Thresholds (spec-06 §2 — authoritative):
|
||||
|
||||
| Measured attribute | D1 | D2 | D3 | D4 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `node_count` | ≤3 | ≤8 | ≤15 | >15 |
|
||||
| `cross_session_edge_count` | 0 | — | 1–3 | ≥4 |
|
||||
| `contract_change_surface` | none | — | additive | breaking |
|
||||
|
||||
- `cross_session_edge_count`: 0 → D1; 1–3 → D3; ≥4 → D4. **D2 is not derivable
|
||||
from this dimension.**
|
||||
- `contract_change_surface`: none → D1; additive → D3; breaking → D4 (D2 not
|
||||
derivable from this dimension).
|
||||
- `review_depth` is the review-dag review-resource parameter (reviewers / round
|
||||
cap, spec-06 §3) — analyze-dag computes it; it does not assign `Size/*`
|
||||
labels (DAG-routed tickets ignore `Size/*`).
|
||||
|
||||
## Page-Size Budget (spec-02 §2.6)
|
||||
|
||||
> Threshold single source: the page-size budget and the `> 页尺寸自检: 超限`
|
||||
> signal are judged by `core/checklists/dag-single-gate.md` (TOPO 页尺寸自检信号
|
||||
> row) — recalibrate there, not here. (One-line summary: the `{epic-slug}/dag`
|
||||
> body has a line budget (~800 lines at calibration time); on breach, sink
|
||||
> node detail to sub-pages and emit the self-check signal per the checklist
|
||||
> row.)
|
||||
|
||||
The `{epic-slug}/dag` page body — node table + edge table + requirement registry
|
||||
+ breadth coverage matrix (§2.1) + exclusion ledger (§2.2) + `dag_metrics` —
|
||||
must fit the checklist's page-size budget (see the single-source note above;
|
||||
~800 lines at calibration time). On breach, analyze-dag MUST:
|
||||
|
||||
1. Sink node AC detail into sub-pages `{epic-slug}/dag-nodes/{node-id}`
|
||||
(sub-page naming `{epic-slug}/dag-nodes/{node-id}` is **frozen**);
|
||||
2. Keep an in-page pointer `{node-id} → {epic-slug}/dag-nodes/{node-id}` for
|
||||
each sunk node;
|
||||
3. Put a return-link line on each sub-page, **above** its status line:
|
||||
`> 返回 [{epic-slug}/dag](…)`;
|
||||
4. Write a page-size self-check signal into the DAG page header, **parallel to**
|
||||
the `> DAG 工件状态` line:
|
||||
`> 页尺寸自检: 超限({node-id} 已下沉子页)`.
|
||||
|
||||
Category detail (matrix/ledger 细目) may likewise sink to
|
||||
`{epic-slug}/dag-coverage` (same mechanics — see Breadth Coverage). This is
|
||||
**NOT** an `oversize_signal` and does **NOT** trigger re-derivation —
|
||||
page overflow does not change the three derivation inputs
|
||||
(`node_count` / `cross_session_edge_count` / `contract_change_surface`); sub-page
|
||||
sinking is purely organizational. (Re-derivation is triggered only by the four
|
||||
spec-06 §5 oversize signals.)
|
||||
|
||||
## Product-Epic External-System Rule (spec-04 §2 ARCH 1.1 alternative)
|
||||
|
||||
For a **product-type** Epic, external systems and integration protocols MUST
|
||||
explicitly appear in the DAG — **either** as task nodes (with acceptance
|
||||
criteria) **or** as cross-session contract edges (`contract_ref` pointing at an
|
||||
external contract). This is what lets the REQMAP "需求覆盖" (requirement
|
||||
coverage) check mechanically verify that external-integration requirements map
|
||||
to nodes. (The `ARCH 1.1` system-context diagram is discarded only for
|
||||
process-infrastructure-type Epics; product Epics get this alternative rule
|
||||
instead.) [org-internal #2905]: this rule is the 外部系统集成 category of the breadth
|
||||
coverage matrix held at its original strength — see Breadth Coverage above.
|
||||
|
||||
## Exec-Resource Configuration (spec-07 §4 — mechanical destination of folded checklist rows)
|
||||
|
||||
This clause is the **mechanically checkable destination** of the folded plan
|
||||
checklist rows `RISK 3.7–3.10` / `EST 6.3–6.12` / `EXE 8.7–8.11` (single-gate
|
||||
does not re-audit these — they live here, in the skill). A analyze-dag run MUST
|
||||
observe:
|
||||
|
||||
| Resource | Configuration |
|
||||
| --- | --- |
|
||||
| Model tier assignment | reasoning-tier for decomposition; fast-tier for mechanical transcription (node/edge/registry/dag_metrics table assembly). reviewer-tier — i.e. the `Reviewer` Explorer role (`_shared/roles/reviewer.yaml`; the vocabulary the review skills use for reviewer dispatch is agent type Explorer) — is used **only** via the review-dag gate (see `review-artifact` target review-dag "Reviewer Constants": Model tier) — **never** inside analyze-dag. |
|
||||
| Token budget | a single analyze-dag agent call's estimated token consumption must stay within the model's context-window capacity, and the `{epic-slug}/dag` page body ≤ 800 lines (page-size budget) bounds the artifact's token footprint. |
|
||||
| Context-window ceiling | keep single-call consumption ≤ 70% of the model's context window. |
|
||||
| RAG supplement | if the Epic's scope touches knowledge newer than the model's training cutoff, configure RAG supplementation before decomposition. |
|
||||
| API-unavailable fallback | define a fallback path (retry / degrade to a lower tier / pause and report) when the model API is unavailable. |
|
||||
| Deterministic tasks | deterministic tasks (mechanical transcription) run at `temperature ≤ 0.1`. |
|
||||
| AI-artifact origin annotation | the DAG artifact is annotated with its origin — model + temperature + timestamp. |
|
||||
|
||||
## Output & Lifecycle
|
||||
|
||||
1. analyze-dag writes wiki page `{epic-slug}/dag` with first line:
|
||||
`> DAG 工件状态: draft`.
|
||||
2. After the single-gate (`review-dag`) PASS, the **aggregator agent** (main
|
||||
session / Orchestrator — not analyze-dag) asks the freeze confirmation
|
||||
question ([org-internal #3379]), then on confirmation flips the page to
|
||||
`> DAG 工件状态: frozen` and stamps version `v1`. The confirmation is an
|
||||
OPTIONIZED `question` call (stage `review-dag`, never `destructive` —
|
||||
freeze is a process gate, not a business decision): options
|
||||
`Freeze as v1 (Recommended)` / `Hold — back to review`, plus a one-line
|
||||
summary of the gate verdict (rounds, worst severity, open waivers) in the
|
||||
question text. Choosing hold = do NOT flip — leave the page `draft`,
|
||||
record the hold and the reason as a comment on the source Epic, and route
|
||||
back to `review-artifact`. An unconfirmed flip is a spec violation: the
|
||||
flip MUST be preceded by the question call in the same session, and the
|
||||
confirmation leaves a trace — post a comment on the source Epic
|
||||
(`DAG frozen: {epic-slug}/dag v1 — confirmed via question, {date}`) before
|
||||
flipping. In autonomous runs the question auto-approves via its stage
|
||||
(choosing the recommended freeze) — the gate guarantees the decision
|
||||
SURFACES with a defined shape at a defined moment, not that a human
|
||||
always attends it.
|
||||
3. On each post-freeze re-review PASS (an oversize-signal re-run, spec-06
|
||||
§5), the aggregator agent updates the frozen copy in place and increments
|
||||
the version stamp `vN → vN+1` (no re-confirmation — only the initial
|
||||
freeze gates on a decision).
|
||||
|
||||
## Backfill Input Mode (Kind/MVP graduation & big-bug relabels, [org-internal #3061])
|
||||
|
||||
When a `Kind/MVP` ticket graduates (relabel → `Kind/Feature` → Step 0 → this
|
||||
skill), the DAG is derived from **what was actually built**, not from greenfield
|
||||
decomposition:
|
||||
|
||||
- **Inputs**: the ticket body's `## 决策日志` (one line per decision: what /
|
||||
why / expiry) + `## Debt Register` section + codegraph reverse call graph
|
||||
of the implemented surface.
|
||||
- **Nodes** follow the code's actual module/contract structure; each node's AC
|
||||
traces to a decision-log line or an existing test. The decision log is the
|
||||
REQMAP baseline for the single gate — decisions still marked deferred/expires
|
||||
become explicit open questions on the Epic, not silent assumptions.
|
||||
- **Debt register items** map to node ACs when they describe unfinished
|
||||
behavior; remaining shortcuts stay debt (prototype/EVOLUTIONARY path →
|
||||
verify Phase 5.5 registration).
|
||||
- **Big-bug relabel inputs** (a `Kind/Bug` relabeled `Kind/Feature` per the
|
||||
big-bug rule, `rules/workflow-routing.md` §"Runtime growth signals
|
||||
(post-sizing)" — big-bug relabel rule): the repro
|
||||
+ root-cause notes (wiki page `{slug}/repro-notes`) carry over as node
|
||||
input — node ACs trace to the reproduction steps and root-cause findings,
|
||||
and the root-cause analysis (not a decision log) serves as the REQMAP
|
||||
baseline for the single gate.
|
||||
- Disqualifiers STILL apply in backfill mode: an MVP whose decision log shows
|
||||
unresolved conflicting deferred decisions bounces (stabilize first) instead
|
||||
of freezing a contradictory graph.
|
||||
|
||||
## What analyze-dag does NOT do
|
||||
|
||||
- **No synthesis.** analyze-dag does not produce a synthesis — the review-artifact skill
|
||||
Phase E produces the single-gate synthesis after review-dag converges.
|
||||
- **No `## 工件索引` issue comment.** analyze-dag is a sub-agent; it does NOT
|
||||
create the Epic's `## 工件索引` comment. The aggregator agent / main session
|
||||
creates and maintains that comment (issue-cross-linking Pattern 10), and the
|
||||
review / verify stages report their rows to it — analyze-dag only publishes the
|
||||
`{epic-slug}/dag` artifact.
|
||||
|
||||
## References
|
||||
|
||||
- `core/templates/dag.md` — the DAG artifact structure contract (this skill writes `{epic-slug}/dag` against it; node/edge/milestone/dag_metrics shape + §6 topology constraints)
|
||||
- `dag-pipeline/spec-02` — frozen contracts (§2.1 node/edge, §2.6 dag_metrics + page-size budget, §2.9 requirement registry + test_id)
|
||||
- `dag-pipeline/spec-03` — node/edge schema + topology constraints
|
||||
- `dag-pipeline/spec-04` — single-gate TOPO/REQMAP/RELEASE dimensions (folded-row destinations)
|
||||
- `dag-pipeline/spec-06` — dag_metrics derivation + thresholds
|
||||
- `dag-pipeline/spec-07` — dag.route landing row (entry skill, skip set, single-gate) + §4 landing list
|
||||
- `dag-pipeline/spec-08` — DAG artifact lifecycle / version stamp
|
||||
- `<instance-root>/archive/skills/roadmap/SKILL.md` — legacy route (archived 2026-08-21, [org-internal #3072] phase 3; this skill replaces it for all Epics)
|
||||
@@ -0,0 +1,297 @@
|
||||
---
|
||||
name: browser-debug
|
||||
description: >
|
||||
Use ONLY when an agent needs to interact with a live browser to debug, verify,
|
||||
or iterate on Web UI work in packages/app — navigate to the dev server, snapshot
|
||||
the DOM/accessibility tree, capture console and network errors, screenshot
|
||||
component states, and exercise user flows end-to-end. Triggers on UI bugs,
|
||||
visual regressions, layout/positioning issues, keyboard-focus problems,
|
||||
console-error reports, and any "open the page and check X" task. Loads the
|
||||
Playwright MCP toolset (browser_navigate, browser_snapshot,
|
||||
browser_take_screenshot, browser_console_messages, browser_evaluate, etc.).
|
||||
Use ONLY for interactive browser work; for writing Playwright *test files*
|
||||
use the existing e2e/ suites under packages/app, not this skill.
|
||||
triggers:
|
||||
- browser debug
|
||||
- open the page
|
||||
- check the page
|
||||
- screenshot the
|
||||
- inspect element
|
||||
- console error
|
||||
- visual bug
|
||||
- visual regression
|
||||
- layout broken
|
||||
- alignment off
|
||||
- focus ring
|
||||
- tab order
|
||||
- 浏览器调试
|
||||
- 打开页面
|
||||
- 截图
|
||||
- 视觉
|
||||
- 布局错乱
|
||||
- 对齐
|
||||
- 焦点
|
||||
- 控制台报错
|
||||
role: Producer
|
||||
---
|
||||
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatimDir)。机制、结构与 frontmatter 保持;实例术语(工具名、路径、工单号)按 `core/adapters/TERMINOLOGY.md` 绑定到具体实例。
|
||||
|
||||
# Browser Debug
|
||||
|
||||
Interactive browser automation for Web UI work in `packages/app`. This skill
|
||||
teaches the *recipe* for driving the Playwright MCP toolset — when to reach
|
||||
for it, which tool for which job, and how to capture evidence a reviewer can
|
||||
trust.
|
||||
|
||||
## When this skill applies
|
||||
|
||||
Reach for `browser-debug` when a task requires **observing** the live
|
||||
application, not just reasoning about code:
|
||||
|
||||
- Reproducing a UI bug (focus lost, dropdown won't close, scroll jumps).
|
||||
- Verifying a visual change actually landed (computed style, layout box,
|
||||
dark-mode token).
|
||||
- Capturing console errors or failed network requests during a user flow.
|
||||
- Recording a screenshot / trace as evidence for a review or DoD entry.
|
||||
- Exercising a keyboard / screen-reader flow end-to-end.
|
||||
|
||||
Do **not** reach for it when:
|
||||
|
||||
- You are writing Playwright *test files* — those live in
|
||||
`packages/app/e2e/...` and run via `cd packages/app && bun run test:e2e`
|
||||
(variant suites: `test:e2e:perf`, `test:e2e:a11y`, `test:e2e:smoke`,
|
||||
`test:e2e:visual`). This skill is for
|
||||
ad-hoc, agent-driven sessions, not for adding tests.
|
||||
- The question can be answered by reading code or running a unit test.
|
||||
Browser time is expensive; spend it only when the live DOM is the oracle.
|
||||
|
||||
## Preconditions
|
||||
|
||||
1. **Playwright MCP is configured.** The project `octopus.jsonc` carries a
|
||||
`mcp.playwright` block launching `@playwright/mcp`. If the MCP tools are
|
||||
not visible, run `/kickoff` or follow the `codegraph-setup` skill's
|
||||
recipe (the closest analog for debugging missing MCP servers).
|
||||
2. **Dev server is running.** The app under test must be reachable. For
|
||||
`packages/app`, run `bash script/dev-fresh.sh --full` for the dev server
|
||||
(Windows workstations: `bash script/dev-fresh-windows.sh` — dev-fresh.sh's
|
||||
stop path depends on setsid/ss, silently ineffective under Git Bash; see
|
||||
local-workstation-quirks.md §5);
|
||||
frontend is at `http://localhost:4444` and backend at `http://localhost:4096`
|
||||
(see AGENTS.md Dev services section).
|
||||
3. **Chromium is installed.** First run of `@playwright/mcp` downloads it
|
||||
automatically; if the tool reports a missing browser, run
|
||||
`bunx playwright install chromium` once in any shell.
|
||||
|
||||
## Tool selection recipe
|
||||
|
||||
The Playwright MCP exposes many tools. Pick by *what you need to observe*,
|
||||
not by familiarity:
|
||||
|
||||
| Need | Tool | Why |
|
||||
| --- | --- | --- |
|
||||
| Open a URL | `browser_navigate` | Always the first call. |
|
||||
| See the page structure (roles, names) | `browser_snapshot` | Returns the accessibility tree — stable across CSS refactors. Prefer this over `browser_take_screenshot` when asserting "does element X exist / is it labelled Y?". |
|
||||
| See the page *visually* | `browser_take_screenshot` | For layout, spacing, color, overflow. Attach the PNG to your report. |
|
||||
| Read console errors / warnings | `browser_console_messages` | Filter by level (`error`, `warn`). The first stop for any "page is blank" bug. |
|
||||
| Read failed network calls | `browser_network_requests` | Locate 4xx/5xx, CORS failures, hanging fetches. |
|
||||
| Click, type, hover, select | `browser_click`, `browser_type`, `browser_hover`, `browser_select_option` | Drive the flow. Always `browser_snapshot` first to get the `ref` for the target element. |
|
||||
| Assert computed style / DOM state | `browser_evaluate` | Last resort. Snapshots cover 90%; reach for `evaluate` only when you need `getComputedStyle`, scroll positions, or arbitrary JS state. |
|
||||
| Keyboard navigation | `browser_press_key` | For tab order, focus rings, escape-to-close. |
|
||||
| Record a video / trace | `browser_start_tracing`, `browser_stop_tracing` | Heavy; use only when a bug is timing-sensitive and console+snapshot can't catch it. |
|
||||
|
||||
## Standard recipe — reproduce a UI bug
|
||||
|
||||
Follow these steps in order. Do not skip the snapshot step: the `ref` it
|
||||
returns is the stable handle every subsequent tool needs.
|
||||
|
||||
1. `browser_navigate` to the page where the bug lives.
|
||||
2. `browser_console_messages` — record any errors *before* touching anything.
|
||||
Many "interaction bugs" are actually load-time errors.
|
||||
3. `browser_snapshot` — locate the target element; capture its `ref` and
|
||||
accessible name.
|
||||
4. Drive the interaction with `browser_click` / `browser_type` /
|
||||
`browser_press_key` until the bug reproduces.
|
||||
5. Re-read `browser_console_messages` and `browser_network_requests` —
|
||||
capture the *delta* introduced by the interaction.
|
||||
6. `browser_take_screenshot` (or a second `browser_snapshot`) to freeze the
|
||||
broken state.
|
||||
7. Report: URL, the minimal interaction sequence, console delta, network
|
||||
delta, screenshot, and the suspected component path in
|
||||
`packages/app/src/...`.
|
||||
|
||||
## Standard recipe — verify a UI fix
|
||||
|
||||
1. `browser_navigate` to the affected page.
|
||||
2. `browser_snapshot` — confirm the fixed element is present with the
|
||||
expected accessible name.
|
||||
3. `browser_take_screenshot` — full page **and** element-cropped if the fix
|
||||
is local.
|
||||
4. If the fix touched layout, `browser_evaluate` a `getBoundingClientRect()`
|
||||
or `getComputedStyle()` on the key node and paste the value into your
|
||||
report. Numbers beat adjectives.
|
||||
5. If the fix touched interaction, replay the original bug reproduction
|
||||
steps and assert the previous failure no longer fires.
|
||||
|
||||
## Standard recipe — capture evidence for verify / DoD
|
||||
|
||||
When a `verify` work item has an E2E or UI DoD entry:
|
||||
|
||||
1. Run the recipe above for each DoD line.
|
||||
2. Save each screenshot as `.playwright-mcp/dod-{item-id}-{state}.png` —
|
||||
the worktree's gitignored output dir. See "Screenshot output paths"
|
||||
below.
|
||||
3. In the verification report, cite the file names and tell the reviewer
|
||||
where to look: open the worktree as an octopus project → session side
|
||||
panel "Screenshots" tab (or the standalone screenshots page). Verify
|
||||
with `GET /screenshots?directory={worktree}` before citing. A screenshot
|
||||
the reviewer can open beats a paragraph of "I checked it".
|
||||
4. When the DoD row will cite the evidence as a `BROWSER:{pack-ref}`
|
||||
reference (UI-touching PRs, `verify` skill Phase 2.7 / contract
|
||||
`browser-evidence-4486/shared/evidence-ref-v1`), promote the captures
|
||||
into an evidence pack first — feed COPIES through
|
||||
`writeEvidencePack` (`<harness-package>/src/browser/evidence-pack.ts`,
|
||||
the single mandatory sanitize boundary; the `.playwright-mcp/` layer
|
||||
keeps its current semantics, additive not replaced) and cite the pack
|
||||
directory. Evidence ladder: raw capture → `.playwright-mcp/` working
|
||||
layer (gitignored) → sanitized Tier-1 pack → `BROWSER:` reference the
|
||||
verifier validates mechanically.
|
||||
|
||||
## Rationalizations to refuse
|
||||
|
||||
| Excuse | Reality |
|
||||
| --- | --- |
|
||||
| "The unit test already covers it." | Unit tests assert component logic, not what Chromium actually renders. Visual and focus bugs live in the gap. |
|
||||
| "I can tell from the JSX that it's aligned." | Computed layout depends on parent containers, fonts, and viewport. Screenshot the real page. |
|
||||
| "Browser automation is overkill for a small change." | A `browser_navigate` + `browser_take_screenshot` pair is ~2 seconds. The cost is in *not* looking. |
|
||||
| "I'll just run the whole e2e suite." | The suite answers "did anything break?" — it does not answer "does this specific change look right?". |
|
||||
| "Snapshot is enough, skip the screenshot." | Accessibility trees hide overflow, clipping, and z-index bugs. Take both for visual changes. |
|
||||
|
||||
## Boundaries
|
||||
|
||||
- **Read-only by default.** This skill never writes files under
|
||||
`packages/app/src/` — it observes. Edits flow through `implement`
|
||||
(bugfix/refactor/port mode).
|
||||
- **One page at a time.** Close tabs (`browser_close`) between scenarios so
|
||||
state doesn't leak.
|
||||
- **Evidence lives in the worktree's `.playwright-mcp/` dir.** Any screenshot
|
||||
or trace you reference in a PR comment, verification report, or DoD entry
|
||||
must be copied into the workflow worktree's `.playwright-mcp/` directory
|
||||
(gitignored) — never left in `/tmp/`, `playwright-report/`, or any
|
||||
machine-local path the reviewer cannot reach through octopus. Cite file
|
||||
names + the worktree project path; the reviewer opens the octopus
|
||||
screenshots UI (session panel tab or screenshots page) to view them.
|
||||
|
||||
## Session cleanup (mandatory)
|
||||
|
||||
A Playwright MCP browser is a real Chrome with a `/tmp` user-data-dir — its
|
||||
mode follows `mcp.playwright.headless` (`headless-field-v1`, [org-internal #4393]): the repo
|
||||
default `auto` spawns headed on desktop (win32/darwin; linux only with
|
||||
DISPLAY/WAYLAND_DISPLAY set) and headless otherwise; an explicit `true`/`false`
|
||||
or `OCTOPUS_MCP_HEADLESS=1|0` pins it. The mode is fixed per new session —
|
||||
switching the config does not hot-switch a running browser. It
|
||||
stays alive as long as its MCP server runs, and the MCP server stays alive as
|
||||
long as its parent octopus backend runs. When a browser-debug session ends
|
||||
without an explicit `browser_close`, OR the octopus session times out mid-task,
|
||||
the Chrome + its MCP server are orphaned and accumulate across sessions —
|
||||
burning CPU and leaving dead profile dirs (issue [org-internal #1315]); in headed mode the
|
||||
orphan is a visible stray window.
|
||||
|
||||
**Iron rule: every browser-debug session MUST close its browser before ending.**
|
||||
|
||||
1. **Always close when done.** The last action of any browser-debug task is
|
||||
`browser_close`. Treat it like a `return` — if you opened a browser, you
|
||||
close it. Do not rely on session teardown to do it for you; it doesn't.
|
||||
2. **Close on error too.** If the task aborts (bug not reproduced, tool error,
|
||||
user redirect), still call `browser_close` before moving on. An abandoned
|
||||
debugging session is the most common orphan source.
|
||||
3. **If a browser was already orphaned** (you spot a stale Chrome in `ps`, or
|
||||
a `/tmp/playwright_chromiumdev_profile-*` dir with no live session), do not
|
||||
leave it. `bash script/dev-fresh.sh --full` (Windows workstations:
|
||||
`dev-fresh-windows.sh`, same stop-path caveat as Preconditions §2) reaps
|
||||
orphan dev sessions and
|
||||
init-adopted browsers before restarting; run it to clean up. For a targeted
|
||||
clean without restarting the dev server, kill the Chrome by its profile:
|
||||
`pkill -f 'playwright_chromiumdev_profile'` (and any `agent-browser` harness
|
||||
reparented to init).
|
||||
|
||||
**Why this matters:** a single forgotten `browser_close` leaves a Chrome
|
||||
subtree (8+ processes) running indefinitely. Over days this is hundreds of
|
||||
orphaned processes and wasted CPU. The MCP server does not auto-close its
|
||||
browser on disconnect — only an explicit `browser_close` (or process reaping)
|
||||
releases it.
|
||||
|
||||
## Screenshot output paths
|
||||
|
||||
The Playwright MCP server is launched WITHOUT `--output-dir` (see
|
||||
`octopus.jsonc` → `mcp.playwright.command`): its default output location is
|
||||
`.playwright-mcp/` in the process cwd (the worktree root), gitignored and
|
||||
auto-evicted at 100 MB via `--output-max-size`. Auto-named output (no
|
||||
`filename`) lands there — but an explicit `filename` can resolve against the
|
||||
cwd and drop the PNG into the repo root, where the `/*.png` gitignore
|
||||
backstop hides it from `git status` and nothing ever cleans it up.
|
||||
|
||||
**Iron rule: 截图文件名必须写进 `.playwright-mcp/`。** Every `filename`
|
||||
passed to `browser_take_screenshot` (or any tool that writes a file) MUST
|
||||
resolve into `.playwright-mcp/` — write it as `.playwright-mcp/<name>.png`,
|
||||
or use an absolute path under `/tmp/octopus/`. NEVER a bare relative name
|
||||
like `2202-after-models.png`: it leaks a root-level PNG that gitignore masks
|
||||
but nobody deletes (the 2026-08 repo-root buildup — 40+ orphan PNGs — came
|
||||
from exactly this).
|
||||
|
||||
Two valid destinations for screenshots, by purpose:
|
||||
|
||||
| Purpose | Destination | Why |
|
||||
| --- | --- | --- |
|
||||
| **Ad-hoc / throwaway** (debug a layout, confirm a fix landed, never cited in any report) | `.playwright-mcp/<name>.png`, or omit `filename` to auto-save into the same dir | Survives across the session, auto-evicted at 100 MB, never pollutes the repo. |
|
||||
| **Evidence cited in a PR / report / DoD entry** | `.playwright-mcp/<name>.png` — the cwd is the worktree root, so no copy step is needed | Zero repo bloat (gitignored, never committed); viewable by the reviewer through octopus's own screenshots UI (session panel "Screenshots" tab / standalone screenshots page, served by `GET /screenshots?directory={worktree}`); lifecycle bounded to the worktree = the review window. |
|
||||
|
||||
**Evidence publishing ladder** (verified 2026-08-14 on this Gitea version):
|
||||
|
||||
1. **Copy into the workflow worktree's `.playwright-mcp/`** — primary path.
|
||||
Cite file names + worktree project path; reviewer views via the octopus
|
||||
screenshots UI. Verify reachability with
|
||||
`GET /screenshots?directory={worktree}` before citing. Never commit
|
||||
evidence binaries to git — screenshots accumulate per PR and git history
|
||||
is unreclaimable (measured ~80KB/PR today, unbounded growth). Do not put
|
||||
routine debug screenshots on the wiki either — base64-in-page bloats the
|
||||
wiki git repo equivalently; the wiki evidence namespace is reserved for
|
||||
the durable verify-stage evidence channel (rung 3).
|
||||
2. **Issue attachments API** (`POST /api/v1/repos/{o}/{r}/issues/{n}/assets`)
|
||||
— alternative when the reviewer cannot reach the worktree through the
|
||||
octopus UI. The endpoint exists but has returned 500 on this instance;
|
||||
verify before relying on it.
|
||||
3. **Publish to the wiki evidence namespace `{slug}/verify/evidence/{name}`
|
||||
via `wiki 读写 API(见 TERMINOLOGY)`** — last resort only, when no other channel
|
||||
works AND the evidence must outlive the worktree (this is the same durable
|
||||
channel `verify` mandates for DoD evidence entries — see
|
||||
`core/skills/verify/SKILL.md` References). Keep it small:
|
||||
≤5 images per PR, each ≤200KB (compress/downscale, prefer element
|
||||
crops over full-page).
|
||||
|
||||
**Iron rule: never cite a machine-local path (`/tmp/...`, `~`, project
|
||||
root) as evidence in a Tier-2 output** (PR comment, issue comment,
|
||||
verification report). The reviewer must be able to open the evidence
|
||||
through octopus (`.playwright-mcp/` + screenshots UI) or a repo/wiki URL
|
||||
(worktree files, or the `{slug}/verify/evidence/{name}` wiki pages). A
|
||||
citation they cannot reach is a fabricated citation.
|
||||
|
||||
Never write screenshots to the project root. The `.gitignore` rejects
|
||||
`/*.png`, `/.playwright-mcp/`, and `/playwright-report/` only as a backstop —
|
||||
an ignored file is still an orphan on disk. The `.playwright-mcp/` filename
|
||||
prefix is the real fix; rely on that, not on the backstop.
|
||||
|
||||
## References
|
||||
|
||||
- `core/skills/frontend/SKILL.md` — where UI changes are planned; invoke
|
||||
this skill during Phase 3 / Phase 4 to verify each state visually.
|
||||
- `core/skills/implement/SKILL.md` — Frontend Mode; pair with
|
||||
`browser-debug` when the work item touches rendering.
|
||||
- `core/skills/implement/SKILL.md` (Mode: bugfix) — Phase 1 reproduction; use this skill
|
||||
to capture the failing state before isolating root cause.
|
||||
- `core/skills/verify/SKILL.md` — Phase 2 / DoD matrix; use this skill
|
||||
to generate screenshot evidence for UI-tagged DoD entries.
|
||||
- `packages/app/e2e/` — permanent Playwright test suites; this skill is the
|
||||
ad-hoc complement, not a replacement.
|
||||
- `rules/dev-server` on the wiki — how to run the dev server for TUI work;
|
||||
consult `packages/app/README.md` for the web equivalent.
|
||||
@@ -0,0 +1,213 @@
|
||||
---
|
||||
name: codegraph-setup
|
||||
description: >
|
||||
Use ONLY when installing, configuring, initializing, or troubleshooting CodeGraph
|
||||
(@colbymchenry/codegraph — https://github.com/colbymchenry/codegraph) in the
|
||||
octopus environment. Covers global CLI install, per-project `.codegraph/`
|
||||
indexing, and maintenance commands (status, sync, upgrade, uninstall). MCP
|
||||
wiring into octopus.jsonc is handled by the kickoff pipeline
|
||||
(`octopus kickoff` or `/kickoff`). Also use when a project session reports a
|
||||
missing/stale `.codegraph/` index.
|
||||
triggers:
|
||||
- codegraph
|
||||
- code graph
|
||||
- codegraph setup
|
||||
- codegraph install
|
||||
- codegraph init
|
||||
- .codegraph
|
||||
- codegraph_explore
|
||||
- codegraph_node
|
||||
- codegraph_callers
|
||||
- codegraph_status
|
||||
- install codegraph
|
||||
# Chinese — bare nouns for code-graph concepts. The English "codegraph" brand
|
||||
# name above already covers mixed-language phrasings; these cover pure Chinese.
|
||||
- 代码图
|
||||
- 代码图谱
|
||||
- 代码索引
|
||||
- 索引代码
|
||||
- 装代码图
|
||||
- 初始化代码图
|
||||
role: Producer
|
||||
---
|
||||
|
||||
# CodeGraph Setup in the Octopus Environment
|
||||
|
||||
CodeGraph is a local-first code intelligence layer (https://github.com/colbymchenry/codegraph)
|
||||
that octopus consumes as a **stdio MCP server**. It exposes the `codegraph_explore`,
|
||||
`codegraph_node`, `codegraph_callers`, `codegraph_search`, and `codegraph_status`
|
||||
tools, letting agents query a pre-indexed knowledge graph instead of scanning files.
|
||||
|
||||
Per `.octopus/rules/code-graph.md` (the L1 code-graph rule): **Code Graph First is mandatory.**
|
||||
Structural questions MUST use `codegraph_explore` / `codegraph_search` /
|
||||
`codegraph_callers`; grep is the fallback for fuzzy text only. So a working
|
||||
CodeGraph install is a prerequisite for every cross-file task.
|
||||
|
||||
Setup has two independent layers. Each can be installed/reinstalled without
|
||||
touching the others:
|
||||
|
||||
1. **Global CLI** — the `codegraph` binary, installed once per machine.
|
||||
2. **Per-project index** — the `.codegraph/` directory, built once per repo.
|
||||
|
||||
MCP wiring into octopus.jsonc is handled by the kickoff pipeline
|
||||
(`octopus kickoff` or `/kickoff`). See the `project-kickoff` skill for details.
|
||||
|
||||
---
|
||||
|
||||
## 1. Install the global CLI
|
||||
|
||||
The package is `@colbymchenry/codegraph` on npm. It bundles its own runtime and
|
||||
ships platform-specific binaries as optional dependencies (e.g.
|
||||
`@colbymchenry/codegraph-linux-x64`), so there is nothing to compile.
|
||||
|
||||
Pick one method (all equivalent; they only differ in how the binary lands on
|
||||
`$PATH`):
|
||||
|
||||
```bash
|
||||
# Via bun global (what this environment currently uses)
|
||||
bun add -g @colbymchenry/codegraph
|
||||
|
||||
# Via npm global
|
||||
npm i -g @colbymchenry/codegraph
|
||||
|
||||
# Run-once, no install
|
||||
npx @colbymchenry/codegraph
|
||||
|
||||
# Via the official install scripts (puts `codegraph` on PATH, no shell change)
|
||||
curl -fsSL https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh | sh # macOS / Linux
|
||||
irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex # Windows (PowerShell)
|
||||
```
|
||||
|
||||
**In this environment**, the install is bun-global:
|
||||
|
||||
| Item | Value |
|
||||
|------|-------|
|
||||
| Binary on PATH | `~/.bun/bin/codegraph` (symlink) |
|
||||
| Real package dir | `~/.bun/install/global/node_modules/@colbymchenry/codegraph/` |
|
||||
| Entry point | `npm-shim.js` → resolves to the platform binary |
|
||||
| Platform binary | `@colbymchenry/codegraph-linux-x64` |
|
||||
| Version | `1.0.0` |
|
||||
|
||||
Verify the install:
|
||||
|
||||
```bash
|
||||
codegraph --version # prints the installed version
|
||||
which codegraph # should resolve under ~/.bun/bin or ~/.npm-global/bin
|
||||
```
|
||||
|
||||
Upgrade any time — it auto-detects how you installed:
|
||||
|
||||
```bash
|
||||
codegraph upgrade # upgrade to latest
|
||||
codegraph upgrade --check # only report if an update exists
|
||||
codegraph upgrade 1.2.3 # pin a specific version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Initialize the per-project index
|
||||
|
||||
Run once from inside each project you want indexed. `-i` builds the initial
|
||||
graph in the same step; omit it to create the `.codegraph/` dir and build
|
||||
later with `codegraph index`.
|
||||
|
||||
```bash
|
||||
codegraph init -i # create .codegraph/ AND build the initial graph
|
||||
codegraph init # create .codegraph/ only; run `codegraph index` afterwards
|
||||
```
|
||||
|
||||
This creates (relative to the project root):
|
||||
|
||||
```
|
||||
.codegraph/
|
||||
├── .gitignore # ignores itself; safe to leave uncommitted
|
||||
├── codegraph.db # the SQLite knowledge graph (full WAL mode)
|
||||
├── codegraph.db-shm # SQLite shared-memory (WAL)
|
||||
├── codegraph.db-wal # SQLite write-ahead log
|
||||
├── daemon.log # watcher/MCP daemon log
|
||||
├── daemon.pid # daemon process id
|
||||
└── daemon.sock # daemon IPC socket
|
||||
```
|
||||
|
||||
`codegraph init` writes a `.gitignore` inside `.codegraph/`, so the index dir
|
||||
is normally left uncommitted (it is machine- and version-specific). If you
|
||||
choose to commit it anyway, add `.codegraph/*.db*` to the repo `.gitignore`
|
||||
instead and treat the index as a build artifact.
|
||||
|
||||
**In this environment** the index is already built:
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Files indexed | 1,868 |
|
||||
| Nodes | 32,592 |
|
||||
| Edges | 118,912 |
|
||||
| DB size | ~85 MB |
|
||||
| Backend | `node:sqlite` — built-in (full WAL) |
|
||||
|
||||
Check any project's own stats at any time:
|
||||
|
||||
```bash
|
||||
codegraph status
|
||||
```
|
||||
|
||||
### Auto-sync — you do not normally run `codegraph sync`
|
||||
|
||||
Once the MCP server (`codegraph serve --mcp`) is running, three layers keep the
|
||||
index in step with your code without manual sync:
|
||||
|
||||
1. **Debounced file watcher.** A native FSEvents (macOS) / inotify (Linux) /
|
||||
ReadDirectoryChangesW (Windows) watcher captures every create/modify/delete
|
||||
and re-indexes after a debounce. Default `2000 ms`; tune via
|
||||
`CODEGRAPH_WATCH_DEBOUNCE_MS` (clamped to `[100ms, 60s]`).
|
||||
2. **Connect-time catch-up.** On (re)connect the server reconciles
|
||||
`(size, mtime)` + content-hash against the working tree, so edits made while
|
||||
no MCP server was running (a `git pull` from the terminal, another editor, a
|
||||
previous session that exited) are absorbed before the first query.
|
||||
3. **On-demand sync.** `codegraph sync` is available as an escape hatch.
|
||||
|
||||
Disable the watcher on slow filesystems (WSL2 `/mnt` drives, network mounts):
|
||||
|
||||
```bash
|
||||
codegraph serve --mcp --no-watch
|
||||
```
|
||||
|
||||
### Workflow worktrees get their own index ([org-internal #2561] / TD-006)
|
||||
|
||||
The main checkout's `.codegraph/` does NOT cover git worktrees — they live
|
||||
outside the repo path, so nearest-`.codegraph/` resolution finds nothing under
|
||||
them and graph queries degrade to grep+read. Both worktree lifecycle scripts
|
||||
hook a per-worktree init into their `create` flow:
|
||||
|
||||
- `script/session-worktree.sh create <slug>` and
|
||||
`script/scratch-worktree.sh create <slug> <base>` run
|
||||
`codegraph init -i <worktree-path>` in the background. Best-effort: a
|
||||
missing CLI, an existing index, or an init failure never blocks worktree
|
||||
creation — the ready/failed signal lands in `<worktree>/.codegraph/init.log`
|
||||
(one line). The background subshell never holds the caller's stdout pipe, so
|
||||
output-capturing callers (CI, nested scripts) are not blocked either.
|
||||
- `CODEGRAPH_SKIP=1` disables the hook (selftests / CI).
|
||||
- Worktrees created before this hook landed have no index — run
|
||||
`codegraph init -i` once from inside them.
|
||||
- After create, the index builds asynchronously (a full repo takes ~minutes).
|
||||
Verify readiness with `codegraph status` INSIDE the worktree (`Files > 0`,
|
||||
`Nodes > 0`) before relying on graph queries.
|
||||
|
||||
---
|
||||
|
||||
## 3. Verification checklist
|
||||
|
||||
After a fresh setup, confirm each layer is working:
|
||||
|
||||
1. **CLI** — `codegraph --version` prints a version; `which codegraph` resolves.
|
||||
2. **Index** — `codegraph status` from the project root shows `Files > 0` and
|
||||
`Nodes > 0`; `.codegraph/codegraph.db` exists.
|
||||
|
||||
---
|
||||
|
||||
## 4. How octopus uses CodeGraph (usage rules)
|
||||
|
||||
This skill covers **setup** only. Usage conventions (query the graph before
|
||||
reading; `codegraph_explore` for flows, `codegraph_search`/`codegraph_callers`
|
||||
for symbols; `grep` is the fallback) are normative in `.octopus/rules/code-graph.md` — refer there. The short version: run `codegraph_status`
|
||||
on session start, and prefer graph tools over grep+read for any structural
|
||||
question.
|
||||
@@ -0,0 +1,322 @@
|
||||
---
|
||||
name: frontend
|
||||
description: >
|
||||
Use ONLY when doing frontend development work — building or modifying UI
|
||||
components, pages, styles, and client-side interactions. Detects the
|
||||
project's framework and styling approach, then implements with a plan
|
||||
template, mandatory loading/empty/error/success state coverage, and an
|
||||
accessibility + self-check pass against core/checklists/frontend.md.
|
||||
For full pipeline features (a new page/route, ≥3 new components, new API
|
||||
contracts or a new data model, a new state-management pattern, or work
|
||||
spanning ≥5 files), use the `implement` skill and apply these templates
|
||||
within it.
|
||||
triggers:
|
||||
# English — phrases + UI-element nouns (short nouns are safe here: they
|
||||
# rarely embed in non-frontend words, and the description gates false fires).
|
||||
- create component
|
||||
- build ui
|
||||
- implement frontend
|
||||
- add page
|
||||
- add screen
|
||||
- implement page
|
||||
- frontend
|
||||
- UI component
|
||||
- style this
|
||||
- add styles
|
||||
- make it responsive
|
||||
- component
|
||||
- button
|
||||
- modal
|
||||
- dropdown
|
||||
- tooltip
|
||||
- navbar
|
||||
- checkbox
|
||||
- toggle
|
||||
# Chinese — high-frequency bare nouns only. Because matching is substring,
|
||||
# a bare noun (组件) subsumes every verb-noun combo (写组件 / 做个组件 /
|
||||
# 创建组件 / 改组件), so listing phrases is redundant. Chinese substrings
|
||||
# do not collide with English words, so bare nouns are safe + maximal-recall.
|
||||
- 前端
|
||||
- 组件
|
||||
- 页面
|
||||
- 样式
|
||||
- 按钮
|
||||
- 表单
|
||||
- 弹窗
|
||||
- 模态框
|
||||
- 输入框
|
||||
- 导航
|
||||
- 菜单
|
||||
- 卡片
|
||||
- 下拉
|
||||
- 标签页
|
||||
- 界面
|
||||
- 交互
|
||||
- 切图
|
||||
- 响应式
|
||||
|
||||
role: Producer
|
||||
---
|
||||
|
||||
# Frontend
|
||||
|
||||
Surgically implement frontend UI — components, pages, styles, and client-side
|
||||
interactions — guided by framework conventions, with mandatory state-coverage
|
||||
and accessibility checks before code review.
|
||||
|
||||
## Scope & Routing
|
||||
|
||||
This skill covers **standalone** frontend work: one or two components, a style
|
||||
tweak, a single page or UI element. The user's description plus the existing
|
||||
codebase is the specification.
|
||||
|
||||
Escalate to the `implement` skill (full pipeline: analyze-dag → review-dag →
|
||||
implement → review-code → verify) when the work involves ANY of:
|
||||
|
||||
- ≥ 3 new components, or a new page/route
|
||||
- new API contracts or a new data model
|
||||
- a new state-management pattern
|
||||
- ≥ 5 files changed
|
||||
|
||||
For pipeline work, still apply this skill's **Framework Detection**, **plan &
|
||||
report templates**, and **self-check** within the `implement` workflow.
|
||||
|
||||
## Agent Role
|
||||
|
||||
Owned by the **Developer** (Worker). Responsibilities:
|
||||
|
||||
- Detect framework + styling from the existing code; match conventions exactly.
|
||||
- Read ≥ 3 neighbor components before writing (brownfield).
|
||||
- Implement all UI states: loading, empty, error, success, and edge cases.
|
||||
- Ensure accessibility: semantic HTML, ARIA, keyboard nav, focus, contrast.
|
||||
- Self-check against `core/checklists/frontend.md` before handoff.
|
||||
- Write no more and no less than the scope — no opportunistic refactoring.
|
||||
|
||||
The Builder validates output and passes it to code review; it MUST NOT write
|
||||
implementation code.
|
||||
|
||||
<!-- inject: ../_shared/large-prompts.md -->
|
||||
|
||||
> **Context compaction**: this skill is a pipeline stage boundary. The main
|
||||
> session (orchestrator) compacts at this clean boundary ONLY when a
|
||||
> capacity/projection trigger holds, per the L1 rule `core/rules/compact.md`
|
||||
> §"Stage-boundary compaction" (long multi-stage runs — DAG Epic orchestration — keep the legacy
|
||||
> every-boundary compaction; short runs — bugfix / DAG task — and standalone
|
||||
> runs default to NOT compacting). The
|
||||
> sub-agent this skill dispatches persists its artifacts to the Gitea wiki
|
||||
> under `{slug}/` as it goes, so a mid-run compaction loses nothing — re-read
|
||||
> the stage's wiki index to resume.
|
||||
|
||||
## Framework Detection
|
||||
|
||||
Before writing any code, detect the project's frontend framework:
|
||||
|
||||
| Signal | Framework |
|
||||
| --------------------------------- | ---------- |
|
||||
| `package.json` has `react` | React |
|
||||
| `package.json` has `vue` | Vue |
|
||||
| `package.json` has `svelte` | Svelte |
|
||||
| `package.json` has `solid-js` | SolidJS |
|
||||
| `package.json` has `@angular/core`| Angular |
|
||||
| `.tsx`/`.jsx` files present | React or SolidJS (check package.json) |
|
||||
| `.vue` files present | Vue |
|
||||
| `.svelte` files present | Svelte |
|
||||
|
||||
Detect the styling approach:
|
||||
|
||||
| Signal | Approach |
|
||||
| ----------------------------------------- | ----------------- |
|
||||
| `tailwind.config.*` or `postcss.config.*` with tailwind | Tailwind CSS |
|
||||
| `.module.css` or `.module.scss` files | CSS Modules |
|
||||
| `styled-components` in package.json | styled-components |
|
||||
| `@emotion/*` in package.json | Emotion |
|
||||
| `uno.config.*` | UnoCSS |
|
||||
| Plain `.css` or `.scss` imports | Plain CSS/SCSS |
|
||||
|
||||
Follow the detected convention exactly. Do NOT introduce a new styling
|
||||
approach unless the work item explicitly requires it.
|
||||
|
||||
## Greenfield vs. Brownfield
|
||||
|
||||
**Greenfield** (new project): Create new files following the design. Use the
|
||||
framework's standard conventions (`create-vite`, `create-next-app`, etc.) as
|
||||
the baseline. Prefer functional components, TypeScript, and the framework's
|
||||
current recommended patterns.
|
||||
|
||||
**Brownfield** (existing project + new UI):
|
||||
|
||||
- **Read neighbors first.** Before writing code, read at least 3 existing
|
||||
files in the same module (component files for UI work) to absorb the
|
||||
project's patterns: component structure, prop
|
||||
typing, styling approach, state management, and file organization.
|
||||
(Shared brownfield rule — canonical statement:
|
||||
`core/skills/review-code/SKILL.md` §"Greenfield vs. Brownfield".)
|
||||
- Match existing conventions exactly: component declaration style, export
|
||||
pattern, file naming, directory structure, import ordering, and CSS
|
||||
organization.
|
||||
- New code MUST follow existing conventions consistently — no style drift.
|
||||
- No opportunistic refactoring of unrelated components. If you see a pattern
|
||||
violation, log it in the implementation report — do not fix it.
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Preconditions
|
||||
|
||||
- [ ] Task description exists (component name, props, behavior).
|
||||
- [ ] Existing codebase is accessible for convention discovery.
|
||||
- [ ] `core/checklists/frontend.md` is accessible.
|
||||
|
||||
If the task is vague ("make it look better"), ask for specifics:
|
||||
|
||||
1. What component/page needs work? (name or path)
|
||||
2. What should it look like or do? (screenshot, description, or reference)
|
||||
3. What states should it handle? (loading, empty, error, success)
|
||||
4. Any accessibility requirements?
|
||||
|
||||
### Phase 1 — Parse Context
|
||||
|
||||
1. Detect framework and styling approach (see Framework Detection).
|
||||
2. Read ≥ 3 neighbor components to absorb patterns.
|
||||
3. Check existing tests — follow the same test pattern.
|
||||
4. Check existing routes if adding a page.
|
||||
5. Check design tokens (theme, spacing, palette) if the project has them.
|
||||
|
||||
### Phase 2 — Plan UI Implementation
|
||||
|
||||
Before writing code, produce a brief implementation plan:
|
||||
|
||||
```markdown
|
||||
## Frontend Implementation Plan
|
||||
|
||||
**Component(s)**: {ComponentName} at {path}
|
||||
**Framework**: {React / Vue / Svelte / SolidJS / Angular}
|
||||
**Styling**: {Tailwind / CSS Modules / styled-components / plain CSS}
|
||||
**State variants**: loading | empty | error | success | {edge case}
|
||||
|
||||
**Files to create**:
|
||||
- `path/to/Component.tsx` — {purpose}
|
||||
|
||||
**Files to modify**:
|
||||
- `path/to/existing.tsx` — {what changes, why}
|
||||
|
||||
**Component API**:
|
||||
- Props: {prop}: {type} — {description}
|
||||
- Events/Callbacks: {onX}: {signature} — {description}
|
||||
- Slots/Children: {description if applicable}
|
||||
|
||||
**Accessibility checklist**:
|
||||
- [ ] Semantic HTML elements used
|
||||
- [ ] ARIA labels for icon-only buttons/images
|
||||
- [ ] Keyboard navigation (Tab order, Enter/Space for actions)
|
||||
- [ ] Focus management (auto-focus, focus trapping for modals)
|
||||
- [ ] Color contrast ≥ 4.5:1 for text, ≥ 3:1 for large text
|
||||
```
|
||||
|
||||
### Phase 3 — Implement
|
||||
|
||||
Implement in layers:
|
||||
|
||||
1. **Structure first** — scaffold the component with correct HTML semantics
|
||||
and prop types. No styling yet.
|
||||
2. **Add styling** — apply styles following the project's convention. Handle
|
||||
responsive breakpoints if the design specifies them.
|
||||
3. **Add state variants** — implement loading, empty, error, and edge case
|
||||
states before the happy path. This forces you to handle all conditions.
|
||||
4. **Wire interactivity** — add event handlers, form validation, keyboard
|
||||
shortcuts, and focus management.
|
||||
5. **Accessibility pass** — audit every interactive element for keyboard
|
||||
access, every image/icon for alt text, every form control for labels.
|
||||
|
||||
**Rules**:
|
||||
|
||||
- Use the project's existing component library (Kobalte, Radix, Headless UI,
|
||||
etc.) where applicable — do not reinvent accessible primitives.
|
||||
- Every component MUST render gracefully in all four states: loading, empty,
|
||||
error, and success.
|
||||
- If the project uses TypeScript, all props must be typed — no `any`.
|
||||
- Image `alt` text must be meaningful, not decorative if the image conveys
|
||||
information.
|
||||
- Form inputs must have associated `<label>` elements (not just placeholders).
|
||||
|
||||
### Phase 4 — Self-Check
|
||||
|
||||
Run the project's verification commands:
|
||||
|
||||
1. **Typecheck**: `bun typecheck` (or project equivalent). Fix all errors.
|
||||
2. **Lint**: `bun oxlint --deny-warnings` (repo root; `bun lint` is the package-script alias). Fix all errors.
|
||||
3. **Tests**: `bun run test:changed` (or project equivalent). All affected tests pass.
|
||||
|
||||
Then self-check against `core/checklists/frontend.md`:
|
||||
|
||||
- Verify every PRE item was satisfied before coding.
|
||||
- Verify every POST item is satisfied now.
|
||||
- For any failed item, fix before reporting.
|
||||
|
||||
### Phase 5 — Report
|
||||
|
||||
```markdown
|
||||
## Frontend Implementation Report
|
||||
|
||||
**Component(s)**: {ComponentName}
|
||||
**Framework**: {framework}
|
||||
**Styling**: {approach}
|
||||
|
||||
### Files Changed
|
||||
|
||||
| File | Action | Purpose |
|
||||
| ------------------ | -------- | -------------- |
|
||||
| `path/to/file.tsx` | created | {purpose} |
|
||||
| `path/to/file.css` | created | {purpose} |
|
||||
|
||||
### State Coverage
|
||||
|
||||
| State | Handled | How |
|
||||
| ------- | ------- | -------------------------------- |
|
||||
| loading | ✅ | Skeleton/spinner while fetching |
|
||||
| empty | ✅ | "No items" message with CTA |
|
||||
| error | ✅ | Error message with retry button |
|
||||
| success | ✅ | Renders data as designed |
|
||||
|
||||
### Accessibility
|
||||
|
||||
| Check | Status | Notes |
|
||||
| --------------------------- | ------ | -------------- |
|
||||
| Semantic HTML | ✅ | |
|
||||
| Keyboard navigation | ✅ | |
|
||||
| Focus management | ✅ | |
|
||||
| Color contrast | ✅ | |
|
||||
| Screen reader labels | ✅ | |
|
||||
|
||||
### Verification
|
||||
|
||||
- Typecheck: {pass / fail}
|
||||
- Lint: {pass / fail}
|
||||
- Tests: {N} passed, {M} failed
|
||||
|
||||
### Open Items
|
||||
|
||||
{anything incomplete with reason, or "None"}
|
||||
|
||||
---
|
||||
|
||||
**Handoff**: {if > 20 lines or ≥ 3 files → run review-code | else → complete}
|
||||
```
|
||||
|
||||
### Phase 6 — Approval
|
||||
|
||||
Present the report to the user. Route to code review if > 20 lines or ≥ 3 files.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- `core/checklists/frontend.md` — Frontend self-check checklist
|
||||
- `core/skills/implement/SKILL.md` — General + pipeline implementation workflow
|
||||
- `core/skills/review-code/SKILL.md` — Code review (next step)
|
||||
- `<instance-root>/archive/skills/design/SKILL.md` — Design document production (legacy upstream, archived [org-internal #3072] phase 3)
|
||||
- `core/skills/browser-debug/SKILL.md` — Interactive browser verification (invoke during Phase 3 / 4 for visual + interaction evidence)
|
||||
- `core/rules/testing.md`, `core/rules/type-checking.md` — Repository conventions (test commands, typecheck)
|
||||
- `core/rules/code-graph.md` — Code graph first
|
||||
- `core/adapters/gitea/reading.md` — Read SDLC artifacts from wiki
|
||||
@@ -0,0 +1,183 @@
|
||||
---
|
||||
name: gitea-rest
|
||||
description: Use ONLY when an agent must call the Gitea REST API over HTTP (curl / fetch / HttpClient) 鈥?not for other gitea tooling (the retired gitea-mcp wrapper / gitea_* MCP tools). Covers auth schemes + token scopes, pagination, error format, this fork's specifics, copy-paste recipes for the daily loop (issues, PRs, comments, labels, releases, raw files), and the on-demand recipe for extracting any endpoint's definition from the auto-generated swagger spec (`/swagger.v1.json`) instead of keeping an endpoint catalog in context. This is the only supported Gitea API path 鈥?the gitea-mcp wrapper is retired.
|
||||
triggers:
|
||||
# Direct Gitea HTTP/REST. Composite "gitea rest"/"gitea api" to avoid
|
||||
# colliding with other gitea tooling.
|
||||
- gitea rest
|
||||
- gitea api
|
||||
- gitea http
|
||||
- call gitea api
|
||||
- /api/v1
|
||||
- gitea swagger
|
||||
- swagger.v1.json
|
||||
- 璋?gitea api
|
||||
- gitea 鎺ュ彛
|
||||
- gitea rest api
|
||||
role: Producer
|
||||
---
|
||||
|
||||
> Core 涓珛鐗堬紙Increment 6a 鏀瑰啓锛夈€傛湰鏂囦欢鍚屾椂浣滀负 gitea adapter 鐨?**reference implementation** 绀轰緥锛坒rontmatter 鍘熸牱锛夛紱瀹炰緥鏈瀵圭収 `core/adapters/TERMINOLOGY.md`銆?
|
||||
# Gitea REST 鈥?conventions + on-demand endpoint lookup
|
||||
|
||||
Gitea ships a JSON REST API under **`/api/v1`**. The instance also publishes an
|
||||
**auto-generated OpenAPI spec at `/swagger.v1.json`** (~889KB, 316 endpoints) 鈥?
|
||||
never load it whole into context; extract single endpoint definitions with jq
|
||||
(see "Endpoint lookup"). The spec is generated from this fork's code, so it is
|
||||
always current 鈥?no manually maintained catalog can drift.
|
||||
|
||||
## Quick start
|
||||
|
||||
```sh
|
||||
BASE=<instance-base-url>
|
||||
TOKEN=$(cat <config-home>/octopus/gitea-token) # or $WORKSPACE_GIT_TOKEN inside a workspace container
|
||||
AUTH="Authorization: token $TOKEN"
|
||||
curl -fsS -H "$AUTH" $BASE/api/v1/version # => {"version":"1.22.0"}
|
||||
```
|
||||
|
||||
Inside a workspace container: `WORKSPACE_GIT_TOKEN` already carries the
|
||||
provisioned token, and the instance host resolves to the VPC-internal address
|
||||
(no EIP bandwidth cap) 鈥?use it as `TOKEN` directly.
|
||||
|
||||
## Authentication
|
||||
|
||||
| Scheme | Header | Notes |
|
||||
| --------------------- | ------------------------------------------ | ----------------------------------------------- |
|
||||
| **PAT (recommended)** | `Authorization: token <PAT>` | Also accepts `bearer`. 40-char hex. |
|
||||
| OAuth2 JWT | `Authorization: bearer <jwt>` | OAuth2-app token. |
|
||||
| Basic | `Authorization: Basic <base64(user:pass)>` | Server must enable it; 2FA needs `X-Gitea-OTP`. |
|
||||
|
||||
- Query-param token (`?token=`) is deprecated 鈥?avoid.
|
||||
- `Sudo: <username>` header acts as another user (admin only).
|
||||
- Keep tokens out of git and out of process argv where possible: read from a
|
||||
file/env into a shell var at call time, not in a committed script.
|
||||
|
||||
## Token scopes (write implies read)
|
||||
|
||||
Categories: `issue`, `repository`, `organization`, `user`, `notification`,
|
||||
`package`, `admin`, `misc`, `activitypub` 鈥?each as `read:X` / `write:X`.
|
||||
HTTP method sets the level: **GET 鈫?read**, **POST/PUT/PATCH/DELETE 鈫?write**.
|
||||
A scope miss returns `403` with `token does not have at least one of required
|
||||
scope(s)...` 鈥?re-issue the token rather than widening other permissions.
|
||||
|
||||
The full agent loop (push commits, create/merge PRs, comment) needs at minimum
|
||||
**`write:repository` + `write:issue`**. Workspace container tokens are
|
||||
provisioned `read:repository` by default 鈥?write operations fail with 403
|
||||
until the operator widens the scope.
|
||||
|
||||
## Pagination
|
||||
|
||||
- `page` (1-based, default 1), `limit` (default 30, **hard cap 50**).
|
||||
- `X-Total-Count` response header = total results.
|
||||
- `Link: <url?page=2>; rel="next"` 鈥?walk `next` links until absent.
|
||||
- Some endpoints (commits, pulls) also set `X-Page` / `X-PerPage` /
|
||||
`X-PageCount` / `X-HasMore`.
|
||||
|
||||
## Errors
|
||||
|
||||
Every error is `{"message":"...","url":".../api/swagger"}` with the matching
|
||||
status: `400` bad arg, `403` permission/scope, `404` not found or wrong BASE,
|
||||
`409` already exists, `413` too large, `422` malformed JSON / missing field.
|
||||
There is **no rate limiting** on `/api/v1` 鈥?don't expect `X-RateLimit-*`.
|
||||
|
||||
## Endpoint lookup (swagger, on demand)
|
||||
|
||||
For any endpoint not covered by the recipes below, pull the spec once per
|
||||
session and jq out just the definition you need (50鈥?00 tokens each):
|
||||
|
||||
```sh
|
||||
curl -s $BASE/swagger.v1.json -o /tmp/gitea-sw.json
|
||||
jq '.paths["/repos/{owner}/{repo}/pulls"].post' /tmp/gitea-sw.json # create PR
|
||||
jq '.paths["/repos/{owner}/{repo}/issues"].get.parameters' /tmp/gitea-sw.json
|
||||
jq '.definitions.CreateIssueOption' /tmp/gitea-sw.json # a body model
|
||||
jq -r '.paths | keys[]' /tmp/gitea-sw.json | grep actions # discover endpoints
|
||||
```
|
||||
|
||||
Definitions include parameter names/types/required flags and `$ref` response
|
||||
models (under `.definitions` on this Gitea version). Prefer discovering via
|
||||
the spec over guessing paths.
|
||||
|
||||
## Daily-loop recipes
|
||||
|
||||
`BASE`/`TOKEN`/`AUTH` as in Quick start; `OWNER=Octopus REPO=octopus` as
|
||||
the example. All bodies are JSON.
|
||||
|
||||
**Create an issue**
|
||||
|
||||
```sh
|
||||
curl -fsS -X POST "$BASE/api/v1/repos/$OWNER/$REPO/issues" -H "$AUTH" -H 'Content-Type: application/json' \
|
||||
-d '{"title":"Bug: X fails","body":"steps...","labels":[12]}'
|
||||
```
|
||||
|
||||
**Comment on an issue / PR** (same path for both 鈥?PRs are issues by index)
|
||||
|
||||
```sh
|
||||
curl -fsS -X POST "$BASE/api/v1/repos/$OWNER/$REPO/issues/42/comments" -H "$AUTH" \
|
||||
-H 'Content-Type: application/json' -d '{"body":"LGTM"}'
|
||||
```
|
||||
|
||||
**Create a PR**
|
||||
|
||||
```sh
|
||||
curl -fsS -X POST "$BASE/api/v1/repos/$OWNER/$REPO/pulls" -H "$AUTH" -H 'Content-Type: application/json' \
|
||||
-d '{"head":"workflow/fix/x","base":"main","title":"fix: X"}'
|
||||
```
|
||||
|
||||
**Merge a PR**
|
||||
|
||||
```sh
|
||||
curl -fsS -X POST "$BASE/api/v1/repos/$OWNER/$REPO/pulls/7/merge" -H "$AUTH" -H 'Content-Type: application/json' \
|
||||
-d '{"Do":"merge","MergeTitleField":"feat: add X","MergeMessageField":"closes #7"}'
|
||||
```
|
||||
|
||||
**Merge returned 405? Read the response body 鈥?two distinct causes:**
|
||||
|
||||
- `{"message":"head branch is behind the base branch"}` 鈥?the keep-mergeable
|
||||
bot advanced main after your push, or Gitea's mergeable cache is stale.
|
||||
Fix: `git fetch origin main && git merge origin/main && git push` the head
|
||||
branch, wait ~2s, retry the merge. (Recurring on this server 鈥?seen 3 PRs
|
||||
in a row on 2026-08-23.)
|
||||
- `{"message":"The PR is already merged"}` 鈥?**treat as success**. The merge
|
||||
landed during a previous "failed" attempt (racy async recompute). Confirm
|
||||
with `GET .../pulls/7 | jq '.merged, .merge_commit_sha'` before assuming
|
||||
failure. Never re-push/re-create the PR on this signal.
|
||||
|
||||
**Auth hygiene** 鈥?always pass the token via the `Authorization` header
|
||||
(`-H "Authorization: token $TOKEN"`), never embedded in a remote URL
|
||||
(`https://user:token@host/...`): it persists into `.git/config` and shell
|
||||
history. If a token leaks into a remote URL, rewrite the remote
|
||||
(`git remote set-url`) and rotate.
|
||||
|
||||
**List PRs / changed files** 鈥?`GET .../pulls?state=open`, `GET .../pulls/7/files`
|
||||
|
||||
**Search issues across repos** 鈥?`GET $BASE/api/v1/repos/issues/search?q=&type=pulls`
|
||||
|
||||
**List labels** 鈥?`GET .../labels?limit=50 | jq '.[] | {name,color}'` (label create/update via POST/PATCH on the same path)
|
||||
|
||||
**Raw file contents** 鈥?`GET .../raw/README.md` (plain text; the `contents/{path}` variant returns base64 in `.content`)
|
||||
|
||||
**Create a release** 鈥?`POST .../releases {"tag_name":"v1.2.0","target":"main",...}`
|
||||
|
||||
**Create a wiki page** 鈥?`POST .../wiki/new {"title":"My Page","content_base64":"<b64>","message":"add page"}`. **`content_base64` is the ONLY field that writes body text** 鈥?this fork **silently ignores** the upstream-style `content` field on wiki create/update (no error, `201` returned, page saved 0 bytes; evidence [org-internal #3944]: commits f5eaf18/4f0217d/bad5880). `content` is valid only on the file API, never on wiki.
|
||||
|
||||
**Read / edit / delete a wiki page** 鈥?`GET|PATCH|DELETE .../wiki/page/{pageName}`. **Read by the mangled name, not the logical title**: this fork rewrites stored filenames for titles containing `/`, spaces, or `:` (slash percent-encoded into the filename + a `.-` suffix 鈥?see [org-internal #3218]), so `GET /wiki/page/bugfix-3204/bugfix-report` 404s. Always `GET .../wiki/pages` first and use the returned `sub_url` **verbatim** (it already carries the `%2F` encoding and `.-` suffix). PATCH/DELETE take the same mangled `pageName`. For PATCH, **omit `title` to keep the page name** and send only `content_base64`+`message` (fixed in gitea `dev-421-g7ff56aec08`, [org-internal #3510] 鈥?before that a title-less PATCH silently renamed the page to `unnamed.md` and later edits deleted target pages). Same trap as create: a PATCH carrying `content` instead of `content_base64` returns `200` with a **0-byte page** ([org-internal #3944]) 鈥?always base64-encode the body and verify non-empty via a follow-up `GET`.
|
||||
|
||||
## This fork's specifics
|
||||
|
||||
- **Projects are repo-level only** 鈥?no `/orgs/.../projects` or `/users/.../projects` REST endpoints.
|
||||
- **Aggregated inline review comments**: no single list-all endpoint 鈥?list
|
||||
reviews, then fetch each review's `/comments`.
|
||||
- Wiki REST = 6 method脳path combos on 4 paths (`new`, `page/{pageName}`
|
||||
GET/PATCH/DELETE, `pages`, `revisions/{pageName}`). This fork has **no**
|
||||
upstream `raw/{pageName}` or `pages/{pageName}` single-page endpoints, and
|
||||
`page/{pageName}` only matches the mangled filename (see the wiki recipe
|
||||
above and [org-internal #3218]) 鈥?clone the wiki git repo (default branch `main`) only if
|
||||
you need history beyond `/wiki/revisions` or bulk filename surgery.
|
||||
- The authoritative route table is `routers/api/v1/api.go` in the Gitea
|
||||
source; the human-readable docs UI is `<BASE>/api/swagger`.
|
||||
|
||||
## In Effect code
|
||||
|
||||
Prefer `HttpClient` from `@effect/platform` with the same headers (see
|
||||
`rules/effect-rules` on the wiki). Outside Effect, `curl` / `Bun.fetch` are
|
||||
fine 鈥?the wire format is identical.
|
||||
@@ -0,0 +1,547 @@
|
||||
---
|
||||
name: headless-session-ops
|
||||
description: >
|
||||
Use ONLY when an agent must autonomously launch a headless main session 鈥?
|
||||
create a session and drive agent generation over the HTTP `prompt_async`
|
||||
endpoint with NO persistent client attached (Web UI / TUI absent) 鈥?OR
|
||||
lifecycle-manage such a session afterwards: health-check polling, hung-stream
|
||||
diagnosis (state=generating + frozen message count), abort + re-wake rescue,
|
||||
provider quota-wall recovery (worker killed mid-task by a usage limit 鈥?
|
||||
detect, re-wake on a known-good provider, inject facts), and fact-baseline
|
||||
injection to correct a stale worldview in a woken worker.
|
||||
This is the path used by in-session agents spawning sibling sessions,
|
||||
`octopus run --attach` non-interactive mode, and cron / CI drivers. Covers
|
||||
the 3-step flow (create session 鈫?POST prompt_async WITH a known-good
|
||||
`model` 鈫?poll `/session/:id/message` to verify generation), how to REUSE
|
||||
the current session's model for the new session, the model-field
|
||||
requirement, the directory default (current session's directory unless
|
||||
targeting another repo), and the ticket-driven recipe (launch a session
|
||||
seeded from issue #N). Do NOT use when a persistent client (Web UI / TUI)
|
||||
is driving the session 鈥?those use the synchronous `prompt` endpoint and
|
||||
always send `model`.
|
||||
triggers:
|
||||
# English 鈥?the brand phrases for headless launch.
|
||||
- headless session
|
||||
- launch session
|
||||
- prompt_async
|
||||
- prompt async
|
||||
- autonomous session
|
||||
# English 鈥?the failure mode (agent sees a stalled session and needs to know why).
|
||||
- session not generating
|
||||
- session generation stuck
|
||||
# English 鈥?ticket-driven launch.
|
||||
- launch session for issue
|
||||
- launch session for ticket
|
||||
- spawn session for ticket
|
||||
# English 鈥?patrol mode (delayed self-wake polling loops, [org-internal #3937]).
|
||||
- patrol loop
|
||||
- patrol mode
|
||||
- delayed wake
|
||||
# Chinese 鈥?bare noun phrases (matches() is a contiguous substring).
|
||||
- 鎷夎捣涓讳細璇?
|
||||
- 鎷夎捣浼氳瘽
|
||||
- 鑷不浼氳瘽
|
||||
- 鏃犲ご浼氳瘽
|
||||
# Chinese 鈥?patrol mode ([org-internal #3937]).
|
||||
- 宸℃妯″紡
|
||||
- 寤惰繜鍞ら啋
|
||||
- 浼氳瘽涓嶇敓鎴?
|
||||
- 浼氳瘽鍗℃
|
||||
- 涓哄伐鍗曟媺璧蜂細璇?
|
||||
- 宸ュ崟鎷夎捣浼氳瘽
|
||||
# English 鈥?lifecycle (hung rescue, stale-worldview correction, [org-internal #2459]).
|
||||
- abort session
|
||||
- revive session
|
||||
- session rescue
|
||||
- fact baseline
|
||||
# English 鈥?quota-wall recovery ([org-internal #3669]): worker killed by provider usage limit.
|
||||
- quota wall
|
||||
- usage limit reached
|
||||
- quota exhausted
|
||||
- switch provider
|
||||
# Chinese 鈥?lifecycle ops ([org-internal #2459]).
|
||||
- 鍞ら啋浼氳瘽
|
||||
- 浼氳瘽鎸傛
|
||||
- 浜嬪疄鍩虹嚎
|
||||
# Chinese 鈥?quota-wall recovery ([org-internal #3669]).
|
||||
- 閰嶉澧?
|
||||
- 閰嶉鑰楀敖
|
||||
- 鐢ㄩ噺涓婇檺
|
||||
- 鎹緵搴斿晢
|
||||
role: Producer
|
||||
---
|
||||
|
||||
> Core 涓珛鐗堬紙Increment 6a 鏀瑰啓锛屽師 deferHard verbatimDir锛夈€傛満鍒躲€佺粨鏋勪笌 frontmatter 淇濇寔锛涘疄渚嬫湳璇紙宸ュ叿鍚嶃€佽矾寰勩€佸伐鍗曞彿锛夋寜 `core/adapters/TERMINOLOGY.md` 缁戝畾鍒板叿浣撳疄渚嬨€?
|
||||
# Headless Main Session Launch (prompt_async)
|
||||
|
||||
Launch a **main session for a specific issue / work item from a headless
|
||||
context** 鈥?no Web UI, no TUI, just HTTP. This is the "fire-and-forget" path: an
|
||||
agent inside another session, a cron job, a CI script, or `octopus run --attach`
|
||||
non-interactive mode all land here when they need to spin up a session that
|
||||
actually generates.
|
||||
|
||||
## Directory convention 鈥?independent session in the current workspace, no worktree
|
||||
|
||||
The default is an **independent session sharing the current session's
|
||||
directory** 鈥?the repo you are already working in. No new worktree, no
|
||||
checkout, no extra `bun install` or `.codegraph/` rebuild. Session creation
|
||||
and worktree discipline are **orthogonal**: this skill only opens a session
|
||||
record pointed at a directory; whether the launched session later creates its
|
||||
own worktree is a _workflow_ decision (per the worktree-discipline rule) it
|
||||
makes when it starts actual work 鈥?not something the launcher does. One
|
||||
exception: the **ticket-driven recipe** below, where claim-first ([org-internal #2297])
|
||||
requires the launcher to push the workflow branch before launch, making a
|
||||
pre-built worktree the claim carrier + ready workspace (see Pitfall #10
|
||||
carve-out). So, unless a different repo / path is specifically targeted,
|
||||
`?directory=` defaults to the current session's directory and is NOT mandatory:
|
||||
|
||||
- **You are an in-session agent** (most common): set `DIR` to your own
|
||||
session's `directory` (you already know it from context, or read `.directory`
|
||||
from your own session record via `GET /session/$LAUNCHER_SID`). The new
|
||||
session runs independently in that same workspace. Pass an explicit
|
||||
different `?directory=` only when the new session must work in another
|
||||
**repo** 鈥?never a worktree of the same repo. The Web UI sidebar groups
|
||||
sessions by directory (`sidebar-project.tsx` per-workspace
|
||||
`workspaceSessions(directory)`, fetched via `session.list({ directory })`),
|
||||
so a session pointed at a worktree disappears from the workspace the creator
|
||||
manages. A pre-built ticket worktree is handed to the launched session via
|
||||
the claim comment + seed prompt, NOT via `?directory=`.
|
||||
- **No current session** (cron / CI / bare script): the server-side fallback
|
||||
when `?directory=` is omitted is the **server process's `process.cwd()`**
|
||||
(`server/routes/instance/httpapi/middleware/workspace-routing.ts:76`), which
|
||||
is wherever the server was started 鈥?unreliable. In that context `?directory=`
|
||||
remains effectively required.
|
||||
- **Dispatch convention 鈥?directory = code location, not tracker repo.** When
|
||||
launching a session for a ticket, set `?directory=` to the **main workspace**
|
||||
checkout that holds the **code the ticket edits**, which may differ from the
|
||||
repo where the ticket is tracked. Example: `<owner>/<backend-repo>#<n>` is filed in
|
||||
the ticket-tracker repo but edits the main repo's workflow files 鈫?the
|
||||
session's `?directory=` is the **code checkout root**
|
||||
(`<workspace-root>/<org>/<repo>`), not the tracker repo checkout. Never point
|
||||
`?directory=` at a ticket worktree 鈥?the worktree goes in the claim comment +
|
||||
seed prompt, not the directory field (sidebar visibility, see bullet 1). The
|
||||
owner-check (`GET /session?directory=$DIR`) is scoped per directory, so
|
||||
rooting the session at the correct code checkout is also what makes the
|
||||
duplicate-session guard meaningful. Server-side backstop ([org-internal #3190]): creating a
|
||||
session whose directory IS a linked git worktree answers with an
|
||||
`X-Session-Directory-Warning` response header and a server warn log naming
|
||||
the owning main repo 鈥?if you see that header, the session will be invisible
|
||||
to `GET /session?directory=<mainRepo>` owner-checks; re-check your launch
|
||||
parameters before proceeding.
|
||||
|
||||
> **Authoritative reference:** `rules/headless-session-ops` wiki page
|
||||
> (<<instance-base-url>/Octopus/octopus/wiki/rules%2Fheadless-session-ops>).
|
||||
> This skill is its runtime carrier. Provenance: issue **[org-internal #1695]** (which
|
||||
> corrected the misdiagnosed [org-internal #1691], see comment 9205). Code citations below were
|
||||
> verified against `<harness-package>` at HEAD.
|
||||
|
||||
## The `model` field 鈥?always provide a known-good model (read this first)
|
||||
|
||||
`prompt_async` is **fire-and-forget**: after HTTP returns 204 (or 202 when the
|
||||
message is queued behind a wedge/zombie session 鈥?no live runner in that
|
||||
process), the server runs the full agent loop in a separate fiber. **Always
|
||||
include an explicit `model`
|
||||
in the payload, and reuse the current session's model for it** (see the next
|
||||
section). Two reasons:
|
||||
|
||||
1. **Current code does not hard-require `model`.** The HTTP boundary marks
|
||||
`model` as optional (`<harness-package>/src/session/prompt.ts:1860`), and
|
||||
`createUserMessage` fills a missing model via a fallback chain
|
||||
(`prompt.ts:858`):
|
||||
|
||||
```ts
|
||||
const model = input.model ?? ag.model ?? yield * currentModel(input.sessionID)
|
||||
// ^^^^^^^^^ agent ^^^^^^^^^^^^^^^^^ session/default
|
||||
```
|
||||
|
||||
So omission does **not** produce a clean validation error 鈥?it silently
|
||||
resolves to the agent's model, the session's last-used model, or the
|
||||
provider default (`currentModel`, `prompt.ts:824-838`).
|
||||
|
||||
2. **But the fallback is not safe in the autonomous context.** If it resolves
|
||||
to a model that is unusable here (region-blocked, wrong subscription tier,
|
||||
not configured), the runLoop's `getModel` raises `ModelNotFoundError`
|
||||
(`prompt.ts:1399`, `:804-822`). In the **async** handler that error is caught
|
||||
by the delivery fork's failure handling (`forkPromptDelivery` in
|
||||
`handlers/session.ts`) and never reaches the HTTP
|
||||
caller 鈥?so the session looks healthy (HTTP 204, user message persisted) yet
|
||||
**never generates**. This is exactly the "silent stall" observed empirically
|
||||
in [org-internal #1695]: a session with a 1-message transcript (user only) and frozen
|
||||
`time.updated`.
|
||||
|
||||
| payload | result |
|
||||
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `{agent, parts}` 鈥?no `model` | 204/202; model filled by fallback; if the resolved model is unusable 鈫?**apparent silent stall** (no error surfaced, no generation). |
|
||||
| `{agent, model:{providerID,modelID}, parts}` | 204/202; deterministic generation within seconds (verified: "1+1 绛変簬 2銆? in ~6s). |
|
||||
|
||||
> **Iron Law:** every `prompt_async` payload MUST carry a **known-good** `model`,
|
||||
> and the safest known-good model is **the one driving this session right now**
|
||||
> (it is, by definition, generating). Never rely on the fallback chain in a
|
||||
> headless/autonomous launch.
|
||||
|
||||
**Field-name gotcha when copying a model:** the prompt payload's `ModelRef` uses
|
||||
`{ providerID, modelID }` (`prompt.ts:1852-1855`), but the **session record's**
|
||||
model uses `{ id, providerID, variant }` (`session.ts:256-260`) 鈥?i.e. `modelID`
|
||||
is called `id`. When you reuse a model read from a session record, remap
|
||||
`model.id 鈫?modelID` (see recipe below). User-message `info.model` already uses
|
||||
`modelID` (`message.ts:403-407`).
|
||||
|
||||
## Reuse the current session's model (recommended default)
|
||||
|
||||
The model powering the launching session is proven to work 鈥?reuse it. Discover
|
||||
it by whichever path fits your context:
|
||||
|
||||
- **You are an in-session agent** (most common): read it straight from your own
|
||||
system prompt, which states verbatim:
|
||||
|
||||
> "You are powered by the model named **{modelID}** (provider: **{providerID}**)."
|
||||
|
||||
e.g. `modelID = "glm-5.2"`, `providerID = "zai-coding-plan"`锛堟浘鍚?
|
||||
zhipuai-coding-plan锛?026-08 鏇村悕锛?
|
||||
|
||||
- **Programmatic / no system prompt** (cron, CI, sibling caller): `GET` the
|
||||
launching session's record and read `.model`:
|
||||
|
||||
```sh
|
||||
curl -s "$BASE/session/$LAUNCHER_SID?directory=$DIR" \
|
||||
| jq '.model' # 鈫?{ "id": "glm-5.2", "providerID": "zai-coding-plan", "variant": ... }
|
||||
```
|
||||
|
||||
Then **remap** `model.id 鈫?modelID` when building the payload.
|
||||
|
||||
- **Verify the provider is actually connected** before launching (cheap
|
||||
insurance 鈥?the model only generates if its provider is live):
|
||||
```sh
|
||||
curl -s "$BASE/provider?directory=$DIR" | jq '.connected | index("<current providerID>")'
|
||||
# 鈫?null = provider NOT connected (do NOT launch); 0..n = connected
|
||||
```
|
||||
or call the `list-models` tool. Note: model-level availability is only
|
||||
knowable at runtime 鈥?a _configured_ model can still fail (subscription-tier
|
||||
or region errors); `GET /config/providers` lists _configured_ models, not
|
||||
usable ones. The safest pre-flight is "provider connected" + reusing the
|
||||
current session's model, which is generating by definition. There is **no
|
||||
`GET /model` HTTP endpoint** (it 404s); do not use it.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A reachable octopus HTTP instance (e.g. `http://127.0.0.1:4096`).
|
||||
- The target directory (passed as `?directory=`). Defaults to the current
|
||||
session's directory for in-session launches 鈥?see the Directory convention
|
||||
above; only specify a different one explicitly when targeting another repo.
|
||||
- A known-good `model` (reuse the current session's 鈥?see above). Model IDs
|
||||
must match the provider config in `<config-home>/octopus/octopus.jsonc`;
|
||||
credentials live in the octopus account keyring (not env), so an isolated
|
||||
HOME cannot reuse them.
|
||||
|
||||
## The 3-step flow
|
||||
|
||||
Set the base URL and directory once:
|
||||
|
||||
```sh
|
||||
BASE=http://127.0.0.1:4096
|
||||
DIR=/data/octopus
|
||||
```
|
||||
|
||||
### Step 1 鈥?Create the session
|
||||
|
||||
```sh
|
||||
SID=$(curl -s -X POST "$BASE/session?directory=$DIR" \
|
||||
-H 'content-type: application/json' \
|
||||
-d '{"title":"#N 鈥?<short description>","agent":"builder"}' | jq -r .id)
|
||||
echo "session=$SID"
|
||||
```
|
||||
|
||||
- `POST /session` returns `Session.Info` with `.id` (`groups/session.ts:207-211`).
|
||||
- `?directory=` points at the repo root. For in-session launches it defaults to
|
||||
the **current session's directory** (Directory convention above) 鈥?keep it
|
||||
that way so the new session stays visible in the creator's sidebar; pass an
|
||||
explicit path only when targeting a different repo (a worktree of the same
|
||||
repo is NOT a directory target 鈥?hand it over via the seed prompt), and
|
||||
always pass it from cron / CI where no current session exists (server falls
|
||||
back to its own `process.cwd()` otherwise).
|
||||
- `agent` is `builder` (the main agent). Sub-agents (explorer/worker) are
|
||||
spawned within a session 鈥?never named here.
|
||||
|
||||
### Step 2 鈥?Deliver the seed prompt (CRITICAL: include a known-good `model`)
|
||||
|
||||
```sh
|
||||
curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
|
||||
-H 'content-type: application/json' \
|
||||
-d '{
|
||||
"agent": "builder",
|
||||
"model": { "providerID": "<current providerID>", "modelID": "<current modelID>" },
|
||||
"parts": [ { "type": "text", "text": "<seed prompt: restate goal, constraints, acceptance criteria, cite wiki artifact paths>" } ]
|
||||
}'
|
||||
```
|
||||
|
||||
- Endpoint is `POST /session/:sessionID/prompt_async`; it answers **204, no
|
||||
body** when processing starts now (idle session, live runner, noReply
|
||||
injection, or waiting-question preemption), **202** when the message is
|
||||
queued behind a session that is generating with no live runner in this
|
||||
process (wedge/zombie shape 鈥?stored and re-driven on restart drain or the
|
||||
next runner), **202** when `delay_sec` defers delivery to an in-memory
|
||||
delayed wake ([org-internal #3937] A 鈥?see "宸℃妯″紡 / Patrol mode" below), and **409**
|
||||
when the session is paused (message NOT stored) or the maintenance-mode
|
||||
hold queue is full.
|
||||
- **Global route alternative ([org-internal #4307]):** when the client only knows the
|
||||
sessionID (no `?directory=` routing), `POST /prompt_async` (no path prefix)
|
||||
resolves the instance from the app database by the body's `sessionID` and
|
||||
delegates to the same delivery core 鈥?same 204/202/409 semantics, same
|
||||
`delay_sec` contract, body is the session-scoped payload plus a required
|
||||
`sessionID` field. Unknown sessionID 鈫?404 naming the session-scoped
|
||||
route. This is the natural fit for the completion-report POST (fixed port,
|
||||
no directory context).
|
||||
- `delay_sec` (optional, integer 0..86400, else 400) schedules the prompt for
|
||||
a one-shot delayed self-wake: the server holds the prompt in memory and
|
||||
fires it exactly once after `delay_sec` seconds, only for an idle session
|
||||
at intake. **Precedence ([org-internal #3937] review):** `delay_sec` applies only when the
|
||||
intake would otherwise start processing now (immediate class). For the
|
||||
queued-behind-wedge 202 member and under maintenance queue mode it is
|
||||
stripped 鈥?retention semantics win (the server logs the drop; the delay is
|
||||
NOT honored). Pending wakes are **in-memory only 鈥?a server restart abandons
|
||||
them**; a patrol loop must therefore tolerate a missed wake and re-arm.
|
||||
Never use `sleep <big>; curl ...` in a bash tool call to wait 鈥?that blocks
|
||||
the turn with zero output (the [org-internal #3937] incident shape); the bash tool now
|
||||
warns on bare sleeps > 120s (long-sleep guard, [org-internal #3937] C).
|
||||
- `model` is `{ providerID, modelID }` or the shorthand string
|
||||
`"providerID/modelID"` (split on the FIRST `/`; accepted since [org-internal #4307] 鈥?
|
||||
e.g. `"zai-coding-plan/glm-5.2"`). **Do not omit it.** Fill the values from
|
||||
your own session's model (see "Reuse the current session's model" above) 鈥?
|
||||
never hardcode a specific model. A string without a `/` is rejected with
|
||||
400 naming both accepted forms.
|
||||
- When the seed prompt contains newlines/quotes, build the payload from a file
|
||||
to avoid shell-escaping errors:
|
||||
```sh
|
||||
jq -n --rawfile p seed.txt '{agent:"builder",model:{providerID:"<current providerID>",modelID:"<current modelID>"},parts:[{type:"text",text:$p}]}'
|
||||
```
|
||||
|
||||
### Step 3 鈥?Verify the session actually generated (do NOT assume success)
|
||||
|
||||
HTTP 204/202 鈮?generation success. Poll until an `assistant` message appears. The
|
||||
endpoint is **`/session/:id/message` 鈥?SINGULAR**, and it returns a bare array
|
||||
whose elements are `{ info, parts }` with `role` at `.info.role`
|
||||
(`groups/session.ts:88,183-194`). Poll the **tail with `limit=1`** 鈥?the
|
||||
no-`limit` form loads the ENTIRE transcript server-side (O(messages脳parts) per
|
||||
poll; pitfall #14's 478-message session paid it on every health check), while
|
||||
`limit=1` returns just the newest message (REQ-F-010 windowing, `message.ts`
|
||||
`page()`):
|
||||
|
||||
```sh
|
||||
# wait 5鈥?0s, then poll the TAIL 鈥?never the full transcript
|
||||
curl -s "$BASE/session/$SID/message?directory=$DIR&limit=1" \
|
||||
| jq '{latest_role: .[0].info.role, latest_id: .[0].info.id}'
|
||||
```
|
||||
|
||||
Verdict: `latest_role == "assistant"` 鈫?the first generation completed (the
|
||||
seed is `user`; the newest message flips to `assistant` only once the model
|
||||
answered 鈥?an errored turn also persists an assistant message carrying the 鈿狅笍
|
||||
usage-limit part, [org-internal #2912], so check the tail text before declaring success).
|
||||
Stays `user` across 2鈥? polls spaced 10鈥?0 s 鈫?**apparent silent stall**;
|
||||
almost certainly the model did not resolve to a usable one (fallback landed
|
||||
badly, or you omitted `model`). Redeliver Step 2 with an explicit known-good
|
||||
`model`. Need everything newer than a known point? Page forward with
|
||||
`after=<cursor>&limit=N` and follow the `X-Next-Cursor` response header 鈥?
|
||||
just never omit `limit`.
|
||||
|
||||
> 鈿狅笍 Do NOT write `/messages` (plural) 鈥?that path does not exist for listing.
|
||||
> (`POST /session/:id/message`, same singular path but POST, is the synchronous
|
||||
> prompt endpoint; don't confuse the two.)
|
||||
|
||||
## Launch a session for a specific ticket (#N)
|
||||
|
||||
The common case: an agent (or cron/CI) spins up a fresh main session to work a
|
||||
tracked issue 鈥?seed prompt composed **from the issue body**, launched on the
|
||||
current session's model. **Step-0 owner check is MANDATORY ([org-internal #1803])**: four data
|
||||
sources (session-title scan, assignee/claim, open PRs, remote branches); any
|
||||
live claim 鈫?ABORT the launch (`session-scope-guard.md` points here for that
|
||||
pre-step). Full recipe 鈥?owner-check scan script with `X-Total-Count` /
|
||||
`X-Has-More` pagination handling ([org-internal #3190]), claim-first atomic 3-step ([org-internal #2297],
|
||||
+ one-command provisioning via `script/claim-provision.sh`, [org-internal #3642]),
|
||||
seed-prompt MUSTs, backend duplicate-ticket hard guard ([org-internal #1989], on by default
|
||||
[org-internal #2350]), launch + poll commands: `reference/ticket-recipe.md` (read BEFORE
|
||||
creating any ticket-driven session).
|
||||
|
||||
## Completion-report protocol (瀹屾垚鍥炴姤鍗忚, [org-internal #2374])
|
||||
|
||||
`prompt_async` is fire-and-forget **for the launcher too**: without this
|
||||
protocol the orchestrator has NO push channel and must poll forever ([org-internal #2366]).
|
||||
The worker actively reports `status=done|blocked|handoff` back into the
|
||||
orchestrator's session via `prompt_async`, with an issue-comment fallback.
|
||||
Default report format is `branch=<ref>`-based 鈥?workers never open PRs
|
||||
(TD-678/[org-internal #4425]); `pr=#N` appears only on `uncoordinated` self-opens.
|
||||
Hard constraints: a headless worker MUST NOT call the `question` tool
|
||||
([org-internal #2378] 鈥?directional/irreversible decisions are `status=blocked` reports,
|
||||
then STOP), and MUST NOT self-merge its PR / self-close the issue / touch
|
||||
`main` ([org-internal #2386] 鈥?keep the PR open; the orchestrator merges and closes). Full
|
||||
protocol 鈥?orchestrator-side seed block (verbatim template), worker-side
|
||||
steps, decision-authority layering: `reference/completion-report.md` (read
|
||||
BEFORE composing a seed prompt for any worker you need to hear back from).
|
||||
|
||||
## Post-launch lifecycle: health check, hung rescue, stale-worldview correction ([org-internal #2459])
|
||||
|
||||
Launching is half the job 鈥?a headless worker can hang silently or wake with
|
||||
a stale worldview. Health check = two polls 30鈥?0 s apart (`.state` + `progress`
|
||||
+ message count); `state=generating` with ALL counters frozen across BOTH
|
||||
samples = hung 鈫?abort FIRST (`POST /session/:id/abort`), then re-wake with a
|
||||
known-good model + the anti-re-hang clause. Stale worldview 鈫?fact-baseline
|
||||
injection (only the CURRENT authority injects; if YOU might be the stale one,
|
||||
verify identity against the durable record first). Full runbooks 鈥?verdict
|
||||
table + [org-internal #3215] dual-sample rule, abort + re-wake sequence, fact-baseline
|
||||
structure, authorization asymmetry: `reference/lifecycle-ops.md` (read when
|
||||
managing a launched session). Quota wall 鈥?the worker's turn killed by a
|
||||
terminal usage-limit error (looks like silent completion: no report, ticket
|
||||
stalled) 鈥?has its own runbook: detection signals (finish=error 鈿狅笍 tail,
|
||||
provider quota markers), provider-switch re-wake, fact-baseline template,
|
||||
pre-dispatch provider check: `reference/quota-wall-recovery.md` ([org-internal #3669], evidence
|
||||
[org-internal #3627]; read when a worker goes silent mid-task).
|
||||
|
||||
## 宸℃妯″紡 / Patrol mode (delayed self-wake, [org-internal #3937])
|
||||
|
||||
A patrol loop periodically nudges a headless session without holding a
|
||||
connection or burning a turn on a blocking sleep. Since [org-internal #3937] A the server
|
||||
supports this natively via `delay_sec` on `prompt_async`.
|
||||
|
||||
```sh
|
||||
curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
|
||||
-H 'content-type: application/json' \
|
||||
-d '{
|
||||
"agent": "builder",
|
||||
"model": { "providerID": "<current providerID>", "modelID": "<current modelID>" },
|
||||
"parts": [ { "type": "text", "text": "<patrol instruction>" } ],
|
||||
"delay_sec": 3600
|
||||
}'
|
||||
```
|
||||
|
||||
Rules of the road:
|
||||
|
||||
- **202 + `delay_sec` 鈮?queued-behind-wedge 202.** Both are 202, but the
|
||||
delayed wake holds the prompt in memory and delivers it exactly once when
|
||||
the delay elapses (session must have been idle at intake; `0` equals
|
||||
omission 鈫?immediate 204 path). If the session was NOT idle at intake, the
|
||||
intake keeps the queued semantics and `delay_sec` is ignored (logged
|
||||
server-side) 鈥?the same precedence as maintenance queue mode, where the
|
||||
intake is retained and `delay_sec` never delays the flush.
|
||||
- **Paused at wake 鈫?dropped, not stored.** The wake's `prompt()` rejects and
|
||||
the server publishes an error event; the message is never stored. A patrol
|
||||
driver should check session state before assuming delivery.
|
||||
- **Restart abandons pending wakes.** They are in-memory server fibers 鈥?no
|
||||
persistence, no drain. After any server restart the patrol driver owns
|
||||
re-arming the next wake; treat a missed wake as expected, not as an error.
|
||||
- **Latest wake wins, per session ([org-internal #4069]).** Arming `delay_sec` on a session
|
||||
that already has a pending wake SUPERSEDES it: the previous countdown is
|
||||
cancelled, only the latest wake fires. Re-arming each turn is the intended
|
||||
patrol pattern 鈥?a superseded wake never delivers its prompt and publishes
|
||||
no error. (Pre-[org-internal #4069] stacks both FIFO 鈥?on old binaries, re-arm only after
|
||||
the previous wake fired.)
|
||||
- **Never emulate a delay with the bash tool.** `sleep 900; curl ...` blocks
|
||||
the turn with zero output and gets manually aborted as a hang (the [org-internal #3937]
|
||||
incident). The bash tool now prepends a `<shell_warning>` on bare sleeps
|
||||
beyond 120s ([org-internal #3937] C) 鈥?treat that warning as a redirect to this section.
|
||||
|
||||
## Do NOT use this skill when
|
||||
|
||||
- A **persistent client** (Web UI / interactive TUI) is attached 鈥?those use the
|
||||
synchronous `prompt` endpoint (it blocks until generation completes) and send
|
||||
`model` automatically. Using `prompt_async` there gains nothing and loses the
|
||||
in-line response. This includes the rescue runbook: a hung session driven by
|
||||
a persistent client is the client user's to handle (refresh / re-prompt from
|
||||
the UI) 鈥?abort + re-wake targets headless / managed sessions only.
|
||||
- You want the response **inline** in the same HTTP call 鈥?`prompt_async` is
|
||||
fire-and-forget; use the synchronous `prompt` endpoint instead.
|
||||
|
||||
## Common pitfalls
|
||||
|
||||
| # | Pitfall | Fix |
|
||||
| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 1 | Omitting `model` / relying on the fallback 鈫?apparent silent stall (most common) | Always send a known-good `model:{providerID,modelID}` 鈥?reuse the current session's |
|
||||
| 2 | Listing endpoint written `/messages` (plural) 鈫?404 | Use `/session/:id/message` (singular); returns a bare array, role at `.info.role` |
|
||||
| 3 | Copying the session record's model verbatim into the payload | Remap field names: session `.model.id` 鈫?payload `modelID` (session uses `id`, payload uses `modelID`) |
|
||||
| 4 | Stale `octopus` binary rejects newer config schema (`Unrecognized key`) | Use the source CLI `bun run ./src/index.ts ...`; the config itself is valid |
|
||||
| 5 | Missing `?directory=` query param | In-session launch: default to the current session's directory. Cron/CI (no current session): pass the repo root explicitly 鈥?server falls back to its own `process.cwd()` |
|
||||
| 6 | Using synchronous `prompt` for headless launch | `prompt` blocks until done 鈥?wrong for fire-and-forget; use `prompt_async` |
|
||||
| 7 | Shell-escaping errors in a multi-line seed prompt | Build the payload with `jq -n --rawfile` from a file |
|
||||
| 8 | Verifying models via `GET /model` 鈥?no such endpoint (404) | Use `GET /provider` 鈫?`.connected` (provider live check); `list-models` tool also works. `GET /config/providers` lists _configured_ models only |
|
||||
| 9 | Launching a session for a ticket another live session already owns 鈫?conflicting diffs, duplicate work ([org-internal #1744]/[org-internal #1753]) | Run the Step-0 owner check (`GET /session` title scan + worktree/branch/PR scan); ABORT and report on conflict |
|
||||
| 10 | Creating a worktree / fresh checkout for the new session (unnecessary `bun install` + `.codegraph/` rebuild cost) | Don't 鈥?for _general_ launches: a session is a conversation pointed at a directory, so reuse the current workspace; worktree creation is a separate _workflow_ decision the launched session makes if/when it starts a workflow. **Ticket-recipe carve-out ([org-internal #2297]):** the ticket's work happens in a dedicated worktree anyway (worktree discipline), and claim-first pushes the workflow branch before launch 鈥?so the launcher pre-builds the worktree as the claim carrier + ready workspace, and the session reuses it (cost is front-loaded, not wasted). The worktree path goes in the claim comment + seed prompt; `?directory=` stays on the creator's workspace |
|
||||
| 11 | Launching a worker and never hearing back 鈥?orchestrator polls a few times, stops, loses visibility ([org-internal #2374]) | Append the completion-report block to the seed prompt (Completion-report protocol section): worker `prompt_async`s a one-line status back into the orchestrator session on done/blocked/handoff, with issue-comment fallback |
|
||||
| 12 | Headless worker calls the `question` tool 鈫?no attached client: the question deadlocks in a queue, or `auto_approve` silently auto-approves a direction-setting decision ([org-internal #2378]) | NEVER call `question` from a headless worker. Decision boundary (Completion-report protocol section): directional/irreversible decision 鈫?`status=blocked` report + one-line decision point, then STOP; the orchestrator is the sole human-decision entry point |
|
||||
| 13 | Headless worker self-merges its PR / self-closes the issue once the PR looks ready, despite a seed instruction to keep it open ([org-internal #2386]; N-02 [org-internal #2367] / N-03 [org-internal #2368], commits `d1565c99` / `040f21b7`) | Self-merge / self-close / touching `main` are irreversible directional actions 鈫?blocked by the Decision boundary. PR ready 鈫?keep it OPEN, report `status=done branch=<ref>`; the orchestrator opens/admits the PR, merges and closes uniformly |
|
||||
| 14 | Trusting `state=generating` as "alive" 鈥?a hung stream goes unrescued (session A, 2026-08-16: `generating` 6+ min, count frozen at 478) | Poll state AND message count twice 30鈥?0 s apart; frozen count = hung 鈫?abort + re-wake (Post-launch lifecycle section) |
|
||||
| 15 | Re-sending a wake prompt to a hung session and waiting 鈥?queued prompts are never consumed while the stream is dead | Abort FIRST (`POST /session/:id/abort` 鈫?200, verify `idle`), THEN re-send; a prompt queued before the abort may still never fire 鈥?always re-deliver |
|
||||
| 16 | A worker woken from restart / long idle acts on its stale worldview 鈥?re-dispatches superseded work or claims authority it no longer holds (2026-08-16: revived worker re-ran an already-published FAIL verify; ops session announced an inherited "orchestrator" takeover) | Fact-baseline injection (Post-launch lifecycle section): authoritative facts + invalidated assumptions + one concrete re-assignment. Only the CURRENT authority injects; a session that suspects IT is stale verifies identity against the durable record first (`rules/compact.md` identity clause) |
|
||||
| 17 | Worker goes silent mid-task: provider quota wall killed the turn (usage limit / 429) 鈥?no completion-report, no blocked report; from the issue side indistinguishable from silent completion (2026-08-29 [org-internal #3627]: opencode-go `weekly usage limit reached` after the 13:18 Round-1 FAIL; ticket stalled until manually re-woken) | Detect via tail probe (`finish=error` + 鈿狅笍 usage-limit part, `metadata.reason` in the quota family) + `GET /provider` quota markers; re-wake on a known-good provider with the fact-baseline template 鈥?`reference/quota-wall-recovery.md` ([org-internal #3669]). Prevent: pre-dispatch provider check + the seed's 閰嶉鑷姤 clause (`reference/completion-report.md`, [org-internal #3669]) 鈥?quota exhaustion is a `status=blocked reason=quota-exhausted` report, not a silent death |
|
||||
|
||||
## Known limitation (separate from this workaround)
|
||||
|
||||
A missing `model` producing an **apparent silent stall** (no surfaced error, no
|
||||
generation) is an observability / robustness gap. Current code does NOT
|
||||
hard-require `model` 鈥?it fills it via a fallback chain (`prompt.ts:858`) and any
|
||||
resolution failure surfaces as a published `Session.Event.Error` on the
|
||||
synchronous path (`prompt.ts:804-822`), but the **async** `prompt_async` handler
|
||||
catches that cause (`forkPromptDelivery` in `handlers/session.ts`) so the HTTP
|
||||
caller never sees it. The proper fix is to surface the error (or reject unusable
|
||||
models at the boundary) so a stalled session is diagnosable. Until then, **always
|
||||
provide an explicit known-good `model`** 鈥?this skill is the documented
|
||||
workaround (see [org-internal #1695] "閬楃暀 minor").
|
||||
|
||||
**Update ([org-internal #2912], 2026-08-20)**: usage-limit turn failures (HTTP 429 /
|
||||
FreeUsageLimitError / GoUsageLimitError) are no longer silent shells. When such
|
||||
a turn terminates (retry schedule ends or the spin is aborted), the errored
|
||||
assistant message persists `finish=error` plus a human-readable 鈿狅笍 text part
|
||||
carrying provider, limit reason, and reset hint (`metadata: { error: true,
|
||||
reason, provider }`) 鈥?visible via `GET /session/:id/message` and rendered in
|
||||
the web UI like any assistant text. Pitfall #14's double-poll is still the
|
||||
liveness check while a 429 retry spin is IN progress (the spin itself retries
|
||||
with backoff until interrupted); the explicit error part only appears once the
|
||||
turn ends. The general async-path observability gap above remains tracked by
|
||||
[org-internal #1695].
|
||||
|
||||
## See also
|
||||
|
||||
- Source issue / full manual: **[org-internal #1695]** (this skill adds the ticket-driven recipe
|
||||
- current-model reuse, and corrects the endpoint/claim against current code).
|
||||
- Completion-report protocol: **[org-internal #2374]** (worker-side active report back to the
|
||||
launching orchestrator session; fallback to issue comment).
|
||||
- Quota-wall recovery recipe: **[org-internal #3669]** (evidence instance [org-internal #3627]) 鈥?detect a
|
||||
worker killed by a provider usage limit, re-wake on a known-good provider,
|
||||
pre-dispatch prevention.
|
||||
- Decision boundary: **[org-internal #2378]** (headless worker MUST NOT call `question`;
|
||||
directional decisions go through `status=blocked` reports to the
|
||||
orchestrator) and **[org-internal #2386]** (the worker MUST NOT self-merge its PR,
|
||||
self-close the issue, or touch `main` 鈥?the orchestrator merges and closes
|
||||
uniformly).
|
||||
- Correction of the prior misdiagnosis: **[org-internal #1691]** (closed Invalid), comment 9205.
|
||||
- Synchronous client always sends `model`:
|
||||
`packages/app/src/components/prompt-input/submit.ts:158-165`.
|
||||
- Code citations: `<harness-package>/src/session/prompt.ts:858,804-822,1399`,
|
||||
`handlers/session.ts` `forkPromptDelivery` (async failure handling),
|
||||
`server/routes/instance/httpapi/groups/session.ts`.
|
||||
|
||||
## References
|
||||
|
||||
**On-demand references** (NOT injected 鈥?read at the declared timing):
|
||||
|
||||
- `reference/ticket-recipe.md` 鈥?ticket-driven launch (`Launch a session for a
|
||||
specific ticket (#N)`): read BEFORE creating any session for issue #N 鈥?
|
||||
Step-0 owner check, seed-prompt composition, launch + poll commands.
|
||||
- `reference/completion-report.md` 鈥?completion-report protocol (瀹屾垚鍥炴姤鍗忚,
|
||||
[org-internal #2374]): read BEFORE composing a seed prompt for a worker you need to hear
|
||||
back from 鈥?orchestrator block template, worker-side report steps, decision
|
||||
boundary ([org-internal #2378] / [org-internal #2386]), quota self-report clause ([org-internal #3669]).
|
||||
- `reference/lifecycle-ops.md` 鈥?post-launch lifecycle ([org-internal #2459]): read when
|
||||
managing a launched session 鈥?health-check polling, hung-stream diagnosis
|
||||
(state=generating + frozen counters), abort + re-wake rescue,
|
||||
fact-baseline injection, authorization asymmetry.
|
||||
- `reference/quota-wall-recovery.md` 鈥?quota-wall recovery ([org-internal #3669], evidence
|
||||
[org-internal #3627]): read when a worker goes silent mid-task (suspected provider usage
|
||||
limit) or BEFORE dispatching a long-running worker 鈥?detection signals
|
||||
(finish=error 鈿狅笍 tail, provider quota markers, log grep patterns),
|
||||
provider-switch re-wake + fact-baseline template, pre-dispatch provider
|
||||
check, quota self-report clause.
|
||||
|
||||
- `rules/headless-session-ops` wiki page 鈥?authoritative reference (this
|
||||
skill is its runtime carrier; link in the Directory-convention note above).
|
||||
- `core/rules/session-scope-guard.md` 鈥?owner-check + claim-first rule
|
||||
(the ticket recipe's Step 0 is its launch-time application).
|
||||
- `core/rules/compact.md` 鈥?identity-verification clause backing the
|
||||
authorization-asymmetry rule (`reference/lifecycle-ops.md`).
|
||||
@@ -0,0 +1,173 @@
|
||||
> Extracted from headless-session-ops/SKILL.md (Completion-report protocol (完成回报协议, [org-internal #2374])) — moved verbatim 2026-08-26, ticket [org-internal #3480].
|
||||
|
||||
## Completion-report protocol (完成回报协议, [org-internal #2374])
|
||||
|
||||
`prompt_async` is fire-and-forget **for the launcher too**: once the worker
|
||||
session starts generating, the orchestrator receives NO event when it finishes
|
||||
or blocks. The `task` tool auto-returns `task_result` for subagents; a
|
||||
`prompt_async`-launched independent main session has no equivalent — the
|
||||
orchestrator's only built-in channel is bare polling of
|
||||
`GET /session/:id/message?limit=1` (tail probe — the no-`limit` form loads the
|
||||
whole transcript server-side), which it eventually stops doing ([org-internal #2366]: worker ran
|
||||
~1h past the orchestrator's last poll, merged PR [org-internal #2371] and closed [org-internal #2366] while
|
||||
the orchestrator had zero visibility).
|
||||
|
||||
The protocol below closes that gap WITHOUT changing server semantics: the
|
||||
**worker** actively posts a completion/block message back into the
|
||||
orchestrator's session via `prompt_async`. Fire-and-forget launch is
|
||||
unchanged; the report is a worker-side obligation, not a new server dependency.
|
||||
|
||||
### Orchestrator side — pass your SID in the seed prompt
|
||||
|
||||
When launching a worker that you need to hear back from (DAG node execution,
|
||||
parallel chunk work, any long-running delegated ticket), append a
|
||||
completion-report block to the seed prompt. It MUST state:
|
||||
|
||||
- the orchestrator's **session id** (`$LAUNCHER_SID`) and **directory**;
|
||||
- the report endpoint: `POST $BASE/session/$LAUNCHER_SID/prompt_async?directory=$DIR` (or, when the directory is unknown to the worker, the global route `POST $BASE/prompt_async` with the sessionID in the body — [org-internal #4307]);
|
||||
- the exact report format (one-line, machine-greppable);
|
||||
- the fallback (issue comment) if the report call fails.
|
||||
|
||||
Template (include verbatim in `seed.txt` when you are the launcher):
|
||||
|
||||
```text
|
||||
## 完成回报协议 (mandatory, [org-internal #2374])
|
||||
|
||||
你由编排会话 (orchestrator) 启动。完成、阻塞、或将工单移交他人时,必须向编排会话回报:
|
||||
POST <BASE>/session/<LAUNCHER_SID>/prompt_async?directory=<DIR>
|
||||
body: {"agent":"builder","model":{...},"parts":[{"type":"text","text":"<report>"}]}
|
||||
<report> 格式(单行,machine-greppable):
|
||||
[#<ticket>] [<node-or-slug>] status=done|blocked|handoff branch=<ref> [pr=#<N>] verify=<changed+typecheck> risk=<high|low> summary=<one line>
|
||||
默认(TD-678 / [org-internal #4425]):worker 只推分支不开 PR——交付推送前置本地验证
|
||||
(<harness-package> 下 test:changed 全绿 + typecheck 0 error,verify 字段回执);
|
||||
risk 按 risk-classifier 冻结表(HIGH_RISK_GLOBS)自报。PR 由编排按容量串行开
|
||||
1:1(正文携 worker 报告),或 batch 模式 epic 经 land-batch 技能按迭代统一合成
|
||||
(见 core/skills/land-batch/)。pr=#<N> 仅限编排不可达自开(uncoordinated)。
|
||||
若回报调用失败(HTTP 非 2xx / 连接拒绝 — orchestrator 会话可能已结束或实例重启),
|
||||
退而在源 issue(或父 Epic)发同内容评论作为兜底(fallback, 非主通道)。
|
||||
回报是你的 mandatory step,与 issue-checklist-sync 并列;不得静默结束。
|
||||
决策边界([org-internal #2378]):headless worker 禁止调用 `question` 工具 —— 遇方向性/不可逆
|
||||
决策点 → `status=blocked` + 一行决策点描述后 STOP,勿调 question(无人应答死锁 /
|
||||
被 auto_approve 静默自动批准)。
|
||||
配额自报([org-internal #3669]):若收到配额/用量上限错误(usage limit / 429 / 402
|
||||
insufficient_quota),立即回报 status=blocked reason=quota-exhausted
|
||||
summary=<provider 与 ⚠️ 部分中的重置提示>(branch=<ref> 必带)后停止
|
||||
——勿自行换模型重试;换供应商重唤醒由编排者执行
|
||||
(reference/quota-wall-recovery.md)。
|
||||
PR 就绪后保持 open:不自行合并 PR、不关闭 issue、不动 main([org-internal #2386]);
|
||||
合并与 issue 关闭由编排者统一执行。
|
||||
```
|
||||
|
||||
The orchestrator discovers its own `$LAUNCHER_SID` from context (it is the
|
||||
session the orchestrator IS — e.g. the SID it already used for the
|
||||
`GET /session/$LAUNCHER_SID` model lookup in the recipe above) or from its
|
||||
session record.
|
||||
|
||||
### Worker side — report on completion / block / handoff
|
||||
|
||||
A session whose seed prompt contains the completion-report block MUST, as its
|
||||
final step (before idling):
|
||||
|
||||
1. **Report** by `prompt_async` into the orchestrator session, with the
|
||||
one-line format above. Include `branch=<ref>` (plus `verify=`/`risk=` on
|
||||
done); `pr=#N` only for an `uncoordinated` self-open; use
|
||||
`status=blocked` + the blocker description when stuck; use
|
||||
`status=handoff` when deliberately transferring the ticket.
|
||||
1. **Quota exhaustion is a blocked report, not a silent death ([org-internal #3669]).** A
|
||||
worker whose turn is killed by a usage-limit error (429 / 402 quota
|
||||
family) cannot finish the turn — but per the seed's 配额自报 clause it
|
||||
reports `status=blocked reason=quota-exhausted summary=<provider +
|
||||
reset hint>` (with `branch=<ref>`) BEFORE stopping
|
||||
(issue-comment fallback if the orchestrator session is unreachable).
|
||||
The orchestrator then runs the provider-switch recovery in
|
||||
`reference/quota-wall-recovery.md` instead of discovering the wall by
|
||||
polling.
|
||||
2. **On failure** of that POST (non-2xx / connection refused — the
|
||||
orchestrator may have ended or the instance restarted), fall back to a
|
||||
`工单评论 API(见 TERMINOLOGY)` on the source issue / parent Epic carrying
|
||||
the same one-line report. The issue comment is the durable record of last
|
||||
resort, NOT the primary channel — the orchestrator does not watch issues in
|
||||
real time.
|
||||
3. The report is **additive** to existing obligations (issue checklist sync,
|
||||
`## 当前状态` updates, archive-at-close) — it never replaces them. The issue
|
||||
remains the stakeholder-facing record; the report is the
|
||||
orchestrator-facing wake-up signal.
|
||||
|
||||
### Orchestrator side — serial PR admission on done reports ([org-internal #4425])
|
||||
|
||||
On a `status=done branch=<ref> verify=… risk=…` report:
|
||||
|
||||
1. Enqueue the ticket; admit PRs serially per `ticket-lifecycle.md` §PR
|
||||
准入 — one open PR at a time, next only after double-green merge. Branch
|
||||
rot is bounded by the existing keep-mergeable/syncMain machinery, not new
|
||||
code; keep the queue shallow.
|
||||
2. Open the 1:1 PR (title from the ticket node, body carrying the worker
|
||||
report: branch / files / self-test / verify), apply the `Risk/*` label
|
||||
from the report's `risk=` hint, merge via `script/pr-merge.sh` ([org-internal #3864]).
|
||||
Batch-mode epics: one `land-batch` PR per iteration instead ([org-internal #3731]).
|
||||
3. A first red on the opened PR goes back through the report fallback
|
||||
channel (issue comment) — the worker may already be idle; the issue
|
||||
comment is the durable re-entry point.
|
||||
|
||||
Prose discipline on the orchestrator session only — no new daemon, no
|
||||
mergeable-state automation (merge-coordinator retired, [org-internal #4385]).
|
||||
|
||||
### Decision boundary (决策边界, [org-internal #2378])
|
||||
|
||||
The report channel defines how the worker *answers*; this subsection defines
|
||||
the boundary of what it may *ask*. A headless worker session has NO attached
|
||||
client (no Web UI / TUI), so calling the `question` tool has exactly two
|
||||
outcomes, both structurally broken:
|
||||
|
||||
- the question sits in a queue nobody answers → the session deadlocks;
|
||||
- or it is silently auto-approved by `auto_approve` → a direction-setting
|
||||
decision gets decided by a default value, violating the human-gate
|
||||
semantics of the review gates.
|
||||
|
||||
**Rule: a headless worker MUST NOT call the `question` tool.** Decision
|
||||
authority is layered instead:
|
||||
|
||||
- **Directional / irreversible decisions** (scope changes, mid-stream
|
||||
reclassification — big-bug relabel / DAG re-derivation, merge timing,
|
||||
contract changes, approach selection) → the
|
||||
worker reports `status=blocked` with a one-line description of the decision
|
||||
point, then STOPs. The orchestrator is the sole human-decision entry point:
|
||||
it interacts with the user, then replies to the worker (or acts on its
|
||||
behalf).
|
||||
- **No self-merge / no self-close / never touch main ([org-internal #2386])**: a headless
|
||||
worker MUST NOT merge its own PR, close the source issue, or rebase /
|
||||
force-push `main`. These are irreversible directional actions of exactly
|
||||
the class this boundary blocks — even when the seed prompt's instruction
|
||||
to "keep the PR open" is absent or the worker judges the PR ready. When
|
||||
the PR is ready the worker keeps it OPEN and reports
|
||||
`status=done pr=#N` to the orchestrator; the orchestrator performs the
|
||||
merge and the issue close uniformly. (Counter-example: N-02 [org-internal #2367] /
|
||||
N-03 [org-internal #2368] self-merged their PRs — commits `d1565c99` / `040f21b7` —
|
||||
despite a seed instruction to keep them open.)
|
||||
- **Local implementation decisions within the AC scope** (pattern choice,
|
||||
helper extraction, test shape) → the worker decides autonomously and notes
|
||||
the choice in its report / PR body.
|
||||
|
||||
Rule of thumb: the worker's only way to "ask" is a `status=blocked` report.
|
||||
A genuine ambiguity left by the seed prompt that would change scope is a
|
||||
blocked report, NOT a `question` call. (Field sample: N-04 [org-internal #2369] — the
|
||||
worker spontaneously did NOT merge its own PR and left merging to the
|
||||
orchestrator; this subsection codifies that behavior.)
|
||||
|
||||
### Semantics preserved
|
||||
|
||||
- **Fire-and-forget launch is unchanged**: Step 1–3 above are identical; the
|
||||
orchestrator still gets no server push at launch time.
|
||||
- **No server change**: the report reuses the existing `prompt_async` endpoint
|
||||
against the orchestrator's SID. Nothing new is required of the backend.
|
||||
- **Polling stays as fallback**: an orchestrator that never got a report can
|
||||
still poll `/session/:id/message`; the protocol removes the *need* to poll
|
||||
forever, not the ability.
|
||||
|
||||
### (Optional / future) server-side callback
|
||||
|
||||
Longer-term, `prompt_async` could accept a `callback_session_id` so the server
|
||||
itself posts an event to the callback session when the run loop terminates —
|
||||
removing the prompt-level manual protocol. That is a separate platform
|
||||
enhancement and intentionally NOT in this ticket's scope; the manual protocol
|
||||
above works today and remains compatible with a future automatic callback.
|
||||
@@ -0,0 +1,106 @@
|
||||
> Extracted from headless-session-ops/SKILL.md (Post-launch lifecycle: health check, hung rescue, stale-worldview correction ([org-internal #2459])) — moved verbatim 2026-08-26, ticket [org-internal #3480].
|
||||
|
||||
## Post-launch lifecycle: health check, hung rescue, stale-worldview correction ([org-internal #2459])
|
||||
|
||||
Launching is half the job — a headless worker can hang silently or wake up
|
||||
with an outdated worldview. All three failure modes below were observed in
|
||||
production on 2026-08-16 during Epic [org-internal #2459] (ops-restart window); the runbooks
|
||||
are extracted from that incident record.
|
||||
|
||||
### Health check — three states, one poll pattern
|
||||
|
||||
Poll two signals together — `GET /session?directory=` for `.state` (plus the
|
||||
per-session `progress` object when present), and `GET
|
||||
/session/:id/message?limit=1` for the newest message's `.info.id` — **twice,
|
||||
spaced 30–60 s**. `MessageID` is monotonic (`MessageID.ascending`,
|
||||
`message.ts`), so a frozen newest-id across both samples is exactly the old
|
||||
"message count frozen" signal at O(1) per poll instead of O(transcript) —
|
||||
never poll the no-`limit` form for liveness, it loads the whole transcript
|
||||
server-side:
|
||||
|
||||
| Verdict | Signals | Action |
|
||||
|---|---|---|
|
||||
| healthy | `state=generating` AND (`progress.stepCount` growing OR newest message id advancing) | leave it alone |
|
||||
| idle | `state=idle` AND `progress` absent-or-stale across both polls | turn ended — read the LAST message (same `limit=1` fetch): task unfinished → wake prompt; `status=done` report → harvest |
|
||||
| **hung** | `state=generating` AND `progress.stepCount`/`lastStepAt` AND newest message id ALL frozen across both polls | the generation stream is dead; queued prompts will NEVER be consumed — rescue required |
|
||||
|
||||
`state` alone lies in BOTH directions ([org-internal #3215]): a hung session still reports
|
||||
`generating` (field sample: session A, 2026-08-16 — `state=generating` for 6+
|
||||
minutes with the message count frozen at 478), and a healthy mid-step worker
|
||||
can read `idle`/empty message tails in a single snapshot (2026-08-23 W3 wave:
|
||||
4 healthy workers aborted off one snapshot). **The dual-sample delta is the
|
||||
discriminator; a single snapshot is NEVER an abort basis.**
|
||||
|
||||
`progress` (`stepCount` cumulative LLM-round counter, `lastStepAt` epoch-ms
|
||||
heartbeat — [org-internal #3215]) moves on every round even when message tails are
|
||||
transiently empty or `state` flickers; it is absent for sessions that never
|
||||
ran since instance start (treat absent = no signal, fall back to count
|
||||
deltas). `lastStepAt` freshness alone does NOT prove liveness (a long tool
|
||||
call inside one round keeps it stale for minutes) — always compare TWO
|
||||
samples spaced ≥30 s.
|
||||
|
||||
### Hung rescue — abort, then re-wake (in this order)
|
||||
|
||||
```sh
|
||||
# 1. Abort the dead stream (queued-but-unconsumed prompts do NOT unblock it)
|
||||
curl -s -X POST "$BASE/session/$SID/abort?directory=$DIR" -o /dev/null -w "%{http_code}\n" # → 200
|
||||
# 2. Verify idle
|
||||
curl -s "$BASE/session?directory=$DIR" | jq -r --arg s "$SID" '.[] | select(.id==$s) | .state' # → idle
|
||||
# 3. Check the worktree — the hung turn may have left uncommitted files (NOT lost)
|
||||
git -C <worktree> status --short
|
||||
# 4. Re-deliver the wake prompt (same Iron Law: known-good model).
|
||||
# A prompt queued BEFORE the abort may still never fire — always re-send.
|
||||
```
|
||||
|
||||
Endpoint: `POST /session/:sessionID/abort` (`groups/session.ts:113`,
|
||||
`handlers/session.ts:311`). Abort stops the run loop; filesystem writes the
|
||||
hung turn already made survive — inspect the worktree and list any recovered
|
||||
files IN the wake prompt so the worker re-validates them instead of redoing
|
||||
work (field sample: session A's hung turn had produced 3 src + 1 test file
|
||||
that its revived self adopted).
|
||||
|
||||
**Anti-re-hang clause — include in every wake prompt.** The trigger for the
|
||||
observed hang was a system-injected "请在适当的时机压缩当前会话" (compact at an
|
||||
appropriate time): the worker ended its turn after compacting, leaving the
|
||||
task half-done and idle. A wake prompt MUST carry, verbatim:
|
||||
|
||||
```text
|
||||
若系统再注入「请在适当的时机压缩当前会话」:执行压缩后立即在后续 turn 继续任务,
|
||||
绝不在任务未完成时以 idle 结束。
|
||||
```
|
||||
|
||||
### Stale worldview — fact-baseline injection (correct BEFORE it acts)
|
||||
|
||||
A worker woken after an ops restart / long idle carries the worldview it went
|
||||
to sleep with. It may re-dispatch superseded work, overwrite newer state, or
|
||||
claim authority it does not hold (field samples, 2026-08-16: a revived worker
|
||||
re-ran an M-01 verify another session had already published as FAIL —
|
||||
deduplicated via flag #comment-21590; an ops-notification session inherited an
|
||||
"orchestrator" identity from a compaction summary and announced a takeover —
|
||||
corrected via 勘误 #comment-21654/[org-internal #21668]).
|
||||
|
||||
When you detect a stale-worldview session, do not wait for it to finish being
|
||||
wrong — inject a fact-baseline prompt immediately (regular `prompt_async`),
|
||||
structured as:
|
||||
|
||||
1. **You were woken; your worldview is stale** — name the event (restart /
|
||||
maintenance window) and the current time.
|
||||
2. **Authoritative state** — numbered facts with artifact links (wiki page,
|
||||
issue comment), each with its timestamp; state explicitly which of the
|
||||
recipient's standing assumptions are now INVALID.
|
||||
3. **Your actual assignment now** — one concrete task (or explicit standby).
|
||||
4. **Evidence rule** — verify each fact at its cited source before acting;
|
||||
never act on this baseline alone.
|
||||
|
||||
(Field sample: the 2026-08-16 injection to session B pivoted it from the
|
||||
superseded M-01 verify to the N-04b fix within one turn — the format works.)
|
||||
|
||||
### Authorization asymmetry — read this BEFORE "correcting" anyone
|
||||
|
||||
Only the session that CURRENTLY holds the authority may inject a baseline or
|
||||
re-task a worker. If YOU might be the stale one — you woke from a restart,
|
||||
your context came from a compaction summary, you cannot find your claim in
|
||||
the durable record — assume YOU are stale: verify your identity/authority
|
||||
against the record (issue assignee, claim comment, orchestrator session id)
|
||||
BEFORE issuing any instruction. See the identity-verification clause in
|
||||
`core/rules/compact.md` (recovery contract).
|
||||
@@ -0,0 +1,226 @@
|
||||
> Added from headless-session-ops/SKILL.md (triggers `quota wall` / `配额墙`,
|
||||
> Post-launch lifecycle section, pitfall #17, References) — new runbook
|
||||
> 2026-08-29, ticket [org-internal #3669] (evidence instance [org-internal #3627]). Companion runbooks:
|
||||
> `lifecycle-ops.md` (hung rescue, fact-baseline structure) and
|
||||
> `completion-report.md` §"Orchestrator side" (the quota self-report clause);
|
||||
> §Prevention is this file's own closing section.
|
||||
|
||||
## Quota wall — worker killed mid-task by a provider usage limit
|
||||
|
||||
A **quota wall** is when a headless worker's turn is killed by a terminal
|
||||
provider-quota error (usage limit / 429 family) mid-task. From the issue side
|
||||
it looks EXACTLY like silent completion: no completion-report, no
|
||||
`status=blocked` report, the ticket just stops moving. The stalled window
|
||||
equals the orchestrator's polling interval, and without this runbook the
|
||||
recovery was improvised.
|
||||
|
||||
**Evidence instance ([org-internal #3627], 2026-08-29)** — the shapes below are field-verified:
|
||||
|
||||
|时刻 (+08)| 事件 |
|
||||
|---|---|
|
||||
| 08-29 13:18 | review-code Round-1 synthesis (FAIL) posted on [org-internal #3627]; worker session `ses_fb46e705bffe9kYbSoHayoJ1OQ` (builder, opencode-go model, branch `workflow/session/3627-wedge-promptasync`) entered its revision loop |
|
||||
| ~13:18–16:00 | worker hit the provider's `weekly usage limit reached` mid-revision — turn killed, **no** completion-report, **no** blocked report; issue side silent |
|
||||
| detection | orchestrator noticed the `/session` state stall, then confirmed via the quota error in the session log (message tail carries the ⚠️ usage-limit part, [org-internal #2912]) |
|
||||
| recovery | re-wake via `prompt_async` on a known-good provider (`zhipuai-coding-plan`/glm-5.3 — since renamed `zai-coding-plan`) **plus a fact-baseline injection** (what happened / what is done / where to resume) |
|
||||
| 16:26 / 16:51 | Round-2 synthesis PASS; `status=done pr=[org-internal #3675]` report received |
|
||||
|
||||
## Detection — three signals, in this order
|
||||
|
||||
Set the usual variables first:
|
||||
|
||||
```sh
|
||||
BASE=http://127.0.0.1:4096 # dev backend (:4180 prod)
|
||||
DIR=/data/octopus
|
||||
SID=<worker session id>
|
||||
```
|
||||
|
||||
**Signal 1 — tail probe (primary; survives restarts, lives in the DB).** Since
|
||||
[org-internal #2912]/[org-internal #3190] a quota-killed turn persists an assistant message with
|
||||
`finish=error` plus a human-readable ⚠️ text part whose metadata names the
|
||||
quota family. Probe the TAIL (`limit=1`, never the no-`limit` full transcript):
|
||||
|
||||
```sh
|
||||
curl -s "$BASE/session/$SID/message?directory=$DIR&limit=1" | jq '
|
||||
{role: .[0].info.role, finish: .[0].info.finish,
|
||||
error_parts: [.[0].parts[]? | select(.type=="text" and .metadata?.error == true)
|
||||
| {text: .text[0:160], metadata: .metadata}]}'
|
||||
```
|
||||
|
||||
Quota-wall verdict: `role=assistant` AND `finish=error` AND an error part with
|
||||
`metadata.reason` in `{account_rate_limit, free_tier_limit, account_usage_limit,
|
||||
rate_limit}` (metadata also carries `provider`). The ⚠️ text is one of — all
|
||||
strings observed in this repo's runtime (`session/retry.ts`):
|
||||
|
||||
- `` `weekly usage limit` reached. It will reset in … `` / `Go limit reached`
|
||||
(GoUsageLimitError — the [org-internal #3627] shape; `weekly` is the body's `limitName`)
|
||||
- `Free limit reached` (FreeUsageLimitError)
|
||||
- `Account-level usage limit — switching API keys does not reset it. You can
|
||||
continue after HH:mm.` ([org-internal #3407], zhipu/zai code 1308 — account-level)
|
||||
- `Provider <id> rate limited (HTTP 429)` (generic 429)
|
||||
- `Generation failed — …` with a 402 `insufficient_quota` body (balance, not
|
||||
window — recovery is top-up, not provider switch)
|
||||
|
||||
**Signal 2 — state (two shapes; use the lifecycle-ops dual-sample rule).**
|
||||
|
||||
```sh
|
||||
curl -s "$BASE/session?directory=$DIR" | jq -r --arg s "$SID" \
|
||||
'.[] | select(.id==$s) | .state'
|
||||
```
|
||||
|
||||
- **idle** + Signal-1 tail → the turn already died on the quota error. No
|
||||
abort needed; go straight to Recovery.
|
||||
- **generating** with `progress`/newest-message-id frozen across two polls
|
||||
30–60 s apart → the 429 retry spin is still running (it retries with backoff
|
||||
until the schedule ends) OR the stream is hung — treat exactly like the hung
|
||||
shape in `lifecycle-ops.md`: abort FIRST, then re-wake. A single snapshot is
|
||||
never an abort basis ([org-internal #3215]).
|
||||
|
||||
**Signal 3 — provider-level confirmation ([org-internal #2911] quota markers).** `GET
|
||||
/provider` overlays active usage-limit cooldowns, so the dead provider is
|
||||
visible without reading logs:
|
||||
|
||||
```sh
|
||||
curl -s "$BASE/provider?directory=$DIR" | jq '[.all[] | select(.quota) | {id, quota}]'
|
||||
# quota: {markedAt: <epoch-ms>, resetAt?: <epoch-ms>} — resetAt absent = reset unknown
|
||||
date -d @$(($(curl -s "$BASE/provider?directory=$DIR" | jq '[.all[].quota.resetAt // 0] | max') / 1000)) # earliest full-reset wall clock
|
||||
```
|
||||
|
||||
**Log grep (fallback; the dev log is restart-truncated).** Patterns actually
|
||||
seen in the wild — [org-internal #3627] produced the first:
|
||||
|
||||
```sh
|
||||
grep -E "usage limit reached|Go limit reached|Free limit reached|Account-level usage limit|GoUsageLimitError|FreeUsageLimitError|rate limited \(HTTP 429\)|insufficient_quota" \
|
||||
/tmp/octopus-backend.log | tail -20
|
||||
# the pool-failover WARN precedes the wall when same-name pools exist:
|
||||
# "Usage limit reached on <provider> — retrying <model> on <provider>"
|
||||
```
|
||||
|
||||
The wall is only terminal after failover has exhausted the same-name pools
|
||||
([org-internal #2911] failover); the `retrying … on …` WARNs above tell you the pool was
|
||||
already draining before it died.
|
||||
|
||||
## Recovery — abort (only if needed), re-wake on a known-good provider, inject facts
|
||||
|
||||
**Step 0 — classify the state (Signal 2).** `idle` → skip abort.
|
||||
`generating` + frozen across dual samples → abort first, exactly per
|
||||
`lifecycle-ops.md` (queued prompts are never consumed by a dead stream):
|
||||
|
||||
```sh
|
||||
curl -s -X POST "$BASE/session/$SID/abort?directory=$DIR" -o /dev/null -w "%{http_code}\n" # → 200
|
||||
curl -s "$BASE/session?directory=$DIR" | jq -r --arg s "$SID" '.[] | select(.id==$s) | .state' # → idle
|
||||
```
|
||||
|
||||
**Step 1 — inventory surviving work.** The killed turn's filesystem writes
|
||||
survive; list them so the worker re-validates instead of redoing:
|
||||
|
||||
```sh
|
||||
git -C <worktree> status --short
|
||||
git -C <worktree> log --oneline -3
|
||||
```
|
||||
|
||||
**Step 2 — pick a known-good provider/model.** The re-wake payload MUST carry
|
||||
an explicit `model` (SKILL.md Iron Law), and after a quota wall the CURRENT
|
||||
provider is by definition NOT known-good. Pick a provider that is connected
|
||||
AND not quota-marked:
|
||||
|
||||
```sh
|
||||
curl -s "$BASE/provider?directory=$DIR" | jq -r \
|
||||
'.connected as $c | [.all[] | select(.id as $id | $c | index($id)) | select(.quota | not) | .id] | join("\n")'
|
||||
# then pick a modelID you know works on that provider (e.g. the orchestrator's
|
||||
# own current model — it is generating by definition).
|
||||
```
|
||||
|
||||
([org-internal #3627]: opencode-go dead → re-woken on zhipuai-coding-plan/glm-5.3, since
|
||||
renamed `zai-coding-plan`. Do NOT "wait for reset" as the default — weekly
|
||||
windows mean days.)
|
||||
|
||||
**Empty list — every connected provider is quota-marked.** The loop needs an
|
||||
explicit exit; never re-wake into a wall you can see. Find the earliest reset
|
||||
among the marked connected providers:
|
||||
|
||||
```sh
|
||||
curl -s "$BASE/provider?directory=$DIR" | jq -r \
|
||||
'.connected as $c
|
||||
| [.all[] | select(.id as $id | $c | index($id)) | select(.quota)]
|
||||
| min_by(.quota.resetAt // 9007199254740991) # resetAt absent → sorts last
|
||||
| "\(.id): markedAt=\(.quota.markedAt) resetAt=\(.quota.resetAt // "unknown")"'
|
||||
# resetAt is epoch-ms → wall clock: date -d @$((<resetAt> / 1000))
|
||||
```
|
||||
|
||||
- Known `resetAt` within reach → suspend the re-wake (leave the worker down)
|
||||
and re-run this Step after the reset; weekly windows mean days — schedule
|
||||
the retry, do not poll in a tight loop.
|
||||
- `resetAt` unknown, or the earliest window is unacceptable (402
|
||||
`insufficient_quota` is balance — reset means top-up, not time) →
|
||||
**escalate to the human orchestrator**: report every marked provider's
|
||||
`id`/`markedAt`/`resetAt` and stop. Top-up or onboarding a new provider is
|
||||
a human decision, not another re-wake.
|
||||
|
||||
**Step 3 — re-wake with a fact-baseline prompt.** Build the payload from a file
|
||||
(`jq --rawfile`, SKILL.md pitfall #7), same wake shape as
|
||||
`lifecycle-ops.md` but with the quota facts. The template also restates the
|
||||
completion-report protocol ([org-internal #2374]) so the recovery is self-contained: it
|
||||
assumes the original seed still contains the protocol block — if you are
|
||||
unsure it does (e.g. the worker was dispatched before [org-internal #3669] added the quota
|
||||
clause), the template's own block below carries it; fill
|
||||
`<BASE>`/`<LAUNCHER_SID>`/`<DIR>` with the orchestrator session's values:
|
||||
|
||||
```sh
|
||||
cat > wake.txt <<'EOF'
|
||||
## 事实基线(配额墙恢复)
|
||||
你上一回合被供应商配额错误杀死,世界观停留在被杀时刻。先读以下事实再行动:
|
||||
1. 发生了什么:<HH:mm> 你的回合因 <provider> 配额耗尽终止(usage limit,
|
||||
预计重置 <HH:mm 或 未知>);本消息由已知良好供应商重新唤醒,非你自行恢复。
|
||||
2. 已做什么:<已完成步骤/commit/PR 状态>;worktree 未提交变更:
|
||||
<git status --short 摘要>——先 re-validate 这些文件,勿重做已完成工作。
|
||||
3. 从哪继续:<一件具体的下一步任务>。
|
||||
4. 证据规则:逐条到引用来源(issue 评论 / PR / 工件)核实后再行动,
|
||||
勿仅凭本基线行动。
|
||||
## 完成回报协议(重申 [org-internal #2374]/[org-internal #3669];不确定原始 seed 是否含协议块时以本节为准)
|
||||
完成、阻塞或移交时,必须回报编排会话(你的唤醒者):
|
||||
POST <BASE>/session/<LAUNCHER_SID>/prompt_async?directory=<DIR>
|
||||
body: {"agent":"builder","model":{...},"parts":[{"type":"text","text":"<report>"}]}
|
||||
<report> 单行格式:[#<ticket>] [<node-or-slug>] status=done|blocked|handoff pr=#<N> summary=<一行>
|
||||
约束:若再遇配额/用量上限错误,立即回报 status=blocked
|
||||
reason=quota-exhausted summary=<provider 与重置提示>(pr=#<N> 有 PR 时带上)
|
||||
后停止,勿自行换模型重试;回报失败 → 源 issue 评论兜底。
|
||||
EOF
|
||||
jq -n --rawfile p wake.txt '{agent:"builder",
|
||||
model:{providerID:"<known-good providerID>",modelID:"<known-good modelID>"},
|
||||
parts:[{type:"text",text:$p}]}' \
|
||||
| curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
|
||||
-H 'content-type: application/json' -d @- -o /dev/null -w "%{http_code}\n" # → 204
|
||||
```
|
||||
|
||||
Then poll the tail (`limit=1`) until an assistant message appears, and keep
|
||||
expecting the completion-report per protocol — the re-woken worker owes you
|
||||
`status=done|blocked` like any other (the restated block in the template
|
||||
makes this hold even when the original seed predates [org-internal #3669]).
|
||||
|
||||
## Prevention — before dispatch, and in the seed
|
||||
|
||||
**Pre-dispatch provider check (30 seconds, catches most walls):**
|
||||
|
||||
```sh
|
||||
# 1) no active quota markers on the dispatch provider (else pick another)
|
||||
curl -s "$BASE/provider?directory=$DIR" | jq '[.all[] | select(.quota) | .id]'
|
||||
# 2) dispatch provider is connected AND ≥1 other connected provider exists as fallback
|
||||
curl -s "$BASE/provider?directory=$DIR" | jq '{connected, fallbacks: (.connected | length > 1)}'
|
||||
```
|
||||
|
||||
If the intended provider is already quota-marked, dispatch on a different one
|
||||
— do not launch into a wall you can see coming.
|
||||
|
||||
**Quota self-report clause (turns a silent death into a harvestable
|
||||
blocked).** The canonical clause text lives in `completion-report.md`
|
||||
§"Orchestrator side" (配额自报, [org-internal #3669]) — append it VERBATIM to the seed
|
||||
prompt's completion-report block. Single source of truth: do not fork or
|
||||
restate the clause here; its blocked payload already follows the one-line
|
||||
key=value spec (`status=blocked reason=quota-exhausted summary=<provider +
|
||||
reset hint> pr=#N`, completion-report.md `<report>` format).
|
||||
|
||||
With the clause in the seed, a worker that hits the wall reports `blocked`
|
||||
instead of dying silently — the orchestrator harvests the report and runs the
|
||||
Recovery section directly. The clause is also why the re-wake prompt's
|
||||
constraint (Step 3) repeats it: the re-woken worker must know the rule still
|
||||
holds on the new provider.
|
||||
@@ -0,0 +1,182 @@
|
||||
> Extracted from headless-session-ops/SKILL.md (Launch a session for a specific ticket (#N)) — moved verbatim 2026-08-26, ticket [org-internal #3480].
|
||||
|
||||
## Launch a session for a specific ticket (#N)
|
||||
|
||||
The common case: an agent (or cron/CI) needs to spin up a fresh main session to
|
||||
work a tracked issue. Compose the seed prompt **from the issue itself** so the
|
||||
new session starts with real context, and launch it on **the current session's
|
||||
model**.
|
||||
|
||||
0. **Owner check (MANDATORY pre-step, [org-internal #1803]).** Before creating anything,
|
||||
verify no other live session already owns this ticket or code area —
|
||||
duplicate ownership is how [org-internal #1744]/[org-internal #1753] collided. The check spans four
|
||||
data sources; any live claim → **ABORT the launch and report the
|
||||
conflict**.
|
||||
|
||||
**Mechanized ([org-internal #3667])**: `bash script/session-conflicts.sh <N>` runs the
|
||||
session-title scan (a), the branch scan (d), and the open-PR check (c)
|
||||
in one read-only pass (exit 4 = conflict, `--json` for a machine
|
||||
summary); the assignee/claim check (b) is `claim.sh`'s compare-and-swap
|
||||
at claim time. The manual recipes below remain the fallback and the
|
||||
normative definition (`core/rules/session-scope-guard.md`).
|
||||
|
||||
**a. Session-title scan (live session check).** The listing endpoint
|
||||
returns at most `limit` (default 100) sessions — on busy repos (300+ live
|
||||
sessions in one directory) that silently truncates and the scan misses
|
||||
owners ([org-internal #3190]). Non-range listing responses carry `X-Total-Count` (full
|
||||
filter population, untruncated) and `X-Has-More: true` (only when the
|
||||
page is truncated). Scan procedure: fetch page 1, and when
|
||||
`X-Has-More: true` is present re-fetch once with `limit=$TOTAL` — treat
|
||||
any failure to obtain the full population as "more owners may exist"
|
||||
(ABORT or widen the scan — never assume the first page is the whole
|
||||
population).
|
||||
|
||||
```sh
|
||||
# N must be the digits-only issue number (e.g. N=1803) — never interpolate
|
||||
# raw issue text here; the regex below assumes digits.
|
||||
[[ "$N" =~ ^[0-9]+$ ]] || { echo "N must be digits only"; exit 1; }
|
||||
HDR=$(mktemp)
|
||||
BATCH=$(curl -sD "$HDR" "$BASE/session?directory=$DIR")
|
||||
TOTAL=$(awk -F': ' 'tolower($1)=="x-total-count"{print $2}' "$HDR" | tr -d '\r')
|
||||
if [[ "$TOTAL" =~ ^[0-9]+$ ]] && awk -F': ' 'tolower($1)=="x-has-more"{print $2}' "$HDR" | grep -q true; then
|
||||
# truncated page + known population — refetch the full set in one request
|
||||
BATCH=$(curl -s "$BASE/session?limit=$TOTAL&directory=$DIR")
|
||||
elif awk -F': ' 'tolower($1)=="x-has-more"{print $2}' "$HDR" | grep -q true; then
|
||||
# truncated page but no usable X-Total-Count (older server) — the
|
||||
# population is unknown; do NOT re-fetch with the same default limit
|
||||
# (it would silently re-truncate). Escalate instead.
|
||||
echo "ERROR: session listing truncated but X-Total-Count unavailable — cannot establish full owner population" >&2
|
||||
rm -f "$HDR"; exit 1
|
||||
fi
|
||||
rm -f "$HDR"
|
||||
echo "scanned $(echo "$BATCH" | jq 'length') of ${TOTAL:-?} sessions in $DIR" >&2
|
||||
echo "$BATCH" | jq -r --arg n "$N" '.[] | select(.title | test("#" + $n + "([^0-9]|$)")) | "\(.id)\t\(.title)\t\(.time.updated)"'
|
||||
```
|
||||
|
||||
- Any hit whose `time.updated` is recent (session still active) → **ABORT
|
||||
the launch and report the conflict** (issue comment naming the owning
|
||||
session id). Do not launch a second session for the same ticket.
|
||||
|
||||
**b. Assignee / claim check ([org-internal #2297]).** Read the issue via
|
||||
`工单 API(见 TERMINOLOGY)get(owner, repo, index: N)` (or `GET /api/v1/repos/$OWNER/$REPO/issues/$N`):
|
||||
- If `assignee` is set and is **not** the launching agent → **ABORT** and
|
||||
report (the ticket is already claimed).
|
||||
- If a claim comment names a **different branch / session id** → **ABORT**
|
||||
and coordinate on the issue before proceeding.
|
||||
|
||||
**c. Open-PR check.** List open PRs referencing `#N` —
|
||||
`工单 API(见 TERMINOLOGY)search(q: "#N", type: "pulls", state: "open")` (API source)
|
||||
or `gitea_pull__list` — and **ABORT** if an open PR already covers the ticket.
|
||||
|
||||
**d. Remote branch check ([org-internal #2297]).** A local branch is invisible to other
|
||||
sessions — check the remote too:
|
||||
`git ls-remote origin 'workflow/*'` and scan for a branch tail covering `#N`.
|
||||
Also check the repo side locally: `git worktree list` +
|
||||
`git branch --list 'workflow/*'` for a branch / worktree already covering #N.
|
||||
|
||||
- A stale hit (session idle for hours / clearly abandoned) → do NOT
|
||||
hand-post a takeover comment (retired admin workaround — unauditable);
|
||||
dead-claim takeover is mechanized ([org-internal #3668]): `bash script/claim.sh
|
||||
takeover <ticket> <branch> --session-id <id>`. It enforces the
|
||||
evidence gate (branch ABSENT on origin AND (session 404 OR comment at
|
||||
least CLAIM_TAKEOVER_STALE_DAYS old)) and exits 10 when the evidence
|
||||
does not hold — a valid claim is never superseded; an idle-but-alive
|
||||
session is NOT provably dead, coordinate on the issue instead (exit 4
|
||||
= owned/conflict, in claim.sh and session-conflicts.sh alike).
|
||||
Evidence rules: `core/rules/session-scope-guard.md`
|
||||
§"Dead-claim takeover" (claim.sh header is normative).
|
||||
The runtime counterpart of this check is `core/rules/session-scope-guard.md`
|
||||
("One task, one owner"), injected into every session's prompt.
|
||||
- **Backend hard guard ([org-internal #1989]):** `POST /session` hard-rejects duplicate-ticket
|
||||
session creation with HTTP 400. The backend guard is **on by default ([org-internal #2350])**;
|
||||
set `enabled: false` in config to opt out. It fails open ONLY on defect
|
||||
paths (DB / config errors), never as a configured disable. The front-end
|
||||
soft check above is now backed by this backend hard check for defense in
|
||||
depth.
|
||||
- **Claim-first ([org-internal #2297]).** Claiming a ticket is one atomic 3-step action:
|
||||
(1) set the issue assignee to the working account, (2) post a claim comment
|
||||
naming the workflow branch and session id, (3) push the workflow branch to
|
||||
remote (`git push -u origin workflow/...`). The claim is valid ONLY when all
|
||||
three steps complete — an incomplete claim is NOT a claim. Do NOT launch the
|
||||
session until all three steps are done (see
|
||||
`core/rules/session-scope-guard.md` §"Claim-first"). If a collision HAS
|
||||
already happened, follow `rules/ownership-collision-runbook` (wiki, L2 on-demand).
|
||||
- **Provisioning after claim ([org-internal #3642]) is ONE command** — claim + worktree +
|
||||
Tier-1 runs scaffold:
|
||||
`bash script/claim-provision.sh <N> workflow/<branch>` (runs claim.sh with
|
||||
exit codes passed through, attaches the session worktree to the claimed
|
||||
branch via `session-worktree.sh create <slug> --branch <branch>`, then
|
||||
scaffolds `<runs-root>/{slug}/` via `<harness-package>/script/runs-init.ts`
|
||||
with kind-aware exemptions; prints one JSON summary; idempotent re-runs).
|
||||
Pitfall #10's pre-built worktree is exactly what it produces — put the
|
||||
returned worktree path in the claim comment + seed prompt.
|
||||
|
||||
1. **Read the issue.** Use the REST API (`gitea-rest` skill; `gitea-mcp` is retired):
|
||||
|
||||
```sh
|
||||
curl -s "$GITEA/api/v1/repos/$OWNER/$REPO/issues/$N" | jq '{title, body}'
|
||||
```
|
||||
|
||||
or `工单 API(见 TERMINOLOGY)get(owner, repo, index: N)`.
|
||||
|
||||
2. **Compose the seed prompt** from the issue body. The seed MUST, at minimum:
|
||||
- restate the **goal** in one sentence;
|
||||
- list the **constraints** and **acceptance criteria**;
|
||||
- cite every **wiki artifact path** / related issue referenced in the body
|
||||
(e.g. `{epic-slug}/dag`, `{epic-slug}/dag-nodes/{node-id}`, `[org-internal #1691]`);
|
||||
- tell the new agent to follow the right pipeline skill for the work type
|
||||
(implement / bugfix / design / …).
|
||||
Write it to a file (handles newlines):
|
||||
|
||||
```sh
|
||||
cat > seed.txt <<'EOF'
|
||||
Work issue #N: <one-line title>.
|
||||
Goal: <…>.
|
||||
Constraints: <…>.
|
||||
Acceptance: <…>.
|
||||
Artifacts: <wiki paths from the issue body>.
|
||||
Follow the `implement` skill (Mode: bugfix) for this.
|
||||
Session scope guard (mandatory): if a test fails and it was NOT caused by
|
||||
your change — classify (pre-existing/flaky), file a BF/FT issue per
|
||||
core/rules/session-scope-guard.md, and continue this task. Do NOT fix
|
||||
unrelated failing tests in place.
|
||||
|
||||
## 完成回报协议 (mandatory, [org-internal #2374])
|
||||
|
||||
完成、阻塞、或移交时,向编排会话回报:
|
||||
POST <BASE>/session/<LAUNCHER_SID>/prompt_async?directory=<DIR>
|
||||
(编排目录未知时可用全局路由 POST <BASE>/prompt_async,body 携 sessionID,[org-internal #4307])
|
||||
body: {"agent":"builder","model":{...},"parts":[{"type":"text","text":"<report>"}]}
|
||||
<report>: [#N] [<node-or-slug>] status=done|blocked|handoff branch=<ref> [pr=#<PR>] verify=<changed+typecheck> risk=<high|low> summary=<one line>
|
||||
约束:worker 只推分支不开 PR(PR 由编排串行开);交付推送前 test:changed+typecheck 须绿。若再遇配额/用量上限错误,立即回报 status=blocked reason=quota-exhausted summary=<provider 与重置提示>(branch=<ref> 必带)后停止,勿自行换模型重试。
|
||||
回报失败(非 2xx / 连接拒绝)→ 在本 issue(或父 Epic)发同内容评论兜底。
|
||||
决策边界([org-internal #2378]):遇方向性决策点 → status=blocked 回报,勿调 question。
|
||||
EOF
|
||||
```
|
||||
|
||||
3. **Determine the known-good model** (reuse the current session's — see the
|
||||
"Reuse the current session's model" section).
|
||||
|
||||
4. **Run the 3-step flow** with `title:"#N — <short>"` and the file-built
|
||||
payload. Read the model from the launcher session's own record — this also
|
||||
applies the `model.id → modelID` remap from the "Reuse" section:
|
||||
|
||||
```sh
|
||||
MODEL=$(curl -s "$BASE/session/$LAUNCHER_SID?directory=$DIR" \
|
||||
| jq -c '.model | {providerID, modelID: .id}')
|
||||
SID=$(curl -s -X POST "$BASE/session?directory=$DIR" \
|
||||
-H 'content-type: application/json' \
|
||||
-d "{\"title\":\"#$N — <short>\",\"agent\":\"builder\"}" | jq -r .id)
|
||||
PAYLOAD=$(jq -n --argjson m "$MODEL" --rawfile p seed.txt \
|
||||
'{agent:"builder",model:$m,parts:[{type:"text",text:$p}]}')
|
||||
curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
|
||||
-H 'content-type: application/json' -d "$PAYLOAD"
|
||||
```
|
||||
|
||||
5. **Poll** Step 3 until an `assistant` message appears — bounded tail poll
|
||||
(`&limit=1`), never the no-`limit` full-transcript form. Record `$SID` somewhere
|
||||
durable (issue comment, CI log) so the session is traceable to the ticket.
|
||||
|
||||
6. **Add the completion-report block to the seed prompt** (see next section) so
|
||||
the worker reports back when it finishes or blocks — without it, this
|
||||
orchestrator has no push channel and must poll forever ([org-internal #2374]).
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
name: image-interpret
|
||||
description: >
|
||||
Use ONLY when the agent needs to read or analyze an image (screenshot,
|
||||
design mockup, UI capture, .png/.jpg/.webp file) but the active model lacks
|
||||
vision capability. Delegates to the Image Interpreter role (vision-capable
|
||||
model) which returns a structured text spec — layout regions, colors (hex),
|
||||
spacing (px), typography, component hierarchy, and responsive breakpoints.
|
||||
Do not use for non-image tasks or when the active model already supports
|
||||
vision.
|
||||
triggers:
|
||||
- screenshot
|
||||
- mockup
|
||||
- design mockup
|
||||
- UI capture
|
||||
- .png
|
||||
- .jpg
|
||||
- .jpeg
|
||||
- .webp
|
||||
- image
|
||||
- 读图
|
||||
- 设计稿
|
||||
- 截图
|
||||
- 视觉
|
||||
role: Tool
|
||||
---
|
||||
|
||||
# Image Interpret — delegate image reading to a vision-capable role
|
||||
|
||||
## When to use
|
||||
|
||||
You (the calling agent) cannot read images — your model has no vision. Any
|
||||
task that requires *seeing* a visual artifact must be delegated to the
|
||||
**Image Interpreter** role, which runs on a vision-capable model and returns a
|
||||
structured text description you can act on.
|
||||
|
||||
Trigger situations:
|
||||
|
||||
- The user references a screenshot, design mockup, or image file.
|
||||
- A task requires comparing rendered UI against a design.
|
||||
- You need layout/color/spacing specs extracted from a picture.
|
||||
- A file path ending in `.png` / `.jpg` / `.jpeg` / `.webp` must be understood.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Identify the image.** Confirm the file path (or paths) that must be read.
|
||||
Resolve it to an absolute path the subagent can access.
|
||||
|
||||
2. **Frame the question.** Decide exactly what the caller needs — full layout
|
||||
spec, a specific region, color tokens, component list, diff vs. existing UI.
|
||||
|
||||
3. **Delegate via the Task tool with `role: "Tool"`.** Do NOT pass
|
||||
`subagent_type` — it is derived automatically from the role.
|
||||
|
||||
```
|
||||
Task(
|
||||
role: "Tool",
|
||||
description: "Extract header layout spec",
|
||||
prompt: "Read the image at /abs/path/mockup.png. Extract: (1) header
|
||||
layout regions with bounding positions, (2) all colors as hex,
|
||||
(3) spacing in px, (4) typography (family/size/weight). Return
|
||||
a structured text spec the caller can implement without seeing
|
||||
the image."
|
||||
)
|
||||
```
|
||||
|
||||
4. **Consume the returned text spec.** The Image Interpreter returns a
|
||||
structured description only — it cannot write code or artifacts. Use the
|
||||
returned spec to drive your own work (code generation, design review,
|
||||
planning).
|
||||
|
||||
5. **Do not attempt to read the image yourself.** If you are a non-vision
|
||||
model, opening the image yields nothing. Always delegate.
|
||||
|
||||
## Notes
|
||||
|
||||
- The Image Interpreter is **read-only** — it describes images, it does not edit
|
||||
files or write code.
|
||||
- One delegation per distinct visual question. Batch multiple images into a
|
||||
single delegation only when they must be compared.
|
||||
- If the returned spec is ambiguous, delegate again with a sharper question
|
||||
rather than guessing.
|
||||
@@ -0,0 +1,350 @@
|
||||
---
|
||||
name: implement
|
||||
description: >
|
||||
Use ONLY when implementing a work item from an approved iteration plan,
|
||||
OR fixing a bug (bugfix mode), OR refactoring code (refactor mode),
|
||||
OR porting a feature (port mode). The Developer (Worker) reads the work
|
||||
item, design sections, and acceptance criteria, writes code, and
|
||||
self-checks against the relevant checklist before handing off to code
|
||||
review.
|
||||
# `stage` is intentionally omitted: `implement` is a production-phase name the
|
||||
# stage registry rules invalid post-[org-internal #3072] phase 3 (see
|
||||
# core/schemas/workflow-routing.schema.json stage enum — production-phase
|
||||
# names are "no longer valid anywhere"). The implement skill has no registry
|
||||
# gate id; the gates it feeds into are `review-code` and `verify`.
|
||||
triggers:
|
||||
- implement
|
||||
- implement work item
|
||||
- implement WI-
|
||||
- work item
|
||||
- 实现工作项
|
||||
- 开始写代码
|
||||
- implement the iteration
|
||||
- bugfix
|
||||
- fix a bug
|
||||
- fix bug
|
||||
- fix this bug
|
||||
- 修复
|
||||
- 修复bug
|
||||
- help me fix
|
||||
- doesn't work
|
||||
- does not work
|
||||
- not working
|
||||
- is broken
|
||||
- stack trace
|
||||
- throws an error
|
||||
- crashes
|
||||
- refactor
|
||||
- 重构
|
||||
- restructure
|
||||
- 删除死代码
|
||||
- dead code
|
||||
- improve code structure
|
||||
- 重新组织代码
|
||||
- graduate experimental
|
||||
- remove experimental guard
|
||||
- 移除实验性
|
||||
- port
|
||||
- porting
|
||||
- port feature
|
||||
- port this feature
|
||||
- migrate feature
|
||||
- 移植
|
||||
- 迁移功能
|
||||
- code change
|
||||
- write code
|
||||
role: Producer
|
||||
---
|
||||
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatimDir)。机制、结构与 frontmatter 保持;实例术语(工具名、路径、工单号)按 `core/adapters/TERMINOLOGY.md` 绑定到具体实例。
|
||||
|
||||
# Skill: implement
|
||||
|
||||
## Mode Selection
|
||||
|
||||
This skill handles four work modes. The main session identifies the mode from the work item's `type` field or the user's request:
|
||||
|
||||
| Mode | When to use | Key difference |
|
||||
|---|---|---|
|
||||
| **implement** (default) | Work item from an approved iteration plan | Standard implementation per acceptance criteria |
|
||||
| **bugfix** | User reports a bug / fix request | Reproduce → root cause → regression test (must FAIL first) → minimal fix |
|
||||
| **refactor** | User asks to restructure code (no behavior change) | Establish baseline → transform in ≤10 steps → verify zero regression |
|
||||
| **port** | User asks to port a feature from another project | Read source → map concepts → implement in target conventions → verify fidelity |
|
||||
|
||||
In **standalone mode** (user-initiated, no iteration plan), skip preconditions about plan/approval and go directly to the mode-specific workflow.
|
||||
|
||||
In **pipeline mode** (work item from iteration plan), follow the standard preconditions then the mode-specific phases.
|
||||
|
||||
**On-demand references**: each mode's phase-by-phase detail (templates, prompts, orchestration flows, historical notes) lives in `reference/{mode}-mode.md`, NOT injected — read it at mode entry. Full read-timing table: References section.
|
||||
|
||||
> **Progressive disclosure ([org-internal #3734])**: when dispatching a Developer scoped to
|
||||
> one mode, pass `skills: ["implement:mode-bugfix"]` (preamble + that
|
||||
> mode's workflow only) instead of the full body; pipeline-mode dispatches
|
||||
> should also carry `pipeline-detection`
|
||||
> (`"implement:mode-implement,pipeline-detection"`). Mode Selection, Agent
|
||||
> Role, Greenfield vs. Brownfield, … are unmarked preamble and always
|
||||
> inject. Full-body remains the default when no selector is passed.
|
||||
|
||||
## Agent Role
|
||||
|
||||
The implementation is owned and produced by the **Developer** (Worker). The Builder delegates each work item to a dedicated Developer sub-agent with the full design and plan context.
|
||||
|
||||
> **Role naming**: throughout this skill, "Builder", "Orchestrator", and "main session" name the same coordinating role (the main session that dispatches Developer sub-agents and validates output) — not a distinct role from the "Orchestrator" used by `review-code` and the shared review pipeline.
|
||||
|
||||
The Developer is responsible for:
|
||||
|
||||
- Reading the assigned work item, design sections, and acceptance criteria.
|
||||
- Writing code that faithfully implements the design.
|
||||
- Running typecheck, lint, and tests to self-verify.
|
||||
- Self-checking against the relevant checklist before handoff.
|
||||
- Persisting the final report to disk before returning — worker-report persistence ([org-internal #2847], see `../_shared/worker-report-persistence.md`).
|
||||
- Writing no more and no less than the work item scope — no opportunistic refactoring of unrelated code.
|
||||
|
||||
The Builder's role is to validate the implementation output and pass it to code review. The Builder MUST NOT write or revise implementation code.
|
||||
|
||||
### Pre-flight checklist injection ([org-internal #2599])
|
||||
|
||||
Before dispatching ANY Developer (or Tester) sub-agent — in every mode (implement / bugfix / refactor / port; pipeline or standalone) — the orchestrator resolves the ticket's route (the Process Assessment Gate Step 0 already resolves the Kind/* route) and reads `<instance-root>/workflow-routing.yaml` `routes.{Kind}.preflight`. When the list is non-empty, prepend it to the sub-agent's task prompt as:
|
||||
|
||||
```
|
||||
Pre-flight self-check (evidence-based, from retrospective — verify each
|
||||
BEFORE writing code; if one is already satisfied, note why in impl-notes):
|
||||
1. {item} (evidence: {evidence})
|
||||
2. ...
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- The list is a human-landed checklist (retro proposes, a human lands it) — NEVER synthesize or extend items at dispatch time.
|
||||
- Cap at `preflight.max_items` entries; beyond it, drop oldest by `added_cycle`.
|
||||
- Items are self-checks, not gates: an unsatisfied item means the Developer addresses it in the implementation (and says how), not that dispatch aborts.
|
||||
|
||||
<!-- inject: ../_shared/large-prompts.md -->
|
||||
|
||||
> **Context compaction**: this skill is a pipeline stage boundary. The main session (orchestrator) compacts at this clean boundary ONLY when a capacity/projection trigger holds, per the L1 rule `core/rules/compact.md` §"Stage-boundary compaction" (long multi-stage runs — DAG Epic orchestration — keep the legacy every-boundary compaction; short runs — bugfix / DAG task — and standalone runs default to NOT compacting). The sub-agent this skill dispatches persists its artifacts to the Gitea wiki under `{slug}/` as it goes, so a mid-run compaction loses nothing — re-read the stage's wiki index to resume.
|
||||
|
||||
## Role Split: Developer vs Tester
|
||||
|
||||
Each execution mode below defines its own Tester focus and any mode-specific orchestration overrides.
|
||||
|
||||
## Greenfield vs. Brownfield
|
||||
|
||||
**Greenfield** (new project): Create new files following the design. Project conventions are defined by the design document.
|
||||
|
||||
**Brownfield** (existing project + new feature):
|
||||
|
||||
- **Read neighbors first.** Before writing code, read at least 3 existing files in the same module to absorb the project's code patterns. (Shared brownfield rule — canonical statement: `core/skills/review-code/SKILL.md` §"Greenfield vs. Brownfield"; the threshold and same-module scope are defined there.)
|
||||
- Match existing conventions exactly: error handling style, logging format, naming, file structure, import ordering, type declaration placement.
|
||||
- New code MUST follow existing conventions consistently — no style drift.
|
||||
- No opportunistic refactoring of unrelated existing code. If you see a bug or improvement opportunity in unmodified files, log it in the implementation report as a separate observation — do not fix it in this work item.
|
||||
- Phase 1 (Parse Context) includes reading neighboring code files to establish the project's conventions. Phase 4 (Self-Check) compares new code against these conventions.
|
||||
|
||||
---
|
||||
|
||||
## Execution Modes
|
||||
|
||||
<!-- section:mode-implement -->
|
||||
### Mode: implement (default)
|
||||
|
||||
Standard workflow for work items from an approved iteration plan. Full phase detail, templates, prompts: `reference/implement-mode.md` (read at mode entry).
|
||||
|
||||
#### Preconditions
|
||||
|
||||
> **DAG-mode input path** (DAG ticket pipeline — `Kind/Epic` / `Kind/Feature` DAG parent, routes-table direct): DAG-routed tickets **ignore `Size/*`** — the tiered Preconditions below are replaced by the node spec: work item + acceptance criteria resolve from the **frozen DAG copy** wiki page `{epic-slug}/dag` (+ `{epic-slug}/dag-nodes/{node-id}` subpages when AC detail is sunk) and the node ticket's issue body — no legacy `{slug}/04-plan-*` page, no `Size/*`-tiered req/design page. The design-space + iteration-plan convergence preconditions are replaced by the **review-dag single-gate convergence**: `octopus review status --stage review-dag` must show `success` before the node is implemented.
|
||||
|
||||
> **DAG-route read map** (every legacy `{slug}/04-plan-*` / `{slug}/03-design-*` reference below resolves from the frozen DAG copy instead — mirror of `verify/SKILL.md`'s DAG branch; full map: `reference/implement-mode.md` § Preconditions):
|
||||
>
|
||||
> - Work item (`04-plan-04-iteration-assignment` / issue body) → node spec in `{epic-slug}/dag` + node ticket body.
|
||||
> - Acceptance criteria (`04-plan-05-acceptance-criteria` / issue body) → node `acceptance_criteria` (+ sunk subpages) + node ticket body.
|
||||
> - `test_id` (測試用例 ID) → the `test_id` declared on the node AC in `{epic-slug}/dag`.
|
||||
> - Design sections / interface design (`03-design-**`) → node spec + cross-session edge contracts (no design page).
|
||||
> - Component mapping (`03-design-08-traceability`) → node `req_refs` + component field.
|
||||
>
|
||||
> At Phase 6 handoff, pass `mode: "dag-task"` to review-code (its DAG Task Mode keys off the same frozen-DAG-copy detection).
|
||||
|
||||
> **Legacy pipeline preconditions retired ([org-internal #3072] phase 3, 2026-08-21)**: tiered artifact-existence and review-convergence checks belonged to the archived legacy pipeline. Live input modes: DAG task mode (above) and standalone modes (the request itself is the spec). Historical tiered publish targets: `reference/implement-mode.md` § Legacy notes.
|
||||
|
||||
Before starting implementation, confirm:
|
||||
|
||||
- [ ] Work item is specified (DAG node ticket `{node-id}`, or a clear task description in standalone modes).
|
||||
- [ ] `core/checklists/implementation.md` is accessible.
|
||||
- [ ] `slug` matches the run's slug (DAG: `{epic-slug}`).
|
||||
- [ ] 跨阶段门控清单: `core/checklists/pipeline-gate.md` accessible and its DAG 路由变体 section confirmed — frozen DAG copy exists, single gate converged, upstream dependencies at terminal state (`ready`); else abort, listing the blocked nodes.
|
||||
|
||||
**If any precondition is unmet, abort and inform the user** — list every missing artifact, un-converged review, and blocked dependency (complete gate checklist + Recovery Protocol: `core/checklists/pipeline-gate.md`). When no work item is specified, resolve the ready/pending task nodes from `{epic-slug}/dag` and present them for selection (prompt: `reference/implement-mode.md` § Preconditions — work-item selection).
|
||||
|
||||
#### Phases skeleton
|
||||
|
||||
Every gate below is hard — agents rationalize skipping exactly these. Full detail: `reference/implement-mode.md`.
|
||||
|
||||
1. **Phase 1 — Parse Context**: read the work item (node spec + node ticket), acceptance criteria (every falsifiable `AC-{n}` / `NFR:` entry and its declared `test_id`), design context (cross-session edge contracts), and the existing codebase; resolve inputs per the DAG-route read map (standalone: the request). Read every referenced design file before writing code.
|
||||
2. **Phase 2 — Plan Implementation**: brief plan (template: `reference/implement-mode.md` § Phase 2). GATES: **≤ 3 files per work item** (more → the Builder/user MUST split it); every file must map to a design component (else flag the design gap and abort); do NOT invent design decisions. Present the plan and ask: proceed? (yes / no / revise).
|
||||
3. **Phase 3 — Implement**: design-exact code; tests cover every AC.
|
||||
- **Design discipline**: component interfaces, method signatures, return types, data model fields, API endpoints/schemas/status codes MUST match the design exactly; an impossible design decision → stop and report the gap, never silently deviate.
|
||||
- **Test discipline (Red → Green, declared test_ids)**: write each declared test FIRST and confirm it fails for the intended reason (Red) before writing the implementation (Green); the test's `file-path :: test-name` MUST match the declared `test_id` exactly — the implement-side handshake with `verify` (DOD-1.6). `MANUAL` / `BENCH:<script>` test_ids are exempt from the Red step; an already-passing test is noted in the Phase 5 report, not forced to fail.
|
||||
- Code quality + incremental commitments (conventions, no unjustified dependencies, public-API docs; shared types → data access → logic → handlers, typecheck per unit): `reference/implement-mode.md` § Code Quality.
|
||||
4. **Phase 4 — Self-Check** (all mandatory):
|
||||
1. `bun typecheck` (or project-equivalent) — zero errors.
|
||||
2. `bun oxlint --deny-warnings` (repo root — the review-code mechanical gate's canonical lint invocation; `bun lint` is the package-script alias) — zero errors.
|
||||
3. `bun run test:changed` (or project-equivalent; full suite `bun run test:parallel` is verify's job, not a per-revision gate, [org-internal #2598]) — all tests pass.
|
||||
4. Post-deletion cleanup (when any code was removed): re-run lint + typecheck to catch orphaned imports/variables/type references.
|
||||
Then self-check `core/checklists/implementation.md`; every new function/method/exported API has ≥ 1 test; interface promises cross-checked against the node's edge contracts. **Review-readiness GATE**: self-attest `core/checklists/code-review.md` (COR, DGN, SEC, PERF, TST, STY, DBT, A11Y, DOC, TRC), record pass/fail per dimension in the Phase 5 report; handoff requires **0 BLOCKERs and 0 MAJORs** — if you can find a MAJOR, the formal review will too; fix it now.
|
||||
5. **Phase 4.5 — Iteration Completion Commit**: after ALL work items in the iteration pass Phase 4, commit with format `[{chunk-id}][{iteration}] {summary}`; commit body REQUIRED for non-trivial commits (> 1 file or > 20 LOC): What (files + purpose + the `WI-{NNN}` ID — code-review TRC 10.1), Why (design/REQ motivation), Evidence (test names / verification commands). Full rules: `reference/implement-mode.md` § Phase 4.5.
|
||||
6. **Phase 4.6 — Issue Checklist Sync (progressive)**: after committing, mark items this iteration delivered `- [x]` + `_(commit {sha}: file/component)_` per the `issue-checklist-sync` L1 rule; do NOT touch items outside this iteration's scope.
|
||||
7. **Phase 4.7 — PR-Creation Sync**: the session pushes its branch and reports `status=done branch=<ref> verify=… risk=…` — the orchestrator opens the PR (serially per TD-678/[org-internal #4425]) and applies the `Risk/*` label from the report's `risk=` hint, computed per the risk-classifier frozen table (`HIGH_RISK_GLOBS` in `.gitea/scripts/risk-classifier.ts`: core/migrations/deploy/`core/rules/**`/`.gitea/workflows/**` → `Risk/High`, else `Risk/Low`); `Risk/High` PRs merge manually by design. Once the PR exists, ensure `## 当前状态` exists (PR / 代码评审 / CI rows are written by the `status-sync` poller, NOT by hand); append the PR reference to the matching Epic task-list row. **Never hand-sync main into the PR branch** — that is the keep-mergeable workflow's job. Detail: `reference/implement-mode.md` § Phase 4.6 / § Phase 4.7.
|
||||
8. **Phase 5 — Report**: implementation report with AC → test traceability (template: `reference/implement-mode.md` § Phase 5). **Persist before returning ([org-internal #2847])** — the Developer's LAST action before returning the report: write it to `<runs-root>/{slug}/workers/{chunk-id}-worker-{seq}.md` (Tier 1 run workspace) else `/tmp/octopus/{chunk-id}-worker-{seq}.md` (`../_shared/worker-report-persistence.md`). The persisted copy is the report of record — applies to EVERY mode's report phase.
|
||||
9. **Phase 6 — Handoff to Code Review**: present the report; signal readiness via `signal_stage_done`. Do NOT mark the work item complete until code review passes.
|
||||
|
||||
#### Tester focus & Common Rationalizations
|
||||
|
||||
Boundary + contract tests — the cases the Developer is structurally biased to miss; every acceptance criterion MUST map to ≥ 1 test. Implementation fails far more from **pressure** than from ignorance — full detail (11-row Excuse → Reality table): `reference/implement-mode.md` § Tester focus for implement / § Common Rationalizations.
|
||||
|
||||
---
|
||||
|
||||
<!-- section:mode-bugfix -->
|
||||
### Mode: bugfix
|
||||
|
||||
Reproduce, isolate, and fix a bug with a regression test that MUST fail before the fix. Small localized bugs → standalone (existing system behavior is the specification; review gate optional — only when > 20 lines or ≥ 3 files); large/complex bugs → pipeline (review + verify mandatory). Phase detail, templates, prompts: `reference/bugfix-mode.md` (read at mode entry).
|
||||
|
||||
> **Routing override (ticket-seeded)**: the Optional/None gate above applies to *user-initiated* standalone mode. A `Kind/*` route whose `keep_gates` includes `review-code` / `verify` (e.g. `Kind/Bug`, `Kind/Testing`) makes those gates MANDATORY regardless of size (a gate is mandatory if EITHER the route OR the skill requires it; skipping is valid only when BOTH agree).
|
||||
|
||||
#### Phases skeleton
|
||||
|
||||
The regression test MUST fail before the fix — the Tester dispatches between Phase 2 and Phase 4, not after the fix (dispatch flow: `reference/bugfix-mode.md` § Role & Responsibilities; single-Developer invocation only for trivial single-file fixes — force the split when the fix touches ≥ 2 files or the root cause spans ≥ 2 levels of indirection).
|
||||
|
||||
1. **Phase 1 — Understand & Reproduce**: read relevant code; check existing tests (a passing test on this path → the bug is in the test or an uncovered branch); reproduce and document (template: `reference/bugfix-mode.md` § Phase 1). **If the bug CANNOT be reproduced, stop and report — do not guess-patch.**
|
||||
2. **Phase 2 — Isolate Root Cause**: trace symptom → proximate cause → root cause (RCA template: `reference/bugfix-mode.md` § Phase 2); fixing a symptom → stop, go deeper; not found after 3 levels of indirection → pause and report, no surface-level patch. Then evaluate routing (below).
|
||||
3. **Phase 3 — Write a Regression Test**: exercise the exact bug path with the failing inputs; it MUST fail with the bug's symptom NOW, before the fix (already passing → the test does not cover the bug, rewrite it; no failing test possible → most targeted test, marked `[flaky]`).
|
||||
4. **Phase 4 — Fix**: minimum change resolving the root cause; one conceptual change per fix — no bundled refactoring, style changes, or "while I'm here" improvements (root cause in a different file → fix it there). Run the regression test (MUST pass) + relevant unit tests.
|
||||
5. **Phase 5 — Self-Check & Report**: `bun typecheck`; `bun oxlint --deny-warnings`; `bun run test:changed` (all pass; full suite belongs to verify); verify `core/checklists/bugfix.md`; publish the bugfix report as wiki page `{slug}/bugfix-report` (template: `reference/bugfix-mode.md` § Bugfix Report); persist per the Phase 5 persistence rule (Mode: implement, [org-internal #2847]).
|
||||
6. **Phase 5.5 — Issue Checklist Sync (standalone bugfix)**: sync the source issue at each transition (commit / PR / review / CI / close) per the `issue-checklist-sync` L1 rule and its standalone-flow table.
|
||||
7. **Phase 6 — Approval**: present the report (prompt: `reference/bugfix-mode.md` § Phase 6 — Approval).
|
||||
|
||||
#### Routing Decision (after Phase 2)
|
||||
|
||||
Full escalate/stay criteria: `reference/bugfix-mode.md` § Routing Decision. In short — **escalate to pipeline** on ANY of: ≥ 5 files · ≥ 2 modules/components · design-level root cause · data migration / schema change · public-API / contract change · dependency change · > 50 lines · user requests full-process; **stay standalone** only when ALL the opposites hold. If uncertain, escalate — a false escalation costs review rounds; a false standalone decision skips quality gates.
|
||||
|
||||
**Big-bug relabel rule ([org-internal #3061])** — before the generic escalation above, split the triggers by kind:
|
||||
|
||||
- **Design-level triggers** (root cause is a design decision — protocol / schema / architecture; shared-contract or public-API change; data migration): do NOT push through bugfix — **relabel the ticket `Kind/Feature`** and reroute via Step 0 (DAG route; 1–3 node small DAG expected); repro + root-cause notes become node input.
|
||||
- **Mechanical size triggers only** (many files / lines, same design): stay in bugfix — batch into iterations, keep review-code + verify. Scale alone never justifies a relabel.
|
||||
|
||||
#### Pipeline Mode (bugfix)
|
||||
|
||||
> **Legacy path retired ([org-internal #3072] phase 3)**: the requirements → design → review → plan front-end was archived; a big bug needing a design-level decision relabels `Kind/Feature` into the DAG route (big-bug relabel rule above).
|
||||
|
||||
When a bugfix escalates beyond standalone scope, the bug report becomes a pipeline input; the bugfix phases (reproduce, root cause, regression test, fix) are embedded within the implement stage, and review-code + verify remain mandatory. On the DAG route the node spec lives in `{epic-slug}/dag`; the Developer follows bugfix Phases 1–6 as the implementation method, then produces the standard implementation report (Mode: implement, Phase 5).
|
||||
|
||||
**Pipeline abort criteria** — before any code is written in pipeline mode, abort if ANY of: (1) bug no longer reproduces; (2) root-cause hypothesis falsified during re-isolation; (3) resolved by external change; (4) reproduction confidence < 3/5 after one re-isolation iteration. Abort procedure (`{slug}/ABORT` wiki page, no commit/merge, retrospective, archive): `reference/bugfix-mode.md` § Pipeline Abort Criteria. Once code is written, abort is no longer valid — the run proceeds review-code → verify.
|
||||
|
||||
**Common Rationalizations (bugfix) & Incident Triage** — full 9-row Excuse → Reality table + Incident Triage Carve-Out (under active incident pressure the Phase 2→3 ordering MAY be relaxed, never skipped — a stop-gap may ship first, BUT the full root-cause trace + failing regression test + proper fix MUST land in the same incident window): `reference/bugfix-mode.md` § Common Rationalizations (bugfix) / § Incident Triage Carve-Out.
|
||||
|
||||
---
|
||||
|
||||
<!-- section:mode-refactor -->
|
||||
### Mode: refactor
|
||||
|
||||
Restructure existing code without changing observable behavior — the existing test suite is the safety net; every step MUST be verified before proceeding. Templates and prompts: `reference/refactor-mode.md` (read at mode entry). **Execution modes**: standalone (user says "refactor {X}"; review gate optional — only when > 50 lines or ≥ 5 files) vs pipeline (refactoring WI / DAG node; scope from the node spec, review mandatory).
|
||||
|
||||
#### Preconditions
|
||||
|
||||
- [ ] Scope is specified (which file, module, or pattern to refactor).
|
||||
- [ ] An existing test suite covers the scope (if unknown, run with coverage first).
|
||||
- [ ] No uncommitted changes (`git status` is clean); `core/checklists/refactoring.md` is accessible.
|
||||
|
||||
**No test coverage? Stop.** Refactoring without tests is rewriting with unknown side effects — write characterization tests first or skip this module (prompt: `reference/refactor-mode.md` § No Test Coverage? Stop.).
|
||||
|
||||
#### Phases skeleton
|
||||
|
||||
1. **Phase 1 — Scope & Baseline**: map exact files + dependents; run `bun run test:parallel` (the refactor baseline legitimately needs the full suite) — ANY pre-existing failure → stop ("Cannot begin refactoring with failing tests. Fix them first."). Capture baseline: test count, coverage, optional complexity (template: `reference/refactor-mode.md` § Baseline).
|
||||
2. **Phase 2 — Define Target Pattern**: Extract / Inline / Rename / Move / Replace / Simplify / Upgrade, with one-sentence goal + success criteria (all tests pass unchanged; structural goal met; coverage does not decrease). Pipeline mode: align with the node spec + contracts, or justify in the report.
|
||||
3. **Phase 3 — Decompose into Steps**: smallest individually-verifiable steps (each reversible, suite-passing, ONE conceptual transformation); present the plan before executing (user may approve / reorder / reject). **If > 10 steps, the scope is too large — split into multiple sessions.**
|
||||
4. **Phase 4 — Incremental Execution**: per step — transform, run `bun run test:changed` (ALL pass; per-step scoped reruns — full suite is verify's job, [org-internal #2598]), commit `refactor: {what} from {where}`. **If FAIL: revert immediately** — do NOT fix the test or code within the same step; find a smaller decomposition (exception: fix a flaky test first as a prerequisite step, then retry).
|
||||
5. **Phase 5 — Final Validation**: `bun run test:parallel` all pass; `bun typecheck` zero errors; `bun oxlint --deny-warnings` zero errors; coverage MUST NOT decrease vs baseline (±1% noise).
|
||||
6. **Phase 6 — Report**: before/after metrics, steps executed, verification results, design deviations (template: `reference/refactor-mode.md` § Refactor Report).
|
||||
7. **Phase 7 — Approval**: present the report (prompt: `reference/refactor-mode.md` § Phase 7 — Approval). Route to code review if > 50 lines or ≥ 5 files.
|
||||
|
||||
---
|
||||
|
||||
<!-- section:mode-port -->
|
||||
### Mode: port
|
||||
|
||||
Port a feature from a source project into the current (target) project, preserving the source's behavioral contract while adapting every implementation detail to the target's tech stack and conventions. Orchestration, per-phase processes, and templates live in the existing `reference/*.md` port files (indexed in References); read `reference/port-mode.md` at mode entry.
|
||||
|
||||
**Tester focus (port)**: behavioral-fidelity tests — every `FID-*` item in `port-{name}/source-analysis/10-fidelity-baseline` MUST map to ≥ 1 ported test (rewrite the source test in the target framework's style, preserving the contract); port edge/error/boundary tests, not just the happy path (missing FID coverage is the #1 cause of incomplete porting); every `[FIDELITY DEVIATION]` gets a test locking the divergent behavior.
|
||||
|
||||
#### Phase 0 — Scope Assessment
|
||||
|
||||
Measure the source scope — file count and LOC alone are insufficient: (1) source files; (2) source LOC (exclude tests); (3) source modules / feature areas; (4) source packages touched; (5) implicit dependency artifacts (schema/migrations, config, env vars, CLI flags, theme files, routes, providers, build config — full list: Phase A1.8). Then:
|
||||
|
||||
- **Standalone** (Part 1): ≤ 5 files, ≤ 200 LOC, 1 package, 0 implicit deps.
|
||||
- **Pipeline** (Part 2): > 5 files or > 200 LOC · spans ≥ 3 source modules · spans ≥ 2 packages · or ≥ 3 implicit dependency artifacts (implicit deps require capability boundary analysis).
|
||||
|
||||
Report the assessment and do NOT proceed without explicit approval (prompt: `reference/port-mode.md` § Phase 0 — Scope Assessment).
|
||||
|
||||
#### Preconditions
|
||||
|
||||
- [ ] Source project path/reference and source feature scope specified; target project structure accessible (all monorepo packages).
|
||||
- [ ] Target is the current working directory, clean workspace, existing test framework (none → warn: behavioral fidelity cannot be guaranteed — `reference/port-mode.md` § Preconditions).
|
||||
- [ ] Target's existing capabilities documented or discoverable (else run the structural survey, Phase A1.7, first); `core/checklists/port.md` is accessible.
|
||||
|
||||
#### Part 1 — Standalone Mode (Small Port)
|
||||
|
||||
The Developer executes the full port lifecycle directly; no DAG decomposition; **code review is mandatory regardless of size** (Phase A8). Phases: A1 source analysis → A1.5 review GATE → A1.7 target surface → A1.8 capability boundary GATE (`reference/source-analysis.md`) · A2 concept mapping → A3 gap analysis → A4 adaptation design (`reference/mapping-and-design.md`) · A5 implement + A5.5 self-check GATE (rules below) · A6 port tests & fidelity (`reference/fidelity-verification.md`) · A7 report (`reference/port-report-template.md`) · A8 approval → code review (`reference/port-mode.md` § Phase A8).
|
||||
|
||||
**Phase A5 — Implement (rules)**: one file at a time (port completely, verify, then move on); **target conventions are law**; **fidelity over aesthetics** — do not "improve" the source logic (port the exact validation, log `[NOTE: weak validation in source]`; stricter behavior is a separate feature item); port comments from source (translated); `bun typecheck` after each file; no new dependencies (use the adaptation-design alternative or reimplement the subset inline).
|
||||
|
||||
**Phase A5.5 — Self-Check GATE (MANDATORY)**: complete `core/checklists/port.md` in full — every item ☑ (pass) or ☐ (fail) with written justification; every ☐ item documents an inline fix plan; known deferrals include a reactivation path (chunk + trigger); publish as wiki page `port-{name}/self-check`. Do NOT proceed to Phase A6 until all items are ☑ OR all ☐ items have documented fix plans AND the Developer has printed `SELF-CHECK COMPLETE — {X} items passing, {Y} items deferred with plan`.
|
||||
|
||||
#### Part 2 — Pipeline Mode (Large Port)
|
||||
|
||||
The Developer produces a source analysis report as the foundation artifact, then the port flows through the pipeline with peer-review gates: `port (source analysis) → analyze-dag (decompose by source module) → review-dag single gate → per node (dag.task_route): implement → review-code → verify (integration + fidelity)`. Phases: B1 12-document source analysis → B1.5 10-dimensional review GATE → B1.7+B1.8 target surface & capability boundary artifacts → B2 checklist self-check → B3 handoff to analyze-dag (slug `port-{name}`) → B4 per-node DAG task route (`reference/large-port-pipeline.md`) · B5 verify — integration + fidelity, every `FID-*` traced to a passing test (`reference/fidelity-verification.md`) · B6 final approval (rules below).
|
||||
|
||||
**Phase B6 — Final Approval**: verify (1) CI is configured (absent → warn `[GAP: no CI — no automated gate before merge]`, flag in report); (2) all review gates passed (review-dag single gate + every node's code review `converged: true`); (3) typecheck + lint + tests pass fresh. Present the consolidated report (prompt: `reference/port-mode.md` § Phase B6). Post-merge cleanup: `reference/port-mode.md` § Phase B7.
|
||||
|
||||
---
|
||||
|
||||
<!-- section:pipeline-detection -->
|
||||
## Pipeline Work Item Detection
|
||||
|
||||
Not all work items involve writing new code — some are refactoring, bugfix, or frontend work items. They use the workflows above (or the frontend skill) but flow through the same pipeline gates (review-code → verify; DAG-routed work resolves its spec from `{epic-slug}/dag`). When detected, read `reference/work-item-detection.md` BEFORE Phase 1 for the per-phase pipeline adaptations. Detection triggers (full keyword lists in the reference):
|
||||
|
||||
| Work item | Detected by (examples) | Route |
|
||||
|---|---|---|
|
||||
| **Refactoring** | "Refactor" / "重构" / "Extract" / "Rename" … prefixes; `[REFACTOR]` tag; behavior-preserving structural node spec; REQ-REFACTOR-* | Mode: refactor + adaptations — code review mandatory regardless of size, no "no review needed" bypass |
|
||||
| **Bugfix** | "Bugfix" / "Fix" / "修复" / "Hotfix" prefixes; `[BUGFIX]` tag; correction-of-behavior node spec; bug report / stack trace / RCA reference | Mode: bugfix + adaptations — code review mandatory regardless of size |
|
||||
| **Frontend** | "Frontend" / "UI" / "组件" / "页面" prefixes; `[FRONTEND]` tag; UI-layer node spec; components in `components/` `pages/` `views/` `ui/`; exclusively `.tsx`/`.jsx`/`.vue`/`.svelte`/`.astro`/`.css`/`.scss` files | `core/skills/frontend/SKILL.md` + adaptations — code review mandatory regardless of size |
|
||||
|
||||
## References
|
||||
|
||||
**On-demand mode references** (NOT injected — read at the declared timing):
|
||||
|
||||
- `reference/implement-mode.md` — Mode: implement: mode entry (phase detail); Phase 2 / Phase 5 templates; Tester focus; Common Rationalizations; legacy notes.
|
||||
- `reference/bugfix-mode.md` — Mode: bugfix: mode entry; Phase 1–3 templates; Phase 5 report + publish; routing escalation; abort procedure; orchestration.
|
||||
- `reference/refactor-mode.md` — Mode: refactor: mode entry; Phase 1 baseline; Phase 6 report; no-coverage stop prompt.
|
||||
- `reference/port-mode.md` — Mode: port: mode entry; Phase 0 prompt; preconditions warnings; A8/B6 approval prompts; B7 cleanup.
|
||||
- `reference/work-item-detection.md` — detected refactoring / bugfix / frontend work item: BEFORE Phase 1 of the matched mode.
|
||||
|
||||
- `core/checklists/implementation.md` — Implementation self-check checklist
|
||||
- `core/checklists/bugfix.md` — Bugfix self-check checklist
|
||||
- `core/checklists/refactoring.md` — Refactoring self-check checklist
|
||||
- `core/checklists/port.md` — Porting self-check checklist
|
||||
- `core/checklists/frontend.md` — Frontend self-check checklist
|
||||
- `core/checklists/code-review.md` — Code review checklist (self-attestation)
|
||||
- `core/checklists/pipeline-gate.md` — Cross-stage pipeline gate checklist
|
||||
- `core/skills/frontend/SKILL.md` — Frontend workflow (for frontend work items)
|
||||
- `core/skills/browser-debug/SKILL.md` — Interactive browser verification (for frontend work items) and UI bug reproduction (bugfix Phase 1)
|
||||
- `core/skills/review-code/SKILL.md` — Code review (next step after implementation)
|
||||
- `core/skills/verify/SKILL.md` — Integration + fidelity verification (pipeline-mode bugfix, large port)
|
||||
- `<instance-root>/archive/skills/` — Legacy requirements/design/plan/roadmap skills (archived [org-internal #3072] phase 3; their templates moved to `<instance-root>/archive/templates/`)
|
||||
- `reference/source-analysis.md` — Port Phase A1, A1.5, A1.7, A1.8 detailed processes & templates
|
||||
- `reference/mapping-and-design.md` — Port Phase A2, A3, A4 detailed processes & templates
|
||||
- `reference/large-port-pipeline.md` — Port Phase B1–B4 detailed processes
|
||||
- `reference/fidelity-verification.md` — Port Phase A6, B5 detailed processes & templates
|
||||
- `reference/source-analysis-templates.md` — Port B1 document format templates
|
||||
- `reference/capability-boundary-template.md` — Port A1.8/B1.8 13-dimension table
|
||||
- `reference/target-surface-template.md` — Port A1.7/B1.7 output format
|
||||
- `reference/port-report-template.md` — Port A7/B6 report format
|
||||
- Martin Fowler, _Refactoring: Improving the Design of Existing Code_ (2nd ed.)
|
||||
- `core/rules/` — Project engineering conventions (test commands, typecheck)
|
||||
- L2 wiki pages for style-guide, effect-rules, and database conventions (see the "L2 on-demand reference" section of AGENTS.md)
|
||||
@@ -0,0 +1,494 @@
|
||||
> Extracted from implement/SKILL.md (Mode: bugfix) — moved verbatim 2026-08-25, ticket [org-internal #3381].
|
||||
|
||||
### Mode: bugfix
|
||||
|
||||
Reproduce, isolate, and fix a bug with a regression test to prevent
|
||||
recurrence. For small, localized bugs, use standalone mode — the existing
|
||||
system behavior is the specification. For large, complex bugs, route through
|
||||
the full quality pipeline.
|
||||
|
||||
#### Process Overview
|
||||
|
||||
Every diamond below is a gate Developers rationalize skipping — especially
|
||||
under "the bug is obvious" pressure.
|
||||
|
||||
```dot
|
||||
digraph bugfix {
|
||||
rankdir=TB;
|
||||
node [shape=box, fontname="Helvetica"];
|
||||
|
||||
repro [shape=diamond, label="Bug reproduces?"];
|
||||
norepro [label="STOP: report cannot-reproduce\n(do not guess-patch)"];
|
||||
rc [shape=diamond, label="Root cause found\n(not just symptom)?"];
|
||||
symptom [label="Go deeper — do NOT\npatch the symptom"];
|
||||
route [shape=diamond, label="Routing:\nstandalone vs pipeline?"];
|
||||
escalate [label="Uncertain → escalate\nto pipeline mode"];
|
||||
rtest [shape=diamond, label="Regression test\nFAILS before fix?"];
|
||||
notest [label="Test does not cover\nthe bug — rewrite it"];
|
||||
fix [label="Phase 4: Fix\n(one change, root cause only)"];
|
||||
green [shape=diamond, label="Regression test PASS\n+ full suite green?"];
|
||||
done [shape=doublecircle, label="Phase 5/6:\nSelf-Check + Report"];
|
||||
|
||||
repro -> norepro [label="no"];
|
||||
repro -> rc [label="yes"];
|
||||
rc -> symptom [label="no"];
|
||||
rc -> route [label="yes"];
|
||||
route -> escalate [label="uncertain"];
|
||||
route -> rtest [label="standalone"];
|
||||
rtest -> notest [label="passes already"];
|
||||
rtest -> fix [label="fails (confirmed)"];
|
||||
fix -> green;
|
||||
green -> fix [label="no: fix + re-run"];
|
||||
green -> done [label="yes"];
|
||||
}
|
||||
```
|
||||
|
||||
#### Execution Modes
|
||||
|
||||
| Mode | Entry Point | Scope Source | Review Gate | Verify Gate |
|
||||
| ---------- | -------------------------------------- | ------------------ | ------------ | ----------- |
|
||||
| Standalone | User says "fix this bug" | Bug report + code | Optional (>20 lines or ≥3 files) | None |
|
||||
| Pipeline | User requests full-process bugfix, or auto-escalation | Bug report → requirements → design → plan → implement | Mandatory | Mandatory |
|
||||
|
||||
> **Routing override (ticket-seeded)**: the Optional/None gate declarations
|
||||
> above apply to *user-initiated* standalone mode. When the ticket carries a
|
||||
> `Kind/*` route whose `keep_gates` includes `review-code` / `verify` (e.g.
|
||||
> `Kind/Bug`, `Kind/Testing` — see `<instance-root>/workflow-routing.yaml`), those
|
||||
> gates are MANDATORY regardless of size. Per `core/rules/workflow-routing.md`,
|
||||
> a gate is mandatory if EITHER the route OR the skill requires it; skipping is
|
||||
> valid only when BOTH agree it is skippable.
|
||||
|
||||
In pipeline mode, the bug report becomes a bugfix work item that flows through
|
||||
the full requirements → design → plan → implement → review-code → verify
|
||||
pipeline.
|
||||
|
||||
#### Role & Responsibilities
|
||||
|
||||
The bugfix is owned and executed by the **Developer** (Worker). The
|
||||
Developer owns both implementation and bugfix — same role, same skill set.
|
||||
|
||||
The Developer is responsible for:
|
||||
|
||||
- Reproducing the bug from the description.
|
||||
- Identifying the root cause (not just patching the symptom).
|
||||
- Writing a regression test that fails before the fix and passes after.
|
||||
- Applying the minimal surgical fix — one change, one purpose.
|
||||
- Running the full test suite to confirm no regressions.
|
||||
|
||||
The Builder's role is to present the bugfix report and route it to code
|
||||
review if the change is non-trivial (> 20 lines or touches ≥ 3 files).
|
||||
|
||||
#### Tester focus for bugfix
|
||||
|
||||
The Tester role in bugfix writes **regression tests** and, uniquely,
|
||||
intervenes BEFORE the fix (a regression test must fail before the fix to
|
||||
prove the bug exists):
|
||||
|
||||
- **Failing regression test** — read `repro-notes.md` (the Developer's
|
||||
reproduction + root-cause analysis from Phases 1–2), write a test that
|
||||
exercises the exact bug path and FAILS with the bug's symptom. This MUST
|
||||
happen before Phase 4 (Fix), not after — it is Phase 3.
|
||||
- **Passing confirmation** — after the Developer's fix, the same test MUST
|
||||
pass (the Developer's green run in Phase 4 verifies this).
|
||||
- **Boundary regression tests** — inputs adjacent to the bug trigger,
|
||||
similar conditions that must NOT trigger the fix (guards against
|
||||
over-fixing), and error paths near the root cause.
|
||||
|
||||
**Bugfix-specific orchestration** (overrides the standard role-split flow):
|
||||
|
||||
Because a regression test must fail BEFORE the fix, the bugfix role split
|
||||
inverts the standard orchestration — the Tester dispatches between
|
||||
Phase 2 and Phase 4, not after the fix:
|
||||
|
||||
```
|
||||
[Worker: developer] Phase 1 reproduce + Phase 2 root cause
|
||||
→ write repro-notes (reproduction steps, root cause, bug path,
|
||||
expected behavior)
|
||||
↓ persist: wiki page `{slug}/repro-notes` (gitea_wiki__create_page)
|
||||
[Worker: tester] read repro-notes → Phase 3 write failing regression test
|
||||
(confirms FAIL before fix)
|
||||
↓ persist: wiki page `{slug}/test-report` (gitea_wiki__create_page, failing test confirmed)
|
||||
[Worker: developer] Phase 4 fix → run test:changed to green
|
||||
→ write impl-notes (post-fix behavior contract)
|
||||
↓ persist: wiki page `{slug}/impl-notes` (gitea_wiki__create_page, post-fix behavior contract)
|
||||
[Worker: tester] supplement boundary regression tests → run test:changed
|
||||
↓ persist: update wiki page `{slug}/test-report` (gitea_wiki__update_page, final)
|
||||
— consumed by the human stakeholder / next iteration planning for DoD regression-test evidence
|
||||
```
|
||||
|
||||
For small, single-file bugfixes with an obvious fix, a single Developer
|
||||
Worker may write the failing test, fix, and confirm green in one invocation
|
||||
— the split is optional for trivial fixes (Phase 3 + Phase 4 in one
|
||||
session). Force the split when the fix touches ≥ 2 files or the root cause
|
||||
spans ≥ 2 levels of indirection.
|
||||
|
||||
Pipeline-mode bugfixes route through the implement pipeline (see ### Mode:
|
||||
implement (default)), with the bugfix-specific Tester focus above layered
|
||||
on top of the standard role-split orchestration.
|
||||
|
||||
---
|
||||
|
||||
#### Preconditions (standalone)
|
||||
|
||||
Before starting the bugfix, confirm:
|
||||
|
||||
- [ ] Bug description exists (user's message, issue tracker link, or error log).
|
||||
- [ ] Existing codebase is accessible.
|
||||
- [ ] `core/checklists/bugfix.md` is accessible.
|
||||
|
||||
If the user describes a symptom without specifics, ask for:
|
||||
|
||||
```
|
||||
To fix this bug, I need:
|
||||
1. What is the expected behavior? (what should happen)
|
||||
2. What is the actual behavior? (what happens instead)
|
||||
3. Steps to reproduce.
|
||||
4. Any error messages, logs, or stack traces.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Routing Decision
|
||||
|
||||
After Phase 1 (reproduce) and Phase 2 (root cause), the Developer evaluates
|
||||
whether the fix qualifies for standalone or pipeline mode:
|
||||
|
||||
**Escalate to pipeline mode when ANY of:**
|
||||
|
||||
| Condition | Reason |
|
||||
| --------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| Fix touches ≥ 5 files | Cross-file changes need design review and integration tests |
|
||||
| Fix spans ≥ 2 modules / components | Multi-module fixes need architectural validation |
|
||||
| Root cause is in a design-level decision (protocol, schema, architecture) | Design change needs requirements + design review |
|
||||
| Fix requires data migration or schema change | Schema changes need data design review and migration plan |
|
||||
| Fix changes a public API or interface contract | API changes need interface design review and compatibility check |
|
||||
| Fix introduces a new dependency or changes an existing one | Dependency changes need review (DGN dimension, code review) |
|
||||
| Estimated lines changed > 50 | Large change carries high regression risk |
|
||||
| User explicitly requests full-process bugfix | User wants quality gates |
|
||||
|
||||
**Stay in standalone mode when ALL of:**
|
||||
|
||||
| Condition |
|
||||
| ---------------------------------------------- |
|
||||
| Fix is ≤ 4 files |
|
||||
| Fix is ≤ 1 module / component |
|
||||
| Fix is a logic error, not a design error |
|
||||
| No data migration or schema change |
|
||||
| No API or interface contract change |
|
||||
| No dependency change |
|
||||
| Estimated lines changed ≤ 50 |
|
||||
|
||||
If the Developer is uncertain, escalate. A false pipeline escalation costs a few
|
||||
extra review rounds. A false standalone decision risks missing a quality gate on
|
||||
a complex change.
|
||||
|
||||
**Big-bug relabel rule ([org-internal #3061])** — before the generic escalation below, split
|
||||
the triggers by kind:
|
||||
|
||||
- **Design-level triggers** (root cause is a design decision — protocol /
|
||||
schema / architecture; shared-contract or public-API change; data
|
||||
migration): do NOT push through bugfix and do NOT run the legacy pipeline
|
||||
escalation — **relabel the ticket `Kind/Feature`** and reroute via Step 0
|
||||
(DAG route; a 1–3 node small DAG is the expected shape for a single
|
||||
design-level fix). The fix work already done (repro notes, root cause)
|
||||
becomes node input, not wasted work.
|
||||
- **Mechanical size triggers only** (many files / many lines, same design):
|
||||
stay in bugfix — batch the change into iterations and keep the
|
||||
review-code + verify gates. Scale alone never justifies a relabel.
|
||||
|
||||
When escalating, the Developer pauses after Phase 2, reports the routing
|
||||
decision, and asks the user to confirm pipeline escalation:
|
||||
|
||||
```
|
||||
This bugfix qualifies for pipeline mode:
|
||||
- {N} files across {M} modules
|
||||
- Root cause: {design-level issue}
|
||||
- Estimated lines: {N}
|
||||
|
||||
→ Route through requirements → design → plan → implement → review → verify?
|
||||
(yes / no — proceed with standalone)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 1 — Understand & Reproduce
|
||||
|
||||
1. **Read relevant code** — find the module/component likely responsible for
|
||||
the bug. Use `grep` for error messages, `glob` for related files.
|
||||
|
||||
2. **Check existing tests** — do existing tests cover this code path? If a
|
||||
test exists but passes, the bug is in the test or in an uncovered branch.
|
||||
|
||||
3. **Reproduce** — run the relevant test(s) or manually trigger the bug.
|
||||
Confirm the actual behavior matches the bug report. Document the
|
||||
reproduction:
|
||||
|
||||
```markdown
|
||||
## Reproduction
|
||||
|
||||
**Steps**:
|
||||
|
||||
1. {step}
|
||||
2. {step}
|
||||
**Expected**: {what should happen}
|
||||
**Actual**: {what happens}
|
||||
```
|
||||
|
||||
4. If the bug CANNOT be reproduced, stop and report:
|
||||
|
||||
```
|
||||
Cannot reproduce the bug. Here's what I tried:
|
||||
- {step 1}
|
||||
- {step 2}
|
||||
|
||||
→ Is the environment different? Are there missing steps? Does a specific
|
||||
data state trigger it?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 2 — Isolate Root Cause
|
||||
|
||||
Trace from the symptom to the root cause:
|
||||
|
||||
1. **Symptom**: surface-level error (e.g. "500 on login").
|
||||
2. **Proximate cause**: the code that throws or returns wrong (e.g. "password
|
||||
hash comparison returns false for valid password").
|
||||
3. **Root cause**: the underlying defect (e.g. "password hashing config changed
|
||||
in commit abc123 but the stored hashes were not re-hashed").
|
||||
|
||||
```markdown
|
||||
## Root Cause Analysis
|
||||
|
||||
**Symptom**: {error message or wrong behavior}
|
||||
**Proximate cause**: {file}:{line} — {what the code does wrong}
|
||||
**Root cause**: {underlying defect — config, data, logic, or assumption}
|
||||
|
||||
**Introduced in**: {commit hash or version if known}
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
|
||||
- If you're fixing a symptom (e.g. adding a null check where the real bug is
|
||||
that null should never reach that line), stop and go deeper.
|
||||
- If you can't find the root cause after examining 3 levels of indirection,
|
||||
pause and report findings. Do NOT apply a surface-level patch.
|
||||
- **After Phase 2, evaluate the routing decision** (see Routing Decision table
|
||||
above). If the fix qualifies for pipeline mode, pause and present the
|
||||
escalation prompt before proceeding to Phase 3.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 3 — Write a Regression Test
|
||||
|
||||
Before fixing, write a test that proves the bug exists:
|
||||
|
||||
1. Write a test that exercises the bug path with the failing inputs.
|
||||
2. Run the test — it MUST fail with the bug's symptom.
|
||||
3. The test must be specific: test the exact condition that was broken, not
|
||||
a general "endpoint returns 200" test.
|
||||
|
||||
```markdown
|
||||
## Regression Test
|
||||
|
||||
- **File**: {path to test file}
|
||||
- **Test name**: {test function name}
|
||||
- **What it verifies**: {the expected behavior that was broken}
|
||||
- **Fails before fix**: ✅ (confirmed)
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
|
||||
- If you cannot write a test that fails (bug is non-deterministic, environment-
|
||||
specific), write the most targeted test you can and mark it `[flaky]`.
|
||||
- The test must fail NOW, before you apply the fix. If it passes already, the
|
||||
test does not cover the bug.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 4 — Fix
|
||||
|
||||
Apply the minimum change that resolves the root cause:
|
||||
|
||||
1. **One conceptual change per fix** — do not bundle a bugfix with refactoring,
|
||||
style changes, or "while I'm here" improvements.
|
||||
2. **Fix the root cause**, not the symptom. If the root cause is in a different
|
||||
file than the symptom, fix it there.
|
||||
3. **Update only what's necessary** — if fixing a null-safety bug requires
|
||||
adding a null check in one place, add one null check, not a comprehensive
|
||||
null-safety overhaul of the entire module.
|
||||
4. Run the regression test — it MUST pass.
|
||||
5. Run the relevant unit tests — all existing tests must still pass.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 5 — Self-Check & Report
|
||||
|
||||
##### Self-Check
|
||||
|
||||
1. **Typecheck**: `bun typecheck` — zero errors.
|
||||
2. **Lint**: `bun oxlint --deny-warnings` — zero errors.
|
||||
3. **Full test suite**: `bun run test:parallel` — all tests pass (new + existing).
|
||||
4. **Checklist**: verify every item in `core/checklists/bugfix.md`.
|
||||
5. **Publish bugfix report**: write the bugfix report as a wiki page via `wiki 读写 API(见 TERMINOLOGY)` with page_name `{slug}/bugfix-report` (`_shared/gitea-write-patterns.md` Pattern 1).
|
||||
|
||||
##### Report
|
||||
|
||||
```markdown
|
||||
## Bugfix Report
|
||||
|
||||
**Bug**: {one-line description}
|
||||
**Root cause**: {file}:{line} — {explanation}
|
||||
**Fix**: {file} — {single-sentence description of change}
|
||||
**Lines changed**: {N}
|
||||
**Regression test**: {test file}:{test name}
|
||||
|
||||
### Verification
|
||||
|
||||
- Regression test: {PASS | FAIL}
|
||||
- Full test suite: {N} passed, 0 failed
|
||||
- Typecheck: ✅
|
||||
- Lint: ✅
|
||||
|
||||
### Files Changed
|
||||
|
||||
| File | Lines | Purpose |
|
||||
| ------------------- | ------ | ------------------------------------------------------ |
|
||||
| `src/auth/login.ts` | +3, -1 | Fix password hash comparison when salt version changes |
|
||||
|
||||
---
|
||||
|
||||
**Handoff**: {if changes > 20 lines or ≥ 3 files → run `core/skills/review-code/SKILL.md`
|
||||
| otherwise → fix complete, no review needed}
|
||||
|
||||
> **Routing override**: when the ticket carries a `Kind/*` route whose
|
||||
> `keep_gates` includes `review-code` / `verify` (e.g. `Kind/Bug`,
|
||||
> `Kind/Testing`), those gates are MANDATORY even for small fixes — the
|
||||
> "no review needed" branch above does not apply (see the Execution Modes
|
||||
> routing-override note above).
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 5.5 — Issue Checklist Sync (standalone bugfix)
|
||||
|
||||
In standalone-bugfix mode there are no skill-exit boundaries between commit,
|
||||
PR, review, and CI — without explicit sync points the issue goes stale. Per
|
||||
the `issue-checklist-sync` L1 rule, sync at each externally visible
|
||||
transition (skip any step if no source issue exists):
|
||||
|
||||
| When | Sync action |
|
||||
|------|-------------|
|
||||
| After the fix commit | Check off fix-delivered ACs with `_(commit {sha}: file)_` |
|
||||
| After PR creation | Ensure the `## 当前状态` section exists (the PR row is auto-written by the status-sync poller — see `issue-checklist-sync.md` § Automated sync) |
|
||||
| After review convergence | Review-related ACs get `_(reviewed: round N PASS)_` (done by review-code Phase E 2.7) |
|
||||
| On CI state transitions | Update process-AC progress (e.g. "N consecutive green") with run number |
|
||||
| At verify PASS / close | Final sweep per `verify` Phase 5.6 |
|
||||
|
||||
Bugfix mode delegates the "after commit" step to the same mechanics as Phase
|
||||
4.6 above (fetch issue body → map `- [ ]` items → `工单 API(见 TERMINOLOGY)update`), and
|
||||
the PR-creation step to Phase 4.7.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 6 — Approval
|
||||
|
||||
Present the report:
|
||||
|
||||
```
|
||||
Bug fixed: {one-line description}
|
||||
- Root cause: {file}:{line}
|
||||
- {N} lines changed in {M} files
|
||||
- Regression test added: {test name}
|
||||
- Full test suite: ✅
|
||||
|
||||
→ {if review needed: "Run code review?" | else: "Fix complete. Approve?"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Common Rationalizations (bugfix)
|
||||
|
||||
Bugfixes fail from **pressure and false confidence** far more than from
|
||||
ignorance — "the bug is obvious" is the thought that precedes a symptom patch,
|
||||
a bundled diff, or a regression that surfaces weeks later. These are the
|
||||
excuses that precede every reopened bug. If you catch yourself thinking any
|
||||
row's "Excuse", stop: the "Reality" column is the exact rule you are about to
|
||||
break.
|
||||
|
||||
| Excuse | Reality (the rule being broken) |
|
||||
|--------|---------------------------------|
|
||||
| "Just add a null check where it crashes" | Symptom-patching. Phase 2: if you are fixing a symptom, stop and go deeper — the real defect is whatever let null reach that line. |
|
||||
| "Can't reproduce, but I'm sure it's X" | Phase 1: if the bug cannot be reproduced, stop and report. Guess-patching a non-reproduced bug fixes nothing verifiable. |
|
||||
| "3 levels deep, can't find it, patch the symptom" | Phase 2: after 3 levels of indirection with no root cause, pause and report — do NOT apply a surface patch. |
|
||||
| "Bug's obvious, I'll fix then add the test" | Phase 3: the regression test MUST fail before the fix. Fix-first means you test your fix, not the bug. |
|
||||
| "Test passed immediately, ship it" | Phase 3 Rules: a test that passes before the fix does not cover the bug — rewrite it until it fails. |
|
||||
| "While I'm in this file, also clean up…" | Phase 4 rule 1: one conceptual change per fix. Bundling refactors/style/other-fixes pollutes the regression signal. |
|
||||
| "Make the whole module null-safe while I'm here" | Phase 4 rule 3: update only what is necessary. Over-fixing turns a 3-line surgical fix into a high-risk diff. |
|
||||
| "Fix is isolated, skip the full suite" | Phase 4 rule 5 + Phase 5: the full suite catches regressions your isolated view cannot. |
|
||||
| "4 files but one module, standalone's fine" | Routing Decision: escalate when uncertain. A false-standalone call skips quality gates on a complex change. |
|
||||
|
||||
##### Incident Triage Carve-Out
|
||||
|
||||
When the bugfix occurs under **active production incident** pressure
|
||||
(user-facing outage, on-call escalation), the Phase 2→3 ordering can be
|
||||
**temporarily relaxed** — but never skipped:
|
||||
|
||||
1. A stop-gap (symptom patch) MAY ship first to restore service.
|
||||
2. BUT the full root-cause trace + failing regression test + proper
|
||||
root-cause fix MUST land in the **same incident window** — never deferred
|
||||
to "tomorrow" or "a follow-up ticket".
|
||||
3. If you defer, you have not fixed the bug — you have shipped a symptom patch
|
||||
with a promise. Promises are not regression tests.
|
||||
|
||||
This carve-out exists because the rationalization table above cannot resolve a
|
||||
*legitimate* priority conflict (service down vs process discipline). It
|
||||
resolves it by permitting triage but forbidding deferral.
|
||||
|
||||
---
|
||||
|
||||
#### Pipeline Mode (bugfix)
|
||||
|
||||
> **Legacy path retired ([org-internal #3072] phase 3, 2026-08-21)**: the full
|
||||
> requirements-elicitation → design → review-artifact(design-space) →
|
||||
> plan-iterations → review-artifact(plan) front-end was archived
|
||||
> (`<instance-root>/archive/skills/`). A big bug that needs a design-level decision
|
||||
> now relabels `Kind/Feature` and enters the DAG route (see the big-bug
|
||||
> relabel rule above) — repro + root-cause notes carry over as node input.
|
||||
> The abort criteria below still apply to any multi-stage bug run before
|
||||
> code is written.
|
||||
|
||||
When a bugfix escalates beyond standalone scope, the bug report becomes a
|
||||
pipeline input; the original bugfix phases (reproduce, root cause, regression
|
||||
test, fix) are embedded within the implement stage, and review-code + verify
|
||||
remain mandatory gates.
|
||||
|
||||
##### Pipeline Abort Criteria
|
||||
|
||||
Before any code is written in pipeline mode, abort the pipeline if ANY of:
|
||||
|
||||
| # | Condition | Action |
|
||||
|---|-----------|--------|
|
||||
| 1 | Bug no longer reproduces after environment change (strace re-isolation returns 0 reproductions, user confirms symptom resolved) | Write ABORT to wiki page `{slug}/ABORT` (wiki 读写 API(见 TERMINOLOGY)), preserve all completed artifacts, run retrospective |
|
||||
| 2 | Root cause hypothesis is falsified during re-isolation (e.g., strace shows suspected git spawn is NOT hanging) | Write ABORT.md, escalate to Architect for design revision OR abort pipeline |
|
||||
| 3 | Bug is resolved by external change (new binary build, dependency update, OS/kernel patch) | Write ABORT.md with resolution evidence, close without code changes |
|
||||
| 4 | Reproduction confidence < 3/5 after re-isolation attempt | Write ABORT.md if confidence cannot be improved within 1 re-isolation iteration |
|
||||
|
||||
**Abort procedure**:
|
||||
1. Write ABORT to wiki page `{slug}/ABORT` (wiki 读写 API(见 TERMINOLOGY)) documenting the reason, evidence, and which artifacts are preserved.
|
||||
2. Do NOT commit or merge the bugfix branch (no code was written).
|
||||
3. Run retrospective to extract process improvements.
|
||||
4. Archive artifacts to wiki page `_archive/{slug}/` (wiki 读写 API(见 TERMINOLOGY)) after retrospective.
|
||||
|
||||
**Scope**: these criteria apply before the implement stage. Once code is written, the pipeline proceeds through review-code → verify — abort is no longer valid.
|
||||
|
||||
##### Stage: Implement → Code Review → Verify
|
||||
|
||||
On the DAG route a bug-fix node's spec (ACs tracing to the repro + root cause)
|
||||
lives in `{epic-slug}/dag`; the Developer follows the bugfix Phases 1–6 (from
|
||||
standalone mode above) as the implementation method, then produces the
|
||||
standard implementation report (see ### Mode: implement (default), Phase 5).
|
||||
Code review runs all 10 dimensions against the bugfix changes. Verify runs
|
||||
the full DoD matrix including regression tests, integration tests, and NFR
|
||||
validation. Output pages: code review → `{slug}/reviews/code/final/report`;
|
||||
verification → `{slug}/05-verify-iteration-1`.
|
||||
@@ -0,0 +1,76 @@
|
||||
# Capability Boundary — Dimension Table & Output Template
|
||||
|
||||
> Used by Phase A1.8 (standalone) and Phase B1.8 (pipeline).
|
||||
> Read this file when executing the Capability Boundary Definition phase.
|
||||
> The 13-dimension table defines WHAT to analyze; the output template defines
|
||||
> HOW to record it.
|
||||
|
||||
## Artifact Dimensions
|
||||
|
||||
Every capability MUST be analyzed across ALL of these dimensions. A dimension
|
||||
with no artifacts is explicitly marked "N/A — none required" (not silently
|
||||
skipped):
|
||||
|
||||
| # | Dimension | What to list | Why it matters |
|
||||
|---|-----------|-------------|----------------|
|
||||
| 1 | **Source code files** | Every .ts/.tsx/.js file in the feature scope | The obvious one — but not the only one |
|
||||
| 2 | **Type definitions / interfaces** | Shared types, branded types, schemas (Zod/Schema.Class) | Types are consumed across files; missing types break compilation silently |
|
||||
| 3 | **Database schema / migrations** | Table definitions, column additions, migration SQL | Data layer changes are invisible in code diffs but block runtime |
|
||||
| 4 | **Configuration entries** | Config keys, settings entries, default values | Missing config = silent runtime failures |
|
||||
| 5 | **Environment variables** | Env vars read by the feature, VITE_* vars | Missing env vars = undefined behavior at runtime |
|
||||
| 6 | **CLI flags / commands** | CLI commands, flags, option definitions | CLI surface changes are easily forgotten |
|
||||
| 7 | **Theme / style files** | CSS files, theme JSON, tailwind config, token files | Styling is per-component and easily orphaned |
|
||||
| 8 | **Route definitions** | New routes, modified redirects, route guards | Routes are defined in a central file far from the feature code |
|
||||
| 9 | **Provider / context hierarchy** | New providers, insertion points in provider tree, context keys | Provider ordering bugs are silent and hard to debug |
|
||||
| 10 | **Build config changes** | vite.config, tsconfig, webpack, tailwind.config | Build config gates whether the feature compiles/bundles |
|
||||
| 11 | **Package.json dependencies** | New npm deps, version changes, workspace dep additions | Missing deps = import errors at runtime |
|
||||
| 12 | **Test files** | Unit tests, integration tests, test fixtures, test helpers | Tests are the fidelity contract — missing tests = unverified behavior |
|
||||
| 13 | **Shared package changes** | Changes to SDK, UI, core packages that the feature depends on | Cross-package deps are the #1 source of incomplete ports |
|
||||
|
||||
## Output Template
|
||||
|
||||
Publish to wiki page `port-{name}/source-analysis/capability-boundary` (standalone) or
|
||||
`port-{name}/source-analysis/12-capability-boundary` (pipeline) via `wiki 读写 API(见 TERMINOLOGY)`.
|
||||
|
||||
```markdown
|
||||
## Capability Boundary: {feature name}
|
||||
|
||||
### Dimension 1 — Source Code Files
|
||||
| Source File | Target Location | Status | Notes |
|
||||
| ----------- | --------------- | ------ | ----- |
|
||||
| src/context/tabs.tsx | src/context/tabs.tsx | ☐ | New file |
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
### Dimension 2 — Type Definitions / Interfaces
|
||||
| Source Type | Target Location | Status | Notes |
|
||||
| ----------- | --------------- | ------ | ----- |
|
||||
| Tab interface | src/context/tabs.tsx | ☐ | Co-located |
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
### Dimension 3 — Database Schema / Migrations
|
||||
| Source Schema | Target Migration | Status | Notes |
|
||||
| ------------- | ---------------- | ------ | ----- |
|
||||
| N/A — none required | — | ⏭ | Feature uses in-memory state only |
|
||||
|
||||
### Dimension 4 — Configuration Entries
|
||||
| Source Config Key | Target Config Key | Status | Notes |
|
||||
| ----------------- | ----------------- | ------ | ----- |
|
||||
| tabs.enabled | tabs.enabled | ☐ | New setting |
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
### Dimension 5 — Environment Variables
|
||||
| Source Env Var | Target Env Var | Status | Notes |
|
||||
| -------------- | -------------- | ------ | ----- |
|
||||
| VITE_TABS_LIMIT | VITE_TABS_LIMIT | ☐ | New |
|
||||
| N/A | — | ⏭ | No env vars required |
|
||||
|
||||
(... repeat for all 13 dimensions ...)
|
||||
|
||||
### Completeness Cross-Check
|
||||
| Cross-Check | Result |
|
||||
| ----------- | ------ |
|
||||
| Every A1 Source Function Inventory item appears in D1 or D2? | ✅ / ❌ |
|
||||
| Every A1.7 Structural Diff gap has a capability boundary entry? | ✅ / ❌ |
|
||||
| All 13 dimensions filled in? | ✅ / ❌ |
|
||||
| All ☐ items have deferral + reactivation path or are pre-implementation? | ✅ / ❌ |
|
||||
```
|
||||
@@ -0,0 +1,77 @@
|
||||
# Fidelity Verification — Detailed Processes
|
||||
|
||||
> Extracted from `implement/SKILL.md` (Mode: port) Phase A6 and B5.
|
||||
> Read this file when executing the test porting and fidelity verification phases.
|
||||
|
||||
---
|
||||
|
||||
## Phase A6 — Port Tests & Verify Fidelity
|
||||
|
||||
1. **Port every test** from the source — not just the happy path. Edge cases,
|
||||
error paths, and boundary tests must all be ported.
|
||||
2. **Rewrite assertions** to match the target test framework's assertion style.
|
||||
3. **Run ported tests** — they must pass. If a test fails:
|
||||
- Behavior mismatch: fix the implementation to match source behavior.
|
||||
- Test logic error (e.g. wrong assertion library syntax): fix the test.
|
||||
- Infrastructure gap (e.g. test tried to connect to Redis): adapt the test
|
||||
to the alternative from Phase A4.
|
||||
4. **Full test suite** — run the target project's existing tests + ported
|
||||
tests. No regression in existing tests.
|
||||
5. **Fidelity checklist** — for each source behavior, verify manually or
|
||||
automatically:
|
||||
|
||||
### Fidelity Verification Template
|
||||
|
||||
```markdown
|
||||
## Fidelity Verification
|
||||
|
||||
| Source Behavior | Tested? | Result | Notes |
|
||||
| --------------------------------- | ------------- | ------ | -------------------------------------------- |
|
||||
| User login with valid credentials | ✅ ported | PASS | |
|
||||
| User login with invalid password | ✅ ported | PASS | |
|
||||
| Session expiry at 1h | ✅ ported | PASS | Adapted to DB session store |
|
||||
| Rate limiting: 5 attempts / min | ⚠️ not ported | — | Target has no rate limiting infra → deferred |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase B5 — Verify (Integration + Fidelity)
|
||||
|
||||
After all chunks are implemented and reviewed, run
|
||||
`core/skills/verify/SKILL.md` with the fidelity baseline
|
||||
(wiki page `port-{name}/source-analysis/10-fidelity-baseline`, read via `wiki 读写 API(见 TERMINOLOGY)`) as the
|
||||
acceptance criteria:
|
||||
|
||||
- Every `FID-*` item must be traced to a passing test in the target project.
|
||||
- Full test suite (existing + ported) must pass with no regressions.
|
||||
- Typecheck and lint must be clean.
|
||||
- Fidelity deviations from any chunk are consolidated into a final
|
||||
fidelity report.
|
||||
- **Reverse coverage (Ported? audit)**: The Source Function Inventory
|
||||
(produced in Phase A1 / B1) "Ported?" column must have **zero** unexplained
|
||||
☐ entries. Any residual ☐ MUST carry a matching `[DEFER]` row — with a
|
||||
reactivation trigger — in the Port Fidelity Report. A bare ☐ is a FAIL,
|
||||
not a deferral. This closes the #1 port-completeness gap: functions that
|
||||
were never ported and never consciously deferred.
|
||||
- **Symbol-level completeness (SRC-CMP)**: Run an automated export-symbol
|
||||
diff between source and target packages. Every source symbol absent from
|
||||
the target MUST appear as `[DEFER]` in the report; a silent gap is a FAIL.
|
||||
|
||||
```bash
|
||||
diff <(codegraph exports <source-pkg>) <(codegraph exports <target-pkg>)
|
||||
# Each left-only symbol must be DEFER'd or ported — silent gaps fail B5.
|
||||
```
|
||||
|
||||
### Port Fidelity Report Template
|
||||
|
||||
```markdown
|
||||
## Port Fidelity Report
|
||||
|
||||
| FID-* | Behavior | Chunk | Target Test | Status |
|
||||
| ------- | ------------------ | ----------- | -------------------- | ------ |
|
||||
| FID-001 | Login valid creds | chunk-auth | auth/login.test.ts | PASS |
|
||||
| FID-002 | Login invalid pw | chunk-auth | auth/login.test.ts | PASS |
|
||||
| FID-004 | Rate limiting | chunk-auth | — | DEFER |
|
||||
|
||||
**Summary**: {X}/{Y} behaviors verified, {Z} deferred
|
||||
```
|
||||
@@ -0,0 +1,592 @@
|
||||
> Extracted from implement/SKILL.md (Mode: implement) — moved verbatim 2026-08-25, ticket [org-internal #3381].
|
||||
|
||||
### Mode: implement (default)
|
||||
|
||||
The standard implementation workflow for work items from an approved
|
||||
iteration plan. Implement a single work item, guided by the approved design,
|
||||
and self-verify before passing to code review.
|
||||
|
||||
#### Pre-flight
|
||||
|
||||
The pre-flight self-check prompt format ([org-internal #2599]), prepended to the Developer
|
||||
sub-agent's task prompt when `routes.{Kind}.preflight` is non-empty:
|
||||
|
||||
```
|
||||
Pre-flight self-check (evidence-based, from retrospective — verify each
|
||||
BEFORE writing code; if one is already satisfied, note why in impl-notes):
|
||||
1. {item} (evidence: {evidence})
|
||||
2. ...
|
||||
```
|
||||
|
||||
#### Process Overview
|
||||
|
||||
Every diamond below is a gate agents rationalize skipping. None are optional.
|
||||
|
||||
```dot
|
||||
digraph implement {
|
||||
rankdir=TB;
|
||||
node [shape=box, fontname="Helvetica"];
|
||||
|
||||
pre [shape=diamond, label="Preconditions\n(artifacts + reviews\nconverged)?"];
|
||||
abort [label="ABORT: list every\nmissing item"];
|
||||
p1 [label="Phase 1: Parse Context"];
|
||||
p2 [label="Phase 2: Plan\n(≤3 files per WI)"];
|
||||
scope [shape=diamond, label="Scope ≤3 files\nAND maps to a\ndesign component?"];
|
||||
gap [label="Flag design gap,\nDO NOT invent decisions"];
|
||||
p3 [label="Phase 3: Implement\n(design-exact, tests cover AC)"];
|
||||
p4 [label="Phase 4: Self-Check\n(typecheck + lint +\ntest:changed + review checklist)"];
|
||||
clean [shape=diamond, label="0 BLOCKERs\nand 0 MAJORs?"];
|
||||
p5 [label="Phase 5: Report\n(AC → test traceability)"];
|
||||
p6 [shape=doublecircle, label="Phase 6: Handoff\nto review-code"];
|
||||
|
||||
pre -> abort [label="no"];
|
||||
pre -> p1 [label="yes"];
|
||||
p1 -> p2;
|
||||
p2 -> scope;
|
||||
scope -> gap [label="no"];
|
||||
scope -> p3 [label="yes"];
|
||||
p3 -> p4;
|
||||
p4 -> clean;
|
||||
clean -> p4 [label="no: fix + re-run"];
|
||||
clean -> p5 [label="yes"];
|
||||
p5 -> p6;
|
||||
}
|
||||
```
|
||||
|
||||
#### Tester focus for implement
|
||||
|
||||
The Tester role in implement writes **boundary + contract tests**:
|
||||
|
||||
- **Contract tests** — for every public API signature in `impl-notes.md`,
|
||||
verify the documented inputs/outputs, error paths, and side effects.
|
||||
Each acceptance criterion (node `acceptance_criteria` in `{epic-slug}/dag`;
|
||||
historically `04-plan-05-acceptance-criteria`) MUST map to at least one
|
||||
test.
|
||||
- **Boundary tests** — empty values, malformed input, permission
|
||||
boundaries, concurrency edges, and the edge cases the node spec's decision
|
||||
tables / state machines imply.
|
||||
- **Failure-path tests** — every error scenario the node's cross-session
|
||||
edge contracts (historically the interface design,
|
||||
`03-design-04-interface-design`) specify.
|
||||
|
||||
The Developer's Phase 4 self-check (`bun run test:changed` to green) covers
|
||||
the happy path and existing tests; the Tester's job is the cases the
|
||||
Developer is structurally biased to miss.
|
||||
|
||||
#### Preconditions
|
||||
|
||||
> **Publish target (tiered targeting retired, [org-internal #3072] phase 3)**: the
|
||||
> `Size/*`-tiered publish rule (`rules/workflow-routing.md` §"Publish target
|
||||
> by Size/* tier — RETIRED") was retired with the legacy producer skills.
|
||||
> Artifacts publish where the live mode puts them: DAG task mode → node spec
|
||||
> in the frozen `{epic-slug}/dag` copy (see the DAG-mode input path below);
|
||||
> standalone bugfix → `{slug}/bugfix-report` + issue body per bugfix Phase 5.
|
||||
> Legacy tiered locations (`{slug}/02-03-req-design`, `{slug}/04-plan-*`, …)
|
||||
> stay readable for historical runs via `_shared/gitea-read-patterns.md`.
|
||||
|
||||
> **DAG-mode input path** (DAG ticket pipeline — `Kind/Epic` / `Kind/Feature`
|
||||
> DAG parent, routes-table direct):
|
||||
> DAG-routed tickets **ignore `Size/*`** (`core/skills/analyze-dag/SKILL.md`).
|
||||
> When the ticket routes through the DAG pipeline, the tiered Preconditions
|
||||
> below are replaced by the node spec: the work item and its acceptance
|
||||
> criteria resolve from the **frozen DAG copy** wiki page `{epic-slug}/dag`
|
||||
> (and the `{epic-slug}/dag-nodes/{node-id}` subpages when AC detail is sunk)
|
||||
> plus the node ticket's issue body — there is no `{slug}/04-plan-*` page and
|
||||
> no `Size/*`-tiered req/design page. The design-space + iteration-plan review
|
||||
> convergence preconditions are replaced by the **review-dag single-gate
|
||||
> convergence**: `octopus review status --stage review-dag` must show state
|
||||
> `success` before the node is implemented.
|
||||
|
||||
> **DAG-route read map** (applies to Phase 1 read inputs and the Phase 3/4
|
||||
> artifact references below — mirror `verify/SKILL.md`'s DAG branch): when
|
||||
> DAG-routed, resolve each legacy tiered artifact reference (any mention below
|
||||
> of `{slug}/04-plan-*` / `{slug}/03-design-*` pages) from the frozen
|
||||
> DAG copy instead:
|
||||
>
|
||||
> - Work item — `{slug}/04-plan-04-iteration-assignment` / issue body → the
|
||||
> node spec in `{epic-slug}/dag` + the node ticket's issue body.
|
||||
> - Acceptance criteria — `{slug}/04-plan-05-acceptance-criteria` / issue body
|
||||
> → the node `acceptance_criteria` in `{epic-slug}/dag` (+
|
||||
> `{epic-slug}/dag-nodes/{node-id}` subpages when AC detail is sunk) + the
|
||||
> node ticket's issue body.
|
||||
> - `test_id` (測試用例 ID) declared in `04-plan-05-acceptance-criteria` → the
|
||||
> `test_id` declared on the node AC in `{epic-slug}/dag`.
|
||||
> - Design sections — `{slug}/03-design-**` / `{slug}/02-03-req-design` → the
|
||||
> node spec + cross-session edge contracts in the frozen DAG copy (design
|
||||
> detail is folded into node AC + contracts; there is no `{slug}/03-design-*`
|
||||
> page).
|
||||
> - Interface design — `03-design-04-interface-design` → the node's
|
||||
> cross-session edge contracts in `{epic-slug}/dag`.
|
||||
> - Component mapping — `{slug}/03-design-08-traceability` → the node
|
||||
> `req_refs` + component field in `{epic-slug}/dag`.
|
||||
>
|
||||
> At Phase 6 handoff, pass `mode: "dag-task"` to review-code (its DAG Task
|
||||
> Mode keys off the same frozen-DAG-copy detection).
|
||||
|
||||
Before starting implementation, confirm:
|
||||
|
||||
> **Legacy pipeline preconditions retired ([org-internal #3072] phase 3, 2026-08-21)**: the
|
||||
> tier-dependent requirements/design/plan artifact-existence checks and the
|
||||
> design-space / iteration-plan review-convergence checks that used to head
|
||||
> this list belonged to the archived legacy pipeline (`<instance-root>/archive/`).
|
||||
> Live input modes: DAG task mode (node spec from the frozen
|
||||
> `{epic-slug}/dag` copy — see the DAG-mode input path above; convergence
|
||||
> precondition = `octopus review status --stage review-dag` shows `success`)
|
||||
> and standalone modes (bugfix / refactor / port — the request itself is the
|
||||
> spec). Historical req/design/plan pages stay readable via
|
||||
> `_shared/gitea-read-patterns.md`.
|
||||
|
||||
- [ ] Work item is specified (DAG node ticket `{node-id}`, or a clear task
|
||||
description in standalone modes).
|
||||
- [ ] `core/checklists/implementation.md` is accessible.
|
||||
- [ ] `slug` matches the run's slug (DAG: `{epic-slug}`).
|
||||
- [ ] 跨阶段门控清单: `core/checklists/pipeline-gate.md` is accessible and
|
||||
its DAG 路由变体 section has been confirmed item by item. Specifically:
|
||||
the frozen DAG copy exists and the single gate has converged; the
|
||||
node's cross-session upstream dependencies are at terminal state
|
||||
(`ready`). If any dependency is not complete → abort, listing the
|
||||
blocked nodes.
|
||||
|
||||
**If any precondition is unmet, abort and inform the user.** Refer to
|
||||
`core/checklists/pipeline-gate.md` for the complete gate checklist. List
|
||||
every missing artifact, every un-converged review, and every blocked dependency
|
||||
explicitly so the user knows exactly what upstream work remains before
|
||||
implementation can begin. Refer to the Recovery Protocol in
|
||||
`core/checklists/pipeline-gate.md` to determine the recovery action for
|
||||
each missing item.
|
||||
|
||||
#### Work-item selection
|
||||
|
||||
When the user requests implementation without specifying a work item, resolve
|
||||
the work-item list from the frozen DAG copy: the ready/pending task nodes in
|
||||
`{epic-slug}/dag` (via `wiki 读写 API(见 TERMINOLOGY)`), cross-checked against the
|
||||
node tickets on the Epic's `## DAG 状态` table. (Legacy tier-based resolution
|
||||
via `{slug}/04-plan-04-iteration-assignment` was archived 2026-08-21,
|
||||
[org-internal #3072] phase 3.) Present the current ready nodes for selection:
|
||||
|
||||
```
|
||||
Current iteration: Iteration {N}: {Goal}
|
||||
Available work items:
|
||||
| Work Item | Description | Complexity | Status |
|
||||
|-----------|-------------|------------|--------|
|
||||
| WI-001 | ... | 3 | PENDING |
|
||||
| WI-002 | ... | 2 | PENDING |
|
||||
|
||||
→ Which work item should be implemented?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 1 — Parse Context
|
||||
|
||||
> **Pipeline stage**: if the source issue exists, move it to the `implement`
|
||||
> column on the Pipeline Stages board per `_shared/gitea-write-patterns.md`
|
||||
> Pattern 7.5. Skip if no source issue exists.
|
||||
|
||||
Read the upstream artifacts to build a complete implementation context.
|
||||
Resolve inputs per the DAG-route read map (Preconditions above); standalone
|
||||
modes read the request/bug report instead:
|
||||
|
||||
1. **Work item** — the node spec in `{epic-slug}/dag` (+ the
|
||||
`{epic-slug}/dag-nodes/{node-id}` subpage when detail is sunk) and the
|
||||
node ticket's issue body:
|
||||
- Node id, title, complexity (`size_attrs`).
|
||||
- Requirements covered (`req_refs`).
|
||||
- Component(s) involved (node component field).
|
||||
|
||||
2. **Acceptance criteria** — the node `acceptance_criteria` in
|
||||
`{epic-slug}/dag` (+ sunk subpages) and the node ticket's issue body:
|
||||
- Every falsifiable AC (`AC-{n}`) and `NFR:` entry.
|
||||
- The declared 测试用例 ID (`test_id`) for each criterion — these drive the
|
||||
Red → Green test-first order in Phase 3 and are the handshake with `verify`
|
||||
(DOD-1.6).
|
||||
|
||||
3. **Design context** — the node spec + the node's cross-session edge
|
||||
contracts in the frozen DAG copy (design detail is folded into node AC +
|
||||
contracts; there is no separate design page). Historical
|
||||
`{slug}/03-design-*` pages from legacy runs stay readable.
|
||||
|
||||
4. **Existing codebase** — use `glob` and `grep` to locate:
|
||||
- Existing files in the component's directory.
|
||||
- Existing tests.
|
||||
- Existing type definitions, schemas, configuration files the work item
|
||||
touches.
|
||||
|
||||
**Output**: internal only. The Developer MUST have read every referenced
|
||||
design file before writing a single line of code.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 2 — Plan Implementation
|
||||
|
||||
Before writing code, produce a brief implementation plan:
|
||||
|
||||
```markdown
|
||||
## Implementation Plan: {WI-ID}
|
||||
|
||||
**Work item**: {description}
|
||||
**Files to create**:
|
||||
|
||||
- `path/to/new/file.ts` — {purpose}
|
||||
|
||||
**Files to modify**:
|
||||
|
||||
- `path/to/existing/file.ts` — {what changes, why}
|
||||
|
||||
**Design compliance**:
|
||||
|
||||
- Component: {COMP-XXX} from {design-file}
|
||||
- Interface: {iface-name} from {design-file}
|
||||
- Data entity: {entity-name} from {design-file}
|
||||
|
||||
**Acceptance criteria to satisfy**:
|
||||
|
||||
- [ ] {criterion 1}
|
||||
- [ ] {criterion 2}
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
|
||||
- If the implementation plan reveals that the work item touches > 3 files,
|
||||
pause and ask: "This work item spans {N} files. Is the scope correct, or
|
||||
should it be split?" The Builder (or user) MUST split it into smaller
|
||||
work items each touching ≤ 3 files before proceeding.
|
||||
- If the work item requires a file that doesn't map to any design component,
|
||||
flag a design gap and abort. Do NOT invent design decisions.
|
||||
|
||||
Present the plan to the user:
|
||||
|
||||
```
|
||||
Implementation plan for {WI-ID}:
|
||||
- {N} files to create, {M} files to modify
|
||||
- {K} acceptance criteria
|
||||
|
||||
→ Proceed? (yes / no / revise)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 3 — Implement
|
||||
|
||||
Write code following these rules:
|
||||
|
||||
##### Design Discipline
|
||||
|
||||
- Component interfaces, method signatures, and return types MUST match the
|
||||
design document exactly.
|
||||
- Data model fields, types, and relationships MUST match the data design.
|
||||
- API endpoints, request/response schemas, and status codes MUST match the
|
||||
interface design.
|
||||
- If a design decision proves impossible in practice, stop and report the gap
|
||||
to the Builder. Do NOT silently deviate.
|
||||
|
||||
##### Code Quality
|
||||
|
||||
- Follow existing project conventions (read neighbor files first to
|
||||
understand patterns).
|
||||
- Use existing libraries and utilities already in the codebase — do not
|
||||
introduce new dependencies without explicit justification.
|
||||
- Keep functions small and single-purpose — but per `rules/style-guide`, do NOT
|
||||
preemptively extract single-use helpers; inline at the call site unless the
|
||||
helper is reused, hides a genuinely complex boundary, or has a clear
|
||||
independent name that improves the caller.
|
||||
- Handle errors at the appropriate layer (matching the design's error
|
||||
handling strategy).
|
||||
- Write self-documenting code; add comments only for genuinely non-obvious
|
||||
logic.
|
||||
- Document all new/modified public APIs inline (JSDoc/TSDoc/pydoc/etc.)
|
||||
with parameter descriptions, return types, and thrown errors.
|
||||
- If the project has an API documentation file (e.g. OpenAPI spec, API.md),
|
||||
update it to reflect the new endpoints, schemas, or behavior changes.
|
||||
|
||||
##### Test Discipline
|
||||
|
||||
- **Test-first (Red → Green) for declared test_ids.** For every acceptance
|
||||
criterion (node AC in `{epic-slug}/dag`, whose `test_id` mapping is declared
|
||||
inline; historically the `04-plan-05-acceptance-criteria` table) that
|
||||
declares a `test_id`, write that test FIRST and confirm it fails for the
|
||||
intended reason (Red) before writing the implementation that satisfies it
|
||||
(Green). The test's `file-path :: test-name` MUST match the declared
|
||||
`test_id` exactly — this is the implement-side handshake with `verify`
|
||||
(DOD-1.6). A `test_id` marked `MANUAL` or `BENCH:<script>` is implemented
|
||||
per its method and is exempt from the Red step. If a test already passes
|
||||
against existing code (the behavior is already present), note it in the
|
||||
Phase 5 report rather than forcing an artificial failure.
|
||||
- Write tests that verify the acceptance criteria.
|
||||
- Tests must be independent (no shared mutable state).
|
||||
- Test edge cases identified in the acceptance criteria.
|
||||
- Test failure paths that the design specifies.
|
||||
|
||||
##### Incremental Commitments
|
||||
|
||||
- Implement in dependency order within the work item: shared types first,
|
||||
then data access, then business logic, then API handlers.
|
||||
- After each coherent unit, run typecheck to catch errors early.
|
||||
|
||||
---
|
||||
|
||||
#### Common Rationalizations
|
||||
|
||||
Implementation fails far more often from **pressure** than from ignorance — the
|
||||
Developer knows the rules and rationalizes skipping them under context or time
|
||||
pressure. These are the excuses that precede every review blocker and silent
|
||||
defect. If you catch yourself thinking any row's "Excuse", stop: the "Reality"
|
||||
column is the exact rule you are about to break, and breaking it is what turns
|
||||
a one-pass implementation into a multi-round review.
|
||||
|
||||
| Excuse | Reality (the rule being broken) |
|
||||
|--------|---------------------------------|
|
||||
| "Design says X, but Y is simpler/better" | Silent deviation is a hidden design gap. Phase 3 Design Discipline: stop and report to the Builder — never silently deviate. |
|
||||
| "Small change, a test is overkill" | A one-line edit can break a contract. Every acceptance criterion maps to ≥1 test (Phase 4 Brownfield check). 30 seconds now vs. a review blocker later. |
|
||||
| "I'll write tests after it works" | Tests-after verify what you built, not what was required — you test your own bias, not the spec. |
|
||||
| "Typecheck passed, lint is cosmetic" | Lint is a Phase 4 gate, not optional polish. Failing lint is an automatic review blocker. |
|
||||
| "Self-check passed, I'll trust it" | Rubber-stamping misses the MAJORs the formal review will catch. Rule: if YOU can find a MAJOR, fix it now — the first review should never discover what you could have. |
|
||||
| "This neighbor looks buggy, I'll fix it too" | Scope creep. Log it as an observation in the report; do not fix unrelated code (Greenfield/Brownfield rule). |
|
||||
| "Spans 5 files but it's one logical change" | The ≤3-files rule is structural, not aesthetic. Split the work item via the Builder (Phase 2 rule). |
|
||||
| "Design is ambiguous here, I'll pick the obvious option" | Inventing a design decision is a Phase 2 abort condition. Flag the gap; do not guess. |
|
||||
| "Already manually verified it works" | Manual ≠ systematic — no record, can't re-run, can't bisect. `bun run test:changed` is the evidence the report demands. |
|
||||
| "Report is busywork, the diff speaks for itself" | No report → review-code cannot trace AC→test. Phase 5 is the handoff contract; skip it and the review stalls. |
|
||||
| "X× improvement — assumed, no measurement" | Quick-measure before it becomes an AC. Unverified assumptions in ACs waste framing cost ([org-internal #1932]: YAML token density assumed 2-3×, measured 0.95 — hypothesis rejected by data). |
|
||||
|
||||
---
|
||||
|
||||
#### Phase 4 — Self-Check
|
||||
|
||||
After writing all code, run the project's verification commands:
|
||||
|
||||
1. **Typecheck**: `bun typecheck` (or project-equivalent). Fix all type errors.
|
||||
2. **Lint**: `bun oxlint --deny-warnings` (repo root — the review-code
|
||||
mechanical gate's canonical lint invocation; `bun lint` is the package-script
|
||||
alias). Fix all lint errors.
|
||||
3. **Tests**: `bun run test:changed` (or project-equivalent). All affected tests must pass.
|
||||
4. **Post-deletion cleanup** (mandatory when any code was removed): If files or code blocks were deleted (dead code, test cleanup, refactored-out modules), re-run `bun oxlint --deny-warnings` specifically to catch orphaned imports and unused variables — these are the most common post-deletion regressions. Re-run `bun typecheck` to catch orphaned type references to deleted modules.
|
||||
|
||||
Then self-check against `core/checklists/implementation.md`:
|
||||
|
||||
- Verify every checklist item marked PRE (pre-implementation) was satisfied
|
||||
before coding.
|
||||
- Verify every checklist item marked POST (post-implementation) is satisfied
|
||||
now.
|
||||
- For any failed checklist item, fix the code before reporting.
|
||||
|
||||
##### Brownfield Self-Check (additional)
|
||||
|
||||
For brownfield work items, additionally:
|
||||
|
||||
1. **Design spec cross-check**: Re-read the node's cross-session edge
|
||||
contracts in the frozen DAG copy (historically the design's interface
|
||||
design section, `03-design-04-interface-design`). Verify every interface
|
||||
promise — method signatures, return types, output formats, error messages,
|
||||
config field names, param descriptions — is satisfied exactly as specified.
|
||||
Schema annotations MUST match actual code behavior.
|
||||
2. **Test coverage**: For each new function, method, or exported API added,
|
||||
confirm at least one test exercises it. If `bun run test:changed` reports zero new
|
||||
tests, add them before handoff.
|
||||
|
||||
##### Review Readiness Self-Check (mandatory before handoff)
|
||||
|
||||
Before submitting to code review, the Developer MUST self-attest against the
|
||||
code review checklist. This reduces round-trips by catching common defects
|
||||
before the first review submission. **The self-check must achieve 0 BLOCKERs
|
||||
and 0 MAJORs before handoff** — if the Developer can find a MAJOR issue during
|
||||
self-check, the formal reviewers will find it too.
|
||||
|
||||
1. **Run the code review checklist**: Read `core/checklists/code-review.md`
|
||||
and self-attest that the code likely passes, for each of its 10 dimensions
|
||||
(COR, DGN, SEC, PERF, TST, STY, DBT, A11Y, DOC, TRC — the authoritative
|
||||
dimension set lives in the checklist's section headers and
|
||||
`review-code/reference/code-review-dimensions.md`; do NOT hand-maintain a
|
||||
copy here).
|
||||
2. Record the self-attestation in the Phase 5 report under "Review Readiness"
|
||||
as a pass/fail per dimension. Any FAIL dimension MUST be fixed before handoff.
|
||||
3. **Hard gate**: self-check MUST find 0 BLOCKERs and 0 MAJORs. If the
|
||||
Developer finds even one MAJOR, fix it and re-run self-check before handoff.
|
||||
The first formal code review should never discover issues the Developer
|
||||
could have caught themselves.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 4.5 — Iteration Completion Commit
|
||||
|
||||
After ALL work items in the current iteration have been implemented and passed
|
||||
Self-Check (Phase 4), create a git commit BEFORE proceeding to the next
|
||||
iteration. This preserves per-iteration traceability and enables `git bisect`
|
||||
per iteration.
|
||||
|
||||
##### Commit Rules
|
||||
|
||||
1. Commit after the last WI of the iteration is done and self-checked.
|
||||
2. Commit message format: `[{chunk-id}][{iteration}] {summary}`.
|
||||
- Example: `[chunk-resolution][iter-1] feat: add two-pass chain resolution engine`
|
||||
3. **Commit body is REQUIRED for non-trivial commits** (any commit touching > 1 file
|
||||
or > 20 LOC). The body MUST contain:
|
||||
- **What**: a 1-3 line summary of the changes (files + purpose), including
|
||||
the work item ID (`WI-{NNN}`) the commit delivers — code-review TRC 10.1
|
||||
requires the commit/PR description to carry the work item ID.
|
||||
- **Why**: the design/requirement motivation (cite REQ-ID or ADR if applicable).
|
||||
- **Evidence**: test names or verification commands run (e.g. `90 compaction
|
||||
tests pass`).
|
||||
- Subject-only commits are acceptable only for single-line fixes or doc tweaks.
|
||||
4. Include all source + test files from the iteration.
|
||||
5. After commit, proceed to Phase 4.6 (Issue Checklist Sync), then Phase 5
|
||||
(Report) for the iteration, then start the next iteration's WIs.
|
||||
|
||||
##### Multi-Iteration Workflow
|
||||
|
||||
```
|
||||
Iteration 1 WIs → Self-Check → Commit [iter-1] → Checklist Sync → Code Review →
|
||||
Iteration 2 WIs → Self-Check → Commit [iter-2] → Checklist Sync → Code Review → Merge
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 4.6 — Issue Checklist Sync (progressive)
|
||||
|
||||
After committing the iteration, sync the source issue's checklist so
|
||||
stakeholders see progress in real time. This is mandated by the
|
||||
`issue-checklist-sync` L1 rule — follow its "How to sync (each point)"
|
||||
procedure (identify source issue → fetch body → map → update, preserving
|
||||
non-checklist content); this phase adds only the implement-specific annotation:
|
||||
|
||||
- **Stage-specific row**: for each `- [ ]` item the iteration's work satisfies,
|
||||
mark `- [x]` and append `_(commit {sha}: file/component)_` or
|
||||
`_(PR #NNN: file)_`.
|
||||
- **Do NOT touch items outside this iteration's scope** — they will be caught
|
||||
at a later sync point (next iteration, DAG-freeze aggregation sync, or
|
||||
verify Phase 5.6). Only check off what this iteration actually delivered.
|
||||
|
||||
This is a **progressive** sync: the checklist fills in incrementally as
|
||||
iterations complete, giving stakeholders a live view of progress without
|
||||
waiting for the final verify gate.
|
||||
|
||||
#### Phase 4.7 — PR-Creation Sync
|
||||
|
||||
The session pushes its branch and reports `status=done branch=<ref> verify=…
|
||||
risk=…`; the orchestrator admits the PR (serially, one open at a time) —
|
||||
workers never open PRs (TD-678/[org-internal #4425]; `uncoordinated` self-open only when
|
||||
the orchestrator is unreachable). Once that PR exists, update the source
|
||||
issue so stakeholders see the mergeable state without waiting for code
|
||||
review. Mandated by the `issue-checklist-sync` L1 rule; skip if no source
|
||||
issue exists.
|
||||
|
||||
> PR shape per mode: default = one 1:1 PR per task (body carries the worker
|
||||
> report); batch-mode epics ([org-internal #3731], per-epic opt-in) = the orchestrator
|
||||
> composes ONE batch PR per iteration via the `land-batch` skill. This phase
|
||||
> then runs per member issue as usual (N times), each pointing at its PR
|
||||
> (batch: the single batch PR); the poller writes the PR/CI/review rows
|
||||
> against every member issue (multi-close-ref fan-out).
|
||||
|
||||
1. Re-fetch the issue body via `工单 API(见 TERMINOLOGY)get`.
|
||||
2. **Ensure the `## 当前状态` live-status section exists** (create it if
|
||||
absent — MANDATORY for incident / standalone-bugfix flows; for quiet
|
||||
pipeline flows, create it only if it already exists, otherwise skip). The
|
||||
`PR` row itself is written by the `status-sync` poller
|
||||
(`.gitea/scripts/status-sync-poll.ts`), NOT this skill — do NOT manually
|
||||
`工单 API(见 TERMINOLOGY)update` the PR / 代码评审 / CI rows (per
|
||||
`issue-checklist-sync.md` § Automated sync).
|
||||
3. If this is an Epic task list, append the PR reference to the row that this
|
||||
iteration's work corresponds to.
|
||||
4. Preserve all non-checklist content.
|
||||
5. **Never hand-sync main into the PR branch.** Keeping the PR mergeable is
|
||||
the keep-mergeable workflow's job: once review converges the orchestrator
|
||||
labels the PR `ready-to-merge` and the server-side keep-mergeable cron
|
||||
(`.gitea/scripts/keep-mergeable.ts`, driven by
|
||||
`script/keep-mergeable-cron.sh` under a systemd timer) fetches the PR head,
|
||||
probes `merge-tree --write-tree`, and pushes a non-force `commit-tree` merge
|
||||
into the head branch (the retired `POST /pulls/{n}/update-branch` API path
|
||||
returned 405 on this instance — see AGENTS.md "PR keep-mergeable").
|
||||
Hand-written `chore: merge origin/main (keep PR mergeable)` commits are
|
||||
retired — each one re-triggered the full CI surface for near-zero re-tested
|
||||
risk.
|
||||
|
||||
> **Kanban column lifecycle**: automated (`工单 API(见 TERMINOLOGY)create` → Backlog,
|
||||
> `gitea_pull__create` → Review; no manual moves). Single shared reference:
|
||||
> `_shared/gitea-write-patterns.md` Pattern 7.5; column semantics: wiki
|
||||
> `kanban-lifecycle`.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 5 — Report
|
||||
|
||||
Produce an implementation report:
|
||||
|
||||
```markdown
|
||||
## Implementation Report: {WI-ID}
|
||||
|
||||
**Work item**: {description}
|
||||
**Iteration**: {iteration number}: {goal}
|
||||
|
||||
### Files Changed
|
||||
|
||||
| File | Action | Purpose |
|
||||
| ------------------ | -------- | -------------- |
|
||||
| `path/to/file.ts` | created | {purpose} |
|
||||
| `path/to/other.ts` | modified | {what changed} |
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
| Criterion | Status | Evidence |
|
||||
| ------------- | ------ | ---------------------------------- |
|
||||
| {criterion 1} | ✅ | {test name or manual verification} |
|
||||
| {criterion 2} | ✅ | {test name or manual verification} |
|
||||
|
||||
### Verification Results
|
||||
|
||||
- Typecheck: {pass / fail + error count}
|
||||
- Lint: {pass / fail + warning count}
|
||||
- Tests: {N} passed, {M} failed, {K} skipped
|
||||
|
||||
### Design Deviations
|
||||
|
||||
{list any intentional deviations from design with rationale, or "None"}
|
||||
|
||||
### Open Items
|
||||
|
||||
{anything incomplete with reason, or "None"}
|
||||
|
||||
---
|
||||
|
||||
**Handoff**: Ready for `core/skills/review-code/SKILL.md`
|
||||
```
|
||||
|
||||
**Persist before returning ([org-internal #2847])**: the Developer MUST write the final
|
||||
report above to disk as its LAST action, BEFORE returning it —
|
||||
`<runs-root>/{slug}/workers/{chunk-id}-worker-{seq}.md` when the Tier 1
|
||||
run workspace exists, else `/tmp/octopus/{chunk-id}-worker-{seq}.md`
|
||||
(`{chunk-id}`/`{seq}` come from the dispatch prompt — see
|
||||
`../_shared/worker-report-persistence.md`). The persisted copy is the
|
||||
report of record; the task notification is a convenience copy. The same
|
||||
step applies to EVERY mode's report phase (bugfix Phase 5, refactor
|
||||
Phase 6, port report) — no worker return may exist only in the task
|
||||
notification.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 6 — Handoff to Code Review
|
||||
|
||||
Present the report to the user and signal readiness for review:
|
||||
|
||||
```
|
||||
Implementation of {WI-ID} complete.
|
||||
- {N} files changed ({C} created, {M} modified)
|
||||
- {T} tests passing
|
||||
- All acceptance criteria satisfied
|
||||
- Typecheck + lint clean
|
||||
|
||||
→ Run code review? (yes / no)
|
||||
```
|
||||
|
||||
Do NOT mark the work item as complete until code review passes.
|
||||
|
||||
To notify workflow completion, call the `signal_stage_done` tool.
|
||||
|
||||
#### Legacy notes
|
||||
|
||||
> **Publish target (tiered targeting retired, [org-internal #3072] phase 3)**: the
|
||||
> `Size/*`-tiered publish rule (`rules/workflow-routing.md` §"Publish target
|
||||
> by Size/* tier — RETIRED") was retired with the legacy producer skills.
|
||||
> Artifacts publish where the live mode puts them: DAG task mode → node spec
|
||||
> in the frozen `{epic-slug}/dag` copy (see the DAG-mode input path above);
|
||||
> standalone bugfix → `{slug}/bugfix-report` + issue body per bugfix Phase 5.
|
||||
> Legacy tiered locations (`{slug}/02-03-req-design`, `{slug}/04-plan-*`, …)
|
||||
> stay readable for historical runs via `_shared/gitea-read-patterns.md`.
|
||||
@@ -0,0 +1,252 @@
|
||||
# Large Port Pipeline — Detailed Processes
|
||||
|
||||
> Extracted from `implement/SKILL.md` (Mode: port) Phase B1 through B4.
|
||||
> Read this file when executing the Pipeline Mode for large ports.
|
||||
|
||||
---
|
||||
|
||||
## Phase B1 — Source Analysis
|
||||
|
||||
Produce a comprehensive source analysis under
|
||||
wiki page namespace `port-{name}/source-analysis/`. This is the authoritative
|
||||
behavioral contract that every downstream stage references.
|
||||
|
||||
### Directory Structure
|
||||
|
||||
```
|
||||
port-{name}/source-analysis/
|
||||
├── index # Summary & reading guide (wiki page)
|
||||
├── 01-source-overview # Source project context, tech stack
|
||||
├── 02-public-api.md # Every public endpoint / method / interface
|
||||
├── 03-data-model.md # Entities, fields, relationships, schemas
|
||||
├── 04-business-logic.md # Validation, rules, edge cases, state machines
|
||||
├── 05-error-handling.md # Error types, codes, messages, recovery paths
|
||||
├── 06-dependencies.md # Libraries, infrastructure, external services
|
||||
├── 07-test-coverage.md # Test inventory: happy path, edge cases, errors
|
||||
├── 08-concept-mapping.md # Source → target concept mapping (Phase A2)
|
||||
├── 09-gap-analysis.md # Gaps, alternatives, decisions (Phase A3)
|
||||
├── 10-fidelity-baseline.md # Full behavioral inventory for end-to-end verify
|
||||
├── 11-target-surface.md # Target project receiving surface analysis (Phase B1.7)
|
||||
└── 12-capability-boundary.md # Complete artifact inventory per capability (Phase B1.8)
|
||||
```
|
||||
|
||||
### Document Templates
|
||||
|
||||
The format templates for documents 01–05 and 10 are in
|
||||
`reference/source-analysis-templates.md` (read it when producing these
|
||||
documents). Documents 06–09 follow the same table-based format as their
|
||||
Phase A1/A2/A3 counterparts in standalone mode. The `10-fidelity-baseline.md`
|
||||
document is the master inventory — every source behavior is listed as a
|
||||
checkable `FID-*` item with a `Chunk` column that drives DAG node decomposition.
|
||||
|
||||
---
|
||||
|
||||
## Phase B1.5 — Source Analysis Review (GATE)
|
||||
|
||||
Before proceeding to self-check, ALL source analysis documents MUST pass
|
||||
peer review. This is the single highest-leverage quality gate in the port
|
||||
pipeline — errors in source analysis propagate to every downstream stage.
|
||||
|
||||
### Review Process
|
||||
|
||||
1. **Spawn 10 parallel reviewer sub-agents** (Explorer), one per dimension.
|
||||
Each reviewer receives:
|
||||
- The relevant source analysis documents (as listed per dimension below).
|
||||
- The source project files (or accessible copies).
|
||||
- The target project files (for TGT-SURF and CAP-BOUND dimensions).
|
||||
- Review instructions for that dimension.
|
||||
|
||||
2. **Review dimensions**:
|
||||
|
||||
| Dimension | Documents Reviewed | Key Question |
|
||||
| --------- | ------------------ | ------------ |
|
||||
| SRC-CMP | 01-source-overview, 03-data-model, 04-business-logic, 10-fidelity-baseline | Is every source behavior/entity/rule captured? |
|
||||
| SRC-API | 02-public-api, 05-error-handling | Is every public endpoint/method/error documented accurately? |
|
||||
| SRC-DATA | 03-data-model | Are entities, fields, types, constraints, and relations correct? |
|
||||
| SRC-BIZ | 04-business-logic | Are every validation rule, edge case, and state transition documented? |
|
||||
| SRC-ERR | 05-error-handling | Are all error types, codes, messages, and recovery paths captured? |
|
||||
| SRC-DEP | 06-dependencies | Are all libraries, infrastructure, and external services listed? |
|
||||
| SRC-TST | 07-test-coverage, 10-fidelity-baseline | Does every source test case map to a FID item? Are file:line references correct? |
|
||||
| SRC-MAP | 08-concept-mapping | Is every source concept mapped to a target equivalent or [GAP]? Are mappings correct? |
|
||||
| TGT-SURF | 11-target-surface | Is the target project's receiving surface fully analyzed? Are all integration points, structural diffs, and readiness items captured? Does every source gap in the structural diff have a plan? |
|
||||
| CAP-BOUND | 12-capability-boundary | Are all 13 artifact dimensions filled in? Does every Source Function Inventory item appear in D1/D2? Does every structural diff gap have a boundary entry? Are all N/A dimensions justified? |
|
||||
|
||||
3. **Output**: Each reviewer writes a findings JSON conforming to
|
||||
`core/schemas/port-analysis.schema.json` as a
|
||||
wiki page `port-{name}/source-analysis/reviews/{dimension}`
|
||||
with format:
|
||||
```json
|
||||
{
|
||||
"dimension": "SRC-CMP",
|
||||
"findings": [
|
||||
{
|
||||
"id": "SRC-CMP-001",
|
||||
"severity": "BLOCKER|MAJOR|MINOR",
|
||||
"description": "...",
|
||||
"source_evidence": "file:line",
|
||||
"recommendation": "..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
4. **Synthesis**: An Orchestrator (Worker) collects all 10 dimension reports,
|
||||
deduplicates, and produces a synthesis:
|
||||
wiki page `port-{name}/source-analysis/reviews/synthesis`
|
||||
with summary counts per dimension and consolidated action items.
|
||||
|
||||
5. **Iterate until convergence**:
|
||||
- Developer addresses all BLOCKER and MAJOR findings.
|
||||
- Reviewer re-checks affected dimensions.
|
||||
- Repeat until all dimensions show 0 BLOCKER and 0 MAJOR.
|
||||
|
||||
6. **GATE**: Phase B2 cannot start until synthesis shows ALL dimensions
|
||||
converged (0 BLOCKER, 0 MAJOR). Developer prints:
|
||||
`SOURCE ANALYSIS REVIEW CONVERGED — 10/10 dimensions pass`
|
||||
|
||||
---
|
||||
|
||||
## Phase B1.7 + B1.8 — Target Surface & Capability Boundary (Pipeline)
|
||||
|
||||
In pipeline mode, Phase A1.7 (Target Surface Analysis) and Phase A1.8
|
||||
(Capability Boundary Definition) produce formal artifacts in the
|
||||
source-analysis directory for peer review:
|
||||
|
||||
- **`11-target-surface.md`** — same process and format as Phase A1.7, using
|
||||
`reference/target-surface-template.md`. Reviewed by the **TGT-SURF**
|
||||
dimension in Phase B1.5.
|
||||
- **`12-capability-boundary.md`** — same process and format as Phase A1.8,
|
||||
using `reference/capability-boundary-template.md`. Reviewed by the
|
||||
**CAP-BOUND** dimension in Phase B1.5.
|
||||
|
||||
### Roadmap decomposition link
|
||||
|
||||
The capability boundary directly feeds the DAG decomposition (Phase B3):
|
||||
- Chunks are defined by grouping capability boundary artifacts by source
|
||||
module / feature area.
|
||||
- Cross-chunk dependencies are derived from dimension 13 (shared package
|
||||
changes).
|
||||
- The fidelity baseline (`10-fidelity-baseline.md`) is partitioned across
|
||||
chunks based on which capability boundary artifacts implement each FID item.
|
||||
|
||||
**GATE**: Do NOT proceed to Phase B2 until the B1.5 review converges on ALL
|
||||
10 dimensions including TGT-SURF and CAP-BOUND.
|
||||
|
||||
---
|
||||
|
||||
## Phase B2 — Self-Check Source Analysis
|
||||
|
||||
Run the port checklist (`core/checklists/port.md`) against the source
|
||||
analysis:
|
||||
|
||||
- Section 0.5 (SRV — Source Analysis Review): 10-dimension review converged.
|
||||
- Section 0.7 (TGT — Target Surface Analysis): target receiving surface
|
||||
fully mapped, structural diff complete, integration points identified.
|
||||
- Section 0.8 (CAP — Capability Boundary): all 13 artifact dimensions filled,
|
||||
cross-checks passed.
|
||||
- Section 1 (SRC — Source Understanding): every source file and test read.
|
||||
- Section 2 (MAP — Concept Mapping): every source concept has a target
|
||||
equivalent or `[GAP]`.
|
||||
- Section 3 (GAP — Gap Analysis): all gaps have alternatives and decisions.
|
||||
- Sections 4–7 (ADAPT, FID, TST, BEH): deferred to downstream stages —
|
||||
marked as `[DEFERRED TO DESIGN]`, `[DEFERRED TO IMPLEMENT]`, etc.
|
||||
|
||||
---
|
||||
|
||||
## Phase B3 — Handoff to DAG Decomposition
|
||||
|
||||
> Legacy note ([org-internal #3072] phase 3, 2026-08-21): this handoff used to target the
|
||||
> archived `roadmap` skill; it now targets `analyze-dag`.
|
||||
|
||||
Present the source analysis and request task-DAG decomposition:
|
||||
|
||||
```
|
||||
Source analysis complete for port-{name}.
|
||||
- {N} source files ({L} LOC) across {M} modules
|
||||
- {K} public API endpoints / methods documented
|
||||
- {B} business rules captured
|
||||
- {F} fidelity baseline items (for end-to-end verify)
|
||||
- {G} gaps identified with alternatives
|
||||
|
||||
Artifacts: wiki pages under `port-{name}/source-analysis/`
|
||||
|
||||
→ Approve and begin DAG decomposition? (yes / no / revise)
|
||||
```
|
||||
|
||||
Upon approval, the Builder routes to
|
||||
`core/skills/analyze-dag/SKILL.md` with:
|
||||
- **Slug**: `port-{name}`
|
||||
- **Scope**: the source modules and fidelity baseline from the source analysis.
|
||||
|
||||
analyze-dag decomposes the port into a task DAG by source module / feature
|
||||
area. Each node is a self-contained porting unit (e.g. `N-auth`, `N-api`,
|
||||
`N-models`); node ACs derive from the source analysis (`FID-*` items become
|
||||
ACs tracing to source tests; concept-mapping and gap decisions become the
|
||||
node spec; porting order follows source-file dependency order as edge
|
||||
topology). After the `review-dag` single gate PASSes, each node ticket flows
|
||||
`dag.task_route`:
|
||||
|
||||
```
|
||||
core/skills/implement/SKILL.md
|
||||
→ core/skills/review-code/SKILL.md
|
||||
→ core/skills/verify/SKILL.md
|
||||
```
|
||||
|
||||
(The legacy per-chunk `requirements-elicitation` → `design` →
|
||||
`review-artifact(design-space)` → `plan-iterations` → `review-artifact(plan)`
|
||||
front-end was archived 2026-08-21, [org-internal #3072] phase 3 — `<instance-root>/archive/`.)
|
||||
|
||||
### implement
|
||||
|
||||
The Developer ports code following the rules from Phase A5 (target
|
||||
conventions, fidelity over aesthetics, no new dependencies, port comments).
|
||||
|
||||
Additionally:
|
||||
- Each implementation report references the corresponding `FID-*` items
|
||||
from the fidelity baseline.
|
||||
- Ported tests reference source test file and line number.
|
||||
|
||||
### review-code
|
||||
|
||||
Standard code review. Additionally:
|
||||
- Reviewer checks fidelity: does the ported code match the source behavior
|
||||
as documented in the chunk's requirements?
|
||||
- Reviewer checks convention compliance: does the new code look native to
|
||||
the target project?
|
||||
|
||||
### Target-Side Refactoring in Port Pipeline
|
||||
|
||||
Large ports often require restructuring the target codebase to accommodate
|
||||
ported code — extracting interfaces, renaming conflicting modules, removing
|
||||
dead code, or adapting existing abstractions. These refactoring needs MUST
|
||||
flow through the pipeline's quality gates, not as ad hoc changes.
|
||||
|
||||
**Identification**: The Architect identifies target-side refactoring needs
|
||||
during design and documents them as design decisions. Each refactoring
|
||||
decision references the gap that requires it (e.g. "Target's UserService must
|
||||
be extracted to an interface before porting source AuthService to avoid
|
||||
tight coupling").
|
||||
|
||||
**Planning**: The Planner creates refactoring work items alongside
|
||||
implementation work items. A refactoring work item's description starts with
|
||||
"Refactor" (or 重构) and its requirements coverage maps to a refactoring
|
||||
requirement derived from the design decision. Dependencies are enforced:
|
||||
- Refactoring work items that unblock port work items appear earlier in the
|
||||
iteration order.
|
||||
- No port work item depends on an incomplete refactoring.
|
||||
|
||||
**Execution**: The `implement` skill handles refactoring work items via its
|
||||
Refactoring Mode (see
|
||||
`core/skills/implement/SKILL.md` — Refactoring Mode). The refactoring
|
||||
produces a standard implementation report and proceeds to code review.
|
||||
|
||||
**Quality**: Refactoring work items go through the full review gate —
|
||||
code review is mandatory regardless of change size. The reviewer verifies:
|
||||
- Behavioral fidelity: did the refactoring preserve existing behavior?
|
||||
- Test baseline: did any existing tests break or change?
|
||||
- Design alignment: does the refactoring match the design decision?
|
||||
|
||||
```
|
||||
pipeline for refactoring work items (DAG node):
|
||||
implement (refactoring mode) → review-code → verify
|
||||
```
|
||||
@@ -0,0 +1,102 @@
|
||||
# Concept Mapping, Gap Analysis & Adaptation Design — Detailed Processes
|
||||
|
||||
> Extracted from `implement/SKILL.md` (Mode: port) Phase A2, A3, A4.
|
||||
> Read this file when executing the Mapping and Design phases in standalone mode.
|
||||
|
||||
---
|
||||
|
||||
## Phase A2 — Map Concepts
|
||||
|
||||
For every source concept, identify the target project's equivalent. This is a
|
||||
bidirectional mapping — every source entity, every source API call, every
|
||||
source pattern must have a corresponding target concept.
|
||||
|
||||
### Mapping Table
|
||||
|
||||
| Source Concept | Target Equivalent | Notes |
|
||||
| ------------------ | ------------------------------------------- | ---------------------------------------------------- |
|
||||
| Express middleware | Fastify hook | Different signature — adapt order and error handling |
|
||||
| Sequelize model | Drizzle schema | Different migration strategy — adapt CLI |
|
||||
| bcrypt hash | argon2 | Target project's existing auth module uses argon2 |
|
||||
| Redis cache | Memory cache (no Redis) | Compromise — simplify to in-memory with TTL |
|
||||
| Pino logger | Existing logger module in `src/util/log.ts` | Reuse target's logger |
|
||||
|
||||
### Rules
|
||||
|
||||
- If a source concept has no clear target equivalent, pause and log `[GAP]`.
|
||||
- If the target has a different pattern for the same concept (e.g. callbacks
|
||||
vs. async/await), prefer the TARGET pattern, not the source's.
|
||||
- If the source uses a library that exists in the target's ecosystem, use the
|
||||
version already in the target's `package.json` — do not introduce a different
|
||||
version.
|
||||
|
||||
---
|
||||
|
||||
## Phase A3 — Gap Analysis
|
||||
|
||||
For every `[GAP]` from Phase A2, analyze the impact:
|
||||
|
||||
### Gap Analysis Table
|
||||
|
||||
| Gap | Impact | Alternatives | Decision | Deferred To (slug) | Reactivation Trigger |
|
||||
| ------------------ | ------------------------------------ | ------------------------------------------------------------------------------------------- | ---------- | ------------------ | -------------------- |
|
||||
| No Redis in target | Source uses Redis for session store | 1. Add Redis to target, 2. Use DB for sessions, 3. Use in-memory (not for production) | {decision} | | |
|
||||
| No message queue | Source uses RabbitMQ for async tasks | 1. Add queue to target, 2. Make synchronous, 3. Use a simpler queue (e.g. database polling) | {decision} | | |
|
||||
|
||||
### Rules
|
||||
|
||||
- Do NOT add infrastructure to the target unless absolutely necessary — prefer
|
||||
alternatives that use existing target infrastructure.
|
||||
- If a gap forces a behavior change, mark it as `[FIDELITY DEVIATION]` — the
|
||||
port will not behave identically. This must be explicitly approved.
|
||||
|
||||
### Deferral Rules
|
||||
|
||||
- A gap marked `Deferred` MUST populate both "Deferred To" and "Reactivation Trigger" columns.
|
||||
- "Deferred To" must reference a concrete artifact slug (e.g. `port-{name}/chunk-http`).
|
||||
- "Reactivation Trigger" must specify a condition (e.g. "After chunk-auth verification passes").
|
||||
- Gaps without a reactivation path are treated as `[PORT GAP]` — a blocker for the current port.
|
||||
|
||||
---
|
||||
|
||||
## Phase A4 — Adaptation Design
|
||||
|
||||
Design how the source feature will fit into the target project:
|
||||
|
||||
1. **File structure** — where in the target project will the ported code live?
|
||||
2. **Interface adaptations** — source API signatures must adapt to target
|
||||
conventions (e.g. source uses `snake_case`, target uses `camelCase`).
|
||||
3. **Dependency replacements** — for each source dependency, use the target
|
||||
equivalent or the Gap decision from Phase A3.
|
||||
4. **Test adaptation** — source test framework → target test framework mapping
|
||||
(e.g. `describe`/`it` → `describe`/`it` if both use the same pattern, or
|
||||
map to target's test DSL).
|
||||
|
||||
### Adaptation Design Template
|
||||
|
||||
````markdown
|
||||
## Adaptation Design
|
||||
|
||||
### File Structure
|
||||
```
|
||||
src/
|
||||
{module}/
|
||||
{ported_file}.ts — (from source/src/{module}/{file}.js)
|
||||
...
|
||||
test/
|
||||
{module}/
|
||||
{ported_test}.test.ts — (from source/test/{module}/{file}.test.js)
|
||||
```
|
||||
|
||||
### Interface Adaptations
|
||||
| Source | Target | Reason |
|
||||
|--------|--------|--------|
|
||||
| `req.body.created_at` | `req.body.createdAt` | Target convention: camelCase |
|
||||
| `throw new AppError(400, '...')` | `yield* new BadRequest('...')` | Target uses Effect errors |
|
||||
|
||||
### Fidelity Deviations
|
||||
| What Changes | Why | Impact |
|
||||
|-------------|-----|--------|
|
||||
| Session store: Redis → DB | Target has no Redis | Slightly higher latency (~5ms), CAP consistency trade |
|
||||
| Async queue: RabbitMQ → DB polling | Target has no queue broker | Higher latency, lower throughput — acceptable for < 100 ops/min |
|
||||
````
|
||||
@@ -0,0 +1,389 @@
|
||||
> Extracted from implement/SKILL.md (Mode: port) — moved verbatim 2026-08-25, ticket [org-internal #3381].
|
||||
|
||||
### Mode: port
|
||||
|
||||
Port a feature from a source project into the current (target) project. Unlike
|
||||
greenfield (no existing code), brownfield (new feature in same project), or
|
||||
bugfix (restore intended behavior), porting requires preserving the source's
|
||||
behavioral contract while adapting every implementation detail to the target's
|
||||
tech stack and conventions.
|
||||
|
||||
#### Role & Responsibilities
|
||||
|
||||
The port is owned by the **Developer** (Worker).
|
||||
|
||||
The Developer is responsible for:
|
||||
|
||||
- Reading and understanding the source feature end-to-end.
|
||||
- Mapping source concepts to target equivalents.
|
||||
- Identifying gaps (missing infrastructure, incompatible libraries).
|
||||
- Designing adaptations that preserve behavior.
|
||||
- For small ports: implementing in the target project following target
|
||||
conventions end-to-end.
|
||||
- For large ports: producing a source analysis report, then handing off to
|
||||
the DAG pipeline (analyze-dag → review-dag → per-node implement →
|
||||
review-code → verify) with the single-gate review.
|
||||
- Porting source tests to the target test framework.
|
||||
- Verifying behavioral fidelity (same inputs → same outputs).
|
||||
|
||||
The Builder's role is to validate the port output and route to the next stage
|
||||
(analyze-dag for large ports, code review for non-trivial small ports).
|
||||
|
||||
#### Tester focus for port
|
||||
|
||||
The Tester role in port writes **behavioral-fidelity tests** — verifying
|
||||
that ported code behaves identically to the source, not just that it passes
|
||||
its own assertions:
|
||||
|
||||
- **Fidelity-anchored tests** — every `FID-*` item in
|
||||
`port-{name}/source-analysis/10-fidelity-baseline` (or `port-{name}/source-analysis/fid-raw` in standalone mode) MUST map
|
||||
to at least one ported test. The Tester reads the source test for each
|
||||
FID and rewrites it in the target test framework's assertion style,
|
||||
preserving the behavioral contract (same inputs → same outputs).
|
||||
- **Source-test porting** — port edge cases, error paths, and boundary
|
||||
tests from the source, not just the happy path. Missing FID coverage is
|
||||
the #1 cause of incomplete porting.
|
||||
- **Fidelity-deviation tests** — for every `[FIDELITY DEVIATION]` in the
|
||||
adaptation design (Phase A4), write a test that documents and locks the
|
||||
divergent behavior so the deviation is intentional, not accidental drift.
|
||||
|
||||
The port orchestration follows the standard role-split flow (implement
|
||||
before test): the Developer ports code in Phase A5 and runs test:changed
|
||||
to green; the Tester then ports source tests and verifies fidelity in
|
||||
Phase A6. In pipeline mode (Part 2), the role split applies within each
|
||||
chunk's implement stage.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 0 — Scope Assessment
|
||||
|
||||
Before starting, measure the source scope across **four dimensions** — file
|
||||
count and LOC alone are insufficient because a 3-file port that spans 3
|
||||
packages with implicit dependencies (schema, config, routes) is far more
|
||||
complex than a 10-file port within a single self-contained module.
|
||||
|
||||
1. Count source files in the feature scope.
|
||||
2. Count total source lines of code (exclude tests).
|
||||
3. Count source modules / feature areas (distinct functional areas).
|
||||
4. **Count source packages touched** — how many monorepo packages does the
|
||||
feature span? (e.g. `packages/app`, `packages/sdk`, `packages/ui`,
|
||||
`packages/core`).
|
||||
5. **Count implicit dependency artifacts** — schema/migration files, config
|
||||
entries, env vars, CLI flags, theme/style files, route definitions,
|
||||
Provider/context hierarchy changes, build config changes. These are the
|
||||
artifacts that are NOT source code files but are required for the feature
|
||||
to function. See Phase A1.8 for the full artifact dimension list.
|
||||
|
||||
Determine the port path:
|
||||
|
||||
| Scope | Mode | Pipeline |
|
||||
| ---------------------------------- | ------------- | ------------------------------------------------------ |
|
||||
| ≤ 5 files, ≤ 200 LOC, 1 package, 0 implicit deps | **Standalone**| Standalone Developer flow (Phases A1–A8, mandatory code review) |
|
||||
| > 5 files or > 200 LOC | **Pipeline** | Full pipeline: source analysis → analyze-dag → per-node |
|
||||
| Spans ≥ 3 source modules | **Pipeline** | Full pipeline (regardless of file count / LOC) |
|
||||
| Spans ≥ 2 packages | **Pipeline** | Full pipeline (cross-package ports have hidden coupling) |
|
||||
| ≥ 3 implicit dependency artifacts | **Pipeline** | Full pipeline (implicit deps require capability boundary analysis) |
|
||||
|
||||
Report the assessment:
|
||||
|
||||
```
|
||||
Port scope assessment:
|
||||
- Source files: {N}
|
||||
- Source LOC: {L}
|
||||
- Source modules: {M}
|
||||
- Source packages touched: {P}
|
||||
- Implicit dependency artifacts: {I}
|
||||
- Path: A (standalone) / B (full pipeline)
|
||||
|
||||
→ Proceed? (yes / no / revise)
|
||||
```
|
||||
|
||||
Do NOT proceed without explicit approval.
|
||||
|
||||
---
|
||||
|
||||
#### Preconditions
|
||||
|
||||
- [ ] Source project path or reference is specified.
|
||||
- [ ] Source feature scope is specified (which files, module, or feature).
|
||||
- [ ] Target project is the current working directory and has a clean
|
||||
workspace.
|
||||
- [ ] Target project has an existing test framework.
|
||||
- [ ] **Target project structure is accessible** — the Developer can read all
|
||||
target project packages, config files, and build configs. If the target
|
||||
is a monorepo, all packages must be accessible.
|
||||
- [ ] **Target project's existing capabilities are documented or discoverable**
|
||||
— the Developer must be able to identify what the target already has
|
||||
(existing modules, routes, providers, schemas) to avoid redundant porting
|
||||
and to identify integration points. If not documented, the Developer
|
||||
must run a structural survey (Phase A1.7) before proceeding.
|
||||
- [ ] `core/checklists/port.md` is accessible.
|
||||
|
||||
If the target project has no test framework, warn:
|
||||
|
||||
```
|
||||
Target project has no test framework. Porting without tests cannot verify
|
||||
behavioral fidelity. Options:
|
||||
1. Add a test framework to the target project first.
|
||||
2. Proceed without tests — behavioral fidelity cannot be guaranteed.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Part 1 — Standalone Mode (Small Port)
|
||||
|
||||
For small ports (≤ 5 files, ≤ 200 LOC), the Developer executes the full
|
||||
port lifecycle directly. No DAG decomposition, no per-stage review gates.
|
||||
Code review is mandatory (regardless of size, per Phase A8).
|
||||
|
||||
| Phase | Summary | Detail |
|
||||
|-------|---------|--------|
|
||||
| A1 | Deeply analyze source: public API, data model, dependencies, function inventory, test-to-FID extraction | `reference/source-analysis.md` |
|
||||
| A1.5 | Source Analysis Review GATE — 4-dimensional peer review of A1 deliverables | `reference/source-analysis.md` |
|
||||
| A1.7 | Target Surface Analysis — map target receiving surface, structural diffs, integration points | `reference/source-analysis.md` |
|
||||
| A1.8 | Capability Boundary Definition GATE — 13-dimension artifact inventory | `reference/source-analysis.md` |
|
||||
| A2 | Map every source concept to a target equivalent or mark as `[GAP]` | `reference/mapping-and-design.md` |
|
||||
| A3 | Gap Analysis — alternatives, decisions, deferral paths for every `[GAP]` | `reference/mapping-and-design.md` |
|
||||
| A4 | Adaptation Design — file structure, interface adaptations, fidelity deviations | `reference/mapping-and-design.md` |
|
||||
| A5 | **Implement** — port code following target conventions | *(inline below)* |
|
||||
| A5.5 | **Self-Check Gate** — complete port checklist | *(inline below)* |
|
||||
| A6 | Port Tests & Verify Fidelity — port every test, fidelity checklist | `reference/fidelity-verification.md` |
|
||||
| A7 | **Report** — produce port report | `reference/port-report-template.md` |
|
||||
| A8 | **Approval** — present report, route to code review | *(inline below)* |
|
||||
|
||||
##### Phase A1 — Understand Source
|
||||
|
||||
Deeply analyze the source feature: public API, data model, dependencies, and
|
||||
function inventory. Extract every test case as a `FID-*` entry in `port-{name}/source-analysis/fid-raw` (wiki page).
|
||||
See `reference/source-analysis.md` for the full process, templates, and
|
||||
Function Inventory format.
|
||||
|
||||
##### Phase A1.5 — Source Analysis Review (GATE)
|
||||
|
||||
A reviewer (Explorer sub-agent) cross-checks all A1 deliverables against source
|
||||
files across 4 dimensions (SRC-CMP, SRC-API, SRC-TST, SRC-DEP). All BLOCKER
|
||||
findings must be resolved before Phase A2. See `reference/source-analysis.md`.
|
||||
|
||||
##### Phase A1.7 — Target Surface Analysis
|
||||
|
||||
Analyze the target project's receiving surface: directory tree, existing
|
||||
capabilities, automated structural diffs, integration points, and readiness.
|
||||
See `reference/source-analysis.md` for the full process.
|
||||
Output follows `reference/target-surface-template.md`.
|
||||
|
||||
##### Phase A1.8 — Capability Boundary Definition (GATE)
|
||||
|
||||
Define the complete artifact boundary across all 13 dimensions (code, types,
|
||||
schema, config, env, CLI, theme, routes, providers, build, deps, tests, shared
|
||||
packages). Cross-reference with A1 inventory and A1.7 diffs.
|
||||
See `reference/source-analysis.md` for the full process.
|
||||
Output follows `reference/capability-boundary-template.md`.
|
||||
|
||||
##### Phase A2 — Map Concepts
|
||||
|
||||
Map every source concept to a target equivalent — bidirectional, complete.
|
||||
Mark missing equivalents as `[GAP]`. See `reference/mapping-and-design.md`.
|
||||
|
||||
##### Phase A3 — Gap Analysis
|
||||
|
||||
Analyze every `[GAP]`: impact, alternatives, decision, deferral path with
|
||||
reactivation trigger. See `reference/mapping-and-design.md`.
|
||||
|
||||
##### Phase A4 — Adaptation Design
|
||||
|
||||
Design file structure, interface adaptations, dependency replacements, and
|
||||
document `[FIDELITY DEVIATION]` items. See `reference/mapping-and-design.md`.
|
||||
|
||||
##### Phase A5 — Implement
|
||||
|
||||
Port the code file by file, following these rules:
|
||||
|
||||
1. **One file at a time** — port completely, verify, then move to the next.
|
||||
2. **Target conventions are law** — the ported code must follow target
|
||||
conventions exactly. Use target's naming, patterns, and idioms.
|
||||
3. **Fidelity over aesthetics** — do not "improve" the source logic. If the
|
||||
source validates email with `/^.+@.+$/`, port that exact validation (then
|
||||
log a `[NOTE: weak validation in source]`). If you want stricter validation,
|
||||
that's a separate feature item, not part of the port.
|
||||
4. **Port comments from source** (translated to target language) — they capture
|
||||
the original author's intent.
|
||||
5. **After each file** — run `bun typecheck` to catch type errors early.
|
||||
6. **Do not introduce new dependencies** — if the source uses a library not in
|
||||
the target's lockfile, use the alternative from the adaptation design or
|
||||
reimplement the needed subset inline.
|
||||
|
||||
##### Phase A5.5 — Self-Check Gate (MANDATORY)
|
||||
|
||||
Before proceeding to test porting, the Developer MUST complete the port
|
||||
checklist (`core/checklists/port.md`) in full:
|
||||
|
||||
1. **Run every checklist section** — all 12 sections, all items.
|
||||
2. **Mark every item** — ☑ (pass) or ☐ (fail) with written justification.
|
||||
3. **For any ☐ item** — document a fix plan inline in the remarks column.
|
||||
If the item is a known deferral (e.g. infrastructure gap), the deferral
|
||||
must include a reactivation path (chunk + trigger).
|
||||
4. **Publish the completed checklist** as a Gitea wiki page:
|
||||
`wiki 读写 API(见 TERMINOLOGY)(owner="Octopus", repo="octopus", title="port-{name}/self-check", content="{checklist body}", message="Publish port self-check for {name}")`.
|
||||
5. **GATE** — do NOT proceed to Phase A6 until:
|
||||
- All items are ☑, OR
|
||||
- All ☐ items have documented fix plans with reactivation paths,
|
||||
AND the Developer has printed: `SELF-CHECK COMPLETE — {X} items passing, {Y} items deferred with plan`
|
||||
|
||||
##### Phase A6 — Port Tests & Verify Fidelity
|
||||
|
||||
Port every source test to the target test framework. Run all tests (ported +
|
||||
existing), verify behavioral fidelity, and produce a fidelity verification
|
||||
table. See `reference/fidelity-verification.md` for the full process and
|
||||
fidelity checklist template.
|
||||
|
||||
##### Phase A7 — Report
|
||||
|
||||
Produce a port report following the template in
|
||||
`reference/port-report-template.md`. The report covers: fidelity assessment
|
||||
(fully ported / adapted / deferred / N/A), portfolio map (source → target file
|
||||
mapping), gaps & deferred items, and verification results.
|
||||
|
||||
##### Phase A8 — Approval
|
||||
|
||||
Present the report:
|
||||
|
||||
```
|
||||
Port complete: {one-line summary}
|
||||
- {N} files ported ({L} lines)
|
||||
- {M} tests ported, all pass
|
||||
- Fidelity: {X}% fully ported, {Y}% adapted, {Z}% deferred
|
||||
|
||||
→ Run code review? (mandatory)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Part 2 — Pipeline Mode (Large Port)
|
||||
|
||||
For large ports, the Developer produces a source analysis report as the
|
||||
foundation artifact, then the port flows through the full pipeline with
|
||||
peer-review gates at every stage:
|
||||
|
||||
```
|
||||
port (source analysis)
|
||||
→ analyze-dag (decompose by source module into DAG nodes)
|
||||
→ review-dag single gate (replaces the legacy design-space + plan reviews)
|
||||
→ per node (dag.task_route):
|
||||
implement (port code, target conventions) — see ### Mode: implement (default)
|
||||
→ review-code
|
||||
→ verify (integration + fidelity)
|
||||
```
|
||||
|
||||
> Legacy diagram (archived [org-internal #3072] phase 3): the former per-chunk pipeline ran
|
||||
> requirements-elicitation → design → review (target: design-space) →
|
||||
> plan-iterations → review (target: plan) → implement → review-code. Those
|
||||
> skills/targets are archived (`<instance-root>/archive/`); the live path is the
|
||||
> analyze-dag → review-dag route above (see Phase B3/B4 below).
|
||||
|
||||
| Phase | Summary | Detail |
|
||||
|-------|---------|--------|
|
||||
| B1 | Produce comprehensive 12-document source analysis | `reference/large-port-pipeline.md` |
|
||||
| B1.5 | **GATE** — 10-dimensional peer review of source analysis | `reference/large-port-pipeline.md` |
|
||||
| B1.7+B1.8 | Target surface & capability boundary as formal artifacts for peer review | `reference/large-port-pipeline.md` |
|
||||
| B2 | Self-check source analysis against port checklist | `reference/large-port-pipeline.md` |
|
||||
| B3 | Handoff to analyze-dag — decompose by source module into DAG nodes (legacy: roadmap skill, archived [org-internal #3072] phase 3) | `reference/large-port-pipeline.md` |
|
||||
| B4 | Per-node DAG task route: implement → review-code (the review-dag single gate replaces the legacy design-space + plan reviews at the Epic level) | `reference/large-port-pipeline.md` |
|
||||
| B5 | Verify — integration + fidelity, every `FID-*` traced to a passing test | `reference/fidelity-verification.md` |
|
||||
| B6 | **Final Approval** — consolidated port report | *(inline below)* |
|
||||
|
||||
##### Phase B1 — Source Analysis
|
||||
|
||||
Produce a comprehensive 12-document source analysis as Gitea wiki pages under
|
||||
`port-{name}/source-analysis/`. Each document is a separate wiki page.
|
||||
See `reference/large-port-pipeline.md` for the full document list, templates (in
|
||||
`reference/source-analysis-templates.md`), and production process.
|
||||
|
||||
##### Phase B1.5 — Source Analysis Review (GATE)
|
||||
|
||||
Spawn 10 parallel Explorer reviewers across all dimensions (SRC-CMP, SRC-API,
|
||||
SRC-DATA, SRC-BIZ, SRC-ERR, SRC-DEP, SRC-TST, SRC-MAP, TGT-SURF, CAP-BOUND),
|
||||
synthesize findings, and iterate until convergence (0 BLOCKER, 0 MAJOR).
|
||||
See `reference/large-port-pipeline.md` for the full process.
|
||||
|
||||
##### Phase B1.7 + B1.8 — Target Surface & Capability Boundary
|
||||
|
||||
Same processes as A1.7/A1.8, producing formal artifacts (`11-target-surface.md`,
|
||||
`port-{name}/source-analysis/12-capability-boundary`) for peer review. The capability boundary feeds
|
||||
directly into DAG node decomposition.
|
||||
See `reference/large-port-pipeline.md`.
|
||||
|
||||
##### Phase B2 — Self-Check Source Analysis
|
||||
|
||||
Run the port checklist against the source analysis (sections 0.5, 0.7, 0.8, 1,
|
||||
2, 3; sections 4–7 deferred to downstream stages).
|
||||
See `reference/large-port-pipeline.md`.
|
||||
|
||||
##### Phase B3 — Handoff to DAG Decomposition
|
||||
|
||||
Present source analysis summary and request task-DAG decomposition. The
|
||||
Builder routes to `core/skills/analyze-dag/SKILL.md` with slug
|
||||
`port-{name}` (nodes per source module; the source analysis supplies node
|
||||
ACs and edge contracts). (Legacy: this handed off to the archived `roadmap`
|
||||
skill — `<instance-root>/archive/skills/roadmap/`, [org-internal #3072] phase 3.) See
|
||||
`reference/large-port-pipeline.md` for the handoff format.
|
||||
|
||||
##### Phase B4 — Per-Node Pipeline
|
||||
|
||||
Each node ticket flows `dag.task_route`: `implement` → `review-code`
|
||||
(the `review-dag` single gate replaces the legacy design-space + plan
|
||||
reviews at the Epic level). Includes rules for target-side refactoring work
|
||||
items routed through the refactor workflow (see Mode: refactor above).
|
||||
See `reference/large-port-pipeline.md` for full per-stage details.
|
||||
|
||||
##### Phase B5 — Verify (Integration + Fidelity)
|
||||
|
||||
Run `core/skills/verify/SKILL.md` with the fidelity baseline as acceptance
|
||||
criteria. Every `FID-*` must trace to a passing test. Produce a consolidated
|
||||
Port Fidelity Report. See `reference/fidelity-verification.md`.
|
||||
|
||||
##### Phase B6 — Final Approval
|
||||
|
||||
Before declaring the port complete, verify:
|
||||
|
||||
1. **CI is configured** — Check `.gitea/workflows/` (this repo's CI location),
|
||||
`.github/workflows/ci.yml`, or equivalent.
|
||||
If absent, warn: `[GAP: no CI — no automated gate before merge]`.
|
||||
The PR may be merged, but flag the gap in the port report.
|
||||
2. **All review gates passed** — The `review-dag` single gate and every
|
||||
node's code review have `converged: true`.
|
||||
3. **Final typecheck + lint + tests pass** — Run all three commands fresh.
|
||||
|
||||
Present the consolidated port report:
|
||||
|
||||
```
|
||||
Port complete: port-{name}
|
||||
- {N} chunks implemented
|
||||
- {F} files ported ({L} lines)
|
||||
- {T} tests ported, all pass
|
||||
- Fidelity: {X}/{Y} behaviors verified, {Z} deferred
|
||||
- All peer-review gates passed
|
||||
- CI: {configured / absent — manual gate required}
|
||||
|
||||
Artifacts: wiki pages under `port-{name}/`
|
||||
|
||||
→ Approve port? (yes / no)
|
||||
```
|
||||
|
||||
##### Phase B7 — Post-Merge Cleanup
|
||||
|
||||
After the PR is merged and the port branch is no longer needed:
|
||||
|
||||
1. **Delete the remote branch**:
|
||||
```
|
||||
git push origin --delete workflow/port/{name}
|
||||
```
|
||||
2. **Delete the local branch**:
|
||||
```
|
||||
git branch -d workflow/port/{name}
|
||||
```
|
||||
3. **Remove associated worktrees**:
|
||||
```
|
||||
git worktree list | grep "workflow/port/{name}" | awk '{print $1}' | xargs git worktree remove
|
||||
```
|
||||
4. **Verify cleanup**: `git branch -a | grep workflow/port/{name}` should
|
||||
return empty.
|
||||
@@ -0,0 +1,43 @@
|
||||
# Port Report Template
|
||||
|
||||
```markdown
|
||||
# Port Report
|
||||
|
||||
**Source**: {project name} — {feature name}
|
||||
**Target**: {current project}
|
||||
**Files ported**: {N}
|
||||
**Tests ported**: {M}
|
||||
|
||||
## Fidelity Assessment
|
||||
|
||||
| Category | Status | Count |
|
||||
| ------------------------------------- | ------ | ------------- |
|
||||
| Fully ported | ✅ | {N} behaviors |
|
||||
| Adapted (minor change) | ⚠️ | {N} behaviors |
|
||||
| Deferred (not ported) | ❌ | {N} behaviors |
|
||||
| Not applicable (different tech stack) | N/A | {N} behaviors |
|
||||
|
||||
## Portfolio Map
|
||||
|
||||
| Source File | Target File | Lines | Status |
|
||||
| -------------------------------- | -------------------------------- | --------- | ------ |
|
||||
| `source/src/auth/login.js` | `target/src/auth/login.ts` | 45 → 52 | ✅ |
|
||||
| `source/test/auth/login.test.js` | `target/test/auth/login.test.ts` | 120 → 118 | ✅ |
|
||||
|
||||
## Gaps & Deferred
|
||||
|
||||
| Item | Reason | Deferred to |
|
||||
| ------------- | -------------------------- | ------------------------------------- |
|
||||
| Rate limiting | Target has no rate limiter | Separate feature: "Add rate limiting" |
|
||||
|
||||
## Verification
|
||||
|
||||
- Ported tests: {M} passed, 0 failed
|
||||
- Existing tests: {K} passed, 0 failed
|
||||
- Typecheck: ✅
|
||||
- Lint: ✅
|
||||
|
||||
---
|
||||
|
||||
**Handoff**: → Run `core/skills/review-code/SKILL.md` (mandatory for all ports, regardless of size)
|
||||
```
|
||||
@@ -0,0 +1,258 @@
|
||||
> Extracted from implement/SKILL.md (Mode: refactor) — moved verbatim 2026-08-25, ticket [org-internal #3381].
|
||||
|
||||
### Mode: refactor
|
||||
|
||||
Restructure existing code to improve maintainability, readability, or
|
||||
performance without changing observable behavior. The existing test suite
|
||||
is the safety net — every refactoring step MUST be verified before proceeding.
|
||||
|
||||
#### Execution Modes
|
||||
|
||||
| Mode | Entry Point | Scope Source | Review Gate |
|
||||
| ---------- | -------------------------------------------- | --------------------------- | ---------------------- |
|
||||
| Standalone | User says "refactor {X}" | User specifies scope | Optional (>50 lines or ≥5 files) |
|
||||
| Pipeline | `implement` skill dispatches refactoring WI | Node spec (DAG) or request | Mandatory |
|
||||
|
||||
In pipeline mode, the scope and target pattern come from the design document
|
||||
and iteration plan, not from user input. The Developer must read the design
|
||||
sections referenced by the work item before starting Phase 1. After completing
|
||||
Phases 1–6, the Developer produces the standard implementation report (see
|
||||
Mode: implement (default), Phase 5) and hands off to code review.
|
||||
|
||||
#### Role & Responsibilities
|
||||
|
||||
The refactoring is owned and executed by the **Developer** (Worker). The
|
||||
Developer owns both implementation and refactoring — same role, same skill set.
|
||||
|
||||
The Developer is responsible for:
|
||||
|
||||
- Establishing a passing test baseline before any code change.
|
||||
- Decomposing the refactoring into small, reversible, verifiable steps.
|
||||
- Running the full test suite after every step — never skip a verification.
|
||||
- Reverting immediately if any step causes a test failure.
|
||||
- Comparing before/after coverage and complexity metrics.
|
||||
|
||||
The Builder's role is to present the refactor report and route to code review
|
||||
if the change is non-trivial (> 50 lines or touches ≥ 5 files).
|
||||
|
||||
---
|
||||
|
||||
#### Preconditions
|
||||
|
||||
Before starting the refactoring, confirm:
|
||||
|
||||
- [ ] Scope is specified (which file, module, or pattern to refactor).
|
||||
- [ ] An existing test suite covers the scope. If test coverage is unknown,
|
||||
run the test suite with coverage first.
|
||||
- [ ] No uncommitted changes in the working tree (`git status` is clean).
|
||||
- [ ] `core/checklists/refactoring.md` is accessible.
|
||||
|
||||
##### No Test Coverage? Stop.
|
||||
|
||||
If the scope has **no existing tests**:
|
||||
|
||||
```
|
||||
Cannot safely refactor {scope} — no existing test coverage.
|
||||
|
||||
Refactoring without tests is not restructuring, it's rewriting with unknown
|
||||
side effects. Options:
|
||||
1. Write characterization tests first (tests that capture current behavior),
|
||||
then refactor.
|
||||
2. Skip this module — refactor only modules with test coverage.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 1 — Scope & Baseline
|
||||
|
||||
1. **Identify scope** — confirm the exact files, classes, or modules to
|
||||
refactor. Use `glob` and `grep` to map all files and their dependents.
|
||||
|
||||
2. **Establish baseline**:
|
||||
- Run `bun run test:parallel` (or project-equivalent) — all tests must pass.
|
||||
- If any test fails before you start, stop and report: "Cannot begin
|
||||
refactoring with failing tests. Fix them first."
|
||||
- Capture test count as baseline: `tests: {N} total, {N} passed`.
|
||||
- Capture coverage if available: run the package's coverage script from
|
||||
the package dir (e.g. `cd <harness-package> && bun run test:coverage`) —
|
||||
`bun run test:parallel` does not emit coverage (its runner script drops
|
||||
positional args, so `test:parallel --coverage` silently ignores the
|
||||
flag); use the package's `test:coverage` script instead.
|
||||
|
||||
3. **Capture complexity metrics** (optional but recommended):
|
||||
- Lines of code in scope.
|
||||
- Cyclomatic complexity or equivalent (if tooling exists).
|
||||
- Dependencies (fan-in / fan-out).
|
||||
|
||||
```markdown
|
||||
## Baseline
|
||||
|
||||
**Scope**: {list of files}
|
||||
**Tests**: {N} passed, 0 failed
|
||||
**Coverage**: {X}% lines, {Y}% branches
|
||||
**LOC**: {N}
|
||||
**Complexity**: {measured or "no metrics tool available"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 2 — Define Target Pattern
|
||||
|
||||
Define what "done" looks like. A refactoring without a target pattern is
|
||||
code churn, not improvement.
|
||||
|
||||
The target pattern must be one of:
|
||||
|
||||
| Category | Examples |
|
||||
| ------------ | -------------------------------------------------------------------------------------------------------------- |
|
||||
| **Extract** | Extract class, extract function, extract module |
|
||||
| **Inline** | Inline function, inline variable, inline class |
|
||||
| **Rename** | Rename function, variable, class, file, module |
|
||||
| **Move** | Move function/class to a more appropriate module |
|
||||
| **Replace** | Replace callback with Promise/await, replace loop with functional style, replace conditional with polymorphism |
|
||||
| **Simplify** | Remove dead code, collapse redundant logic, flatten nested conditionals |
|
||||
| **Upgrade** | Migrate to new API, adopt new library version patterns |
|
||||
|
||||
```markdown
|
||||
## Target Pattern
|
||||
|
||||
**Category**: {Extract | Inline | Rename | Move | Replace | Simplify | Upgrade}
|
||||
**Goal**: {one sentence — e.g. "Extract UserRepository from UserController to
|
||||
separate persistence logic from HTTP handling"}
|
||||
**Success criteria**:
|
||||
|
||||
1. All existing tests pass unchanged.
|
||||
2. {specific structural goal — e.g. "UserController no longer imports Database"}.
|
||||
3. Coverage does not decrease.
|
||||
4. {additional criteria if applicable}.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 3 — Decompose into Steps
|
||||
|
||||
Break the refactoring into the smallest individually-verifiable steps.
|
||||
Each step must:
|
||||
|
||||
- Be reversible (if tests break, revert and reassess).
|
||||
- Pass the full test suite independently.
|
||||
- Be one conceptual transformation (not "rename + extract + inline" in one step).
|
||||
- Take ≤ 5 minutes to write.
|
||||
|
||||
```markdown
|
||||
## Refactoring Steps
|
||||
|
||||
| Step | Action | Files Affected | Expected Outcome |
|
||||
| ---- | ----------------------------------------------- | ------------------------------------------------ | ---------------------------------- |
|
||||
| 1 | Extract `findById` method from controller | `user.controller.ts`, `user.repository.ts` (new) | Controller delegates to repository |
|
||||
| 2 | Extract `create` method | `user.controller.ts`, `user.repository.ts` | Same pattern as step 1 |
|
||||
| 3 | Inline `formatUser` helper (used once) | `user.controller.ts` | Remove one-line helper |
|
||||
| 4 | Rename `user.controller.ts` → `user.handler.ts` | `user.controller.ts`, 3 imports | Naming consistency |
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
|
||||
- Present the step plan to the user before executing.
|
||||
- If > 10 steps, the scope is too large — split into multiple refactoring
|
||||
sessions.
|
||||
- The user may approve, reorder, or reject individual steps.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 4 — Incremental Execution
|
||||
|
||||
For each step, in order:
|
||||
|
||||
1. **Transform**: apply the single conceptual change.
|
||||
2. **Verify**: run `bun run test:changed` — ALL affected tests must pass.
|
||||
3. **If PASS**: commit the step with a message describing the transformation:
|
||||
```
|
||||
refactor: extract {what} from {where}
|
||||
```
|
||||
4. **If FAIL**: revert the change. Do NOT fix the test or the code. Assess
|
||||
whether the step decomposition is wrong or the test was already flaky.
|
||||
- If the test was flaky (fails non-deterministically), fix the test first
|
||||
as a prerequisite step, then retry.
|
||||
- If the step decomposition is wrong, re-decompose from Phase 3.
|
||||
|
||||
**Revert policy**: revert immediately on failure. Do not attempt to fix
|
||||
within the same step — a failing test during refactoring means the step is
|
||||
not behavior-preserving, and you must find a smaller decomposition.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 5 — Final Validation
|
||||
|
||||
After all steps are complete:
|
||||
|
||||
1. **Full test suite**: `bun run test:parallel` — all tests must pass.
|
||||
2. **Typecheck**: `bun typecheck` — zero errors.
|
||||
3. **Lint**: `bun oxlint --deny-warnings` — zero errors.
|
||||
4. **Coverage comparison**: compare post-refactor coverage to baseline.
|
||||
Coverage MUST NOT decrease (within ±1% for measurement noise).
|
||||
5. **Complexity comparison** (optional): confirm the refactoring improved
|
||||
the target metric (e.g. lower cyclomatic complexity).
|
||||
|
||||
---
|
||||
|
||||
#### Phase 6 — Report
|
||||
|
||||
```markdown
|
||||
# Refactor Report
|
||||
|
||||
**Scope**: {module/pattern}
|
||||
**Target**: {one-sentence goal}
|
||||
**Steps executed**: {N}
|
||||
|
||||
## Before / After
|
||||
|
||||
| Metric | Before | After | Delta |
|
||||
| ---------------- | ------ | ----- | --------- |
|
||||
| LOC in scope | {N} | {N} | {N} |
|
||||
| Coverage (lines) | {X}% | {Y}% | {delta} |
|
||||
| Complexity | {N} | {N} | {delta} |
|
||||
| Files touched | — | {N} | — |
|
||||
| Tests | {N} | {N} | 0 changed |
|
||||
|
||||
## Steps
|
||||
|
||||
| # | Action | Outcome |
|
||||
| --- | ------------------ | ------------- |
|
||||
| 1 | Extract `findById` | ✅ tests pass |
|
||||
| 2 | Extract `create` | ✅ tests pass |
|
||||
| ... | ... | ... |
|
||||
|
||||
## Verification
|
||||
|
||||
- `bun run test:parallel`: {N} passed, 0 failed
|
||||
- `bun typecheck`: ✅
|
||||
- `bun oxlint --deny-warnings`: ✅
|
||||
- Coverage delta: {delta}
|
||||
|
||||
## Design Deviation
|
||||
|
||||
{If the refactoring changes the internal architecture in a way that merits an
|
||||
ADR, reference the ADR. **Deprecated:** `.artifacts/{slug}/design/adr/{NNNN}-*.md`
|
||||
→ ADRs now live on the Gitea wiki at page `{slug}/03-adr-{NNNN}-{title}`,
|
||||
readable via `gitea_wiki__get_page`. Or "None".}
|
||||
|
||||
---
|
||||
|
||||
**Handoff**: {if > 50 lines or ≥ 5 files → run `core/skills/review-code/SKILL.md`
|
||||
| otherwise → refactor complete, no review needed}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 7 — Approval
|
||||
|
||||
Present the report:
|
||||
|
||||
```
|
||||
Refactor complete: {one-line summary}
|
||||
- {N} steps executed, all tests pass
|
||||
- {before} → {after} ({delta} LOC)
|
||||
- Coverage: {before}% → {after}%
|
||||
|
||||
→ {if review needed: "Run code review?" | else: "Refactor complete. Approve?"}
|
||||
```
|
||||
@@ -0,0 +1,105 @@
|
||||
# Source Analysis — Document Templates
|
||||
|
||||
> Used by Phase B1 (pipeline mode).
|
||||
> Read this file when producing the source-analysis document set.
|
||||
> Each section below is the template for the corresponding numbered file
|
||||
> under `port-{name}/source-analysis/` (wiki pages via `wiki 读写 API(见 TERMINOLOGY)`).
|
||||
|
||||
## 01-source-overview.md
|
||||
|
||||
```markdown
|
||||
## Source Overview
|
||||
|
||||
- **Project**: {name}
|
||||
- **Language / Runtime**: {e.g. Python 3.11}
|
||||
- **Framework**: {e.g. FastAPI}
|
||||
- **Feature scope**: {description of what's being ported}
|
||||
- **Source files**: {N}
|
||||
- **Source LOC**: {L}
|
||||
- **Source modules**: {list of distinct functional areas}
|
||||
```
|
||||
|
||||
## 02-public-api.md
|
||||
|
||||
Document every public interface:
|
||||
|
||||
```markdown
|
||||
## Public API
|
||||
|
||||
| Method / Endpoint | Input Schema | Output Schema | Errors | Notes |
|
||||
| ----------------- | -------------- | --------------- | --------------------- | --------------- |
|
||||
| POST /auth/login | `{email, pw}` | `{token, user}` | 400, 401, 429, 500 | Rate limited |
|
||||
| GET /users/:id | path param | `User` object | 401, 403, 404 | Auth required |
|
||||
```
|
||||
|
||||
## 03-data-model.md
|
||||
|
||||
```markdown
|
||||
## Data Model
|
||||
|
||||
### Entity: User
|
||||
| Field | Type | Constraints |
|
||||
| ----------- | ---------- | ------------------- |
|
||||
| id | UUID | PK, not null |
|
||||
| email | string | unique, not null |
|
||||
| password | string | hashed, not null |
|
||||
| created_at | datetime | not null |
|
||||
|
||||
### Relationships
|
||||
- User 1—N Session
|
||||
- User N—M Role
|
||||
```
|
||||
|
||||
## 04-business-logic.md
|
||||
|
||||
Capture every business rule, validation, edge case, and state transition
|
||||
from the source. Write each rule as an executable assertion:
|
||||
|
||||
```markdown
|
||||
## Business Logic
|
||||
|
||||
### Login
|
||||
- RULE-01: Valid credentials → return JWT + user object
|
||||
- RULE-02: Invalid password → 401 "Invalid credentials"
|
||||
- RULE-03: Non-existent email → 401 "Invalid credentials" (same message, no enumeration)
|
||||
- RULE-04: 5 failed attempts in 1 min → 429 + lock for 15 min
|
||||
- RULE-05: Locked account + valid password → 423 "Account locked"
|
||||
|
||||
### Edge Cases
|
||||
- Empty email → 400 "Email is required"
|
||||
- Email > 254 chars → 400 "Email too long"
|
||||
- Password < 8 chars → 400 "Password too short"
|
||||
```
|
||||
|
||||
## 05-error-handling.md
|
||||
|
||||
```markdown
|
||||
## Error Handling
|
||||
|
||||
| Error Code | HTTP Status | Message | Source Condition |
|
||||
| ---------- | ----------- | -------------------- | --------------------- |
|
||||
| AUTH_001 | 400 | Email is required | empty email |
|
||||
| AUTH_002 | 401 | Invalid credentials | wrong email or pw |
|
||||
| AUTH_003 | 429 | Too many attempts | rate limit exceeded |
|
||||
| AUTH_004 | 423 | Account locked | locked out |
|
||||
```
|
||||
|
||||
## 10-fidelity-baseline.md
|
||||
|
||||
This is the master inventory used by `verify` at the end. Every source
|
||||
behavior is listed as a checkable item:
|
||||
|
||||
```markdown
|
||||
## Fidelity Baseline
|
||||
|
||||
| ID | Behavior | Type | Source Test | Chunk |
|
||||
| ---------- | ----------------------------------- | ------------ | -------------------- | ------------ |
|
||||
| FID-001 | Login with valid credentials | happy path | test_login_ok | chunk-auth |
|
||||
| FID-002 | Login with invalid password | error path | test_login_bad_pw | chunk-auth |
|
||||
| FID-003 | Login with empty email | edge case | test_login_empty | chunk-auth |
|
||||
| FID-004 | Rate limiting after 5 attempts | error path | test_rate_limit | chunk-auth |
|
||||
```
|
||||
|
||||
Each `FID-*` item maps to a `Chunk` column — this drives the DAG node
|
||||
decomposition. Behaviors in the same chunk are ported together. The
|
||||
`Source Test` column traces back to the original test for the verify stage.
|
||||
@@ -0,0 +1,301 @@
|
||||
# Source Analysis & Review — Detailed Processes
|
||||
|
||||
> Extracted from `implement/SKILL.md` (Mode: port) Phase A1, A1.5, A1.7, A1.8.
|
||||
> Read this file when executing the Source Analysis phases in standalone mode.
|
||||
|
||||
---
|
||||
|
||||
## Phase A1 — Understand Source
|
||||
|
||||
Read the source feature thoroughly — you must understand it well enough to
|
||||
reimplement it from scratch in a different tech stack.
|
||||
|
||||
### Process
|
||||
|
||||
1. **Source code** — read every file in the source scope. Understand:
|
||||
- Public API (method signatures, request/response schemas).
|
||||
- Data model (entities, fields, relationships).
|
||||
- Business logic (validation, business rules, edge cases).
|
||||
- Error handling (exception types, error codes, error messages).
|
||||
- Configuration (environment variables, feature flags, constants).
|
||||
|
||||
2. **Source tests** — read all tests for the source feature. Tests are the
|
||||
authoritative specification of behavior. Pay attention to:
|
||||
- Happy path assertions.
|
||||
- Edge case and boundary condition tests.
|
||||
- Error path tests.
|
||||
- Mock/stub setup (external dependencies).
|
||||
|
||||
After reading all source tests, run an automated extraction to seed the
|
||||
fidelity baseline:
|
||||
|
||||
1. **For each test file**, extract every test case name (e.g. `describe`/`it`
|
||||
blocks, function names in test files).
|
||||
2. **Generate a raw FID list** — one `FID-*` entry per test case:
|
||||
| FID-* | Test Name | Source File:Line | Type |
|
||||
| ----- | --------- | ---------------- | ---- |
|
||||
3. **Do NOT skip** — every test case becomes a FID item. Missing FID items
|
||||
are the #1 cause of incomplete porting.
|
||||
4. **Save** the raw FID list to wiki page `port-{name}/source-analysis/fid-raw` (via `wiki 读写 API(见 TERMINOLOGY)`).
|
||||
|
||||
In pipeline mode (Phase B1), this raw FID list feeds into `10-fidelity-baseline.md`.
|
||||
|
||||
3. **Source dependencies** — list every library, service, and infrastructure
|
||||
the source feature depends on:
|
||||
- Language runtime and version.
|
||||
- Framework (web framework, ORM, etc.).
|
||||
- Libraries (auth, logging, data parsing, etc.).
|
||||
- Infrastructure (database, cache, message queue, file storage).
|
||||
- External services (APIs, SaaS).
|
||||
|
||||
4. **Source Function Inventory** — produce a function-level catalog of every
|
||||
public API, private helper, and configuration constant in the source scope.
|
||||
This is the completeness audit trail — every unported function is visible.
|
||||
|
||||
### Output Templates
|
||||
|
||||
#### Source Analysis Document
|
||||
|
||||
```markdown
|
||||
## Source Analysis: {source feature name}
|
||||
|
||||
### Public API
|
||||
|
||||
| Endpoint / Method | Input | Output | Error Cases |
|
||||
| ----------------- | ----- | ------ | ----------- |
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
### Data Model
|
||||
|
||||
| Entity | Fields | Relations |
|
||||
| ------ | ------ | --------- |
|
||||
| ... | ... | ... |
|
||||
|
||||
### Dependencies
|
||||
|
||||
| Dep | Purpose | Available in Target? |
|
||||
| ------ | --------- | ------------------------ |
|
||||
| {name} | {purpose} | {yes / no / alternative} |
|
||||
```
|
||||
|
||||
#### Source Function Inventory
|
||||
|
||||
```markdown
|
||||
### Source Function Inventory
|
||||
|
||||
| Source File | Function / Symbol | Line | Type (public/private/config) | Ported? | Target Location |
|
||||
| ----------- | ----------------- | ---- | ---------------------------- | ------- | --------------- |
|
||||
| ... | ... | ... | ... | ☐ | |
|
||||
```
|
||||
|
||||
After Phase A5 (or at the end), require the Developer to backfill the "Ported?"
|
||||
and "Target Location" columns. Add a note: "Any ☐ remaining in the 'Ported?'
|
||||
column is a port gap."
|
||||
|
||||
---
|
||||
|
||||
## Phase A1.5 — Source Analysis Review (GATE)
|
||||
|
||||
Before proceeding to concept mapping, a reviewer (Explorer sub-agent) MUST
|
||||
cross-check the source analysis deliverables against the original source files.
|
||||
This is a lightweight but mandatory gate — source misunderstandings are the #1
|
||||
root cause of incomplete porting.
|
||||
|
||||
### Review Process
|
||||
|
||||
1. **Spawn a reviewer** (Explorer sub-agent) with access to:
|
||||
- All source files in the original project (or their copies if offline).
|
||||
- All A1 deliverables: Source Analysis doc, Source Function Inventory,
|
||||
`fid-raw.md`.
|
||||
|
||||
2. **Reviewer checks (4 dimensions)**:
|
||||
- **SRC-CMP** (Completeness — **automated, not manual**): Does the Source
|
||||
Function Inventory list every public/private function, symbol, and config
|
||||
constant found in source files? This dimension MUST be verified by an
|
||||
automated symbol diff (see step 2.5), not by the reviewer reading source
|
||||
files one-by-one. Manual "looks complete" judgments are the dominant
|
||||
failure mode for port completeness — they are the reason functions get
|
||||
silently dropped. Any symbol present in source but absent from the
|
||||
inventory is a BLOCKER gap.
|
||||
- **SRC-API** (API Accuracy): Does the Public API table correctly capture
|
||||
every endpoint/method, its input/output schema, and all documented error
|
||||
cases? Compare against source route/method definitions and error handling
|
||||
code.
|
||||
- **SRC-TST** (Test Coverage): Does `fid-raw.md` contain one FID entry for
|
||||
every `describe`/`it`/`test` block in the source test files? Any test case
|
||||
without a FID is a gap. Does every FID reference the correct source
|
||||
file:line?
|
||||
- **SRC-DEP** (Dependency Accuracy): Are all libraries, infrastructure
|
||||
services, and external APIs the source depends on listed? Check source
|
||||
package manager files (`package.json`, `Cargo.toml`, `requirements.txt`,
|
||||
etc.) and imports.
|
||||
|
||||
2.5. **SRC-CMP automated symbol verification (mandatory)** — Enumerate every
|
||||
symbol the source actually exports, then diff against the Source Function
|
||||
Inventory. This converts "is the inventory complete?" from a subjective
|
||||
judgment into an objective set difference. **Do NOT skip even if codegraph
|
||||
is unavailable** — fall back to `grep`, never to a manual glance.
|
||||
|
||||
```bash
|
||||
# Preferred: codegraph symbol enumeration (one call per source file in scope)
|
||||
codegraph_node --symbolsOnly <source-file>
|
||||
|
||||
# Fallback: grep for declarations in the source language
|
||||
grep -rEn '^\s*(export (async )?(function|const|class|interface|type|enum)|export \{|def |class |fn |public )' <source-dir>
|
||||
|
||||
# Then diff the enumerated source-symbol set against the inventory's
|
||||
# "Function / Symbol" column. Every source-only symbol is a BLOCKER.
|
||||
```
|
||||
|
||||
Record the command used and the resulting symbol-set delta under dimension
|
||||
`SRC-CMP` in `source-analysis-review.md`. A review that omits this
|
||||
automated delta is itself a BLOCKER — the gate was bypassed, not passed.
|
||||
|
||||
3. **Output**: Reviewer writes findings to wiki page `port-{name}/source-analysis/review` (via `wiki 读写 API(见 TERMINOLOGY)`) with format:
|
||||
|
||||
| Dimension | Finding | Severity (BLOCKER/MAJOR/MINOR) | Source Evidence |
|
||||
| --------- | ------- | ------------------------------ | --------------- |
|
||||
| SRC-CMP | Missing function `validateSession` in `auth/middleware.js:45` | MAJOR | Source file line 45 |
|
||||
|
||||
4. **GATE**: All BLOCKER findings MUST be resolved (add missing items to
|
||||
inventory/fid list) before proceeding to Phase A2. MAJOR findings require
|
||||
documented justification if deferred.
|
||||
|
||||
5. **Pass condition**: Developer prints:
|
||||
`SOURCE ANALYSIS REVIEW COMPLETE — {N} BLOCKER items fixed, {M} MAJOR items documented`
|
||||
|
||||
---
|
||||
|
||||
## Phase A1.7 — Target Surface Analysis
|
||||
|
||||
> **Root cause addressed**: Ports fail when the target project's receiving
|
||||
> surface is not analyzed. The Developer knows the source inside-out but has
|
||||
> no systematic picture of what the target already has, what it lacks, and
|
||||
> what structural changes are needed to receive the port. This phase closes
|
||||
> that gap.
|
||||
|
||||
Analyze the **target project's current state** to establish the receiving
|
||||
surface for the port. This is the mirror image of Phase A1 — instead of
|
||||
understanding the source, you understand the target.
|
||||
|
||||
### Process
|
||||
|
||||
1. **Target directory tree** — map the target project's package structure,
|
||||
especially the packages that will receive ported code or that the source
|
||||
feature depends on. For monorepos, list every package and its role.
|
||||
|
||||
2. **Target existing capabilities** — identify what the target project
|
||||
already has that overlaps with or relates to the source feature:
|
||||
- Existing modules, components, services in the same domain.
|
||||
- Existing routes, providers, context hierarchy.
|
||||
- Existing schemas, migrations, config entries.
|
||||
- Existing CLI commands, flags.
|
||||
- Existing theme/style files.
|
||||
|
||||
3. **Automated structural diff** — run a source vs target comparison across
|
||||
multiple dimensions to surface gaps that manual reading misses:
|
||||
|
||||
```bash
|
||||
# Directory structure diff (source feature scope vs target equivalent)
|
||||
diff <(cd /source && find packages/app/src -name '*.tsx' | sort) \
|
||||
<(cd /target && find packages/app/src -name '*.tsx' | sort)
|
||||
|
||||
# Dependency diff (package.json)
|
||||
diff <(jq '.dependencies | keys' /source/packages/app/package.json) \
|
||||
<(jq '.dependencies | keys' /target/packages/app/package.json)
|
||||
|
||||
# Export symbol diff (if codegraph is available)
|
||||
diff <(codegraph exports @source-ai/app) \
|
||||
<(codegraph exports @target-ai/app)
|
||||
```
|
||||
|
||||
If `codegraph` is not available, use `grep` for exported symbols or
|
||||
`glob` for file presence. The goal is **systematic, not manual** —
|
||||
never rely on reading files one by one to discover what the target has.
|
||||
|
||||
4. **Integration point identification** — where in the target project will
|
||||
the ported code connect?
|
||||
- Route table changes (new routes, modified redirects).
|
||||
- Provider/context hierarchy changes (new providers, insertion points).
|
||||
- Schema/migration additions (new tables, new columns).
|
||||
- Config/settings additions (new config entries, new setting keys).
|
||||
- CLI command additions or flag additions.
|
||||
- Build config changes (vite/webpack/tsconfig).
|
||||
- Package.json dependency additions.
|
||||
|
||||
5. **Target readiness assessment** — does the target need structural
|
||||
refactoring before it can receive the port?
|
||||
- Does the target need a new package? (e.g. a new `packages/timeline/`)
|
||||
- Does the target need an interface extraction? (e.g. extract
|
||||
`ServerService` to an interface before porting a new implementation)
|
||||
- Does the target need a migration to add tables/columns?
|
||||
- Does the target need config schema changes?
|
||||
|
||||
### Output
|
||||
|
||||
Publish to wiki page `port-{name}/source-analysis/target-surface` (via `wiki 读写 API(见 TERMINOLOGY)`) using the format
|
||||
in `reference/target-surface-template.md` (read it when executing this phase).
|
||||
|
||||
---
|
||||
|
||||
## Phase A1.8 — Capability Boundary Definition (GATE)
|
||||
|
||||
> **Root cause addressed**: Ports fail because the porting unit is "files"
|
||||
> rather than "capabilities". A single capability (e.g. "draft/tab system")
|
||||
> spans code files, type definitions, schemas, config, routes, providers,
|
||||
> themes, and tests. When the Developer ports only the files they see and
|
||||
> misses the implicit artifacts, the port is incomplete. This phase enforces
|
||||
> a complete artifact inventory per capability before any implementation.
|
||||
|
||||
Define the **complete boundary** of the capability being ported. A capability
|
||||
is not a file — it is the full set of artifacts required for the feature to
|
||||
function in the target project.
|
||||
|
||||
### Artifact Dimensions
|
||||
|
||||
Every capability MUST be analyzed across ALL 13 dimensions listed in
|
||||
`reference/capability-boundary-template.md` (read it when executing this
|
||||
phase). The 13 dimensions are: source code files, type definitions/interfaces,
|
||||
database schema/migrations, configuration entries, environment variables, CLI
|
||||
flags/commands, theme/style files, route definitions, provider/context
|
||||
hierarchy, build config changes, package.json dependencies, test files, and
|
||||
shared package changes. A dimension with no artifacts is explicitly marked
|
||||
"N/A — none required" (not silently skipped).
|
||||
|
||||
### Process
|
||||
|
||||
1. **For each dimension**, list every artifact:
|
||||
- **Source has**: what exists in the source project for this dimension.
|
||||
- **Target already has**: what the target project already has (from
|
||||
Phase A1.7 Target Surface Analysis).
|
||||
- **Needs creation / modification**: what must be created or changed in
|
||||
the target.
|
||||
- **Status**: ☐ not ported / ☑ ported / ⏭ N/A (none required)
|
||||
|
||||
2. **Cross-reference with Phase A1 Source Function Inventory** — every
|
||||
function/symbol in the inventory MUST appear in dimension 1 (source code
|
||||
files) or dimension 2 (type definitions). Any orphan is a gap.
|
||||
|
||||
3. **Cross-reference with Phase A1.7 Target Surface Analysis** — every
|
||||
"Gap" in the structural diff table MUST have a corresponding entry in
|
||||
the capability boundary. Any orphan is a gap.
|
||||
|
||||
4. **GATE**: All 13 dimensions MUST be filled in. A dimension with artifacts
|
||||
marked "☐ not ported" is acceptable ONLY if there is a documented deferral
|
||||
with a reactivation path (same rules as Phase A3 Gap Analysis). Dimensions
|
||||
that are "N/A — none required" must include a one-line justification.
|
||||
|
||||
### Output
|
||||
|
||||
Publish to wiki page `port-{name}/source-analysis/capability-boundary` (via `wiki 读写 API(见 TERMINOLOGY)`) using the format in
|
||||
`reference/capability-boundary-template.md` (includes the full 13-dimension
|
||||
table and output template).
|
||||
|
||||
### Pass condition
|
||||
|
||||
Developer prints:
|
||||
`CAPABILITY BOUNDARY COMPLETE — {N}/{13} dimensions have artifacts, {M} dimensions N/A, {K} items deferred with reactivation path`
|
||||
|
||||
**Do NOT proceed to Phase A2 until this gate passes.** The capability boundary
|
||||
is the single source of truth for "what must be ported" — every downstream
|
||||
phase references it.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Target Surface Analysis — Output Template
|
||||
|
||||
> Used by Phase A1.7 (standalone) and Phase B1.7 (pipeline).
|
||||
> Read this file when executing the Target Surface Analysis phase, then
|
||||
> produce the output document following this format.
|
||||
|
||||
Publish the output to wiki page `port-{name}/source-analysis/target-surface`
|
||||
(standalone) or `port-{name}/source-analysis/11-target-surface`
|
||||
(pipeline) via `wiki 读写 API(见 TERMINOLOGY)`.
|
||||
|
||||
```markdown
|
||||
## Target Surface Analysis
|
||||
|
||||
### Target Project Structure
|
||||
- Package map (package name → role)
|
||||
- Relevant directory trees
|
||||
|
||||
### Existing Capabilities (overlapping with source)
|
||||
| Target Module | Overlap with Source | Action (reuse / replace / extend) |
|
||||
| ------------- | ------------------- | --------------------------------- |
|
||||
| ... | ... | ... |
|
||||
|
||||
### Structural Diff Summary
|
||||
| Dimension | Source has | Target has | Gap |
|
||||
| --------------- | ---------- | ---------- | --- |
|
||||
| Files (.tsx) | {N} files | {M} files | {N-M} new |
|
||||
| Dependencies | {list} | {list} | {diff} |
|
||||
| Export symbols | {list} | {list} | {diff} |
|
||||
| Routes | {list} | {list} | {diff} |
|
||||
| Providers | {list} | {list} | {diff} |
|
||||
| Schemas | {list} | {list} | {diff} |
|
||||
| CLI commands | {list} | {list} | {diff} |
|
||||
| Theme files | {list} | {list} | {diff} |
|
||||
| Config entries | {list} | {list} | {diff} |
|
||||
| Env vars | {list} | {list} | {diff} |
|
||||
| Build config | {list} | {list} | {diff} |
|
||||
|
||||
### Integration Points
|
||||
| Integration Point | Change Required | Affected Target Files |
|
||||
| ----------------- | --------------- | --------------------- |
|
||||
| Route table | Add /new-session route | src/app.tsx |
|
||||
| Provider hierarchy | Insert TabsProvider | src/app.tsx |
|
||||
| ... | ... | ... |
|
||||
|
||||
### Target Readiness
|
||||
| Readiness Item | Required? | Complexity | Blocking? |
|
||||
| -------------- | --------- | ---------- | --------- |
|
||||
| New package | No | — | No |
|
||||
| Interface extraction | Yes | Medium | Yes |
|
||||
| Migration | Yes | Low | Yes |
|
||||
| Config schema | No | — | No |
|
||||
```
|
||||
@@ -0,0 +1,123 @@
|
||||
> Extracted from implement/SKILL.md (Pipeline Work Item Detection) — moved verbatim 2026-08-25, ticket [org-internal #3381].
|
||||
|
||||
## Pipeline Work Item Detection
|
||||
|
||||
Not all work items in an iteration plan involve writing new code. Some require
|
||||
fixing bugs, restructuring existing code, porting features, or building
|
||||
frontend UI. These specialized work items use different execution workflows
|
||||
(defined above) but flow through the same pipeline gates (review-code →
|
||||
verify; DAG-routed work resolves its spec from `{epic-slug}/dag`).
|
||||
|
||||
### Refactoring Work Items
|
||||
|
||||
A work item is a refactoring work item when:
|
||||
- Its description starts with "Refactor", "重构", "Restructure", "Extract",
|
||||
"Inline", "Move", "Rename", "Simplify", "Upgrade", or "Remove dead code".
|
||||
- It is explicitly tagged `[REFACTOR]` in the node ticket / request.
|
||||
- The node spec identifies it as a structural change that preserves
|
||||
behavior (no new capabilities, no bug fixes).
|
||||
- Requirements coverage is a refactoring requirement (REQ-REFACTOR-*).
|
||||
|
||||
When a work item is a refactoring work item, follow the workflow defined in
|
||||
Mode: refactor above with these adaptations:
|
||||
|
||||
1. **Scope & Baseline** (refactor Phase 1): The scope is the node spec /
|
||||
work-item description, not free-form user input.
|
||||
2. **Define Target Pattern** (refactor Phase 2): The target pattern must align
|
||||
with the baseline's architecture decisions (node spec + contracts). If the
|
||||
baseline does not prescribe a pattern, justify the choice in the refactor
|
||||
report.
|
||||
3. **Decompose into Steps** (refactor Phase 3): Present steps to the user for
|
||||
approval per the refactor workflow. If the design document decides the target
|
||||
pattern, the steps are not negotiable — they are derived from that decision.
|
||||
4. **Incremental Execution** (refactor Phase 4): Same as standalone refactor.
|
||||
Commit each step separately.
|
||||
5. **Final Validation** (refactor Phase 5): Run `bun run test:parallel`, `bun typecheck`,
|
||||
`bun oxlint --deny-warnings`. Coverage must not decrease.
|
||||
6. **Produce the implementation report** using the standard Phase 5 (Report)
|
||||
from ### Mode: implement (default). Embed the refactor report as the
|
||||
report body. The file change table and acceptance criteria table follow the
|
||||
standard format so the handoff to code review is seamless.
|
||||
|
||||
After the refactoring is complete, proceed to the standard Phase 5 (Report)
|
||||
and Phase 6 (Handoff to Code Review) exactly as a standard implementation
|
||||
work item would. The code review gate is mandatory for all refactoring work
|
||||
items regardless of size — there is no "no review needed" bypass.
|
||||
|
||||
### Bugfix Work Items
|
||||
|
||||
A work item is a bugfix work item when:
|
||||
- Its description starts with "Bugfix", "Fix", "修复", "Bug", or "Hotfix".
|
||||
- It is explicitly tagged `[BUGFIX]` in the node ticket / request.
|
||||
- The node spec identifies it as a correction of existing behavior
|
||||
(no new capabilities).
|
||||
- Its description references a bug report, stack trace, or root cause analysis
|
||||
from the bugfix workflow's Phases 1–2.
|
||||
|
||||
When a work item is a bugfix work item, follow the workflow defined in
|
||||
Mode: bugfix above with these adaptations:
|
||||
|
||||
1. **Understand & Reproduce** (bugfix Phase 1): The scope is the work item
|
||||
description and the bug reproduction steps captured in requirements.
|
||||
2. **Isolate Root Cause** (bugfix Phase 2): The root cause may already be
|
||||
documented in the requirements; verify it against the current codebase.
|
||||
If the root cause differs, flag a design gap and abort.
|
||||
3. **Write Regression Test** (bugfix Phase 3): Before fixing, write a test
|
||||
that fails with the bug's symptom. The test must exercise the exact
|
||||
condition described in the acceptance criteria.
|
||||
4. **Fix** (bugfix Phase 4): Apply the minimal surgical fix. The fix MUST
|
||||
match the design document's component and interface decisions.
|
||||
5. **Self-Check & Report** (bugfix Phase 5): Run `bun run test:changed`, `bun typecheck`,
|
||||
`bun oxlint --deny-warnings`. Verify every item in `core/checklists/bugfix.md`.
|
||||
6. **Produce the implementation report** using the standard Phase 5 (Report)
|
||||
from ### Mode: implement (default). Embed the bugfix report as the report
|
||||
body. The file change table and acceptance criteria table follow the
|
||||
standard format so the handoff to code review is seamless.
|
||||
|
||||
After the bugfix is complete, proceed to the standard Phase 5 (Report) and
|
||||
Phase 6 (Handoff to Code Review) exactly as a standard implementation work
|
||||
item would. The code review gate is mandatory for all bugfix work items
|
||||
regardless of size — there is no "no review needed" bypass for pipeline
|
||||
bugfixes.
|
||||
|
||||
### Frontend Work Items
|
||||
|
||||
A work item is a frontend work item when:
|
||||
- Its description starts with "Frontend", "UI", "Component", "Page", "Style",
|
||||
"前端", "UI", "组件", "页面", or "样式".
|
||||
- It is explicitly tagged `[FRONTEND]` in the node ticket / request.
|
||||
- The node spec identifies it as a UI-layer change.
|
||||
- The work item's component mapping (node `req_refs` + component field in
|
||||
`{epic-slug}/dag`; historically `{slug}/03-design-08-traceability`) shows
|
||||
components in `components/`, `pages/`, `views/`, `ui/`, or frontend
|
||||
framework directories.
|
||||
- The work item involves `.tsx`, `.jsx`, `.vue`, `.svelte`, `.astro`, `.css`,
|
||||
or `.scss` files exclusively (no backend or data-layer files).
|
||||
|
||||
When a work item is a frontend work item, follow the workflow defined in
|
||||
`core/skills/frontend/SKILL.md` with these adaptations:
|
||||
|
||||
1. **Parse Context** (frontend Phase 1): The scope is the work item
|
||||
description, the design sections it references, and the project's
|
||||
framework/styling conventions. Read neighboring frontend files to absorb
|
||||
patterns before writing code.
|
||||
2. **Plan UI Implementation** (frontend Phase 2): Produce a brief
|
||||
implementation plan covering component structure, props, state variants
|
||||
(loading/empty/error/edge), and accessibility requirements. Cross-
|
||||
reference every design specification.
|
||||
3. **Implement** (frontend Phase 3): Implement in layers — structure → style
|
||||
→ state variants → interactivity → accessibility pass. Every component
|
||||
MUST render gracefully in all states.
|
||||
4. **Self-Check** (frontend Phase 4): Run `bun typecheck`, `bun oxlint --deny-warnings`,
|
||||
`bun run test:changed`. Verify every item in `core/checklists/frontend.md`.
|
||||
5. **Produce the implementation report** using the standard Phase 5 (Report)
|
||||
from ### Mode: implement (default). Embed the frontend report as the report
|
||||
body. Include state coverage and accessibility verification tables. The file
|
||||
change table and acceptance criteria table follow the standard format so the
|
||||
handoff to code review is seamless.
|
||||
|
||||
After the frontend implementation is complete, proceed to the standard Phase 5
|
||||
(Report) and Phase 6 (Handoff to Code Review) exactly as a standard
|
||||
implementation work item would. The code review gate is mandatory for all
|
||||
frontend work items regardless of size — there is no "no review needed" bypass
|
||||
for pipeline frontend work.
|
||||
@@ -0,0 +1,203 @@
|
||||
---
|
||||
name: land-batch
|
||||
description: >
|
||||
Use ONLY when the orchestrator lands the finished task branches of one DAG
|
||||
iteration as a single batch PR ([org-internal #3731] batch-landing pilot). Composes
|
||||
path-disjoint member branches via the deterministic batch-compose script,
|
||||
pre-validates locally, opens ONE PR for N tasks (one CI fan-out instead of
|
||||
N on the single-runner pool), pastes each member's review synthesis into
|
||||
the PR conversation, runs one delta-scoped review-code on the batch head,
|
||||
then hands the labeled PR to the merge coordinator. NOT for hotfixes /
|
||||
CI-infra fixes (fast lane: 1:1 PR), single tasks, or single-session
|
||||
mono-branch batches (topology A — one session owns every ticket on one
|
||||
branch from inception; hand-compose those, see Applicability).
|
||||
stage: merge-pr
|
||||
triggers:
|
||||
- land batch
|
||||
- batch land
|
||||
- batch-compose
|
||||
- 批量归并
|
||||
- 批量提交 pr
|
||||
role: Producer
|
||||
---
|
||||
|
||||
# land-batch — batch PR composition for one DAG iteration
|
||||
|
||||
**Orchestrator-only.** The composing session is the epic's orchestrator
|
||||
(single writer at iteration close — the same role that already owns uniform
|
||||
merge/close). `workflow/batch/*` branches are NOT claim branches: no
|
||||
`claim.sh` interaction, no assignee; the orchestrator is the only composer.
|
||||
Pilot charter: the decision comment on [org-internal #3731] (scope, opt-in protocol, cap,
|
||||
success criteria) — this skill implements that charter, not the epic's
|
||||
frozen DAG v1 (which covers only the merge-coordinator nodes N-01..04).
|
||||
|
||||
## Applicability: post-hoc composition only (topology B)
|
||||
|
||||
Two batch shapes exist ([org-internal #3731] charter addendum, comment 39347):
|
||||
|
||||
- **Topology A — single-session mono-branch batch**: one session owns every
|
||||
ticket from inception and writes them on ONE branch (instance: PR [org-internal #3861],
|
||||
audit-h1). Hand composition is natural there — do NOT force this skill on
|
||||
it; record the outcome on [org-internal #3731] as a manual-batch datum instead.
|
||||
- **Topology B — parallel workers, post-hoc composition**: workers finish on
|
||||
SEPARATE branches at different times; the orchestrator composes at
|
||||
iteration close. This is the skill's ONLY applicability — cross-branch
|
||||
conflict attribution, lockfile convergence, and composition ordering are
|
||||
exactly the error-prone parts it mechanizes (path-disjoint admission,
|
||||
fail-closed attribution, `--convergent` surgery, per-member commit
|
||||
retention for revert granularity).
|
||||
|
||||
Quick discriminator: if every "member" would be a commit on a branch you are
|
||||
already writing, that is topology A — keep going, skip this skill. If you
|
||||
are landing OTHER workers' branches you never owned, that is topology B —
|
||||
this skill applies.
|
||||
|
||||
## Preconditions (all mandatory, fail-closed)
|
||||
|
||||
1. **Pilot opt-in**: the epic runs in batch-landing mode — its workers were
|
||||
seeded to report `status=done branch=<ref>` WITHOUT opening PRs (see the
|
||||
pilot note in `headless-session-ops/reference/completion-report.md`).
|
||||
Non-pilot epics keep the per-task 1:1 PR flow.
|
||||
2. **Every member is done**: review-code PASS on its own branch, run bundle
|
||||
archived (`gen-run-bundle.ts --slug <slug> --ticket <N>` — the bundle is
|
||||
committed on the member branch, so composition carries it; the
|
||||
merge-gate's presence check is per close-ref and expects it).
|
||||
3. **2..7 members** — a single done task rides its own 1:1 PR; more than 7
|
||||
(`batch-compose --max-members`, the pilot cap) splits into multiple
|
||||
batches along the path-overlap report.
|
||||
4. **No fast-lane cargo**: prod hotfixes and CI-infra fixes bypass batching
|
||||
entirely (1:1 PR, land immediately) — batching latency must never gate
|
||||
urgent work.
|
||||
|
||||
## Procedure
|
||||
|
||||
### 1. Compose (deterministic core)
|
||||
|
||||
```bash
|
||||
bun .gitea/scripts/batch-compose.ts \
|
||||
--branch workflow/task/<a> --branch workflow/task/<b> … \
|
||||
--out workflow/batch/<epic-slug>-iter<N> --dry-run
|
||||
```
|
||||
|
||||
For batches whose members ALL rewrite a regenerable shared file — the
|
||||
rule, not the exception, for dependency-bump batches (every `bun install`
|
||||
rewrites the root `bun.lock`) — pass `--convergent bun.lock`: the file is
|
||||
exempt from the overlap admission, and a merge that conflicts ONLY on it is
|
||||
surgically resolved to the running head's version (dropped from the index
|
||||
outright when the head no longer carries the path), with the JSON
|
||||
reporting `converged: ["bun.lock"]` and a regeneration hint. A conflict
|
||||
report carrying shapes the tool cannot attribute (rename/delete etc.)
|
||||
fails CLOSED — convergent exemption never widens into a blind pass.
|
||||
|
||||
Exit codes / handling: `1 usage / no-changes-over-base` → drop the named
|
||||
member or fix the invocation; `3 path-overlap` → split the batch along the
|
||||
reported pairs (or land the overlapping member 1:1) and re-run per group;
|
||||
`4 merge conflict` / `5 transport/git error` → fall
|
||||
back to 1:1 for this batch; `0` → drop `--dry-run`
|
||||
and push the composed branch. The script never touches the worktree or
|
||||
index (keeper-style plumbing) and each member keeps its original commits —
|
||||
revert granularity stays per-task.
|
||||
|
||||
### 2. Pre-validate locally (never enter CI red)
|
||||
|
||||
```bash
|
||||
git fetch origin workflow/batch/<slug>
|
||||
git switch --detach origin/workflow/batch/<slug>
|
||||
cd packages/octopus && bun run test:changed
|
||||
cd ../.. && git switch - # back to the orchestrator branch
|
||||
```
|
||||
|
||||
If the compose output carried a `regenerate convergent files (…)` hint —
|
||||
regenerate them FIRST, on the detached batch branch, and push the result
|
||||
(the composed branch deliberately carries a stale convergent file: either
|
||||
the surgically-resolved running-head version, or a member's clean-merged
|
||||
one; either way it is deterministic only after regeneration):
|
||||
|
||||
```bash
|
||||
git switch --detach origin/workflow/batch/<slug>
|
||||
bun install # regenerates bun.lock
|
||||
git add bun.lock # NEVER `commit -am` here: -a stages the
|
||||
# worktree's symlink stubs and trips the
|
||||
# pre-commit symlink guard (core.symlinks=false)
|
||||
git commit -m "[<epic-slug>][iter-N] chore(deps): regenerate bun.lock for batch <slug>"
|
||||
git push origin HEAD:refs/heads/workflow/batch/<slug>
|
||||
```
|
||||
|
||||
(On detached HEAD the commit-msg hook's format check is inert — the
|
||||
`[slug][iter-N]` prefix above keeps the corpus contract anyway.)
|
||||
|
||||
Failure → fix on the offending member branch (it is the only place the
|
||||
fault can live — paths are disjoint), re-compose, re-validate. Do NOT push
|
||||
a known-red batch.
|
||||
|
||||
### 3. Open the batch PR (gitea-rest recipes)
|
||||
|
||||
Title: `[<epic-slug>][iter-N] batch: <task-a> / <task-b> / …`
|
||||
|
||||
Body constraints (each is a merge-gate requirement, not style):
|
||||
|
||||
- **English close keywords, one per member issue**: `Closes #A`, `Closes #B`,
|
||||
… — Gitea auto-closes all of them; Chinese 关闭/修复 never auto-close.
|
||||
- **Zero unchecked top-level `- [ ]` lines in the body** — the gate's regex
|
||||
(`^-\s+\[ \]`) blocks on any unindented unchecked box, with no fence
|
||||
stripping. Render deferred/pending items as
|
||||
plain text (`· deferred: …`), never as checkboxes; the per-task live
|
||||
checklists stay on their issues.
|
||||
- Per-member summary (ticket, branch, one line) + bundle index.
|
||||
|
||||
### 4. Paste member syntheses into the PR conversation
|
||||
|
||||
Post one comment per member carrying its review-code verdict block
|
||||
(`**Verdict**: PASS|WARN` + the fenced JSON synthesis, from the member
|
||||
issue's review thread). **Why**: the merge-gate hunts FAIL verdicts only in
|
||||
the PR's own comment stream — a batch PR without these comments passes
|
||||
vacuously (a fake-green hole). Pasting restores FAIL visibility; a member
|
||||
synthesis of FAIL blocks the gate exactly as a 1:1 PR's would.
|
||||
|
||||
### 5. One review-code pass on the batch head
|
||||
|
||||
Run review-code against the batch PR. Members already PASS'd individually —
|
||||
scope the review delta to what composition added (merge seams, cross-member
|
||||
integration the per-task reviews could not see). This posts the required
|
||||
per-sha `pipeline/review-code` status; member statuses do NOT transfer to
|
||||
the composed head.
|
||||
|
||||
### 6. Label and hand off
|
||||
|
||||
Label the PR `ready-to-merge`. From here the flow is stock: the merge
|
||||
coordinator's double-green queue serially merges (CI/check +
|
||||
pipeline/merge-gate on the batch head, bot approve, main-account merge).
|
||||
Do NOT hand-merge, do NOT hand-sync main into the batch branch.
|
||||
|
||||
### 7. Post-merge expectations
|
||||
|
||||
Member issues auto-close via the English keywords; their `## 当前状态`
|
||||
PR/代码评审/CI rows fan out to EVERY close ref (status-sync poller Wide
|
||||
behavior — requires [org-internal #3867], merged 2026-08-31). `task done ≡ PR merged`
|
||||
still holds, transitively
|
||||
through the batch PR; verify milestones fire unchanged.
|
||||
|
||||
## Fallback matrix
|
||||
|
||||
| Failure | Action |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| path-overlap (exit 3) | split batch along overlap pairs / 1:1 for the odd member |
|
||||
| merge conflict (exit 4) | 1:1 for the whole batch |
|
||||
| local pre-validation red | fix on the offending member, re-compose |
|
||||
| batch CI red after mergeable | coordinator dequeues with trail; fix on member branches, re-compose as a new batch PR |
|
||||
|
||||
Any fallback preserves today's behavior — batch landing is an optimization,
|
||||
never a dependency.
|
||||
|
||||
## Measurement (pilot duty — revised per [org-internal #3731] charter addendum 39347)
|
||||
|
||||
Per-iteration attribution only; the global 7-day ratio is trend context,
|
||||
never a pass/fail judge (same-window confounders: test-slim [org-internal #3855] /
|
||||
test-dedupe [org-internal #3854] / the pilot's own PRs). On the server,
|
||||
`bun .gitea/scripts/ci-run-stats.ts --days 7 --json /tmp/<slug>-ci-stats.json`
|
||||
(the `--json` flag takes a filename — bare, it is a silent no-op; the human
|
||||
summary still prints). Same methodology as the checklist-merge-gate.yml
|
||||
2026-08-27 note. Record on [org-internal #3731]: (a) this batch iteration's per-workflow
|
||||
trigger counts vs a historically similar 1:1 iteration, and (b) one
|
||||
fallback drill (construct a path-overlap group → exit-3 split executed).
|
||||
Baseline, trend only: 2026-08-31, 5,081 runs / TOTAL 2.75x single runner.
|
||||
@@ -0,0 +1,132 @@
|
||||
---
|
||||
name: project-kickoff
|
||||
description: >
|
||||
Use ONLY when the user asks about project setup, kickoff, or project
|
||||
initialization ("kickoff", "project setup", "椤圭洰鍒濆鍖?, "椤圭洰閰嶇疆").
|
||||
Provides guidance on the octopus project kickoff workflow: checking readiness,
|
||||
running /kickoff or octopus kickoff, configuring codegraph, and understanding
|
||||
project scaffolding. Do not use for application-level configuration 鈥?
|
||||
this skill covers the octopus project lifecycle, not user application code.
|
||||
triggers:
|
||||
# High-discrimination phrases only ([org-internal #4525]): bare "setup"/"init"/"閰嶇疆" fire
|
||||
# on any configuration mention, and the skill gate folds the previous
|
||||
# assistant turn on approval turns 鈥?one stray "閰嶇疆" in a reply re-fires
|
||||
# the gate every turn. Keep triggers to phrases that name THIS workflow;
|
||||
# generic setup/init questions still route via the description in the
|
||||
# skills listing.
|
||||
- kickoff
|
||||
- project setup
|
||||
- 椤圭洰鍒濆鍖?
|
||||
- 椤圭洰閰嶇疆
|
||||
role: Producer
|
||||
---
|
||||
|
||||
> Core 涓珛鐗堬紙Increment 6a 鏀瑰啓锛屽師 deferHard verbatimDir锛夈€傛満鍒躲€佺粨鏋勪笌 frontmatter 淇濇寔锛涘疄渚嬫湳璇紙宸ュ叿鍚嶃€佽矾寰勩€佸伐鍗曞彿锛夋寜 `core/adapters/TERMINOLOGY.md` 缁戝畾鍒板叿浣撳疄渚嬨€?
|
||||
# Project Kickoff
|
||||
|
||||
## 1. Overview
|
||||
|
||||
Project kickoff is the octopus project initialization workflow that gets a project from zero to fully configured in one command. It covers readiness detection 鈫?setup 鈫?scaffold 鈫?config 鈫?codegraph init 鈫?skills overlay pull, producing a project that is ready for the full SDLC pipeline without manual configuration steps.
|
||||
|
||||
## 2. When This Skill Activates
|
||||
|
||||
This skill activates when the user's text matches any of the trigger keywords:
|
||||
|
||||
- `kickoff` 鈥?asking about the `/kickoff` command or the kickoff workflow
|
||||
- `project setup` 鈥?asking how to set up a project in octopus
|
||||
- `椤圭洰鍒濆鍖朻 / `椤圭洰閰嶇疆` 鈥?asking how to initialize or configure an octopus project (Chinese)
|
||||
|
||||
Generic "setup"/"init"/"閰嶇疆" mentions are deliberately NOT triggers ([org-internal #4525]): they collide with everyday configuration talk, and the hard skill gate folds the previous assistant turn into its match text on approval turns 鈥?a passing "鎴栭厤缃?x: false" in a reply would re-fire the gate every turn. `/init` note: `/init` generates AGENTS.md via LLM; kickoff uses the managed template from `octopus setup` 鈥?the Builder distinguishes based on context.
|
||||
|
||||
Typical user intents include: encountering a readiness prompt at session start, wondering how to initialize a project, or wanting to understand the kickoff workflow steps.
|
||||
|
||||
## 3. Prerequisites
|
||||
|
||||
Before kickoff can run:
|
||||
|
||||
- `codegraph` CLI must be installed and on `$PATH` (covered by the `codegraph-setup` skill)
|
||||
- Global octopus config (`<config-home>/octopus/octopus.jsonc`) must be merged and valid
|
||||
- The project root must be identified (the directory octopus is running in)
|
||||
|
||||
### CI Readiness
|
||||
|
||||
After kickoff completes, check for CI configuration:
|
||||
|
||||
```bash
|
||||
ls .gitea/workflows/ .github/workflows/ 2>/dev/null || echo "[GAP: no CI workflows]"
|
||||
```
|
||||
|
||||
If no workflows exist, recommend creating `.gitea/workflows/ci.yml` (this repo
|
||||
uses Gitea Actions) 鈥?or `.github/workflows/ci.yml` for a GitHub-hosted project
|
||||
鈥?with at minimum: typecheck, lint, and test gates. Without CI, every merge is a
|
||||
manual gate 鈥?type errors, lint regressions, and test failures can land on
|
||||
main and go undetected until the next local checkout.
|
||||
|
||||
## 4. Workflow
|
||||
|
||||
The kickoff workflow runs in four automated steps:
|
||||
|
||||
1. **Check readiness** 鈥?Determine if the project needs setup. Run `checkReadinessWithGitea()` or `octopus kickoff --check-only` to inspect the current state.
|
||||
2. **Run kickoff** 鈥?Execute `/kickoff` from the TUI or `octopus kickoff` from the terminal.
|
||||
3. **What happens** 鈥?A Phase 0 gate, then five sequential phases:
|
||||
- Phase 0 鈥?**Gitea auth check**: derives the `gitea` block from the git remote origin (persisting missing fields), then verifies the token (`GET /user`) and runs the auth wizard on failure; never aborts kickoff
|
||||
- [1/5] **Setup** 鈥?Creates project directories and writes `AGENTS.md` from the managed template
|
||||
- [2/5] **Scaffold** 鈥?Creates the `<instance-root>/` skeleton (`core/skills/` directory + default `octopus.jsonc` template if absent); domain skills content arrives via the [5/5] overlay pull
|
||||
- [3/5] **Config stub + MCP wiring** 鈥?Writes the `mcp.codegraph` block into `octopus.jsonc`
|
||||
- [4/5] **Codegraph init** 鈥?Initializes and builds the per-project `.codegraph/` index
|
||||
- [5/5] **Skills remote + overlay pull** 鈥?Opt-in semantics ([org-internal #4370]); branches by flags when no `overlay.remotes` entry is configured:
|
||||
- no flags (interactive run) 鈥?**safe default**: nothing is written; a remediation hint is printed (pass `--skills-remote`, or edit `overlay.remotes`), and the readiness skills item stays not-ready
|
||||
- `--yes` 鈥?legacy behavior kept: writes the default skills remote, then pulls domain skills content (`octopus skills pull` equivalent)
|
||||
- `--skills-remote <owner/repo>` (+ optional `--skills-remote-base-url <url>`) 鈥?writes the specified remote instead (ref `main`; base URL defaults to `<instance-base-url>`), then pulls from it
|
||||
- `overlay.remotes` already configured 鈥?the write is skipped (idempotent; an explicit `--skills-remote` is ignored with a warning)
|
||||
4. **Post-kickoff verification** 鈥?Run `octopus kickoff --check-only` again. After a `--yes` or `--skills-remote` run it reports all 5 items as ready; after a no-flag interactive run it reports 4 of 5 鈥?the skills item stays not-ready until a remote is configured (`--skills-remote`, `--yes`, or a manual `overlay.remotes` edit + `octopus skills pull`).
|
||||
|
||||
### First-ticket hint ([org-internal #3061])
|
||||
|
||||
For a brand-new project still validating its concept, open the FIRST ticket as
|
||||
`Kind/MVP` 鈥?the interactive no-pipeline mode (decision log + debt register in
|
||||
the ticket body). It graduates by relabeling to `Kind/Feature` (analyze-dag
|
||||
backfill mode picks up the decision log) when a second session, an external
|
||||
contract, or a second consumer of a shared type appears. Do not route early
|
||||
concept work into the full pipeline "to be safe" 鈥?the pipeline's coordination
|
||||
machinery is cost, not quality, at zero coordination.
|
||||
|
||||
## 5. Re-kickoff / Idempotency
|
||||
|
||||
Re-running kickoff on an already-configured project is safe 鈥?every step is idempotent:
|
||||
|
||||
- **Setup** skips existing directories
|
||||
- **Scaffold** skips existing files
|
||||
- **Config mutation** is a no-op if the `mcp.codegraph` block already matches
|
||||
- **Codegraph** re-indexes (rebuilds the graph, which is a safe refresh)
|
||||
- **Skills remote** ([5/5]) skips the write when `overlay.remotes` is already configured
|
||||
|
||||
**Idempotent silence** (REQ-F-005): A fully configured project produces zero startup prompts. Users can run `/kickoff` or `octopus kickoff --check-only` at any time for explicit readiness checks without side effects.
|
||||
|
||||
## 6. Troubleshooting
|
||||
|
||||
Common issues and their resolutions:
|
||||
|
||||
- **`codegraph: command not found`** 鈥?Run the `codegraph-setup` skill, or install via `bun add -g @colbymchenry/codegraph`
|
||||
- **Permission denied** 鈥?Check project directory ownership and write permissions
|
||||
- **Partially complete state** 鈥?Re-run `/kickoff`; idempotent steps will complete the missing parts without affecting already-completed steps
|
||||
- **MCP tools not available** 鈥?Verify `mcp.codegraph` exists in `octopus.jsonc`; re-run `octopus kickoff` to write it
|
||||
|
||||
## 7. Related Commands
|
||||
|
||||
- `/init` 鈥?Generates `AGENTS.md` using an LLM (custom, context-aware); kickoff uses the managed template from `octopus setup`
|
||||
- `octopus setup` 鈥?Creates directories + `AGENTS.md` from the managed template; kickoff calls this internally
|
||||
- `octopus scaffold` 鈥?Creates the `<instance-root>/` skeleton (`core/skills/` directory + default `octopus.jsonc` template if absent); run `octopus skills pull` to fetch domain skills content; kickoff calls this internally
|
||||
- `octopus kickoff --check-only` 鈥?Readiness check only, no pipeline execution
|
||||
- `octopus kickoff --skills-remote <owner/repo>` 鈥?Explicit opt-in: writes the given skills remote to `overlay.remotes` (ref `main`) and pulls it instead of the default
|
||||
- `octopus kickoff --skills-remote-base-url <url>` 鈥?Base URL for `--skills-remote` (requires it); defaults to `<instance-base-url>` when omitted
|
||||
- `octopus kickoff --yes` 鈥?Skips the opt-in confirmation and keeps the legacy behavior of writing the default skills remote (`Octopus/octopus-skills`)
|
||||
- `codegraph-setup` skill 鈥?For installing the codegraph CLI and initializing the per-project index
|
||||
|
||||
## 8. Reference
|
||||
|
||||
- `<harness-package>/src/project-kickoff/readiness.ts` 鈥?`checkReadiness()` implementation (sync 4-item base)
|
||||
- `<harness-package>/src/project-kickoff/readiness-gitea.ts` 鈥?`checkReadinessWithGitea()` composed 5-item check (adds gitea auth reachable)
|
||||
- `<harness-package>/src/cli/cmd/kickoff.ts` 鈥?`/kickoff` command handler
|
||||
- `core/skills/codegraph-setup/SKILL.md` 鈥?CodeGraph setup skill
|
||||
- Project roadmap at `.artifacts/project-kickoff/` (deprecated 鈥?now stored as wiki page `project-kickoff/roadmap/` via `wiki 璇诲啓 API锛堣 TERMINOLOGY锛塦)
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
name: prototype
|
||||
description: >
|
||||
Use ONLY when the user asks to build a prototype, proof-of-concept, spike, or
|
||||
exploratory implementation where the code's ultimate fate (keep vs discard)
|
||||
is not yet decided by the standard pipeline. The Developer (Worker) first
|
||||
classifies the work as THROWAWAY (spike) or EVOLUTIONARY (high-fidelity
|
||||
prototype that will be promoted to production), then applies the matching
|
||||
disposition contract. Do NOT use for standard feature work (use implement),
|
||||
bug fixes (use implement, Mode: bugfix), or refactoring (use implement, Mode: refactor).
|
||||
triggers:
|
||||
- prototype
|
||||
- spike
|
||||
- proof of concept
|
||||
- poc
|
||||
- 原型
|
||||
- 高保真原型
|
||||
- 演进式原型
|
||||
- 技术探索
|
||||
- 技术验证
|
||||
- throwaway
|
||||
- evolutionary prototype
|
||||
role: Producer
|
||||
---
|
||||
|
||||
# Prototype / Spike
|
||||
|
||||
Classify the work BEFORE writing code, then apply the disposition contract.
|
||||
The classification is a one-line decision recorded in the artifact header.
|
||||
|
||||
## Phase 0 — Disposition Decision (MANDATORY)
|
||||
|
||||
Answer this question in one sentence, citing the user's words as evidence:
|
||||
|
||||
> **Will this exact code be promoted to production (EVOLUTIONARY), or will it
|
||||
> be discarded after we learn from it (THROWAWAY)?**
|
||||
|
||||
| Signal from user | Disposition |
|
||||
|---|---|
|
||||
| "spike", "validate", "prove it works", "throwaway", "探索", "验证" | THROWAWAY |
|
||||
| "prototype then keep iterating", "evolve", "no rewrite", "MVP from this", "演进", "不重写" | EVOLUTIONARY |
|
||||
| Conditional evolution: "if it works, we'll keep iterating", "if users like it, we'll build on it" | **ASK the user**: "Do you intend to evolve this exact codebase (EVOLUTIONARY), or rewrite cleanly if validated (THROWAWAY)?" |
|
||||
| Ambiguous ("let's prototype and see") | **ASK the user.** Do not guess. |
|
||||
|
||||
Record the decision in the artifact you produce:
|
||||
|
||||
```
|
||||
@disposition: THROWAWAY | EVOLUTIONARY
|
||||
@evidence: "<the user's exact phrase that decided it>"
|
||||
```
|
||||
|
||||
If you cannot cite the user's exact phrase, you have not decided — ask.
|
||||
|
||||
---
|
||||
|
||||
## THROWAWAY contract (spike)
|
||||
|
||||
Goal = **learning**, not code. Code is a means to a decision.
|
||||
|
||||
- **Time-box**: state a deadline (hours/days) up front. Stop when the box ends, even mid-sentence.
|
||||
- **Artifacts**:
|
||||
1. Throwaway code (no tests, no lint, no polish — label `@ai-artifact: spike`)
|
||||
2. **Learning report** (wiki page `{slug}/spike-report`): what was validated, what failed, decision (go / pivot / stop)
|
||||
- **Fencing**: spike code MUST NOT be importable from production code. Put it in `spike/` or a scratch worktree; delete it after the report is accepted.
|
||||
- **Exit gate**: the learning report is the deliverable. Code without a report = incomplete.
|
||||
- **Terminal artifact**: wiki page `{slug}/spike-report` is consumed by the human decision-maker (go/pivot/stop), not by a downstream skill. This is an intentional pipeline terminal, analogous to `release` and `retrospective` outputs.
|
||||
|
||||
## EVOLUTIONARY contract (high-fidelity prototype)
|
||||
|
||||
Goal = **seed code that becomes production**. Code is the product.
|
||||
|
||||
- **Debt register** (wiki page `{slug}/prototype-debt`): every shortcut is a line item with an owner and a promotion criterion. Example:
|
||||
| Shortcut | Why | Promotion criterion |
|
||||
|---|---|---|
|
||||
| Hardcoded auth token | unblock UI flow | Replace with real auth before any external user |
|
||||
| No tests on prototype UI | speed | Add source-verification tests before merging to main |
|
||||
- **Quality floors that apply from day 1**: typecheck passes, no `any` without justification, no `console.log` left in committed code.
|
||||
- **Quality floors explicitly deferred** (and tracked in the debt register): full test coverage, error states, observability, performance budgets.
|
||||
- **Promotion gate**: the prototype is "production" only when the debt register is empty OR each remaining item has an explicit waiver from the reviewer. Route through `review-code` with `mode: "prototype-promotion"` before promotion; the debt register is a required review input in that mode. Waived items become `ACCEPTED_RISK` findings in the review report, which `verify` Phase 5.5 registers as `## TD 登记` rows — promoted to `tech-debt` labeled Gitea issues on claim (`.octopus/rules/ticket-lifecycle.md`); the register lives in the issue tracker, not a parallel file.
|
||||
|
||||
## Rationalization table
|
||||
|
||||
| Excuse | Reality |
|
||||
|---|---|
|
||||
| "The user said 'no rewrite', so I'll just skip design forever" | EVOLUTIONARY defers design DETAILS, not design ITSELF. The debt register IS the design surface; promotion requires review-code. |
|
||||
| "This is just a spike, so no need to record anything" | The learning report IS the spike's deliverable. No report = no learning = wasted spike. |
|
||||
| "The prototype works, let's ship it and clean up later" | "Later" never comes. The debt register is the cleanup plan; promote only when it's empty or waived. |
|
||||
| "I'll decide throwaway vs evolutionary after I see how it goes" | Deciding after = throwaway by default (no debt register, no promotion path). If it might evolve, declare EVOLUTIONARY up front. |
|
||||
| "'If it works, we'll keep iterating' — so EVOLUTIONARY" | "If it works" is conditional. The user has NOT committed to evolving this exact codebase. ASK: evolve-this-code or validate-then-rewrite? |
|
||||
|
||||
## Anti-patterns (do not do these)
|
||||
|
||||
- ❌ Building a "prototype" with full production quality "just in case" — that's not a prototype, that's slow feature work.
|
||||
- ❌ Building a "spike" then quietly merging it because it happened to work — that's evolutionary without the debt register.
|
||||
- ❌ Freezing an evolutionary prototype and rewriting it in `src/` — that's a throwaway; if the user said "no rewrite", honor it.
|
||||
|
||||
## References
|
||||
|
||||
- `.octopus/checklists/prototype.md` — Prototype self-check checklist
|
||||
@@ -0,0 +1,447 @@
|
||||
---
|
||||
name: release
|
||||
description: >
|
||||
Use ONLY when ready to cut a release. The Release Manager (Worker) inspects
|
||||
project state (git log, version files, build) to determine the semver bump,
|
||||
generate the changelog, tag, and produce a release artifact. Works on any
|
||||
project state 鈥?no pipeline artifacts required.
|
||||
triggers:
|
||||
- release
|
||||
- cut release
|
||||
- 鍙戠増
|
||||
- publish release
|
||||
- ship release
|
||||
role: Producer
|
||||
---
|
||||
|
||||
> Core 涓珛鐗堬紙Increment 6a 鏀瑰啓锛屽師 deferHard verbatimDir锛夈€傛満鍒躲€佺粨鏋勪笌 frontmatter 淇濇寔锛涘疄渚嬫湳璇紙宸ュ叿鍚嶃€佽矾寰勩€佸伐鍗曞彿锛夋寜 `core/adapters/TERMINOLOGY.md` 缁戝畾鍒板叿浣撳疄渚嬨€?
|
||||
# Release
|
||||
|
||||
Prepare and tag a release based on project facts 鈥?git history since the last
|
||||
tag, current version, build status, and dependency health. No dependency on `.artifacts/{slug}/`
|
||||
(**deprecated** 鈥?SDLC artifacts now live in the Gitea wiki at `{slug}/...`;
|
||||
see `_shared/gitea-read-patterns.md`) or any pipeline skill outputs.
|
||||
|
||||
**This SKILL does not deploy.** Deployment is project-specific (K8s, Docker,
|
||||
npm publish, etc.) and varies too widely to template. The release artifact
|
||||
produced here is the input to project-specific deployment workflows.
|
||||
|
||||
## Agent Role
|
||||
|
||||
The release is owned and executed by the **Release Manager** (Worker).
|
||||
|
||||
**Context compaction**: release is a pipeline stage boundary. The main session
|
||||
compacts at this clean boundary ONLY when a capacity/projection trigger holds,
|
||||
per `core/rules/compact.md` 搂"Stage-boundary compaction" (long multi-stage
|
||||
runs 鈥?DAG Epic orchestration 鈥?keep the legacy every-boundary compaction;
|
||||
short runs 鈥?bugfix / DAG task 鈥?and standalone runs default to NOT
|
||||
compacting). The Release Manager itself is single-phase and
|
||||
artifact-driven (version files, changelog, tag); a mid-run compaction loses
|
||||
nothing 鈥?re-read the release checklist state and git log to resume.
|
||||
|
||||
---
|
||||
|
||||
## Preconditions
|
||||
|
||||
Before starting the release:
|
||||
|
||||
- [ ] Working tree is clean (`git status` has no uncommitted changes).
|
||||
- [ ] `core/checklists/release.md` is accessible.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 鈥?Pre-release Gate
|
||||
|
||||
Run each check against the live project. Stop and report failures.
|
||||
|
||||
1. **Clean workspace**: `git status` must show nothing to commit.
|
||||
2. **Branch**: confirm the current branch. Default assumption: release from
|
||||
`main` or `master`. If on another branch, note it.
|
||||
3. **Build**: run the project's build command. Must pass.
|
||||
4. **Typecheck + Lint**: run the project's typecheck and lint. Must pass.
|
||||
5. **Tests**: run the project's test suite. Must pass.
|
||||
6. **Dependency audit**: run the project's vulnerability scanner
|
||||
(e.g. `npm audit`, `bun audit`, `pip-audit`, `cargo audit`).
|
||||
- No new HIGH or CRITICAL CVEs block the release.
|
||||
- Pre-existing HIGH/CRITICAL CVEs do NOT block but **MUST be filed as
|
||||
tech-debt** before the release lands. File them in TWO TIERS (batching
|
||||
rule, [org-internal #3846] 鈥?a single release audit once fanned out to 14 TDs 鈫?14 PRs 鈫?
|
||||
56-84 CI runs on a saturated runner pool): Tier A below is the general
|
||||
mechanical-change batching rule; Tier B and the escape hatch follow it.
|
||||
|
||||
<a id="phase-1-tier-a"></a>
|
||||
### Tier A 鈥?閫氱敤鏈烘鍙樻洿骞跺崟瑙勫垯 / Universal mechanical-change batching rule
|
||||
|
||||
Origin [org-internal #3846] (audit-batch pilot, generalized in place): the rule covers ANY
|
||||
single-source fan-out of mechanical changes, not just dependency audits.
|
||||
Two source types today: (a) dependency bump audits; (b) docs 鎵归噺淇
|
||||
(batch docs revisions).
|
||||
|
||||
#### 瑙勫垯姝f枃 / Rule text
|
||||
|
||||
- **Core**: mechanical changes of ONE class fanning out from a single
|
||||
source are filed as ONE batch issue + ONE batch PR 鈥?never N脳issue +
|
||||
N脳PR. The batch body keeps one row per item (per-CVE / per-doc mapping),
|
||||
so per-item traceability is unchanged; work the batch as a single PR.
|
||||
- **(a) Dependency bump audits** (the [org-internal #3846] origin case): advisories whose
|
||||
remediation is a plain version bump (lockfile-only diff, no semver-major
|
||||
jump, no overrides/catalog surgery, no API or adapter fallout) are filed
|
||||
together via `宸ュ崟 API锛堣 TERMINOLOGY锛塩reate` with
|
||||
`title="[{origin}] audit-batch@{version}: mechanical bumps for {N} advisories (TD-{NNN})"`
|
||||
and labels `[tech_debt_label_id, severity_label_id, kind_bug_label_id]`
|
||||
(severity = highest in the batch). One row per advisory 鈥?CVE/GHSA ID,
|
||||
affected package + from鈫抰o version, severity (CVSS), advisory URL,
|
||||
recommended remediation, and a per-advisory Reactivation Trigger
|
||||
("resolved when `npm audit` / `bun audit` reports no HIGH/CRITICAL for
|
||||
this advisory") 鈥?so the release-notes `TD-NNN 鈫?#NNNN` mapping stays
|
||||
per-advisory.
|
||||
- **(b) Docs 鎵归噺淇 (batch docs revisions)**: many small same-class docs
|
||||
corrections discovered in one pass (e.g. a terminology sweep) file as ONE
|
||||
issue via the same flow,
|
||||
`title="[{origin}] docs-batch: {class} revisions for {N} files"`, with one
|
||||
row per file (path, correction, reason); same ONE-batch-PR landing.
|
||||
- **Landing**: when members live on separate branches, compose the single
|
||||
batch PR via the `land-batch` skill (`core/skills/land-batch/SKILL.md`
|
||||
鈥?cross-branch batch composition, topology B). For dependency-bump
|
||||
batches pass `batch-compose --convergent bun.lock` (script
|
||||
`.gitea/scripts/batch-compose.ts`): the regenerable lockfile is exempt
|
||||
from path-overlap admission and lockfile-only conflicts are surgically
|
||||
resolved to the running head's version.
|
||||
|
||||
#### 閫傜敤杈圭晫 / Applicability boundary
|
||||
|
||||
- **闈?mechanical锛堝惈鍒ゆ柇鎴愬垎鐨勫彉鏇达紝涓嶅苟鍗曪級** 鈥?items requiring judgment
|
||||
(wording decisions, behavior/API changes, review-dependent edits) are
|
||||
never batched; file each as its own issue.
|
||||
- **璺緞閲嶅彔锛坆atch 鎴愬憳瑙︾鐩稿悓鏂囦欢 鈫?涓嶅苟鍗曪紱鍞竴璞佸厤 =
|
||||
鍙啀鐢熷叡浜枃浠?`bun.lock` 缁?batch-compose `--convergent` 鏀舵暃锛?* 鈥?the sole
|
||||
path-overlap exemption is the regenerable shared lockfile under
|
||||
`--convergent`; every other same-file collision stays un-batched.
|
||||
- **semver-major 璺冲彉 / overrides路catalog 鎵嬫湳 / adapter路peer 鑱斿姩锛堚啋
|
||||
Tier B 鐙珛鍗曪紝缁存寔 per-package锛?* 鈥?these stay per-package Tier B
|
||||
issues. Tier B and the escape hatch (below) survive this generalization
|
||||
unchanged.
|
||||
|
||||
#### 闄嶇骇璺緞 / Degradation path
|
||||
|
||||
- **骞跺崟 PR CI 澶辫触 鈫?鎸夐攣鏂囦欢 hunk 鎷嗗寘鍥為€€锛坆isect锛?*: for dependency
|
||||
batches, bisect by splitting lockfile hunks per package back into
|
||||
per-package PRs 鈥?the original Tier A bisect semantics carried over
|
||||
verbatim (鍚岃涔夛紝娉涘寲鎺緸: every mechanical batch degrades the same way).
|
||||
- **Docs batches**: split per file back into per-file PRs.
|
||||
- **land-batch composition fallback** (exit codes per
|
||||
`.gitea/scripts/batch-compose.ts`): `3` path-overlap 鈫?split the batch
|
||||
along the reported pairs (or land the overlapping member 1:1) and re-run
|
||||
per group; `4` merge conflict / `5` transport/git error 鈫?fall back to
|
||||
1:1 PRs for the whole batch.
|
||||
- **Preflight hedge ([org-internal #3846])**: run `bun install --dry-run` before opening
|
||||
a dependency-batch PR 鈥?the resolver accepting the composed version set
|
||||
is a cheap pre-CI rejection of impossible bump combinations.
|
||||
|
||||
Tier B and the escape hatch (unchanged by the generalization):
|
||||
|
||||
- **Tier B 鈥?surgery, one issue per package**: semver-major jumps,
|
||||
adapter/peer fallout, or overrides/catalog surgery keep the
|
||||
per-package issue: `title="[{origin}] {CVE-ID/GHSA-ID} in {package} (TD-{NNN})"`
|
||||
with the same labels; body requirements match the Tier A rows.
|
||||
- Escape hatch: either tier may carve a single advisory into its own
|
||||
issue when same-day remediation is required (urgent HIGH/CRITICAL).
|
||||
|
||||
De-duplicate against open `tech-debt` issues (match by
|
||||
CVE/GHSA ID) before creating. Record the `TD-NNN 鈫?#NNNN` mapping in the
|
||||
release notes. See `verify` Phase 5.5 for the tech-debt promotion body
|
||||
template and `_shared/gitea-write-patterns.md` Pattern 3.
|
||||
|
||||
```markdown
|
||||
## Pre-release Gate
|
||||
|
||||
| Check | Status |
|
||||
| ----------------- | ----------------------------------------------------- |
|
||||
| Clean workspace | 鉁?/ 鉂? |
|
||||
| Branch | {branch name} |
|
||||
| Build | 鉁?/ 鉂? |
|
||||
| Typecheck + Lint | 鉁?/ 鉂? |
|
||||
| Tests | 鉁?/ 鉂?(N passed, M failed) |
|
||||
| Dependency audit | 鉁?/ 鈿狅笍 N known CVEs (pre-existing) / 鉂?N new CVEs |
|
||||
```
|
||||
|
||||
If any gate fails except known CVEs, stop and report what failed.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 鈥?Version Bump
|
||||
|
||||
Determine the new version by inspecting git history since the last tag.
|
||||
|
||||
1. **Find last tag**: `git describe --tags --abbrev=0` (or `git tag --sort=-v:refname | head -1`).
|
||||
- **No previous tag?** This is the first tracked release. Use the initial commit as
|
||||
baseline: `git rev-list --max-parents=0 HEAD`. After tagging this release, create
|
||||
a retroactive baseline tag (`v{base-version}`) on the initial commit so future
|
||||
cycles have a clean `{tag}..{tag}` range. Document the gap in the release report.
|
||||
2. **Read commits since last tag**: `git log <last-tag>..HEAD --oneline`.
|
||||
3. **Read current version** from the project's version file (`package.json`
|
||||
`version`, `Cargo.toml`, `VERSION`, etc.).
|
||||
4. **Categorize commits** by change type:
|
||||
|
||||
| Conventional prefix | Semver | Examples |
|
||||
| -------------------- | ------- | ----------------------------- |
|
||||
| `BREAKING CHANGE:` / `!:` | MAJOR | API removal, schema change |
|
||||
| `feat:` | MINOR | New feature, new endpoint |
|
||||
| `fix:` | PATCH | Bug fix only |
|
||||
| `perf:` | PATCH | Performance improvement |
|
||||
| `refactor:` | PATCH | Internal restructuring |
|
||||
| `docs:` / `chore:` | (skip) | Not user-visible |
|
||||
|
||||
If no conventional prefix found, infer from the subject line:
|
||||
- "add", "implement", "introduce" 鈫?MINOR
|
||||
- "fix", "resolve", "correct" 鈫?PATCH
|
||||
- "remove", "drop", "rename" (public API) 鈫?MAJOR
|
||||
|
||||
5. **Compute bump**:
|
||||
- If any MAJOR commit 鈫?bump MAJOR.
|
||||
- Else if any MINOR commit 鈫?bump MINOR.
|
||||
- Else 鈫?bump PATCH.
|
||||
|
||||
```markdown
|
||||
## Version Bump
|
||||
|
||||
**Last tag**: {tag}
|
||||
**Current version**: {old version}
|
||||
**New version**: {new version}
|
||||
**Type**: MAJOR / MINOR / PATCH
|
||||
**Commits since last tag**: {N}
|
||||
**Reason**: {justification 鈥?e.g. "2 feat + 3 fix 鈫?MINOR"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 鈥?Changelog
|
||||
|
||||
Generate the changelog from `git log <last-tag>..HEAD`:
|
||||
|
||||
1. **List all commits**. For each, extract:
|
||||
- Type (from prefix or inferred).
|
||||
- Scope (if present, e.g. `feat(auth):`).
|
||||
- Description (the subject line, past tense, human-readable).
|
||||
2. **Group by type**:
|
||||
- **Added** 鈥?`feat:` commits.
|
||||
- **Changed** 鈥?modifications to existing behavior (non-breaking).
|
||||
- **Fixed** 鈥?`fix:` commits.
|
||||
- **Breaking** 鈥?`BREAKING CHANGE:` or `!:` commits.
|
||||
3. **Deduplicate**: squash multiple commits for the same change into one entry
|
||||
where it makes narrative sense.
|
||||
4. Read the existing root `CHANGELOG.md` (if any) and prepend this release.
|
||||
|
||||
Write to root `CHANGELOG.md` (prepend section).
|
||||
|
||||
```markdown
|
||||
## [{version}] 鈥?{YYYY-MM-DD}
|
||||
|
||||
### Added
|
||||
|
||||
- {feature} ({commit hash short})
|
||||
|
||||
### Changed
|
||||
|
||||
- {change} ({hash})
|
||||
|
||||
### Fixed
|
||||
|
||||
- {bugfix} ({hash})
|
||||
|
||||
### Breaking
|
||||
|
||||
- {breaking change} ({hash}) 鈥?see migration notes above
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 鈥?Tag & Finalize
|
||||
|
||||
1. **Update version file** 鈥?write the new version to the project's version
|
||||
manifest.
|
||||
2. **Commit**: `git add` version file + changelog file, commit with message:
|
||||
```
|
||||
release: {version}
|
||||
```
|
||||
3. **Tag**: `git tag v{version}` (adjust prefix per project convention 鈥?
|
||||
check existing tags with `git tag -l`).
|
||||
4. **Verify tag**: `git tag -l v{version}` confirms the tag exists.
|
||||
|
||||
**Do not push** 鈥?the user must explicitly request pushing to remote.
|
||||
|
||||
```markdown
|
||||
## Release Artifact
|
||||
|
||||
- **Version**: {version}
|
||||
- **Commit**: {commit hash}
|
||||
- **Tag**: v{version}
|
||||
- **Changelog**: CHANGELOG.md updated
|
||||
|
||||
### Files Changed
|
||||
|
||||
| File | Change |
|
||||
| -------------- | ------------------------- |
|
||||
| {version file} | {old} 鈫?{new} |
|
||||
| CHANGELOG.md | Prepended {version} |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4b 鈥?Publish Release Artifacts (octopus project, manual)
|
||||
|
||||
> Octopus-specific. Other projects: substitute your own artifact pipeline 鈥?
|
||||
> the goal is identical (turn the tag into downloadable assets).
|
||||
|
||||
The CI publish pipeline (`.gitea/workflows/publish.yml`) was **retired by
|
||||
[org-internal #2003]** (138/138 historical runs cancelled; the pipeline sat unused for 35+
|
||||
days). Releasing octopus is now a **manual local process**. All former CI
|
||||
steps live in repo scripts, runnable from a maintainer machine with the right
|
||||
credentials in the environment.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Clean checkout of the release commit (tag pushed or about to be pushed).
|
||||
- Credentials in env:
|
||||
- `GITEA_TOKEN` 鈥?PAT with `write:repository` (release create/undraft,
|
||||
tag push) and `write:package` (container registry). The `CI_PAT` secret
|
||||
value is the canonical token.
|
||||
- `NODE_AUTH_TOKEN` 鈥?only if publishing to npm.
|
||||
- `AUR_KEY` 鈥?only if pushing the AUR package.
|
||||
- Docker logged in to the Gitea container registry:
|
||||
`echo "$GITEA_TOKEN" | docker login <instance-registry-host> -u <user> --password-stdin`.
|
||||
|
||||
### Steps
|
||||
|
||||
1. **Version + draft release** (idempotent 鈥?skips if already published):
|
||||
```bash
|
||||
GITEA_TOKEN=<pat> GH_REPO=Octopus/octopus bun script/version.ts
|
||||
```
|
||||
Creates (or refines) the draft Gitea release for `v{version}`.
|
||||
|
||||
2. **Build the 12 platform binaries + archives** (longest step):
|
||||
```bash
|
||||
OCTOPUS_VERSION={version} OCTOPUS_RELEASE={release-id} \
|
||||
GH_REPO=Octopus/octopus GITEA_TOKEN=<pat> \
|
||||
bun <harness-package>/script/build.ts
|
||||
```
|
||||
`build.ts` attaches the 12 release assets to the draft release.
|
||||
|
||||
3. **Publish npm / docker / AUR + git sync**:
|
||||
```bash
|
||||
OCTOPUS_VERSION={version} OCTOPUS_RELEASE={release-id} \
|
||||
GITEA_TOKEN=<pat> GH_REPO=Octopus/octopus \
|
||||
NODE_AUTH_TOKEN=<npm-token> \
|
||||
bun script/publish.ts
|
||||
```
|
||||
Set `OCTOPUS_GITEA_ONLY=true` to skip npm/docker/AUR/homebrew.
|
||||
|
||||
4. **Undraft the release** (makes it public):
|
||||
```bash
|
||||
curl -fsS -X PATCH \
|
||||
"<instance-base-url>/api/v1/repos/Octopus/octopus/releases/{release-id}" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" -d '{"draft": false}'
|
||||
```
|
||||
|
||||
5. **Verify assets** 鈥?the release must carry 12 assets:
|
||||
```bash
|
||||
curl -sS "<instance-base-url>/api/v1/repos/Octopus/octopus/releases/tags/v{version}" \
|
||||
-H "Authorization: token $GITEA_TOKEN" | jq '.assets | length'
|
||||
```
|
||||
|
||||
6. **Sync the public mirror repo** (`Octopus/octopus-release`):
|
||||
```bash
|
||||
CI_PAT=<pat> VERSION={version} bash script/sync-public-install.sh
|
||||
CI_PAT=<pat> VERSION={version} bash script/sync-public-assets.sh
|
||||
CI_PAT=<pat> VERSION={version} bash script/verify-public-sync.sh
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
- **Windows code-signing** is not provisioned (no Windows runner / Azure
|
||||
Trusted Signing); the CLI ships unsigned, as before ([org-internal #252]).
|
||||
- **Rollback**: if a step fails mid-release, the draft release + tag can be
|
||||
deleted and re-run; every script above is idempotent or safely re-runnable.
|
||||
- If a future CI pipeline replaces this manual flow, update this section and
|
||||
reference [org-internal #2003] for the retirement rationale.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 鈥?Rollback Plan
|
||||
|
||||
Document how to undo this release:
|
||||
|
||||
1. **Git rollback**:
|
||||
- `git tag -d v{version}` (if not yet pushed).
|
||||
- `git revert {commit_hash}` (if already merged).
|
||||
- If the release includes DB migrations, confirm the `down` migration exists.
|
||||
2. **Data rollback** (if applicable):
|
||||
- For each migration, confirm the `down` migration exists and has been tested.
|
||||
- If the release changes data format without a reversible migration, mark
|
||||
`鈿狅笍 IRREVERSIBLE DATA CHANGE`.
|
||||
|
||||
```markdown
|
||||
## Rollback Plan
|
||||
|
||||
### Git Rollback
|
||||
|
||||
git tag -d v{version}
|
||||
# or: git revert {hash}
|
||||
|
||||
### Data Rollback
|
||||
- Migration `{name}`: down {exists / NOT FOUND}
|
||||
- {additional risks}
|
||||
|
||||
### Rollback Triggers
|
||||
| Condition | Threshold | Duration |
|
||||
| ------------------ | ------------ | -------- |
|
||||
| P99 latency spike | 2x baseline | 5 min |
|
||||
| Error rate spike | 1% | 1 min |
|
||||
| Critical bug | Data loss / security breach | immediate |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 6 鈥?Post-release Smoke Test
|
||||
|
||||
1. Run the project's build command on the tagged commit. Must pass.
|
||||
2. Run the test suite. Must pass.
|
||||
3. Return to the branch: `git checkout <original-branch>`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 7 鈥?Report
|
||||
|
||||
```markdown
|
||||
## Release Report
|
||||
|
||||
**Version**: {old} 鈫?{new} (MAJOR / MINOR / PATCH)
|
||||
**Tag**: v{version}
|
||||
**Commits**: {N} since last tag ({feat} features, {fix} fixes)
|
||||
**Typecheck**: 鉁?| Lint: 鉁?| Tests: 鉁?
|
||||
|
||||
### Changelog
|
||||
|
||||
{paste changelog section}
|
||||
|
||||
### Release Artifacts
|
||||
|
||||
- Commit: {hash} 鈥?`release: {version}`
|
||||
- Tag: v{version}
|
||||
- Changelog: CHANGELOG.md updated
|
||||
|
||||
---
|
||||
|
||||
**Deploy**: {manual step 鈥?"merge to main triggers CI", etc.}
|
||||
**Rollback**: see Phase 5 above
|
||||
**Next**: `core/skills/retrospective/SKILL.md` (optional 鈥?run on any project state)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- `core/checklists/release.md` 鈥?Release checklist
|
||||
- Semver spec: https://semver.org
|
||||
- Conventional Commits: https://www.conventionalcommits.org
|
||||
@@ -0,0 +1,762 @@
|
||||
---
|
||||
name: retrospective
|
||||
description: >
|
||||
Use ONLY when running a retrospective at the end of any work cycle
|
||||
(release, feature, bugfix, port). The Retrospective Lead (Worker) inspects project
|
||||
facts — git log, file churn, test history, commit patterns — and generates
|
||||
actionable improvement items. No pipeline artifacts required.
|
||||
triggers:
|
||||
- retrospective
|
||||
- 复盘
|
||||
- lessons learned
|
||||
- post-mortem
|
||||
- postmortem
|
||||
role: Producer
|
||||
---
|
||||
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatimDir)。机制、结构与 frontmatter 保持;实例术语(工具名、路径、工单号)按 `core/adapters/TERMINOLOGY.md` 绑定到具体实例。
|
||||
|
||||
# Retrospective
|
||||
|
||||
Inspect the project's recent work cycle to extract patterns and generate
|
||||
concrete action items. Based purely on project facts: git history, code churn,
|
||||
commit quality, test results, and build health.
|
||||
|
||||
**Purpose**: Continuous improvement of BOTH the project AND the SDLC pipeline
|
||||
itself. Every retrospective may modify templates, checklists, or SKILLs.
|
||||
|
||||
## Agent Role
|
||||
|
||||
The retrospective is owned and executed by the **Retrospective Lead** (Worker).
|
||||
|
||||
**Context compaction**: retrospective is a pipeline stage boundary. The main
|
||||
session compacts at this clean boundary ONLY when a capacity/projection trigger
|
||||
holds, per `core/rules/compact.md` §"Stage-boundary compaction" (long multi-stage runs
|
||||
— DAG Epic orchestration — keep the legacy every-boundary compaction; short
|
||||
runs — bugfix / DAG task — and standalone runs default to NOT compacting). The Retrospective Lead itself is single-phase and
|
||||
data-driven: all findings are written to the retrospective report as they are
|
||||
produced, so a mid-run compaction loses nothing — re-read the report artifact
|
||||
to resume.
|
||||
|
||||
---
|
||||
|
||||
## Preconditions
|
||||
|
||||
Before starting the retrospective:
|
||||
|
||||
- [ ] A work cycle has been completed (release was cut, feature merged,
|
||||
bug fixed, or port landed).
|
||||
- [ ] The project is a git repository with recent commits.
|
||||
- [ ] `core/checklists/retrospective.md` is accessible.
|
||||
|
||||
No `.artifacts/{slug}/` files are required. (**Deprecated**: `.artifacts/` file-system access is being migrated to Gitea wiki. SDLC artifacts now live as wiki pages under `{slug}/`; use `wiki 读写 API(见 TERMINOLOGY)` and `wiki 读写 API(见 TERMINOLOGY)` to read them.) The retrospective reads the project's own state and git history.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — Scope the Cycle
|
||||
|
||||
Determine the time range to analyze:
|
||||
|
||||
1. If a **release was just done**: use `git log <last-tag>..<new-tag>`.
|
||||
2. If **no tag exists**: prompt the user for a time range or revision range
|
||||
(e.g. `HEAD~20..HEAD`, or `--since="last work cycle"`).
|
||||
3. If the user specifies a range, use that.
|
||||
|
||||
```markdown
|
||||
## Cycle Scope
|
||||
|
||||
**Range**: {commit range or "last work cycle"}
|
||||
**Date**: {start} → {end}
|
||||
**Commits analyzed**: {N}
|
||||
**Authors**: {names}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — Collect Data
|
||||
|
||||
Run project-inspection commands and summarize findings:
|
||||
|
||||
### 2.1 Commit patterns
|
||||
|
||||
```bash
|
||||
git log {range} --oneline --no-merges
|
||||
git log {range} --format='%s' | grep -cE '^(BREAKING|feat|fix|perf|refactor|docs|chore)'
|
||||
```
|
||||
|
||||
- Count commits by type (BREAKING, feat, fix, refactor, etc.).
|
||||
- Flag commits with no conventional prefix — these are opaque and hard to
|
||||
trace → log as `[SIGNAL: low commit hygiene]`.
|
||||
- Count revert commits (`git log {range} --grep="Revert" --oneline`).
|
||||
- High revert count → likely insufficient testing or review before merge.
|
||||
|
||||
### 2.2 File churn
|
||||
|
||||
```bash
|
||||
git diff --stat {range}
|
||||
git diff --numstat {range} | sort -k1 -rn | head -20
|
||||
```
|
||||
|
||||
- Identify the most-churned files (top 10 by lines added + deleted).
|
||||
- High churn in a single file (> 200 lines in one cycle) → possible
|
||||
monolithic module, design issue, or scope creep.
|
||||
- List new files vs. deleted files.
|
||||
|
||||
### 2.3 Test health
|
||||
|
||||
1. Run the test suite: confirm pass/fail count and duration.
|
||||
2. Check if any test files changed during the cycle:
|
||||
```bash
|
||||
git diff --name-only {range} | grep -E 'test|spec|__tests__'
|
||||
```
|
||||
3. If test files were NOT changed but source files were → `[SIGNAL: untested changes]`.
|
||||
**Architecture-A exemption**: If the cycle touches ONLY `<instance-root>/` and
|
||||
`.gitea/` files (config, skills, templates, rules, checklists) with zero
|
||||
`packages/*` source changes, the "untested changes" signal does NOT apply
|
||||
— `<instance-root>/` files are validated by the audit-process review gate, not
|
||||
by unit tests. Record this as `[NOTE: Architecture A — config-only cycle,
|
||||
unit-test exemption applies]` in the test summary.
|
||||
4. Check for skipped/flaky tests if the framework reports them.
|
||||
|
||||
### 2.4 Build health
|
||||
|
||||
1. Run `bun typecheck` (or project equivalent). Note first-time errors.
|
||||
2. Run `bun oxlint --deny-warnings` (repo root; `bun lint` is the package-script alias). Note any first-time warnings.
|
||||
3. If the project has CI, check the latest run status.
|
||||
|
||||
### 2.5 Dependency health
|
||||
|
||||
1. Run `bun audit` (or equivalent). Flag any new HIGH/CRITICAL CVEs.
|
||||
2. Check if any dependency was added/removed/upgraded:
|
||||
```bash
|
||||
git diff {range} -- package.json bun.lockb
|
||||
```
|
||||
|
||||
### 2.6 Process quality
|
||||
|
||||
Inspect the SDLC infrastructure around the project — not just the code, but
|
||||
the factory that produces it. These checks are all file-existence and
|
||||
configuration reads; they require zero prior pipeline artifacts.
|
||||
|
||||
1. **Pre-commit guards**:
|
||||
- Does the project have pre-commit hooks? (Check `.husky/`, `lefthook.yml`,
|
||||
`.pre-commit-config.yaml`, `package.json` `"lint-staged"` key.)
|
||||
- If absent → `[GAP: no pre-commit guard — bad code can land]`.
|
||||
- If present → what commands do they run? (lint? typecheck? test?)
|
||||
|
||||
2. **CI/CD pipeline**:
|
||||
- Does CI exist? (Check `.gitea/workflows/` — this repo's CI location —
|
||||
then `.github/workflows/`, `.gitlab-ci.yml`, `Jenkinsfile`, etc.)
|
||||
- If absent → `[GAP: no CI — no automated gate before merge]`.
|
||||
- If present → does it block merge on failure? Check branch protection
|
||||
(GitHub: `gh api repos/{owner}/{repo}/branches/main/protection`).
|
||||
|
||||
3. **Code review practice**:
|
||||
- Does the project have a review template or guideline? (Check
|
||||
`.github/PULL_REQUEST_TEMPLATE.md`, `CONTRIBUTING.md`, etc.)
|
||||
- Are PRs used? Look at merge commits: do they reference PR numbers?
|
||||
- If direct pushes to main → `[SIGNAL: no review gate]`.
|
||||
|
||||
4. **SDLC artifacts**:
|
||||
- Use `wiki 读写 API(见 TERMINOLOGY)(owner="Octopus", repo="octopus")` to check
|
||||
for `{slug}/` wiki pages (`.artifacts/` file-system access is retired —
|
||||
SDLC artifacts now live as wiki pages under `{slug}/`).
|
||||
- If no `{slug}/` pages exist → `[NOTE: no structured SDLC artifacts — decisions not traceable]`.
|
||||
- If present but stale → `[SIGNAL: artifacts not maintained — process drift]`.
|
||||
|
||||
5. **Tooling consistency**:
|
||||
- Does the project have a unified formatter config? (`.prettierrc`,
|
||||
`biome.json`, etc.)
|
||||
- Does it have a unified typecheck/lint/test command? (Check `package.json`
|
||||
scripts.)
|
||||
- Are there multiple competing tools for the same concern? (e.g. both
|
||||
prettier AND biome, both jest AND vitest.)
|
||||
|
||||
6. **Documentation health**:
|
||||
- Does `README.md` include setup, build, and test instructions?
|
||||
- Does `AGENTS.md` or `<instance-root>/AGENTS.md` exist?
|
||||
- Are there any outdated docs? (Check for files referencing removed
|
||||
commands or directories.)
|
||||
|
||||
### 2.7 Token telemetry
|
||||
|
||||
Collect LLM token-consumption signals to assess workflow quality. Five
|
||||
metrics: M1 from an inline check; M2–M5 from the token-telemetry probe.
|
||||
|
||||
1. **M1 — Review convergence.** The number of rounds each review dimension
|
||||
needed to converge is the strongest signal of upstream-stage quality.
|
||||
|
||||
Discover review rounds via `wiki 读写 API(见 TERMINOLOGY)(owner="Octopus", repo="octopus")`
|
||||
with prefix `{slug}/reviews/` (`.artifacts/` file-system listing is retired).
|
||||
For each review, `max(roundN)` is its convergence round count.
|
||||
- 🟢 1–2 / 🟡 3–4 / 🔴 ≥5.
|
||||
- ≥5 rounds → `[SIGNAL: review convergence ≥5 — upstream stage quality
|
||||
insufficient, rework deferred to review]`.
|
||||
|
||||
The remaining four metrics come from a single probe run:
|
||||
|
||||
```bash
|
||||
bun run core/skills/retrospective/scripts/token-telemetry.ts
|
||||
```
|
||||
|
||||
It scans `~/.local/share/octopus/octopus-*.db` (token usage) and review
|
||||
rounds from two sources — the Tier 1 review-status file
|
||||
`<runs-root>/{slug}/reviews/{stage}/status.json` (canonical — reads the
|
||||
`history[]` field (legacy alias `rounds[]`) and `current_round`) with the
|
||||
legacy `.artifacts/**/reviews/*/status.json` tree fallback, plus the
|
||||
committed archive bundles `<runs-root>/archive/{slug}.json` for closed
|
||||
runs — and prints M2–M5.
|
||||
|
||||
2. **M2 — Stage distribution.** Token spend per pipeline stage (design,
|
||||
review, implement, …). The probe reconstructs a per-session stage
|
||||
timeline from `~/.local/share/octopus/token-stage-ledger.jsonl` —
|
||||
written by the auto-discovered `<instance-root>/plugin/token-stage-ledger.ts`
|
||||
plugin, which hooks `tool.execute.after` on the `skill` tool — and
|
||||
attributes each message's tokens to the stage active at its creation.
|
||||
- **Ledger-gated.** If the plugin was not active during the cycle the
|
||||
ledger is absent and the probe prints
|
||||
`[NOTE: token-stage-ledger.jsonl absent — M2 skipped]`. That is
|
||||
"unavailable", not "failed" — proceed.
|
||||
- Review-stage share: 🟢 <35% / 🟡 35–60% / 🔴 >60%.
|
||||
- > 60% → `[SIGNAL: review stage >60% of token spend — over-reviewing,
|
||||
review findings not actionable upstream]`.
|
||||
|
||||
3. **M3 — Review rework.** Rework fraction — review rounds beyond the first
|
||||
as a share of total review rounds.
|
||||
The probe merges two sources, deduped by `{slug}/reviews/{stage}` (the
|
||||
ACTIVE status.json wins): (a) the Tier 1 review-status file
|
||||
`<runs-root>/{slug}/reviews/{stage}/status.json` (canonical — `history[]`
|
||||
field, legacy alias `rounds[]`, and `current_round`; skips `_archive/`)
|
||||
with the legacy `.artifacts/**/reviews/*/status.json` tree fallback —
|
||||
in-flight runs only, since the active workspace is deleted at
|
||||
archive-at-close; (b) the committed archive bundles
|
||||
`<runs-root>/archive/{slug}.json`, where per-review round counts are
|
||||
reconstructed from the documented `reviews/{stage}/round{N}/` layout in
|
||||
`index.artifacts[].path` ([org-internal #2591] — the archive source is what makes M3
|
||||
durable instead of structurally emptying as runs close). Available once
|
||||
any run has closed.
|
||||
- Rework fraction: 🟢 <15% / 🟡 15–30% / 🔴 >30%.
|
||||
- > 30% → `[SIGNAL: rework fraction >30% — review findings not actionable
|
||||
or upstream design unclear]`.
|
||||
|
||||
4. **M4 — Context hygiene.** Per-session input-token growth and cache
|
||||
efficiency, read from the octopus session database. The cache
|
||||
read/input ratio measures context reuse.
|
||||
- 🟢 cache_read/input > 10:1 / 🟡 3–10:1 / 🔴 < 3:1.
|
||||
- < 3:1 → `[SIGNAL: cache hit <3:1 — context re-read, code-graph-first
|
||||
not followed]`.
|
||||
- **Compact frequency ([org-internal #2601] pilot data)**: report the token-telemetry
|
||||
"Compactor Activity" line (compactor messages/tokens in window) and the
|
||||
zero-compact share of short runs (bugfix / DAG task) — the capacity-driven compaction
|
||||
pilot metrics. A rising zero-compact share with NO late-stage
|
||||
degradation signal is the evidence that retires the pilot gate in
|
||||
`rules/compact.md` § Stage-boundary compaction.
|
||||
|
||||
5. **M5 — Explore/execute ratio.** Token spend by agent type
|
||||
(`data.agent` per message). A low ratio means workers are doing
|
||||
explorers' job — context-gathering that should be delegated.
|
||||
- 🟢 > 2:1 / 🟡 1–2:1 / 🔴 < 1:1.
|
||||
- < 1:1 → `[SIGNAL: explore/execute <1:1 — workers doing explorers' job,
|
||||
exploration skipped or under-delegated]`.
|
||||
|
||||
### 2.8 Gate defect-escape analysis (single metric, [org-internal #3061])
|
||||
|
||||
Gates earn their cost by what they CATCH, and the honest test is what slips
|
||||
past them: a gate that runs clean while the same defect resurfaces downstream
|
||||
is under-powered — the opposite of redundant. This probe computes ONE metric
|
||||
per `gate_id` (the legacy per-(Kind × Size × gate) keying is retired with the
|
||||
sizing subsystem — sub-5 cell counts produced noise, not evidence):
|
||||
|
||||
**`escape_rate` = clean runs with a downstream escape / clean runs**
|
||||
|
||||
- **Clean run**: the gate ran on a cycle ticket and passed round 1 with zero
|
||||
INFO-or-worse findings.
|
||||
- **Downstream escape**: within 14 days after the clean gate, either
|
||||
(a) a verify FAIL/WARN finding in the same area (module/dimension) on the
|
||||
same ticket, or (b) a post-merge `Kind/Bug` ticket whose body references
|
||||
the area the gate covered.
|
||||
|
||||
**Data sources** (all read-only):
|
||||
|
||||
- **Review status**: commit-status context `pipeline/{review_type}`
|
||||
(`_shared/gitea-write-patterns.md` Pattern 8), read via `octopus review
|
||||
status` CLI or commit-status inspection. Final reports at
|
||||
`{slug}/reviews/{stage}/final/report` record round count and highest
|
||||
severity.
|
||||
- **Downstream (a)**: verify reports `{slug}/05-verify-iteration-{N}` for the
|
||||
same slug.
|
||||
- **Downstream (b)**: `工单 API(见 TERMINOLOGY)list(labels="Kind/Bug", state=closed)`
|
||||
within the window; match by module/path references in the body.
|
||||
- **slug ↔ issue map**: the issue's `## 工件索引` comment (legacy:
|
||||
`## Pipeline 工件追踪表`) or `Closes #N` in the PR body.
|
||||
|
||||
**Procedure**:
|
||||
|
||||
1. Enumerate closed tickets in the cycle with any review run; resolve slugs.
|
||||
2. Per gate run: classify clean / not-clean; for clean runs, search both
|
||||
downstream sources for an escape.
|
||||
3. Tally per `gate_id`: `clean_runs`, `escapes`, `escape_rate`.
|
||||
4. Reading (thresholds are this skill's own policy — the `gate_trim:` block
|
||||
they once deferred to was retired 2026-08-21, [org-internal #3072] phase 3; nothing
|
||||
trims gates anymore, so the trim-candidate branch below is gone):
|
||||
- `escape_rate ≥ 0.3` → the gate is
|
||||
**UNDER-POWERED**: route the escape causes into §2.9 pre-flight
|
||||
(producer-side self-checks) and note the gate in the report. Do NOT
|
||||
propose trimming it.
|
||||
- `escape_rate < 0.3` → healthy; report the numbers, no further verdict.
|
||||
|
||||
**DAG single-gate exclusion ([org-internal #2267])**: `review-dag` is structurally
|
||||
non-trimmable (`dag.route.single_gate.never_trim: true` — self-contained in
|
||||
the `dag:` block); `verify` and `merge-pr` were `never_trim` in the retired
|
||||
`gate_trim` block and stay untouchable by convention.
|
||||
|
||||
**keep_gates exclusion (TD-390, [org-internal #3061]) — RETIRED with gate_trim**: the
|
||||
rule that a trim candidate's gate must not be a member of the target route's
|
||||
`keep_gates` guarded a landing field (`sizing.tiers.*.additional_skip`)
|
||||
that no longer exists. Historical record: wiki `rules/gate-trim`.
|
||||
|
||||
**Sample-sufficiency gate** (structural, not optional): fewer than 5 clean
|
||||
runs for a gate → report the raw numbers with
|
||||
`[NOTE: insufficient sample for gate {id}]` and emit no verdict for it.
|
||||
|
||||
```markdown
|
||||
### Gate defect-escape (M6)
|
||||
|
||||
| gate | clean runs | escapes | escape_rate | verdict |
|
||||
| ----------------------------------- | ---------- | ------- | ----------- | ------------------------- |
|
||||
| review-code | 12 | 1 | 0.08 | healthy |
|
||||
| review-design-space (sticky legacy) | 6 | 3 | 0.50 | UNDER-POWERED → feed §2.9 |
|
||||
| ... | ... | ... | ... | ... |
|
||||
|
||||
- [NOTE: insufficient sample for gate X] where clean runs < 5.
|
||||
- UNDER-POWERED gates (escape_rate ≥ 0.3) feed §2.9 pre-flight — never a
|
||||
unilateral trim: the only exit is a furlough entry in
|
||||
`<instance-root>/gate-ledger.yaml` (evidence + reopen condition, [org-internal #3607]).
|
||||
```
|
||||
|
||||
### 2.9 Pre-flight defect-prevention analysis ([org-internal #2599])
|
||||
|
||||
First-round FAIL/WARN findings are the pipeline's cost multiplier: every
|
||||
multi-round review pays for them twice (review round + revision round).
|
||||
This probe tallies their root causes and proposes producer-side self-checks —
|
||||
the defensive complement of the retired gate-trim meta-process: instead of
|
||||
removing a gate that never catches anything, inject the causes that keep
|
||||
costing rounds as a pre-flight checklist the Producer verifies BEFORE writing
|
||||
code.
|
||||
|
||||
**Data sources** (read-only, same as 2.8): review status pages / final
|
||||
reports for the cycle's tickets (`{slug}/reviews/{stage}/final/report` —
|
||||
round-1 findings with severity FAIL/WARN, their dimension codes, and the
|
||||
finding text for root-cause categorization) + the source issues'
|
||||
`Kind/*` labels. For DAG-routed Epics ([org-internal #2905] 方案 3), two further
|
||||
read-only sources feed the `REQ × late-discovery` root cause (step 1):
|
||||
(a) **DAG oversize-signal events** — `node_split` / registry-row additions
|
||||
recorded on the Epic's issue timeline / `## DAG 状态` whose trigger is a NEW
|
||||
requirement rather than a refactor; (b) **demo-period feedback** —
|
||||
stakeholder comments naming functionality the frozen DAG never covered. For browser-evidence
|
||||
cycles ([org-internal #4499], contract `browser-evidence-4486/shared/evidence-ref-v1` §3), a
|
||||
third read-only
|
||||
source: (c) **browser session rows** — per browser-debug session
|
||||
`{session_id, outcome: evidence-captured | env-unavailable | replay-failed,
|
||||
replay summary, env.mode}`, derived from Tier-1 pack manifests (sanitized
|
||||
transitively by the N-03 write boundary — this probe never touches raw
|
||||
captures). Replay failure attribution rides the FIXED
|
||||
`ReplayFailureCategory` enum (`browser-evidence-4486/shared/pack-manifest-v1`
|
||||
§4); `env-unavailable` sessions default to the enum's own environment slot
|
||||
(`env-binary-missing`) unless the row carries an explicit attribution;
|
||||
browser-class
|
||||
causes enter the step-2 route-class tally as `BROWSER × {category}` and a
|
||||
qualifying cause becomes a PRE-FLIGHT PROPOSAL candidate landing on
|
||||
`routes.{Kind}.preflight` (human-landed in Phase 6, never auto-applied).
|
||||
Rows are currently Task-DAG cycle products by default (the helper's
|
||||
`--route-class` override / row-level `route_class` field re-keys a future
|
||||
Bug- or Feature-cycle session to its own landing slot). The replay
|
||||
success-rate baseline (N-03 BENCH) is exposed as a consumable metric — a SEEDED CONVENTION (fixture-authored outcomes, channel health),
|
||||
NOT a live-browser trend baseline. Mechanical helper:
|
||||
`<harness-package>/scripts/browser-retro-tally.ts`. Boundary rules ([org-internal #4499]
|
||||
AC-3): out-of-enum or empty categories fall into an explicit `other` bucket
|
||||
with provenance (recorded, never silently swallowed, never a crash); rows
|
||||
missing replay/attribution fields yield structured errors in the tally
|
||||
output; a cycle with no browser session rows keeps this probe's current
|
||||
behavior (empty tally, no synthetic proposal rows).
|
||||
|
||||
**Procedure**:
|
||||
|
||||
1. For each ticket with a multi-round review in the cycle, read the round-1
|
||||
findings with severity FAIL or WARN. Categorize each by
|
||||
(dimension × root-cause) — the dimension code is checklist-qualified
|
||||
(checklist-local namespace; e.g. `code-review.md TST × missing-boundary-test`,
|
||||
`code-review.md COR × unhandled-null`,
|
||||
`code-review.md STY × wrong-import-order`).
|
||||
Canonical root-cause category for DAG-routed Epics: `REQ ×
|
||||
late-discovery` — 冻结后才被发现的需求 (a requirement that surfaced only
|
||||
after the DAG froze). A late discovery usually manifests as a round-1
|
||||
REQMAP FAIL on the re-run review, but the underlying event is an
|
||||
oversize signal or demo comment — count it from sources (a)/(b) above,
|
||||
not only from review findings.
|
||||
2. Tally per **route-class** — `Bug`, `Feature-DAG`, `Epic-DAG-route`,
|
||||
`Task-DAG` (Kind/Feature routes to the DAG pipeline since [org-internal #3061] Phase 2 /
|
||||
TD-388 — `Feature-legacy` survives only as a historical bucket for tickets
|
||||
closed before 2026-08-20; the Size dimension is retired from retro keying,
|
||||
[org-internal #3061]: DAG-routed tickets derive depth instead of carrying `Size/*`
|
||||
(spec-06), and `Kind/Bug` carries no ladder). First-round hit count
|
||||
and distinct-ticket count per cause per class.
|
||||
3. A cause is a **pre-flight candidate** when ALL hold (thresholds from
|
||||
`workflow-routing.yaml` `preflight` — do NOT restate values here):
|
||||
- hit count ≥ `preflight.min_sample`
|
||||
- distinct tickets ≥ `preflight.consecutive_recur`
|
||||
A qualifying `Epic × DAG-route` cause (e.g. `REQ × late-discovery`) lands
|
||||
on `<instance-root>/workflow-routing.yaml` `dag.route.preflight` — NOT
|
||||
`routes.Kind/Epic.preflight` (legacy-roadmap-era landing; the legacy route
|
||||
was archived [org-internal #3072] phase 3) ([org-internal #2905] 方案 3). The analyze-dag skill reads
|
||||
`dag.route.preflight` before decomposition.
|
||||
4. Aging: for causes ALREADY landed in `routes.{Kind}.preflight` or
|
||||
`dag.route.preflight`, count
|
||||
consecutive clean first rounds since landing (from this and prior retro
|
||||
data); at ≥ `preflight.aging_consecutive_clean`, emit a REMOVAL row.
|
||||
|
||||
Sample-sufficiency guard mirrors 2.8: zero qualifying tickets → emit
|
||||
`[NOTE: insufficient sample for pre-flight proposals]` and skip this probe.
|
||||
|
||||
```markdown
|
||||
### Pre-flight defect prevention (M7)
|
||||
|
||||
| route-class | Cause (checklist-qualified dim × root-cause) | 1st-round hits | tickets | qualify? |
|
||||
| -------------- | ------------------------------------------- | -------------- | --------------------------- | -------------------------------- |
|
||||
| Bug | code-review.md COR × unhandled-null | 5 | 4 ([org-internal #2400] [org-internal #2429] [org-internal #2471] [org-internal #2488]) | ✅ PROPOSE |
|
||||
| Bug | code-review.md STY × wrong-import-order | 6 | 2 | ⛔ tickets <3 |
|
||||
| Feature-DAG | code-review.md TST × missing-boundary-test | 3 | 3 | ⛔ hits <5 |
|
||||
| Epic-DAG-route | REQ × late-discovery | 5 | 3 (#27xx #28xx #29xx) | ✅ PROPOSE → dag.route.preflight |
|
||||
|
||||
- [NOTE: no pre-flight proposals] if the table is empty or nothing clears threshold.
|
||||
- ✅ PROPOSE rows feed Phase 5 PRE-FLIGHT PROPOSAL action items.
|
||||
- Landed-item aging: `cor-unhandled-null clean streak 5 ≥ aging 5` → REMOVAL row.
|
||||
```
|
||||
|
||||
### 2.10 Derived-ticket health ([org-internal #3061])
|
||||
|
||||
Derived tickets (TD promotions, BF umbrellas, FT tickets) are the pipeline's
|
||||
exhaust. Unmanaged they accumulate into flush cycles (2026-08 evidence: 122
|
||||
open tech-debt tickets, ~9.4/day creation, zero pre-August closures, a
|
||||
115-ticket bulk flush). Under the registry-first regime (verify Phase 5.5)
|
||||
this probe checks whether the system DIGESTS what it records:
|
||||
|
||||
**Metrics** (cycle window):
|
||||
|
||||
- **TD flow**: registry rows created / rows promoted to tickets / promoted
|
||||
tickets closed-as-fixed vs closed-as-wontfix.
|
||||
- **Median open age**: open TD rows + promoted tickets, by module/origin.
|
||||
- **BF triage compliance**: % of verify-Phase-5.55 BF umbrellas triaged
|
||||
(assigned or scheduled) within 1 day of filing.
|
||||
- **FT expiry compliance**: % of verify-Phase-5.56 FT tickets fixed-or-
|
||||
isolated within their N-day window.
|
||||
- **Per-module open count** vs the promotion quota (verify Phase 5.5).
|
||||
|
||||
**Actions**:
|
||||
|
||||
- A category (module/origin) with creation ≥ fix across ≥2 consecutive
|
||||
retros → mark it **register-only**: verify Phase 5.5 stops promoting rows
|
||||
to tickets there until one retro shows net-negative backlog.
|
||||
- Open TD rows unclaimed for >3 retro cycles → mark the row `[COLD]`
|
||||
(revivable — a pull event clears the mark). Cold rows are excluded from
|
||||
adjacency-quota pressure and do not count against the module quota.
|
||||
- BF/FT compliance < 100% → name the untriaged/expired items in the report
|
||||
(they are SLA breaches, not statistics).
|
||||
|
||||
```markdown
|
||||
### Derived-ticket health (M8)
|
||||
|
||||
| metric | this cycle | last cycle | trend |
|
||||
| ----------------------------------------- | ---------- | ---------- | ----- |
|
||||
| TD rows created / promoted / closed-fixed | | | |
|
||||
| median open-TD age (days) | | | |
|
||||
| BF same-day triage % | | | |
|
||||
| FT fix-or-isolate % | | | |
|
||||
|
||||
- register-only categories: {list or "none"}
|
||||
- rows marked [COLD] this cycle: {list or "none"}
|
||||
```
|
||||
|
||||
### 2.11 Threshold calibration tally ([org-internal #3380])
|
||||
|
||||
Process prose constants (round caps, D1–D4 thresholds, preflight knobs,
|
||||
escape-rate cutoffs, quotas — full index:
|
||||
`docs/workflow-refactor/thresholds-ledger.md`) are only as good as the data
|
||||
behind them. This probe keeps them honest, mirroring §2.9's propose-and-human-
|
||||
lands pattern:
|
||||
|
||||
1. **Round-cap distribution** (per stage): read `{slug}/reviews/*/final/report`
|
||||
headers (`**Rounds completed**: N`) for the cycle's slugs; tally per stage
|
||||
(code / dag), report n / p50 / p90 / p95 / max, and how often the cap was
|
||||
the binding stop (`Rounds completed` == cap with `Converged: false`).
|
||||
2. **CALIBRATION PROPOSAL rows**: for any ledger constant whose data source
|
||||
this retro tallied, emit a proposal row — `constant | current | observed |
|
||||
proposed | evidence` — when the data contradicts the current value (cap
|
||||
never binding AND p95 ≪ cap → propose lowering; cap binding with
|
||||
converged-improving runs → propose raising). Proposals are **never
|
||||
auto-landed**: a human lands them by editing the definition site AND the
|
||||
ledger row in one PR citing this retro (preflight evidence-field pattern).
|
||||
3. **Ledger sync**: if any constant's definition site changed since the last
|
||||
retro (value or location), update the ledger row — drift between the two
|
||||
is a TD-480-class double-source failure.
|
||||
|
||||
```markdown
|
||||
### Threshold calibration (M9)
|
||||
|
||||
| stage | n | p50 | p90 | p95 | max | cap-binding runs |
|
||||
| --------------------------------- | ------- | -------- | -------- | ------------------- | --- | ---------------- |
|
||||
| code | | | | | | |
|
||||
| dag | | | | | | |
|
||||
| - CALIBRATION PROPOSAL: {constant | current | observed | proposed | evidence} or "none" |
|
||||
|
||||
- ledger sync: {rows updated or "none"}
|
||||
```
|
||||
|
||||
```markdown
|
||||
## Data Summary
|
||||
|
||||
### Commits
|
||||
|
||||
| Type | Count |
|
||||
| ------------- | ------ |
|
||||
| BREAKING | {N} |
|
||||
| feat | {N} |
|
||||
| fix | {N} |
|
||||
| refactor | {N} |
|
||||
| docs/chore | {N} |
|
||||
| **no prefix** | {N} ⚠️ |
|
||||
| **reverts** | {N} |
|
||||
|
||||
### Churn Top 10
|
||||
|
||||
| File | +lines | -lines | Concern |
|
||||
| --------------------- | ------ | ------ | ----------- |
|
||||
| src/{module}/large.ts | 200 | 150 | Monolithic? |
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
### Tests
|
||||
|
||||
- Suite: {N} passed, {M} failed, {S} skipped — {duration}s
|
||||
- Test files changed: {N} / {M}
|
||||
- Untested source changes: {list or "none"}
|
||||
|
||||
### Build
|
||||
|
||||
- Typecheck: ✅ / ❌ (N errors)
|
||||
- Lint: ✅ / ⚠️ (N warnings)
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Added: {list}
|
||||
- Removed: {list}
|
||||
- Upgraded: {list}
|
||||
- Audit: ✅ / ⚠️ N CVEs
|
||||
|
||||
### Process
|
||||
|
||||
| Check | Status | Detail |
|
||||
| ------------------- | ------------ | ---------------------------------- |
|
||||
| Pre-commit hooks | ✅ / ❌ | {what runs / "none"} |
|
||||
| CI/CD | ✅ / ❌ | {provider / "none"} |
|
||||
| PR / review gate | ✅ / ❌ | {PR # pattern / direct push} |
|
||||
| SDLC artifacts | ✅ / ⚠️ / ❌ | {present & fresh / stale / absent} |
|
||||
| Formatter config | ✅ / ❌ | {tool / "none"} |
|
||||
| Typecheck+Lint+Test | ✅ / ⚠️ | {unified scripts?} |
|
||||
| README / AGENTS.md | ✅ / ⚠️ | {present / stale / absent} |
|
||||
|
||||
### Token
|
||||
|
||||
- M1 review convergence: max {N} rounds ({review name}) — 🟢/🟡/🔴
|
||||
- M2 stage distribution: design {X}% / review {X}% / implement {X}% (review share {X}% — 🟢/🟡/🔴)
|
||||
- M3 rework: {X}% rework fraction ({rework}/{total} rounds across {N} reviews) — 🟢/🟡/🔴
|
||||
- M4 context: p50={N} / p90={N} / max={N} input tokens; cache {ratio}:1 — 🟢/🟡/🔴
|
||||
- M5 explore/execute: {ratio}:1 (explorer {N} / worker {M} tokens) — 🟢/🟡/🔴
|
||||
- M6 gate defect-escape: {N} gates scanned, {M} UNDER-POWERED (escape_rate at/above threshold, top: {gate}) — 🟢 all healthy / 🟡 {M} under-powered → feed §2.9 / 🔴 broad under-powering across gates
|
||||
- [NOTE: M2 skipped if token-stage-ledger.jsonl absent]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — What Went Well?
|
||||
|
||||
Identify positive patterns — things to repeat or amplify:
|
||||
|
||||
1. **High-quality commits**: commits with clear prefixes, atomic scope, good
|
||||
descriptions → name specific examples.
|
||||
2. **Low-churn modules**: files that were changed but had low +/− counts
|
||||
(well-factored, easy to modify).
|
||||
3. **Tests that caught bugs**: if any test was added before the fix commit,
|
||||
that's TDD → highlight it.
|
||||
4. **Fast turnaround**: if any commit → production cycle was unusually fast.
|
||||
5. **Process safeguards active**: pre-commit hooks catching errors before push,
|
||||
CI blocking broken builds, PR review catching design issues early.
|
||||
6. **Fresh documentation**: README and AGENTS.md are up to date and referenceable.
|
||||
|
||||
```markdown
|
||||
## What Went Well
|
||||
|
||||
1. {finding} — {evidence from data} — {why it worked, do again}
|
||||
2. {finding}
|
||||
3. {finding}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — What Went Wrong?
|
||||
|
||||
Identify problems — focus on patterns in the data, not blame:
|
||||
|
||||
| Data signal | Root cause pattern | Example |
|
||||
| ------------------------------------------- | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
||||
| Revert commits > 0 | Bug slipped through review / testing | "Revert 'fix auth' — original fix broke login" |
|
||||
| High churn in a single file (> 300) | Monolithic module, hard to change safely | `src/handler.ts` +400/-350 in one cycle |
|
||||
| feat commits without test changes | New features landed untested | 3 feat commits, 0 test files changed |
|
||||
| No conventional commit prefix | Low commit discipline, harder to auto-changelog | 8 of 12 commits have no prefix |
|
||||
| Test suite growing slower | Test debt accumulating | Source +200 lines, tests +10 lines |
|
||||
| Dependency added without audit check | Supply chain risk | New dep added, `audit` not run |
|
||||
| Typecheck broke mid-cycle | No pre-commit / pre-push hooks | Type error landed on main, fixed later |
|
||||
| No pre-commit hooks | Every developer must remember to run checks manually | Type errors and lint violations land on main |
|
||||
| No CI/CD | No automated gate before merge | Broken build merged, discovered later |
|
||||
| No PR template / direct pushes | No structured review process | Design flaws not caught until production |
|
||||
| Stale SDLC artifacts | Process was followed once then abandoned | `.artifacts/` exists but empty for last 3 cycles (**deprecated**: check Gitea wiki `{slug}/` pages instead) |
|
||||
| Multiple formatters / test frameworks | Tooling inconsistency slows onboarding | Both prettier and biome configured |
|
||||
| Review rounds ≥5 | Upstream stage quality low — rework deferred to review | design converged only at round 5 |
|
||||
| cache_read/input < 3:1 | Context re-read repeatedly — code-graph-first not followed | 2.8:1 across design stage |
|
||||
| explore/execute < 1:1 | Workers doing explorers' job — exploration skipped or under-delegated | 0.7:1 — worker tokens exceed explorer |
|
||||
| review stage >60% of token spend | Over-reviewing — review findings not actionable upstream | design 20% / review 65% / implement 15% |
|
||||
| rework fraction >30% | Review findings not actionable or upstream design unclear | 62% rework — 30 of 48 rounds beyond first |
|
||||
| retrospective skill modified in cycle range | Retrospective reviewing its own modification — potential self-review bias | `core/skills/retrospective/SKILL.md` changed in `git diff {range}` |
|
||||
|
||||
```markdown
|
||||
## What Went Wrong
|
||||
|
||||
1. **{signal}** — {root cause} — {impact: what broke / slowed down}
|
||||
2. **{signal}** — {root cause} — {impact}
|
||||
3. **{signal}** — {root cause} — {impact}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 — Action Items
|
||||
|
||||
For each "what went wrong", generate a concrete, measurable action item.
|
||||
Each item MUST specify:
|
||||
|
||||
- **What** — the change to make.
|
||||
- **Where** — which template, checklist, SKILL, or project config to modify.
|
||||
- **Who** — which role or agent is responsible.
|
||||
- **When** — effective immediately or next cycle.
|
||||
|
||||
```markdown
|
||||
## Action Items
|
||||
|
||||
| # | What | Where | Who | When |
|
||||
| --- | -------------------------------------------------- | --------------------------------------- | ------------------ | ---------- |
|
||||
| 1 | Add pre-commit hook: typecheck + lint on staged | `.husky/pre-commit` | Developer | immediate |
|
||||
| 2 | Require test file changes for every feat commit | `core/checklists/implementation.md` | Retrospective Lead | next cycle |
|
||||
| 3 | Add commit message template (conventional commits) | `.gitmessage` or `CONTRIBUTING.md` | Retrospective Lead | next cycle |
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
|
||||
- Maximum 5 action items per retrospective. If you have more, group by root
|
||||
cause and take the top 5.
|
||||
- Every action item MUST reference a specific file to modify.
|
||||
- Action items that modify SKILLs or templates are applied immediately (the
|
||||
Retrospective Lead can edit `<instance-root>/` files).
|
||||
- Action items MUST NOT be vague ("try harder", "be more careful").
|
||||
- **Layering question** (injection-budget ruling item 3, [org-internal #3547]): every action
|
||||
item that encodes a lesson as a process constraint MUST answer **"which
|
||||
layer carries this lesson?"** — options in ascending per-turn cost, pick
|
||||
the cheapest that actually enforces it:
|
||||
1. **L0 tool-enforced** — hook / bash guard / CI validation (zero prompt
|
||||
bytes);
|
||||
2. **Skill step** — inline in the phase skill that executes the work
|
||||
(bytes paid only when that skill is loaded);
|
||||
3. **L2 on-demand** — wiki / rule doc fetched when a task needs it;
|
||||
4. **L1 per-turn injection** — an `core/rules/*.md` whitelist entry
|
||||
(bytes paid by EVERY role on EVERY turn; item must cite the current
|
||||
corpus bytes vs `bun run check:rule-budget` cap headroom).
|
||||
Option 4 is **default-deny**: choosing it requires stating why 1–3 cannot
|
||||
carry the lesson (2026-08 batch-1 cut builder -41% / explorer -69% per-turn
|
||||
corpus — do not casually re-grow it; the rule-GC report in that same check
|
||||
flags aging L1 rules for L2 retirement).
|
||||
|
||||
**TRIM PROPOSAL rows — RETIRED ([org-internal #3072] phase 3, 2026-08-21)**: the
|
||||
gate-trim landing machinery (`gate_trim.action` →
|
||||
`sizing.tiers.{Size}.additional_skip` / `routes.{Kind}.skip` /
|
||||
`auto_approve.stages`) was removed with the `gate_trim:` and `sizing:`
|
||||
blocks — nothing trims gates anymore, so Phase 5 emits NO trim proposals.
|
||||
Gate health findings flow exclusively through §2.8's escape-rate verdict
|
||||
(UNDER-POWERED → §2.9 pre-flight) and ordinary action items. Historical
|
||||
spec: wiki `rules/gate-trim` (L2).
|
||||
|
||||
**PRE-FLIGHT PROPOSAL rows** (from Phase 2.9 defect-prevention, [org-internal #2599]): format
|
||||
the What cell as
|
||||
`Add pre-flight '{id}' to {Kind} ({hits} first-round FAIL/WARN hits, {tickets} tickets — {evidence})`
|
||||
and the Where cell as
|
||||
`<instance-root>/workflow-routing.yaml` `routes.{Kind}.preflight` — or, for causes
|
||||
keyed `Epic × DAG-route` (e.g. `REQ × late-discovery`, [org-internal #2905] 方案 3),
|
||||
`<instance-root>/workflow-routing.yaml` `dag.route.preflight` (consumed by
|
||||
analyze-dag before decomposition).
|
||||
Aging removal rows: `Remove pre-flight '{id}' from {Kind} (clean streak {N} ≥ aging threshold)`.
|
||||
|
||||
Same doctrine the retired TRIM proposals used: a PRE-FLIGHT PROPOSAL is a
|
||||
recommendation, not an
|
||||
auto-apply — landing (and removal) happens in Phase 6 only when the action
|
||||
item survives the retrospective's own review. Landed checklists must stay
|
||||
within `preflight.max_items` entries per route (drop-oldest by `added_cycle`).
|
||||
|
||||
---
|
||||
|
||||
## Phase 6 — Apply Improvements
|
||||
|
||||
For each action item that modifies a `<instance-root>/` or project-config file:
|
||||
|
||||
1. Read the current file.
|
||||
2. Apply the change.
|
||||
3. Note the change in the retrospective report.
|
||||
|
||||
```markdown
|
||||
## Applied Improvements
|
||||
|
||||
1. Modified `{.file}`: {what was changed} — {commit hash}
|
||||
2. Modified `{.file}`: {what was changed} — {commit hash}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 7 — Report
|
||||
|
||||
Publish the retrospective report as a Gitea wiki page `_retrospectives/{cycle-name}` via `wiki 读写 API(见 TERMINOLOGY)`. The `_retrospectives/` namespace is an intentional cross-cycle, slug-less exception to the `{slug}/...` artifact-path convention (retrospectives aggregate multiple slugs and outlive any one pipeline run) — analogous to the audit `audit/{date}/` date-slug exception documented in the NAM 4.4 checklist item.
|
||||
|
||||
```markdown
|
||||
# Retrospective: {cycle description}
|
||||
|
||||
**Date**: {YYYY-MM-DD}
|
||||
**Range**: {commit range or time range}
|
||||
**Commits**: {N}
|
||||
**Overall**: 🟢 GREEN / 🟡 YELLOW / 🔴 RED
|
||||
|
||||
## What Went Well
|
||||
|
||||
{list}
|
||||
|
||||
## What Went Wrong
|
||||
|
||||
{list}
|
||||
|
||||
## Action Items
|
||||
|
||||
{table}
|
||||
|
||||
## Applied Improvements
|
||||
|
||||
{list}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- `core/checklists/retrospective.md` — Retrospective self-check
|
||||
@@ -0,0 +1,830 @@
|
||||
import { Database } from "bun:sqlite"
|
||||
import { readdirSync, readFileSync, existsSync, statSync } from "node:fs"
|
||||
import { join, relative } from "node:path"
|
||||
import { homedir } from "node:os"
|
||||
|
||||
const DBS_DIR = join(homedir(), ".local/share/octopus")
|
||||
const LEDGER_PATH = join(DBS_DIR, "token-stage-ledger.jsonl")
|
||||
|
||||
// #2628: workspace containers (octopus-ws-*) persist their session store in
|
||||
// named volumes octopus-sessions-<id> (bind added in
|
||||
// packages/containers/src/runtime/docker.ts). Scan those alongside the host
|
||||
// dir so telemetry no longer depends on which machine/container ran a session.
|
||||
const SESSION_VOLUMES_ROOT = "/data/docker/volumes"
|
||||
|
||||
function collectSessionVolumeDbs(): string[] {
|
||||
let vols: string[] = []
|
||||
try {
|
||||
vols = readdirSync(SESSION_VOLUMES_ROOT).filter((d) => d.startsWith("octopus-sessions-"))
|
||||
} catch {
|
||||
return [] // not on the docker host (e.g. a dev workstation) — fine
|
||||
}
|
||||
const out: string[] = []
|
||||
for (const v of vols) {
|
||||
const dir = join(SESSION_VOLUMES_ROOT, v, "_data")
|
||||
try {
|
||||
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
||||
if (e.isFile() && e.name.endsWith(".db") && (e.name === "octopus.db" || e.name.startsWith("octopus-"))) {
|
||||
out.push(join(dir, e.name))
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// unreadable volume — skip it
|
||||
}
|
||||
}
|
||||
return out.sort()
|
||||
}
|
||||
|
||||
function dbLabel(dbPath: string): string {
|
||||
const base = dbPath.split(/[/\\]/).pop()!.replace(".db", "")
|
||||
const vol = dbPath.match(/octopus-sessions-([a-zA-Z0-9-]+)[/\\]_data/)
|
||||
return vol ? `${base}@${vol[1]!.slice(0, 8)}` : base
|
||||
}
|
||||
|
||||
// SINCE_DAYS=<N> env var scopes queries to messages from the last N days,
|
||||
// avoiding full-table scans on multi-GB databases.
|
||||
// message.time_created is MILLISECONDS (verified: raw values ~1.78e12). Keep
|
||||
// `since` in ms — a seconds-based value is always smaller than every ms
|
||||
// timestamp, so the filter would silently match everything (#2599).
|
||||
const sinceDays = Number(process.env.SINCE_DAYS ?? "30")
|
||||
const since = sinceDays > 0 ? Date.now() - sinceDays * 86_400_000 : 0
|
||||
|
||||
// Cycle-window filters (retro #4034 quick-wins): `--since <ISO-date>` and
|
||||
// `--slug <slug>` constrain the cycle-window metrics (M2/M3/M5) to the
|
||||
// window / matching run. M1/M4 keep the SINCE_DAYS env semantics. A filter
|
||||
// that yields no data prints an explicit "no data in window" line for the
|
||||
// metric — never a silent fallback to all-time numbers.
|
||||
const argv = process.argv.slice(2)
|
||||
const arg = (name: string): string | undefined => {
|
||||
const i = argv.indexOf(`--${name}`)
|
||||
return i >= 0 ? argv[i + 1] : undefined
|
||||
}
|
||||
const sinceArg = arg("since")
|
||||
const slugArg = arg("slug")
|
||||
const windowSince = sinceArg !== undefined ? Date.parse(sinceArg) : undefined
|
||||
if (sinceArg !== undefined && Number.isNaN(windowSince)) {
|
||||
console.error(`invalid --since "${sinceArg}" — use an ISO date (e.g. 2026-09-02)`)
|
||||
process.exit(1)
|
||||
}
|
||||
const filtersActive = sinceArg !== undefined || slugArg !== undefined
|
||||
const windowOrSince = windowSince ?? since
|
||||
const m5Filter = filtersActive ? { since: windowOrSince, slug: slugArg } : undefined
|
||||
|
||||
function percentile(sorted: number[], p: number): number {
|
||||
if (sorted.length === 0) return 0
|
||||
const idx = Math.min(Math.floor((sorted.length * p) / 100), sorted.length - 1)
|
||||
return sorted[idx] ?? 0
|
||||
}
|
||||
|
||||
type AgentStats = Map<string, { msgCount: number; totalTokens: number }>
|
||||
|
||||
type SessionRow = {
|
||||
id: string
|
||||
parent_id: string | null
|
||||
slug: string | null
|
||||
title: string | null
|
||||
directory: string | null
|
||||
}
|
||||
|
||||
function loadSessionRows(db: Database): Map<string, SessionRow> | null {
|
||||
const hasTable = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='session'").get()
|
||||
if (!hasTable) return null
|
||||
const rows = db.prepare("SELECT id, parent_id, slug, title, directory FROM session").all() as SessionRow[]
|
||||
return new Map(rows.map((r) => [r.id, r]))
|
||||
}
|
||||
|
||||
// Session→run matching for --slug: a session matches when its slug, title,
|
||||
// or directory contains the run slug (workflow sessions live in worktrees
|
||||
// named after the run). A match propagates to the whole subtree (subagents),
|
||||
// so every message of the run's sessions is included.
|
||||
function slugSessionIncludeSet(sessions: Map<string, SessionRow>, slug: string): Set<string> {
|
||||
const needle = slug.toLowerCase()
|
||||
const children = new Map<string, string[]>()
|
||||
for (const r of sessions.values()) {
|
||||
if (!r.parent_id) continue
|
||||
const arr = children.get(r.parent_id) ?? []
|
||||
arr.push(r.id)
|
||||
children.set(r.parent_id, arr)
|
||||
}
|
||||
const include = new Set<string>()
|
||||
const markSubtree = (id: string) => {
|
||||
if (include.has(id)) return
|
||||
include.add(id)
|
||||
for (const c of children.get(id) ?? []) markSubtree(c)
|
||||
}
|
||||
for (const r of sessions.values()) {
|
||||
const hay = [r.slug, r.title, r.directory].filter((x): x is string => typeof x === "string")
|
||||
if (hay.some((x) => x.toLowerCase().includes(needle))) markSubtree(r.id)
|
||||
}
|
||||
return include
|
||||
}
|
||||
|
||||
function processDb(dbPath: string, since = 0, m5Filter?: { since: number; slug?: string }) {
|
||||
const dbName = dbLabel(dbPath)
|
||||
const sessions: {
|
||||
sessionId: string
|
||||
msgCount: number
|
||||
inputs: number[]
|
||||
totalInput: number
|
||||
totalCacheRead: number
|
||||
}[] = []
|
||||
const agents: AgentStats = new Map()
|
||||
|
||||
let db: Database | null = null
|
||||
try {
|
||||
db = new Database(dbPath, { readonly: true })
|
||||
db.exec("PRAGMA busy_timeout = 5000")
|
||||
} catch {
|
||||
return { dbName, sessionCount: 0, messageCount: 0, sessions, agents }
|
||||
}
|
||||
|
||||
try {
|
||||
const hasTable = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='message'").get()
|
||||
if (!hasTable) return { dbName, sessionCount: 0, messageCount: 0, sessions, agents }
|
||||
|
||||
// Single query replaces the former N+1 pattern (one query per session).
|
||||
// Grouping in JS avoids N full-table scans with json_extract.
|
||||
const rows = db
|
||||
.prepare(
|
||||
`
|
||||
SELECT
|
||||
session_id,
|
||||
CAST(json_extract(data, '$.tokens.input') AS REAL) as inp,
|
||||
CAST(json_extract(data, '$.tokens.cache.read') AS REAL) as cr
|
||||
FROM message
|
||||
WHERE json_extract(data, '$.role') = 'assistant'
|
||||
AND json_extract(data, '$.tokens.input') IS NOT NULL
|
||||
${since > 0 ? "AND time_created >= ?" : ""}
|
||||
ORDER BY session_id, time_created
|
||||
`,
|
||||
)
|
||||
.all(...(since > 0 ? [since] : [])) as {
|
||||
session_id: string
|
||||
inp: number | null
|
||||
cr: number | null
|
||||
}[]
|
||||
|
||||
const sessionMap = new Map<string, { inputs: number[]; totalInput: number; totalCacheRead: number }>()
|
||||
|
||||
for (const r of rows) {
|
||||
const inp = Number(r.inp ?? 0)
|
||||
const cr = Number(r.cr ?? 0)
|
||||
if (inp <= 0) continue
|
||||
let s = sessionMap.get(r.session_id)
|
||||
if (!s) {
|
||||
s = { inputs: [], totalInput: 0, totalCacheRead: 0 }
|
||||
sessionMap.set(r.session_id, s)
|
||||
}
|
||||
s.inputs.push(inp)
|
||||
s.totalInput += inp
|
||||
s.totalCacheRead += cr
|
||||
}
|
||||
|
||||
for (const [sessionId, s] of sessionMap) {
|
||||
if (s.inputs.length === 0) continue
|
||||
sessions.push({
|
||||
sessionId,
|
||||
msgCount: s.inputs.length,
|
||||
inputs: s.inputs,
|
||||
totalInput: s.totalInput,
|
||||
totalCacheRead: s.totalCacheRead,
|
||||
})
|
||||
}
|
||||
|
||||
if (m5Filter) {
|
||||
let include: Set<string> | undefined
|
||||
if (m5Filter.slug !== undefined) {
|
||||
const sessions = loadSessionRows(db)
|
||||
include = sessions ? slugSessionIncludeSet(sessions, m5Filter.slug) : new Set<string>()
|
||||
}
|
||||
// include.size === 0 (or a slug-less filter) → contribute nothing;
|
||||
// the merged M5 output prints the explicit "no data in window" line.
|
||||
if (include === undefined || include.size > 0) {
|
||||
const rows = db
|
||||
.prepare(
|
||||
`
|
||||
SELECT
|
||||
session_id,
|
||||
COALESCE(json_extract(data, '$.agent'), 'unknown') as agent,
|
||||
CAST(json_extract(data, '$.tokens.input') AS REAL) as inp,
|
||||
CAST(json_extract(data, '$.tokens.output') AS REAL) as outp,
|
||||
CAST(json_extract(data, '$.tokens.reasoning') AS REAL) as rea
|
||||
FROM message
|
||||
WHERE json_extract(data, '$.role') = 'assistant'
|
||||
AND json_extract(data, '$.tokens.input') IS NOT NULL
|
||||
${m5Filter.since > 0 ? "AND time_created >= ?" : ""}
|
||||
`,
|
||||
)
|
||||
.all(...(m5Filter.since > 0 ? [m5Filter.since] : [])) as {
|
||||
session_id: string
|
||||
agent: string
|
||||
inp: number | null
|
||||
outp: number | null
|
||||
rea: number | null
|
||||
}[]
|
||||
for (const r of rows) {
|
||||
if (include !== undefined && !include.has(r.session_id)) continue
|
||||
const cur = agents.get(r.agent) ?? { msgCount: 0, totalTokens: 0 }
|
||||
cur.msgCount++
|
||||
cur.totalTokens += Number(r.inp ?? 0) + Number(r.outp ?? 0) + Number(r.rea ?? 0)
|
||||
agents.set(r.agent, cur)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const agentRows = db
|
||||
.prepare(
|
||||
`
|
||||
SELECT
|
||||
COALESCE(json_extract(data, '$.agent'), 'unknown') as agent,
|
||||
COUNT(*) as msg_count,
|
||||
CAST(TOTAL(json_extract(data, '$.tokens.input')) AS REAL) as inp,
|
||||
CAST(TOTAL(json_extract(data, '$.tokens.output')) AS REAL) as outp,
|
||||
CAST(TOTAL(json_extract(data, '$.tokens.reasoning')) AS REAL) as rea
|
||||
FROM message
|
||||
WHERE json_extract(data, '$.role') = 'assistant'
|
||||
AND json_extract(data, '$.tokens.input') IS NOT NULL
|
||||
${since > 0 ? "AND time_created >= ?" : ""}
|
||||
GROUP BY agent
|
||||
`,
|
||||
)
|
||||
.all(...(since > 0 ? [since] : [])) as {
|
||||
agent: string
|
||||
msg_count: number
|
||||
inp: number
|
||||
outp: number
|
||||
rea: number
|
||||
}[]
|
||||
|
||||
for (const r of agentRows) {
|
||||
agents.set(r.agent, {
|
||||
msgCount: Number(r.msg_count ?? 0),
|
||||
totalTokens: Number(r.inp ?? 0) + Number(r.outp ?? 0) + Number(r.rea ?? 0),
|
||||
})
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
db.close()
|
||||
}
|
||||
|
||||
const messageCount = sessions.reduce((s, se) => s + se.msgCount, 0)
|
||||
return { dbName, sessionCount: sessions.length, messageCount, sessions, agents }
|
||||
}
|
||||
|
||||
function dbSummary(db: ReturnType<typeof processDb>) {
|
||||
if (db.sessionCount === 0) return null
|
||||
return ` ${db.dbName}: ${db.sessionCount} sessions, ${db.messageCount.toLocaleString()} messages`
|
||||
}
|
||||
|
||||
function computeSessionMedians(dbs: ReturnType<typeof processDb>[]) {
|
||||
const medians: number[] = []
|
||||
for (const db of dbs) {
|
||||
for (const s of db.sessions) {
|
||||
const sorted = [...s.inputs].sort((a, b) => a - b)
|
||||
medians.push(percentile(sorted, 50))
|
||||
}
|
||||
}
|
||||
return medians
|
||||
}
|
||||
|
||||
function computeSessionMaxes(dbs: ReturnType<typeof processDb>[]) {
|
||||
const maxes: number[] = []
|
||||
for (const db of dbs) {
|
||||
for (const s of db.sessions) {
|
||||
maxes.push(Math.max(...s.inputs))
|
||||
}
|
||||
}
|
||||
return maxes
|
||||
}
|
||||
|
||||
function computeSessionP90s(dbs: ReturnType<typeof processDb>[]) {
|
||||
const p90s: number[] = []
|
||||
for (const db of dbs) {
|
||||
for (const s of db.sessions) {
|
||||
const sorted = [...s.inputs].sort((a, b) => a - b)
|
||||
p90s.push(percentile(sorted, 90))
|
||||
}
|
||||
}
|
||||
return p90s
|
||||
}
|
||||
|
||||
function computeOverallCacheRatio(dbs: ReturnType<typeof processDb>[]) {
|
||||
let totalInput = 0
|
||||
let totalCacheRead = 0
|
||||
for (const db of dbs) {
|
||||
for (const s of db.sessions) {
|
||||
totalInput += s.totalInput
|
||||
totalCacheRead += s.totalCacheRead
|
||||
}
|
||||
}
|
||||
return totalInput > 0 ? totalCacheRead / totalInput : 0
|
||||
}
|
||||
|
||||
function trafficLight(ratio: number): string {
|
||||
if (ratio > 10) return "🟢"
|
||||
if (ratio >= 3) return "🟡"
|
||||
return "🔴"
|
||||
}
|
||||
|
||||
const dbFiles = [
|
||||
...readdirSync(DBS_DIR, { withFileTypes: true })
|
||||
.filter((e) => e.isFile() && e.name.endsWith(".db") && (e.name === "octopus.db" || e.name.startsWith("octopus-")))
|
||||
.map((e) => join(DBS_DIR, e.name)),
|
||||
...collectSessionVolumeDbs(), // #2628
|
||||
].sort()
|
||||
|
||||
for (const f of dbFiles) {
|
||||
const size = statSync(f).size
|
||||
if (size > 1_000_000_000 && since === 0) {
|
||||
console.error(
|
||||
`[WARN] ${f.split("/").pop()} is ${(size / 1e9).toFixed(1)} GB — query may be slow. Set SINCE_DAYS=<N> to scope to recent sessions.`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const allDbs = dbFiles.map((p) => processDb(p, since, m5Filter))
|
||||
|
||||
console.log("# Token Telemetry: Context Hygiene (M4)")
|
||||
console.log()
|
||||
if (filtersActive) {
|
||||
const parts = [sinceArg ? `--since ${sinceArg}` : "", slugArg ? `--slug ${slugArg}` : ""].filter(Boolean)
|
||||
console.log(`Cycle-window filters active (${parts.join(" ")}): M2/M3/M5 constrained to the window; M1/M4 unchanged`)
|
||||
console.log()
|
||||
}
|
||||
|
||||
console.log("## Per-Database Summaries")
|
||||
console.log()
|
||||
for (const db of allDbs) {
|
||||
const s = dbSummary(db)
|
||||
if (s) console.log(s)
|
||||
}
|
||||
|
||||
const totalSessions = allDbs.reduce((s, d) => s + d.sessionCount, 0)
|
||||
const totalMessages = allDbs.reduce((s, d) => s + d.messageCount, 0)
|
||||
console.log()
|
||||
console.log(
|
||||
`Total across ${allDbs.filter((d) => d.sessionCount > 0).length} databases: ${totalSessions} sessions, ${totalMessages.toLocaleString()} messages`,
|
||||
)
|
||||
|
||||
const sessionMedians = computeSessionMedians(allDbs)
|
||||
const sessionP90s = computeSessionP90s(allDbs)
|
||||
const sessionMaxes = computeSessionMaxes(allDbs)
|
||||
const cacheRatio = computeOverallCacheRatio(allDbs)
|
||||
|
||||
const sortedMedians = [...sessionMedians].sort((a, b) => a - b)
|
||||
const sortedP90s = [...sessionP90s].sort((a, b) => a - b)
|
||||
const sortedMaxes = [...sessionMaxes].sort((a, b) => a - b)
|
||||
|
||||
console.log()
|
||||
console.log("## Aggregate Input Token Stats (per-session metrics)")
|
||||
console.log()
|
||||
console.log("| Metric | p50 | p90 | max |")
|
||||
console.log("| ------ | --- | --- | --- |")
|
||||
console.log(
|
||||
`| Per-session median input | ${percentile(sortedMedians, 50).toLocaleString()} | ${percentile(sortedMedians, 90).toLocaleString()} | ${percentile(sortedMedians, 100).toLocaleString()} |`,
|
||||
)
|
||||
console.log(
|
||||
`| Per-session p90 input | ${percentile(sortedP90s, 50).toLocaleString()} | ${percentile(sortedP90s, 90).toLocaleString()} | ${percentile(sortedP90s, 100).toLocaleString()} |`,
|
||||
)
|
||||
console.log(
|
||||
`| Per-session max input | ${percentile(sortedMaxes, 50).toLocaleString()} | ${percentile(sortedMaxes, 90).toLocaleString()} | ${percentile(sortedMaxes, 100).toLocaleString()} |`,
|
||||
)
|
||||
|
||||
console.log()
|
||||
const ratioLabel = cacheRatio >= 1 ? `${cacheRatio.toFixed(1)}:1` : `1:${(1 / cacheRatio).toFixed(1)}`
|
||||
const light = trafficLight(cacheRatio)
|
||||
console.log(`## Cache Read / Input Ratio: ${ratioLabel} ${light}`)
|
||||
console.log()
|
||||
|
||||
const desc =
|
||||
cacheRatio > 10
|
||||
? "Excellent — context reuse is very high, indicating effective caching"
|
||||
: cacheRatio >= 3
|
||||
? "Moderate — reasonable cache hits, room for improvement"
|
||||
: "Low — consider strategies to increase context cache reuse"
|
||||
console.log(` ${desc}`)
|
||||
|
||||
const mergedAgents: Map<string, { msgCount: number; totalTokens: number }> = new Map()
|
||||
for (const db of allDbs) {
|
||||
for (const [agent, stats] of db.agents) {
|
||||
const existing = mergedAgents.get(agent)
|
||||
if (existing) {
|
||||
existing.msgCount += stats.msgCount
|
||||
existing.totalTokens += stats.totalTokens
|
||||
} else {
|
||||
mergedAgents.set(agent, { ...stats })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let exploreTokens = 0
|
||||
let workerTokens = 0
|
||||
for (const [agent, stats] of mergedAgents) {
|
||||
const lower = agent.toLowerCase()
|
||||
if (lower.includes("explorer")) exploreTokens += stats.totalTokens
|
||||
else if (lower.includes("worker")) workerTokens += stats.totalTokens
|
||||
}
|
||||
|
||||
const m5Ratio = workerTokens > 0 ? exploreTokens / workerTokens : 0
|
||||
|
||||
function m5TrafficLight(ratio: number): string {
|
||||
if (ratio > 2) return "🟢"
|
||||
if (ratio >= 1) return "🟡"
|
||||
return "🔴"
|
||||
}
|
||||
|
||||
console.log()
|
||||
console.log("## Explore / Execute Ratio (M5)")
|
||||
console.log()
|
||||
console.log("| Agent | Messages | Total tokens |")
|
||||
console.log("| --------- | -------- | ------------ |")
|
||||
|
||||
const sortedAgents = [...mergedAgents.entries()].sort((a, b) => b[1].totalTokens - a[1].totalTokens)
|
||||
for (const [agent, stats] of sortedAgents) {
|
||||
console.log(
|
||||
`| ${agent.padEnd(9)} | ${stats.msgCount.toLocaleString().padStart(7)} | ${stats.totalTokens.toLocaleString().padStart(12)} |`,
|
||||
)
|
||||
}
|
||||
|
||||
console.log()
|
||||
if (filtersActive && mergedAgents.size === 0) {
|
||||
console.log("[NOTE: no data in window for M5 — no messages match the requested window/slug]")
|
||||
} else {
|
||||
const m5Light = m5TrafficLight(m5Ratio)
|
||||
console.log(`Explore/Execute: ${m5Ratio.toFixed(2)}:1 ${m5Light}`)
|
||||
console.log()
|
||||
|
||||
const m5Desc =
|
||||
m5Ratio > 2
|
||||
? "Explorer-heavy — exploration dominates execution, good for discovery but may need more synthesis"
|
||||
: m5Ratio >= 1
|
||||
? "Balanced — reasonable split between exploration and execution"
|
||||
: "Execution-heavy — workers are spending tokens on discovery work that explorers should handle"
|
||||
console.log(` ${m5Desc}`)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Compactor activity — compact-frequency proxy (#2601 pilot data).
|
||||
// The compactor agent runs once per agent-initiated compaction, so its
|
||||
// message count in the window approximates how often compaction fired.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
console.log()
|
||||
console.log("## Compactor Activity (#2601 compact-frequency proxy)")
|
||||
console.log()
|
||||
const compactor = mergedAgents.get("compactor")
|
||||
if (compactor) {
|
||||
console.log(
|
||||
`compactor: ${compactor.msgCount.toLocaleString()} messages, ${compactor.totalTokens.toLocaleString()} tokens in window`,
|
||||
)
|
||||
console.log(" (per-run distribution = the #2601 pilot metric; rising zero-compact")
|
||||
console.log(" share for short runs (bugfix / DAG task) with no late-stage degradation retires the pilot gate)")
|
||||
} else {
|
||||
console.log("[NOTE: no compactor messages in window — zero agent-initiated compactions recorded]")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// M2 — Stage token distribution (ledger-gated).
|
||||
// Reads token-stage-ledger.jsonl (written by .octopus/plugin/token-stage-ledger.ts),
|
||||
// reconstructs a per-root-session stage timeline, and attributes every
|
||||
// assistant message's tokens to the stage that was active when the message
|
||||
// was created. Skips cleanly when no ledger exists.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type LedgerEntry = { sessionID: string; stage: string; t: number }
|
||||
|
||||
function loadLedger(): Map<string, { stage: string; t: number }[]> | null {
|
||||
if (!existsSync(LEDGER_PATH)) return null
|
||||
const bySession = new Map<string, { stage: string; t: number }[]>()
|
||||
let any = false
|
||||
for (const line of readFileSync(LEDGER_PATH, "utf8").split("\n")) {
|
||||
const trimmed = line.trim()
|
||||
if (!trimmed) continue
|
||||
try {
|
||||
const e = JSON.parse(trimmed) as LedgerEntry
|
||||
const arr = bySession.get(e.sessionID) ?? []
|
||||
arr.push({ stage: e.stage, t: e.t })
|
||||
bySession.set(e.sessionID, arr)
|
||||
any = true
|
||||
} catch {
|
||||
// skip malformed lines
|
||||
}
|
||||
}
|
||||
if (!any) return null
|
||||
for (const arr of bySession.values()) arr.sort((a, b) => a.t - b.t)
|
||||
return bySession
|
||||
}
|
||||
|
||||
function stageTokensForDb(
|
||||
dbPath: string,
|
||||
ledger: Map<string, { stage: string; t: number }[]>,
|
||||
since = 0,
|
||||
slug?: string,
|
||||
): Map<string, number> | null {
|
||||
let db: Database | null = null
|
||||
try {
|
||||
db = new Database(dbPath, { readonly: true })
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
try {
|
||||
const sessions = loadSessionRows(db)
|
||||
if (!sessions) return null
|
||||
|
||||
// timelines keyed by root sessions present in this db
|
||||
const timelines = new Map<string, { stage: string; t: number }[]>()
|
||||
for (const [sid, entries] of ledger) {
|
||||
if (sessions.has(sid)) timelines.set(sid, entries)
|
||||
}
|
||||
if (timelines.size === 0) return null
|
||||
|
||||
let include: Set<string> | undefined
|
||||
if (slug !== undefined) {
|
||||
include = slugSessionIncludeSet(sessions, slug)
|
||||
if (include.size === 0) return null // no session matches the slug
|
||||
}
|
||||
|
||||
const rootOf = (id: string): string => {
|
||||
let cur = id
|
||||
let guard = 0
|
||||
while (guard++ < 100) {
|
||||
const parent = sessions.get(cur)?.parent_id
|
||||
if (!parent) break
|
||||
cur = parent
|
||||
}
|
||||
return cur
|
||||
}
|
||||
const stageAt = (rootId: string, time: number): string | null => {
|
||||
const tl = timelines.get(rootId)
|
||||
if (!tl) return null
|
||||
let stage: string | null = null
|
||||
for (const e of tl) {
|
||||
if (e.t <= time) stage = e.stage
|
||||
else break
|
||||
}
|
||||
return stage
|
||||
}
|
||||
|
||||
const byStage = new Map<string, number>()
|
||||
const rows = db
|
||||
.prepare(
|
||||
`
|
||||
SELECT session_id, time_created,
|
||||
CAST(json_extract(data, '$.tokens.input') AS REAL) as inp,
|
||||
CAST(json_extract(data, '$.tokens.output') AS REAL) as outp,
|
||||
CAST(json_extract(data, '$.tokens.reasoning') AS REAL) as rea
|
||||
FROM message
|
||||
WHERE json_extract(data, '$.role') = 'assistant'
|
||||
AND json_extract(data, '$.tokens.input') IS NOT NULL
|
||||
${since > 0 ? "AND time_created >= ?" : ""}
|
||||
`,
|
||||
)
|
||||
.all(...(since > 0 ? [since] : [])) as {
|
||||
session_id: string
|
||||
time_created: number
|
||||
inp: number | null
|
||||
outp: number | null
|
||||
rea: number | null
|
||||
}[]
|
||||
|
||||
for (const r of rows) {
|
||||
if (include !== undefined && !include.has(r.session_id)) continue
|
||||
const stage = stageAt(rootOf(r.session_id), r.time_created)
|
||||
if (!stage) continue
|
||||
const tokens = Number(r.inp ?? 0) + Number(r.outp ?? 0) + Number(r.rea ?? 0)
|
||||
byStage.set(stage, (byStage.get(stage) ?? 0) + tokens)
|
||||
}
|
||||
return byStage
|
||||
} finally {
|
||||
db.close()
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// M3 — Review rework fraction (#2591).
|
||||
// Two sources, merged with dedup by review identity `{slug}/reviews/{stage}`
|
||||
// (the ACTIVE status.json wins when both exist — it is canonical):
|
||||
// 1. ACTIVE runs — status.json under the Tier 1 location
|
||||
// .octopus/runs/{slug}/reviews/{stage}/status.json (reads history[],
|
||||
// legacy alias rounds[], current_round) and the legacy
|
||||
// .artifacts/**/reviews/*/status.json tree. In-flight runs only: the
|
||||
// active workspace is deleted at archive-at-close, so this source alone
|
||||
// structurally empties as runs close.
|
||||
// 2. ARCHIVED runs — the committed archive bundle
|
||||
// .octopus/runs/archive/{slug}.json. Bundles store digests, not
|
||||
// status.json content, so the per-review round count is reconstructed
|
||||
// from the documented Tier 1 layout `reviews/{stage}/round{N}/…`
|
||||
// (templates/runs-layout.md) by counting distinct roundN path segments
|
||||
// per stage across index.artifacts[].path. This archive source is what
|
||||
// makes M3 durable.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type ReviewRounds = Map<string, number> // `${slug}/reviews/${stage}` -> rounds
|
||||
|
||||
function collectActiveReviewRounds(roots: string[]): { rounds: ReviewRounds; startedMs: Map<string, number> } {
|
||||
const out: ReviewRounds = new Map()
|
||||
const startedMs = new Map<string, number>()
|
||||
const walk = (dir: string, top: string) => {
|
||||
let entries: ReturnType<typeof readdirSync>
|
||||
try {
|
||||
entries = readdirSync(dir, { withFileTypes: true })
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
for (const e of entries) {
|
||||
if (e.name === "_archive" || e.name === "archive") continue
|
||||
const full = join(dir, e.name)
|
||||
if (e.isDirectory()) walk(full, top)
|
||||
else if (e.name === "status.json" && dir.includes("/reviews/")) {
|
||||
try {
|
||||
const data = JSON.parse(readFileSync(full, "utf8")) as {
|
||||
rounds?: unknown[]
|
||||
history?: unknown[]
|
||||
current_round?: number
|
||||
started_at?: string
|
||||
}
|
||||
// Canonical field is `history[]` (per review-status.schema.json);
|
||||
// `rounds[]` is a legacy alias that maps to it. Prefer the array
|
||||
// forms; fall back to current_round.
|
||||
let rounds = 0
|
||||
if (Array.isArray(data.rounds) && data.rounds.length > 0) rounds = data.rounds.length
|
||||
else if (Array.isArray(data.history) && data.history.length > 0) rounds = data.history.length
|
||||
else if (typeof data.current_round === "number" && data.current_round > 0) rounds = data.current_round
|
||||
if (rounds > 0) {
|
||||
const key = relative(top, dir).split("\\").join("/")
|
||||
out.set(key, rounds)
|
||||
if (typeof data.started_at === "string") {
|
||||
const t = Date.parse(data.started_at)
|
||||
if (!Number.isNaN(t)) startedMs.set(key, t)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// skip unreadable / malformed status files
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const root of roots) walk(root, root)
|
||||
return { rounds: out, startedMs }
|
||||
}
|
||||
|
||||
function collectBundleReviewRounds(archiveDir: string): { rounds: ReviewRounds; closedMs: Map<string, number> } {
|
||||
let files: string[] = []
|
||||
try {
|
||||
files = readdirSync(archiveDir).filter((f) => f.endsWith(".json"))
|
||||
} catch {
|
||||
return { rounds: new Map(), closedMs: new Map() } // no archive dir (e.g. a fresh checkout) — fine
|
||||
}
|
||||
const out: ReviewRounds = new Map()
|
||||
const closedMs = new Map<string, number>()
|
||||
for (const f of files) {
|
||||
try {
|
||||
const bundle = JSON.parse(readFileSync(join(archiveDir, f), "utf8")) as {
|
||||
index?: { artifacts?: { path?: unknown }[] }
|
||||
meta?: { closed_at?: string; updated_at?: string; created_at?: string }
|
||||
}
|
||||
// Identity = the bundle filename stem (= the archived run's workspace
|
||||
// dir name). meta.slug is NOT unique — epic task-node bundles carry the
|
||||
// parent epic slug while filenames stay per-node.
|
||||
const slug = f.replace(/\.json$/, "")
|
||||
const closedRaw = bundle.meta?.closed_at ?? bundle.meta?.updated_at ?? bundle.meta?.created_at
|
||||
// Distinct roundN segments per review stage across artifact paths
|
||||
// (paths may or may not carry the slug prefix — match the segment).
|
||||
const byStage = new Map<string, Set<string>>()
|
||||
for (const a of bundle.index?.artifacts ?? []) {
|
||||
if (typeof a?.path !== "string") continue
|
||||
const hit = a.path.match(/reviews\/([^/]+)\/(round\d+)\//)
|
||||
if (!hit?.[1] || !hit[2]) continue
|
||||
const set = byStage.get(hit[1]) ?? new Set<string>()
|
||||
set.add(hit[2])
|
||||
byStage.set(hit[1], set)
|
||||
}
|
||||
for (const [stage, rounds] of byStage) {
|
||||
if (rounds.size > 0) out.set(`${slug}/reviews/${stage}`, rounds.size)
|
||||
if (closedRaw !== undefined) {
|
||||
const t = Date.parse(closedRaw)
|
||||
if (!Number.isNaN(t)) closedMs.set(`${slug}/reviews/${stage}`, t)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// skip unreadable / malformed bundles
|
||||
}
|
||||
}
|
||||
return { rounds: out, closedMs }
|
||||
}
|
||||
|
||||
// --- M2 output ---
|
||||
const ledger = loadLedger()
|
||||
console.log()
|
||||
console.log("## Stage Token Distribution (M2)")
|
||||
console.log()
|
||||
if (!ledger) {
|
||||
console.log("[NOTE: token-stage-ledger.jsonl absent — M2 skipped]")
|
||||
console.log(" (enable the .octopus/plugin/token-stage-ledger plugin to populate)")
|
||||
} else {
|
||||
const mergedStages = new Map<string, number>()
|
||||
for (const dbPath of dbFiles) {
|
||||
const byStage = stageTokensForDb(dbPath, ledger, windowOrSince, slugArg)
|
||||
if (!byStage) continue
|
||||
for (const [stage, tokens] of byStage) mergedStages.set(stage, (mergedStages.get(stage) ?? 0) + tokens)
|
||||
}
|
||||
const grandTotal = [...mergedStages.values()].reduce((a, b) => a + b, 0)
|
||||
if (grandTotal === 0) {
|
||||
if (filtersActive) {
|
||||
console.log("[NOTE: no data in window for M2 — no attributed tokens match the requested window/slug]")
|
||||
} else {
|
||||
console.log("[NOTE: ledger present but no sessions matched — M2 has no attributed data yet]")
|
||||
}
|
||||
} else {
|
||||
const sortedStages = [...mergedStages.entries()].sort((a, b) => b[1] - a[1])
|
||||
console.log("| Stage | Tokens | Share |")
|
||||
console.log("| ------------- | ------ | ----- |")
|
||||
for (const [stage, tokens] of sortedStages) {
|
||||
const pct = ((tokens / grandTotal) * 100).toFixed(1)
|
||||
console.log(`| ${stage.padEnd(13)} | ${tokens.toLocaleString().padStart(13)} | ${pct.padStart(5)}% |`)
|
||||
}
|
||||
const reviewTokens = mergedStages.get("review") ?? 0
|
||||
const reviewShare = (reviewTokens / grandTotal) * 100
|
||||
const m2Light = reviewShare > 60 ? "🔴" : reviewShare >= 35 ? "🟡" : "🟢"
|
||||
console.log()
|
||||
console.log(`Review-stage share: ${reviewShare.toFixed(1)}% ${m2Light}`)
|
||||
console.log(
|
||||
` ${
|
||||
reviewShare > 60
|
||||
? "Review dominates token spend — possible over-reviewing"
|
||||
: reviewShare >= 35
|
||||
? "Moderate review spend"
|
||||
: "Review spend is proportionate"
|
||||
}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// --- M3 output ---
|
||||
const runsDir = join(process.cwd(), ".octopus", "runs")
|
||||
const artifactsDir = join(process.cwd(), ".artifacts")
|
||||
const archiveDir = join(runsDir, "archive")
|
||||
const activeRoots = [runsDir, artifactsDir].filter((d) => existsSync(d))
|
||||
console.log()
|
||||
console.log("## Review Rework (M3)")
|
||||
console.log()
|
||||
const active = collectActiveReviewRounds(activeRoots)
|
||||
const archived = collectBundleReviewRounds(archiveDir)
|
||||
const activeRounds = active.rounds
|
||||
const archivedRounds = archived.rounds
|
||||
const mergedRounds: ReviewRounds = new Map(activeRounds)
|
||||
let archivedOnly = 0
|
||||
for (const [key, rounds] of archivedRounds) {
|
||||
if (mergedRounds.has(key)) continue // active status.json is canonical
|
||||
mergedRounds.set(key, rounds)
|
||||
archivedOnly++
|
||||
}
|
||||
// Cycle-window filters: constrain to reviews whose run identity contains the
|
||||
// slug and whose start (active) / close (archived) time falls in the window.
|
||||
// Reviews without a parseable timestamp are excluded when a window is set —
|
||||
// strict, so filtered numbers never silently fall back to all-time totals.
|
||||
const windowRounds: ReviewRounds = new Map()
|
||||
for (const [key, rounds] of mergedRounds) {
|
||||
const identity = key.split("/reviews/")[0] ?? key
|
||||
if (slugArg !== undefined && !identity.includes(slugArg)) continue
|
||||
if (windowSince !== undefined) {
|
||||
const t = active.startedMs.get(key) ?? archived.closedMs.get(key)
|
||||
if (t === undefined || t < windowSince) continue
|
||||
}
|
||||
windowRounds.set(key, rounds)
|
||||
}
|
||||
if (mergedRounds.size === 0) {
|
||||
console.log(
|
||||
activeRoots.length === 0 && archivedRounds.size === 0
|
||||
? "[NOTE: no .octopus/runs or .artifacts directory in cwd — M3 skipped]"
|
||||
: "[NOTE: no review rounds found (active status.json or archive bundles) — M3 skipped]",
|
||||
)
|
||||
} else if (filtersActive && windowRounds.size === 0) {
|
||||
console.log("[NOTE: no data in window for M3 — no reviews match the requested window/slug]")
|
||||
} else {
|
||||
const roundsMap = filtersActive ? windowRounds : mergedRounds
|
||||
const reviews = roundsMap.size
|
||||
const totalRounds = [...roundsMap.values()].reduce((a, b) => a + b, 0)
|
||||
const reworkRounds = [...roundsMap.values()].reduce((a, b) => a + (b - 1), 0)
|
||||
const nonFirstPass = [...roundsMap.values()].filter((r) => r > 1).length
|
||||
const fraction = totalRounds > 0 ? reworkRounds / totalRounds : 0
|
||||
const nonFirstPct = (nonFirstPass / reviews) * 100
|
||||
const m3Light = fraction > 0.3 ? "🔴" : fraction >= 0.15 ? "🟡" : "🟢"
|
||||
if (filtersActive)
|
||||
console.log(`Window filter: ${windowRounds.size}/${mergedRounds.size} reviews match (--since/--slug)`)
|
||||
console.log(`Reviews: ${reviews} | total rounds: ${totalRounds} | rework rounds: ${reworkRounds}`)
|
||||
console.log(
|
||||
`Sources: ${activeRounds.size} active status.json + ${archivedOnly} archive bundles (dedup by slug+stage)`,
|
||||
)
|
||||
console.log(`Non-first-pass reviews: ${nonFirstPass}/${reviews} (${nonFirstPct.toFixed(0)}%)`)
|
||||
console.log()
|
||||
console.log(`Rework fraction: ${(fraction * 100).toFixed(1)}% ${m3Light}`)
|
||||
console.log(
|
||||
` ${
|
||||
fraction > 0.3
|
||||
? "High rework — review findings not actionable or design unclear"
|
||||
: fraction >= 0.15
|
||||
? "Moderate rework — some review churn"
|
||||
: "Low rework — reviews converge efficiently"
|
||||
}`,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
---
|
||||
name: review-artifact
|
||||
description: >-
|
||||
Use ONLY after analyze-dag has produced the DAG artifact — review the task DAG (review-dag single gate) — OR when auditing the <instance-root>/ process infrastructure itself (audit-process). Spawns N parallel Explorer reviewers (1 dimension each) against the matching checklist, synthesizes findings with a Worker Synthesizer, and iterates with a Worker reviser until convergence. The legacy roadmap / design-space / iteration-plan targets were archived 2026-08-21 ([org-internal #3072] phase 3, <instance-root>/archive/). Do NOT use for code review — use the review-code skill for that.
|
||||
triggers:
|
||||
- audit-process
|
||||
- review-dag
|
||||
- dag review
|
||||
- DAG 评审
|
||||
- process audit
|
||||
- parallel review
|
||||
- reviewer
|
||||
- checklist review
|
||||
- mandatory gate
|
||||
# `stage` is set to the primary gate `audit-process`; the second live target
|
||||
# (review-dag) carries its own stage id in the Target Selection table below.
|
||||
stage: audit-process
|
||||
role: Reviewer
|
||||
---
|
||||
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatimDir)。机制、结构与 frontmatter 保持;实例术语(工具名、路径、工单号)按 `core/adapters/TERMINOLOGY.md` 绑定到具体实例。
|
||||
|
||||
# Skill: review-artifact
|
||||
|
||||
## Tool Availability & Path Resolution
|
||||
|
||||
This skill's reviewers (parallel Explorer subagents) read two kinds of files.
|
||||
Some runs lack the `gitea_wiki__*` MCP tools (cross-repo scenario, [org-internal #2115]); the
|
||||
fallbacks below keep reviews working without them.
|
||||
|
||||
The Orchestrator probes availability **before dispatching reviewers** (Phase A
|
||||
step 1, `_shared/review-pipeline-phases.md`) and fails fast if neither path is
|
||||
usable, so every reviewer has a working read path by the time it runs.
|
||||
|
||||
### Wiki artifacts (DAG artifact / review reports)
|
||||
|
||||
- **Primary**: `wiki 读写 API(见 TERMINOLOGY)(owner, repo, page_name)` — use when the
|
||||
tool is available. `page_name` construction is in
|
||||
`_shared/gitea-read-patterns.md` Pattern 1.
|
||||
- **Fallback** (when `gitea_wiki__*` is unavailable): read the **local wiki
|
||||
clone** at `<wiki-clone-root>/{repo}-wiki/` — a git checkout of the wiki repo,
|
||||
kept current. Wiki filenames are URL-encoded (`/` → `%2F`, with a possible
|
||||
`.-` before `.md`), so resolve the file with `glob` rather than constructing
|
||||
one fixed path:
|
||||
|
||||
```
|
||||
glob(pattern="*{slug}*{page-title}*.md", path="<wiki-clone-root>/{repo}-wiki/")
|
||||
# then read the returned path
|
||||
```
|
||||
|
||||
### Checklists (`core/checklists/*.md`)
|
||||
|
||||
`<instance-root>/` **is git-tracked** (skills, checklists, rules — only runtime
|
||||
artifacts under `<instance-root>/locks/`, `entropy-baseline.json`, etc. are
|
||||
gitignored). Therefore the relative paths referenced throughout this skill
|
||||
(`core/checklists/dag-single-gate.md`, etc.) **resolve correctly inside
|
||||
any git worktree** — no absolute path is needed.
|
||||
|
||||
- **Primary**: read `core/checklists/<name>.md` relative to the project /
|
||||
worktree root (the reviewer subagent's CWD).
|
||||
- **Fallback** (non-standard CWD only): read from the main project directory,
|
||||
e.g. `/data/{repo}/core/checklists/<name>.md`.
|
||||
|
||||
## Target Selection
|
||||
|
||||
This skill reviews different artifact types. Identify the target from the pipeline stage that triggered the review:
|
||||
|
||||
| Target | Stage ID | Checklist | Dimensions | Revision Role | Output namespace |
|
||||
|---|---|---|---|---|---|
|
||||
| **process** | `audit-process` | `core/checklists/audit-process.md` | 10 | Remediator | `audit/{date}/` |
|
||||
| **review-dag** | `review-dag` | `core/checklists/dag-single-gate.md` | 3 | Analyst | `{epic-slug}/reviews/review-dag/` |
|
||||
|
||||
> **Retired targets (historical)**: the `roadmap`, `design-space`, and `plan`
|
||||
> targets were archived on 2026-08-21 per [org-internal #3072] phase 3 (ruling:
|
||||
> `docs/workflow-refactor/phase3-retirement-ruling.md`) — the legacy
|
||||
> production skills they gated no longer exist (see `<instance-root>/archive/`).
|
||||
> Their stage ids (`review-roadmap`, `review-design-space`,
|
||||
> `review-iteration-plan`) were retired from the stage registry in the same
|
||||
> ruling. Historical review pages under `{slug}/reviews/roadmap|design-space|plan/`
|
||||
> remain readable; no new reviews run against those targets.
|
||||
|
||||
The stage IDs remain as-is — they are used by the stage registry and auto-approve system. The skill name is `review-artifact`, but the stage IDs do not change.
|
||||
|
||||
> **Progressive disclosure ([org-internal #3734])**: this skill has two disjoint live
|
||||
> targets — dispatch an auditor/reviewer with
|
||||
> `skills: ["review-artifact:target-process"]` or
|
||||
> `["review-artifact:target-review-dag"]` (preamble + that target only)
|
||||
> instead of the full body. Tool availability, target selection, and shared
|
||||
> pipeline mechanics are unmarked preamble and always inject. Full-body
|
||||
> remains the default when no selector is passed.
|
||||
|
||||
## Shared Pipeline Mechanics
|
||||
|
||||
This skill follows the standardized parallel-review pipeline. The full mechanics are in:
|
||||
|
||||
- `<!-- inject: ../_shared/review-pipeline-phases.md -->` — Phase A through Phase E
|
||||
- **Phase A step 0**: move the source issue to the `review` column on the
|
||||
Pipeline Stages board per `_shared/gitea-write-patterns.md` Pattern 7.5
|
||||
(see `_shared/review-pipeline-phases.md` for the canonical orchestrator
|
||||
actions — step 0 is now part of the shared flow).
|
||||
- `<!-- inject: ../_shared/review-orchestrator-rules.md -->` — mandatory orchestrator rules
|
||||
- `<!-- inject: ../_shared/review-final-report-template.md -->` — final report format
|
||||
- `<!-- inject: ../_shared/review-revision-prompt.md -->` — Phase C revision prompt template
|
||||
- `<!-- inject: ../_shared/compaction-round-boundary.md -->` — round-boundary compaction procedure (Phase D)
|
||||
|
||||
**Context compaction**: this skill compacts at every review round ≥ 2. The
|
||||
round-boundary procedure (`compaction-round-boundary.md`) is injected directly
|
||||
above — that is the normative source; do not restate it here. At the stage
|
||||
level, the main session also compacts after this skill converges, per
|
||||
`core/rules/compact.md` §"Stage-boundary compaction".
|
||||
|
||||
Each target binds the canonical phase placeholders and defines target-specific overrides in its section below.
|
||||
|
||||
### JSON Schemas
|
||||
|
||||
| Schema File | Used For |
|
||||
|---|---|
|
||||
| `core/schemas/reviewer-output.schema.json` | Per-dimension reviewer findings |
|
||||
| `core/schemas/synthesis.schema.json` | Orchestrator round synthesis |
|
||||
| `core/schemas/review-status.schema.json` | Review lifecycle tracking |
|
||||
|
||||
### Reviewer Prompt
|
||||
|
||||
The Orchestrator MUST use the standardized reviewer prompt template at `core/skills/_shared/review-reviewer-prompt.md` when dispatching each reviewer. The template is the same for all reviewers; the Orchestrator fills in skill-level constants and per-dimension values. Each target defines its own constants in its section below.
|
||||
|
||||
### Agent Roles
|
||||
|
||||
| Role | Agent Type | Count | Responsibility |
|
||||
|---|---|---|---|
|
||||
| Orchestrator | Worker | 1 | Spawns reviewers, reads synthesis, drives revision loop |
|
||||
| Reviewer | Explorer | N (per target) | Reviews ONE dimension, outputs structured JSON |
|
||||
| Revision Role | Worker | 1 | Processes findings, revises artifact (role varies by target) |
|
||||
|
||||
All reviewers run in parallel. The orchestrator and revision role run sequentially.
|
||||
|
||||
### Preconditions
|
||||
|
||||
Before launching any review, confirm:
|
||||
- [ ] The target artifact exists (source varies by target: wiki pages for
|
||||
review-dag; the local `<instance-root>/` corpus for audit-process — no upstream
|
||||
producer, self-authored).
|
||||
- [ ] The matching checklist is accessible (see target selection table above).
|
||||
- [ ] `core/schemas/` contains the shared review schemas (`reviewer-output`, `synthesis`, `review-status`).
|
||||
- [ ] `slug` is known (for process audits, `slug` is the literal `audit`).
|
||||
|
||||
Each target adds specific preconditions listed in its section below. If any precondition is unmet, abort and inform the user.
|
||||
|
||||
> ## Retired targets (historical, [org-internal #3072] phase 3)
|
||||
|
||||
The three legacy production-review targets — **roadmap** (stage
|
||||
`review-roadmap`, checklist `roadmap.md`, revision role Analyst),
|
||||
**design-space** (stage `review-design-space`, checklists
|
||||
`requirements-analysis.md` + `design.md`, revision role Architect), and
|
||||
**plan** (stage `review-iteration-plan`, checklist `iteration-plan.md`,
|
||||
revision role Planner) — were archived on 2026-08-21 together with the
|
||||
production skills they gated (ruling:
|
||||
`docs/workflow-refactor/phase3-retirement-ruling.md`; skills and exclusive
|
||||
checklists/templates recoverable under `<instance-root>/archive/`). Their stage ids
|
||||
are retired from the stage registry; no new reviews run against them, and
|
||||
historical review pages under `{slug}/reviews/{roadmap|design-space|plan}/`
|
||||
stay readable as produced. Live targets: **process** (below) and
|
||||
**review-dag** (single gate).
|
||||
---
|
||||
|
||||
<!-- section:target-process -->
|
||||
## Target: process (audit)
|
||||
|
||||
The audit target is the **SDLC factory itself** — the set of skills, checklists, templates, and schemas under `<instance-root>/` — judged for internal consistency, cross-reference integrity, and conformance to audit standards (ISO 19011:2018, IEEE 1028-2008) and `AGENTS.md` conventions.
|
||||
|
||||
**Scope boundary**: This skill audits the *definition* of the pipeline. It does NOT audit whether a project *followed* the pipeline — that is the `retrospective` skill's responsibility.
|
||||
|
||||
### Audit Subject
|
||||
|
||||
| Area | Path | What is audited |
|
||||
|---|---|---|
|
||||
| Skills | `core/skills/**/SKILL.md` | Frontmatter, references, pipeline wiring |
|
||||
| Checklists | `core/checklists/*.md` | Coverage, orphans, dimension count match |
|
||||
| Templates | `core/templates/*.md` | Orphans, referenced existence |
|
||||
| Schemas | `core/schemas/*.json` | Validity, field alignment with skill text |
|
||||
| Conventions | `AGENTS.md` | Engineering-convention conformance |
|
||||
|
||||
### Preconditions
|
||||
|
||||
- [ ] `core/skills/` exists and contains at least one `SKILL.md`.
|
||||
- [ ] `core/checklists/audit-process.md` is accessible.
|
||||
- [ ] `core/schemas/` contains the shared review schemas (`reviewer-output`, `synthesis`, `review-status`).
|
||||
|
||||
No `.artifacts/{slug}/` inputs are required — the audit reads `<instance-root>/` itself. Audit outputs are published as Gitea wiki pages under `audit/{date}/`.
|
||||
|
||||
### Audit Dimensions
|
||||
|
||||
| # | Dimension | Label | Checklist Ref | Files to Read |
|
||||
|---|---|---|---|---|
|
||||
| 1 | INV | Inventory Completeness | INV 1.1–1.7 | `core/skills/` tree, `checklists/`, `templates/`, `schemas/` listings |
|
||||
| 2 | XREF | Cross-Reference Integrity | XREF 2.1–2.7 | ALL `SKILL.md`; resolve every referenced path |
|
||||
| 3 | FM | Frontmatter Conformance | FM 3.1–3.7 | Frontmatter block of every `SKILL.md` |
|
||||
| 4 | NAM | Naming Convention | NAM 4.1–4.6 | ALL `SKILL.md`, checklist filenames, schema enums |
|
||||
| 5 | FLOW | Pipeline Cohesion | FLOW 5.1–5.6 | Preconditions + output sections of every `SKILL.md` |
|
||||
| 6 | SCH | Schema Contract | SCH 6.1–6.5 | `core/schemas/*.json` + skills that reference them |
|
||||
| 7 | DUP | Duplication & Drift | DUP 7.1–7.4 | ALL `SKILL.md` + ALL `checklists/*.md` |
|
||||
| 8 | STD | Audit Standards (ISO/IEEE) | STD 8.1–8.7 | `review-*` skills, `review-artifact` (target: `audit-process`) itself |
|
||||
| 9 | AGT | AGENTS.md Conformance | AGT 9.1–9.5 | ALL `SKILL.md` + `AGENTS.md` |
|
||||
| 10 | TRC | Traceability | TRC 10.1–10.5 | `review-*` skills, schemas, status artifacts |
|
||||
|
||||
### Phase A Threshold
|
||||
|
||||
The process target has 10 dimensions, so Phase A is satisfied when **≥8 of 10
|
||||
dimension findings are produced after retries** (≥75% coverage). If fewer
|
||||
than 8 dimension JSONs are available after
|
||||
retries, abort the round with FAIL — an audit round must not proceed with an
|
||||
underspecified fraction of dimensions covered. Crashed/timed-out dimensions are
|
||||
marked `UNRESOLVED` and retried in the next round.
|
||||
|
||||
### Auditor Constants
|
||||
|
||||
In auditor output, the `dimension` field carries the audit dimension code (INV, XREF, FM, ...) and `slug` in the commit status is the literal `audit`.
|
||||
|
||||
The Orchestrator MUST use the standardized auditor prompt template from `core/skills/_shared/review-reviewer-prompt.md` with these constants:
|
||||
|
||||
- Model tier: default session reviewer model (`Reviewer` Explorer role, `_shared/roles/reviewer.yaml`) — the process target sets no `reviewer-tier` override; record any assignment change here so sibling review skills stay comparable (TRC 10.5)
|
||||
- `{REVIEWER_ROLE}`: process auditor
|
||||
- `{BASELINE_SECTION}`:
|
||||
```
|
||||
## Audit Subject
|
||||
You are auditing the <instance-root>/ process infrastructure ITSELF — the SDLC factory of skills, checklists, templates, and schemas. You are NOT auditing any application code, and NOT auditing whether a project followed the process.
|
||||
```
|
||||
- `{READ_FIRST_STEPS}`:
|
||||
1. Read every file listed above. Do NOT skip any file.
|
||||
2. For XREF/SCH dimensions: for every path or field referenced in a skill, actually resolve it (Read/Glob) and confirm it exists and matches. A referenced-but-missing path or a field-name mismatch is a finding.
|
||||
3. For all other dimensions, read every assigned file and flag any cross-file inconsistency (terminology, numbering, drift) you find.
|
||||
- `{ARTIFACT_NOUN}`: process infrastructure
|
||||
- `{FINDING_DETAILS}`: file path and line number, a summary, detailed explanation, and quoted evidence from the file
|
||||
- `{FINDING_ID_EXAMPLE}`: INV-F001
|
||||
- `{CONTRADICTION_RULE}`:
|
||||
6. (not applicable — the audit is a self-contained inspection, not a baseline comparison)
|
||||
- `{SEVERITY_DEFINITIONS}`:
|
||||
- BLOCKER: broken cross-reference (skill references a non-existent checklist/template/schema/path), missing required frontmatter `description` (skill never triggers), a pipeline stage with no producer for its declared precondition, schema field mismatch that would make written artifacts invalid
|
||||
- MAJOR: orphan checklist/template/schema, dimension count mismatch between a review skill and its checklist, naming inconsistency for a dimension code or role, missing convergence/approval gate, AGENTS.md command divergence (e.g. `bun test` instead of `bun run test:parallel`)
|
||||
- MINOR: terminology drift, duplicated rule that should live in one place, inconsistent standard-number formatting, missing cross-reference comment
|
||||
- INFO: observation or improvement suggestion beyond the checklist
|
||||
- `{LOOKS_FINE_PHRASE}`: the pipeline looks fine
|
||||
|
||||
### Phase Bindings
|
||||
|
||||
| Placeholder | Value |
|
||||
|---|---|
|
||||
| `{DIMENSION_COUNT}` | 10 |
|
||||
| `{REVISION_ROLE}` | Remediator |
|
||||
| `{REVIEW_TYPE}` | audit-process |
|
||||
| `{STAGE}` | audit-process |
|
||||
| `{REVIEWER_OR_AUDITOR}` / `{REVIEWER_OR_AUDITOR_NAME}` | auditor / Audit |
|
||||
| `{REVIEWER_OR_AUDIT_LOOP}` | Re-audit |
|
||||
| Output mode | Tier 1 local files under `<runs-root>/audit/reviews/audit-process/round{N}/findings-{DIMENSION_CODE}.json` (validated by `core/schemas/reviewer-output.schema.json`); synthesis wiki page + commit status stay Tier 2 |
|
||||
| Commit status context | `pipeline/audit-process` |
|
||||
| Wiki namespace | `audit/{date}/` |
|
||||
| Status tracking | Commit status `pipeline/audit-process` (via REST, `_shared/gitea-write-patterns.md` Pattern 8) — `slug: "audit"` |
|
||||
| Date resolution | `{date}` resolved as `YYYY-MM-DD` |
|
||||
| Task file names | `task-synthesizer`, `task-remediator` |
|
||||
|
||||
### Phase E — Close-actor (process target)
|
||||
|
||||
After the final report is written and the `## 工件索引` REVIEW row is
|
||||
updated per the shared Phase E step 2, IF this route closes the source issue
|
||||
(audit-only close, the common Kind/Enhancement process-improvement path that
|
||||
does NOT run verify), execute archive-at-close per
|
||||
`core/rules/artifact-index-guards.md` guard #3: edit the index comment
|
||||
in place — add the canonical archive banner (defined in
|
||||
`_shared/gitea-write-patterns.md` Pattern 10) above the table, and set
|
||||
every row's 重读 column to `ARCHIVE`. Mechanism details (single-comment
|
||||
invariant, no URL deletion, reversibility) per Pattern 10.
|
||||
|
||||
### Remediator Constraints
|
||||
|
||||
The Remediator revises `<instance-root>/` files. Constraints:
|
||||
- Do NOT change the meaning of a skill's workflow to satisfy a finding; fix the inconsistency, not the intent.
|
||||
- Preserve frontmatter `name`/`description` semantics; only correct conformance issues.
|
||||
- When fixing a broken cross-reference, fix the reference OR create the missing file — whichever preserves the pipeline's intent; explain the choice.
|
||||
|
||||
### Output Artifacts
|
||||
|
||||
```
|
||||
<runs-root>/audit/reviews/audit-process/
|
||||
├── index.json # Tier 1 manifest (one row per findings/synthesis/task file; validated by runs-index.schema.json)
|
||||
├── status.json # Review lifecycle tracking (shared Phase A step 0.5; validated by review-status.schema.json)
|
||||
└── round{N}/
|
||||
├── task-remediator.md # Remediator input prompt (Tier 1 copy; index row type `remediation-prompt`)
|
||||
├── task-synthesizer.md # Synthesizer input prompt (Tier 1; index row type `synthesis-prompt`)
|
||||
├── findings-{DIMENSION_CODE}.json # Auditor dimension findings (Tier 1 local, validated by reviewer-output.schema.json; index row type `review-findings`)
|
||||
└── synthesis-return.md # Synthesizer return summary (index row type `other`)
|
||||
|
||||
audit/{date}/ # Wiki page hierarchy (Tier 2)
|
||||
├── round{N}/
|
||||
│ ├── task-remediator # Remediator input (wiki page)
|
||||
│ ├── revision-summary # Remediator revision summary
|
||||
│ └── synthesis # Orchestrator synthesis (```json block, wiki page — audits have no source issue comment)
|
||||
├── round{N+1}/...
|
||||
└── final/
|
||||
└── report # Final human-readable report
|
||||
```
|
||||
|
||||
> **Wiki namespace override**: the audit target writes `task-{ROLE}`,
|
||||
> `revision-summary`, `synthesis`, and `final/report` under `audit/{date}/`
|
||||
> (date-slug), NOT the shared `{slug}/reviews/{stage}/` formula. See the Phase
|
||||
> Bindings `Wiki namespace` row above and `_shared/review-pipeline-phases.md`
|
||||
> Phase C step 1 / Phase E step 1 for the audit carve-out.
|
||||
|
||||
### References
|
||||
|
||||
- `core/checklists/audit-process.md` — The authoritative checklist for all 10 audit dimensions
|
||||
- `core/schemas/` — Shared JSON schemas (reviewer-output, synthesis, review-status) reused by this audit
|
||||
- `retrospective` skill — Complementary: audits a project's *adherence* to the process; this skill audits the process *definition* itself
|
||||
- ISO 19011:2018 — Guidelines for auditing management systems (independence, evidence-based, documented)
|
||||
- IEEE 1028-2008 — Software Reviews and Audits (completeness, repeatability, severity classification)
|
||||
|
||||
---
|
||||
|
||||
<!-- section:target-review-dag -->
|
||||
## Target: review-dag
|
||||
|
||||
The DAG review is a **single gate** (spec-02 §1 D-02) that replaces `review-design-space` +
|
||||
`review-iteration-plan` for a DAG artifact. It reviews one artifact — the DAG
|
||||
at wiki page `{epic-slug}/dag` (NOT per-size-tier pages) — against
|
||||
`core/checklists/dag-single-gate.md` with three fixed dimensions. It saves
|
||||
orchestration overhead, not the revision loop — the three dimensions each
|
||||
review an independent risk surface.
|
||||
|
||||
### Preconditions
|
||||
|
||||
- [ ] The DAG artifact exists at wiki page `{epic-slug}/dag` (read via `wiki 读写 API(见 TERMINOLOGY)`).
|
||||
- [ ] The DAG's `dag_metrics.review_depth` is set (D1–D4) — review depth, reviewer count, `dim_split`, and the Phase D round cap are all derived from this field, NOT from `Size/*` labels.
|
||||
- [ ] `core/checklists/dag-single-gate.md` is accessible.
|
||||
- [ ] `{epic-slug}` is known.
|
||||
|
||||
### Review Dimensions
|
||||
|
||||
Fixed dimension ids — do NOT rename:
|
||||
|
||||
| # | Dimension | Label | Checklist Ref | Files to Read |
|
||||
|---|---|---|---|---|
|
||||
| 1 | TOPO | Topology | `core/checklists/dag-single-gate.md` §TOPO | `{epic-slug}/dag` |
|
||||
| 2 | REQMAP | Requirement Mapping | `core/checklists/dag-single-gate.md` §REQMAP | `{epic-slug}/dag` (+ `{epic-slug}/dag-nodes/{node-id}` subpages when AC detail is sunk) |
|
||||
| 3 | RELEASE | Rolling Release | `core/checklists/dag-single-gate.md` §RELEASE | `{epic-slug}/dag` |
|
||||
|
||||
> **REQMAP reviews task nodes only** — milestone nodes carry no `req_refs` /
|
||||
> `acceptance_criteria` (only a DoD) and are not part of the REQMAP surface.
|
||||
|
||||
### Review Depth (from `dag_metrics.review_depth`)
|
||||
|
||||
Review depth is derived from the DAG's `dag_metrics.review_depth` (D1–D4), NOT
|
||||
from `Size/*` labels.
|
||||
|
||||
| Depth | Reviewers | dim_split |
|
||||
|---|---|---|
|
||||
| D1 | 1 | `all` |
|
||||
| D2 | 3 | `TOPO:1 REQMAP:1 RELEASE:1` |
|
||||
| D3 | 5 | `TOPO:2 REQMAP:2 RELEASE:1` |
|
||||
| D4 | 5 ([org-internal #3061] cap) | `TOPO:2 REQMAP:2 RELEASE:1` |
|
||||
|
||||
> **D4 cap ([org-internal #3061])**: D4 keeps its own rounds budget (4) but its fanout is
|
||||
> capped at D3 level (5 reviewers) — under the 3-dimension + checklist-bound
|
||||
> review the marginal coverage of 9 reviewers was redundancy, and round
|
||||
> iteration is the backstop.
|
||||
|
||||
### Reviewer slots & findings files (dim_split > 1)
|
||||
|
||||
`dim_split` may assign MORE THAN ONE reviewer to a dimension (D3/D4
|
||||
`TOPO:2`). Each reviewer within a dimension occupies a 1-based **slot**
|
||||
and writes its OWN findings file — parallel reviewers must never share one
|
||||
file (concurrent write race):
|
||||
|
||||
- 1 reviewer on the dimension → `findings-{DIM}.json` (unchanged shape).
|
||||
- n > 1 reviewers → `findings-{DIM}-r{k}.json`, k = 1..n — e.g. a D3/D4 round
|
||||
(dim_split `TOPO:2 REQMAP:2 RELEASE:1`) produces `findings-TOPO-r1.json` /
|
||||
`-r2.json`, `findings-REQMAP-r1.json` / `-r2.json`, and a single
|
||||
`findings-RELEASE.json` (RELEASE has one reviewer at every depth ≥ D2).
|
||||
- Multi-slot findings JSONs MUST set `reviewer_slot` (their k) and
|
||||
`reviewer_count` (n) — optional fields in
|
||||
`core/schemas/reviewer-output.schema.json`. Finding ids inside slot
|
||||
files MUST be slot-prefixed (`{DIM}R{k}-F{NNN}`, e.g. `TOPOR1-F001`) so ids
|
||||
stay unique across a dimension's slot files. This preserves per-reviewer
|
||||
attribution (retrospective overlap analysis, [org-internal #2996] step ①a) and makes the
|
||||
write race impossible by construction.
|
||||
- Synthesis input for a dimension = ALL `findings-{DIM}*.json` in the round
|
||||
directory — the synthesizer already globs every `findings-*.json`; the slot
|
||||
files of one dimension aggregate as that dimension's findings.
|
||||
- Phase A's "dimension produced" check: a dimension counts as produced only
|
||||
when EVERY assigned slot's file exists.
|
||||
|
||||
### Reviewer Constants
|
||||
|
||||
- Model tier: `reviewer-tier` (per `analyze-dag` §Exec-Resource Configuration — reviewer-tier's only allowed consumer; corresponds to the `Reviewer` Explorer role, `_shared/roles/reviewer.yaml`)
|
||||
- `{REVIEWER_ROLE}`: DAG reviewer
|
||||
- `{BASELINE_SECTION}`:
|
||||
```
|
||||
## Baseline
|
||||
The artifact under review is the DAG at wiki page `{epic-slug}/dag` (read via `gitea_wiki__get_page`). Read it first for orientation. Its `dag_metrics.review_depth` (D1–D4) sets this review's depth and round cap.
|
||||
```
|
||||
- `{READ_FIRST_STEPS}`:
|
||||
1. Read the DAG artifact at `{epic-slug}/dag` first for orientation.
|
||||
2. Read `core/checklists/dag-single-gate.md`.
|
||||
3. For REQMAP when AC detail is sunk: follow the `{node-id} → {epic-slug}/dag-nodes/{node-id}` pointers to read the subpage ACs.
|
||||
- `{ARTIFACT_NOUN}`: DAG artifact
|
||||
- `{FINDING_DETAILS}`: location in the document, a summary, detailed explanation, quoted evidence from the document
|
||||
- `{FINDING_ID_EXAMPLE}`: TOPO-F001
|
||||
- `{CONTRADICTION_RULE}`:
|
||||
6. When a DAG element contradicts its own declared topology, requirement mapping, or rolling-release plan, this is at minimum a MAJOR finding. When a required DAG section is entirely absent, this is a BLOCKER.
|
||||
- `{SEVERITY_DEFINITIONS}`:
|
||||
- BLOCKER: cycle in the DAG edge graph (TOPO 环检测), milestone missing at a `cross_session_in ≥ 2` convergence point (TOPO 里程碑位置), ≥3 task nodes below the granularity floor (`estimated_sessions < 0.25`, TOPO 粒度下限), a requirement with zero node coverage (REQMAP 需求覆盖)
|
||||
- MAJOR: missing or reversed dependency edge (TOPO 依赖正确性), giant node (`estimated_sessions ≥ 2`) needing split (TOPO 大小均匀性), `estimated_hours`/`estimated_sessions` field inconsistency (`|estimated_hours − 8 × estimated_sessions| > 2`), a single task node below the granularity floor, AC missing error/boundary-path coverage (REQMAP AC 路径覆盖), contract↔node AC inconsistency (REQMAP 契约↔节点一致性), task node with empty/invalid `req_refs` (REQMAP 节点溯源)
|
||||
- MINOR: `1 < estimated_sessions < 2` (over-upper-limit, suggest split), formatting issue, missing cross-reference, terminology drift, revision record not initialized
|
||||
- INFO: observation or recommendation beyond the checklist, including the TOPO page-size signal (see below) — does not affect any gate verdict
|
||||
- `{LOOKS_FINE_PHRASE}`: the DAG looks fine
|
||||
|
||||
### Phase Bindings
|
||||
|
||||
| Placeholder | Value |
|
||||
|---|---|
|
||||
| `{DIMENSION_COUNT}` | 3 |
|
||||
| `{REVISION_ROLE}` | Analyst |
|
||||
| `{REVIEW_TYPE}` | review-dag |
|
||||
| `{STAGE}` | review-dag |
|
||||
| `{REVIEWER_OR_AUDITOR}` / `{REVIEWER_OR_AUDITOR_NAME}` | reviewer / Review |
|
||||
| `{REVIEWER_OR_AUDIT_LOOP}` | Re-review |
|
||||
| Output mode | Tier 1 local files under `<runs-root>/{epic-slug}/reviews/review-dag/round{N}/findings-{DIMENSION_CODE}.json` — one file per reviewer slot when dim_split > 1 (`findings-{DIM}-r{k}.json`, see §"Reviewer slots & findings files") — validated by `core/schemas/reviewer-output.schema.json`; synthesis comment + commit status stay Tier 2 |
|
||||
| Commit status context | `pipeline/review-dag` |
|
||||
| Wiki namespace | `{epic-slug}/reviews/review-dag/` |
|
||||
| Task file names | `task-synthesizer`, `task-analyst` |
|
||||
|
||||
### Output (single gate)
|
||||
|
||||
The review produces a **single-gate synthesis**: the synthesis comment (reusing
|
||||
`core/schemas/synthesis.schema.json`) uses `dimensions` key =
|
||||
`TOPO` / `REQMAP` / `RELEASE`, and the commit status context is
|
||||
`pipeline/review-dag`. Revision role = **Analyst** (the DAG author).
|
||||
|
||||
### Phase A Threshold
|
||||
|
||||
Unlike the process target (≥8 of 10 dimensions), review-dag has only 3 dimensions, so
|
||||
Phase A is satisfied when **ALL THREE dimension findings are produced (3/3 = 100%)**.
|
||||
|
||||
- "3/3=100%" applies to a **full round** only (all 3 dimensions assigned and produced).
|
||||
- In a **partial re-review round** (rounds ≥3 re-review only FAIL/WARN/UNRESOLVED dimensions), Phase A is satisfied when the assigned dimensions' findings are all produced.
|
||||
- In a **full round where one dimension is UNRESOLVED** (e.g. a reviewer crashed, only 2/3 produced), that round proceeds with the produced dimensions and the UNRESOLVED dimension defers to the next partial re-review round — do NOT abort and do NOT apply the 8-file threshold.
|
||||
|
||||
### Phase D MAX_ROUNDS (per depth)
|
||||
|
||||
review-dag's Phase D convergence loop round cap is read from
|
||||
`dag_metrics.review_depth`, NOT the shared `review-artifact` skill default of 3:
|
||||
|
||||
| Depth | MAX_ROUNDS |
|
||||
|---|---|
|
||||
| D1 | ≤2 |
|
||||
| D2 | ≤3 |
|
||||
| D3 | ≤3 |
|
||||
| D4 | ≤4 |
|
||||
|
||||
**D4 override**: the shared `review-artifact` skill MAX_ROUNDS is 3 (see
|
||||
`_shared/review-pipeline-phases.md` Phase D "`{MAX_ROUNDS}` binding"), but
|
||||
review-dag overrides it per depth. D4 reaches round 4 (≤4), which exceeds the
|
||||
shared default — the D4 round cap MUST take precedence for review-dag.
|
||||
|
||||
### Round-3 Escalation Guardrail interaction
|
||||
|
||||
The Round-3 Escalation Guardrail (see `_shared/review-pipeline-phases.md`
|
||||
Phase D) applies to review-dag. Its interaction with the per-depth round cap:
|
||||
|
||||
- **D1–D3** (cap ≤3): the guardrail and the per-depth cap coincide — no conflict.
|
||||
- **D4** (cap ≤4): when the Round-3 guardrail triggers, it MUST offer the option "(b) 继续进入第 4 轮" (continue into round 4) rather than a hard stop. The guardrail means "escalate to human / give the user a choice", not "force-stop at round 3" — so D4's round 4 is reachable via that option. Independently of the guardrail, entering round 4 at all is gated by the shared **Round-4 user-choice escalation point** (`_shared/review-pipeline-phases.md` Phase D) — the choice is offered unconditionally at the round-3→4 transition, whether or not the guardrail's >50% condition fired.
|
||||
|
||||
### TOPO Page-Size Signal
|
||||
|
||||
The TOPO reviewer reads the `> 页尺寸自检: 超限` flag at the top of
|
||||
`{epic-slug}/dag`. If present, the TOPO reviewer records it as an **INFO
|
||||
finding** in the synthesis — summary = page-size over-limit, with the
|
||||
acceptance-criteria detail sunk to the `{subpages}` of the DAG. This INFO
|
||||
finding does NOT change any gate verdict and does NOT trigger re-derivation of
|
||||
the DAG.
|
||||
|
||||
### Output Artifacts
|
||||
|
||||
```
|
||||
<runs-root>/{epic-slug}/reviews/review-dag/
|
||||
└── round{N}/
|
||||
├── findings-{DIMENSION_CODE}.json # single-reviewer dimension (Tier 1 local, validated by reviewer-output.schema.json)
|
||||
└── findings-{DIMENSION_CODE}-r{k}.json # one per reviewer slot when dim_split > 1 (D3/D4) — reviewer_slot/reviewer_count set
|
||||
|
||||
Wiki: {epic-slug}/reviews/review-dag/
|
||||
├── round{N}/
|
||||
│ ├── task-analyst # Phase C dispatch prompt (wiki page)
|
||||
│ └── revision-summary # Analyst revision summary (wiki page)
|
||||
└── final/
|
||||
└── report # Final human-readable report (wiki page)
|
||||
|
||||
Ticket: #{issue_number}
|
||||
└── synthesis comment # Orchestrator synthesis (```json block, Tier 2) — dimensions key = TOPO / REQMAP / RELEASE
|
||||
|
||||
Commit status: context "pipeline/review-dag"
|
||||
```
|
||||
|
||||
### References
|
||||
|
||||
- `core/checklists/dag-single-gate.md` — The authoritative checklist for the 3 review dimensions (TOPO / REQMAP / RELEASE)
|
||||
- `core/schemas/` — Shared JSON schemas for reviewer output, synthesis, and status
|
||||
- IEEE 1028-2008 — Software Reviews and Audits
|
||||
- ISO/IEC/IEEE 24765:2017 — Systems and software engineering vocabulary
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- `core/checklists/audit-process.md` — Process audit dimensions
|
||||
- `core/checklists/dag-single-gate.md` — DAG single-gate review dimensions (TOPO / REQMAP / RELEASE)
|
||||
- `core/schemas/reviewer-output.schema.json` — Per-dimension reviewer findings
|
||||
- `core/schemas/synthesis.schema.json` — Orchestrator round synthesis
|
||||
- `core/schemas/review-status.schema.json` — Review lifecycle tracking
|
||||
- `core/templates/dag.md` — DAG artifact structure contract (review-dag target)
|
||||
- IEEE 1028-2008 — Software Reviews and Audits
|
||||
- ISO 19011:2018 — Guidelines for auditing management systems
|
||||
- ISO/IEC/IEEE 24765:2017 — Systems and software engineering vocabulary
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
# Code Review Dimensions
|
||||
|
||||
> Reference data for the 10 review dimensions. Read on-demand by the Orchestrator
|
||||
> when dispatching reviewers and filling per-dimension prompt values.
|
||||
|
||||
Each reviewer is assigned exactly **one** dimension at **L tier** (1 reviewer
|
||||
per dimension). At S/M tiers the risk-tier table groups dimensions into a
|
||||
single-reviewer sweep (S: all 10) or three grouped sweeps (M: COR+TST+DBT /
|
||||
DGN+SEC+PERF / STY+A11Y+DOC+TRC) — a reviewer covering a group inspects each
|
||||
dimension's checklist items listed below and nothing else (see
|
||||
`review-code/SKILL.md` "Risk Tier Assessment").
|
||||
|
||||
| # | Dimension | Label | Checklist Ref | Code Files to Review | Design Reference Files |
|
||||
|---|-----------|-------|---------------|----------------------|------------------------|
|
||||
| 1 | COR | Correctness, Error Handling & Compatibility | COR 1.1-1.17 | All changed code files | `{slug}/03-design-03-component-design-*`, `{slug}/03-design-04-interface-design` (error paths) |
|
||||
| 2 | DGN | Design Compliance & Dependencies | DGN 2.1-2.15 | All changed code files, package manifests | ALL design files |
|
||||
| 3 | SEC | Security | SEC 3.1-3.10 | Auth, input, data-handling | `{slug}/03-design-06-non-functional-design` (security section) |
|
||||
| 4 | PERF | Performance | PERF 4.1-4.10 | Loops, queries, I/O paths | `{slug}/03-design-06-non-functional-design` (performance section) |
|
||||
| 5 | TST | Test Quality | TST 5.1-5.8 | All test files | `{slug}/03-design-03-component-design-*` (expected behavior) |
|
||||
| 6 | STY | Style & Convention | STY 6.1-6.8 | All changed code files | (none — self-contained against project conventions) |
|
||||
| 7 | DBT | Database & Data | DBT 7.1-7.7 | Migrations, query code | `{slug}/03-design-05-data-design` |
|
||||
| 8 | A11Y | Accessibility & Browser Compatibility | A11Y 8.1-8.13 | UI components, markup | `{slug}/03-design-06-non-functional-design` (a11y section) |
|
||||
| 9 | DOC | Documentation | DOC 9.1-9.6 | All changed code files | `{slug}/03-design-03-component-design-*` (for cross-ref accuracy) |
|
||||
| 10 | TRC | Traceability | TRC 10.1-10.7 | All changed code files, tests | `{slug}/03-design-08-traceability`, work items (DAG task mode: the node `req_refs` + requirement registry rows in `{epic-slug}/dag` + node AC `test_id` declarations) |
|
||||
|
||||
> **DAG task mode**: the legacy `{slug}/03-design-*` Design Reference Files in
|
||||
> the rows above (COR/SEC/PERF/TST/DBT/A11Y/DOC) resolve from the frozen DAG
|
||||
> copy per the DAG-route read map — node spec + cross-session edge contracts
|
||||
> in `{epic-slug}/dag` + node ticket body (same remap as the TRC row above;
|
||||
> see `implement/SKILL.md` § Preconditions "DAG-route read map"d map").
|
||||
|
||||
**Rationale**: Single-dimension assignments prevent context explosion.
|
||||
A reviewer inspecting one dimension examines fewer checklist items and can
|
||||
read code files more deeply within the token budget. The 10 dimensions
|
||||
consolidate the previous 13: COR absorbs error handling (ERR) and
|
||||
compatibility (COR 1.15-1.17), DGN absorbs dependencies (DEP).
|
||||
|
||||
## Design Baseline
|
||||
|
||||
The approved design document at wiki page `{slug}/03-design-index` is the
|
||||
**authoritative baseline** for code correctness. Every reviewer receives the
|
||||
design `index.md` as a mandatory input for orientation.
|
||||
|
||||
Reviewers whose dimension involves design compliance (COR, DGN, SEC, PERF,
|
||||
TST, DBT, A11Y, DOC) additionally receive the relevant design sub-files listed
|
||||
above. Reviewers whose dimension is self-contained (STY) use the design baseline
|
||||
index only. (TRC is NOT self-contained — it reads `{slug}/03-design-08-traceability`
|
||||
and the work-item list to verify REQ references, per its row above; on the
|
||||
DAG route that archived page does not exist — TRC's declared input remaps to
|
||||
the node `req_refs` + requirement registry rows in `{epic-slug}/dag` and the
|
||||
node AC `test_id` declarations, mirroring the DGN remap in
|
||||
`review-code/SKILL.md` § DAG Task Mode. Historical `{slug}/03-design-*`
|
||||
pages stay readable via `_shared/gitea-read-patterns.md`.)
|
||||
@@ -0,0 +1,27 @@
|
||||
# Code Review Output Artifacts
|
||||
|
||||
> Output tree for code review artifacts. Read on-demand for reference.
|
||||
> Output model ([org-internal #1988] Tier 1 migration): Tier 1 local files (raw dimension findings) + Tier 2 wiki pages (task prompts, revision summaries, final reports) + Tier 2 synthesis comment. Status tracking via commit status `pipeline/review-code`.
|
||||
|
||||
```
|
||||
<runs-root>/{slug}/reviews/code/
|
||||
├── round{N}/
|
||||
│ ├── findings-{DIMENSION_CODE}.json # Reviewer dimension findings (Tier 1 local, validated by reviewer-output.schema.json)
|
||||
│ └── task-synthesizer.md # Synthesis prompt (Tier 1 local, passed to Synthesizer via prompt_file)
|
||||
|
||||
Ticket: #{issue_number}
|
||||
└── synthesis comment # Orchestrator synthesis (```json block, Tier 2)
|
||||
|
||||
Wiki: {slug}/reviews/code/
|
||||
├── round{N}/
|
||||
│ ├── task-developer # Phase C dispatch prompt (wiki page)
|
||||
│ └── revision-summary # Developer revision summary (wiki page)
|
||||
└── final/
|
||||
└── report # Final human-readable report (wiki page)
|
||||
|
||||
Commit status: context "pipeline/review-code"
|
||||
```
|
||||
|
||||
**Dimensions (10)**: COR, DGN, SEC, PERF, TST, STY, DBT, A11Y, DOC, TRC
|
||||
|
||||
See `code-review-dimensions.md` for mapping. ERR/DEP/CMP were merged into COR/DGN per the 13→10 consolidation.
|
||||
@@ -0,0 +1,510 @@
|
||||
// Mechanical-green precondition gate for review-code (#2598).
|
||||
//
|
||||
// Runs the CI-aligned checks BEFORE any reviewer is dispatched — at Phase A
|
||||
// entry and at every Phase D re-entry — so an LLM review round is never spent
|
||||
// on a diff a deterministic check would reject:
|
||||
// 1. oxlint — `bun oxlint --deny-warnings` (repo root)
|
||||
// 2. typecheck — `bun turbo typecheck` (repo root)
|
||||
// 3. test:changed — `cd packages/octopus && CI=true TEST_SHARDS=3 bun run
|
||||
// test:changed` (mirrors .gitea/workflows/ci.yml exactly)
|
||||
//
|
||||
// `test:parallel` is deliberately NOT a gate check — the full suite belongs
|
||||
// to verify (#2598 check-tiering).
|
||||
//
|
||||
// Known-failure waivers (#4380): a main-preexisting red can reach this gate
|
||||
// through test:changed's transitive import closure with zero causal link to
|
||||
// the diff under review (instance: #4296 N-01, TD-930 red via the `Config`
|
||||
// edge). Waivers are declared in `.octopus/known-failures.json` (tracked —
|
||||
// they ride PRs like code) as:
|
||||
//
|
||||
// {
|
||||
// "schema_version": 1,
|
||||
// "entries": [
|
||||
// {
|
||||
// "test": "packages/octopus/test/foo.test.ts::widget > broken",
|
||||
// "fingerprint": "<sha256 below>",
|
||||
// "evidence": "https://…/issues/4296#issuecomment-…", // REQUIRED
|
||||
// "reason": "one-line attribution",
|
||||
// "added_ts": "2026-09-07T00:00:00.000Z"
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
// - fingerprint = `--fingerprint` helper output = sha256("<file>\0<name>"),
|
||||
// where <file> is the repo-relative test file and <name> is the bun test
|
||||
// name exactly as printed in the `(fail)` line (describe chain joined
|
||||
// with " > ", timing suffix stripped).
|
||||
// - ALL failures of a `test:changed` run matched by fingerprint downgrade
|
||||
// that check to WARN: the gate passes (`blocked: false`) and the record
|
||||
// carries `waivers_applied` with the evidence URL. oxlint/typecheck
|
||||
// failures, timeouts, and unparsable runs are NEVER waivable (fail-closed).
|
||||
// - Invalidation is automatic — 指纹消失即失效: once the waived test no
|
||||
// longer fails while its file still ran (fix landed), the entry matches
|
||||
// nothing and the gate emits a `waiver-stale` warning + record entry;
|
||||
// physical removal rides the fix PR (the gate never rewrites repo files).
|
||||
//
|
||||
// Result cache: each run is keyed on a tree fingerprint (HEAD sha + tracked
|
||||
// diff + untracked file contents + the check-set). If the newest record for
|
||||
// this slug with the same fingerprint is green, the mechanical run is
|
||||
// skipped (cache hit) — the Developer pre-handoff gate (Phase C step 4) and
|
||||
// the Phase A/D dispatch gate collapse into ONE execution per tree state.
|
||||
// verify Phase 2.0 consumes the same green record as its mechanical-evidence
|
||||
// reuse source (eff-gate-cache: run this script; a cache-hit GREEN transfers
|
||||
// to verify's typecheck/lint DoD without a local re-run). Only GREEN results are ever reused; a red result always re-runs. The
|
||||
// fingerprint does NOT cover the environment (bun version, node_modules
|
||||
// state) — pass --no-cache to force a full re-run after an env change. A
|
||||
// waived green is cacheable like any other green: the waiver file is part of
|
||||
// the tree (tracked diff or untracked contents), so editing it changes the
|
||||
// fingerprint and forces a re-run.
|
||||
//
|
||||
// Usage (from the workflow worktree root):
|
||||
// bun .octopus/skills/review-code/scripts/precondition-gate.ts <slug> [--round N] [--only oxlint|typecheck|test:changed] [--no-cache]
|
||||
// bun .octopus/skills/review-code/scripts/precondition-gate.ts --fingerprint <repo-relative-test-file> "<bun test name>"
|
||||
//
|
||||
// Harness mode (tests only, GATE_* env precedent — see GATE_ARCHIVE_DIR):
|
||||
// GATE_CHECKS_JSON=<path> replace the check-set with a JSON array of
|
||||
// { name, command, cwd, timeoutMs?, env? }
|
||||
// GATE_WAIVER_FILE=<path> replace the default waiver file location
|
||||
// (<repoRoot>/.octopus/known-failures.json)
|
||||
//
|
||||
// Exit codes: 0 = green (dispatch reviewers); 1 = RED — a PRECONDITION-BLOCK
|
||||
// record has been appended to .octopus/runs/<slug>/reviews/code/precondition-gate.jsonl
|
||||
// (Tier 1); do NOT dispatch reviewers, hand the output to the Developer
|
||||
// (Phase C) and re-run after the fix. 2 = usage error.
|
||||
//
|
||||
// Every invocation (green, red, or cache hit) appends a record — per-check
|
||||
// durations feed the gate-latency telemetry (#2598 DoD).
|
||||
|
||||
import { spawnSync } from "node:child_process"
|
||||
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs"
|
||||
import { createHash } from "node:crypto"
|
||||
import { join } from "node:path"
|
||||
|
||||
// ---- Waiver types & helpers (#4380) ----
|
||||
|
||||
interface WaiverEntry {
|
||||
test: string // "<repo-relative file>::<bun test name>"
|
||||
fingerprint: string // sha256("<file>\0<name>")
|
||||
evidence: string // attribution URL (issue comment)
|
||||
reason?: string
|
||||
added_ts?: string
|
||||
}
|
||||
|
||||
interface WaiverLoad {
|
||||
entries: WaiverEntry[]
|
||||
warnings: string[]
|
||||
}
|
||||
|
||||
function testFingerprint(file: string, name: string): string {
|
||||
return createHash("sha256").update(`${file}\0${name}`).digest("hex")
|
||||
}
|
||||
|
||||
// Fail-closed loader: any malformation drops the affected entries (or the
|
||||
// whole file) with a warning — never grants an exemption it cannot verify.
|
||||
function loadWaivers(waiverPath: string): WaiverLoad {
|
||||
const warnings: string[] = []
|
||||
if (!existsSync(waiverPath)) return { entries: [], warnings }
|
||||
let parsed: unknown
|
||||
try {
|
||||
parsed = JSON.parse(readFileSync(waiverPath, "utf8"))
|
||||
} catch (e) {
|
||||
return {
|
||||
entries: [],
|
||||
warnings: [
|
||||
`malformed known-failures file (unparsable JSON) at ${waiverPath}: ${(e as Error).message} — treating as no waivers`,
|
||||
],
|
||||
}
|
||||
}
|
||||
const wf = parsed as { schema_version?: unknown; entries?: unknown }
|
||||
if (typeof wf.schema_version !== "number" || wf.schema_version !== 1) {
|
||||
return {
|
||||
entries: [],
|
||||
warnings: [`malformed known-failures file at ${waiverPath}: schema_version must be 1 — treating as no waivers`],
|
||||
}
|
||||
}
|
||||
if (!Array.isArray(wf.entries)) {
|
||||
return {
|
||||
entries: [],
|
||||
warnings: [`malformed known-failures file at ${waiverPath}: entries must be an array — treating as no waivers`],
|
||||
}
|
||||
}
|
||||
const entries: WaiverEntry[] = []
|
||||
for (const raw of wf.entries) {
|
||||
const e = raw as Partial<WaiverEntry>
|
||||
const desc = typeof e.test === "string" ? e.test : JSON.stringify(raw).slice(0, 120)
|
||||
if (typeof e.test !== "string" || !e.test.includes("::")) {
|
||||
warnings.push(`waiver entry ignored (test must be "<file>::<name>"): ${desc}`)
|
||||
continue
|
||||
}
|
||||
if (typeof e.fingerprint !== "string" || !/^[0-9a-f]{64}$/.test(e.fingerprint)) {
|
||||
warnings.push(`waiver entry ignored (fingerprint must be 64-hex — run --fingerprint): ${desc}`)
|
||||
continue
|
||||
}
|
||||
if (typeof e.evidence !== "string" || !/^https?:\/\//.test(e.evidence)) {
|
||||
warnings.push(`waiver entry ignored (evidence must be an issue/comment URL): ${desc}`)
|
||||
continue
|
||||
}
|
||||
entries.push({
|
||||
test: e.test,
|
||||
fingerprint: e.fingerprint,
|
||||
evidence: e.evidence,
|
||||
...(typeof e.reason === "string" ? { reason: e.reason } : {}),
|
||||
...(typeof e.added_ts === "string" ? { added_ts: e.added_ts } : {}),
|
||||
})
|
||||
}
|
||||
return { entries, warnings }
|
||||
}
|
||||
|
||||
// ---- bun test output parsing (formats verified against bun 1.3.14 non-TTY) ----
|
||||
//
|
||||
// stdout carries the test:changed selection list BEFORE execution:
|
||||
// test:changed: N test file(s) selected from M changed file(s):
|
||||
// packages/octopus/test/foo.test.ts
|
||||
// stderr carries per-file headers + failure lines (passing-only files and
|
||||
// (pass) lines leave no trace):
|
||||
// test/foo.test.ts:
|
||||
// (fail) widget > known broken thing [0.08ms]
|
||||
|
||||
interface ParsedFailure {
|
||||
file: string // repo-relative
|
||||
name: string
|
||||
fingerprint: string
|
||||
}
|
||||
|
||||
function parseSelection(stdoutText: string): string[] {
|
||||
const lines = stdoutText.split(/\r?\n/)
|
||||
const selected: string[] = []
|
||||
let inList = false
|
||||
for (const line of lines) {
|
||||
if (!inList) {
|
||||
if (/^test:changed: \d+ test file\(s\) selected from /.test(line)) inList = true
|
||||
continue
|
||||
}
|
||||
const m = line.match(/^ (\S+\.(?:test|spec)\.(?:ts|tsx))(?: \(isolated\))?$/)
|
||||
if (!m) break // the list ends at the first non-entry line
|
||||
selected.push(m[1]!)
|
||||
}
|
||||
return selected
|
||||
}
|
||||
|
||||
// bun prints file headers exactly as the path was passed on the CLI;
|
||||
// test:changed passes package-relative paths, so resolve against the
|
||||
// repo-relative selection list by unique suffix. Ambiguous/unresolvable
|
||||
// headers stay raw — a properly-authored waiver then misses, which is the
|
||||
// fail-closed direction.
|
||||
function resolveRepoRelative(header: string, selection: string[]): string {
|
||||
if (selection.includes(header)) return header
|
||||
const candidates = selection.filter((s) => s.endsWith(`/${header}`))
|
||||
return candidates.length === 1 ? candidates[0]! : header
|
||||
}
|
||||
|
||||
function parseFailures(outputText: string, selection: string[]): ParsedFailure[] {
|
||||
const byFp = new Map<string, ParsedFailure>()
|
||||
let header: string | null = null
|
||||
for (const line of outputText.split(/\r?\n/)) {
|
||||
const h = line.match(/^(\S+\.(?:test|spec)\.(?:ts|tsx)):\s*$/)
|
||||
if (h) {
|
||||
header = h[1]!
|
||||
continue
|
||||
}
|
||||
const f = line.match(/^\(fail\) (.+) \[[0-9.]+ms\]$/)
|
||||
if (f && header !== null) {
|
||||
const file = resolveRepoRelative(header, selection)
|
||||
const name = f[1]!
|
||||
byFp.set(testFingerprint(file, name), { file, name, fingerprint: testFingerprint(file, name) })
|
||||
}
|
||||
}
|
||||
return [...byFp.values()]
|
||||
}
|
||||
|
||||
// ---- CLI ----
|
||||
|
||||
const args = process.argv.slice(2)
|
||||
|
||||
const fpIdx = args.indexOf("--fingerprint")
|
||||
if (fpIdx >= 0) {
|
||||
const file = args[fpIdx + 1]
|
||||
const name = args[fpIdx + 2]
|
||||
if (!file || !name) {
|
||||
console.error(
|
||||
'usage: bun .octopus/skills/review-code/scripts/precondition-gate.ts --fingerprint <repo-relative-test-file> "<bun test name>"',
|
||||
)
|
||||
process.exit(2)
|
||||
}
|
||||
console.log(testFingerprint(file, name))
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
let slug = ""
|
||||
let round = 1
|
||||
let only: string | null = null
|
||||
let noCache = false
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === "--round") round = Number(args[++i] ?? 1)
|
||||
else if (args[i] === "--no-cache") noCache = true
|
||||
else if (args[i] === "--only") only = args[++i] ?? ""
|
||||
else slug = args[i]
|
||||
}
|
||||
if (!slug || !/^[a-zA-Z0-9._-]+$/.test(slug) || !Number.isFinite(round) || round < 1) {
|
||||
console.error(
|
||||
"usage: bun .octopus/skills/review-code/scripts/precondition-gate.ts <slug> [--round N] [--only oxlint|typecheck|test:changed] [--no-cache]",
|
||||
)
|
||||
console.error(
|
||||
' bun .octopus/skills/review-code/scripts/precondition-gate.ts --fingerprint <repo-relative-test-file> "<bun test name>"',
|
||||
)
|
||||
process.exit(2)
|
||||
}
|
||||
|
||||
const root = spawnSync("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8" })
|
||||
if (root.status !== 0 || !root.stdout) {
|
||||
console.error("precondition-gate: not inside a git worktree")
|
||||
process.exit(2)
|
||||
}
|
||||
const repoRoot = root.stdout.trim()
|
||||
|
||||
type Check = { name: string; command: string; cwd: string; env?: Record<string, string>; timeoutMs: number }
|
||||
const defaultChecks: Check[] = [
|
||||
{ name: "oxlint", command: "bun oxlint --deny-warnings", cwd: repoRoot, timeoutMs: 5 * 60_000 },
|
||||
{ name: "typecheck", command: "bun turbo typecheck", cwd: repoRoot, timeoutMs: 10 * 60_000 },
|
||||
{
|
||||
name: "test:changed",
|
||||
command: "bun run test:changed",
|
||||
cwd: join(repoRoot, "packages", "octopus"),
|
||||
env: { CI: "true", TEST_SHARDS: "3" }, // mirror ci.yml Test step
|
||||
timeoutMs: 20 * 60_000,
|
||||
},
|
||||
]
|
||||
|
||||
// Harness mode (#4380): GATE_CHECKS_JSON replaces the production check-set
|
||||
// with fixture commands (GATE_* env precedent). Unset in production.
|
||||
let allChecks: Check[] = defaultChecks
|
||||
if (process.env.GATE_CHECKS_JSON) {
|
||||
try {
|
||||
const parsed = JSON.parse(readFileSync(process.env.GATE_CHECKS_JSON, "utf8")) as unknown
|
||||
if (!Array.isArray(parsed) || parsed.length === 0) throw new Error("must be a non-empty JSON array")
|
||||
for (const c of parsed) {
|
||||
const chk = c as Partial<Check>
|
||||
if (typeof chk.name !== "string" || typeof chk.command !== "string" || typeof chk.cwd !== "string") {
|
||||
throw new Error("each check needs string name/command/cwd")
|
||||
}
|
||||
if (chk.timeoutMs !== undefined && typeof chk.timeoutMs !== "number")
|
||||
throw new Error("timeoutMs must be a number")
|
||||
if (chk.env !== undefined && typeof chk.env !== "object") throw new Error("env must be an object")
|
||||
}
|
||||
allChecks = parsed as Check[]
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`precondition-gate: invalid GATE_CHECKS_JSON (${process.env.GATE_CHECKS_JSON}): ${(e as Error).message}`,
|
||||
)
|
||||
process.exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
const checks = only ? allChecks.filter((c) => c.name === only) : allChecks
|
||||
if (checks.length === 0) {
|
||||
console.error(`--only must be one of: ${allChecks.map((c) => c.name).join(", ")}`)
|
||||
process.exit(2)
|
||||
}
|
||||
|
||||
const runsDir = join(repoRoot, ".octopus", "runs", slug, "reviews", "code")
|
||||
const gateLog = join(runsDir, "precondition-gate.jsonl")
|
||||
|
||||
const gitOut = (gitArgs: string[]): string => {
|
||||
const r = spawnSync("git", gitArgs, { encoding: "utf8", cwd: repoRoot })
|
||||
return r.status === 0 ? r.stdout : ""
|
||||
}
|
||||
|
||||
// Fingerprint = check-set + HEAD + tracked diff (staged+unstaged) + untracked
|
||||
// contents. Above the untracked cap the fingerprint is made unique so this
|
||||
// run can never cache-hit (fail-safe: always re-run).
|
||||
const UNTRACKED_CACHE_CAP = 500
|
||||
function treeFingerprint(): string {
|
||||
const h = createHash("sha256")
|
||||
h.update("gate-fingerprint-v1\n")
|
||||
h.update(checks.map((c) => c.name).join(",") + "\n")
|
||||
h.update(gitOut(["rev-parse", "HEAD"]))
|
||||
h.update("\0")
|
||||
h.update(gitOut(["diff", "HEAD"]))
|
||||
h.update("\0")
|
||||
const untracked = gitOut(["ls-files", "--others", "--exclude-standard"]).split(/\r?\n/).filter(Boolean)
|
||||
if (untracked.length > UNTRACKED_CACHE_CAP) return `nocache-${Date.now()}-${Math.random()}`
|
||||
const hashes = spawnSync("git", ["hash-object", "--stdin-paths"], {
|
||||
input: untracked.join("\n"),
|
||||
encoding: "utf8",
|
||||
cwd: repoRoot,
|
||||
})
|
||||
h.update(hashes.status === 0 ? hashes.stdout : "")
|
||||
return h.digest("hex")
|
||||
}
|
||||
|
||||
// Only a GREEN record with the identical fingerprint may skip a re-run; red
|
||||
// always re-runs (an environment-caused red must not wedge the gate until
|
||||
// the tree changes).
|
||||
function lastGreenHit(fp: string): { round: number; ts: string } | null {
|
||||
if (!existsSync(gateLog)) return null
|
||||
const lines = readFileSync(gateLog, "utf8")
|
||||
.split(/\r?\n/)
|
||||
.filter((l) => l.trim() !== "")
|
||||
for (let i = lines.length - 1; i >= 0; i--) {
|
||||
try {
|
||||
const r = JSON.parse(lines[i]!) as { fingerprint?: string; blocked?: boolean; round?: number; ts?: string }
|
||||
if (r.fingerprint === fp && r.blocked === false && typeof r.ts === "string") {
|
||||
return { round: r.round ?? 0, ts: r.ts }
|
||||
}
|
||||
} catch {
|
||||
// tolerate a malformed/truncated line — keep scanning
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const fingerprint = treeFingerprint()
|
||||
const hit = noCache ? null : lastGreenHit(fingerprint)
|
||||
|
||||
const waiverPath = process.env.GATE_WAIVER_FILE ?? join(repoRoot, ".octopus", "known-failures.json")
|
||||
const waivers = loadWaivers(waiverPath)
|
||||
|
||||
const record: Record<string, unknown> = {
|
||||
schema_version: 3,
|
||||
gate: "precondition-gate",
|
||||
ts: new Date().toISOString(),
|
||||
slug,
|
||||
round,
|
||||
fingerprint,
|
||||
cache_hit: hit !== null,
|
||||
checks: [] as Array<Record<string, unknown>>,
|
||||
blocked: false,
|
||||
}
|
||||
|
||||
if (hit) {
|
||||
record.checks = checks.map((c) => ({ name: c.name, skipped: true }))
|
||||
console.log(`PRECONDITION-GATE GREEN (cache hit — slug=${slug} round=${round})`)
|
||||
console.log(`Reusing green result from ${hit.ts} (round ${hit.round}) — identical tree fingerprint.`)
|
||||
} else {
|
||||
const tail = (s: string | null | undefined): string[] =>
|
||||
(s ?? "")
|
||||
.split(/\r?\n/)
|
||||
.filter((l) => l.trim() !== "")
|
||||
.slice(-15)
|
||||
|
||||
for (const w of waivers.warnings) console.log(`[waiver] WARN ${w}`)
|
||||
|
||||
let blocked = false
|
||||
for (const c of checks) {
|
||||
const started = Date.now()
|
||||
const r = spawnSync(c.command, {
|
||||
shell: true,
|
||||
cwd: c.cwd,
|
||||
encoding: "buffer",
|
||||
env: { ...process.env, ...c.env },
|
||||
timeout: c.timeoutMs,
|
||||
})
|
||||
const timedOut =
|
||||
r.error?.name === "TimeoutError" || (r.error as NodeJS.ErrnoException | undefined)?.code === "ETIMEDOUT"
|
||||
const exitCode = timedOut ? 124 : (r.status ?? 1)
|
||||
const failed = exitCode !== 0
|
||||
const outText = r.stdout?.toString() ?? ""
|
||||
const errText = r.stderr?.toString() ?? ""
|
||||
|
||||
// ---- waiver pass (#4380): test:changed only ----
|
||||
// A red is waivable only when every parsed failure matches a waiver
|
||||
// fingerprint. Timeouts and zero-failure reds (crash, harness gap, output
|
||||
// drift) stay blocking — no fingerprint to vouch for, fail closed.
|
||||
let waived = false
|
||||
let applied: WaiverEntry[] = []
|
||||
let failures: ParsedFailure[] = []
|
||||
let stale: WaiverEntry[] = []
|
||||
if (c.name === "test:changed") {
|
||||
const selection = parseSelection(outText)
|
||||
failures = parseFailures(`${outText}\n${errText}`, selection)
|
||||
if (failed && !timedOut && failures.length > 0 && waivers.entries.length > 0) {
|
||||
const fpSet = new Set(failures.map((f) => f.fingerprint))
|
||||
applied = waivers.entries.filter((e) => fpSet.has(e.fingerprint))
|
||||
waived = applied.length === failures.length
|
||||
}
|
||||
// 指纹消失即失效: the entry's file ran in this selection yet its
|
||||
// fingerprint is absent from the failures — the waiver is inert now.
|
||||
// Report only; removal rides the fix PR (the gate never rewrites
|
||||
// repo-tracked state).
|
||||
if (waivers.entries.length > 0 && selection.length > 0) {
|
||||
const failFps = new Set(failures.map((f) => f.fingerprint))
|
||||
stale = waivers.entries.filter((e) => {
|
||||
const file = e.test.split("::")[0]!
|
||||
return selection.includes(file) && !failFps.has(e.fingerprint)
|
||||
})
|
||||
}
|
||||
}
|
||||
if (failed && !waived) blocked = true
|
||||
|
||||
const errorLines = failed ? tail(errText || outText) : []
|
||||
;(record.checks as Array<Record<string, unknown>>).push({
|
||||
name: c.name,
|
||||
command: c.command,
|
||||
cwd: c.cwd.replace(/\\/g, "/"),
|
||||
exit_code: exitCode,
|
||||
timed_out: timedOut,
|
||||
...(failures.length > 0 ? { failures } : {}),
|
||||
...(waived ? { waived: true } : {}),
|
||||
duration_ms: Date.now() - started,
|
||||
...(errorLines.length > 0 ? { error_tail: errorLines } : {}),
|
||||
})
|
||||
if (applied.length > 0) {
|
||||
record.waivers_applied = [
|
||||
...((record.waivers_applied as WaiverEntry[]) ?? []),
|
||||
...applied.map((e) => ({ test: e.test, fingerprint: e.fingerprint, evidence: e.evidence })),
|
||||
]
|
||||
}
|
||||
if (stale.length > 0) {
|
||||
record.waivers_stale = [
|
||||
...((record.waivers_stale as WaiverEntry[]) ?? []),
|
||||
...stale.map((e) => ({ test: e.test, fingerprint: e.fingerprint })),
|
||||
]
|
||||
}
|
||||
|
||||
if (waived) {
|
||||
console.log(
|
||||
`[${c.name}] WARN (waived — ${applied.length} known failure(s) exempted) (${((Date.now() - started) / 1000).toFixed(1)}s)`,
|
||||
)
|
||||
for (const e of applied) console.log(` [waiver] ${e.test} — evidence: ${e.evidence}`)
|
||||
} else if (failed && applied.length > 0) {
|
||||
console.log(
|
||||
`[${c.name}] FAIL (${applied.length}/${failures.length} failure(s) waived — unmatched failures block the gate) (${((Date.now() - started) / 1000).toFixed(1)}s)`,
|
||||
)
|
||||
for (const e of applied) console.log(` [waiver] ${e.test} — evidence: ${e.evidence}`)
|
||||
} else {
|
||||
console.log(`[${c.name}] ${failed ? "FAIL" : "ok"} (${((Date.now() - started) / 1000).toFixed(1)}s)`)
|
||||
}
|
||||
for (const e of stale) {
|
||||
console.log(
|
||||
` [waiver-stale] ${e.test} — fingerprint absent from this run; entry is inert, remove it in the fix PR`,
|
||||
)
|
||||
}
|
||||
if (failed) for (const l of errorLines.slice(0, 8)) console.log(` ${l}`)
|
||||
}
|
||||
record.blocked = blocked
|
||||
}
|
||||
|
||||
try {
|
||||
mkdirSync(runsDir, { recursive: true })
|
||||
appendFileSync(gateLog, JSON.stringify(record) + "\n")
|
||||
} catch (e) {
|
||||
// The gate verdict must not depend on Tier 1 logging succeeding; surface but proceed.
|
||||
console.error(`[warn] could not append gate record: ${(e as Error).message}`)
|
||||
}
|
||||
|
||||
console.log()
|
||||
if (record.blocked) {
|
||||
console.log("PRECONDITION-BLOCK — mechanically red. Do NOT dispatch reviewers.")
|
||||
console.log("Route the failing check output above to the Developer (Phase C), fix, then re-run this gate.")
|
||||
process.exit(1)
|
||||
}
|
||||
const appliedCount = ((record.waivers_applied as WaiverEntry[]) ?? []).length
|
||||
if (hit) {
|
||||
// cache-hit path already printed above
|
||||
} else if (appliedCount > 0) {
|
||||
console.log(
|
||||
`PRECONDITION-GATE GREEN (slug=${slug} round=${round}) — ${appliedCount} known failure(s) waived; evidence in the gate record. Dispatch reviewers.`,
|
||||
)
|
||||
} else {
|
||||
console.log(`PRECONDITION-GATE GREEN (slug=${slug} round=${round}) — dispatch reviewers.`)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user