Initial publish v0.1.0: standalone workflow core (corpus + examples + guards)

This commit is contained in:
octopus
2026-09-15 08:41:51 +08:00
commit 663f43d9cd
114 changed files with 20240 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Octopus Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+50
View File
@@ -0,0 +1,50 @@
# octopus-workflow
Standalone workflow core extracted from the [octopus](https://eightarms.net/Octopus/octopus)
agent platform: a **provider-neutral corpus** of agent workflow skills, rules,
checklists, templates, and JSON schemas, plus the **drift guards** that keep
the corpus internally consistent and publish-ready.
The corpus does not assume any specific forge (GitHub / Gitea / GitLab),
ticketing, or CI system. System-specific behavior lives behind an explicit
adapter contract (`core/adapters/`), so the same core can be adopted by any
octopus instance — or any other agent platform — with its own adapters.
## Layout
| Path | Contents |
|---|---|
| `core/skills/` | 17 workflow skills (analyze-dag, implement, review-code, verify, release, ...) |
| `core/rules/` | Behavioral rule corpus (claim discipline, compaction, routing, artifact addressing, ...) |
| `core/checklists/` | Per-stage checklists consumed by the skills |
| `core/templates/` | Artifact templates (DAG, iteration docs) |
| `core/schemas/` | JSON schemas under the public namespace `https://eightarms.net/octopus-workflow/schemas/` |
| `core/adapters/` | Adapter contract: terminology SSOT + Gitea reference implementation |
| `core/CORE-MANIFEST.json` | Registry of core↔instance mappings and sync semantics (verbatim / verbatimDir / rewritten / split / core-only) |
| `examples/` | Organization-agnostic narrative essays referencing the core |
| `guards/` | Standalone drift guards (bun + node stdlib only) |
## Guards
Runnable inside this repo with [bun](https://bun.sh), no install step needed:
```bash
bun run check:core-cohesion # corpus structural invariants
bun run check:core-p1 # publish-readiness neutrality (no org-internal links)
bun run delink:core -- --check # no replaceable org-internal refs remain
```
The instance-side guards (`check-core-parity`, `check-schema-ids`,
`check-dangling-refs`, `core-sync.sh`) compare this corpus against a
consuming instance's `.octopus/` tree and live in the octopus platform
repo, not here.
## Versioning & compatibility
See [core/COMPATIBILITY.md](core/COMPATIBILITY.md) for the compatibility
matrix between core versions and consuming instances, and
[core/MIGRATION.md](core/MIGRATION.md) for adoption batches.
## License
[MIT](LICENSE)
+41
View File
@@ -0,0 +1,41 @@
# 兼容矩阵(COMPATIBILITY
Core 与 octopus 实例(dogfood)之间的兼容性契约。随各 Increment 更新;
自首个 semver 发布起,本表挂接 changelog(发布仓 `CHANGELOG.md`
条目以本表的行作为兼容性依据)。
## core 版本 × octopus 版本
| core 版本 | 对应 octopus 侧状态 | 兼容说明 |
| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 0.xIncrement 14:骨架 + G0G4 全量迁移) | octopus 实例 `.octopus/` 为消费镜像;core 与 dogfood 内容逐字/改写对齐 | core 是 SSOT 权威副本;dogfood 经 `script/core-sync.sh` 单向同步。schema 尚未迁入 core。 |
| 0.xIncrement 5schema $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 变更。
+431
View File
@@ -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"
}
]
}
+136
View File
@@ -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/verification6a 改写为 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`
## G6Increment 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 implementationfrontmatter 原样)。
manifestsync → rewritten(目录行保持目录路径)、batch → G6、deferHard
键全删。
- **守卫小扩展**parity/cohesion/dangling/core-sync 支持 rewritten 目录行
(存在性 = 目录存在;C-1 扫描 = 递归目录内 .md/.yaml);cohesion 默认
扫描面纳入 .yamlcore-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-p198 文本文件全树扫描;豁免 id-aliases.json
与 runs-index 历史命名空间声明;verbatim 镜像 2 处命中列 6b 递延)。
- 最终分类:71 mappings = 15 verbatim + 7 verbatimDir + 46 rewritten +
2 split + 1 core-only。
## G6bIncrement 6b:字节锁解锁 + EMBED 重指)
- **字节锁镜像翻转 rewritten**v3 去链接化契约规定 dogfood 侧保留活链接
→ 不能三侧同批 delink。18 个携带 refs/P1 命中的 verbatim/verbatimDir
镜像(13 个 verbatim 文件行 + 5 个 verbatimDir 目录行)翻转为
sync = "rewritten"、batch = "G6b"core 侧运行 delink60 处
#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 deferred6a 递延清单全部清偿)。
- 最终分类:71 mappings = 2 verbatim + 2 verbatimDir + 64 rewritten +
2 split + 1 core-only。
+116
View File
@@ -0,0 +1,116 @@
# octopus Core(工作流标准公共层)
`core/` 是工作流体系标准化后的**公共 Core**:自包含的规则、技能、
清单、模板、schema 与适配器层。`.octopus/` 下的对应文件是**消费副本**
dogfood 实例),`examples/` 仅承载叙事性示例。
## 不变量 C-1Core 不引用实例
- 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.jsonSCH-F401)与 runs-index 历史命名空间声明豁免。
Inc 6b 已清偿 6a 递延的 2 处旧 owner token 命中(0 递延)。
- **examples/ 方向性**:组织沉积教学案例(叙事),只引用 Core、不被 Core
引用(C-1 单向);不入 manifest、不进 cohesion 扫描面。
## 目录语义
| 目录 | 语义 |
| ---------------------- | ------------------------------------------------------- |
| `core/rules/` | L1 强制规则的 Core 源(14 份已全部迁入,G0G3) |
| `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 Schema8 份已迁入,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` 文件
(含 adaptersadapters 只查 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。
View File
+36
View File
@@ -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 |
| 工单评论 APIticket backend 评论接口) | `gitea_issue_comment__*` |
| wiki 读写 APITier-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 扫描剥离行内代码后本表零命中)。
+215
View File
@@ -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-statusREST 回退)
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` |
+123
View File
@@ -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-statusCLI
```
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 = 全部
success1 = 有 failure/error。
## Pattern 6: read-artifact-indexcompaction 恢复主路径)
压缩后或新会话从工单种子恢复时,读工件索引评论(写侧 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 仍有效);归档只改重读优先级,不改可访问性。
View File
+211
View File
@@ -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 / NANA=逐项"不适用"判定标记,不属于 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 流水线各阶段均有对应 skillanalyze-dag→review-dag 单门→implement→review-code→verify→release→retrospectivelegacy 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.11.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 + evidenceIEEE 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 横向可比) | ☐ | ☐ | |
+85
View File
@@ -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 | 复现置信度评级 15(≥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 | ☐ | ☐ | |
+198
View File
@@ -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 已修复或路由 DeveloperFalse 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 筛选(测试与工作项可交叉引用) | ☐ | ☐ | |
+59
View File
@@ -0,0 +1,59 @@
# 单门三维审查 checklistreview-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`(只有 DoDspec-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≤2D2≤3D3≤3D4≤4 |
| PASS | 三维均 0 BLOCKER 且 0 MAJOR |
| WARN | 0 BLOCKERMAJOR 在修订轮内关闭;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/ UNRESOLVEDMINOR/INFO 维视为通过不复跑。任一维修订改变**共享 DAG 形状(节点/边集合)或共享字段(`size_attrs`、AC 文本、契约内容)**时,以该字段为判据输入的相关维随轮重验(三维互为副作用面) |
| 输出 | 单门 synthesis(复用 `synthesis.schema.json``dimensions` 键为 TOPO/REQMAP/RELEASE+ commit status `pipeline/review-dag` |
+115
View File
@@ -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`(或项目等效命令)全部通过 | ☐ | ☐ | ☐ | |
+153
View File
@@ -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-1PRE-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.1legacy 设计模板 [org-internal #3072] phase 3 | ☐ | ☐ | ☐ | |
| PRE-16 | 设计文档覆盖了组件所需的全部交互行为:列表导航、焦点管理、键盘快捷键、展开/折叠、实时过滤(参见 `.octopus/archive/templates/design.md` §11.2legacy 设计模板 [org-internal #3072] phase 3 | ☐ | ☐ | ☐ | |
| PRE-17 | 设计文档覆盖了组件的全部可访问性要求:ARIA role/label、键盘可达、焦点环、对比度、色彩独立性(参见 `.octopus/archive/templates/design.md` §11.3legacy 设计模板 [org-internal #3072] phase 3 | ☐ | ☐ | ☐ | |
| PRE-18 | 设计文档已逐组件声明测试策略类型(render / source-verification / E2E / manual)。已知测试基础设施限制(Kobalte portal + happydom、路由上下文缺失等)已有对应替代方案标记(参见 `.octopus/archive/templates/design.md` §11.4legacy 设计模板 [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 版本变更返回类型或参数) | ☐ | ☐ | ☐ | |
+129
View File
@@ -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 skilltarget:
review-dag)直至收敛;DAG 副本缺失或未冻结 → 回到 `analyze-dag`
(分解 → 单门 → 冻结)后再重试。
## Upstream Artifact Existencelive — standalone modes
- [ ] standalone bugfixrepro notes wiki page `{slug}/repro-notes` 存在且含环境快照(via `wiki 读写 API(见 TERMINOLOGY`
- [ ] 验收条件可解析:来自请求/工单 body 或节点 AC(等价的验收条件文档,via `wiki 读写 API(见 TERMINOLOGY` / `工单 API(见 TERMINOLOGYget`
> Legacy[org-internal #3072] phase 32026-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(见 TERMINOLOGYlist(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 而非绕过
+159
View File
@@ -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` 无错误 | ☐ | ☐ | |
+59
View File
@@ -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 reportwiki `{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 registerwiki `{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 | ☐ | ☐ | ☐ | |
+64
View File
@@ -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 | 未引入新的运行时错误/异常路径 | ☐ | ☐ | |
+76
View File
@@ -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 | 版本号遵循 semverMAJOR.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 | 回到了原始分支 | ☐ | ☐ | |
+64
View File
@@ -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-flightgate-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 | 所有问题归因于流程/工具/信息不足,非个人能力 | ☐ | ☐ | |
+146
View File
@@ -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 issueFT-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 上也失败 → 预先存在(BFPhase 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 TriggerTD-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 issuelabel `baseline-failure`via `工单 API(见 TERMINOLOGYlist(labels="baseline-failure")` 可查) | ☐ | ☐ | |
| 9.2 | 每个 baseline-failure issue 含 `BF-NNN`(标题)、Test 标识、SeverityPriority 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 issuelabel `flaky-test`via `工单 API(见 TERMINOLOGYlist(labels="flaky-test")` 可查) | ☐ | ☐ | |
| 10.2 | 每个 flaky-test issue 含 `FT-NNN`(标题)、Test 标识、SeverityPriority 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" | ☐ | ☐ | |
+116
View File
@@ -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 可复用。
+38
View File
@@ -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.
+109
View File
@@ -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.
+56
View File
@@ -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.
+47
View File
@@ -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.
+112
View File
@@ -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.
+159
View File
@@ -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.
+72
View File
@@ -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.
+115
View File
@@ -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.
+113
View File
@@ -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].
+145
View File
@@ -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.
+98
View File
@@ -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-admissionTD-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.
+122
View File
@@ -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.
+11
View File
@@ -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.
+137
View File
@@ -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/FT5.55/5.56)不受影响。谁翻转:冲刺负责人(人),
在冲刺跟踪单记一行;开关值即 routing table 当前值,verify 每次运行时读取。
动机与细节:Tier-2 参考页(经 ticket backend`rules/workflow-routing-detail`
View File
+39
View File
@@ -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"
}
]
}
+56
View File
@@ -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"
}
}
}
}
}
}
+55
View File
@@ -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"
}
}
}
}
}
}
+113
View File
@@ -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"
}
}
}
+273
View File
@@ -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" } }
}
}
}
}
+122
View File
@@ -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."
}
}
}
}
}
+110
View File
@@ -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)."
}
}
}
+79
View File
@@ -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" }
}
}
}
}
}
+328
View File
@@ -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" }
}
}
}
}
}
}
View File
View File
@@ -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.
+35
View File
@@ -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 13 (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 铁律)"
+41
View File
@@ -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)"
+30
View File
@@ -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)"
+26
View File
@@ -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)"
+40
View File
@@ -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)"
+236
View File
@@ -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.
+503
View File
@@ -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 D1D4
```
- `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 | — | 13 | ≥4 |
| `contract_change_surface` | none | — | additive | breaking |
- `cross_session_edge_count`: 0 → D1; 13 → 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.73.10` / `EST 6.36.12` / `EXE 8.78.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)
+297
View File
@@ -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.
+213
View File
@@ -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.
+322
View File
@@ -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
+183
View File
@@ -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.
+547
View File
@@ -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 errorverify 字段回执);
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 13 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 3060 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:1816: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
3060 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(见 TERMINOLOGYget(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(见 TERMINOLOGYsearch(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(见 TERMINOLOGYget(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_asyncbody 携 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]).
+82
View File
@@ -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.
+350
View File
@@ -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; 13 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 16 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 13 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 B1B4 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 12), 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 13 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(见 TERMINOLOGYupdate`), 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 16 (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(见 TERMINOLOGYget`.
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(见 TERMINOLOGYupdate` 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(见 TERMINOLOGYcreate` → 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 0105 and 10 are in
`reference/source-analysis-templates.md` (read it when producing these
documents). Documents 0609 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 47 (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 A1A8, 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 47 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 16, 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 12.
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.
+203
View File
@@ -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.
+132
View File
@@ -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锛塦)
+99
View File
@@ -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
+447
View File
@@ -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
+762
View File
@@ -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; M2M5 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.
- 🟢 12 / 🟡 34 / 🔴 ≥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 M2M5.
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% / 🟡 3560% / 🔴 >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% / 🟡 1530% / 🔴 >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 / 🟡 310: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 / 🟡 12: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(见 TERMINOLOGYlist(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, D1D4 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 13 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"
}`,
)
}
+519
View File
@@ -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.11.7 | `core/skills/` tree, `checklists/`, `templates/`, `schemas/` listings |
| 2 | XREF | Cross-Reference Integrity | XREF 2.12.7 | ALL `SKILL.md`; resolve every referenced path |
| 3 | FM | Frontmatter Conformance | FM 3.13.7 | Frontmatter block of every `SKILL.md` |
| 4 | NAM | Naming Convention | NAM 4.14.6 | ALL `SKILL.md`, checklist filenames, schema enums |
| 5 | FLOW | Pipeline Cohesion | FLOW 5.15.6 | Preconditions + output sections of every `SKILL.md` |
| 6 | SCH | Schema Contract | SCH 6.16.5 | `core/schemas/*.json` + skills that reference them |
| 7 | DUP | Duplication & Drift | DUP 7.17.4 | ALL `SKILL.md` + ALL `checklists/*.md` |
| 8 | STD | Audit Standards (ISO/IEEE) | STD 8.18.7 | `review-*` skills, `review-artifact` (target: `audit-process`) itself |
| 9 | AGT | AGENTS.md Conformance | AGT 9.19.5 | ALL `SKILL.md` + `AGENTS.md` |
| 10 | TRC | Traceability | TRC 10.110.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 (D1D4) — 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` (D1D4), 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` (D1D4) 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:
- **D1D3** (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.

Some files were not shown because too many files have changed in this diff Show More