Initial publish v0.1.0: standalone workflow core (corpus + examples + guards)
This commit is contained in:
@@ -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.
|
||||
@@ -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)
|
||||
@@ -0,0 +1,41 @@
|
||||
# 兼容矩阵(COMPATIBILITY)
|
||||
|
||||
Core 与 octopus 实例(dogfood)之间的兼容性契约。随各 Increment 更新;
|
||||
自首个 semver 发布起,本表挂接 changelog(发布仓 `CHANGELOG.md` 的
|
||||
条目以本表的行作为兼容性依据)。
|
||||
|
||||
## core 版本 × octopus 版本
|
||||
|
||||
| core 版本 | 对应 octopus 侧状态 | 兼容说明 |
|
||||
| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 0.x(Increment 1–4:骨架 + G0–G4 全量迁移) | octopus 实例 `.octopus/` 为消费镜像;core 与 dogfood 内容逐字/改写对齐 | core 是 SSOT 权威副本;dogfood 经 `script/core-sync.sh` 单向同步。schema 尚未迁入 core。 |
|
||||
| 0.x(Increment 5:schema $id 迁移四件套) | dogfood `.octopus/schemas/*.json` 的 `$id` **保持旧命名空间不动**(活系统仍引用);core 侧 `$id` 已迁至 `https://eightarms.net/octopus-workflow/schemas/` | 双轨期:core schema 与 dogfood schema 是**不同 $id 的两份文件**(manifest 登记为 `rewritten`,不逐字同步)。活归档 run 的 index.json 携带旧 $id,读取侧经别名表解析。 |
|
||||
| 1.0.0(首个 semver 发布,规划中) | 发布仓创建;live 迁移(dogfood 侧切新命名空间)完成 | `id-aliases.json` 置 `frozen: true`——$id 一经发布不可变(v3 §G)。别名保留 ≥2 个 major 版本。 |
|
||||
|
||||
## builtin overlay 语料兼容
|
||||
|
||||
- 自 Increment 6b 起,builtin overlay(`builtin-data.gen.ts`)内嵌
|
||||
**中立版语料**(源权威 = `core/`);旧版内嵌本组织实例表述(含组织
|
||||
工单号活链接与实例 token)。overlay 仅在实例磁盘无 `<instance-root>/`
|
||||
目录时生效——已初始化实例(含本组织 dogfood)运行时行为不变。
|
||||
|
||||
## $id 行(schema 命名空间兼容)
|
||||
|
||||
- 旧命名空间 `$id`(历史形态 `https://eightarms.net/{legacy-owner}/octopus/.octopus/schemas/*.schema.json`)
|
||||
经 `core/schemas/id-aliases.json` 解析到新命名空间
|
||||
(`https://eightarms.net/octopus-workflow/schemas/*.schema.json`)。
|
||||
- **别名保留期 ≥2 个 major 版本**:自 frozen 起至少两个 major 版本内,
|
||||
旧 `$id` 仍被接受(`runs-index.schema.json` 的 `artifacts[].schema`
|
||||
字段声明该历史接受集)。
|
||||
- **twin token 归一记录**:scaffold-template 侧 schema `$id` 的 owner
|
||||
token(旧命名空间 owner → Octopus)差异经 `normalizeSchemaOwnerToken`
|
||||
(`script/check-scaffold-parity.ts`)归一化比较,双向等价;Increment 5
|
||||
已将残留旧 owner token 的 6 份模板 schema 统一为 Octopus token(纯
|
||||
一致性清理,归一化语义不变)。
|
||||
|
||||
## 后续 semver changelog 挂钩
|
||||
|
||||
- 首个 semver 发布(1.0.0)起,任何影响本表行的变更($id 变更、别名
|
||||
增删、兼容语义调整)必须在发布仓 changelog 中登记,并同步更新本表。
|
||||
- 别名表 `frozen: true` 后新增别名视为 minor 变更;删除别名须满足
|
||||
「≥2 个 major 版本保留期」并视为 major 变更。
|
||||
@@ -0,0 +1,431 @@
|
||||
{
|
||||
"version": 1,
|
||||
"mappings": [
|
||||
{
|
||||
"core": "core/rules/claim-discipline.md",
|
||||
"dogfood": ".octopus/rules/claim-discipline.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/stash-discipline.md",
|
||||
"dogfood": ".octopus/rules/stash-discipline.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/code-graph.md",
|
||||
"dogfood": ".octopus/rules/code-graph.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/compact.md",
|
||||
"dogfood": ".octopus/rules/compact.md",
|
||||
"batch": "G1",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/workflow-routing.md",
|
||||
"dogfood": ".octopus/rules/workflow-routing.md",
|
||||
"batch": "G1",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/auto-approve.md",
|
||||
"dogfood": ".octopus/rules/auto-approve.md",
|
||||
"batch": "G1",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/two-tier-artifacts.md",
|
||||
"dogfood": ".octopus/rules/two-tier-artifacts.md",
|
||||
"batch": "G1",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/ticket-lifecycle.md",
|
||||
"dogfood": ".octopus/rules/ticket-lifecycle.md",
|
||||
"batch": "G1",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/testing.md",
|
||||
"dogfood": ".octopus/rules/testing.md",
|
||||
"batch": "G1",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/artifact-addressing.md",
|
||||
"dogfood": null,
|
||||
"batch": "G2",
|
||||
"sync": "core-only"
|
||||
},
|
||||
{
|
||||
"core": "core/adapters/gitea/patterns.md",
|
||||
"dogfood": ".octopus/skills/_shared/gitea-write-patterns.md",
|
||||
"batch": "G2",
|
||||
"sync": "split"
|
||||
},
|
||||
{
|
||||
"core": "core/adapters/gitea/reading.md",
|
||||
"dogfood": ".octopus/skills/_shared/gitea-read-patterns.md",
|
||||
"batch": "G2",
|
||||
"sync": "split"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/issue-checklist-sync.md",
|
||||
"dogfood": ".octopus/rules/issue-checklist-sync.md",
|
||||
"batch": "G3",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/issue-cross-linking.md",
|
||||
"dogfood": ".octopus/rules/issue-cross-linking.md",
|
||||
"batch": "G3",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/artifact-index-guards.md",
|
||||
"dogfood": ".octopus/rules/artifact-index-guards.md",
|
||||
"batch": "G3",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/type-checking.md",
|
||||
"dogfood": ".octopus/rules/type-checking.md",
|
||||
"batch": "G3",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/rules/session-scope-guard.md",
|
||||
"dogfood": ".octopus/rules/session-scope-guard.md",
|
||||
"batch": "G3",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/analyze-dag/",
|
||||
"dogfood": ".octopus/skills/analyze-dag/",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/browser-debug/",
|
||||
"dogfood": ".octopus/skills/browser-debug/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/codegraph-setup/",
|
||||
"dogfood": ".octopus/skills/codegraph-setup/",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/frontend/",
|
||||
"dogfood": ".octopus/skills/frontend/",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/gitea-rest/",
|
||||
"dogfood": ".octopus/skills/gitea-rest/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/headless-session-ops/",
|
||||
"dogfood": ".octopus/skills/headless-session-ops/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/image-interpret/",
|
||||
"dogfood": ".octopus/skills/image-interpret/",
|
||||
"batch": "G4",
|
||||
"sync": "verbatimDir"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/implement/",
|
||||
"dogfood": ".octopus/skills/implement/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/land-batch/",
|
||||
"dogfood": ".octopus/skills/land-batch/",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/project-kickoff/",
|
||||
"dogfood": ".octopus/skills/project-kickoff/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/prototype/",
|
||||
"dogfood": ".octopus/skills/prototype/",
|
||||
"batch": "G4",
|
||||
"sync": "verbatimDir"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/release/",
|
||||
"dogfood": ".octopus/skills/release/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/retrospective/",
|
||||
"dogfood": ".octopus/skills/retrospective/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/review-artifact/",
|
||||
"dogfood": ".octopus/skills/review-artifact/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/review-code/",
|
||||
"dogfood": ".octopus/skills/review-code/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/verify/",
|
||||
"dogfood": ".octopus/skills/verify/",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/writing-skills/",
|
||||
"dogfood": ".octopus/skills/writing-skills/",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/compaction-round-boundary.md",
|
||||
"dogfood": ".octopus/skills/_shared/compaction-round-boundary.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/large-prompts.md",
|
||||
"dogfood": ".octopus/skills/_shared/large-prompts.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/review-final-report-template.md",
|
||||
"dogfood": ".octopus/skills/_shared/review-final-report-template.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/review-orchestrator-rules.md",
|
||||
"dogfood": ".octopus/skills/_shared/review-orchestrator-rules.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/review-pipeline-phases.md",
|
||||
"dogfood": ".octopus/skills/_shared/review-pipeline-phases.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/review-reviewer-prompt.md",
|
||||
"dogfood": ".octopus/skills/_shared/review-reviewer-prompt.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/review-revision-prompt.md",
|
||||
"dogfood": ".octopus/skills/_shared/review-revision-prompt.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/synthesis_task.md",
|
||||
"dogfood": ".octopus/skills/_shared/synthesis_task.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/worker-report-persistence.md",
|
||||
"dogfood": ".octopus/skills/_shared/worker-report-persistence.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/roles/coordinator.yaml",
|
||||
"dogfood": ".octopus/skills/_shared/roles/coordinator.yaml",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/roles/producer.yaml",
|
||||
"dogfood": ".octopus/skills/_shared/roles/producer.yaml",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/roles/reviewer.yaml",
|
||||
"dogfood": ".octopus/skills/_shared/roles/reviewer.yaml",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/roles/tool.yaml",
|
||||
"dogfood": ".octopus/skills/_shared/roles/tool.yaml",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/skills/_shared/roles/verifier.yaml",
|
||||
"dogfood": ".octopus/skills/_shared/roles/verifier.yaml",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/audit-process.md",
|
||||
"dogfood": ".octopus/checklists/audit-process.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/bugfix.md",
|
||||
"dogfood": ".octopus/checklists/bugfix.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/code-review.md",
|
||||
"dogfood": ".octopus/checklists/code-review.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/dag-single-gate.md",
|
||||
"dogfood": ".octopus/checklists/dag-single-gate.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/frontend.md",
|
||||
"dogfood": ".octopus/checklists/frontend.md",
|
||||
"batch": "G4",
|
||||
"sync": "verbatim"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/implementation.md",
|
||||
"dogfood": ".octopus/checklists/implementation.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/pipeline-gate.md",
|
||||
"dogfood": ".octopus/checklists/pipeline-gate.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/port.md",
|
||||
"dogfood": ".octopus/checklists/port.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/prototype.md",
|
||||
"dogfood": ".octopus/checklists/prototype.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/refactoring.md",
|
||||
"dogfood": ".octopus/checklists/refactoring.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/release.md",
|
||||
"dogfood": ".octopus/checklists/release.md",
|
||||
"batch": "G4",
|
||||
"sync": "verbatim"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/retrospective.md",
|
||||
"dogfood": ".octopus/checklists/retrospective.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/checklists/verification.md",
|
||||
"dogfood": ".octopus/checklists/verification.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/templates/dag.md",
|
||||
"dogfood": ".octopus/templates/dag.md",
|
||||
"batch": "G6b",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/templates/runs-layout.md",
|
||||
"dogfood": ".octopus/templates/runs-layout.md",
|
||||
"batch": "G6",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/port-analysis.schema.json",
|
||||
"dogfood": ".octopus/schemas/port-analysis.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/review-status.schema.json",
|
||||
"dogfood": ".octopus/schemas/review-status.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/reviewer-output.schema.json",
|
||||
"dogfood": ".octopus/schemas/reviewer-output.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/runs-bundle.schema.json",
|
||||
"dogfood": ".octopus/schemas/runs-bundle.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/runs-index.schema.json",
|
||||
"dogfood": ".octopus/schemas/runs-index.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/runs-meta.schema.json",
|
||||
"dogfood": ".octopus/schemas/runs-meta.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/synthesis.schema.json",
|
||||
"dogfood": ".octopus/schemas/synthesis.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
},
|
||||
{
|
||||
"core": "core/schemas/workflow-routing.schema.json",
|
||||
"dogfood": ".octopus/schemas/workflow-routing.schema.json",
|
||||
"batch": "G5",
|
||||
"sync": "rewritten"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
# Core 迁移映射表
|
||||
|
||||
编号保真(不变量 C-2):迁移仅改位置,不改编号。映射登记的运行时事实源
|
||||
是 `core/CORE-MANIFEST.json`;本表是批次级视图。
|
||||
|
||||
| 源路径 | Core 路径 | 批次 | 状态 |
|
||||
| --------------------------------------------------------- | ----------------------------------------------------- | ---- | ---------------------------------------------------------------------------------------- |
|
||||
| `.octopus/rules/claim-discipline.md` | `core/rules/claim-discipline.md` | G0 | 已迁移(逐字复制) |
|
||||
| `.octopus/rules/stash-discipline.md` | `core/rules/stash-discipline.md` | G0 | 已迁移(逐字复制) |
|
||||
| `.octopus/rules/code-graph.md` | `core/rules/code-graph.md` | G0 | 已迁移(逐字复制) |
|
||||
| `.octopus/rules/compact.md` | `core/rules/compact.md` | G1 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/workflow-routing.md` | `core/rules/workflow-routing.md` | G1 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/auto-approve.md` | `core/rules/auto-approve.md` | G1 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/two-tier-artifacts.md` | `core/rules/two-tier-artifacts.md` | G1 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/ticket-lifecycle.md` | `core/rules/ticket-lifecycle.md` | G1 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/testing.md` | `core/rules/testing.md` | G1 | 已迁移(接口中立化改写) |
|
||||
| (Core 原生,无 dogfood 源) | `core/rules/artifact-addressing.md` | G2 | 新增(Core 原生契约) |
|
||||
| `.octopus/skills/_shared/gitea-write-patterns.md` | `core/adapters/gitea/patterns.md` | G2 | 已拆分(adapter 拆分上提) |
|
||||
| `.octopus/skills/_shared/gitea-read-patterns.md` | `core/adapters/gitea/reading.md` | G2 | 已拆分(adapter 拆分上提) |
|
||||
| `.octopus/rules/issue-checklist-sync.md` | `core/rules/issue-checklist-sync.md` | G3 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/issue-cross-linking.md` | `core/rules/issue-cross-linking.md` | G3 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/artifact-index-guards.md` | `core/rules/artifact-index-guards.md` | G3 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/type-checking.md` | `core/rules/type-checking.md` | G3 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/rules/session-scope-guard.md` | `core/rules/session-scope-guard.md` | G3 | 已迁移(接口中立化改写) |
|
||||
| `.octopus/skills/analyze-dag/` | `core/skills/analyze-dag/` | G4 | 已迁移(整目录逐字) |
|
||||
| `.octopus/skills/browser-debug/` | `core/skills/browser-debug/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/codegraph-setup/` | `core/skills/codegraph-setup/` | G4 | 已迁移(整目录逐字) |
|
||||
| `.octopus/skills/frontend/` | `core/skills/frontend/` | G4 | 已迁移(整目录逐字) |
|
||||
| `.octopus/skills/gitea-rest/` | `core/skills/gitea-rest/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/headless-session-ops/` | `core/skills/headless-session-ops/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/image-interpret/` | `core/skills/image-interpret/` | G4 | 已迁移(整目录逐字) |
|
||||
| `.octopus/skills/implement/` | `core/skills/implement/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/land-batch/` | `core/skills/land-batch/` | G4 | 已迁移(整目录逐字) |
|
||||
| `.octopus/skills/project-kickoff/` | `core/skills/project-kickoff/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/prototype/` | `core/skills/prototype/` | G4 | 已迁移(整目录逐字) |
|
||||
| `.octopus/skills/release/` | `core/skills/release/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/retrospective/` | `core/skills/retrospective/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/review-artifact/` | `core/skills/review-artifact/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/review-code/` | `core/skills/review-code/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/verify/` | `core/skills/verify/` | G4 | 已迁移(整目录逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/writing-skills/` | `core/skills/writing-skills/` | G4 | 已迁移(整目录逐字) |
|
||||
| `.octopus/skills/_shared/compaction-round-boundary.md` | `core/skills/_shared/compaction-round-boundary.md` | G4 | 已迁移(逐字,protected 纪律载体;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/_shared/large-prompts.md` | `core/skills/_shared/large-prompts.md` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/review-final-report-template.md` | `core/skills/_shared/review-final-report-template.md` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/review-orchestrator-rules.md` | `core/skills/_shared/review-orchestrator-rules.md` | G4 | 已迁移(逐字;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/_shared/review-pipeline-phases.md` | `core/skills/_shared/review-pipeline-phases.md` | G4 | 已迁移(逐字,protected 纪律载体;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/_shared/review-reviewer-prompt.md` | `core/skills/_shared/review-reviewer-prompt.md` | G4 | 已迁移(逐字,protected 纪律载体;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/_shared/review-revision-prompt.md` | `core/skills/_shared/review-revision-prompt.md` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/synthesis_task.md` | `core/skills/_shared/synthesis_task.md` | G4 | 已迁移(逐字,protected 纪律载体;6a 改写为 rewritten) |
|
||||
| `.octopus/skills/_shared/worker-report-persistence.md` | `core/skills/_shared/worker-report-persistence.md` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/roles/coordinator.yaml` | `core/skills/_shared/roles/coordinator.yaml` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/roles/producer.yaml` | `core/skills/_shared/roles/producer.yaml` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/roles/reviewer.yaml` | `core/skills/_shared/roles/reviewer.yaml` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/roles/tool.yaml` | `core/skills/_shared/roles/tool.yaml` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/skills/_shared/roles/verifier.yaml` | `core/skills/_shared/roles/verifier.yaml` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/checklists/*.md`(13 份) | `core/checklists/` | G4 | 已迁移(逐字;audit-process/bugfix/pipeline-gate/port/prototype/verification;6a 改写为 rewritten) |
|
||||
| `.octopus/templates/*.md`(2 份) | `core/templates/` | G4 | 已迁移(逐字) |
|
||||
| `.octopus/schemas/port-analysis.schema.json` | `core/schemas/port-analysis.schema.json` | G5 | 已迁移($id 迁至公共命名空间) |
|
||||
| `.octopus/schemas/review-status.schema.json` | `core/schemas/review-status.schema.json` | G5 | 已迁移($id 迁至公共命名空间) |
|
||||
| `.octopus/schemas/reviewer-output.schema.json` | `core/schemas/reviewer-output.schema.json` | G5 | 已迁移($id 迁至公共命名空间) |
|
||||
| `.octopus/schemas/runs-bundle.schema.json` | `core/schemas/runs-bundle.schema.json` | G5 | 已迁移($id 迁至公共命名空间) |
|
||||
| `.octopus/schemas/runs-index.schema.json` | `core/schemas/runs-index.schema.json` | G5 | 已迁移($id 迁至公共命名空间;artifacts[].schema 声明历史接受集) |
|
||||
| `.octopus/schemas/runs-meta.schema.json` | `core/schemas/runs-meta.schema.json` | G5 | 已迁移($id 迁至公共命名空间) |
|
||||
| `.octopus/schemas/synthesis.schema.json` | `core/schemas/synthesis.schema.json` | G5 | 已迁移($id 迁至公共命名空间) |
|
||||
| `.octopus/schemas/workflow-routing.schema.json` | `core/schemas/workflow-routing.schema.json` | G5 | 已迁移($id 迁至公共命名空间) |
|
||||
| (待登记) | (待登记) | G5+ | 未开始 |
|
||||
|
||||
## G5 twin 归一记录
|
||||
|
||||
scaffold-template 侧(`packages/octopus/src/cli/cmd/scaffold-template/schemas/`)
|
||||
schema `$id` 的 owner token 归一(旧命名空间 owner token → Octopus,与既有 2 份多数对齐;
|
||||
`normalizeSchemaOwnerToken` 归一化下双向等价,纯一致性清理):
|
||||
|
||||
- `review-status.schema.json`
|
||||
- `reviewer-output.schema.json`
|
||||
- `runs-bundle.schema.json`
|
||||
- `runs-index.schema.json`
|
||||
- `synthesis.schema.json`
|
||||
- `workflow-routing.schema.json`
|
||||
|
||||
## 保护注记(protected 纪律载体)
|
||||
|
||||
以下四个 `_shared` 文件是多轮评审管线的纪律载体(round-boundary 压缩、
|
||||
评审提示词、综合任务),G4 仅逐字迁移不改内容;其改写留给 Increment 6
|
||||
(deferHard 标记已在 manifest 登记):
|
||||
|
||||
- `review-pipeline-phases.md`
|
||||
- `review-reviewer-prompt.md`
|
||||
- `synthesis_task.md`
|
||||
- `compaction-round-boundary.md`
|
||||
|
||||
## G6(Increment 6a:发布单元达标面)
|
||||
|
||||
- **deferHard 27 件 → rewritten**:10 个 verbatimDir 技能目录 + 17 个
|
||||
verbatim 文件(_shared md/yaml + 清单/模板)全部按 G1 风格中立化改写
|
||||
(机制/结构/frontmatter 保留;实例术语以占位符表述并指向
|
||||
core/adapters/TERMINOLOGY.md;技能内 .ts 脚本未动)。gitea-rest SKILL.md
|
||||
标注为 gitea adapter reference implementation(frontmatter 原样)。
|
||||
manifest:sync → rewritten(目录行保持目录路径)、batch → G6、deferHard
|
||||
键全删。
|
||||
- **守卫小扩展**:parity/cohesion/dangling/core-sync 支持 rewritten 目录行
|
||||
(存在性 = 目录存在;C-1 扫描 = 递归目录内 .md/.yaml);cohesion 默认
|
||||
扫描面纳入 .yaml;core-sync 对 rewritten 目录行报 managed-by 跳过。
|
||||
- **去链接化契约执行**:core/** 全树 #NNNN 活引用 → [org-internal #NNNN]
|
||||
注记(457 处、48 文件);delink:core 幂等;verbatim 镜像内 60 处递延
|
||||
至 6b。
|
||||
- **examples/ 建立**:4 篇组织沉积教学案例(sprint-mode 吞吐事故、
|
||||
preflight 证据闭环、WIP cap 与 TD 登记、评审轮次预算),只引用 Core
|
||||
不被引用。
|
||||
- **P1 终验门**:check:core-p1(98 文本文件全树扫描;豁免 id-aliases.json
|
||||
与 runs-index 历史命名空间声明;verbatim 镜像 2 处命中列 6b 递延)。
|
||||
- 最终分类:71 mappings = 15 verbatim + 7 verbatimDir + 46 rewritten +
|
||||
2 split + 1 core-only。
|
||||
|
||||
## G6b(Increment 6b:字节锁解锁 + EMBED 重指)
|
||||
|
||||
- **字节锁镜像翻转 rewritten**:v3 去链接化契约规定 dogfood 侧保留活链接
|
||||
→ 不能三侧同批 delink。18 个携带 refs/P1 命中的 verbatim/verbatimDir
|
||||
镜像(13 个 verbatim 文件行 + 5 个 verbatimDir 目录行)翻转为
|
||||
sync = "rewritten"、batch = "G6b";core 侧运行 delink(60 处
|
||||
#NNNN → [org-internal #NNNN])+ 两处旧 owner legacy-token 手工中立化
|
||||
(checklists/code-review.md 5.8 行、checklists/refactoring.md 4.7 行,
|
||||
改为不指名组织的 token 漂移表述);dogfood 侧不动(parity 的 rewritten
|
||||
语义 = 双侧存在 + core 模式扫描,无字节比较)。翻转账本的 md 链接改写
|
||||
为 core/ 与 <instance-root>/ 引用形态(analyze-dag/frontend/
|
||||
writing-skills 共 21 处路径去实例化)。
|
||||
- **EMBED 重指 core/**:generate-builtin.ts 的 EMBED_SOURCES 源权威从
|
||||
`<instance-root>/` 改为 core/(audit §G 承诺)。CLOSURE_PATTERN / META_SKILL_
|
||||
PREFIXES / closure 路径解析同步重指;service 键名(rules/auto-approve.md
|
||||
等)保持不变,运行时读取不受影响。builtin overlay 仅在无 <instance-root>/
|
||||
磁盘目录时生效——本组织恒有 → dogfood 运行时不变;shipped 默认语料自此
|
||||
为中立版(内嵌 [org-internal #NNNN] 注记、无组织实例 token)。
|
||||
- **终验归零**:delink-core --check 0 replaceable / 0 deferred;
|
||||
check-core-p1 0 hits / 0 deferred(6a 递延清单全部清偿)。
|
||||
- 最终分类:71 mappings = 2 verbatim + 2 verbatimDir + 64 rewritten +
|
||||
2 split + 1 core-only。
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
# octopus Core(工作流标准公共层)
|
||||
|
||||
`core/` 是工作流体系标准化后的**公共 Core**:自包含的规则、技能、
|
||||
清单、模板、schema 与适配器层。`.octopus/` 下的对应文件是**消费副本**
|
||||
(dogfood 实例),`examples/` 仅承载叙事性示例。
|
||||
|
||||
## 不变量 C-1:Core 不引用实例
|
||||
|
||||
- Core 内的工件**不得引用** `examples/` 或任何实例专有路径;`core/adapters/**`
|
||||
例外——adapter 目录承载后端绑定的 API 形态(工具名、端点),但仍
|
||||
**禁止实例机密与实例专有地址**(内网 IP、实例主机名、实例配置路径)。
|
||||
- 引用方向是**单向的**:`examples/` 与实例层可以引用 Core,Core 永远不
|
||||
回头引用它们。
|
||||
- 机械化校验:`script/check-core-cohesion.ts` 的 C-1 扫描——HARD 模式
|
||||
(实例机密/专有地址)对全部 core 生效;SOFT 模式(实例路径/工具名)
|
||||
对 `core/adapters/**` 豁免、其余 core 文件生效(见「增量采用」)。
|
||||
|
||||
## 不变量 C-2:迁移保编号
|
||||
|
||||
- 清单迁移**仅改位置、不改编号**:任何规则 / 技能 / 清单迁入 Core 时,
|
||||
其标识符(issue 编号、TD 编号、检查项编号)原样保留。
|
||||
- 编号是跨实例的稳定契约;重编号会切断历史追溯链。
|
||||
- 映射登记:`core/CORE-MANIFEST.json`(core 路径 ↔ dogfood 路径 ↔ 批次),
|
||||
编号保真映射表骨架见 `core/MIGRATION.md`。
|
||||
|
||||
## SSOT 契约(单一事实源)
|
||||
|
||||
- **Core = 全量权威副本**:规则、技能、\_shared、清单、模板已全部迁入,
|
||||
所有修改先落在 `core/`。**builtin overlay 源权威 = core/**(Increment 6b
|
||||
起,`packages/octopus/script/generate-builtin.ts` 的 EMBED_SOURCES 从
|
||||
core/ 读取——shipped 默认语料为中立版;仅当实例磁盘无
|
||||
`<instance-root>/` 目录时 overlay 才生效)。
|
||||
- **`.octopus/` = 消费镜像**:dogfood 实例通过 `script/core-sync.sh` 从
|
||||
Core 单向同步(verbatim / verbatimDir 自动逐文件同步;rewritten /
|
||||
split 按术语表落地),永不反向。
|
||||
- **`examples/` 仅叙事**:只引用 Core,不承载事实源内容。
|
||||
- 机械化守护:
|
||||
- `script/core-sync.sh` — 单向同步(`--check` 只检不写,CI 用);
|
||||
- `script/check-core-parity.ts` — 逐对比较 manifest 登记的文件内容,
|
||||
不一致即报漂移;
|
||||
- `script/check-core-cohesion.ts` — manifest 路径存在性 + C-1 扫描;
|
||||
- `script/check-dangling-refs.ts` — 双侧路径存在性 + core 内部引用
|
||||
完整性(intra-core 悬空检出;`--scope-report` 输出覆盖摘要);
|
||||
- `script/check-scaffold-parity.ts` — scaffold-template twin 逐字节
|
||||
校验(schema $id owner token 归一化比较);
|
||||
- `script/delink-core.ts` — 去链接化契约执行(#NNNN 活引用 → [org-internal #NNNN]
|
||||
注记;幂等;--check 零剩余才过;Inc 6b 字节锁镜像翻转 rewritten 后无递延);
|
||||
- `script/check-core-p1.ts` — P1 零残留终验(私网 IP / 实例主机名 / 个人
|
||||
身份 / 旧 owner token 全树 grep;豁免 id-aliases.json 与历史命名空间
|
||||
URL 声明;Inc 6b 起无字节锁递延);
|
||||
- `script/check-schema-ids.ts` — core schemas $id 命名空间/文件名匹配
|
||||
- id-aliases 一对一/存在性 + 历史接受集声明在位(完整 guards 表见
|
||||
文末「机械化守护(guards)」)。
|
||||
|
||||
## 发布单元达标(Increment 6a + 6b)
|
||||
|
||||
- **去链接化契约已执行**:core/** 全树 #NNNN 活引用已注记化
|
||||
([org-internal #NNNN]),delink:core 幂等可重跑,--check 为零剩余门。
|
||||
Inc 6b 已解锁 6a 递延的 60 处字节锁镜像引用(翻转 rewritten 后 core 侧
|
||||
全量注记化,0 递延)。
|
||||
- **P1 零残留终验门**:check:core-p1 扫 core/ 全部文本文件(md/yaml/
|
||||
json/ts),命中私网 IP、实例主机名、个人身份、旧 owner token 即 FAIL;
|
||||
id-aliases.json(SCH-F401)与 runs-index 历史命名空间声明豁免。
|
||||
Inc 6b 已清偿 6a 递延的 2 处旧 owner token 命中(0 递延)。
|
||||
- **examples/ 方向性**:组织沉积教学案例(叙事),只引用 Core、不被 Core
|
||||
引用(C-1 单向);不入 manifest、不进 cohesion 扫描面。
|
||||
|
||||
## 目录语义
|
||||
|
||||
| 目录 | 语义 |
|
||||
| ---------------------- | ------------------------------------------------------- |
|
||||
| `core/rules/` | L1 强制规则的 Core 源(14 份已全部迁入,G0–G3) |
|
||||
| `core/skills/` | 技能整目录的 Core 源(17 个已全部迁入,G4 verbatimDir) |
|
||||
| `core/skills/_shared/` | 技能共享工件(角色 yaml、评审管线文件等,14 份,G4) |
|
||||
| `core/checklists/` | 清单(13 份已全部迁入,G4) |
|
||||
| `core/templates/` | 模板(dag.md、runs 布局,2 份已全部迁入,G4) |
|
||||
| `core/schemas/` | JSON Schema(8 份已迁入,G5;$id 已迁至公共命名空间) |
|
||||
| `core/adapters/` | 实例适配层(后端参考实现;SOFT 扫描豁免、HARD 仍生效) |
|
||||
|
||||
## 增量采用
|
||||
|
||||
Core 的采用以 `core/CORE-MANIFEST.json` 登记为准——**登记了才算采用**。
|
||||
G0–G4 批次已完成全量迁移(14 规则、17 技能整目录、\_shared 14 份、
|
||||
13 清单、2 模板);后续批次按 `core/MIGRATION.md` 的映射表推进。
|
||||
|
||||
### C-1 扫描的增量形态(设计决策)
|
||||
|
||||
模式分两级(Increment 3):
|
||||
|
||||
- **HARD**(全 core 生效,含 adapters):实例主机名、内网 IP 段、实例
|
||||
配置路径——实例机密与实例专有地址任何 core 文件不得出现。
|
||||
- **SOFT**(`core/adapters/**` 豁免):`.octopus/`、`packages/octopus`、
|
||||
MCP 工具名、工作树路径等实例绑定内容——adapter 目录是 Gitea 参考
|
||||
实现,允许承载;其余 core 文件不得出现。
|
||||
|
||||
G0 三份规则是从实例规则**逐字复制**的,规则正文里出现实例路径字样属
|
||||
**叙述性引用**,且都在行内代码/围栏内(扫描先剥离,零命中)。
|
||||
脚本以 `--strict` 开关预留更严形态:`--strict` 时额外对 verbatim 文件
|
||||
按全模式集扫描(剥离语义同前)。默认扫描面 = core 下全部 `.md` 文件
|
||||
(含 adapters,adapters 只查 HARD)。扫描匹配前先剥离行内代码 span
|
||||
(反引号内文字)与围栏代码块——反引号内的是定义性提及(如本文件对
|
||||
C-1 模式列表的描述),不是活引用。
|
||||
|
||||
### manifest 语义(sync 字段)
|
||||
|
||||
| sync 值 | 语义 | parity 校验 | core-sync 行为 |
|
||||
| ------------- | --------------------------------------------------------------------- | ------------------------------------------------- | ------------------------------------ |
|
||||
| `verbatim` | 逐字复制 | 字节比较 | Core → dogfood 自动 `cp` |
|
||||
| `verbatimDir` | 整目录逐字复制(目录级行,core 路径以 `/` 结尾) | 递归文件集一致(排除 `.gitkeep`)+ 逐文件字节比较 | Core → dogfood 逐文件同步 |
|
||||
| `rewritten` | 接口中立化改写 | 双侧存在 + core 侧 HARD+SOFT 扫描 | `managed-by: rewrite (no auto-sync)` |
|
||||
| `split` | adapter 拆分上提(页名/寻址语义已上提 Core 契约,API 形态留 adapter) | 双侧存在 + core 侧 HARD 扫描(SOFT 豁免) | `managed-by: split (no auto-sync)` |
|
||||
| `core-only` | Core 原生契约,无 dogfood 对应(`dogfood: null`) | core 存在 + HARD+SOFT 扫描,无 dogfood 检查 | `managed-by: core-only` |
|
||||
|
||||
`deferHard: true`(历史字段,**Increment 6a 已全数清零**):曾标记暂含实例
|
||||
表述的行;6a 已将全部 27 行中立化改写为 rewritten 并删除该键。现
|
||||
manifest 中不存在 deferHard 行,cohesion `--strict` 的 deferred 计数为 0。
|
||||
@@ -0,0 +1,36 @@
|
||||
# 术语映射表(Core ↔ dogfood 实例)
|
||||
|
||||
> 本表是 Core 改写与 dogfood 实例对照的**唯一事实源**(v3 §E):Core 侧
|
||||
> 工件使用下表左列的中立术语,右列是本组织 dogfood 实例(Gitea 后端)的
|
||||
> 实际表述。任何 Core 工件需要落到本实例时,按本表做机械替换;反向
|
||||
> 改写(实例 → Core)同样以本表为准。新增映射必须先改本表,再改工件。
|
||||
|
||||
| Core 中立术语 | dogfood/Gitea 实例表述 |
|
||||
| --- | --- |
|
||||
| 工单后端(ticket backend) | Gitea |
|
||||
| Tier-2 工件库 | Gitea wiki |
|
||||
| 工单评论 API(ticket backend 评论接口) | `gitea_issue_comment__*` |
|
||||
| wiki 读写 API(Tier-2 工件库读写接口) | `gitea_wiki__*` |
|
||||
| 看板移动 API(工单看板列迁移接口) | `gitea_column__move_issue` |
|
||||
| 项目看板 API(工单面板管理接口) | `gitea_project__*` |
|
||||
| 路由解析工具(Step 0 路由门依赖) | `route_resolver` |
|
||||
| 会话工作树路径(并发分支工作区根) | `<worktrees-root>` |
|
||||
| 测试命令·变更集(本地前置检查) | `bun run test:changed` |
|
||||
| 测试命令·全量分片(交付前置 / nightly) | `bun run test:parallel` |
|
||||
| 类型检查命令 | `bun typecheck` |
|
||||
| 产品代码路径(harness 实现所在包) | `packages/octopus/src/...` |
|
||||
| 私化工单引用(组织内部工单号) | `#NNNN` 活链接(Core 侧用 `[org-internal #NNNN]` 注记) |
|
||||
| 实例规则目录(dogfood 消费副本所在) | `.octopus/`(如 `.octopus/rules/`) |
|
||||
| 运行工作区(Tier-1 本地工件) | `.octopus/runs/{slug}/` |
|
||||
| 实例配置文件(会话/管线配置) | `octopus.jsonc` |
|
||||
|
||||
## 备注
|
||||
|
||||
- **私化工单引用**:Core 侧不得产出指向具体实例的活链接 `#NNNN`(会被
|
||||
实例渲染为真实工单);一律写 `[org-internal #NNNN]` 注记格式,落地
|
||||
实例时按本表还原为 `#NNNN`。
|
||||
- **命令占位**:Core 规则正文中的 `<test-cmd:changed>`、
|
||||
`<test-cmd:parallel>` 等占位符按本表「测试命令」两行绑定到实例命令。
|
||||
- **裸词 Gitea**:仅当作为 adapter 名称出现时允许(如「Gitea adapter」);
|
||||
- 表内反引号包裹的实例表述仅供对照,不构成 Core 对实例的引用
|
||||
(C-1 扫描剥离行内代码后本表零命中)。
|
||||
@@ -0,0 +1,215 @@
|
||||
# Gitea Adapter — 写模式(Write Patterns)
|
||||
|
||||
> Gitea adapter 参考实现(Increment 3,自 dogfood 源
|
||||
> `_shared/gitea-write-patterns.md` 拆分上提)。本目录承载后端绑定的
|
||||
> API 形态(MCP 工具名、REST 端点、curl 形态);页名规范与寻址语义
|
||||
> 是后端中立契约,见 `core/rules/artifact-addressing.md`(本文不重复)。
|
||||
> 实例基址由实例配置提供(下文 `<gitea-base-url>`),见
|
||||
> `core/adapters/TERMINOLOGY.md`。
|
||||
|
||||
Owner/repo 固定为 `Octopus/octopus`。所有 wiki 页名遵循 Core 契约
|
||||
`{slug}/{type}-{seq:02d}-{title}`(例外页全枚举见 artifact-addressing.md)。
|
||||
|
||||
## Wiki URL 构造 — html_url 规则
|
||||
|
||||
**黄金规则:绝不手工拼接 wiki URL。** `gitea_wiki__create_page` /
|
||||
`gitea_wiki__get_page` / `gitea_wiki__list_pages` 响应中的 `html_url`
|
||||
字段是唯一权威链接,发布时捕获并原样复用。`page_name` → `html_url`
|
||||
的变换不可推导(`/`→`%2F`、含斜杠页名带 `.-` 尾缀、CJK 百分号编码),
|
||||
必须读 API。
|
||||
|
||||
**两种标识符勿混淆**:
|
||||
|
||||
| 标识符 | 是什么 | 用途 |
|
||||
| ----------- | ------------------------------------ | ---------------------------------------- |
|
||||
| `page_name` | 原始页标识;字面 `/`、无主机、无编码 | wiki MCP 工具的 `page_name`/`title` 参数 |
|
||||
| `html_url` | 后端生成的完整可点击 URL | markdown 链接、`target_url`、PR 正文 |
|
||||
|
||||
**去向**:
|
||||
|
||||
- **工件索引位置列** — 一个单元格同时存两者:
|
||||
``[`{page_name}`]({html_url})``。链接文本供读侧调
|
||||
`gitea_wiki__get_page`,href 供人点击(见 Pattern 10)。
|
||||
- **commit-status `target_url`** — 终报页的 `html_url`(见 Pattern 8)。
|
||||
- **页内交叉链接** — 用 `html_url`。
|
||||
|
||||
无 API 响应可用时(静态源串)用 `<gitea-base-url>`,且仅此一处来源。
|
||||
|
||||
## Pattern 1: create-wiki-page
|
||||
|
||||
```
|
||||
gitea_wiki__create_page(owner="Octopus", repo="octopus",
|
||||
title="{slug}/{type}-{seq:02d}-{title}",
|
||||
content="{内容}",
|
||||
message="{可选 commit message}")
|
||||
```
|
||||
|
||||
**发布→验证(强制)**:发布后立刻回读确认存在且内容一致:
|
||||
|
||||
```
|
||||
gitea_wiki__get_page(owner="Octopus", repo="octopus", page_name="{同 title}")
|
||||
```
|
||||
|
||||
404 / 内容不一致 → 修复后重发。页名冲突(409)→ 该页已存在,改用
|
||||
Pattern 2 update,绝不另发新页。响应的 `html_url` 立即捕获复用。
|
||||
|
||||
## Pattern 2: update-wiki-page
|
||||
|
||||
```
|
||||
gitea_wiki__update_page(owner="Octopus", repo="octopus",
|
||||
page_name="{页名}",
|
||||
content="{新内容}",
|
||||
message="{commit message}")
|
||||
```
|
||||
|
||||
更新后再回读验证;409 冲突 → 拉最新内容手工合并后重试。
|
||||
|
||||
## Pattern 3: create-issue
|
||||
|
||||
```
|
||||
gitea_issue__create(owner="Octopus", repo="octopus",
|
||||
title="{标题}", body="{正文}", labels=["{label}"])
|
||||
```
|
||||
|
||||
### 工单交叉链接(强制)
|
||||
|
||||
父子工单组必须双向链接:父工单 task list 引用 `#<number>`;子工单正文
|
||||
带 `## 父级 / Parent` 节引用父 `#<number>`。
|
||||
|
||||
### 衍生工单创建
|
||||
|
||||
- 技术债(verify Phase 5.5):`TD-NNN` 经分配台账取号后升票,`## Parent`
|
||||
指回登记册源工单。
|
||||
- 基线失败(Phase 5.55):label `baseline-failure` + `BF-NNN`(族伞签,
|
||||
按失败签名去重)。
|
||||
- 不稳定测试(Phase 5.56):label `flaky-test` + `FT-NNN`(同上)。
|
||||
|
||||
## Pattern 4: update-issue
|
||||
|
||||
```
|
||||
gitea_issue__update(owner="Octopus", repo="octopus",
|
||||
index={issue_number}, body="{正文}", state="{open|closed}", ...)
|
||||
```
|
||||
|
||||
原位更新正文(checklist 勾选、live 状态表维护);关闭工单即触发
|
||||
归档动作(见 artifact-addressing.md §4.3 + Pattern 10)。
|
||||
|
||||
## Pattern 5: add-issue-comment
|
||||
|
||||
```
|
||||
gitea_issue_comment__create(owner="Octopus", repo="octopus",
|
||||
index={issue_number}, body="{评论正文}")
|
||||
```
|
||||
|
||||
首次评论后捕获返回的 `comment_id`——后续对同一逻辑评论的更新必须走
|
||||
Pattern 6 原位 edit,绝不再 create。用于:评审综合(Synthesis)、
|
||||
状态备注、TD 登记、claim 认领。
|
||||
|
||||
## Pattern 6: edit-issue-comment
|
||||
|
||||
```
|
||||
gitea_issue_comment__edit(owner="Octopus", repo="octopus",
|
||||
comment_id={comment_id}, body="{新正文}")
|
||||
```
|
||||
|
||||
单评论聚合不变量(工件索引、当前状态表等)的执行手段。
|
||||
|
||||
## Pattern 7: move-issue-to-column
|
||||
|
||||
```
|
||||
gitea_column__move_issue(owner="Octopus", repo="octopus",
|
||||
project_id={project_id}, column_id={column_id}, index={issue_number})
|
||||
```
|
||||
|
||||
看板列迁移(Todo → In Progress → Review → Done)。
|
||||
|
||||
## Pattern 7.5: move-issue-to-pipeline-stage
|
||||
|
||||
管线阶段板列(Pipeline Stages board column)承载阶段迁移——阶段转移
|
||||
落到板列,**不落** `## 当前状态` 行(该表只承载 PR / 评审 / CI 行与
|
||||
非阶段阻塞项)。列序列按管线阶段定义;移动用 Pattern 7 同款
|
||||
`gitea_column__move_issue`,column 由 `gitea_project__list` /
|
||||
`gitea_column__list` 发现。
|
||||
|
||||
## Pattern 8: post-commit-status(REST 回退)
|
||||
|
||||
MCP 工具缺席时用 REST 直发 commit status(评审综合的 Tier-2 落点):
|
||||
|
||||
```bash
|
||||
curl -X POST "<gitea-base-url>/api/v1/repos/Octopus/octopus/statuses/{sha}" \
|
||||
-H "Authorization: token <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"context": "pipeline/review-{stage}",
|
||||
"state": "{success|failure|pending|error}",
|
||||
"target_url": "{html_url}",
|
||||
"description": "{≤140 chars 摘要}"
|
||||
}'
|
||||
```
|
||||
|
||||
context 公式:`pipeline/review-{stage}`(`code` / `review-dag` /
|
||||
`audit-process`)。token 从实例配置读取(此处 `<token>` 占位)。
|
||||
merge 前读回验证:`GET /commits/{PR_SHA}/status`。
|
||||
|
||||
## Pattern 9: create-iteration-board
|
||||
|
||||
```
|
||||
gitea_project__create(owner="Octopus", repo="octopus",
|
||||
title="{slug} — Iteration {N}", description="…")
|
||||
gitea_column__create(owner="Octopus", repo="octopus",
|
||||
project_id={project_id}, title="Todo")
|
||||
# … In Progress / Review / Done 同款
|
||||
```
|
||||
|
||||
DAG 聚合 agent 在单门 PASS 后建板;工单正文模板带 `## Node Reference`
|
||||
(指向 `{epic-slug}/dag`)、`## Acceptance Criteria`、`## Parent`。
|
||||
|
||||
## Pattern 10: artifact-index(工单 ↔ 工件索引)
|
||||
|
||||
技能发布工件后,在源工单维护 **`## 工件索引` 评论**——单一原位编辑的
|
||||
索引(反向链接 + compaction 恢复主路径;语义不变量见
|
||||
`core/rules/artifact-addressing.md` §4):
|
||||
|
||||
```
|
||||
# 1. 找源工单(PR body / commit 的 Closes #N,或 DAG 父映射);无则跳过
|
||||
# 2. 评论已存在?
|
||||
gitea_issue_comment__list(owner="Octopus", repo="octopus", index={issue_number})
|
||||
# → 扫 body 以 "## 工件索引" 开头的评论(遗留前缀 "## Pipeline 工件追踪表"
|
||||
# 原位升级,不重复发)
|
||||
# 3a. 不存在 → gitea_issue_comment__create 初始化
|
||||
# 3b. 存在 → gitea_issue_comment__edit 原位编辑(复用 comment_id)
|
||||
```
|
||||
|
||||
**索引表模板**(每工件一行;技能只增改自己的行,绝不删他技的行):
|
||||
|
||||
## 工件索引
|
||||
|
||||
slug: `{slug}` — source issue #{N}
|
||||
|
||||
| 工件 | 类型 | 版本 | 位置 | 重读 |
|
||||
|------|------|------|------|------|
|
||||
| DAG | 任务图 | v1 (frozen) | [`{epic-slug}/dag`]({html_url}) | CORE |
|
||||
|
||||
**位置列填充规则**:单元格 = markdown 链接 ``[`{page_name}`]({html_url})``;
|
||||
链接文本(page_name,字面 `/`)供读侧 `gitea_wiki__get_page`;href
|
||||
(html_url)供人点击,必须取自 API 响应,严禁拼接。
|
||||
|
||||
**重读优先级**:`CORE` = compaction 后必读(重读集 = 全部 CORE 行);
|
||||
`ON-Demand` → `ON-DEMAND` = 按需;`ARCHIVE` = 已归档不读。
|
||||
|
||||
**归档动作(archive-at-close)**:工单关闭时由关闭方 agent 原位 edit
|
||||
本评论——表格上方加归档横幅(`> **状态**: ✅ 已归档 — issue #{N} 关闭于
|
||||
{date}`)+ 全部行 重读 置 `ARCHIVE`;不删行、不改位置列、不发第二条
|
||||
评论。主路径 verify Phase 5.6;跳过 verify 的路由由关闭 agent 补执行。
|
||||
|
||||
**各技能行映射**:
|
||||
|
||||
| 技能 | 工件 ID | 位置 |
|
||||
| ------------------------- | ---------------------------- | ------------------------------------------------------------ |
|
||||
| `analyze-dag` | `DAG` | `{epic-slug}/dag` |
|
||||
| `review-artifact` | `REVIEW-{stage}` | `{slug}/reviews/{stage}/final/report` |
|
||||
| `review-code` | `REVIEW-code` | `{slug}/reviews/code/final/report` |
|
||||
| `review-code`(DAG task) | `REVIEW-code-task-{node-id}` | `{epic-slug}/reviews/code/final/report-task-{node-id}` |
|
||||
| `verify` | `VERIFY-{N}` | `{slug}/05-verify-iteration-{N}` |
|
||||
| `verify`(milestone) | `VERIFY-M-{M-id}` | `{epic-slug}/05-verify-milestone-{M-id}`(重读 `ON-DEMAND`) |
|
||||
| `verify`(DAG task) | `VERIFY-TASK-{node-id}` | `{epic-slug}/05-verify-task-{node-id}`(重读 `ON-DEMAND`) |
|
||||
@@ -0,0 +1,123 @@
|
||||
# Gitea Adapter — 读模式(Read Patterns)
|
||||
|
||||
> Gitea adapter 参考实现(Increment 3,自 dogfood 源
|
||||
> `_shared/gitea-read-patterns.md` 拆分上提)。页名规范与寻址语义见
|
||||
> `core/rules/artifact-addressing.md`(后端中立契约,本文不重复)。
|
||||
> 实例基址 `<gitea-base-url>` 由实例配置提供,见 TERMINOLOGY.md。
|
||||
|
||||
Owner/repo 固定 `Octopus/octopus`。compaction 恢复从 Pattern 6
|
||||
(读工件索引)开始——源工单上的工件索引是上下文恢复的规范入口,
|
||||
硬编码页名路径仅作回退。
|
||||
|
||||
## Pattern 1: read-wiki-page
|
||||
|
||||
```
|
||||
gitea_wiki__get_page(owner="Octopus", repo="octopus", page_name="{slug}/{page_title}")
|
||||
```
|
||||
|
||||
常用页名(全量文法 + 例外枚举见 `core/rules/artifact-addressing.md` §2):
|
||||
|
||||
| 工件 | page_name |
|
||||
| -------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| 验证报告 | `{slug}/05-verify-iteration-{N}` |
|
||||
| DAG 工件(单门) | `{epic-slug}/dag`;AC 子页 `{epic-slug}/dag-nodes/{node-id}`;覆盖子页 `{epic-slug}/dag-coverage` |
|
||||
| 评审终报(review-dag) | `{epic-slug}/reviews/review-dag/final/report` |
|
||||
| 评审终报(code) | `{slug}/reviews/code/final/report` |
|
||||
| 评审终报(code, DAG task) | `{epic-slug}/reviews/code/final/report-task-{node-id}` |
|
||||
| 验证(DAG milestone) | `{epic-slug}/05-verify-milestone-{M-id}` |
|
||||
| 验证(DAG task) | `{epic-slug}/05-verify-task-{node-id}` |
|
||||
| DAG 共享契约 | `{epic-slug}/shared/{file}` |
|
||||
| bugfix 附件 | `{slug}/repro-notes` · `{slug}/test-report` · `{slug}/bugfix-report` · `{slug}/ABORT` |
|
||||
| 原型/实现笔记 | `{slug}/prototype-debt` · `{slug}/spike-report` · `{slug}/impl-notes` |
|
||||
| 设计修订 | `{slug}/03-design-amendments` |
|
||||
| 审计工件 | `audit/{date}/{page}`,如 `audit/{date}/final/report` |
|
||||
| 移植工件 | `port-{name}/source-analysis/{file}` · `port-{name}/self-check` |
|
||||
| 回顾报告 | `_retrospectives/{cycle-name}` |
|
||||
| 回顾归档 | `_archive/{slug}/` |
|
||||
| 技能评估 | `_evals/{skill-name}/{page}` |
|
||||
| 浏览器调试证据 | `{slug}/verify/evidence/{name}`(临时截图留在工作区截图目录) |
|
||||
|
||||
历史只读页名(旧管线产物仍可读):`{slug}/01-stakeholder-interview`、
|
||||
`{slug}/02-requirements-index`、`{slug}/02-req-{seq:02d}-{title}`、
|
||||
`{slug}/02-03-req-design`、`{slug}/03-design-index`、
|
||||
`{slug}/03-design-{seq:02d}-{title}`、`{slug}/03-adr-{NNNN}-{title}`、
|
||||
`{slug}/04-plan-index`、`{slug}/04-plan-{seq:02d}-{title}`、
|
||||
`{slug}/roadmap/{page}`、`{slug}/shared/{file}`、
|
||||
`{slug}/reviews/{roadmap|design-space|plan}/final/report`。
|
||||
|
||||
**本地回退(wiki MCP 工具不可用)**:读本地 wiki 克隆(实例路径见
|
||||
TERMINOLOGY.md)。wiki 文件名 URL 编码(`/`→`%2F`,`.md` 前可能带
|
||||
`.-`),用 glob 而非固定路径解析:
|
||||
|
||||
```
|
||||
glob(pattern="*{slug}*{page-title}*.md", path="<local-wiki-clone>/{repo}-wiki/")
|
||||
```
|
||||
|
||||
## Pattern 2: read-issue
|
||||
|
||||
```
|
||||
gitea_issue__get(owner="Octopus", repo="octopus", index={issue_number})
|
||||
```
|
||||
|
||||
读工单详情、正文、元数据(label、assignee、milestone、state)。
|
||||
|
||||
## Pattern 3: read-issue-comments
|
||||
|
||||
```
|
||||
gitea_issue_comment__list(owner="Octopus", repo="octopus", index={issue_number})
|
||||
```
|
||||
|
||||
读工单全部评论(评审结果、状态备注、讨论线程)。
|
||||
|
||||
## Pattern 4: read-project-column-issues
|
||||
|
||||
```
|
||||
gitea_column__list_issues(owner="Octopus", repo="octopus",
|
||||
project_id={project_id}, column_id={column_id})
|
||||
```
|
||||
|
||||
看板列工单队列。project/column 发现:`gitea_project__list` +
|
||||
`gitea_column__list`。
|
||||
|
||||
## Pattern 5: read-review-status(CLI)
|
||||
|
||||
```
|
||||
octopus review status [--stage <stage>] [--sha <sha>] [--json]
|
||||
```
|
||||
|
||||
读评审收敛状态(commit status)。stage 值:`code`、`review-dag`、
|
||||
`audit-process`;省略 `--stage` 列出该提交全部状态。历史 stage 值
|
||||
(`design-space` 等)仅可查历史。
|
||||
|
||||
**stage-id → 评审目录映射**(Tier-1 本地 `reviews/{stage}/` 段):
|
||||
`review-code` → `code`;`review-dag` → `review-dag`;
|
||||
`audit-process` → `audit-process`。只有 code 评审用短名,不得发明其他。
|
||||
|
||||
收敛判定:context `pipeline/review-{stage}` 的 status 存在且
|
||||
state = `success`(= PASS 或 WARN,无未决 BLOCKER)。退出码 0 = 全部
|
||||
success,1 = 有 failure/error。
|
||||
|
||||
## Pattern 6: read-artifact-index(compaction 恢复主路径)
|
||||
|
||||
压缩后或新会话从工单种子恢复时,读工件索引评论(写侧 Pattern 10)
|
||||
而非猜测页名路径:
|
||||
|
||||
```
|
||||
gitea_issue__get(owner="Octopus", repo="octopus", index={issue_number})
|
||||
gitea_issue_comment__list(owner="Octopus", repo="octopus", index={issue_number})
|
||||
# 找 body 以 "## 工件索引"(遗留 "## Pipeline 工件追踪表")开头的评论
|
||||
gitea_wiki__get_page(owner="Octopus", repo="octopus", page_name=page_name)
|
||||
```
|
||||
|
||||
**位置列提取规则**:单元格形如 ``[`{page_name}`]({html_url})`` ——
|
||||
取**链接文本**(首个 `[` 与 `]` 之间、剥反引号)作为 page_name,不是
|
||||
href(含 `%2F`/`.-`,API 不认)。裸 page_name 单元格(无链接包裹)也
|
||||
接受:整格即 page_name。形如 `#comment-{id}` 的值改用
|
||||
`gitea_issue_comment__list` 读,不得喂给 `gitea_wiki__get_page`。
|
||||
|
||||
**重读规则**:重读集 = 全部 `CORE` 行;`ON-DEMAND` 行仅当前任务需要
|
||||
时读;绝不读 `ARCHIVE` 行。索引缺失(历史工单)→ 回退 issue body +
|
||||
`## 当前状态` + 硬编码页名路径。
|
||||
|
||||
**已归档索引**:无 `CORE` 行,恢复时重读集为空,按需直读目标工件
|
||||
(位置列 URL 仍有效);归档只改重读优先级,不改可访问性。
|
||||
@@ -0,0 +1,211 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# 流程审计检查清单(Process Audit Checklist)
|
||||
|
||||
> 用于审计 `<instance-root>/` 流程基础设施本身的完整性、一致性与合规性。
|
||||
> 审计对象是这套 SDLC「工厂」本身(skills / checklists / templates / schemas),
|
||||
> 而非某个具体项目对流程的遵循情况(后者归 `retrospective`)。
|
||||
>
|
||||
> 依据:内部一致性规则、ISO 19011:2018(审核指南)、IEEE 1028-2008(软件评审与审计)、
|
||||
> 以及本仓库 `AGENTS.md` 工程约定。
|
||||
|
||||
> **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TRC)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TRC=Traceability vs 本清单的 TRC=Traceability 审计维度)。跨清单引用时必须带清单限定(如 audit-process.md TRC 10.3),不得使用裸代码。
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. 审计对象是 `<instance-root>/` 目录下的 `skills/`、`checklists/`、`templates/`、`schemas/`;
|
||||
2. 每个审计维度由一名独立审计员(Explorer,只读)负责,逐项判定 PASS / FAIL / NA(NA=逐项"不适用"判定标记,不属于 findings JSON 的维度 verdict 枚举 PASS/WARN/FAIL/UNRESOLVED——UNRESOLVED 专指审计员崩溃/超时状态,勿混用);
|
||||
3. 对 FAIL 项必须给出:具体文件位置、引用证据、可执行的修复建议;
|
||||
4. 审计员必须保持**独立性**(ISO 19011:2018):仅依据文件事实判定,不臆测意图;
|
||||
5. 任一维度存在 FAIL 项须修订后重新审计,直至收敛。
|
||||
|
||||
**Known exceptions to INV 1.3** (inline self-check checklists — skills whose
|
||||
checklist obligations are embedded in the SKILL.md body by design):
|
||||
- `core/skills/writing-skills/SKILL.md` §"Authoring checklist" — methodology
|
||||
skill, inline RED/GREEN/REFACTOR self-check is its normative form.
|
||||
- `core/skills/codegraph-setup/SKILL.md` §"Verification checklist" —
|
||||
setup/installation skill, inline verification checklist is its normative form.
|
||||
- `core/skills/project-kickoff/SKILL.md` §"4. Workflow" step 4 (**Post-kickoff verification**) —
|
||||
setup/lifecycle skill; its self-check is the `octopus kickoff --check-only`
|
||||
tool's 5 readiness items, which is the tool-based equivalent of an inline
|
||||
checklist (analogous to codegraph-setup's CLI verification), not a
|
||||
`core/checklists/` artifact checklist.
|
||||
- `core/skills/analyze-dag/SKILL.md` — pipeline Producer (DAG route entry
|
||||
skill). Its producer-side self-check obligations are folded inline into the
|
||||
SKILL.md body (Topology Constraints §, Page-Size Budget, Requirement
|
||||
Registry, Exec-Resource Configuration) as the mechanically-checkable
|
||||
destination of the folded plan-checklist rows; the **gate-side** coverage is
|
||||
`core/checklists/dag-single-gate.md` (TOPO/REQMAP/RELEASE), not a
|
||||
producer self-check checklist. A thin `analyze-dag.md` wrapper would add no
|
||||
value over the in-skill rows + the single-gate checklist.
|
||||
- `core/skills/land-batch/SKILL.md` — merge-pr stage skill (batch PR
|
||||
landing). Its self-check obligations are inline by design: the fail-closed
|
||||
`## Preconditions (all mandatory, fail-closed)` block and the
|
||||
"Pre-validate locally (never enter CI red)" rung are the normative gates,
|
||||
backstopped by CI merge-gate mechanical checks — same doctrine as
|
||||
project-kickoff's tool-based self-check; a thin wrapper checklist would
|
||||
add no value.
|
||||
- Tool/utility & integration skills — `browser-debug`,
|
||||
`gitea-rest`, `image-interpret`, `headless-session-ops` — are
|
||||
non-artifact-producing (they drive tools / sessions / MCP servers, not SDLC
|
||||
pipeline artifacts), so INV 1.3's "产物型 skill" clause does not apply to
|
||||
them; their operational obligations are inline normative statements (e.g.
|
||||
`browser-debug`'s session-cleanup / screenshot-naming iron rules) by the same
|
||||
doctrine as the listed exemptions above.
|
||||
These are exempted per `audit/2026-08-11/round1/revision-summary` (writing-skills,
|
||||
codegraph-setup), `audit/2026-08-13/round1/revision-summary` (project-kickoff),
|
||||
`audit/2026-08-17/round1/revision-summary` (analyze-dag), and
|
||||
`audit/2026-09-01/round1/revision-summary` (land-batch);
|
||||
creating thin wrapper checklist files would add no value.
|
||||
|
||||
---
|
||||
|
||||
## 1. 清单完整性(INV — Inventory Completeness)
|
||||
|
||||
> 流水线阶段齐全、无孤儿文件。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 1.1 | SDLC 流水线各阶段均有对应 skill(analyze-dag→review-dag 单门→implement→review-code→verify→release→retrospective;legacy roadmap/requirements/design/plan 已归档 [org-internal #3072] phase 3,见 `<instance-root>/archive/`) | ☐ | ☐ | |
|
||||
| 1.2 | 每个 `skills/<name>/` 目录恰好包含一个 `SKILL.md`(例外:`_shared/` 目录不含 `SKILL.md`,为共享引用文档目录) | ☐ | ☐ | |
|
||||
| 1.3 | 每个产物型 skill 都有配套 `checklists/<name>.md`(无清单的自检要求即为缺口) | ☐ | ☐ | |
|
||||
| 1.4 | `checklists/` 中无孤儿清单(存在清单但无任何 skill 引用) | ☐ | ☐ | |
|
||||
| 1.5 | `templates/` 中无孤儿模板(存在模板但无任何 skill 引用) | ☐ | ☐ | |
|
||||
| 1.6 | `schemas/` 中无孤儿 schema(存在 schema 但无任何 skill 引用) | ☐ | ☐ | |
|
||||
| 1.7 | 每个 review-* skill 的维度数量与其引用清单的章节数量一致,或存在文档化的合并说明(每节恰好被一个维度覆盖) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 2. 交叉引用完整性(XREF — Cross-Reference Integrity)
|
||||
|
||||
> skill ↔ checklist ↔ template ↔ schema 的引用路径不得断裂。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 2.1 | skill 中引用的每个 `core/checklists/*.md` 路径真实存在 | ☐ | ☐ | |
|
||||
| 2.2 | skill 中引用的每个 `core/templates/*.md` 路径真实存在 | ☐ | ☐ | |
|
||||
| 2.3 | skill 中引用的每个 `core/schemas/*.json` 路径真实存在 | ☐ | ☐ | |
|
||||
| 2.4 | skill 中引用的 wiki `{slug}/...` 路径结构与其它 skill 一致;引用模式见 `_shared/gitea-read-patterns.md` / `_shared/gitea-write-patterns.md` | ☐ | ☐ | |
|
||||
| 2.5 | 清单中引用的标准编号(IEEE/ISO)在 skill References 中亦有呼应 | ☐ | ☐ | |
|
||||
| 2.6 | skill 之间相互引用的前序/后继阶段名称真实存在(无断链) | ☐ | ☐ | |
|
||||
| 2.7 | 引用的检查项编号(如 `ARCH 1.1–1.7`)在目标清单中确实存在 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 元数据合规性(FM — Frontmatter Conformance)
|
||||
|
||||
> 每个 SKILL.md 的 frontmatter 必须符合 octopus skill 规范。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 3.1 | `name` 存在、小写连字符、≤64 字符,且与所在目录名一致 | ☐ | ☐ | |
|
||||
| 3.2 | `description` 存在且非空(缺失会被加载器过滤、永不触发) | ☐ | ☐ | |
|
||||
| 3.3 | `description` 同时说明「做什么」与「何时触发」 | ☐ | ☐ | |
|
||||
| 3.4 | `description` 使用第三人称("Use when...",而非 "I help...") | ☐ | ☐ | |
|
||||
| 3.5 | 需要静默于相邻话题的 skill 使用了 "Use ONLY when..." 或 "Use ONLY after..." 限定 | ☐ | ☐ | |
|
||||
| 3.6 | `triggers`(如有)为关键词/文件名,前置了用户可能说出的字面词 | ☐ | ☐ | |
|
||||
| 3.7 | frontmatter 无未知顶层字段,YAML 可解析 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 命名约定一致性(NAM — Naming Convention)
|
||||
|
||||
> 跨流程基础设施的命名必须统一。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 4.1 | 清单文件名使用产物名词形式(如 `implementation.md`、`code-review.md`),skill 名使用动词形式或标准 SDLC 产物名(如 `implement`、`review-code`;`frontend`、`writing-skills` 等按产物/领域命名的 skill,以及按工具命名的集成类 skill(`gitea-rest`、`codegraph-setup`)除外),共享清单以产物名命名(如 `verification.md`、`port.md`;已归档的 legacy 清单见 `<instance-root>/archive/checklists/`) | ☐ | ☐ | |
|
||||
| 4.2 | 维度代码(如 ARCH / SEC / TRC)在 skill 与清单间拼写一致 | ☐ | ☐ | |
|
||||
| 4.3 | 角色名称与 core/skills/_shared/roles/*.yaml 的 name 字段一致(Producer/Reviewer/Verifier/Tool/Coordinator)。旧角色名(Developer/Analyst/Architect 等)由 role.ts 中的 ROLE_ALIASES 安全映射,不再需要独立 YAML。不得使用已废弃的角色名(如 'Organizer')。`escalation` 字段除可指向上述注册角色外,亦可指向有效的 subagent_type(如 'Builder',其作为运行时可加载的构建型子代理类型) | ☐ | ☐ | |
|
||||
| 4.4 | 产物路径片段(wiki `{slug}/...`)命名跨 skill 统一;引用模式见 `_shared/gitea-read-patterns.md` / `_shared/gitea-write-patterns.md`。审计类产物使用日期 slug(如 wiki `audit/{date}/`)属允许例外 | ☐ | ☐ | |
|
||||
| 4.5 | 严重度等级(BLOCKER/MAJOR/MINOR/INFO)跨 skill 与 schema 一致 | ☐ | ☐ | 允许例外:`port-analysis.schema.json` 使用三值契约(BLOCKER/MAJOR/MINOR,永不产出 INFO),已在 schema description 中自证为对四值集的文档化例外 |
|
||||
| 4.6 | 裁决值(PASS/WARN/FAIL)跨 skill 与 schema 一致 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 流水线衔接(FLOW — Pipeline Cohesion)
|
||||
|
||||
> 阶段之间的前置条件与产物链必须闭合,无断裂。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 5.1 | 每个 skill 的 Preconditions 引用的前序产物,确由某个上游 skill 产出 | ☐ | ☐ | |
|
||||
| 5.2 | 每个 skill 的产物,确被某个下游 skill 作为输入消费(终点除外:verify、release、retrospective,以及发布/副作用类 skill 如 `review-artifact` (target: process),其产出由人类或外部系统消费) | ☐ | ☐ | |
|
||||
| 5.3 | review-* skill 的输入路径与其对应生产 skill 的输出路径精确匹配(代码评审输入为项目源码树中 git diff 标识的文件;其余评审输入为 wiki `{slug}/` 下的文档,读取模式见 `_shared/gitea-read-patterns.md`;process 审计目标为第三输入类——本地 `<instance-root>/` 语料,无上游生产 skill,自产自审) | ☐ | ☐ | |
|
||||
| 5.4 | 收敛/审批关卡(如「Do NOT advance without approval」)在阶段切换处存在 | ☐ | ☐ | |
|
||||
| 5.5 | 阶段顺序无环(不存在 A 依赖 B 同时 B 依赖 A) | ☐ | ☐ | |
|
||||
| 5.6 | 每个 skill 声明的角色与模型分配在同类 skill 间一致 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 6. Schema 契约(SCH — Schema Contract)
|
||||
|
||||
> schema 被正确引用,且 skill 描述的字段与 schema 定义对齐。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 6.1 | skill 中要求写入的 JSON 产物均指明了对应的 `core/schemas/*.json` | ☐ | ☐ | 允许例外:(a) `precondition-gate.jsonl`(review-code/verify)为自版本化、机器可再生的 JSONL 记录,有意无 backing schema;(b) browser-debug 证据包(`browser/{session-id}/manifest.json` 等)由冻结共享契约 `browser-evidence-4486/shared/pack-manifest-v1` 等校验(写入方 `<harness-package>/src/browser/evidence-pack.ts`),不在 `core/schemas/` 下 |
|
||||
| 6.2 | skill 文中描述的字段名与 schema 的 `required`/`properties` 一致 | ☐ | ☐ | |
|
||||
| 6.3 | skill 描述的枚举值(verdict/severity)与 schema `enum` 一致 | ☐ | ☐ | |
|
||||
| 6.4 | 每个 schema 自身合法(`$schema`、`$id`、`type` 齐备) | ☐ | ☐ | |
|
||||
| 6.5 | 多个 skill 共用同一 schema 时语义一致(无相互冲突的字段约定) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 7. 重复与漂移(DUP — Duplication & Drift)
|
||||
|
||||
> 单一事实来源;术语不漂移。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 7.1 | 同一概念在不同 skill 中术语一致(无同义词漂移) | ☐ | ☐ | |
|
||||
| 7.2 | 检查项/规则无跨文件的实质性重复(应集中于清单而非散落于 skill) | ☐ | ☐ | |
|
||||
| 7.3 | 收敛准则(max_rounds、停止条件)在各 review-* skill 间一致或有理由不一致 | ☐ | ☐ | |
|
||||
| 7.4 | 标准编号引用一致(同一标准不出现多种写法) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 8. 审计原则合规(STD — Audit Standards / ISO 19011:2018 · IEEE 1028-2008)
|
||||
|
||||
> 体现独立性、客观证据、分级判定、可重复。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 8.1 | 审计员独立性:review-* skill 明确审计员只读、不得越权(ISO 19011:2018 §4) | ☐ | ☐ | |
|
||||
| 8.2 | 客观证据:findings 要求 location + evidence(IEEE 1028-2008 证据留痕) | ☐ | ☐ | |
|
||||
| 8.3 | 分级判定:严重度与裁决规则量化(pass_rate 阈值明确,非主观) | ☐ | ☐ | |
|
||||
| 8.4 | 可重复:审计员 prompt 标准化、温度低(确定性输出) | ☐ | ☐ | |
|
||||
| 8.5 | 防范范围收窄:明令禁止「只看重点/从简」类弱化措辞(IEEE 1028-2008 完整性) | ☐ | ☐ | |
|
||||
| 8.6 | 留痕:审计产物落盘到结构化文件,而非仅存于对话 | ☐ | ☐ | |
|
||||
| 8.7 | 闭环:存在重审循环与收敛/审批终止条件(ISO 19011:2018 跟踪与关闭) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 9. 工程约定符合度(AGT — AGENTS.md Conformance)
|
||||
|
||||
> 流程基础设施所描述/示例的工程做法须符合本仓库 `AGENTS.md` 及其**委托的约定源**:
|
||||
> `AGENTS.md` 将工程约定委托给 L1 `core/rules/*`(注入主会话)与 L2 wiki `rules/*`
|
||||
> (按需读取),故本维各项的「基线」是 `AGENTS.md` 委托到的实际约定文档(下各条注明)。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 9.1 | skill 示例命令与测试约定一致(基线:`core/rules/testing.md` — `bun run test:parallel`、从 package 目录运行、勿从 repo 根运行) | ☐ | ☐ | |
|
||||
| 9.2 | skill 示例的类型检查命令与 `core/rules/type-checking.md` 一致(package 目录 `bun typecheck`,非直接 `tsc`;repo 根整仓为 `bun turbo typecheck`) | ☐ | ☐ | |
|
||||
| 9.3 | skill 引用的 DB/迁移流程与 L2 wiki `rules/database`(Drizzle、`bun run db generate`)一致 | ☐ | ☐ | |
|
||||
| 9.4 | skill 描述的模块形态与 L2 wiki `rules/style-guide`(无 `export namespace`、自再导出)一致 | ☐ | ☐ | |
|
||||
| 9.5 | skill 描述的 Effect 用法与 L2 wiki `rules/effect-rules` 一致(如适用) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 10. 可追溯性(TRC — Traceability)
|
||||
|
||||
> 从需求到验证全链路可追溯;审计自身亦可追溯。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 10.1 | 存在贯穿全流水线的追溯机制(需求 ID → 设计 → 实现 → 验证) | ☐ | ☐ | |
|
||||
| 10.2 | 每个 review-* skill 都包含 Traceability 维度或等价检查 | ☐ | ☐ | |
|
||||
| 10.3 | 审计 finding 的 ID 规则唯一且可定位到具体检查项 | ☐ | ☐ | |
|
||||
| 10.4 | 审计历史(轮次、裁决、blocker/major 数)被记录于 status 产物 | ☐ | ☐ | |
|
||||
| 10.5 | 模型/角色分配变更可在 skill 中追溯(同类 skill 横向可比) | ☐ | ☐ | |
|
||||
@@ -0,0 +1,85 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Bugfix 检查清单
|
||||
|
||||
> 开发者在修复 Bug 时自检使用。确保修复的是根因而非症状,且不引入新问题。
|
||||
|
||||
> **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TST/BEH/GATE/REG)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TST=Test Quality、verification.md 的 TST=Test Suite Completeness、port.md 的 TST=Test Porting)。跨清单引用时必须带清单限定(如 code-review.md TST 5.8),不得使用裸代码。
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. Phase 1-4 每阶段结束时检查对应项;
|
||||
2. Phase 5 完成后检查全部项;
|
||||
3. 所有项通过后方可提交。
|
||||
|
||||
---
|
||||
|
||||
## 1. 复现确认(REPRO — Reproduction)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------- | ---- | ------ | ---- |
|
||||
| 1.1 | Bug 可以稳定复现(有明确的复现步骤) | ☐ | ☐ | |
|
||||
| 1.2 | 实际行为与 Bug 报告一致 | ☐ | ☐ | |
|
||||
| 1.3 | 如果无法复现,已向用户反馈原因并等待补充信息 | ☐ | ☐ | |
|
||||
| 1.4 | 已捕获环境快照(`ps aux`、`lsof -p PID`、日志文件大小、进程树 — pipeline 模式强制,standalone 建议) | ☐ | ☐ | |
|
||||
| 1.5 | 复现置信度评级 1–5(≥3 方可进入 pipeline 设计阶段;<3 → abort) | ☐ | ☐ | |
|
||||
| 1.6 | 已检索 Gitea wiki `{slug}/repro-notes` / `{slug}/bugfix-report` 中是否存在同类症状的先前调查报告,复用已知根因而非重新推导(via `wiki 读写 API(见 TERMINOLOGY)`) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 2. 根因分析(ROOT — Root Cause)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------ | ---- | ------ | ---- |
|
||||
| 2.1 | 根因已定位到具体代码行(非模块级别) | ☐ | ☐ | |
|
||||
| 2.2 | 根因是底层逻辑缺陷,非表面症状 | ☐ | ☐ | |
|
||||
| 2.3 | 如果根因来自数据/环境/配置,已注明具体差异 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 回归测试(TEST — Regression Test)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 3.1 | 已编写针对性的回归测试 | ☐ | ☐ | |
|
||||
| 3.2 | 回归测试在修复前 FAIL(确认覆盖了 Bug) | ☐ | ☐ | |
|
||||
| 3.3 | 回归测试在修复后 PASS | ☐ | ☐ | |
|
||||
| 3.4 | 如果无法编写自动化测试,已标注 `[flaky]` 并说明原因 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 修复质量(FIX — Fix Quality)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------- | ---- | ------ | ---- |
|
||||
| 4.1 | 修复是最小变更——只改了必须改的部分 | ☐ | ☐ | |
|
||||
| 4.2 | 未混入重构、风格调整或不相关的"顺手改" | ☐ | ☐ | |
|
||||
| 4.3 | 修复的是根因而非症状 | ☐ | ☐ | |
|
||||
| 4.4 | 同一模块的已有测试全部通过(修复未引入退化) | ☐ | ☐ | |
|
||||
| 4.5 | Commit 消息使用常规提交前缀(`fix:` / `refactor:` / `docs:`)并位于 `[{chunk-id}][{iteration}]` 之后 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 全面回归(REG — Full Regression)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------- | ---- | ------ | ---- |
|
||||
| 5.1 | `bun run test:parallel` 全部通过(零失败) | ☐ | ☐ | |
|
||||
| 5.2 | `bun typecheck` 无错误 | ☐ | ☐ | |
|
||||
| 5.3 | `bun lint` 无错误 | ☐ | ☐ | |
|
||||
| 5.4 | 如果有集成/端到端测试,已运行并全部通过 | ☐ | ☐ | |
|
||||
| 5.5 | 移交评审前已按 `core/checklists/code-review.md` 的 A11Y(§8)与 PERF(§4)节自检本次变更,并对全部改动文件跑 `bunx prettier --check`(pre-commit 钩子只扫提交时的 staged 文件,CR-COMMIT 把提交推迟到评审收敛后,移交前的工作区漂移没有任何钩子拦截——出处:sticky-diff-error 复盘 action item 2) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 6. 闭环(CLOSURE — Fix Linked to Commit)
|
||||
|
||||
> 防止"幽灵 artifact":bugfix 报告已写出但代码修复从未提交/合并,导致 bug
|
||||
> 复发并重复调查。参见 `core/skills/retrospective/SKILL.md` 了解该规则的
|
||||
> 复盘出处。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------- | ---- | ------ | ---- |
|
||||
| 6.1 | standalone bugfix 的 `{slug}/bugfix-report` 必须在同一周期内有**已合并提交**背书;若仅调查未应用修复,必须显式标注 "investigation only — no fix applied" | ☐ | ☐ | |
|
||||
| 6.2 | 若本次修复取代了先前的同类 `{slug}/bugfix-report`,已在该先前报告顶部标注 SUPERSEDED 并指向新 PR | ☐ | ☐ | |
|
||||
@@ -0,0 +1,198 @@
|
||||
# 代码评审检查清单
|
||||
|
||||
> 用于评审代码的正确性、设计一致性、安全性和可维护性。
|
||||
> 依据 IEEE 1028-2008 和行业最佳实践。
|
||||
|
||||
> **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TST/BEH/GATE/REG)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TST=Test Quality、verification.md 的 TST=Test Suite Completeness、port.md 的 TST=Test Porting)。跨清单引用时必须带清单限定(如 code-review.md TST 5.8),不得使用裸代码。
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. 阅读待评审的全部代码文件;
|
||||
2. 同时阅读设计文档中的相关章节作为参考基线;
|
||||
3. 逐项检查,判定为"通过"或"不通过";
|
||||
4. 对"不通过"项注明具体文件名、行号和问题描述;
|
||||
5. 任一检查维度中若存在"不通过"项,须修订后重新评审;
|
||||
6. 所有维度均通过后方可合并或发布。
|
||||
7. 第 2 轮及以后的评审中,任何仅属外观性的 MINOR 发现(命名、注释、死代码、导入顺序、风格等不影响正确性或可读性的问题)必须满足其一:已修复并关闭,或在 synthesis 中以 `WAIVED-{id}` 条目显式豁免并注明原因。`WAIVED-{id}` 约定的规范定义见 `.octopus/skills/_shared/review-revision-prompt.md`(本清单的豁免标记形如 `WAIVED-COR-R2-001`)。
|
||||
8. **CR-COMMIT 门禁**:代码评审收敛(所有维度 PASS)后,必须将所有评审修订提交到工作分支,再进入 verify 阶段。未提交的评审修订不得通过 verify 放行。验证阶段(verify)启动前必须确认 `git status` 无未提交修改。
|
||||
9. **standalone-bugfix 模式的预先存在模式豁免**:在 standalone-bugfix 模式下,修复的范围应以最小化外科手术为原则。若 COR/STY/DOC 维度的发现指向的是**修复前已存在的模式**(并非本次变更引入),且该模式与文件中已有代码保持一致,则 `WAIVED` 或 `ACCEPTED_RISK` 是有效的处理方式。具体适用场景:
|
||||
- COR 1.9/1.10/1.13:错误处理策略在变更前已采用同等模式(如 `console.warn` 而非面向用户的错误提示、无超时策略等),本次变更未使其劣化
|
||||
- STY 6.8:文件/模块长度超过 200 行属于变更前已有的结构,拆分为独立重构任务(非本次 bugfix 范畴)
|
||||
- DOC 9.5:注释中的默认值初始化(如 `lastTriggerScrollTop = -1`)为防御性编程,在观察者建立前即被覆盖,无行为影响
|
||||
每次豁免必须在 synthesis 中以带编号的 `WAIVED-{id}` 或 `ACCEPTED_RISK` 条目记录原因。此豁免不影响其他维度的评审标准。
|
||||
|
||||
---
|
||||
|
||||
## 1. 正确性、错误处理与兼容性检查(COR — Correctness, Error Handling & Compatibility)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | ------------------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 1.1 | 逻辑分支完整,无遗漏的 if/else、switch case 或漏处理的枚举值 | ☐ | ☐ | |
|
||||
| 1.2 | 边界条件正确处理(空值、空集合、零值、负值、最大值/最小值) | ☐ | ☐ | |
|
||||
| 1.3 | null/undefined 在使用前已检查,无空引用风险 | ☐ | ☐ | |
|
||||
| 1.4 | 异步操作正确使用 await 或 Promise 链,无竞态条件 | ☐ | ☐ | |
|
||||
| 1.5 | 循环有正确的终止条件,无无限循环风险 | ☐ | ☐ | |
|
||||
| 1.6 | 类型转换安全(如字符串转数字、JSON 解析),有失败处理 | ☐ | ☐ | |
|
||||
| 1.7 | 无逻辑死区(unreachable code)或死代码(dead code) | ☐ | ☐ | |
|
||||
| 1.8 | 所有可能失败的操作(I/O、网络、解析、数据库)有错误处理 | ☐ | ☐ | |
|
||||
| 1.9 | 错误信息对用户友好(不暴露内部堆栈、路径或 SQL) | ☐ | ☐ | |
|
||||
| 1.10 | 外部服务调用有超时、重试和熔断策略(与设计文档一致) | ☐ | ☐ | |
|
||||
| 1.11 | 事务边界明确,异常时回滚,无部分提交 | ☐ | ☐ | |
|
||||
| 1.12 | 错误码和 HTTP 状态码语义正确(不把 500 当 400 用) | ☐ | ☐ | |
|
||||
| 1.13 | 异常被捕获且传播到合适的层级,无被吞掉的异常 | ☐ | ☐ | |
|
||||
| 1.14 | 异步错误(Promise rejection、EventEmitter error)有处理器 | ☐ | ☐ | |
|
||||
| 1.15 | 公共 API 的签名、参数和返回值未做不兼容变更(或已标注 breaking) | ☐ | ☐ | |
|
||||
| 1.16 | 配置项的新增/删除/改名有迁移路径或向后兼容处理 | ☐ | ☐ | |
|
||||
| 1.17 | 客户端(前端/移动端/SDK)与后端接口版本兼容 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计一致性与依赖检查(DGN — Design Compliance & Dependencies)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | ------------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 2.1 | 代码实现了设计文档中该组件的全部指定接口和方法签名 | ☐ | ☐ | |
|
||||
| 2.2 | 组件职责与设计文档中声明的职责一致,无越界逻辑 | ☐ | ☐ | |
|
||||
| 2.3 | 组件依赖关系与设计文档的依赖图一致,无反向依赖 | ☐ | ☐ | |
|
||||
| 2.4 | 数据模型(字段、类型、关系)与设计文档的数据设计一致 | ☐ | ☐ | |
|
||||
| 2.5 | 接口输入输出的 Schema 与设计文档的接口设计一致 | ☐ | ☐ | |
|
||||
| 2.6 | 代码未引入设计文档中未提及的新依赖或新服务 | ☐ | ☐ | |
|
||||
| 2.7 | 架构模式(工厂、策略、仓储等)的使用方式与设计决策一致 | ☐ | ☐ | |
|
||||
| 2.8 | 若代码偏离设计,有明确的 ADR 或注释说明理由 | ☐ | ☐ | |
|
||||
| 2.9 | 已用 `graph usages`/`impact` 核验改动符号的所有调用方与传递影响均已处理,无遗漏的破坏性变更(评审时用代码图核对,而非 grep 逐文件追踪) | ☐ | ☐ | |
|
||||
| 2.10 | 实现文件结构与迭代计划中的架构描述一致(文件数、模块划分、依赖方向)。若偏离(如单文件合并替代多文件架构),迭代计划已更新或偏离理由于设计文档中标明 | ☐ | ☐ | |
|
||||
| 2.11 | 新增依赖有明确的技术理由(不在审查时追问"为什么需要它") | ☐ | ☐ | |
|
||||
| 2.12 | 依赖版本已锁定(package-lock.json / bun.lock / 等同文件已更新) | ☐ | ☐ | |
|
||||
| 2.13 | 无已知漏洞的依赖版本(依据 CVE 数据库或 `npm audit` 等同检查) | ☐ | ☐ | |
|
||||
| 2.14 | 无引入未使用的依赖 | ☐ | ☐ | |
|
||||
| 2.15 | 许可证兼容,无 GPL/AGPL 等强传染性许可证引入到非 GPL 项目 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 安全性检查(SEC — Security)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | ----------------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 3.1 | 用户输入在执行 SQL/命令/HTML 前已参数化或转义 | ☐ | ☐ | |
|
||||
| 3.2 | 身份认证逻辑无绕过路径,会话令牌安全生成和验证 | ☐ | ☐ | |
|
||||
| 3.3 | 授权检查在关键操作前执行,无 IDOR(越权访问)风险 | ☐ | ☐ | |
|
||||
| 3.4 | 敏感数据(密码、令牌、密钥、PII)不在日志、错误消息或响应中泄露 | ☐ | ☐ | |
|
||||
| 3.5 | 加密算法为业界推荐标准(无 MD5/SHA1/DES 用于安全目的),密钥管理合规 | ☐ | ☐ | |
|
||||
| 3.6 | 输入校验在服务端执行(不依赖客户端校验) | ☐ | ☐ | |
|
||||
| 3.7 | 无硬编码的凭据、密钥、内网地址或环境特定值 | ☐ | ☐ | |
|
||||
| 3.7.1 | 暂存区/提交无符号链接,无机器本地绝对路径引用(`git diff --cached --diff-filter=T` 为空)| ☐ | ☐ | |
|
||||
| 3.8 | 安全相关配置项(CORS、CSP、速率限制)符合设计文档安全章节 | ☐ | ☐ | |
|
||||
| 3.9 | SAST 结论已核对:PR 存在时引用同 SHA 的 CI SAST 结果(`sast.yml`,无新增 HIGH/CRITICAL 发现);无 PR 或同 SHA 无扫描结果时标注 `UNVERIFIABLE-LOCAL`,留待 verify Phase 2.5 处理。评审员为只读权限,不自行运行扫描工具 | ☐ | ☐ | |
|
||||
| 3.10 | 针对 CI SAST 报告的 HIGH 发现:True Positive 已修复或路由 Developer;False Positive 已标注排除理由(引用 `sast.yml` 产物,不重复推导) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 性能检查(PERF — Performance)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | ------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 4.1 | 循环内无同步 I/O 或数据库查询(N+1 问题) | ☐ | ☐ | |
|
||||
| 4.2 | 算法复杂度合理(无 O(n²) 或更高在主路径中,除非设计明确接受) | ☐ | ☐ | |
|
||||
| 4.3 | 资源(连接、文件句柄、缓冲区)在使用后释放,无泄漏风险 | ☐ | ☐ | |
|
||||
| 4.4 | 查询使用了索引,EXPLAIN 计划与设计文档索引设计一致 | ☐ | ☐ | |
|
||||
| 4.5 | 适度使用缓存和批处理,无过早优化但也无非受控的重复计算 | ☐ | ☐ | |
|
||||
| 4.6 | 异步操作非阻塞,长耗时操作用队列或后台任务处理 | ☐ | ☐ | |
|
||||
| 4.7 | 前端:列表子项有稳定 key,事件处理器引用稳定,无不必要的重渲染 | ☐ | ☐ | |
|
||||
| 4.8 | 前端:大列表使用虚拟滚动或分页,非首屏组件使用代码分割(lazy) | ☐ | ☐ | |
|
||||
| 4.9 | 前端:图片有懒加载(`loading="lazy"`)和尺寸占位,无布局偏移 | ☐ | ☐ | |
|
||||
| 4.10 | 前端:`useMemo`/`useCallback`/`computed`/`derived` 使用合理,无不必要的计算 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 测试质量检查(TST — Test Quality)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 5.1 | 新增代码有对应的测试(单元或集成),覆盖主要路径和分支 | ☐ | ☐ | |
|
||||
| 5.2 | 测试覆盖了边界条件(空值、极值、异常路径) | ☐ | ☐ | |
|
||||
| 5.3 | 测试断言验证了具体行为,非仅"不抛异常"或"返回非空" | ☐ | ☐ | |
|
||||
| 5.4 | 测试相互独立,可任意顺序运行,无共享可变状态 | ☐ | ☐ | |
|
||||
| 5.5 | Mock/Stub 的使用合理,模拟的外部行为与真实行为一致 | ☐ | ☐ | |
|
||||
| 5.6 | 测试命名清晰表达了测试场景和预期结果 | ☐ | ☐ | |
|
||||
| 5.7 | 本次变更涉及的测试已通过(`test:changed`,由 mechanical-green gate 机器判定,评审员引用 gate 结果即可)。全量套件(`test:parallel`)归 verify,不是代码评审门禁([org-internal #2598] 检查分层) | ☐ | ☐ | |
|
||||
| 5.8 | 评审发现的"dummy fixture / harness 默认值漂移"类问题,不能仅凭 `WAIVED` 处置:若漂移值是 harness 对**生产 CLI/配置默认值**的镜像(如 harness 旧 owner 默认 vs CLI 新 owner 默认这类 token 漂移),WAIVED 会把真实漂移放行到 post-merge([org-internal #3169] TST-F002 → 后续 [org-internal #3172] 才修)。处置前确认该值是否被生产路径读取;被读取则必须 FIX 或显式登记 TD | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 6. 风格与约定检查(STY — Style & Convention)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 6.1 | 命名遵循项目约定(函数名、变量名、文件名、目录结构) | ☐ | ☐ | |
|
||||
| 6.2 | 缩进、空格、引号、分号、行长度与项目格式化配置一致 | ☐ | ☐ | |
|
||||
| 6.3 | 函数/方法长度合理(通常 ≤ 50 行),单一职责 | ☐ | ☐ | |
|
||||
| 6.4 | 导入语句有序分组(第三方、内部、相对),无未使用的导入 | ☐ | ☐ | |
|
||||
| 6.5 | 无注释掉的代码块(应删除或用版本控制追溯) | ☐ | ☐ | |
|
||||
| 6.6 | 类型声明充分,无不必要的 `any` 或隐式类型 | ☐ | ☐ | |
|
||||
| 6.7 | Lint 零错误已由机器判定(mechanical-green gate 与 CI 的 `bun oxlint --deny-warnings`);评审员引用 gate/CI 结果即可,只复核 lint 覆盖不到的约定项(6.1–6.6、6.8),不重复人工推导 | ☐ | ☐ | |
|
||||
| 6.8 | 文件/模块长度 ≤ 200 行(超出需拆分)。模块过长降低可维护性和 review 效率 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 7. 数据库与数据检查(DBT — Database & Data)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 7.1 | 数据库迁移可安全回滚(down migration 正确且与 up 对称) | ☐ | ☐ | |
|
||||
| 7.2 | 查询使用参数化,无拼接 SQL 字符串 | ☐ | ☐ | |
|
||||
| 7.3 | 索引使用与设计文档的索引设计对应,EXPLAIN 计划合理 | ☐ | ☐ | |
|
||||
| 7.4 | 事务范围最小化(不在事务内执行外部调用或长计算) | ☐ | ☐ | |
|
||||
| 7.5 | 数据库连接生命周期正确,连接池配置合理 | ☐ | ☐ | |
|
||||
| 7.6 | 无大规模数据迁移导致锁表风险,大表变更方案已说明 | ☐ | ☐ | |
|
||||
| 7.7 | 数据库 Schema 变更对已有数据向后兼容(新字段允许 NULL 或有默认值) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 8. 可访问性与浏览器兼容性检查(A11Y — Accessibility & Browser Compatibility)
|
||||
|
||||
> 适用于所有面向用户的前端代码。确保 UI 可被各类用户(包括使用辅助技术者)正常使用。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | ----------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 8.1 | 交互元素使用原生语义 HTML(`<button>`/`<a>`/`<input>` 而非 `<div>` 模拟) | ☐ | ☐ | |
|
||||
| 8.2 | 图片有有意义的 `alt` 文本;纯装饰性图片使用 `alt=""` 或 CSS background | ☐ | ☐ | |
|
||||
| 8.3 | 表单控件有关联的 `<label>` 元素(非仅 placeholder) | ☐ | ☐ | |
|
||||
| 8.4 | 所有交互元素可通过键盘访问和操作(Tab 聚焦,Enter/Space 激活,方向键导航) | ☐ | ☐ | |
|
||||
| 8.5 | 模态框/弹窗:打开时焦点移入首元素,关闭时焦点回退触发按钮,ESC 可关闭 | ☐ | ☐ | |
|
||||
| 8.6 | 动态内容更新有 `aria-live` 通知(如搜索结果显示数、表单错误提示) | ☐ | ☐ | |
|
||||
| 8.7 | 颜色对比度合规:正文 ≥ 4.5:1,大文本 ≥ 3:1,非仅靠颜色传达信息 | ☐ | ☐ | |
|
||||
| 8.8 | 页面标题层级(`h1`→`h2`→`h3`)有逻辑层次,无跳级 | ☐ | ☐ | |
|
||||
| 8.9 | `aria-label`/`aria-labelledby` 为仅图标按钮、导航区提供了屏幕阅读器标签 | ☐ | ☐ | |
|
||||
| 8.10 | 焦点指示器可见(`:focus-visible`),无 `outline: none` 但未提供替代样式 | ☐ | ☐ | |
|
||||
| 8.11 | a11y 自动化扫描(`a11y.yml` / axe-core)结果已引用:PR 命中触发路径时以同 SHA CI 结论为准(无新增违规项),评审员不重复运行;语义性条目(8.1–8.10)仍由评审判断 | ☐ | ☐ | |
|
||||
| 8.12 | 前端:目标浏览器(Chrome/Firefox/Safari/Edge 最近 2 个主版本)下功能正常 | ☐ | ☐ | |
|
||||
| 8.13 | 前端:CSS 特性(Grid/Flexbox/Custom Properties)在目标浏览器均有支持或降级方案 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 9. 文档检查(DOC — Documentation)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 9.1 | 复杂算法、非常规优化或非直觉的逻辑有解释性注释 | ☐ | ☐ | |
|
||||
| 9.2 | 对外 API 的接口文档已同步更新(参数、返回值、错误码) | ☐ | ☐ | |
|
||||
| 9.3 | 面向用户的错误消息清晰、可操作(不应是"系统错误,请重试") | ☐ | ☐ | |
|
||||
| 9.4 | README / runbook / 运维文档如有必要已更新(如新增配置项) | ☐ | ☐ | |
|
||||
| 9.5 | 注释与代码一致(代码已改但注释未改视为文档错误) | ☐ | ☐ | |
|
||||
| 9.6 | 废弃的 API/配置有 `@deprecated` 标记和替代方案说明 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 10. 可追溯性完整性检查(TRC — Traceability Completeness)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | ------------------------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 10.1 | 每次代码修改的 commit/PR 描述包含对应的工作项 ID | ☐ | ☐ | |
|
||||
| 10.2 | 每个验收标准的实现均有对应的自动化测试用例 | ☐ | ☐ | |
|
||||
| 10.3 | 无验收标准遗漏测试或手动验证路径(全部覆盖) | ☐ | ☐ | |
|
||||
| 10.4 | 代码修改范围未超出工作项定义(无"顺便修"的无关变更) | ☐ | ☐ | |
|
||||
| 10.5 | 新增的需求引用(REQ-F-{NNN} / REQ-NF-{NNN})在代码中有对应实现 | ☐ | ☐ | |
|
||||
| 10.6 | 已删除/弃用的代码有明确的移除原因和替代方案说明 | ☐ | ☐ | |
|
||||
| 10.7 | 测试报告能按工作项 ID 筛选(测试与工作项可交叉引用) | ☐ | ☐ | |
|
||||
@@ -0,0 +1,59 @@
|
||||
# 单门三维审查 checklist(review-dag)
|
||||
|
||||
> 单门 `review-dag` 替代 `review-design-space` + `review-iteration-plan` 双门(spec-02 §1 D-02)。
|
||||
> 门内分层三维:**TOPO**(拓扑)、**REQMAP**(需求映射)、**RELEASE**(滚动放行)。
|
||||
> 省的是编排开销,不省修订循环——三维各审独立风险面。
|
||||
>
|
||||
> **权威源**:本文件判据逐行复制 `dag-pipeline/spec-04` §1 三张表
|
||||
> (含 `NFR:` 前缀 / 里程碑边 / 里程碑节点豁免与 `estimated_sessions` 判据)。
|
||||
> 若本文件与 spec-04 §1 不一致,以 spec-04 §1 为唯一权威源。
|
||||
> (`spec-0N` 前身为 `dag-pipeline/03-design-0N-*`,2026-08-21 [org-internal #3072] phase 3
|
||||
> 升格迁移,旧名仅保留墓碑占位以维持旧 URL 可达。)
|
||||
|
||||
## TOPO — 拓扑维
|
||||
|
||||
| 检查项 | 判据 |
|
||||
|---|---|
|
||||
| 环检测 | 边方向图无环;有环 = BLOCKER |
|
||||
| 依赖正确性 | 每条边 `from→to` 方向正确(契约生产方先于消费方);缺边/错边 = MAJOR |
|
||||
| 大小均匀性 | 节点工作量分布均匀,无"巨型节点";巨型节点 = 单个 task 节点 `size_attrs.estimated_sessions ≥ 2` → MAJOR(需拆分;INFO 豁免不适用) |
|
||||
| **粒度下限** | 每个 task 节点预估实现时长由 `size_attrs.estimated_hours`/`estimated_sessions` 承载(冻结字段,见 spec-02 §2.1);判据一律以 `estimated_sessions` 为准(两字段一致性不变量见 spec-02 §2.1,完整分档见 spec-03 §3 规则 3):字段不一致(`|estimated_hours − 8 × estimated_sessions| > 2`)= MAJOR(需 `analyze-dag` 重填);`estimated_sessions < 0.25`(对应约 `estimated_hours < 2`)= 低于下限(MAJOR,≥3 处 = BLOCKER);`1 < estimated_sessions < 2` = 超出上限(MINOR,提示拆分;连续集成性工作不可拆分则 INFO);`estimated_sessions ≥ 2` = 巨型节点 → MAJOR(需拆分,见「大小均匀性」行,INFO 豁免不适用)(防工单元数据成本爆炸) |
|
||||
| 里程碑位置 | 每个 `cross_session_in ≥ 2` 汇聚点已焊入里程碑(spec-03 规则 2);缺失 = BLOCKER |
|
||||
| 可执行性 | 每个 task 节点可被单个 session 独立实现(EXE 折叠项;里程碑节点无实现工作、由 verify 承担,不参与本判据) |
|
||||
| 页尺寸自检信号 | 读 `{epic-slug}/dag` 页首 `> 页尺寸自检: 超限` 标志(spec-02 §2.6)→ 以 **INFO** finding 记录于 synthesis(`summary` = 页尺寸超限、子页已下沉 `{subpages}`),供 retrospective 统计与试点负责人核查;**不改变任何门判据、不触发重派生**(非 PASS/FAIL 判据) |
|
||||
|
||||
## REQMAP — 需求映射维
|
||||
|
||||
| 检查项 | 判据 |
|
||||
|---|---|
|
||||
| 需求覆盖 | **每需求至少一节点**:需求登记表每条 `REQ-F-{NNN}` 被 ≥1 节点的 `req_refs` 引用(无"有需求无节点"遗漏) |
|
||||
| 节点溯源 | **每 task 节点至少一需求**:每 task 节点 `req_refs` 非空且引用有效编号(无"有节点无需求"过度分解) |
|
||||
| 验收标准可证伪 | 每 task 节点 `acceptance_criteria` 可证伪且映射 `test_id`(CLR 折叠项:无"视情况而定";`NFR:` 前缀条目不参与本判据——其验证由 per-task review-code + 里程碑 verify 承担,见 spec-02 §2.9) |
|
||||
| **AC 路径覆盖** | 每 task 节点验收标准覆盖正常路径、错误路径、边界场景三类(`.octopus/archive/checklists/requirements-analysis.md` TST 8.3/8.7 等价);缺错误/边界路径 = MAJOR(`NFR:` 前缀条目不参与本判据——其验证由 per-task review-code + 里程碑 verify 承担,见 spec-02 §2.9) |
|
||||
| 契约↔节点一致性 | 每条 task 间跨 session 边的 `contract_ref` 与两端节点验收标准一致(里程碑边无 `contract_ref`、不参与本判据,见 spec-02 §2.1 / spec-03 §2) |
|
||||
|
||||
> **REQMAP 维仅审 task 节点**:milestone 节点无 `req_refs`/`acceptance_criteria`(只有 DoD,spec-03 §3 规则 2(b)),由 spec-05 里程碑 DoD 承担,非评审对象(spec-08 §2 里程碑行 `评审深度` 列 `—`)——故「每 task 节点」均不含里程碑行,含里程碑的 DAG 不会在 REQMAP 维产生误报。
|
||||
|
||||
> **AC 下沉子页的读取路径**:当节点 AC 细目因 `{epic-slug}/dag` 页尺寸预算超限下沉到 `{epic-slug}/dag-nodes/{node-id}` 子页时(spec-02 §2.6),reviewer 按 `{epic-slug}/dag` 页内 `{node-id} → {epic-slug}/dag-nodes/{node-id}` 指针读取子页核对「验收标准可证伪」「AC 路径覆盖」判据(子页 AC 与页内指针同源,均为冻结副本 `{epic-slug}/dag` 的一部分)。
|
||||
|
||||
## RELEASE — 滚动放行维
|
||||
|
||||
| 检查项 | 判据 |
|
||||
|---|---|
|
||||
| 就绪规则 | 节点 `ready` 当且仅当其所有跨 session 入边源节点均处于各自类型的终结态(task 源节点 `done`、里程碑源节点 `green`;同 session 边不阻塞就绪) |
|
||||
| 依赖批放行 | 按依赖层分批放行:一层内互不依赖的节点同批 `ready`(最大化并行,见 spec-05 里程碑) |
|
||||
| 契约冻结范围 | 仅 task 间的跨 session 边进入 `frozen`;同 session 边与里程碑边不冻结(spec-02 §1 D-05;里程碑边 `contract_state` 不适用,见 spec-02 §2.1 / spec-03 §2) |
|
||||
| 放行风险前移 | 高风险节点(breaking 契约 / 大 fan-out)前移至早期批次(RISK 折叠项)。**操作化锚点**:breaking 契约节点不得晚于其所在依赖层内其它非 breaking 节点的最早可用批次(同层内先于或等于);**大 fan-out** = 单节点跨 session 出边数 ≥ 3(取 `cross_session_edge_count` 阈值表 D3 档起点,spec-06 §2),此类节点同样适用「不晚于同层最早批次」规则——reviewer 按拓扑层序对批次划分做机械核对 |
|
||||
| 估算合理性 | 各 **task** 节点 `size_attrs.estimated_hours`/`estimated_sessions`(冻结字段)已填写、取值在 TOPO「粒度下限」「大小均匀性」界内(两字段一致性不变量见 spec-02 §2.1),并支撑批次划分——同批并行节点由跨 session 边拓扑可达性决定(「依赖批放行」行);容量/并发上限属 Orchestrator 执行期资源配置、单门不审(§2.1 PAR 4.4 丢弃行,EST 折叠项)——reviewer 对照冻结 node schema 逐 **task** 节点核验该字段存在且取值合理(里程碑节点无实现工作、不填,见 spec-02 §2.1 / spec-03 §3) |
|
||||
|
||||
## 门收敛规则
|
||||
|
||||
| 项 | 规则 |
|
||||
|---|---|
|
||||
| 轮次上限 | 由评审深度派生(spec-06):D1≤2,D2≤3,D3≤3,D4≤4 |
|
||||
| PASS | 三维均 0 BLOCKER 且 0 MAJOR |
|
||||
| WARN | 0 BLOCKER;MAJOR 在修订轮内关闭;MINOR/INFO 允许(收敛为 PASS) |
|
||||
| FAIL | 任一维存在 BLOCKER |
|
||||
| INFO 处理 | INFO 不阻塞,记录供 retrospective 统计与试点负责人核查(review-dag 因 `never_trim: true` 结构性不可裁剪——gate-trim 元进程已退役,该字段自足于 `dag:` 块,见 spec-07 §5) |
|
||||
| 部分重审 | **round 3 起**(round 2 仍全维重审——三维共享 DAG 形状/字段副作用面,先以一轮全维重审建立干净基线,再于 round 3 起缩窄;与共享 review-artifact skill「round 2 起部分重审」的差异是有意的,因本门仅 3 维且互为副作用面)轮间只重审上一轮的 FAIL / WARN(含未关闭 BLOCKER 或 MAJOR 的维)/ UNRESOLVED 维(不省修订循环);维度需重审 = 该维 verdict 为 FAIL / WARN(含未关闭 BLOCKER 或 MAJOR)/ UNRESOLVED,MINOR/INFO 维视为通过不复跑。任一维修订改变**共享 DAG 形状(节点/边集合)或共享字段(`size_attrs`、AC 文本、契约内容)**时,以该字段为判据输入的相关维随轮重验(三维互为副作用面) |
|
||||
| 输出 | 单门 synthesis(复用 `synthesis.schema.json`,`dimensions` 键为 TOPO/REQMAP/RELEASE)+ commit status `pipeline/review-dag` |
|
||||
@@ -0,0 +1,115 @@
|
||||
# 前端实现自检清单
|
||||
|
||||
> 开发者在编写前端代码前后自检使用。确保组件结构合理、样式一致、状态完整、
|
||||
> 可访问且可测试。分为"实现前"(PRE)和"实现后"(POST)两部分。
|
||||
> 全部通过后方可提交代码评审。
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. **PRE** 项在开始写组件前检查;
|
||||
2. **POST** 项在完成编码和所有验证命令后检查;
|
||||
3. 对"不通过"项必须在代码评审前修复;
|
||||
4. 无法满足的项标记 `[N/A: <原因>]`。
|
||||
|
||||
---
|
||||
|
||||
## 实现前(PRE — Pre-Implementation)
|
||||
|
||||
### 1. 上下文完备性
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ----- | ------------------------------------------------------------------ | ---- | ------ | --- | ---- |
|
||||
| PRE-1 | 已确定项目框架(React / Vue / Svelte / SolidJS / Angular) | ☐ | ☐ | ☐ | |
|
||||
| PRE-2 | 已确定样式方案(Tailwind / CSS Modules / styled-components / ...) | ☐ | ☐ | ☐ | |
|
||||
| PRE-3 | 已读取至少 3 个同模块的现有文件(UI 工作同类组件),理解命名、结构和样式模式(共享 brownfield 规则,规范出处:`.octopus/skills/review-code/SKILL.md` §"Greenfield vs. Brownfield") | ☐ | ☐ | ☐ | |
|
||||
| PRE-4 | 已确认路由模式(file-based / config-based)和新组件路由位置 | ☐ | ☐ | ☐ | |
|
||||
| PRE-5 | 已确认项目使用的 UI 基础库(Kobalte / Radix / Headless UI / ...) | ☐ | ☐ | ☐ | |
|
||||
| PRE-6 | 若项目有设计系统(token / theme / spacing),已确认取值方式 | ☐ | ☐ | ☐ | |
|
||||
| PRE-7 | Pipeline 模式:已读取设计文档的组件设计、接口设计、NFR 章节(DAG 路由:设计输入解析自冻结 DAG 副本 `{epic-slug}/dag` 节点规格 + 跨 session 边契约,见 `implementation.md` 使用说明第 5 条) | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 2. 组件边界
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ----- | --------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| PRE-8 | 组件职责单一——一个组件只做一件事 | ☐ | ☐ | ☐ | |
|
||||
| PRE-9 | Props 类型已列出(TypeScript 接口 / PropTypes / defineProps) | ☐ | ☐ | ☐ | |
|
||||
| PRE-10| 所有需要的 UI 状态已识别:loading / empty / error / success / edge | ☐ | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 实现后(POST — Post-Implementation)
|
||||
|
||||
### 3. 组件结构
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ----------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-1 | 组件命名清晰、遵循项目约定(PascalCase / kebab-case) | ☐ | ☐ | ☐ | |
|
||||
| POST-2 | 所有 Props 有完整类型,无 `any` 类型 | ☐ | ☐ | ☐ | |
|
||||
| POST-3 | 组件文件结构符合项目约定(单文件 / 目录+index / co-located) | ☐ | ☐ | ☐ | |
|
||||
| POST-4 | 无巨型组件(> 200 行)——必要时已拆分为子组件 | ☐ | ☐ | ☐ | |
|
||||
| POST-5 | 事件处理器和回调遵循项目命名规范(`on*` / `handle*`) | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 4. 样式与设计系统
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | -------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-6 | 使用项目统一的样式方案,未引入新的样式库 | ☐ | ☐ | ☐ | |
|
||||
| POST-7 | 若使用 Tailwind:无冗余类名堆积,复杂样式已提取为 `@apply` 或组件 | ☐ | ☐ | ☐ | |
|
||||
| POST-8 | 响应式断点已处理(移动端/平板/桌面),无横向溢出 | ☐ | ☐ | ☐ | |
|
||||
| POST-9 | 若项目有暗色模式:组件在亮/暗主题下均可正常显示 | ☐ | ☐ | ☐ | |
|
||||
| POST-10| 使用项目设计 token(颜色/间距/字体),无硬编码魔法数值 | ☐ | ☐ | ☐ | |
|
||||
| POST-11| 动画/过渡遵循项目约定(`motion` / CSS transition / ...),无突兀跳动 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 5. 状态覆盖
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ----------------------------------------------------------------| ---- | ------ | --- | ---- |
|
||||
| POST-12| **Loading 状态**:数据加载时有骨架屏/加载指示器,布局不跳动 | ☐ | ☐ | ☐ | |
|
||||
| POST-13| **Empty 状态**:无数据时有友好提示和操作引导(非空白页) | ☐ | ☐ | ☐ | |
|
||||
| POST-14| **Error 状态**:请求失败时显示错误信息和重试/恢复操作 | ☐ | ☐ | ☐ | |
|
||||
| POST-15| **Edge cases**:超长文本截断、特殊字符、空数组、`null`/`undefined` 值均已处理 | ☐ | ☐ | ☐ | |
|
||||
| POST-16| 数据更新后 UI 正确反映最新状态(无过期数据残留) | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 6. 可访问性(a11y)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ------------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-17| 使用语义化 HTML 元素(`<button>` 而非 `<div onclick>`) | ☐ | ☐ | ☐ | |
|
||||
| POST-18| 图片/图标有有意义的 `alt` 文本(纯装饰性图片使用 `alt=""`) | ☐ | ☐ | ☐ | |
|
||||
| POST-19| 表单控件有关联的 `<label>`(非仅 placeholder) | ☐ | ☐ | ☐ | |
|
||||
| POST-20| 所有交互元素可通过键盘访问(Tab 导航,Enter/Space 激活) | ☐ | ☐ | ☐ | |
|
||||
| POST-21| 弹窗/模态框:打开时焦点移入,关闭时焦点回退,ESC 可关闭 | ☐ | ☐ | ☐ | |
|
||||
| POST-22| 动态内容更新(加载完成/错误提示/列表变化)有适当的 `aria-live` 通知 | ☐ | ☐ | ☐ | |
|
||||
| POST-23| 色彩对比度:正文 ≥ 4.5:1,大文本 ≥ 3:1,非仅靠颜色传达信息 | ☐ | ☐ | ☐ | |
|
||||
| POST-24| 页面有逻辑的标题层级(`h1` → `h2` → `h3`),无跳级 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 7. 性能
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | -------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-25| 无不必要的重渲染——列表子项 key 稳定、事件处理器引用稳定 | ☐ | ☐ | ☐ | |
|
||||
| POST-26| 大列表使用虚拟滚动或分页(非一次性渲染全部) | ☐ | ☐ | ☐ | |
|
||||
| POST-27| 图片使用懒加载(`loading="lazy"`),有合适的 `width`/`height` 防止布局偏移 | ☐ | ☐ | ☐ | |
|
||||
| POST-28| 非首屏组件考虑代码分割(`lazy()` / `defineAsyncComponent`) | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 8. 代码质量
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ----------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-29| `bun typecheck`(或项目等效命令)通过,无类型错误 | ☐ | ☐ | ☐ | |
|
||||
| POST-30| `bun lint`(或项目等效命令)通过,无错误或警告 | ☐ | ☐ | ☐ | |
|
||||
| POST-31| 无注释掉的代码块或 `console.log` 调试语句 | ☐ | ☐ | ☐ | |
|
||||
| POST-32| 无硬编码的 API 地址、密钥或环境特定值——使用环境变量或配置 | ☐ | ☐ | ☐ | |
|
||||
| POST-33| 没有因 UI 改动导致的不相关组件样式错乱 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 9. 前端测试
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ------------------------------------------------------------------ | ---- | ------ | --- | ---- |
|
||||
| POST-34| 新组件有基础渲染测试("does it render without crashing") | ☐ | ☐ | ☐ | |
|
||||
| POST-35| 关键交互有行为测试(点击/输入/提交触发预期回调或状态变化) | ☐ | ☐ | ☐ | |
|
||||
| POST-36| 至少覆盖 loading / error / empty 其中一种边界状态的测试 | ☐ | ☐ | ☐ | |
|
||||
| POST-37| 若项目使用 Storybook:新组件有至少一个 story | ☐ | ☐ | ☐ | |
|
||||
| POST-38| `bun run test:parallel`(或项目等效命令)全部通过 | ☐ | ☐ | ☐ | |
|
||||
@@ -0,0 +1,153 @@
|
||||
# 实现自检清单
|
||||
|
||||
> 开发者在编写代码前后自检使用。确保代码忠实实现设计、可测试且符合项目规范。
|
||||
> 分为"实现前"(PRE)和"实现后"(POST)两部分。全部通过后方可提交代码评审。
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. **PRE** 项在开始写代码前检查;
|
||||
2. **POST** 项在完成编码和所有验证命令后检查;
|
||||
3. 对"不通过"项必须在代码评审前修复;
|
||||
4. 无法满足的项标记 `[N/A: <原因>]`;
|
||||
5. **DAG 路由产物解析([org-internal #3072] phase 3 后唯一管线模式)**:DAG 运行不存在
|
||||
legacy `{slug}/04-plan-*` / `{slug}/03-design-*` 页面,PRE-1~PRE-7、
|
||||
PRE-11、PRE-12、PRE-19、POST-15.1 引用的产物按 `implement`/`verify`
|
||||
SKILL 的 DAG-route read map 解析:工作项定义 → 冻结 DAG 副本
|
||||
`{epic-slug}/dag` 节点规格 + 节点工单正文;验收条件与声明的 `test_id`
|
||||
→ 节点 `acceptance_criteria`(含 `{epic-slug}/dag-nodes/{node-id}`
|
||||
下沉子页);组件/接口/数据设计、REQ→组件追溯 → 节点规格 + 跨 session
|
||||
边契约(`{epic-slug}/shared/{file}`);「在 plan 中标注」(PRE-19)与
|
||||
迭代计划页标注(POST-15.1)→ 节点 `acceptance_criteria` 或节点工单
|
||||
正文显式标注。standalone 模式(bugfix/refactor/port)以请求本身为规格,
|
||||
上述项标记 `[N/A: standalone 无 legacy 产物]`。
|
||||
|
||||
---
|
||||
|
||||
## 实现前(PRE — Pre-Implementation)
|
||||
|
||||
### 1. 上下文完备性
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ----- | -------------------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| PRE-1 | 已读取工作项定义(wiki page `{slug}/04-plan-04-iteration-assignment`) | ☐ | ☐ | ☐ | |
|
||||
|
||||
| PRE-2 | 已读取本迭代验收条件(wiki page `{slug}/04-plan-05-acceptance-criteria`) | ☐ | ☐ | ☐ | |
|
||||
|
||||
| PRE-2.1 | 已从 plan/05 验收条件表提取本工作项每条声明的 test_id(测试用例 ID 列),作为 Phase 3 Red→Green 测试优先顺序的依据;MANUAL/BENCH 类型已识别 | ☐ | ☐ | ☐ | |
|
||||
|
||||
| PRE-3 | 已读取可追溯矩阵中的 REQ→组件映射(wiki page `{slug}/03-design-08-traceability`) | ☐ | ☐ | ☐ | |
|
||||
|
||||
| PRE-4 | 已读取涉及组件的设计文档(wiki page `{slug}/03-design-03-component-design`) | ☐ | ☐ | ☐ | |
|
||||
| PRE-5 | 若涉及 API,已读取接口设计(wiki page `{slug}/03-design-04-interface-design`) | ☐ | ☐ | ☐ | |
|
||||
| PRE-6 | 若涉及数据模型,已读取数据设计(wiki page `{slug}/03-design-05-data-design`) | ☐ | ☐ | ☐ | |
|
||||
| PRE-7 | 若涉及非功能需求,已读取对应设计章节(wiki page `{slug}/03-design-06-non-functional-design`) | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 2. 代码图调研(Code Graph)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ------------------------------------------------------------------------------------------ | ---- | ------ | --- | ---- |
|
||||
| PRE-CG1 | 会话已确认代码图就绪(`codegraph_status` 非空,否则 `codegraph init -i`) | ☐ | ☐ | ☐ | |
|
||||
| PRE-CG2 | 已用 `codegraph_explore`/`codegraph_search` 定位待改符号的定义与依赖,而非 grep+read 全文拼凑 | ☐ | ☐ | ☐ | |
|
||||
| PRE-CG3 | 已用 `codegraph_callers` 查清待改符号的所有调用方,确认改动不遗漏调用点 | ☐ | ☐ | ☐ | |
|
||||
| PRE-CG4 | 已用 `codegraph_explore`/`codegraph_callers` 评估改动的传递影响范围,回归风险已知 | ☐ | ☐ | ☐ | |
|
||||
| PRE-CG5 | 精读实现时使用 `read(filePath, symbol: ...)` 只取目标符号,未整文件读取大文件 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 3. 工作项边界
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ---------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| PRE-8 | 工作项范围清晰,不超过 3 个文件变更(单次 Worker 会话上下文窗口限制) | ☐ | ☐ | ☐ | |
|
||||
| PRE-9 | 所有需要创建/修改的文件在设计文档中有对应组件或接口 | ☐ | ☐ | ☐ | |
|
||||
| PRE-10 | 没有设计文档未提及的新组件、新表或新外部依赖需要引入 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 4. 设计与计划门控(Design & Plan Gate)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | -------------------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| PRE-11 | 设计文档(wiki pages `{slug}/03-design-**`)已存在且包含本工作项涉及的全部组件设计 | ☐ | ☐ | ☐ | |
|
||||
| PRE-12 | 迭代计划(plan/)已存在且本工作项有明确的工作项 ID 和验收条件 | ☐ | ☐ | ☐ | |
|
||||
| PRE-13 | 评审门已收敛:DAG 路由下为 review-dag 单门收敛(`octopus review status --stage review-dag` 的 state 为 `success`,与 `pipeline-gate.md` DAG 路由变体一致);standalone 模式(bugfix/refactor/port)无上游评审门,标记 `[N/A: standalone 无上游评审]`(legacy design-space / iteration-plan 双门已随 [org-internal #3072] phase 3 归档) | ☐ | ☐ | ☐ | |
|
||||
| PRE-14 | 合并前基准刷新:当前分支已 rebase 到目标分支(`git fetch origin && git rebase origin/main`),无合并冲突。若 rebase 引入新变更,重新运行 `bun typecheck && bun run test:parallel` 后再提交 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 5. UI 组件设计完整性(仅前端/UI 工作项)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | -------------------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| PRE-15 | 设计文档覆盖了组件的全部四种状态(Loading / Empty / Error / Success),每种状态有明确的渲染内容和触发条件(参见 `.octopus/archive/templates/design.md` §11.1,legacy 设计模板 [org-internal #3072] phase 3) | ☐ | ☐ | ☐ | |
|
||||
| PRE-16 | 设计文档覆盖了组件所需的全部交互行为:列表导航、焦点管理、键盘快捷键、展开/折叠、实时过滤(参见 `.octopus/archive/templates/design.md` §11.2,legacy 设计模板 [org-internal #3072] phase 3) | ☐ | ☐ | ☐ | |
|
||||
| PRE-17 | 设计文档覆盖了组件的全部可访问性要求:ARIA role/label、键盘可达、焦点环、对比度、色彩独立性(参见 `.octopus/archive/templates/design.md` §11.3,legacy 设计模板 [org-internal #3072] phase 3) | ☐ | ☐ | ☐ | |
|
||||
| PRE-18 | 设计文档已逐组件声明测试策略类型(render / source-verification / E2E / manual)。已知测试基础设施限制(Kobalte portal + happydom、路由上下文缺失等)已有对应替代方案标记(参见 `.octopus/archive/templates/design.md` §11.4,legacy 设计模板 [org-internal #3072] phase 3) | ☐ | ☐ | ☐ | |
|
||||
| PRE-19 | 超过 3 个 MANUAL 类型的 AC 已标记为设计风险,并在 plan 中标注更高级测试基础设施依赖 | ☐ | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 实现后(POST — Post-Implementation)
|
||||
|
||||
### 4. 设计一致性
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------ | ------------------------------------------------------ | ---- | ------ | --- | ---- |
|
||||
| POST-1 | 组件接口签名(方法名、参数、返回值)与设计文档一致 | ☐ | ☐ | ☐ | |
|
||||
| POST-2 | 数据模型字段名、类型、约束与数据设计一致 | ☐ | ☐ | ☐ | |
|
||||
| POST-3 | API 端点、方法、请求/响应格式、状态码与接口设计一致 | ☐ | ☐ | ☐ | |
|
||||
| POST-4 | 组件依赖关系与设计的依赖图一致,无反向依赖或新增依赖 | ☐ | ☐ | ☐ | |
|
||||
| POST-5 | 未引入设计文档未提及的新依赖(npm 包、外部服务) | ☐ | ☐ | ☐ | |
|
||||
| POST-6 | 若不得已偏离设计,有明确的注释标注原因和设计修正建议(含 ADR/amend 引用 — 不可仅写"偏离设计") | ☐ | ☐ | ☐ | |
|
||||
| POST-6.1 | 已用设计文档中的决策树/状态机/真值表,代入至少 2 组具体输入手工 trace 每条分支,确认代码输出与设计预期一致(尤其条件取反、`===` vs `!==`、状态翻转等易错点) | ☐ | ☐ | ☐ | |
|
||||
| POST-6.2 | 因 API 不兼容、上游限制或测试基础设施不足而延迟的项,已在代码中用 `[OPEN: <short-id>]` 标注(含延迟原因、影响范围、建议解决时机)。verify Phase 5.5 会为每个 `[OPEN]` 项在源票 `## TD 登记` 评论登记一行(registry-first,`ticket-lifecycle.md`;排期后才升格独立票)。禁止仅标注 `[OPEN]` 而不登记 | ☐ | ☐ | ☐ | |
|
||||
| POST-6.3 | 邻近配额([org-internal #3002] G3):本迭代触碰的区域(包/模块)若在源票 `## TD 登记` 中有适用行,已带走 ≥1 项一并处置(修复或带理由显式再延迟);无适用行时在报告记录 `0 applicable` | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 5. 代码质量
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------- | ----------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-7 | `bun typecheck` 通过,无类型错误 | ☐ | ☐ | ☐ | |
|
||||
| POST-8 | `bun lint` 通过,无 lint 错误或警告 | ☐ | ☐ | ☐ | |
|
||||
| POST-8.1 | 删除代码后(清理死代码、测试文件、重构移除),重新运行 `bun lint` 并确认无 unused-import / unused-variable 警告(常见遗留:删除测试代码后遗漏的 import) | ☐ | ☐ | ☐ | |
|
||||
| POST-8.2 | 删除 `.ts`/`.tsx` 文件后,验证 `bun typecheck` 无"找不到模块"或孤立类型引用错误 | ☐ | ☐ | ☐ | |
|
||||
| POST-9 | 函数/方法长度合理(≤ 50 行),单一职责 | ☐ | ☐ | ☐ | |
|
||||
| POST-10 | 错误处理路径完备(I/O、网络、解析、数据库操作) | ☐ | ☐ | ☐ | |
|
||||
| POST-10.1 | I/O 操作有超时守卫(如 `Effect.timeout`),避免无限挂起 | ☐ | ☐ | ☐ | |
|
||||
| POST-10.2 | 子进程调用有显式退出码检查(`exitCode !== 0` 显式 fail) | ☐ | ☐ | ☐ | |
|
||||
| POST-10.3 | `Effect.orDie`/`orDieWith` 仅用于 unrecoverable 场景;recoverable 错误用 `catchAll`/`recoverWith` | ☐ | ☐ | ☐ | |
|
||||
| POST-10.4 | 多写操作(INSERT + UPDATE)用 `Database.transaction` 包裹保证原子性 | ☐ | ☐ | ☐ | |
|
||||
| POST-11 | 无硬编码凭据、密钥、内网地址或环境特定值 | ☐ | ☐ | ☐ | |
|
||||
| POST-11.1 | 提交前无法跟踪链接检查(单一事实来源:`code-review.md` SEC 3.7.1——`git diff --cached --diff-filter=T` 为空;判据以该条为准)| ☐ | ☐ | ☐ | |
|
||||
| POST-11.2 | 提交前已确认无进程/流程副产品混入暂存区(如 `.claim` 空文件、claim carrier、临时 pid/日志)——`git status` 逐条核对,`git add -A` 前先看 untracked 清单([org-internal #3169] 教训:`.claim` 空文件随 iter-0 混入,round-1 九维评审要求 `git rm`) | ☐ | ☐ | ☐ | |
|
||||
| POST-12 | 无注释掉的代码块或 `console.log` 调试语句 | ☐ | ☐ | ☐ | |
|
||||
| POST-13 | 无 `as any` 类型断言绕过类型检查(生产代码必须保有完整类型安全) | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 6. 测试
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------- | ---------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-14 | `bun run test:parallel` 全部通过,无失败用例 | ☐ | ☐ | ☐ | |
|
||||
| POST-15 | 新增代码有测试覆盖,覆盖主要路径和关键分支 | ☐ | ☐ | ☐ | |
|
||||
| POST-15.1 | 测试与实现位于同一迭代;若分拆到下一迭代,必须在迭代计划(wiki page `{slug}/04-plan-04-iteration-assignment`)中显式标注并给出理由。实现提交时不得处于"零测试覆盖"状态 | ☐ | ☐ | ☐ | |
|
||||
| POST-15.2 | 新增组件源文件(`.tsx`/组件 `.ts`)提交时,同一 commit 必须附带至少一个冒烟测试(render 测试,或 Kobalte portal 类组件的源码验证测试——见 AGENTS.md「Testing Kobalte components with happydom」)。禁止整批源文件无任何测试落地、将全部测试统一延后到未来 chunk | ☐ | ☐ | ☐ | |
|
||||
| POST-15.3 | plan/05 中声明的每个 test_id 已落地为真实测试(文件路径::测试名与声明一致) | ☐ | ☐ | ☐ | |
|
||||
| POST-16 | 测试验证了验收条件中的具体行为 | ☐ | ☐ | ☐ | |
|
||||
| POST-17 | 测试覆盖了边界条件(空值、异常输入、权限边界) | ☐ | ☐ | ☐ | |
|
||||
| POST-18 | 测试相互独立,可任意顺序运行 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 7. 文档与日志
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------- | ---------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-19 | 复杂逻辑有简洁解释(若真正非直觉) | ☐ | ☐ | ☐ | |
|
||||
| POST-20 | 对外 API 的错误消息用户可读且可操作 | ☐ | ☐ | ☐ | |
|
||||
| POST-21 | 关键操作有结构化日志(含上下文如 userId、requestId) | ☐ | ☐ | ☐ | |
|
||||
| POST-22 | JSDoc 与实际函数签名一致,参数(含新增参数)已完整文档化 | ☐ | ☐ | ☐ | |
|
||||
| POST-23 | 带副作用的新代码路径(日志、事件发布)已守卫所有控制流(恢复、空值、错误路径),防止幽灵触发 | ☐ | ☐ | ☐ | |
|
||||
|
||||
### 8. 过程与规范一致性
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
| ------- | -------------------------------------------------------------- | ---- | ------ | --- | ---- |
|
||||
| POST-24 | AI artifact header 使用 `@ai-artifact:` 多行格式(示例见 AGENTS.md)。不得使用其他格式如 `// AI-GENERATED ARTIFACT` | ☐ | ☐ | ☐ | |
|
||||
| POST-25 | 评审修订后,若修改了被测试覆盖的代码,已同步更新对应的测试断言(source-verification 或 render 测试) | ☐ | ☐ | ☐ | |
|
||||
| POST-26 | 测试断言中不得包含 `@opencode-ai` 字符串字面量(namespace gate 扫描字符串字面量,会将其误判为命名空间残留);安全写法见 `.octopus/rules/testing.md` § "Namespace gate and test assertions" | ☐ | ☐ | ☐ | |
|
||||
| POST-31 | commit 消息使用了正确的 conventional 类型:性能优化用 `perf`(非 `feat`),内部重构用 `refactor`,新功能用 `feat`,bug 修复用 `fix`。类型选择直接影响 auto-changelog 和 release notes 准确性 | ☐ | ☐ | ☐ | |
|
||||
| POST-32 | 若工作项依赖关键 upstream 库(如 `marked`、`effect`、`@kobalte/core`),已在本地运行基础 smoke test 确认 API 签名未变(upstream 可能在 minor 版本变更返回类型或参数) | ☐ | ☐ | ☐ | |
|
||||
@@ -0,0 +1,129 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Pipeline Gate Checklist
|
||||
|
||||
跨阶段门控清单。implement、review-code、verify 的 precondition 应引用本清单。
|
||||
每个阶段启动前必须逐项确认。
|
||||
|
||||
## DAG 路由变体(DAG-routed Epic/Feature 及其 Kind/Task 子工单)
|
||||
|
||||
DAG 路由(`dag.route` / `dag.task_route`)**不适用**下方 legacy 的
|
||||
Upstream Artifact Existence 与 Upstream Review Convergence 检查
|
||||
(各 skill 的 DAG 分支——implement §"DAG-mode input path"、review-code
|
||||
§"DAG Task Mode"、verify §"DAG branch"——以下列判据替代):
|
||||
|
||||
- [ ] 冻结 DAG 副本存在:wiki page `{epic-slug}/dag` 存在且首行为
|
||||
`> DAG 工件状态: frozen`(节点 spec、跨 session 边契约、
|
||||
`size_attrs` 均自该副本解析)
|
||||
- [ ] 单门收敛:`octopus review status --stage review-dag` state 为
|
||||
`success`(替代 design-space + iteration-plan 双门;任务工单
|
||||
不重跑单门,只核对其已收敛)
|
||||
- [ ] (仅任务工单)票 body `## 父级 / Parent` 指向 DAG 父级
|
||||
(`Kind/Epic` 或 `Kind/Feature`,[org-internal #3061] 阶段 2),
|
||||
Step 0 解析到 `dag.task_route`(entry `implement`),不继承
|
||||
`dag.route`
|
||||
- [ ] 跨节点依赖由 DAG 拓扑承担:节点 ready 以其全部跨 session 上游
|
||||
处于终态为准(同 session 边不阻塞);fan-in ≥ 2 里程碑节点由
|
||||
verify 里程碑承担 DoD,不建工单、不走本清单
|
||||
|
||||
abort 恢复:单门未收敛 → 聚合 agent 重跑 review-artifact skill(target:
|
||||
review-dag)直至收敛;DAG 副本缺失或未冻结 → 回到 `analyze-dag`
|
||||
(分解 → 单门 → 冻结)后再重试。
|
||||
|
||||
## Upstream Artifact Existence(live — standalone modes)
|
||||
|
||||
- [ ] standalone bugfix:repro notes wiki page `{slug}/repro-notes` 存在且含环境快照(via `wiki 读写 API(见 TERMINOLOGY)`)
|
||||
- [ ] 验收条件可解析:来自请求/工单 body 或节点 AC(等价的验收条件文档,via `wiki 读写 API(见 TERMINOLOGY)` / `工单 API(见 TERMINOLOGY)get`)
|
||||
|
||||
> Legacy([org-internal #3072] phase 3,2026-08-21 归档):requirements/design/plan baseline
|
||||
> 页(`{slug}/02-requirements-index` / `{slug}/03-design-index` /
|
||||
> `{slug}/04-plan-index` / `{slug}/04-plan-05-acceptance-criteria`)与
|
||||
> design-space / iteration-plan 双门收敛检查随 legacy 管线归档
|
||||
> (`<instance-root>/archive/`);历史页面仍可读,DAG 路由由上方 DAG 变体接管。
|
||||
|
||||
## Upstream Review Convergence
|
||||
|
||||
- [ ] (代码评审阶段)code review 已收敛 — run `octopus review status --stage code` and verify state is `success`
|
||||
|
||||
## Upstream Dependency Check
|
||||
|
||||
- [ ] 当前节点的所有跨 session 上游依赖节点处于终态(task `done` / milestone `green`,以冻结 DAG 副本 / `## DAG 状态` 表为准)
|
||||
- [ ] 如果有依赖节点未到终态 → abort,列出阻塞的节点
|
||||
|
||||
## Deferred Items Closure
|
||||
|
||||
- [ ] 上一迭代的 Carried Items 全部有明确的 Target Iteration 且已在当前迭代处理或重新延期
|
||||
- [ ] 上一迭代的 Carried Risks 状态已更新(open/closed)
|
||||
- [ ] verify 阶段:所有 UNVERIFIABLE 项有明确的 reactivation plan 或被标记为 accepted tech debt(有记录)
|
||||
|
||||
## Tech Debt Review
|
||||
|
||||
- [ ] tech debt 可经 Gitea issue 查询(`工单 API(见 TERMINOLOGY)list(labels="tech-debt")`,如果项目有任何已完成的 chunk/迭代)
|
||||
- [ ] 回读 open `tech-debt` issues,检查每项的 Reactivation Trigger 是否已满足
|
||||
- [ ] 到期项(trigger 已满足)已纳入当前迭代工作项或显式延期(更新 trigger)
|
||||
- [ ] 如果有到期项未处理 → warn 并列出,建议纳入当前迭代
|
||||
- [ ] 本迭代全部 tech-debt 项均已创建为 `tech-debt` labeled issue;遗漏的项已在 verify 阶段补建或在报告中 flag
|
||||
|
||||
## PR 准入(pr-admission)
|
||||
|
||||
> 纪律:`core/rules/ticket-lifecycle.md` PR 准入节(TD-678 / [org-internal #3881] / [org-internal #4425])。
|
||||
|
||||
- [ ] PR 标题符合 `[slug][iter-N] type(scope): 描述` 格式
|
||||
- [ ] PR 正文含变更清单 + 自测结果 + 关联合规(关联 issue 引用,如 `Closes #N`;数字后须接 ASCII 标点或行尾——紧跟全角标点会破坏 Gitea 自动关闭,PR [org-internal #3882] 实例)
|
||||
- [ ] 交付推送前本地验证已绿:`<harness-package>` 下 `bun run test:changed` 全绿 + `bun typecheck` 0 error(WIP 备份推送不受此门约束——分支裸推零 CI,交付信号是完成回报而非推送)
|
||||
- [ ] 分支已推送 origin;PR **未**由本会话自行创建(由编排按容量串行开启,一次一张、双绿并入再开下一张)
|
||||
- [ ] 交付报告已发(branch= 分支名 / 改动文件清单 / 自测结果 / verify 与 risk 回执)
|
||||
- [ ] 若编排不可达(fail-open)自开了 PR:PR 正文已标注 `uncoordinated`
|
||||
|
||||
## Issue Checklist Sync
|
||||
|
||||
> 跨阶段门控。source issue 的 checklist 与 `## 当前状态` live-status 表
|
||||
> 必须在每次对外可见的状态跃迁后就地同步(PR 创建 / 评审收敛 / CI
|
||||
> 状态跃迁),不得等到技能退出边界。规则:`core/rules/issue-checklist-sync.md`。
|
||||
|
||||
- [ ] 若存在 source issue:其 checklist 已按同步点表渐进勾选(DAG 冻结、iteration commit、PR 创建、review PASS、CI 跃迁、verify 终扫),无陈旧 `- [ ]` 项
|
||||
- [ ] 若为 incident / standalone-bugfix 流程:issue 含 `## 当前状态` live-status 小节,且 PR / review / CI 行已随跃迁更新([org-internal #1689])
|
||||
- [ ] 过程性 AC(如"连续 N 次绿")的进度注记已更新(带 run 编号)
|
||||
- [ ] 遗留项(未勾选)均有 `(Deferred: ...)` 或 `(Pending: ...)` 注记
|
||||
- [ ] verify PASS 时 `## 当前状态` 小节已折叠进 checklist 注记并移除
|
||||
|
||||
## Bugfix Pipeline Pre-Design Falsification Gate
|
||||
|
||||
> Bugfix 流程特有。必须在进入修复实现前逐项确认。
|
||||
> 单一事实来源:`core/checklists/bugfix.md` §1 REPRO(条目 1.4-1.6)——
|
||||
> 环境快照、复现置信度 ≥3/5、根因假设未被证伪、不满足即 abort 的判据
|
||||
> 以该清单为准,此处不重复列举;逐项核对 bugfix.md §1 后方可进入修复实现。
|
||||
|
||||
## Failure Protocol
|
||||
|
||||
如果任何检查项不满足:
|
||||
1. **abort** 当前阶段,不继续执行
|
||||
2. 列出所有不满足的检查项
|
||||
3. 指出需要完成的上游工作
|
||||
4. 告知用户在 upstream 工作完成前无法继续
|
||||
|
||||
## Recovery Protocol
|
||||
|
||||
abort 后,根据缺失项运行对应的 skill 修复上游工作,然后再重试当前阶段:
|
||||
|
||||
| abort 原因 | 恢复动作(运行的 skill) | 修复后重试 |
|
||||
|-----------|--------------------------|-----------|
|
||||
| 冻结 DAG 副本缺失或未冻结 | `analyze-dag` — 分解 → `review-artifact` (target: review-dag) 单门 → 冻结 | implement / review-code / verify |
|
||||
| review-dag 单门未收敛(`octopus review status --stage review-dag` non-`success`) | `review-artifact` (target: review-dag) — 重新运行单门评审直到收敛(聚合 agent) | implement / review-code / verify |
|
||||
| 上游依赖节点未到终态(task 非 `done` / milestone 非 `green`) | 切换到该依赖节点的工作流,从其当前阶段继续推进,直到其 verify 通过 | implement / verify |
|
||||
| carried items 未闭环(上一迭代 Carried Items 未处理或无延期记录) | 在当前迭代处理该 carried item,或正式延期到下一迭代并更新其 Target Iteration | verify |
|
||||
| 验收条件不可解析(节点 AC 缺失或工单 body 无验收条件) | 在 DAG 副本上补齐节点 AC(修订走 oversize 信号流程),或在工单 body 补验收条件 | verify |
|
||||
| tech debt 到期项未处理(open `tech-debt` issue) | 将到期项纳入当前迭代工作项,或在该 tech-debt issue 中更新 Reactivation Trigger 并说明延期理由 | implement / verify |
|
||||
|
||||
### 恢复流程
|
||||
|
||||
1. abort 时,列出所有不满足的检查项
|
||||
2. 对照上表,确定每个缺失项对应的恢复 skill
|
||||
3. 如果有多个缺失项,按流水线顺序修复(DAG 冻结 → review-dag 单门 → 实现 → code review → verify)
|
||||
4. 每个恢复 skill 完成后,重新检查对应的 precondition
|
||||
5. 所有 precondition 满足后,重试原本 abort 的阶段
|
||||
|
||||
### 注意
|
||||
|
||||
- 恢复上游工作时,不要丢弃已完成的下游工作(如已有代码实现),而是将其作为修复后的验证输入
|
||||
- 如果上游修复导致下游已完成的工件失效,需要重新运行受影响阶段
|
||||
- 上游依赖 chunk 未完成时,优先推进依赖 chunk 而非绕过
|
||||
@@ -0,0 +1,159 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Port 检查清单
|
||||
|
||||
> 跨项目端口移植自检。Developer 在 Phase A1.7/A1.8(目标分析+能力边界)、
|
||||
> Phase 5(实现)、Phase 6(测试)、Phase 7(报告)各过一遍。
|
||||
|
||||
> **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TST/BEH/GATE/REG)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TST=Test Quality、verification.md 的 TST=Test Suite Completeness、port.md 的 TST=Test Porting)。跨清单引用时必须带清单限定(如 code-review.md TST 5.8),不得使用裸代码。
|
||||
|
||||
---
|
||||
|
||||
## 0. 自检门控(GATE — Self-Check Gate)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 0.1 | 全部 12 个章节均已逐项检查并标记 ☑ 或 ☐ | ☐ | ☐ | |
|
||||
| 0.2 | 所有 ☐ 项均有修复计划或推迟路径(含 reactivation path) | ☐ | ☐ | |
|
||||
| 0.3 | 已完成清单已输出为 wiki `port-{name}/self-check`(via `wiki 读写 API(见 TERMINOLOGY)`) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 0.5 源分析评审(SRV — Source Analysis Review)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 0.5.1 | 源函数清单与源文件交叉验证:无遗漏函数/符号(**须用自动化符号 diff,非主观判断**) | ☐ | ☐ | |
|
||||
| 0.5.2 | 公共 API 表与源路由/方法定义一致(输入/输出/错误码准确) | ☐ | ☐ | |
|
||||
| 0.5.3 | FID 清单覆盖所有源测试用例,file:line 引用正确 | ☐ | ☐ | |
|
||||
| 0.5.4 | 依赖列表与源项目包管理文件一致 | ☐ | ☐ | |
|
||||
| 0.5.5 | Pipeline 模式:10 维度评审全部收敛(0 BLOCKER, 0 MAJOR) | ☐ | ☐ | |
|
||||
| 0.5.6 | SRC-CMP 符号差集已记录:codegraph/grep 导出源符号集 vs 清单,差集为空或差集项均有 BLOCKER/DEFER 记录 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 0.7 目标接收面分析(TGT — Target Surface Analysis)
|
||||
|
||||
> 在 Phase A2(概念映射)之前完成。回答"目标项目准备好了吗?"。
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 0.7.1 | 目标项目包结构已映射(每个包的角色已记录) | ☐ | ☐ | |
|
||||
| 0.7.2 | 目标已有能力已识别(与源功能重叠的模块/路由/Provider/Schema) | ☐ | ☐ | |
|
||||
| 0.7.3 | 自动化结构差异已完成(文件/依赖/导出符号/路由/Provider/Schema/CLI/主题/配置/Env/构建) | ☐ | ☐ | |
|
||||
| 0.7.4 | 集成点已识别(每个集成点标注变更类型和受影响的目标文件) | ☐ | ☐ | |
|
||||
| 0.7.5 | 目标就绪评估已完成(是否需要重构/新包/Migration/配置变更,阻塞项已标注) | ☐ | ☐ | |
|
||||
| 0.7.6 | `port-{name}/source-analysis/11-target-surface`(wiki)已输出 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 0.8 能力边界定义(CAP — Capability Boundary)
|
||||
|
||||
> 在 Phase A2(概念映射)之前完成。回答"这个能力的完整边界是什么?"。
|
||||
> **GATE:13 个维度必须全部填写,否则不得进入 Phase A2。**
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 0.8.1 | 维度 1 — 源代码文件:每个文件已列出(含目标位置和状态) | ☐ | ☐ | |
|
||||
| 0.8.2 | 维度 2 — 类型定义/接口:所有共享类型/品牌类型/Schema 已列出 | ☐ | ☐ | |
|
||||
| 0.8.3 | 维度 3 — 数据库 Schema/Migration:表/列/Migration 已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.4 | 维度 4 — 配置条目:Config Key/Setting/默认值已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.5 | 维度 5 — 环境变量:Env Var/VITE_* 已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.6 | 维度 6 — CLI 标志/命令:CLI 命令/标志/选项已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.7 | 维度 7 — 主题/样式文件:CSS/Theme JSON/Tailwind/Token 已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.8 | 维度 8 — 路由定义:新路由/修改重定向/路由守卫已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.9 | 维度 9 — Provider/Context 层级:新 Provider/插入点/Context Key 已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.10 | 维度 10 — 构建配置变更:vite/tsconfig/webpack/tailwind 已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.11 | 维度 11 — Package.json 依赖:新依赖/版本变更/Workspace 依赖已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.12 | 维度 12 — 测试文件:单元测试/集成测试/测试夹具/测试助手已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.13 | 维度 13 — 共享包变更:SDK/UI/Core 等跨包依赖变更已列出或 N/A | ☐ | ☐ | |
|
||||
| 0.8.14 | 交叉验证:源函数清单中每个函数/符号出现在维度 1 或维度 2 中 | ☐ | ☐ | |
|
||||
| 0.8.15 | 交叉验证:结构差异中每个 Gap 在能力边界中有对应条目 | ☐ | ☐ | |
|
||||
| 0.8.16 | 所有 N/A 维度含一行理由 | ☐ | ☐ | |
|
||||
| 0.8.17 | 所有 ☐ 项含推迟路径和 reactivation trigger | ☐ | ☐ | |
|
||||
| 0.8.18 | `port-{name}/source-analysis/12-capability-boundary`(wiki)已输出 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 1. 源项目理解(SRC — Source Understanding)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------ | ---- | ------ | ---- |
|
||||
| 1.1 | 源功能代码已逐文件阅读 | ☐ | ☐ | |
|
||||
| 1.2 | 源测试已全部阅读(测试是权威行为规范) | ☐ | ☐ | |
|
||||
| 1.3 | 源项目依赖已全部列清(库、服务、基础设施) | ☐ | ☐ | |
|
||||
| 1.4 | 源项目的公共 API 已文档化 | ☐ | ☐ | |
|
||||
| 1.5 | 源函数清单已生成(每个公开/私有函数/符号均有记录,含 Ported? 列) | ☐ | ☐ | |
|
||||
| 1.6 | 源测试用例已全部提取为 FID 清单(`port-{name}/source-analysis/fid-raw`,wiki) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 2. 概念映射(MAP — Concept Mapping)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 2.1 | 每个源概念在目标项目中都有对应(或标记 [GAP]) | ☐ | ☐ | |
|
||||
| 2.2 | 映射优先使用目标项目的现有模式和库(不引入新依赖) | ☐ | ☐ | |
|
||||
| 2.3 | 模式冲突时(如回调 vs async/await)以目标项目模式为准 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 差距分析(GAP — Gap Analysis)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 3.1 | 所有 [GAP] 都有替代方案和决策记录;推迟项含 reactivation path | ☐ | ☐ | |
|
||||
| 3.2 | 差距不应通过引入新基础设施解决(除非无替代方案) | ☐ | ☐ | |
|
||||
| 3.3 | 导致行为变更的差距标记为 FIDELITY DEVIATION 并已获批准 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 适配设计(ADAPT — Adaptation Design)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------ | ---- | ------ | ---- |
|
||||
| 4.1 | 文件在目标项目中的位置已规划 | ☐ | ☐ | |
|
||||
| 4.2 | 接口适配已记录(命名、类型、错误处理风格) | ☐ | ☐ | |
|
||||
| 4.3 | 依赖替代方案已内联到 Phase 3 的 Gap 决策 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 实现忠实度(FID — Fidelity)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------- | ---- | ------ | ---- |
|
||||
| 5.1 | 移植代码遵循目标项目约定(命名、模式、风格) | ☐ | ☐ | |
|
||||
| 5.2 | 未"改进"源逻辑(行为一致优先) | ☐ | ☐ | |
|
||||
| 5.3 | 未引入新的第三方依赖 | ☐ | ☐ | |
|
||||
| 5.4 | 源项目注释已同步移植 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 6. 测试移植(TST — Test Porting)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------ | ---- | ------ | ---- |
|
||||
| 6.1 | 源项目的所有测试(含边界/异常/错误路径)均已移植 | ☐ | ☐ | |
|
||||
| 6.2 | 移植的测试全部通过 | ☐ | ☐ | |
|
||||
| 6.3 | 目标项目已有测试无回归 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 7. 行为忠实度验证(BEH — Behavioral Fidelity)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------- | ---- | ------ | ---- |
|
||||
| 7.1 | 源项目的每个公共行为在目标项目中可复现 | ☐ | ☐ | |
|
||||
| 7.2 | 忠实度偏差已有文档记录和批准 | ☐ | ☐ | |
|
||||
| 7.3 | 推迟的功能(不能移植的部分)有后续计划 | ☐ | ☐ | |
|
||||
| 7.4 | 反向覆盖:源函数清单 Ported? 列无残留 ☐(残留项须有 DEFER + reactivation trigger) | ☐ | ☐ | |
|
||||
| 7.5 | 符号级完整性:源/目标导出符号 diff 的 hard gap 均有 DEFER 记录(B5 SRC-CMP) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 8. 最终验证(FINAL — Final Validation)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------ | ---- | ------ | ---- |
|
||||
| 8.1 | `bun run test:parallel` 全部通过(移植测试 + 已有测试) | ☐ | ☐ | |
|
||||
| 8.2 | `bun typecheck` 无错误 | ☐ | ☐ | |
|
||||
| 8.3 | `bun lint` 无错误 | ☐ | ☐ | |
|
||||
@@ -0,0 +1,59 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Prototype / Spike 自检清单
|
||||
|
||||
> 开发者在执行 prototype skill 时自检使用。确保 disposition 决策明确、契约完整、
|
||||
> 产出物可审计。分为 THROWAWAY 与 EVOLUTIONARY 两轨。
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. **Phase 0** 项在写任何代码前检查;
|
||||
2. **THROWAWAY** 或 **EVOLUTIONARY** 项根据 disposition 选择执行;
|
||||
3. 全部通过后方可声明完成;无法满足的项标记 `[N/A: <原因>]`。
|
||||
|
||||
---
|
||||
|
||||
## Phase 0 — Disposition 决策(强制)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
|---|--------|------|--------|-----|------|
|
||||
| P0-1 | 已用一句话明确回答"这段代码会被提升为生产代码(EVOLUTIONARY)还是学习后丢弃(THROWAWAY)?" | ☐ | ☐ | ☐ | |
|
||||
| P0-2 | 已从用户原话中引用证据(`@evidence`)支持 disposition 决策 | ☐ | ☐ | ☐ | |
|
||||
| P0-3 | 若用户措辞为条件式("if it works...")或模糊,已向用户提问澄清而非猜测 | ☐ | ☐ | ☐ | |
|
||||
| P0-4 | disposition 与 evidence 已记录在产出物头部或对应 artifact 中 | ☐ | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## THROWAWAY 轨(Spike)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
|---|--------|------|--------|-----|------|
|
||||
| T-1 | 已声明 time-box(小时/天),并在到期时停止 | ☐ | ☐ | ☐ | |
|
||||
| T-2 | 产出物包含 spike 代码 + learning report(wiki `{slug}/spike-report`,via `wiki 读写 API(见 TERMINOLOGY)`);写入模式见 `_shared/gitea-write-patterns.md` | ☐ | ☐ | ☐ | |
|
||||
| T-3 | spike 代码已标记 `@ai-artifact: spike`,且位于 `spike/` 或 scratch worktree | ☐ | ☐ | ☐ | |
|
||||
| T-4 | learning report 包含:验证了什么、什么失败、go/pivot/stop 决策 | ☐ | ☐ | ☐ | |
|
||||
| T-5 | spike 代码不可被生产代码 import;report 接受后已删除或隔离 | ☐ | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## EVOLUTIONARY 轨(高保真原型)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
|---|--------|------|--------|-----|------|
|
||||
| E-1 | 已创建 debt register(wiki `{slug}/prototype-debt`,via `wiki 读写 API(见 TERMINOLOGY)`),每项 shortcut 有 owner + promotion criterion | ☐ | ☐ | ☐ | |
|
||||
| E-2 | 第 1 天质量底线:typecheck 通过、无未授权的 `any`、committed code 无 `console.log` | ☐ | ☐ | ☐ | |
|
||||
| E-3 | 明确延迟的质量项(测试覆盖、错误状态、可观测性、性能预算)已列入 debt register | ☐ | ☐ | ☐ | |
|
||||
| E-4 | promotion 前已通过 `review-code`(`mode: "prototype-promotion"`),debt register 作为必需输入 | ☐ | ☐ | ☐ | |
|
||||
| E-5 | debt register 为空 OR 每项剩余条目有 reviewer 书面 waiver | ☐ | ☐ | ☐ | |
|
||||
| E-6 | waived 条目已由 `verify` Phase 5.5 创建为 `tech-debt` labeled issue(无平行 tracker) | ☐ | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 反模式(不得出现)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | N/A | 备注 |
|
||||
|---|--------|------|--------|-----|------|
|
||||
| A-1 | 未以"prototype 最终总要重写"为由,在 EVOLUTIONARY 场景下 freeze + delete 原型 | ☐ | ☐ | ☐ | |
|
||||
| A-2 | 未以"spike 只是玩玩"为由,跳过 learning report | ☐ | ☐ | ☐ | |
|
||||
| A-3 | 未以"先跑起来再说"为由,让 EVOLUTIONARY 原型在无 debt register 的情况下进入 review | ☐ | ☐ | ☐ | |
|
||||
@@ -0,0 +1,64 @@
|
||||
# Refactor 检查清单
|
||||
|
||||
> 重构自检。Developer 在 Phase 4(每步)和 Phase 5(最终验证)各过一遍。
|
||||
|
||||
> **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TST/BEH/GATE/REG)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TST=Test Quality、verification.md 的 TST=Test Suite Completeness、port.md 的 TST=Test Porting)。跨清单引用时必须带清单限定(如 code-review.md TST 5.8),不得使用裸代码。
|
||||
|
||||
---
|
||||
|
||||
## 1. 基线检查(BASE — Baseline)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------- | ---- | ------ | ---- |
|
||||
| 1.1 | 重构范围内的代码有测试覆盖 | ☐ | ☐ | |
|
||||
| 1.2 | 全量测试在重构前全部通过 | ☐ | ☐ | |
|
||||
| 1.3 | 测试覆盖率已捕获(行/分支) | ☐ | ☐ | |
|
||||
| 1.4 | 工作区干净(无未提交变更) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 2. 范围控制(SCOPE — Scope)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 2.1 | 重构目标明确(哪种变换,为什么) | ☐ | ☐ | |
|
||||
| 2.2 | 步骤分解 ≤ 10 步 | ☐ | ☐ | |
|
||||
| 2.3 | 不存在范围蔓延(未在同一重构中混入新功能或 Bug 修复) | ☐ | ☐ | |
|
||||
| 2.4 | 若重构涉及身份/owner token 改名(org、账号、邮箱、域名前缀):已先产出"不改清单"——OS 账号、个人邮箱、历史记录归属、dummy fixture 等非组织身份引用逐类明确保留,再执行机械替换([org-internal #3169] 教训:D1 缺此边界 → round-1 15 MAJOR) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 步骤执行(STEP — Per-step Verification)
|
||||
|
||||
> 每一步重构后检查:
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 3.1 | 每步为单一概念变换(非 rename + extract 一步完成) | ☐ | ☐ | |
|
||||
| 3.2 | 每步后 `bun run test:parallel` 全部通过 | ☐ | ☐ | |
|
||||
| 3.3 | 若测试失败,已立即回退而非原地修复 | ☐ | ☐ | |
|
||||
| 3.4 | 每步有独立 commit(便于 revert 或 cherry-pick) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 最终验证(FINAL — Final Validation)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 4.1 | `bun run test:parallel` 全部通过(与重构前相同数量或更多) | ☐ | ☐ | |
|
||||
| 4.2 | `bun typecheck` 无错误 | ☐ | ☐ | |
|
||||
| 4.3 | `bun lint` 无错误 | ☐ | ☐ | |
|
||||
| 4.4 | 测试覆盖率未下降(±1%) | ☐ | ☐ | |
|
||||
| 4.5 | 无新增或修改的测试(重构不应改变测试中的断言逻辑) | ☐ | ☐ | |
|
||||
| 4.6 | 若重构改变了对外可见字符串(stage 名称、tool description、错误消息等),对应的 snapshot 已通过 `bun test --update-snapshots` 更新 | ☐ | ☐ | |
|
||||
| 4.7 | 若重构变更了身份/owner token:残留扫描守卫(如 `script/verify-urls.ts`)已做**语义正反验证**——搜索模式指向**旧** token(旧 owner 前缀的 URL 形态),且已知新 token(新 owner 前缀的 URL 形态)与真实旧残留各跑一次,确认守卫对"合规新 URL"不误报、对"真实残留"不漏报([org-internal #3169] 教训:D5 模式被翻转 → 守卫对新 URL 误报、对旧残留失明) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 无行为变更(BEH — No Behavior Change)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------------- | ---- | ------ | ---- |
|
||||
| 5.1 | 对外接口签名未变化(或已有 @deprecated 说明) | ☐ | ☐ | |
|
||||
| 5.2 | 公共 API 行为一致(相同输入 → 相同输出) | ☐ | ☐ | |
|
||||
| 5.3 | 未引入新的运行时错误/异常路径 | ☐ | ☐ | |
|
||||
@@ -0,0 +1,76 @@
|
||||
# Release 检查清单
|
||||
|
||||
> 发版前自检。Release Manager 在 Phase 1 和 Phase 6 完整过一遍。
|
||||
|
||||
> **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TST/BEH/GATE/REG)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TST=Test Quality、verification.md 的 TST=Test Suite Completeness、port.md 的 TST=Test Porting)。跨清单引用时必须带清单限定(如 code-review.md TST 5.8),不得使用裸代码。
|
||||
|
||||
---
|
||||
|
||||
## 1. 前置闸门(GATE — Pre-release Gates)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------- | ---- | ------ | ---- |
|
||||
| 1.1 | 工作区干净(`git status` 无未提交变更) | ☐ | ☐ | |
|
||||
| 1.2 | 在正确的发布分支上 | ☐ | ☐ | |
|
||||
| 1.3 | 构建通过 | ☐ | ☐ | |
|
||||
| 1.4 | Typecheck + Lint 通过 | ☐ | ☐ | |
|
||||
| 1.5 | 测试全部通过 | ☐ | ☐ | |
|
||||
| 1.6 | 依赖审计已运行,无新增 HIGH/CRITICAL CVE | ☐ | ☐ | |
|
||||
| 1.7 | 无未跟踪的敏感文件(.npmrc 含 token、.env、credentials、私钥等) | ☐ | ☐ | |
|
||||
|
||||
> **注意 1.6**:若使用了无法访问公有 registry 的私有仓库(如自建 Gitea),`bun audit` / `npm audit` 可能报 404。此时无法获取 CVE 数据属于已知盲区,须在 release report 中明确标注「dependency audit: N/A (custom registry)」。
|
||||
|
||||
---
|
||||
|
||||
## 2. 版本号(VER — Version)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------- | ---- | ------ | ---- |
|
||||
| 2.1 | 已找到上一个 tag(或确认这是首个 tag) | ☐ | ☐ | |
|
||||
| 2.2 | 所有 commit 已按类型分类(BREAKING/feat/fix/other) | ☐ | ☐ | |
|
||||
| 2.3 | 版本号遵循 semver(MAJOR.MINOR.PATCH) | ☐ | ☐ | |
|
||||
| 2.4 | 变更类型与 commit 内容一致 | ☐ | ☐ | |
|
||||
| 2.5 | 版本号大于上一个 tag | ☐ | ☐ | |
|
||||
| 2.6 | 版本号已写入所有版本文件 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 变更日志(LOG — Changelog)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------- | ---- | ------ | ---- |
|
||||
| 3.1 | 自上一个 tag 以来的所有非 chore 提交均已收录 | ☐ | ☐ | |
|
||||
| 3.2 | 条目分组正确(Added/Changed/Fixed/Breaking) | ☐ | ☐ | |
|
||||
| 3.3 | Breaking change 有迁移说明 | ☐ | ☐ | |
|
||||
| 3.4 | 每个条目标注了对应的 commit hash | ☐ | ☐ | |
|
||||
| 3.5 | CHANGELOG.md 已更新(prepend 新版本段) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 标签(TAG — Git Tag)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------- | ---- | ------ | ---- |
|
||||
| 4.1 | commit message 含版本号 | ☐ | ☐ | |
|
||||
| 4.2 | tag 已创建且指向正确 commit | ☐ | ☐ | |
|
||||
| 4.3 | tag 命名遵循项目约定(默认 v{version}) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 回滚计划(ROLL — Rollback Plan)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------- | ---- | ------ | ---- |
|
||||
| 5.1 | Git 回退步骤已文档化 | ☐ | ☐ | |
|
||||
| 5.2 | 若有数据库迁移,down migration 存在且已测试 | ☐ | ☐ | |
|
||||
| 5.3 | 回滚触发条件已明确(延迟/错误率/严重 Bug) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 6. 冒烟测试(SMOKE — Post-release Smoke)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------- | ---- | ------ | ---- |
|
||||
| 6.1 | Tagged commit 可构建 | ☐ | ☐ | |
|
||||
| 6.2 | 测试全部通过 | ☐ | ☐ | |
|
||||
| 6.3 | 回到了原始分支 | ☐ | ☐ | |
|
||||
@@ -0,0 +1,64 @@
|
||||
# Retrospective 检查清单
|
||||
|
||||
> 迭代复盘自检。Retrospective Lead 在 Phase 7 报告前检查。
|
||||
|
||||
---
|
||||
|
||||
## 1. 数据收集(DATA — Data Quality)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------------- | ---- | ------ | ---- |
|
||||
| 1.1 | 分析范围已明确(tag range / time range) | ☐ | ☐ | |
|
||||
| 1.2 | Commit 按类型统计已运行(BREAKING/feat/fix/...) | ☐ | ☐ | |
|
||||
| 1.3 | Revert 数和无前缀 commit 数已统计 | ☐ | ☐ | |
|
||||
| 1.4 | 文件改动 Top 10 已列出(按 +/− 排序) | ☐ | ☐ | |
|
||||
| 1.5 | 测试结果已获取(通过/失败/跳过数) | ☐ | ☐ | |
|
||||
| 1.6 | 测试文件改动情况已检查(无测试改动的源码改动) | ☐ | ☐ | |
|
||||
| 1.7 | Typecheck + Lint 当前状态已检查 | ☐ | ☐ | |
|
||||
| 1.8 | 依赖审计已运行,变更已记录 | ☐ | ☐ | |
|
||||
| 1.9 | 流程基础设施已检查(pre-commit/CI/PR/review/doc/artifact) | ☐ | ☐ | |
|
||||
| 1.10 | 已检查是否存在被删除的未合并 workflow/* 分支(废弃工作/资源浪费) | ☐ | ☐ | |
|
||||
| 1.11 | 已检查 merged workflow/* 分支与关联 worktree 在合并后已清理(本地分支删除 + worktree 移除) | ☐ | ☐ | |
|
||||
| 1.12 | 周期时长和团队规模已记录(压缩周期 = 质量风险,单点故障) | ☐ | ☐ | |
|
||||
| 1.13 | 范围估算验证:triage/scope 声明前已统计各标签下的 issue 数量(如 `Status/Blocked`、`Kind/*`、`Priority/*`),并与之前声明对比,偏差 >20% 标注为估算偏差 | ☐ | ☐ | |
|
||||
| 1.14 | 门缺陷逃逸分析已运行(§2.8 escape-rate 单指标,[org-internal #3061]):各 gate 的 clean runs / escapes / escape_rate 已统计,UNDER-POWERED(≥0.3)门已标注并回馈 §2.9 pre-flight(gate-trim 元进程已退役,[org-internal #3072] phase 3) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 2. 分析质量(ANAL — Analysis Quality)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 2.1 | "What went wrong" 每一项都有数据证据支撑 | ☐ | ☐ | |
|
||||
| 2.2 | 根因追溯到具体环节(测试缺失/模块过于庞大/提交不规范)| ☐ | ☐ | |
|
||||
| 2.3 | "What went well" 有可复用的模式描述 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 行动项(ACT — Action Items)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 3.1 | 行动项数 ≤ 5 | ☐ | ☐ | |
|
||||
| 3.2 | 每个行动项有具体文件路径(修改哪个 template/checklist/SKILL/config) | ☐ | ☐ | |
|
||||
| 3.3 | 每个行动项有 Owner 角色 | ☐ | ☐ | |
|
||||
| 3.4 | 行动项可度量(如何判断已执行) | ☐ | ☐ | |
|
||||
| 3.5 | 行动项不是"更努力"、"更仔细"等空泛措辞 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 闭环(LOOP — Feedback Loop)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 4.1 | 所有素材文件已更新(行动项中引用的文件) | ☐ | ☐ | |
|
||||
| 4.2 | 更新的素材变更已在 report 中记录 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 无指责原则(SAFE — Blameless)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | -------------------------------------------- | ---- | ------ | ---- |
|
||||
| 5.1 | 报告不含任何个人指责 | ☐ | ☐ | |
|
||||
| 5.2 | 所有问题归因于流程/工具/信息不足,非个人能力 | ☐ | ☐ | |
|
||||
@@ -0,0 +1,146 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# 迭代验证检查清单
|
||||
|
||||
> 迭代级质量门禁。在所有工作项通过代码评审后执行。
|
||||
> 确保集成正确、非功能达标、无回归。
|
||||
|
||||
> **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TST/BEH/GATE/REG)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TST=Test Quality、verification.md 的 TST=Test Suite Completeness、port.md 的 TST=Test Porting)。跨清单引用时必须带清单限定(如 code-review.md TST 5.8),不得使用裸代码。
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. 在当前迭代所有工作项通过代码评审后执行;
|
||||
2. 逐项检查,判定为"通过"或"不通过";
|
||||
3. "不通过"项注明具体指标和差距;
|
||||
4. 全部通过后迭代正式 Done;
|
||||
5. DoD 行判定词汇 PASS/FAIL/UNVERIFIABLE 为 verify 阶段专用:UNVERIFIABLE=
|
||||
"无自动化验证手段(缺测试/缺工具/缺基线)",与评审维度的 verdict 枚举
|
||||
PASS/WARN/FAIL/UNRESOLVED 有意区分(WARN=评审软通过,UNRESOLVED=评审员
|
||||
崩溃/超时),勿混用。
|
||||
6. **DAG 路由产物解析**:DAG 运行不存在 legacy `{slug}/04-plan-*` /
|
||||
`{slug}/03-design-*` 页面,条目 1.5 引用的验收条件页
|
||||
`{slug}/04-plan-05-acceptance-criteria` 按 DAG-route read map 解析:
|
||||
→ 节点 `acceptance_criteria`(`{epic-slug}/dag`,含
|
||||
`{epic-slug}/dag-nodes/{node-id}` 下沉子页)或节点工单正文显式标注;
|
||||
standalone 模式以请求本身为规格。
|
||||
|
||||
---
|
||||
|
||||
## 1. DoD 矩阵完整性检查(DOD — Definition of Done Matrix)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 1.1 | DoD 矩阵中每一条验收条件有明确判定(PASS/FAIL/UNVERIFIABLE) | ☐ | ☐ | |
|
||||
| 1.2 | 每个 PASS 判定有可验证的证据(测试名称、命令输出、测量值) | ☐ | ☐ | |
|
||||
| 1.3 | 每个 FAIL 判定注明了具体差距(期望值 vs 实测值) | ☐ | ☐ | |
|
||||
| 1.4 | UNVERIFIABLE 项标记了原因(缺测试/缺工具/缺基线)并标注了重新激活路径(目标 chunk+iteration)。不可仅标注"未来处理"(wishlist) | ☐ | ☐ | |
|
||||
| 1.5 | DoD 矩阵覆盖本迭代的全部需求(与 wiki page `{slug}/04-plan-05-acceptance-criteria` 对照) | ☐ | ☐ | |
|
||||
| 1.6 | DoD 表中声明的每个测试用例 ID 在代码库中存在、本轮已运行且通过;缺失或未通过项记为 FAIL 并注明差距(期望断言 vs 实际)。`MANUAL`/`BENCH` 条目须附人工记录或基准输出 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 2. 测试完备性检查(TST — Test Suite Completeness)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------- | ---- | ------ | ---- |
|
||||
| 2.1 | `bun run test:parallel` 全部通过(或项目等同命令)。单次执行:同一棵树只跑一次,结果同时用于 DoD 判定与回归分类(5.1),仅当代码在验证中途变更时重跑 | ☐ | ☐ | |
|
||||
| 2.2 | 单元测试 + 集成测试均已运行(若项目有分离命令) | ☐ | ☐ | |
|
||||
| 2.3 | 所有测试输出已捕获,失败用例有详细记录 | ☐ | ☐ | |
|
||||
| 2.4 | 对于使用 `git worktree add/remove` 的模块,`test:parallel` 可能因 git 内部文件锁定而挂起。接受隔离/串行测试结果(`bun test <file> --timeout 120000`)并通过,并注明 TST-GIT-CONTENTION | ☐ | ☐ | |
|
||||
| 2.5 | 已统计本轮 flaky test(间歇性失败/跳过)数量并与基线对比;新增 flaky test 需追溯根因并路由回 Developer;本轮未修复的 flaky test 已在 Phase 5.56 登记为 `flaky-test` labeled issue(FT-NNN) | ☐ | ☐ | |
|
||||
| 2.6 | E2E 浏览器可用性已检查(`npx playwright install --dry-run` 或等效命令)。若浏览器未安装,E2E 相关 DoD 条目预标记为 `⚠️ UNVERIFIABLE — Playwright browsers not installed`,并在 reactivation trigger 中注明安装命令。禁止在无浏览器环境中对 E2E 条目标记 PASS | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 构建与类型检查(BLD — Build & Typecheck)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------- | ---- | ------ | ---- |
|
||||
| 3.1 | `bun typecheck` 无错误(或项目等同命令) | ☐ | ☐ | |
|
||||
| 3.2 | `bun lint` 无错误(警告可记录但非阻断) | ☐ | ☐ | |
|
||||
| 3.3 | 构建产物可正常生成(如项目有 build 步骤) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. 非功能需求验证(NFR — Non-Functional Requirements)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ----------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 4.1 | 本迭代覆盖的每个性能指标已测量(P50/P95/P99、吞吐量) | ☐ | ☐ | |
|
||||
| 4.2 | 性能指标与设计阈值和基线(前次迭代)双重对比 | ☐ | ☐ | |
|
||||
| 4.3 | 性能超出阈值的指标有差距分析和 profiling 热点 | ☐ | ☐ | |
|
||||
| 4.4 | 安全扫描已执行,无新高危漏洞引入 | ☐ | ☐ | |
|
||||
| 4.5 | 如无自动性能/安全测试,已标注为 UNVERIFIABLE 并建议具体工具 | ☐ | ☐ | |
|
||||
| 4.6 | 前端:Lighthouse / Core Web Vitals 性能评分未下降(FCP/LCP/TBT/CLS 均在阈值内) | ☐ | ☐ | |
|
||||
| 4.7 | 前端:bundle 体积分析已运行,无预期外增长(新增 chunk > 50KB 需说明理由) | ☐ | ☐ | |
|
||||
| 4.8 | 前端:axe-core / Lighthouse a11y 扫描无新增违规项 | ☐ | ☐ | |
|
||||
| 4.9 | 前端:可访问性(键盘导航、屏幕阅读器、对比度)已验证通过 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. 回归检查(REG — Regression)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | ---------------------------------------------- | ---- | ------ | ---- |
|
||||
| 5.1 | 之前迭代通过的测试全部继续通过(零回归)——复用 2.1 同一次全量运行的结果判定,不重复执行 | ☐ | ☐ | |
|
||||
| 5.2 | 任何回归有失败单元测试名和可能原因分析 | ☐ | ☐ | |
|
||||
| 5.3 | 如无之前迭代的测试基线,首次迭代此项标记为 N/A | ☐ | ☐ | |
|
||||
| 5.4 | 用引用缺失/空 `{file:...}` 令牌的可选配置启动 TUI/CLI,确认启动不崩溃。回归测试位于 `<harness-package>/test/config/config-content-substitution.test.ts` | ☐ | ☐ | |
|
||||
| 5.5 | 每个失败用例已在 base 分支/最近绿色 commit 上单独复跑并分类:base 上也失败 → 预先存在(BF,Phase 5.55 登记);base 通过本分支失败 → 回归(阻塞并路由 Developer)。禁止把预先存在失败当作"别人的问题"丢弃 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 6. 报告完整性检查(RPT — Report Completeness)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 6.1 | 验证报告写入 wiki `{slug}/05-verify-iteration-{N}`(via `wiki 读写 API(见 TERMINOLOGY)`) | ☐ | ☐ | |
|
||||
| 6.2 | 报告包含 DoD 矩阵汇总(PASS/FAIL/UNVERIFIABLE 计数) | ☐ | ☐ | |
|
||||
| 6.3 | 报告包含 NFR 验证结果 | ☐ | ☐ | |
|
||||
| 6.4 | 报告包含回归检查状态 | ☐ | ☐ | |
|
||||
| 6.5 | 报告包含 FAIL 项的处理路径(路由回 Developer 或标记为已知) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 7. 上下文卫生验证(CTX — Context Hygiene)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | ----------------------------------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 7.1 | 迭代执行中每个 stage 边界执行了压缩,或确认上下文未超限无需压缩 | ☐ | ☐ | |
|
||||
| 7.2 | 每次压缩后重读了 wiki `{slug}/` 页面恢复当前 slug/stage(非仅依赖压缩摘要);读取模式见 `_shared/gitea-read-patterns.md`(deprecated: `.artifacts/{slug}/`) | ☐ | ☐ | |
|
||||
| 7.3 | 多轮 review 的 round 边界压缩遵循(round ≥ 2 时 compact + 重读 synthesis comment 与 commit status(`octopus review status --stage ...`)) | ☐ | ☐ | |
|
||||
| 7.4 | 未在 stage 中途(tool-call 循环 / sub-agent 派发过程中)执行压缩 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 8. 技术债务登记检查(TD-LOCAL — Tech Debt Local Registration)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 8.1 | 本迭代全部 UNVERIFIABLE / ACCEPTED_RISK / `[OPEN]` 项均已登记为源票 `## TD 登记` 评论中的行(registry-first,`ticket-lifecycle.md`;`filing.sprint-mode: true` 期间禁止升格独立票) | ☐ | ☐ | |
|
||||
| 8.2 | 每行含 `TD-NNN`、type、Severity、origin、一句话摘要与可客观验证的 Reactivation Trigger;TD-NNN 为注册期分配——经 `script/td-alloc.sh` 从号段台账(TD allocation ledger,常设 tracker)取号,任何分配必须先落台账 td-alloc 评论([org-internal #3322] 互斥) | ☐ | ☐ | |
|
||||
| 8.3 | 两处登记的 `TD-NNN` 标识一致,且每项都有可客观验证的 Reactivation Trigger | ☐ | ☐ | |
|
||||
| 8.4 | 邻近配额复核([org-internal #3002] G3):报告含本迭代触碰区域的登记册带走情况(consumed/applicable,或 `0 applicable`);与本 run 的债务配额记录一致(legacy roadmap 状态追踪表已归档,[org-internal #3072] phase 3) | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 9. 基线失败登记检查(BF-LOCAL — Baseline-Failure Registration)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| --- | --------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 9.1 | 本迭代全部预先存在失败(Phase 2.6 分类为 pre-existing)均已创建为 Gitea issue(label `baseline-failure`,via `工单 API(见 TERMINOLOGY)list(labels="baseline-failure")` 可查) | ☐ | ☐ | |
|
||||
| 9.2 | 每个 baseline-failure issue 含 `BF-NNN`(标题)、Test 标识、Severity(Priority label)、`## Parent` 交叉链接、可在 base commit 复现的 Reproduction 步骤 | ☐ | ☐ | |
|
||||
| 9.3 | 已对存量 `baseline-failure` issue 去重(按 failure signature 匹配——错误/断言形状 + 受影响表面,同一根因家族可跨多个测试,与 verify Phase 5.55 及 `core/rules/ticket-lifecycle.md` BF 家族伞口径一致),未重复登记已知失败;若本迭代修复了既有 BF,已关闭对应 issue 并引用修复 commit(编号/去重规则见 `core/rules/testing.md`) | ☐ | ☐ | |
|
||||
| 9.4 | 验证报告 "Baseline Failures (Pre-existing)" 段落记录了 `BF-NNN → #NNNN` 映射(无则记 "0 baseline failures");未登记任何失败时本节为空方为通过 | ☐ | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 10. Flaky Test 登记检查(FT-LOCAL — Flaky-Test Registration)
|
||||
|
||||
| # | 检查项 | 通过 | 不通过 | 备注 |
|
||||
| ---- | --------------------------------------------------------------------- | ---- | ------ | ---- |
|
||||
| 10.1 | 本迭代全部未修复 flaky test 均已创建为 Gitea issue(label `flaky-test`,via `工单 API(见 TERMINOLOGY)list(labels="flaky-test")` 可查) | ☐ | ☐ | |
|
||||
| 10.2 | 每个 flaky-test issue 含 `FT-NNN`(标题)、Test 标识、Severity(Priority label)、Failure rate、`## Parent` 交叉链接、复现步骤(多次重跑) | ☐ | ☐ | |
|
||||
| 10.3 | 已对存量 `flaky-test` issue 去重(按 failure signature 匹配——错误/断言形状 + 受影响表面,与 verify Phase 5.56 及 `core/rules/ticket-lifecycle.md` FT 家族伞口径一致);未重复登记已知 flaky;已稳定的 flaky 已关闭并引用修复 commit(编号/去重规则见 `core/rules/testing.md`) | ☐ | ☐ | |
|
||||
| 10.4 | 验证报告 "Flaky Tests (Intermittent)" 段落记录了 `FT-NNN → #NNNN` 映射(无则记 "0 flaky tests") | ☐ | ☐ | |
|
||||
@@ -0,0 +1,116 @@
|
||||
# 工件寻址契约(Core,后端中立)
|
||||
|
||||
> Core 原生契约(Increment 3,无 dogfood 对应源文件)。术语对照见
|
||||
> `core/adapters/TERMINOLOGY.md`;本文所有实例路径以占位符表述
|
||||
> (`<runs-root>` = 实例运行目录根),落地实例时按术语表绑定。
|
||||
|
||||
## 1. ref 语法
|
||||
|
||||
所有 Tier-2 工件的规范引用格式:
|
||||
|
||||
```
|
||||
{backend}:{kind}/{owner}/{repo}/{id}#{anchor}
|
||||
```
|
||||
|
||||
- `{backend}` — 工单后端标识(如 `gitea`);由 adapter 层注册。
|
||||
- `{kind}` — 工件类别,枚举见下表;新类别扩展时须同步各 adapter 的
|
||||
resolve 实现并在本表登记。
|
||||
- `{owner}/{repo}` — 仓库坐标。
|
||||
- `{id}` — 工件标识:issue/PR 用数字,wiki 页用页名(可含 `/`),
|
||||
评论用 `{issue-number}c{comment-id}`,commit-status 用 `{sha}@{context}`。
|
||||
- `#{anchor}` — 可选锚点(页内段落、行号等),解析时透传不解释。
|
||||
|
||||
| kind | 含义 | id 形态 |
|
||||
| --------------- | --------------- | ---------------------- |
|
||||
| `issue` | 工单 | 数字 |
|
||||
| `issue-comment` | 工单评论 | `{issue}c{comment-id}` |
|
||||
| `wiki-page` | Tier-2 工件库页 | 页名(含 `/`) |
|
||||
| `commit-status` | 提交状态 | `{sha}@{context}` |
|
||||
| `pr` | 合并请求 | 数字 |
|
||||
|
||||
## 2. 页名文法(Core 契约)
|
||||
|
||||
Tier-2 工件库的**常规页名**:
|
||||
|
||||
```
|
||||
{slug}/{type}-{seq:02d}-{title}
|
||||
```
|
||||
|
||||
- `{slug}` 限 `[a-z0-9-]`;`{seq}` 两位零填充;`{title}` 限
|
||||
`[a-z0-9-]`(CJK 标题按 adapter 层编码规则处理)。
|
||||
|
||||
### 2.1 例外页(全枚举)
|
||||
|
||||
以下页名不受常规文法约束(勘自实例的全部真实约定):
|
||||
|
||||
| 例外页 | 形态 | 说明 |
|
||||
| -------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------- |
|
||||
| 评审轮次页 | `{slug}/reviews/{stage}/round{N}/{page}` | `page` ∈ `task-{ROLE}`、`revision-summary` |
|
||||
| 评审终报 | `{slug}/reviews/{stage}/final/report` | 含 DAG task 变体 `…/final/report-task-{node-id}` |
|
||||
| 验证报告 | `{slug}/05-verify-iteration-{N}` | 常规前缀 + 无 title 段的变体 |
|
||||
| 验证报告(里程碑) | `{epic-slug}/05-verify-milestone-{M-id}` | 同上 |
|
||||
| 验证报告(任务节点) | `{epic-slug}/05-verify-task-{node-id}` | 同上 |
|
||||
| DAG 工件 | `{epic-slug}/dag`、`{epic-slug}/dag-nodes/{node-id}`、`{epic-slug}/dag-coverage` | 单门 DAG 管线工件 |
|
||||
| DAG 共享契约 | `{epic-slug}/shared/{file}` | 跨会话契约 |
|
||||
| bugfix 附件 | `{slug}/repro-notes`、`{slug}/test-report`、`{slug}/bugfix-report`、`{slug}/ABORT` | 单段固定名 |
|
||||
| 原型/笔记 | `{slug}/prototype-debt`、`{slug}/spike-report`、`{slug}/impl-notes` | 单段固定名 |
|
||||
| 设计修订 | `{slug}/03-design-amendments` | 常规前缀 + 固定名 |
|
||||
| 浏览器证据 | `{slug}/verify/evidence/{name}` | 验证证据页 |
|
||||
| 审计轮次页 | `audit/{date}/round{N}/{page}` | 日期 slug 例外;`page` ∈ `synthesis`、`task-{ROLE}` |
|
||||
| 审计终报 | `audit/{date}/final/report` | 日期 slug 例外 |
|
||||
| 回顾报告 | `_retrospectives/{cycle-name}` | 跨 slug 命名空间例外 |
|
||||
| 技能评估 | `_evals/{skill-name}/{page}` | 评估命名空间例外 |
|
||||
| 移植工件 | `port-{name}/source-analysis/{file}`、`port-{name}/self-check` | 移植命名空间例外 |
|
||||
| 回顾归档 | `_archive/{slug}/…` | 归档命名空间例外 |
|
||||
|
||||
> 历史只读页名(旧管线产物,仍可读取):`{slug}/01-stakeholder-interview`、
|
||||
> `{slug}/02-requirements-index`、`{slug}/02-req-{seq:02d}-{title}`、
|
||||
> `{slug}/02-03-req-design`、`{slug}/03-design-{seq:02d}-{title}`、
|
||||
> `{slug}/03-adr-{NNNN}-{title}`、`{slug}/04-plan-index`、
|
||||
> `{slug}/04-plan-{seq:02d}-{title}`、`{slug}/roadmap/{page}`。
|
||||
|
||||
### 2.2 kind ↔ type 映射
|
||||
|
||||
| 工件类别(kind 语境) | 页名 `{type}` 段 |
|
||||
| --------------------- | ---------------------------------------------------------------- |
|
||||
| 验证报告 | `05-verify-iteration` / `05-verify-milestone` / `05-verify-task` |
|
||||
| 设计文档 | `03-design` / `03-adr` / `03-design-amendments` |
|
||||
| 计划文档 | `04-plan` |
|
||||
| 需求文档 | `02-req` / `02-requirements-index` |
|
||||
| 评审工件 | `reviews`(目录段,非前缀) |
|
||||
| DAG 工件 | `dag` / `dag-nodes` / `dag-coverage` |
|
||||
| 审计工件 | `audit`(日期前缀命名空间) |
|
||||
|
||||
## 3. Tier-1 ↔ Tier-2 映射
|
||||
|
||||
wiki 页 `{slug}/…` 与实例运行目录 `<runs-root>/{slug}/…`(Tier-1 本地
|
||||
结构化工件,见 Two-Tier 规则)按 slug 一一对应:
|
||||
|
||||
- 页 `{slug}/reviews/{stage}/round{N}/findings-{DIM}.json` ↔ 本地
|
||||
`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIM}.json`;
|
||||
- 页面正文承载 Tier-2 决策记录;原始发现、工作草稿留在 Tier-1 本地。
|
||||
|
||||
**文件名 URL 编码规则**:页名映射为本地文件名时 `/` → `%2F`
|
||||
(逆向解码同理);`.-` 尾缀是实例后端生成的 slug 产物,解码时剥除。
|
||||
|
||||
## 4. 双向链接不变量
|
||||
|
||||
1. **工件 ↔ 工单引用成对**:工件发布到工件库后,源工单侧必须有反向
|
||||
索引(工件索引评论);工单侧索引的每一行必须指向真实存在的工件。
|
||||
2. **单评论聚合**:一个源工单有且仅有一条工件索引评论,各技能只
|
||||
原位增改自己的行,绝不发第二条。
|
||||
3. **归档补全**:工单关闭时,关闭方 agent 原位编辑索引评论——加归档
|
||||
横幅、全部行重读优先级置 `ARCHIVE`;不删行、不改位置列。
|
||||
|
||||
## 5. adapter 义务
|
||||
|
||||
每个工单后端 adapter 必须实现三个操作:
|
||||
|
||||
- **parse** — 解析 ref 字符串为 `{backend, kind, owner, repo, id, anchor}`;
|
||||
- **resolve** — 把解析结果解析为该后端可调用的 API 形态(端点、工具名、
|
||||
参数),API 调用形态归 adapter 层,Core 不约束;
|
||||
- **validate** — 校验页名符合第 2 节文法(含例外枚举)。
|
||||
|
||||
页名规范是**后端中立契约**;仓里的共用解析器
|
||||
`script/resolve-artifact-ref.ts` 提供 parse 与页名/文件名编解码、
|
||||
Tier-1 路径预测的中立实现,adapter 可复用。
|
||||
@@ -0,0 +1,38 @@
|
||||
> Core 中立版(Increment 4 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Artifact-Index Guards (mandatory)
|
||||
|
||||
> Check-mechanized (injection-budget batch-3, [org-internal #3699]): guards
|
||||
> 1-3's checkable invariants run in the repo's artifact-index-check script
|
||||
> (`<artifact-index-check-cmd> <ticket>`, see the term mapping; exit 4 =
|
||||
> violation, `--json` summary; tolerant of wild archive-marker dialects).
|
||||
> This file is the normative definition, NOT in any `agents_md` injection
|
||||
> whitelist since 2026-08-29 — do not re-add; the checks run at write/close
|
||||
> time regardless. Full spec: Pattern 10 in the shared write-patterns doc —
|
||||
> this rule is its on-demand distill.
|
||||
|
||||
### 1. Legacy-prefix scan (never create a duplicate)
|
||||
|
||||
Before creating a `## 工件索引` comment, scan the issue's existing comments
|
||||
for the legacy prefix `## Pipeline 工件追踪表`. If found, UPGRADE it
|
||||
in place (edit, not create) and rename. NEVER create a second index comment
|
||||
(DEFECT-1 / [org-internal #1924]). Mechanized: reports the comment_id to edit.
|
||||
|
||||
### 2. Never-duplicate invariant (single-comment)
|
||||
|
||||
Exactly ONE index comment per source issue, maintained by in-place
|
||||
comment-edit via the ticket-backend comment API (reuse the same
|
||||
`comment_id`). Each skill adds/updates ONLY its own rows, never deletes
|
||||
another skill's rows. Mechanized: >1 prefixed comment (either prefix) →
|
||||
exit 4.
|
||||
|
||||
### 3. Archive-at-close (issue close → archive, by ANY closing agent)
|
||||
|
||||
When a source issue is CLOSED — by ANY agent, verify ran or not — the closing
|
||||
agent edits the index comment in place: (a) add the canonical archive banner
|
||||
(Pattern 10) ABOVE the table with the actual close date; (b) set EVERY row's
|
||||
重读 column to `ARCHIVE`. Do NOT delete rows or change 位置-column URLs;
|
||||
reversible in-place edit. Mechanized: closed-ticket marker + 重读
|
||||
completeness → exit 4. Sequence/actor notes: the issue-checklist-sync rule
|
||||
§ 归档动作; primary path `verify` Phase 5.6 (Verifier); verify-skipping routes
|
||||
(audit-only, documentation, standalone/bugfix) — the closing agent does it.
|
||||
@@ -0,0 +1,109 @@
|
||||
> Core 中立版(Increment 2 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Auto-approve stages
|
||||
|
||||
The `auto_approve` block in the instance config controls which pipeline gates
|
||||
the agent passes without asking the user. Stage identifiers are matched
|
||||
against the stage registry (harness 内建的阶段注册模块). Unknown or retired
|
||||
identifiers fall back to manual confirmation (fail-closed) with a one-time
|
||||
`auto_approve.unknown_stage` warning — copy ids verbatim from the table
|
||||
below.
|
||||
|
||||
## Scope boundary: `auto_approve` vs `permission`
|
||||
|
||||
Two **independent mechanisms** — confusing them is the most common
|
||||
"auto-approve isn't working" report:
|
||||
|
||||
- **`auto_approve`** gates **only the `question` tool** (pipeline decision
|
||||
questions; the harness 内建的自动批准判定模块); no effect on tool
|
||||
execution.
|
||||
- **`permission`** gates **tool / file-system operations** (`bash`, `read`,
|
||||
`write`, `external_directory`, …) and **never consults `auto_approve`**.
|
||||
`external_directory` defaults to ask, so outside-project prompts are
|
||||
expected even with every stage enabled.
|
||||
|
||||
**`auto_approve` does NOT suppress permission prompts — intentionally so.**
|
||||
To stop a permission prompt, change the matching `permission` rule or use the
|
||||
UI's "auto-accept permissions" toggle (browser-local, not the project
|
||||
`auto_approve` config).
|
||||
|
||||
| Stage | Gate | Risk |
|
||||
| --------------- | ---------------------------- | -------------------------------------------- |
|
||||
| `review-dag` | DAG single-gate review | Medium — sets direction |
|
||||
| `review-code` | Code review | Medium — gates what reaches main |
|
||||
| `verify` | Iteration verification | Medium — runs tests / DoD matrix |
|
||||
| `audit-process` | Process infrastructure audit | Medium — read-only but gates process changes |
|
||||
| `merge-pr` | Create PR + merge to main | High — irreversible landing |
|
||||
|
||||
> Risk values mirror the stage registry's `BUILTIN_STAGES[].risk` — the
|
||||
> registry is the runtime authority; if this table and the registry disagree,
|
||||
> fix this table. `merge-pr` is excluded from default/preset `stages` arrays
|
||||
> (not structurally blocked; explicitly adding it is not recommended).
|
||||
> Legacy stage retirement (12 → 5): the phase-3 retirement ruling doc
|
||||
> (Tier-2 reference, reachable via the ticket backend).
|
||||
|
||||
Config shape: `"auto_approve": { "stages": ["review-code", "verify"], "stageless": false }` — `stages` empty (`[]`) = all manual; `stageless: true` also auto-approves questions called without a stage. Absent block defaults to `{ stages: ["review-code", "verify", "audit-process"], stageless: false }`; the removed `enabled` field migrates on load (mapping: Tier-2 reference page `rules/auto-approve-detail`). Presets (`safe`/`full`/`off`/`show`): same reference page — `full` never includes `merge-pr`.
|
||||
|
||||
### Stage matching behavior
|
||||
|
||||
- Question has a stage → auto-approved iff that stage is in `stages`.
|
||||
- Stageless question → auto-approved iff `stageless: true`; otherwise prompts.
|
||||
- `destructive: true` → ALWAYS prompts, regardless of config — the safety net
|
||||
for irreversible actions; business-decision questions are never covered by
|
||||
a stage whitelist (§"Mandatory destructive list").
|
||||
|
||||
Check logic: the harness 内建的问询与自动批准判定模块
|
||||
(resolveAutoApproveDecision).
|
||||
|
||||
### Live-gate degradation chain ([org-internal #3531])
|
||||
|
||||
An auto-approved question is NOT consumed instantly: L1 — a live human
|
||||
watching the session (SSE declaration / fresh presence ping) answers within
|
||||
`question_live_gate.timeout_sec` (default 600) and overrides the default;
|
||||
L2 — blocking questions with a `#<ticket>` title and a ticket-backend token
|
||||
fall back to a ticket comment answered as `**answer <question-id>** <label>`
|
||||
(default 3600s); L3 — auto default (first option, annotated
|
||||
`live-gate … → auto default`). `timeout_sec: 0` disables the whole gate
|
||||
(legacy instant default); `l2_timeout_sec: 0` disables only L2.
|
||||
`nonblocking: true` questions never reach L2/L3 — no live human → parked in
|
||||
the pending-answer queue, answered later from the console. Every final
|
||||
decision publishes a `question.degradation.routed` event.
|
||||
**`destructive: true` is untouched by the chain**: `decide()` rejects it
|
||||
before the gate runs (iron law).
|
||||
|
||||
### Mandatory destructive list (业务决策类问询强制清单)
|
||||
|
||||
Origin: [org-internal #3223] — a stage whitelist auto-answered two
|
||||
irreversible production data changes because nothing marked them
|
||||
`destructive`. A stage whitelist grants "execution gates may proceed
|
||||
uninterrupted"; it must NEVER grant "business decisions may be
|
||||
self-answered". `stage` names the pipeline phase, `destructive` names the
|
||||
business risk.
|
||||
|
||||
**Iron Law: a question whose recommended/executed action falls in ANY category
|
||||
below MUST be called with `destructive: true`. The auto-approve stage whitelist
|
||||
is never a substitute for human confirmation of these categories.**
|
||||
|
||||
1. **Merge / release landing** — merging a PR to main, cutting a tag or
|
||||
release, deploying/promoting to production (server-side auto-merge is a
|
||||
human-configured policy robot, never agent self-answer latitude; kill
|
||||
switch: the auto-merge-low-risk runbook, Tier-2 reference via the ticket
|
||||
backend).
|
||||
2. **Production database writes** — upsert/update/delete against production
|
||||
data stores, production data migrations, live-data backfills.
|
||||
3. **Billing / plan changes** — plan or tier migration, pricing/concurrency
|
||||
changes, anything touching billing state.
|
||||
4. **Org / tenant structure changes** — org or workspace migration, tenant
|
||||
topology changes, moving resources between orgs.
|
||||
5. **Deletion of durable resources** — deleting data, branches, worktrees,
|
||||
deployments, or any resource outside `/tmp` and session-scratch space.
|
||||
6. **Persistent config writes** — config changes that outlive the session
|
||||
(writing to the instance config file, the instance process-config area,
|
||||
deploy/systemd units, or other on-disk configuration consumed by other
|
||||
sessions/services).
|
||||
|
||||
This list is the floor, not the ceiling: any other irreversible
|
||||
business-visible decision also carries `destructive`. When in doubt, mark it —
|
||||
a redundant prompt costs one round-trip; an auto-approved production write can
|
||||
cost the data. Mirrored in the question tool's self-description for paths
|
||||
without L1 injection.
|
||||
@@ -0,0 +1,56 @@
|
||||
## Claim Discipline (mandatory)
|
||||
|
||||
Every status assertion about work — "tests pass", "build succeeds", "bug fixed",
|
||||
"work complete", "no regressions", "fully resolves" — is a CLAIM that requires
|
||||
FRESH evidence produced this turn. Confidence is not evidence; a prior run is
|
||||
not evidence; the change "looking right" is not evidence. Baseline testing
|
||||
showed agents asserting "tests pass, no regressions introduced" for changes
|
||||
whose tests were never run — this rule exists to stop that.
|
||||
|
||||
**Iron Law: NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE.**
|
||||
|
||||
Before asserting any status, run the gate:
|
||||
|
||||
1. **IDENTIFY** — what exact command/output proves this claim?
|
||||
2. **RUN** — execute it fresh, this turn (do not recall a prior result).
|
||||
3. **READ** — the full output and exit code, not just the tail.
|
||||
4. **VERIFY** — does the output actually confirm the claim?
|
||||
5. **ONLY THEN** make the claim, and cite the evidence (`file:line`, test name,
|
||||
exit code, the command run).
|
||||
|
||||
**Red-flag language** — reword or remove unless you attach evidence: "should
|
||||
work", "probably", "seems to", "I'm confident", "fully tested", "comprehensive",
|
||||
"no regressions", "fully resolves", "works in all cases", "regardless of why",
|
||||
"exactly", and any expression of satisfaction ("Done!", "Great!", "Perfect!")
|
||||
uttered before verification has run.
|
||||
|
||||
**If you cannot run the proving command** (hypothetical/analysis task with no
|
||||
codebase, blocked environment): say so — "not verified; would require running X"
|
||||
— and downgrade the claim from assertion to hypothesis. An honest "unverified"
|
||||
beats a confident unevidenced claim.
|
||||
|
||||
This applies everywhere: commit messages, PR bodies, status reports, review
|
||||
conclusions, handoff summaries, and verbal confirmations to the user.
|
||||
|
||||
## Tool-failure honesty (never fabricate results)
|
||||
|
||||
The Iron Law above governs claims about work; this section governs the
|
||||
**source** of those claims. When a tool you need is unavailable, returns an
|
||||
error, returns empty, or you cannot otherwise access a required resource, you
|
||||
MUST report the failure honestly. NEVER fabricate, invent, or hallucinate a
|
||||
plausible-looking result to fill the gap.
|
||||
|
||||
- An honest "I could not call `gitea_wiki__get_page`, so I cannot review
|
||||
dimension Y" is always correct. An honest failure beats a confident fiction.
|
||||
- Producing a complete, specific-looking structured result — review JSON with
|
||||
findings, severity levels, and file references, all invented — without a
|
||||
successful tool call that supplies the evidence is a **critical defect**,
|
||||
more dangerous than aborting: the output looks credible but is entirely
|
||||
fictional and silently poisons every downstream decision.
|
||||
- This covers every tool-gated output: review findings, test results, file
|
||||
contents, API responses, search hits, metrics. If the tool did not return
|
||||
it, you do not know it — say so.
|
||||
|
||||
Origin: [org-internal #2115] root cause 5 / [org-internal #2118] — deepseek-v4-flash reviewers fabricated
|
||||
full review JSON when the `gitea_wiki__*` tools were unavailable to subagents.
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
## Code Graph First (mandatory)
|
||||
|
||||
Before any cross-file understanding or change, query the code graph first. Do
|
||||
NOT grep + read whole files to reconstruct relationships.
|
||||
|
||||
- **Session start**: run `codegraph_status` to confirm the graph is built; if
|
||||
empty, run `codegraph init -i` from the project root once. The graph persists
|
||||
and is reused for the whole session.
|
||||
- **Trust but verify stats**: `codegraph_status` can return a stale or partial
|
||||
snapshot. If counts look low, run `codegraph_status` again — codegraph
|
||||
auto-syncs on file changes (2s debounce via native OS file watcher).
|
||||
Do not run structural queries against a graph you suspect is incomplete.
|
||||
- **Structural questions** ("who calls / what depends on / where defined / what
|
||||
breaks if I change this") MUST use `codegraph_explore` (primary, for flows and
|
||||
surveys), `codegraph_search` (locate symbols), `codegraph_callers` (every call
|
||||
site).
|
||||
Do not piece relationships together from grep + read output.
|
||||
- **Reading an implementation**: use `read(filePath, symbol: "<name>")` to fetch
|
||||
only the target symbol's line range. Do not read an entire large file to see
|
||||
one function.
|
||||
- **grep is the fallback, not the default**: use grep only for fuzzy text the
|
||||
graph cannot answer (comments, string literals, config text). Use
|
||||
`codegraph_node` with `symbolsOnly: true` for a cheap structural overview instead of Glob.
|
||||
- **Delegate exploration**: prefer running graph + read inside an Explorer
|
||||
subagent and returning only `file:line` results and conclusions, to keep the
|
||||
main context lean.
|
||||
|
||||
## Per-worktree indexes (mandatory for worktree sessions)
|
||||
|
||||
The main checkout's `.codegraph/` indexes ONLY that tree. A git worktree lives
|
||||
outside the repo path, so nearest-`.codegraph/` resolution finds nothing under
|
||||
it — graph queries in a workflow worktree silently degrade to grep+read
|
||||
(the [org-internal #2561] / TD-006 gap).
|
||||
|
||||
- `script/session-worktree.sh create` and `script/scratch-worktree.sh create`
|
||||
build a per-worktree index automatically: `codegraph init -i <path>` runs in
|
||||
the background (best-effort — missing CLI, existing index, or init failure
|
||||
never blocks worktree creation). Set `CODEGRAPH_SKIP=1` to disable
|
||||
(selftests / CI).
|
||||
- Worktrees created BEFORE this hook landed have no index: run
|
||||
`codegraph init -i` once from inside them.
|
||||
- First query after creating a worktree may hit a still-building index; if
|
||||
results look empty, check `codegraph status` inside the worktree, then retry.
|
||||
- Kickoff readiness (`checkCodegraph`) checks `<root>/.codegraph/codegraph.db`
|
||||
per root, and `codegraph serve --mcp` resolves from the session's cwd — both
|
||||
pick the worktree's own index once it exists.
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
> Core 中立版(Increment 2 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Compaction Discipline (mandatory)
|
||||
|
||||
Context compaction keeps long pipelines inside the model's context window.
|
||||
Two mechanisms exist:
|
||||
|
||||
1. **Stage-boundary compaction** (main session / orchestrator) —
|
||||
capacity-driven [org-internal #2601]: compact at a clean stage boundary
|
||||
ONLY when a capacity trigger holds (below), not merely because the
|
||||
boundary was reached.
|
||||
2. **Round-boundary compaction** (multi-round reviews) — compact at every
|
||||
review round ≥ 2, after the round's state is persisted.
|
||||
|
||||
### Stage-boundary compaction (capacity-driven, [org-internal #2601])
|
||||
|
||||
The main session orchestrates the whole pipeline while delegating stages to
|
||||
subagents, but still accumulates their result summaries, planning reasoning,
|
||||
and todo state — over a full pipeline this can exhaust the context window.
|
||||
|
||||
**Rule — compact at clean boundaries only when capacity demands it.** Every
|
||||
stage persists its complete output to durable artifacts (Tier-2 artifact
|
||||
pages under `{slug}/…`, Tier-1 run workspace files) on completion. At each
|
||||
CLEAN boundary (artifacts written, next stage not yet dispatched), evaluate
|
||||
the triggers:
|
||||
|
||||
1. **Capacity trigger** — the harness's compaction recommendation has fired,
|
||||
context is near the threshold, or the agent observes context-pressure
|
||||
symptoms (truncation, degraded recall of earlier stage outputs).
|
||||
2. **Projection trigger** — the stages REMAINING on this route (skip list in
|
||||
the routing table) will not fit alongside the context already
|
||||
accumulated, based on per-stage growth observed so far.
|
||||
|
||||
A trigger holding → compact at the NEAREST clean boundary. No trigger → KEEP
|
||||
the context: preserved working context is worth more than the tokens it costs.
|
||||
|
||||
**Mixed capacity threshold — min(water_level, absolute cap)
|
||||
[org-internal #3321] / TD-434]**: the capacity threshold is
|
||||
`min(context × water_level, compaction.max_context_tokens)` — whichever is
|
||||
LOWER (`max_context_tokens` default 100_000; `0` restores pure water_level
|
||||
semantics [org-internal #3342]). On large windows the 100k cap binds first.
|
||||
Measurement basis: the provider's REAL reported usage (input tokens), not
|
||||
local estimates; the rendered guidance and the Trigger A/B overflow paths
|
||||
resolve the same normalized values (harness 内建的压缩引导模块, see the
|
||||
instance term mapping).
|
||||
|
||||
**Sub-session posture [org-internal #3321]**: Explorers compact by returning
|
||||
promptly, persisting only what their skill protocol REQUIRES (review findings
|
||||
JSON to the run workspace reviews directory). Workers persist outputs to
|
||||
Tier 1 BEFORE finishing. The parent context is where the mixed threshold
|
||||
applies. Tier 1 boundary: two-tier-artifacts rule.
|
||||
|
||||
**Short runs target zero intra-run compaction** — an implement →
|
||||
review-code → verify run fits one window; clean boundaries do not compact
|
||||
without a trigger. (Rationale + retired legacy stages: Tier-2 reference page
|
||||
`rules/compact-detail`, reachable via the ticket backend.)
|
||||
|
||||
Clean boundaries where triggers are evaluated (live skills):
|
||||
|
||||
- After `analyze-dag` completes (before the `review-dag` single gate)
|
||||
- After each `review-*` converges (before the next production stage)
|
||||
- After `implement` / `verify` completes (before its review or the next stage)
|
||||
|
||||
**Never compact mid-stage** — not mid tool-call cycle, not mid subagent
|
||||
dispatch. Compact only at clean boundaries where artifacts are already
|
||||
persisted.
|
||||
|
||||
**Re-orient after compacting** (recovery contract): compaction discards the
|
||||
in-conversation record of the current slug and stage. Before resuming,
|
||||
re-read the persisted recovery set — the Tier 1 compact-recovery entry
|
||||
`index.json` in the run workspace, the issue's `## 工件索引` CORE rows, the
|
||||
frozen DAG copy (`{epic-slug}/dag`, `{epic-slug}/dag-nodes/{node-id}`) for
|
||||
DAG-routed runs, the latest review pages, or the DoD matrix
|
||||
(`{slug}/05-verify-*`) — to recover the current slug, stage, and outstanding
|
||||
items. Do not rely on the compacted summary alone.
|
||||
|
||||
**Identity verification precedes re-orientation [org-internal #2459].**
|
||||
Every compacted summary opens with a harness-injected `### Session Identity`
|
||||
card (agent, role/mode/ticket, CORE artifact rows, role re-read set),
|
||||
rendered from role-yaml `compact.preserve` lists and issue CORE rows by the
|
||||
harness 内建的压缩引导模块 (renderIdentityInjection; preserve lines keyed
|
||||
through identityPreserveLines). Role yaml preserve lists are
|
||||
harness-enforced — recovery is always via the persisted artifact, never
|
||||
conversation memory. Verify the card against the durable record
|
||||
(title/purpose, issue assignee + claim, orchestrator id); on mismatch stand
|
||||
by and report, never act on the summary's identity claims.
|
||||
|
||||
### Round-boundary compaction
|
||||
|
||||
Multi-round review skills (`review-artifact`, `review-code`) compact at every
|
||||
round boundary ≥ 2. Canonical text: shared compaction-round-boundary doc
|
||||
(injected by both review skills); read it for the exact orchestrator
|
||||
procedure — do not restate.
|
||||
|
||||
### The `min_turns_since_compaction` guard applies everywhere
|
||||
|
||||
The `compact` tool rejects calls made too soon after the previous one
|
||||
(`compaction.agent.min_turns_since_compaction`, default **3** assistant
|
||||
turns); no boundary type is exempt. Beyond `insufficient_turns` the tool has
|
||||
more guard outcomes (`min_interval`, `no_model`, `circuit_breaker_open`,
|
||||
`disabled` — rejected; `compaction_in_progress`, `deduplicated` — deferred;
|
||||
full table: Tier-2 reference page `rules/compact-detail`, reachable via the
|
||||
ticket backend). All are accept-and-continue: never retry past a guard,
|
||||
never work around the dedup/breaker state. A boundary whose compaction was
|
||||
rejected or deferred simply keeps its context; the persisted artifacts still
|
||||
bound what accumulates.
|
||||
|
||||
### Role-level re-read set
|
||||
|
||||
Role YAML `compact.preserve` lists are harness-enforced: after any compaction
|
||||
the session agent's preserve lines are injected at the summary head; recovery
|
||||
is always via the persisted artifact, never conversation memory.
|
||||
@@ -0,0 +1,159 @@
|
||||
> Core 中立版(Increment 4 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Issue Checklist Sync (mandatory, progressive)
|
||||
|
||||
The source issue's checklist is the **stakeholder-facing progress tracker**.
|
||||
It MUST be kept in sync with actual work state at every pipeline milestone —
|
||||
not just at close time. Stakeholders should be able to open the issue at any
|
||||
point and see exactly what is done, in progress, and deferred.
|
||||
|
||||
### Sync points (progressive)
|
||||
|
||||
| When | Who | What |
|
||||
|------|-----|------|
|
||||
| After the DAG artifact is frozen (`review-dag` PASS + freeze confirmation per `analyze-dag/SKILL.md` §Output & Lifecycle step 2, [org-internal #3379]) | Aggregator agent | Convert the source Epic's draft acceptance criteria into a formal checklist; annotate requirement-covered items `_(DAG frozen: {epic-slug}/dag v1)_` |
|
||||
| After each iteration commit (`implement` Phase 4.5) | Developer | Check off items delivered by this iteration |
|
||||
| After a PR is created | Developer | Append a `## 当前状态` live-status section (or update it) with PR number + mergeable; in an Epic task list, append the PR reference to the corresponding row |
|
||||
| After code review converges (`review-code` PASS, Phase E) | Orchestrator | Check off review-related items; annotate `_(reviewed: round N PASS)_`; update the `## 当前状态` review row; label the PR `ready-to-merge` — keep-mergeable automation takes over (hand-written keep-mergeable commits are retired) |
|
||||
| On CI key state transitions (first green / failure / cumulative goal reached) | Verifier or Developer | Update the process-type AC (e.g. "N consecutive green runs") progress note in the issue; record the run number |
|
||||
| At `verify` Phase 5.6 (final sweep before PASS) / issue close | Closing agent (Verifier on the primary path; otherwise the last role that closes the issue) | Final sweep — every remaining item gets annotated; **archive-at-close**: fold `## 当前状态`, then mark the `## 工件索引` comment archived per the artifact-index-guards rule guard #3 (see「归档动作」below) |
|
||||
|
||||
> Historical ([org-internal #3072] phase 3): the requirements/design/plan exit sync points
|
||||
> were archived with their skills (detail: Tier-2 reference page
|
||||
> `rules/issue-checklist-sync-detail`, reachable via the ticket backend).
|
||||
|
||||
Between sync points the checklist stays as-is. **PR / review / CI
|
||||
transitions** MUST also be visible via the live status note below — the
|
||||
checklist is not the only carrier.
|
||||
|
||||
### Live status note (`## 当前状态`)
|
||||
|
||||
For flows with externally visible transitions between skill exits —
|
||||
standalone bugfix, incident response, long-running reviews — maintain a short
|
||||
live status table near the top of the issue body, updated in place via the
|
||||
ticket-backend issue-update API at the transition moment (closes the
|
||||
stale-issue gap [org-internal #1668]/[org-internal #1689]]).
|
||||
|
||||
All roles use the literal heading `## 当前状态` (no suffix). If an issue carries
|
||||
a legacy variant (e.g. `## 当前状态 (live)` from [org-internal #1668]), normalize it to
|
||||
`## 当前状态` at the first transition touched. Example table + legacy-variant
|
||||
list: Tier-2 reference page `rules/issue-checklist-sync-detail`, reachable
|
||||
via the ticket backend.
|
||||
|
||||
Rules:
|
||||
|
||||
- One row per externally visible transition. Skill-exit sync points are
|
||||
surfaced by checklist annotations alone — do NOT add live-status rows for
|
||||
them. PR / review / CI transitions carry BOTH a checklist annotation AND
|
||||
a live-status row.
|
||||
- **Stage rows live on the board, not here**: pipeline stage transitions are
|
||||
represented by the Pipeline Stages board column (shared write-patterns
|
||||
Pattern 7.5), NOT by `## 当前状态` rows. Keep only the
|
||||
status-sync-automated rows (PR / 代码评审 / CI) and non-stage
|
||||
blockers/deferrals.
|
||||
- Update in place (same section heading `## 当前状态`), never append a second copy.
|
||||
- Optional for quiet pipeline flows; **MANDATORY for incident-response and
|
||||
standalone-bugfix flows** (long windows without skill exits).
|
||||
- At verify PASS / issue close, fold the table's content into checklist
|
||||
annotations and remove the section (see the「归档动作」section below).
|
||||
|
||||
### Automated sync ([org-internal #1833], timer-polled since [org-internal #2813])
|
||||
|
||||
PR / review / CI status-row transitions are automated by the `status-sync`
|
||||
poller (deployed as a host timer, every 5 min; event sources + latency table
|
||||
on the Tier-2 reference page `rules/issue-checklist-sync-detail`, reachable
|
||||
via the ticket backend). Roles do NOT hand-write those three row types. A PR
|
||||
body may carry several close refs (batch PRs, [org-internal #3731]
|
||||
`land-batch`): rows fan out to EVERY referenced issue
|
||||
(`parseClosesFixesWide` — [org-internal #3867], merged 2026-08-31) — Chinese 关闭/修复 refs
|
||||
drive rows too, even though the ticket backend never auto-closes those
|
||||
keywords. The remaining sync points (checklist annotations at skill exits,
|
||||
`## 当前状态` creation, deferral/pending marking, section fold-at-close) are
|
||||
still manual (content judgment, not event detection). Fallback for poller
|
||||
outages / manual replay: the `status-sync` manual dispatch
|
||||
(`issue`/`row`/`value`) — the row-write engine is a no-op when the section
|
||||
or row is absent, never creates or corrupts them; poller failures file a
|
||||
deduplicated `[status-sync-cron]` issue.
|
||||
|
||||
### Standalone bugfix / incident flows
|
||||
|
||||
Pipeline mode has natural sync points at skill exits; standalone bugfix and
|
||||
incident flows do NOT — the only boundaries are commit → PR → review → CI →
|
||||
merge, and EACH is a sync point with the same annotations as the progressive
|
||||
table above (full table: Tier-2 reference page
|
||||
`rules/issue-checklist-sync-detail`, reachable via the ticket backend): fix
|
||||
commit → tick fix-delivered ACs `_(commit {sha}: file)_`; PR → open/update
|
||||
`## 当前状态`; review convergence → tick review ACs + review row; CI
|
||||
transitions → process-AC progress with run number; verify PASS / close →
|
||||
final sweep, fold `## 当前状态`, archive per「归档动作」.
|
||||
|
||||
### DAG-routed tickets — node checklist variant
|
||||
|
||||
For tickets routed through the DAG pipeline (see workflow-routing rule
|
||||
§"DAG 路由 (v2)"), the task-node checklist has a fixed shape (spec-08
|
||||
§1/§2/§3):
|
||||
|
||||
- **Task tickets (nodes) use a fixed 5-row checklist** + an optional 6th row:
|
||||
验收标准 / 依赖就绪 / PR / 评审(review-code) / CI — **fixed 5 rows, fixed
|
||||
order, no free additions**. The `verify里程碑` row exists ONLY when the node
|
||||
fans into a milestone.
|
||||
- **Row 6 close-time semantics**: if at ticket close the milestone is not yet
|
||||
green, keep `- [ ]` + `_(Pending: M-01 未 green)_` — mapped to **Pending**, not
|
||||
STALE, so it satisfies the Iron Law (no stale items at close). After milestone
|
||||
verify PASS the aggregator agent flips it to `- [x]` in place.
|
||||
- **Epic aggregator `## DAG 状态` vs `## 当前状态`**: `## DAG 状态` is the
|
||||
node-status aggregator table (one row per DAG node — never carries ARCHIVE);
|
||||
`## 当前状态` carries only PR / 评审 / CI rows (no stage rows).
|
||||
- **Archive rule unchanged**: the archive marker stays ONLY on the `## 工件索引`
|
||||
comment (single-comment invariant). The body aggregator table is kept as a
|
||||
historical snapshot at close and does NOT carry ARCHIVE.
|
||||
|
||||
### Kind/MVP tickets — exemption ([org-internal #3061])
|
||||
|
||||
`Kind/MVP` tickets run the interactive no-pipeline mode: no stage exits →
|
||||
**nothing to sync**, no `## 当前状态` section (no PR/review/CI rows until
|
||||
graduation). The body itself is the living document: `## 决策日志` is
|
||||
append-only (superseded decisions struck through, never edited); `## Debt
|
||||
Register` rows update in place. On graduation (relabel `Kind/Feature`) normal
|
||||
sync rules resume from the DAG route; the decision log stays as backfill input.
|
||||
|
||||
### Annotation format
|
||||
|
||||
- **Done** → `- [x]` + parenthetical evidence: `_(PR #NNN: file/component)_`
|
||||
or `_(commit {sha}: file)_` if no PR yet.
|
||||
- **Deferred** → keep `- [ ]` + `_(Deferred: TD-XXX <reason>)_`.
|
||||
- **Pending** → keep `- [ ]` + `_(Pending: <blocker>)_`.
|
||||
|
||||
### How to sync (each point)
|
||||
|
||||
Find the source issue (`Closes #N` / `Fixes #N` in the PR body or commit
|
||||
messages, `## 父级 / Parent` / roadmap chunk mapping, or the issue the user
|
||||
referenced; none exists → skip). Fetch the body, map each `- [ ]` item
|
||||
**relevant to this sync point** to evidence (`- [x]` + `_(PR #NNN: …)_` /
|
||||
`_(commit {sha}: file)_`), deferral, or pending — leave out-of-scope items
|
||||
untouched for a later point — and update in place, preserving all
|
||||
non-checklist content. PR / review / CI transitions ALSO go into the
|
||||
`## 当前状态` live-status section.
|
||||
|
||||
### 归档动作(issue 关闭 = 归档)
|
||||
|
||||
**生命周期 = 工单生命周期**([org-internal #1929]):文档生命周期与源 issue 绑定 —— verify PASS → issue 关闭 = 归档。Tier-2 工件库无独立生命周期。
|
||||
|
||||
归档是 **显式动作**,由关闭 issue 的 agent 执行:主路径为 Verifier 在 `verify` Phase 5.6;路径跳过 verify 时(audit-only、文档类、standalone/bugfix)由关闭 issue 的 role 代为执行。无论哪个 actor,均遵循 artifact-index-guards 规则 guard #3(机制以共享 write-patterns Pattern 10 为准)。动作序列:
|
||||
|
||||
1. **Checklist 终扫**(Phase 5.6 既有步骤 / closing agent 补执行):每个 `- [ ]` 项都被注解(done / deferred / pending)。
|
||||
2. **Fold `## 当前状态`**(Phase 5.6 既有步骤 / closing agent 补执行):表格内容折入 checklist 注解后移除该节。
|
||||
3. **标记 `## 工件索引` 归档**:按共享 write-patterns Pattern 10 Archive-at-close 原位 edit(机制以 Pattern 10 为准)。横幅日期以实际关闭日为准。
|
||||
4. **关闭 issue**:PR body 的 `Closes #N` 在 merge 时自动关闭;无 PR 的流程由 **关闭 issue 的 agent** 执行工单后端的 issue-close API 完成闭环。
|
||||
|
||||
约束([org-internal #1928] 设计约束;机制细节以共享 write-patterns Pattern 10 Archive-at-close 为准,此处不重复):
|
||||
|
||||
- **L 级大件(>~300 行)留 Tier-2 工件库不删除** —— 页面与位置列 URL 保持不动(稳定 URL 供历史与共享契约引用)。
|
||||
- **索引/状态/归档判定全部收敛到 issue** —— Tier-2 工件库无独立生命周期;归档标记只落在 `## 工件索引` 评论。
|
||||
- 归档标记可逆(原位 edit):PR 被拒 / issue 重开时恢复行级 重读 即可。
|
||||
|
||||
### Iron Law
|
||||
|
||||
NO ISSUE MAY BE CLOSED WITH STALE CHECKLIST ITEMS. If a checklist item
|
||||
cannot be mapped to evidence or deferral at verify time, the iteration is
|
||||
NOT done.
|
||||
@@ -0,0 +1,72 @@
|
||||
> Core 中立版(Increment 4 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Issue Cross-Linking (mandatory)
|
||||
|
||||
Traceability flows in two directions. Both MUST be maintained so a
|
||||
reviewer can start at any node — an issue or a Tier-2 artifact-library page —
|
||||
and navigate to every related artifact without asking.
|
||||
|
||||
### Issue ↔ issue (parent/child groups)
|
||||
|
||||
When creating a **group** of related issues — an Epic with phase/P-level
|
||||
sub-tasks, a roadmap decomposed into chunk tickets, a bug with a
|
||||
regression-test follow-up, or any parent/child breakdown — the issues MUST be
|
||||
cross-linked:
|
||||
|
||||
- **Parent issue**: after creating all children, update the parent body so its
|
||||
task list references each child by `#<number>` (e.g. `- [ ] [org-internal #1556] — [P0] ...`).
|
||||
The ticket backend renders these as clickable links and auto-generates a
|
||||
back-reference on each child's timeline.
|
||||
- **Child issue**: its body MUST contain a `## 父级 / Parent` section referencing
|
||||
the parent by `#<number>`.
|
||||
- Never create a group of issues that is only one-way linked or not linked at
|
||||
all. The parent task list is the single navigation hub.
|
||||
|
||||
### Issue ↔ Tier-2 artifact (pipeline traceability)
|
||||
|
||||
When a production skill (`analyze-dag`, publishing the `{epic-slug}/dag`
|
||||
artifact; historically the pre-[org-internal #3072] producer skills) publishes
|
||||
an artifact to the Tier-2 artifact library, the link to the source issue is
|
||||
only **half-built** — the library's index page links forward to the issue,
|
||||
but the issue has no link back to the library. A stakeholder opening the
|
||||
issue sees no path to the pipeline artifacts — the traceability chain is
|
||||
broken.
|
||||
|
||||
After publishing, the skill MUST complete the reverse link on the source issue:
|
||||
|
||||
- **Post / update the "## 工件索引" comment** (工件索引协议, per the shared
|
||||
write-patterns doc Pattern 10): a single in-place-edited comment (via the
|
||||
ticket-backend comment-create, then comment-edit API; NEVER post a
|
||||
duplicate; legacy prefix "## Pipeline 工件追踪表" is upgraded in place). One
|
||||
row per artifact: `{工件 | 类型 | 版本 | 位置 | 重读}` where **重读** ∈
|
||||
{`CORE`, `ON-DEMAND`, `ARCHIVE`}.
|
||||
- **The CORE rows ARE the compaction re-read set** — after `compact`, recovery
|
||||
reads the 工件索引 comment and fetches every CORE row's 位置 (shared
|
||||
read-patterns doc Pattern 6), instead of hard-coded library paths. Skills'
|
||||
re-read instructions therefore point at the index.
|
||||
- **Archive-at-close(归档动作, [org-internal #1929]/[org-internal #1928])** — issue 关闭即归档;Tier-2 工件库无独立
|
||||
生命周期。`ARCHIVE` is not just a per-row re-read priority: at issue close the
|
||||
whole index is marked archived by the closing agent (Verifier at `verify`
|
||||
Phase 5.6 on the primary path; otherwise the Orchestrator / last-role that
|
||||
closes the issue) via in-place edit. The mechanism (banner + every row's
|
||||
重读 → ARCHIVE, single-comment invariant, rows/位置 URLs never deleted,
|
||||
L-level library artifacts keep stable URLs) is defined once in the shared
|
||||
write-patterns doc Pattern 10 Archive-at-close — reference it, do not
|
||||
restate. Sequence and actor: the issue-checklist-sync rule § 归档动作.
|
||||
- **Sync the issue's checklist** per the issue-checklist-sync rule for the
|
||||
matching exit point (live: the DAG-freeze sync point after `review-dag`
|
||||
PASS; the legacy requirements-exit / design-exit / plan-exit points were
|
||||
retired with their skills, [org-internal #3072] phase 3).
|
||||
- Skip if no source issue exists (no `Closes #N` / `Fixes #N` / roadmap chunk
|
||||
mapping ties this slug to an issue).
|
||||
|
||||
**Size/S & Size/M exception ([org-internal #1832]) — RETIRED (2026-08-21, [org-internal #3072]
|
||||
phase 3)**: retired with the legacy producer skills and the `sizing:` block.
|
||||
Historical indexes pointing at in-issue artifacts (`位置 = #comment-{id}`)
|
||||
remain valid, readable locations; new artifacts publish where their live
|
||||
skill/route puts them (DAG: library page `{epic-slug}/dag` etc.; MVP: ticket
|
||||
body).
|
||||
|
||||
Authoritative implementation patterns: shared write-patterns doc Pattern 3
|
||||
(issue↔issue) and Pattern 10 (issue↔artifact index) under the skills'
|
||||
shared-artifacts directory.
|
||||
@@ -0,0 +1,115 @@
|
||||
> Core 中立版(Increment 4 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Session Scope Guard (mandatory)
|
||||
|
||||
> Enforcement layer: precheck-mechanized (injection-budget batch-2,
|
||||
> [org-internal #3667]) — pre-flight sources 1-3 below live in the repo's
|
||||
> session-conflicts script, source 4 in the claim script's compare-and-swap.
|
||||
> This file is the normative definition, NOT part of any `agents_md`
|
||||
> injection whitelist since 2026-08-29 — do not re-add; the checks run at
|
||||
> claim time regardless.
|
||||
|
||||
Standalone and ticket-seeded sessions run without a pipeline around them —
|
||||
no plan, no reviewer keeping them inside their work item. When such a
|
||||
session hits a test failure NOT caused by its own change ([org-internal
|
||||
[org-internal #1803]], via [org-internal #1753]/[[org-internal #1744]]), it tends to fix the foreign
|
||||
failure in place (drifting off-task), and sessions seeded for the same issue
|
||||
collide on the same shared flaky test.
|
||||
|
||||
**Iron Law: if a test fails and it was NOT caused by your change, you MUST
|
||||
classify → file → continue. Do NOT fix it in place — unless that test IS your
|
||||
task.**
|
||||
|
||||
### The classify → file → continue protocol
|
||||
|
||||
1. **Classify** — rerun the failing test on the base commit / last-green:
|
||||
fails there too → **baseline failure (BF)**; intermittent across runs →
|
||||
**flaky test (FT)**; fails only with your change → **your regression** —
|
||||
fix it now.
|
||||
2. **File** — register every BF/FT as an issue in the ticket backend labeled
|
||||
`baseline-failure` or `flaky-test` with a `BF-NNN` / `FT-NNN` id, per the
|
||||
testing rule. Deduplicate against an existing BF/FT issue (comment your
|
||||
run evidence instead of filing a twin).
|
||||
3. **Continue** — return to your assigned task; cite the BF/FT issue number
|
||||
in your report / PR body so the next session sees it is owned.
|
||||
|
||||
A foreign failure does not block your task, but the task is NOT done until
|
||||
the failure is filed — an un-filed BF/FT is a process gap. Filing is the
|
||||
whole job: **the fix belongs to whoever owns that test**, not to you.
|
||||
|
||||
### One task, one owner — check before you start
|
||||
|
||||
Before starting work on an issue or code area, you MUST verify no other
|
||||
session already owns it. The pre-flight check spans four sources — (1) live
|
||||
sessions referencing `#N`, (2) a workflow branch already covering #N
|
||||
(worktrees, local branches, remote `workflow/*` refs), (3) an open PR
|
||||
referencing `#N`, (4) assignee + claim comment naming another owner. A hit
|
||||
on ANY source → the ticket is already owned: ABORT (or, for a DEAD claim,
|
||||
take over via the mechanized channel below; otherwise coordinate on the
|
||||
issue), never silently start a second owner.
|
||||
|
||||
**Mechanized ([org-internal #3667])**: the repo's session-conflicts script
|
||||
(`<session-conflicts-cmd> <ticket>`) scans sources 1-3 in one read-only pass
|
||||
(exit 4 = conflict, matching the claim script; source 1 fail-opens visibly
|
||||
when the app API is down — the backend hard guard [org-internal #1989]
|
||||
backstops); source 4 IS the claim script's compare-and-swap. Dead-claim
|
||||
takeover is mechanized in the claim script (see below); manual recipes:
|
||||
the headless-session-ops skill's ticket-recipe reference §0.
|
||||
|
||||
**Dead-claim takeover ([org-internal #3668]).** A claim whose session is
|
||||
gone (404 on the app API) and whose branch was never pushed is a DEAD claim
|
||||
— the [org-internal #3419] shape. Do NOT hand-edit the dead claim comment
|
||||
(the retired admin workaround — unauditable); run the claim script's
|
||||
takeover subcommand (`<claim-takeover-cmd> <ticket> <branch> --session-id
|
||||
<id>`) instead. Evidence rule (the claim script's header is normative): a
|
||||
competing claim is dead iff its named branch is ABSENT on the remote AND
|
||||
(its session 404s OR the comment is at least `CLAIM_TAKEOVER_STALE_DAYS`
|
||||
old — age >= the threshold, default 3d). Evidence holds → one atomic
|
||||
takeover: assignee re-set + a `**claim**` comment carrying
|
||||
`supersedes-claim #<id>` markers and the inline evidence (auditable chain,
|
||||
nothing edited) + branch push, with post-posting race re-verification.
|
||||
Evidence fails (session alive, branch pushed = completed claim, or fresh
|
||||
claim with unknown liveness) → exit 10, zero mutation — coordinate on the
|
||||
issue or salvage the worktree instead. A valid claim is never superseded.
|
||||
|
||||
**Claim-first (atomic 3-step claim).** The FIRST action when starting
|
||||
ticket-driven work is to claim the ticket — one atomic action of THREE
|
||||
steps, valid ONLY when all complete: (1) set the issue assignee to the
|
||||
working account, (2) post a claim comment naming the workflow branch and
|
||||
session id, (3) push the workflow branch to remote (even an empty commit —
|
||||
a purely local branch is invisible to other sessions, [org-internal
|
||||
[org-internal #2280]/[org-internal #2282]]). Use the repo's claim wrappers (staged-transaction claim
|
||||
script; exit codes and CAS semantics in its header; non-zero = NOT claimed)
|
||||
and the provision wrapper (adds the worktree + Tier-1 runs scaffold). The
|
||||
manual 3-step is the fallback where the scripts are unavailable, same
|
||||
re-verification.
|
||||
|
||||
**Merge-time equivalence check.** Before merging, scan the remote main
|
||||
branch's recent commits for `#<ticket>` — surfacing an equivalent
|
||||
implementation already on main *before* the merge rejection.
|
||||
|
||||
**Merge-time review gate ([org-internal #2345]).** `pipeline/review-code` is
|
||||
a required status: merge needs it green OR the route-based exemption
|
||||
(Kind/* close routes skip review-code — posted by the repo's
|
||||
review-gate-exemption script). Read the commit-status endpoint for the PR
|
||||
SHA: `success` + `review not required (route:` description → route
|
||||
exemption, proceed; `success` otherwise → proceed; missing / `error` /
|
||||
`pending` / other → BLOCK — a missing status never approves.
|
||||
|
||||
**Epic ownership table.** Multi-chunk parallel Epics keep a chunk →
|
||||
session/account ownership table on the parent issue — consult it and claim
|
||||
your chunk before starting.
|
||||
|
||||
Two sessions owning the same issue or code area is never faster — it is the
|
||||
exact collision this rule prevents. If a collision HAS happened, follow the
|
||||
failure-path playbook at the ownership-collision runbook (Tier-2 reference
|
||||
page, reachable via the ticket backend).
|
||||
|
||||
### Scope
|
||||
|
||||
Applies semantically to **every session** — pipeline subagents (Producer /
|
||||
Verifier / Reviewer / Tool) and standalone/ticket-seeded sessions alike;
|
||||
for subagents, "your task" is the delegated prompt's scope, and classify →
|
||||
file → continue applies unchanged. Filing rides the testing rule;
|
||||
launch-time owner checks live in the headless-session-ops skill (which
|
||||
points here). This file stays the normative definition — read on demand.
|
||||
@@ -0,0 +1,113 @@
|
||||
## Stash Discipline (mandatory)
|
||||
|
||||
> Enforcement layer: **L0 tool-enforced** (bash guard + husky hooks) — this
|
||||
> file is reference/documentation, NOT part of any `agents_md` per-turn
|
||||
> injection whitelist since the 2026-08-27 injection-budget batch-1. Do not
|
||||
> re-add it to a whitelist; the guard enforces regardless.
|
||||
|
||||
In the octopus worktree model, multiple parallel sessions share a single git
|
||||
repository. The git **stash stack is repository-global** (`stash@{0}`,
|
||||
`stash@{1}`, …) and is NOT isolated per worktree. When two sessions in
|
||||
different worktrees run `git stash` / `git stash pop`, they silently index
|
||||
each other's entries and one session's `pop` returns the other session's
|
||||
content — or `drop`/`clear` wipes uncommitted work belonging to a session in
|
||||
a different worktree.
|
||||
|
||||
This failure mode has already destroyed completed work in production ([org-internal #1655] /
|
||||
[org-internal #1629] — a finished 25-test implementation overwritten on the shared stack).
|
||||
|
||||
**Iron Law: NEVER use `git stash` (or `git stash pop` / `push` / `drop` /
|
||||
`clear` / `apply`) inside an octopus worktree.**
|
||||
|
||||
The bash tool enforces this at L0 via a safety-floor pattern that blocks
|
||||
`git stash` invocations. Use one of the following instead — they are
|
||||
worktree-local and cannot be clobbered by a parallel session:
|
||||
|
||||
### Approved alternatives
|
||||
|
||||
1. **WIP commit on the current workflow branch** (preferred for
|
||||
mid-iteration checkpoints):
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "wip: <slug> — <one-line state>"
|
||||
# … later, before the real commit:
|
||||
git reset --soft HEAD~1 # un-commit but keep the work staged
|
||||
```
|
||||
|
||||
The per-iteration commit model already expects these to be cleaned up by
|
||||
amend / interactive rebase before merge, so a `wip:` commit is never a
|
||||
long-term smell.
|
||||
|
||||
2. **Throwaway side branch** (preferred when you must switch branches and
|
||||
cannot commit yet):
|
||||
|
||||
```bash
|
||||
git switch -c wip/<slug>-<topic>
|
||||
git add -A && git commit -m "wip: <slug> — <one-line state>"
|
||||
git switch - # back to the workflow branch
|
||||
# … later:
|
||||
git cherry-pick wip/<slug>-<topic> # or merge / reset as needed
|
||||
git branch -D wip/<slug>-<topic>
|
||||
```
|
||||
|
||||
3. **`git diff > /tmp/octopus/<slug>.patch`** (last resort, when even a WIP
|
||||
commit is impossible — e.g. the index is in a conflicted state you do not
|
||||
want to disturb):
|
||||
|
||||
```bash
|
||||
git diff --binary > /tmp/octopus/<slug>.patch
|
||||
git apply /tmp/octopus/<slug>.patch # restore later
|
||||
```
|
||||
|
||||
Keep the patch under `/tmp/octopus/` so it survives the session but is
|
||||
never accidentally committed (see wiki page `rules/temp-files`, L2
|
||||
on-demand).
|
||||
|
||||
### What about switching away from a dirty tree?
|
||||
|
||||
If you must `git checkout <base>` to reproduce a failure but the worktree is
|
||||
dirty, do **not** reach for stash. Instead:
|
||||
|
||||
- WIP-commit first (option 1), or
|
||||
- Use the lifecycle-managed scratch-worktree script ([org-internal #2790]) to materialise
|
||||
the base commit in a *separate* worktree and run the failing test there:
|
||||
|
||||
```bash
|
||||
bash script/scratch-worktree.sh create <slug>-base <base>
|
||||
bash script/scratch-worktree.sh remove <slug>-base
|
||||
```
|
||||
|
||||
This is the pattern the `verify` skill uses for the regression-vs-baseline
|
||||
classification check. The script registers the worktree, symlinks
|
||||
`node_modules` from the source checkout when `bun.lock` matches, and the
|
||||
`sweep` backstop reclaims worktrees abandoned by dying sessions — bare
|
||||
`git worktree add /tmp/octopus/...` has no such recovery ([org-internal #2790]).
|
||||
|
||||
### Exceptions
|
||||
|
||||
- Running `git stash list` / `git stash show` to *inspect* the stack (no
|
||||
mutation) is allowed — it cannot lose work. The safety-floor pattern only
|
||||
blocks mutating subcommands (`push`, `pop`, `apply`, `drop`, `clear`,
|
||||
`create`, `store`, `branch`, and bare `git stash`).
|
||||
- The TUI's **prompt stash** (`prompt.stash` keybind, `usePromptStash`) is
|
||||
unrelated — it stashes *input text* in memory, not git refs, and is not
|
||||
affected by this rule.
|
||||
|
||||
### Enforcement
|
||||
|
||||
- **L0 (tool-enforced)**: `packages/octopus/src/tool/bash.ts` adds
|
||||
`/\bgit\s+stash\b(?:\s+(?:push|pop|apply|drop|clear|create|store|branch)|\s*$)/`
|
||||
to `SAFETY_FLOOR_PATTERNS`. Safety-floor items cannot be unblocked via
|
||||
configuration; bypass requires `--no-security-config`.
|
||||
- **L1 (this file)**: reference documentation only — removed from every
|
||||
`agents_md` injection whitelist (2026-08-27, see header); the L0 guard
|
||||
enforces regardless.
|
||||
- **L2 (skills)**: any skill that suggests `git stash` is a defect — file it
|
||||
under Kind/Enhancement with the `retrospective` label (`verify` SKILL.md
|
||||
already uses `script/scratch-worktree.sh` instead).
|
||||
|
||||
If a parallel-session stash collision happens again despite this rule, treat
|
||||
it as a Sev-2 process incident: stop work, recover via
|
||||
`git fsck --unreachable` / `git log -g --walk-reflogs stash`, open a follow-up
|
||||
referencing [org-internal #1655].
|
||||
@@ -0,0 +1,145 @@
|
||||
> Core 中立版(Increment 2 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Testing
|
||||
|
||||
- Avoid mocks as much as possible
|
||||
- Test actual implementation, do not duplicate logic into tests
|
||||
- Tests cannot run from repo root (guard: `do-not-run-tests-from-root`); run from package dirs.
|
||||
- **Local pre-check: run `<test-cmd:changed>`** from the owning package after editing source. It selects only tests affected by your uncommitted + branch changes (path-mapping ∪ CodeGraph direct dependents) and runs them in a single process — seconds, machine stays responsive. Use `--print` to see the selection without running, `--all` to scan beyond the home package.
|
||||
- **Before a delivery push: `<test-cmd:changed>` green + `<typecheck-cmd>` clean** — same selector as CI's PR side (local parity, [org-internal #4425]). The full suite (`<test-cmd:parallel>`) stays with nightly / `run-full-tests` / manual pre-merge runs. 实例命令绑定见 core/adapters/TERMINOLOGY.md。
|
||||
- Never use the bare single-process test runner for the full suite; `<test-cmd:parallel>` shards it and process-isolates known-corrupting files. Parallelism is controlled by `TEST_SHARDS` (default 3; raise it on beefy CI runners, e.g. `TEST_SHARDS=7`).
|
||||
|
||||
### Pre-existing test failures must be filed, not silently logged
|
||||
|
||||
When the full suite surfaces a failure that is **NOT caused by the current
|
||||
change** (it reproduces on the base branch / last-green commit too), it is a
|
||||
**pre-existing / baseline failure**. Do NOT silently drop it as "someone else's
|
||||
problem" and do NOT bury it in a report body.
|
||||
|
||||
- Classify it: run the failing test on the base commit (see `verify` Phase 2.6).
|
||||
Fails there too → pre-existing (BF); passes there → regression you must fix.
|
||||
- De-duplicate BEFORE filing (retro [org-internal #2696]): search existing
|
||||
`baseline-failure` tickets — open AND closed — by test/file name to surface
|
||||
candidates, then match by **failure signature** (error signature + affected
|
||||
surface — one root-cause family may span several tests; see the
|
||||
ticket-lifecycle rule §"BF / FT family umbrellas"). On a signature hit,
|
||||
attach run evidence to the family umbrella; do NOT create a new ticket.
|
||||
- File it: the Verifier registers every un-tracked pre-existing failure as a
|
||||
ticket-backend issue labeled `baseline-failure` with a `BF-NNN` id (`verify`
|
||||
Phase 5.55). The number is repo-wide sequential: highest existing NNN across
|
||||
ALL `baseline-failure` tickets (open AND closed) + 1 — duplicate ids
|
||||
forbidden, including against closed tickets.
|
||||
- A pre-existing failure does not block the iteration that *discovered* it, but
|
||||
the iteration is NOT Done until it is filed — an un-filed baseline failure is
|
||||
a process gap.
|
||||
|
||||
### Flaky tests must be filed, not silently tolerated
|
||||
|
||||
When a test passes and fails intermittently across runs (non-deterministic),
|
||||
it is a **flaky test**. Do NOT silently re-run until it passes and move on.
|
||||
|
||||
- Detect: re-run the suite (or use the project's flaky detector). Record every
|
||||
test that shows intermittent behavior — name, failure rate, and run count.
|
||||
- De-duplicate BEFORE filing (retro [org-internal #2696]): same
|
||||
signature-based procedure as baseline failures above, against existing
|
||||
`flaky-test` tickets.
|
||||
- File it: the Verifier registers every un-tracked flaky test as a
|
||||
ticket-backend issue labeled `flaky-test` with an `FT-NNN` id (`verify`
|
||||
Phase 5.56). `FT-NNN` allocation follows the same repo-wide sequential rule
|
||||
as `BF-NNN`, across ALL `flaky-test` tickets (open AND closed).
|
||||
- A flaky test does not block the iteration verdict, but the iteration is NOT
|
||||
Done until every unfixed flaky test is filed — an un-filed flaky test is a
|
||||
process gap.
|
||||
|
||||
**Filing is the whole job.** If the failure was NOT caused by your change, do
|
||||
NOT fix it in place — classify, file, and return to your task per the
|
||||
session-scope-guard rule. A repository-wide flaky test is a shared magnet —
|
||||
fixing it in place drifts off-task and collides with its owner
|
||||
([org-internal #1803]).
|
||||
|
||||
### Testing Kobalte components with happydom
|
||||
|
||||
Kobalte's portal/context-based components (Collapsible, DropdownMenu, ContextMenu,
|
||||
FormControlContext) do not work reliably under happydom's DOM environment. For
|
||||
these components, use **source-verification tests** — assert that the correct
|
||||
JSX elements, props, and Kobalte primitives are present in the component's
|
||||
source rather than attempting full DOM rendering.
|
||||
|
||||
When implementing a component that uses these Kobalte primitives, prefer
|
||||
source-verification over render tests. The same applies to SolidJS SVG
|
||||
`<filter>`/`<mask>`/`<linearGradient>` elements, which happydom also cannot
|
||||
render reliably.
|
||||
|
||||
### Test file naming convention (one file per component)
|
||||
|
||||
A single component MUST NOT carry both a `.test.ts` and a `.test.tsx` test
|
||||
file — the two-file split duplicated ~3300 lines of overlapping assertions
|
||||
across one UI package [org-internal #1712]. Use exactly one file per
|
||||
component, chosen by test style:
|
||||
|
||||
| Test style | Extension | When to use |
|
||||
| -------------------------------- | -------------- | -------------------------------------------------------------- |
|
||||
| Source-verification | `.test.ts` | Kobalte portal/context components, SVG `<filter>`/`<mask>`/`<linearGradient>` components, and any component happydom cannot render reliably. Asserts against the component source. |
|
||||
| DOM render / interaction | `.test.tsx` | Pure Solid components that render cleanly under happydom. Uses a testing-library harness. |
|
||||
|
||||
Rules:
|
||||
|
||||
- One file per component. If a component needs both source-verification and
|
||||
DOM-render assertions, keep them in a single file matching the dominant
|
||||
style; do not split into a `.test.ts`/`.test.tsx` pair.
|
||||
- When merging an existing dual pair, carry over any assertion unique to the
|
||||
deleted file into the surviving file before deletion.
|
||||
- The app package's `.sv.test.*` suffix is a separate convention for
|
||||
storybook/storyshots source-verification and is unaffected.
|
||||
|
||||
### Source-verification assertion boundaries
|
||||
|
||||
Source-verification tests (per the Kobalte/happydom carve-out above) must assert
|
||||
**structural contracts**, not implementation literals.
|
||||
|
||||
- **Assert**: props wiring, event handler attachment, i18n key presence, ARIA
|
||||
role/attributes present in JSX, namespace correctness, export shape.
|
||||
- **Do NOT assert**: exact source-string literals (`event.preventDefault()`,
|
||||
`'block: "nearest"'`, `event.movementX === 0`), branch operators counted via
|
||||
regex, or any expression that changes when a constant is renamed.
|
||||
- **Do NOT write tautologies** — `source.includes("false") || source.includes("!")`
|
||||
is true for nearly all TSX.
|
||||
- **One source-verification file per component** (see naming convention above).
|
||||
When two test files read the same source, merge into one.
|
||||
- **Prefer behavioral tests** — when source-verification and a behavioral/
|
||||
interaction test cover the same contract, prefer the behavioral test;
|
||||
collapse the SV duplicate.
|
||||
|
||||
### Namespace gate and test assertions
|
||||
|
||||
The namespace gate (pre-commit hook + the check-namespace script) scans ALL
|
||||
string literals in `.ts`/`.tsx`/`.json` files under the product packages for a
|
||||
reserved vendor namespace. Do NOT write test assertions containing that
|
||||
namespace literal — the gate cannot distinguish a test's namespace-absence
|
||||
check from an actual namespace violation, and will fail the commit.
|
||||
|
||||
Instead, use patterns that avoid the literal:
|
||||
```ts
|
||||
const NAMESPACE = "vendor-prefix"
|
||||
expect(source).not.toContain(`@${NAMESPACE}`)
|
||||
```
|
||||
|
||||
### Single test root (per package)
|
||||
|
||||
Each package MUST have exactly ONE test root — no parallel tracks
|
||||
(`src/**/__tests__/`, a `tests/` dir alongside colocated `src/`, or a `test/`
|
||||
alongside `tests/`); a second root drifts and breaks test discovery
|
||||
([org-internal #1711], extended to all packages by [org-internal #1821]).
|
||||
|
||||
Rules:
|
||||
|
||||
- New tests: create them at the package's single root from the start.
|
||||
- Do NOT create `src/**/__tests__/` directories in any package — parallel
|
||||
tracks drift (residuals eliminated by [org-internal #1821]).
|
||||
- For `test/`-root packages: mirror the `src/` path (`test/<src-relative>.test.ts`);
|
||||
type-level tests live alongside the behavioral suites in the config area.
|
||||
- For colocated-root packages: keep tests next to the source file; one test
|
||||
file per component [org-internal #1712].
|
||||
|
||||
Per-package root tables live in the instance layer (they name concrete
|
||||
package paths); the invariant itself — ONE root per package — is universal.
|
||||
@@ -0,0 +1,98 @@
|
||||
> Core 中立版(Increment 2 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Ticket Lifecycle — registry-first filing, claim-to-promote ([org-internal #3002] G1)
|
||||
|
||||
Bugs worth fixing start life as a **registry row**, not an independent ticket.
|
||||
This extends the two-tier-artifacts philosophy to the ticket queue: a
|
||||
"discovery" (no consumer yet, lifecycle undetermined) is Tier-1-shaped — a
|
||||
cheap row in a ledger — and is **promoted** to a Tier-2 independent ticket
|
||||
only when someone schedules or picks it up. (Filing one ticket per discovery
|
||||
is how the 2026-08 backlog formed.)
|
||||
|
||||
### The registry (登记册)
|
||||
|
||||
- **Where**: a `## TD 登记` comment on the **source ticket** (the ticket whose
|
||||
verify/review produced the candidates; a ticket-backend issue comment).
|
||||
No source ticket (standalone iteration) → the owning Epic's tracker
|
||||
ticket; no Epic either → the standing TD 冷存档 tracker ticket (G2's
|
||||
fallback archive target).
|
||||
- **Row format** (one line per item, checkbox-closeable):
|
||||
|
||||
```markdown
|
||||
- [ ] TD-{NNN} · {type: UNVERIFIABLE|ACCEPTED_RISK|DEFERRED|INFO} ·
|
||||
{severity: HIGH|MEDIUM|LOW} · {origin chunk-id/iter-N} — {one-line summary}
|
||||
· trigger: {objectively verifiable reactivation condition}
|
||||
```
|
||||
|
||||
- **TD-NNN** is allocated at REGISTRATION via the ledger mutex
|
||||
[org-internal #3322]: the 配额账本脚本 (`td-alloc` 形态, `bash <alloc-script>
|
||||
<count> <source-slug> [--ticket N]`) takes a contiguous range from the TD
|
||||
allocation ledger (a standing tracker issue) — append-only `**td-alloc**`
|
||||
comments, earliest-comment-id wins on collision. **ANY allocation — manual
|
||||
included — must land a td-alloc comment on the ledger BEFORE the registry
|
||||
row is written** (script, tool API, or web UI). Hand-computed `max + 1`
|
||||
without a ledger comment is FORBIDDEN (parallel-session races,
|
||||
[org-internal #3322]). The id is **reserved for life**: promotion reuses
|
||||
it, never renumbers. Run the alloc script's `--check` (reconciliation)
|
||||
when the last check predates this batch, and in retrospectives.
|
||||
- **De-duplication** queries the TD index (same-source rows) plus open
|
||||
promoted issues (cross-source); index matching replaces title-keyword
|
||||
matching (keyword dedup once produced 18 fragments from one source).
|
||||
- **Query-failure ruling ([org-internal #3383]: fail-open, visible)**: if the
|
||||
dedup query itself errors (backend API failure — NOT "queried and found no
|
||||
match"), retry once; on second failure file anyway, marking the comment
|
||||
`dedup-unverified` for retro/reconciliation sweep. Fail-closed rejected:
|
||||
known dedup failures are match failures it cannot prevent, and a backend
|
||||
hiccup would block a verify PASS gate.
|
||||
|
||||
### Promotion (认领升票)
|
||||
|
||||
A row is promoted to an independent ticket when **scheduled or picked up**
|
||||
(assigned, pulled into a wave/sprint, or chosen):
|
||||
|
||||
1. Create the ticket (ticket backend 立案) with the row's reserved `TD-NNN`
|
||||
(full body per the verify skill's promotion template).
|
||||
2. Tick the registry row (`- [x]`) and append `→ #NNNN` to it.
|
||||
3. The new ticket's `## Parent` points back at the registry's source ticket.
|
||||
|
||||
Unpromoted rows are not second-class: the registry comment IS a visible
|
||||
ticket-backend comment, rows close individually, and the TD index dedups
|
||||
across sessions — the old per-item model's three guarantees at ~1 ticket per
|
||||
source.
|
||||
|
||||
### PR 准入(pr-admission,TD-678)
|
||||
|
||||
会话交付物 = 分支 + 报告;开 PR 属编排职权,按容量串行一次一张、双绿并入
|
||||
再开下一张([org-internal #3864] 管合并出口、本节管 PR 入口)。交付推送前置
|
||||
本地验证(`<test-cmd:changed>` + `<typecheck-cmd>` 绿;实例命令绑定见
|
||||
core/adapters/TERMINOLOGY.md)。编排不可达可自开,PR 正文标
|
||||
`uncoordinated`。细则与门控项:pipeline-gate 清单 PR 准入节。
|
||||
|
||||
### BF / FT family umbrellas
|
||||
|
||||
Baseline failures and flaky tests file **per root-cause family**, not per
|
||||
occurrence: evidence (run, commit, failing output, failure rate) attaches as
|
||||
comments on the family's **umbrella ticket** (ticket-backend issue), keyed by
|
||||
failure signature (error signature + affected surface, not just
|
||||
`file::test-name` — one root-cause family may span several tests; precedents
|
||||
[org-internal #2585], [org-internal #2925]). Filing a new ticket when the
|
||||
signature already matches an existing family (open or closed) is forbidden —
|
||||
attach evidence instead. A family umbrella is closed when its signature no
|
||||
longer reproduces.
|
||||
|
||||
### TTL semantics (feeds G2's issue-reaper)
|
||||
|
||||
- An **unpromoted registry row** stale for 30 days (no promotion, no
|
||||
evidence comment) is reaper-eligible: 7-day notice, then archived into the
|
||||
tracker's archive section.
|
||||
- A **promoted ticket** follows the normal issue lifecycle (its own priority,
|
||||
assignee, milestone).
|
||||
|
||||
### Compatibility (Iron Law preserved)
|
||||
|
||||
- `_(Deferred: TD-XXX <reason>)_` annotations keep their exact format; the
|
||||
`TD-XXX` now resolves to a registry row (or promoted ticket) instead of an
|
||||
issue number.
|
||||
- Archive-at-close, `## 工件索引` guards, and issue-checklist-sync are
|
||||
untouched: the registry comment is an ordinary ticket-backend comment, not
|
||||
a Tier-2 artifact page.
|
||||
@@ -0,0 +1,122 @@
|
||||
> Core 中立版(Increment 2 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Two-Tier Artifact Storage (mandatory)
|
||||
|
||||
Agent-internal handoff (subagents sharing one worktree) and human-visible
|
||||
traceability have different consumers, lifecycles, and costs. The harness
|
||||
separates them into two tiers so internal handoff stops paying the Tier-2
|
||||
tax (HTTP latency, write conflicts, auth, backend health) while
|
||||
human-visible decisions stay in the system of record.
|
||||
|
||||
**Iron Law: an artifact's tier is decided by its CONSUMER and LIFECYCLE, not by
|
||||
its format.** Format (json / md / artifact page / comment) follows the tier
|
||||
decision, never drives it.
|
||||
|
||||
### Tier 1 — local structured (transient, intra-workflow)
|
||||
|
||||
- **Consumer**: subagents within one worktree (Producer / Reviewer /
|
||||
Synthesizer / Verifier) — never a human gate, never cross-worktree.
|
||||
- **Lifecycle**: bounded to one run; one worktree; archived-at-close.
|
||||
- **Visibility**: agent-internal; not human-gated.
|
||||
- **Storage**: the run workspace `runs/{slug}/` — schema-validated local
|
||||
files; active runs gitignored, archive bundle committed at close.
|
||||
- **Examples**: design detail dumps, plan item lists, raw review dimension
|
||||
findings, synthesis prompts, intermediate analysis.
|
||||
|
||||
### Tier 2 — ticket backend / git (stable, cross-worktree, human-visible)
|
||||
|
||||
- **Consumer**: humans (gate decisions), cross-worktree sessions, traceability.
|
||||
- **Lifecycle**: outlives a run; cross-worktree stable; the system of
|
||||
record.
|
||||
- **Visibility**: human-visible; authoritative.
|
||||
- **Storage**: the ticket backend (issues, PRs, artifact pages, commit
|
||||
status) + git-committed artifacts.
|
||||
- **Examples**: frozen inter-chunk contracts, issue status + checklist, PRs,
|
||||
review verdict + synthesis (the human gate), `## 工件索引` comment, commit
|
||||
status, the workflow branch.
|
||||
|
||||
### Decision rule (the 判据)
|
||||
|
||||
An artifact belongs to **Tier 1** if and only if ALL of:
|
||||
|
||||
1. consumed only within one worktree / one run, AND
|
||||
2. not a human gate decision (no human reads it to approve/reject), AND
|
||||
3. not referenced cross-worktree (e.g. a later chunk's frozen contract), AND
|
||||
4. regenerable-or-archivable at close without losing a decision record.
|
||||
|
||||
If ANY condition fails → **Tier 2**. When in doubt, Tier 2: **the Tier-2
|
||||
default载体 is decided by the ticket backend adapter** (参考实现见
|
||||
core/adapters/ 的 adapter 层;本组织实例为 Gitea adapter, 术语对照见
|
||||
core/adapters/TERMINOLOGY.md) — Tier 2 is the default system of record;
|
||||
Tier 1 is an optimization an artifact opts INTO, not out of.
|
||||
|
||||
### Common classifications
|
||||
|
||||
Per-artifact table (review findings, prompt files, working drafts, draft/frozen
|
||||
contracts, issue checklist, 工件索引, `index.json` …): Tier-2 reference page
|
||||
`rules/two-tier-artifacts-detail` (reachable via the ticket backend); the
|
||||
decision rule above classifies anything unlisted.
|
||||
|
||||
### Two indexes, parallel not replacement
|
||||
|
||||
- **Tier 1 `index.json`** (`runs/{slug}/index.json`) — the agent's
|
||||
compact-recovery entry; Tier 1 artifacts only.
|
||||
- **Tier 2 `## 工件索引` comment** — the human-visible traceability hub:
|
||||
artifact-page/issue locations + 重读 priority.
|
||||
|
||||
Both are kept by the producing role but serve different consumers. Migrating a
|
||||
skill's output to Tier 1 does NOT remove its Tier 2 row — it repoints 位置 at
|
||||
the distillation.
|
||||
|
||||
### Archive-at-close
|
||||
|
||||
Closed by the **Verifier** at verify PASS (or the Orchestrator when no
|
||||
verify). The archive step is the ONLY point active-run Tier 1 content enters
|
||||
git history:
|
||||
|
||||
1. Generate the bundle [org-internal #3604]:
|
||||
the run-bundle generator script with `--slug {slug} [--ticket N] [--pr N]`
|
||||
— flips meta to "archived", hashes the workspace into the `sha256_16`
|
||||
manifest, writes `archive/{ticket}-{slug}.json`, validates.
|
||||
2. Commit the bundle on the workflow branch (rides into main via `--no-ff`).
|
||||
Direct pushes to main stay blocked by branch protection [org-internal
|
||||
[org-internal #3604]]; do not bypass.
|
||||
3. **归档前置校验** [org-internal #2727]: the generator validates by default
|
||||
(`--no-validate` must not reach a merge); red exit = fix before close.
|
||||
The L0 merge gate rejects non-compliant bundles at merge anyway.
|
||||
|
||||
merge-gate 另做 **presence(存在性)检查**:PR body/commit 引用 `Closes #N`
|
||||
而 merge 树归档目录无 `meta.ticket.number=N` 的 bundle 时拦截(区别于
|
||||
schema 包的结构合规检查)。预期无 run 的关闭(文档/audit-only/MVP 晋级
|
||||
前/revert/ad-hoc/legacy reopen)贴 `gate-exempt/archive-presence`
|
||||
label——label 事件自动重跑 gate。归档前 PR 呈红是设计行为(fail-closed
|
||||
until archived)。**禁止以豁免 label 替代确实存在过的 run 的 bundle
|
||||
归档**。归档目录的 `.gitkeep` 是该检查的目录存在性不变式,不得删除。
|
||||
|
||||
4. Remove the `{active-slug}/` workspace. Direct `rm -rf` is L0-blocked —
|
||||
sanctioned exit: the post-merge worktree sync script with `--prune-runs`
|
||||
(syncs to the merge commit, prunes the run dir, clears stale-ahead false
|
||||
positives), then the session-worktree removal script.
|
||||
5. Mark the Tier 2 `## 工件索引` archived per the issue-checklist-sync rule
|
||||
§ 归档动作.
|
||||
|
||||
Tier 2 remains the decision record; the Tier 1 git archive is the
|
||||
reproducibility trace — enough to reconstruct what a run produced.
|
||||
|
||||
### Compact interaction
|
||||
|
||||
`compact` never deletes local files: `index.json` survives compaction and is
|
||||
the designated re-read entry — after `compact`, read `runs/{slug}/index.json`,
|
||||
not conversation memory.
|
||||
|
||||
### Gate furlough([org-internal #3607])
|
||||
|
||||
强制门的唯一退出通道是 gate 台账(furlough ledger)的 furlough 条目
|
||||
(测量证据 + reopen 条件);无台账条目的减门/跳过改动一律拒绝,台账
|
||||
本身是 Tier 2 决策工件。
|
||||
|
||||
### Scope
|
||||
|
||||
This rule + the runs layout template + schemas are the foundation; the two
|
||||
tiers coexist — unmigrated artifacts keep writing to the ticket backend per
|
||||
their existing body.
|
||||
@@ -0,0 +1,11 @@
|
||||
> Core 中立版(Increment 4 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Type Checking
|
||||
|
||||
- Always run `<typecheck-cmd>` from package directories, never invoke the
|
||||
underlying compiler driver directly.
|
||||
- Monorepo-wide typecheck from the repo root (all packages in one pass — used
|
||||
by CI and the review-code mechanical-green gate's precondition check): the
|
||||
repo-root turbo-pipeline invocation (see the term mapping). This is the
|
||||
repo-root equivalent of the per-package `<typecheck-cmd>`, not a divergence
|
||||
from it.
|
||||
@@ -0,0 +1,137 @@
|
||||
> Core 中立版(Increment 2 改写)。术语对照见 core/adapters/TERMINOLOGY.md;本组织实例表述见 dogfood 对应文件。
|
||||
|
||||
## Workflow Routing — type-driven process selection (mandatory)
|
||||
|
||||
A ticket's `Kind/*` label determines its process route. The main-session
|
||||
Process Assessment Gate MUST resolve the route BEFORE matching a skill, so a
|
||||
labeled ticket never re-derives its path from scratch. Routing is **additive**:
|
||||
it can only skip stages a ticket doesn't need — it can never weaken a
|
||||
mandatory gate or skip review/verify.
|
||||
|
||||
**Authoritative table**: the workflow-routing table in the instance config
|
||||
area (`label → { entry, skip, keep_gates }`). The table is the single source
|
||||
of truth; this rule only says _when and how_ to consult it.
|
||||
|
||||
### When to route (Step 0 — runs before the gate's "Articulate intent")
|
||||
|
||||
On every substantive request that is tied to a ticket, run Step 0:
|
||||
|
||||
1. **Identify the working ticket**. Priority order: explicit `Closes #N` /
|
||||
`Fixes #N` / `Resolves #N` in the PR or commit → `## 父级 / Parent` link
|
||||
or chunk mapping → the issue the user pasted or referenced by number.
|
||||
None of these → no ticket, skip to degradation.
|
||||
2. **Resolve the route with the 路由解析工具 FIRST** — pass the full
|
||||
`Kind/*` label when known; omit `kind` when not yet read (the tool returns
|
||||
the default route plus a read-ticket-then-re-invoke instruction). Agents
|
||||
with the route-gate option enabled enforce this mechanically
|
||||
[org-internal #3328]: other tool calls are dispatch-rejected until the
|
||||
路由解析工具 runs. Do not hand-read the routing table when the tool is
|
||||
available.
|
||||
3. **Read the ticket's `Kind/*` label** from the issue body or via the issue
|
||||
tools (unblocked once the route gate is satisfied). Pick the FIRST
|
||||
`Kind/*` label if several exist; on a non-default route, re-invoke the
|
||||
路由解析工具 with the full label.
|
||||
4. **Apply the route**:
|
||||
- `entry.skill` non-null → invoke that skill (with `entry.mode` if given)
|
||||
instead of natural-language matching. This satisfies gate Step 3.
|
||||
- `entry.skill` null → normal natural-language match, still honoring
|
||||
`skip` / `keep_gates` downstream. Never drop a gate the route lists in
|
||||
`keep_gates`; never add back a stage the route lists in `skip`.
|
||||
|
||||
### Degradation — label missing, unknown, or wrong ([org-internal #1828] AC)
|
||||
|
||||
- **No ticket / no `Kind/*` label / label not in the table** → `default`
|
||||
route (natural-language matching). State this in one line ("no Kind/\*
|
||||
label → default route") so the choice is auditable; never block or ask
|
||||
the user just because a label is absent.
|
||||
- **Label looks wrong for the work** → do NOT silently override the label.
|
||||
Apply the labeled route, flag the mismatch in your first response, and
|
||||
recommend the correct label — the creator owns label correctness; routing
|
||||
acts only on what is written.
|
||||
- **Routing table file missing/unreadable** → fall back to natural-language
|
||||
matching session-wide and note it once — a broken table never blocks work.
|
||||
|
||||
### Interaction with the rest of the pipeline
|
||||
|
||||
- Routing chooses the **entry** and the **skip set** only. L0/L1 enforcement
|
||||
(worktree/branch discipline, commit-msg format, stash ban,
|
||||
claim-discipline, issue-checklist-sync, issue-cross-linking) applies on
|
||||
every route; a `Kind/Documentation` ticket still needs a worktree and a
|
||||
PR.
|
||||
- `keep_gates` from the route and the skill's own mandatory gates intersect:
|
||||
a gate is mandatory if EITHER source requires it. Skipping is valid only
|
||||
when BOTH the route and the skill agree it's skippable for this ticket type.
|
||||
|
||||
### Runtime growth signals (post-sizing)
|
||||
|
||||
Two live growth mechanisms (narrative: Tier-2 reference page
|
||||
`rules/workflow-routing-detail`, reachable via the ticket backend; retired
|
||||
Size/* history: the instance rules-history archive). Canonical key:
|
||||
`dag.size_derivation.oversize_signals` (TD/DUP-F009).
|
||||
|
||||
- **DAG-routed tickets** (DAG Epic, DAG-routed `Kind/Feature`, their
|
||||
`Kind/Task` children — no `Size/*` tier): runtime growth fires the
|
||||
oversize signals → response is **re-derivation + review-dag re-run**.
|
||||
- **Bug tickets (big-bug relabel rule, [org-internal #3061])**: a `Kind/Bug`
|
||||
hitting a design-level trigger (design-decision root cause, shared-contract /
|
||||
public-API change, data migration) has outgrown the bug type — mandatory
|
||||
action is **relabel `Kind/Feature`** (Step 0 reroutes into the DAG
|
||||
pipeline; repro + root-cause notes carry over as node input). Scale-only
|
||||
triggers stay in bugfix with batched iterations.
|
||||
|
||||
Both follow "act on what's written, surface the doubt": when reality
|
||||
disagrees with the label, pause, reclassify, and make the change visible —
|
||||
never quietly override.
|
||||
|
||||
### DAG 路由 (v2) — default (转正)
|
||||
|
||||
The DAG ticket pipeline (`analyze-dag` → task DAG → single gate `review-dag`)
|
||||
is the **default** route for `Kind/Epic` and `Kind/Feature`
|
||||
([org-internal #3061] Phase 2 / TD-388). Both resolve via the routes table
|
||||
directly — **no trigger/marker check** (route rollback = revert PR).
|
||||
Authoritative definition: the `dag:` block in the workflow-routing table;
|
||||
narrative + history: Tier-2 reference page `rules/workflow-routing-detail`.
|
||||
|
||||
1. **`Kind/Epic` or `Kind/Feature` → `dag.route`**: entry `analyze-dag`,
|
||||
single gate `review-dag` (replaces review-design-space +
|
||||
review-iteration-plan), `keep_gates` `[review-dag, verify]`.
|
||||
2. **`Kind/Task` under a DAG parent → `dag.task_route`** (entry `implement`,
|
||||
`implement → review-code → verify`), NOT `dag.route`. All three skills
|
||||
carry a DAG branch; `pipeline/review-dag` success replaces the
|
||||
design-space + iteration-plan convergence preconditions at review-code
|
||||
and verify.
|
||||
|
||||
**Routing stickiness**: once an Epic is routed to DAG (draft DAG artifact
|
||||
exists or `## DAG 状态` table created), all subsequent Step 0 for that Epic
|
||||
stays on the DAG route — stickiness starts at first routing, NOT at
|
||||
single-gate PASS. Symmetrically, an Epic already decomposed via legacy
|
||||
roadmap stays legacy through close (no new legacy decomposition).
|
||||
|
||||
**Ticket creation after single-gate PASS** (switch values authoritative in
|
||||
the routing table `dag.ticket_creation`; analyze-dag itself does NOT create
|
||||
tickets): on `review-dag` PASS the **aggregator agent** creates one
|
||||
`Kind/Task` ticket per `type: task` node (body: node `acceptance_criteria`
|
||||
or dag subpage link + `test_id` suite; `## 父级 / Parent` → DAG parent so
|
||||
Step 0 resolves `dag.task_route`); `type: milestone` nodes get NO ticket
|
||||
(DoD carried by `dag.verify_milestone`); the parent body aggregates
|
||||
`## DAG 状态` + `## 当前状态` (single writer: the aggregator). Each task
|
||||
ticket then flows implement → review-code (`mode: "dag-task"`) → verify
|
||||
(task mode).
|
||||
|
||||
### Retired sections (index)
|
||||
|
||||
Retired mechanisms (Sizing, JIT decomposition, marker disambiguation,
|
||||
legacy-route sunset, publish-target-by-tier, gate-trim) live in the instance
|
||||
rules archive; rulings in the phase-3 retirement ruling doc (Tier-2
|
||||
reference, reachable via the ticket backend). Nothing trims gates —
|
||||
`never_trim` is structural.
|
||||
|
||||
### 立案降档 — filing/sprint-mode([org-internal #3002] G5 / [org-internal #3007])
|
||||
|
||||
Sprint 期间债务侧显式降档,不靠纪律。开关:routing table `filing.sprint-mode`
|
||||
(默认 `false`)。`true`(冲刺期)时 verify Phase 5.5 registry-only 不分严重度
|
||||
(登记行照写、零丢失;独立工单创建冻结至开关回 `false`,已登记行按
|
||||
ticket-lifecycle 规则正常认领升票;期间 HIGH/MEDIUM 行不被 reaper 触碰,
|
||||
[org-internal #3004])。BF/FT(5.55/5.56)不受影响。谁翻转:冲刺负责人(人),
|
||||
在冲刺跟踪单记一行;开关值即 routing table 当前值,verify 每次运行时读取。
|
||||
动机与细节:Tier-2 参考页(经 ticket backend)`rules/workflow-routing-detail`。
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"namespace": "https://eightarms.net/octopus-workflow/schemas/",
|
||||
"frozen": false,
|
||||
"note": "发布仓创建时置 frozen: true——$id 一经发布不可变(v3 §G);别名保留 ≥2 个 major 版本",
|
||||
"aliases": [
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/port-analysis.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/port-analysis.schema.json"
|
||||
},
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/review-status.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/review-status.schema.json"
|
||||
},
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/reviewer-output.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/reviewer-output.schema.json"
|
||||
},
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/runs-bundle.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/runs-bundle.schema.json"
|
||||
},
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/runs-index.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/runs-index.schema.json"
|
||||
},
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/runs-meta.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/runs-meta.schema.json"
|
||||
},
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/synthesis.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/synthesis.schema.json"
|
||||
},
|
||||
{
|
||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/workflow-routing.schema.json",
|
||||
"new": "https://eightarms.net/octopus-workflow/schemas/workflow-routing.schema.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/port-analysis.schema.json",
|
||||
"title": "Port Analysis Reviewer Output",
|
||||
"description": "Schema for per-dimension port-analysis reviewer findings. Used by the large-port-pipeline source analysis review (Phase B1.5). The port source-analysis pipeline uses a deliberate three-value severity contract 鈥?BLOCKER / MAJOR / MINOR 鈥?and never emits INFO (matching implement/reference/large-port-pipeline.md and source-analysis.md, which name only those three). This is a documented exception to the canonical four-value set (BLOCKER / MAJOR / MINOR / INFO) in reviewer-output.schema.json and synthesis.schema.json.",
|
||||
"type": "object",
|
||||
"required": ["dimension", "findings"],
|
||||
"properties": {
|
||||
"dimension": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"SRC-CMP",
|
||||
"SRC-API",
|
||||
"SRC-DATA",
|
||||
"SRC-BIZ",
|
||||
"SRC-ERR",
|
||||
"SRC-DEP",
|
||||
"SRC-TST",
|
||||
"SRC-MAP",
|
||||
"TGT-SURF",
|
||||
"CAP-BOUND"
|
||||
],
|
||||
"description": "Audit dimension code"
|
||||
},
|
||||
"findings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["id", "severity", "description"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^(SRC-CMP|SRC-API|SRC-DATA|SRC-BIZ|SRC-ERR|SRC-DEP|SRC-TST|SRC-MAP|TGT-SURF|CAP-BOUND)-\\d{3}$",
|
||||
"description": "Finding ID in {DIM_CODE}-{NNN} format"
|
||||
},
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"enum": ["BLOCKER", "MAJOR", "MINOR"]
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Human-readable finding description"
|
||||
},
|
||||
"source_evidence": {
|
||||
"type": "string",
|
||||
"description": "Source file:line reference citing the evidence"
|
||||
},
|
||||
"recommendation": {
|
||||
"type": "string",
|
||||
"description": "Concrete fix suggestion"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/review-status.schema.json",
|
||||
"title": "Review Status",
|
||||
"description": "Schema for tracking the lifecycle state of a review session. This file lives at runs/{slug}/reviews/{stage}/status.json: initialized by the Orchestrator at Phase A (step 0.5) with the full required set, appended by the Synthesizer at Phase B (history[] + current_round), and finalized with `converged` by the Orchestrator at Phase E (see skills/_shared/review-pipeline-phases.md). Reader: the retrospective token-telemetry probe (retrospective/scripts/token-telemetry.ts) walks runs/**/reviews/*/status.json (canonical) and the legacy .artifacts/**/reviews/*/status.json, reading `history[]` (code-level legacy alias `rounds[]`, both handled) and `current_round`. For CLOSED runs (active workspace removed at archive-at-close), the probe reconstructs per-stage round counts from reviews/{stage}/round{N}/ artifact paths in the committed archive bundle runs/archive/{slug}.json ([org-internal #2591]). Since TD/TRC-F006 ([org-internal #2688]) the archive bundle can also embed an explicit digest of this history[] (top-level review_history: {stage, rounds, final_verdict, per_round[]}) so round counts and verdicts stay verifiable offline, without path reconstruction.",
|
||||
"type": "object",
|
||||
"required": ["slug", "started_at", "current_round", "max_rounds", "converged", "history"],
|
||||
"properties": {
|
||||
"slug": {
|
||||
"type": "string"
|
||||
},
|
||||
"started_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"current_round": {
|
||||
"type": "integer"
|
||||
},
|
||||
"max_rounds": {
|
||||
"type": "integer",
|
||||
"default": 3,
|
||||
"description": "Per-skill bound: `review-artifact` skill (process target) = 3 (the roadmap/design/plan targets were archived 2026-08-21, [org-internal #3072] phase 3); `review-code` = 2 default / 3 high-risk (`workflow-routing.yaml` `review.max_rounds`, [org-internal #3567]). `review-dag` (the review-artifact skill's DAG single-gate target) binds per depth from `dag_metrics.review_depth`: D1 鈮?, D2 鈮?, D3 鈮?, D4 鈮? 鈥?D4 reaches round 4, exceeding the shared `review-artifact` default, so the DAG per-depth cap takes precedence (see review-artifact/SKILL.md review-dag Phase D MAX_ROUNDS). Set explicitly by the skill; default 3 is a fallback only."
|
||||
},
|
||||
"converged": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"history": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["round", "overall_verdict", "blockers", "majors"],
|
||||
"properties": {
|
||||
"round": {
|
||||
"type": "integer"
|
||||
},
|
||||
"overall_verdict": {
|
||||
"type": "string",
|
||||
"enum": ["PASS", "WARN", "FAIL"]
|
||||
},
|
||||
"blockers": {
|
||||
"type": "integer"
|
||||
},
|
||||
"majors": {
|
||||
"type": "integer"
|
||||
},
|
||||
"minors": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Count of MINOR findings for this round"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/reviewer-output.schema.json",
|
||||
"title": "Reviewer Output",
|
||||
"description": "Schema for a single reviewer's output for one review dimension. Also the Tier 1 review-findings schema ([org-internal #1988]): migrated skills (review-code) write each dimension's finding as a JSON file at runs/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json validated by this schema; the index.json row for that file carries type=review-findings, schema=this $id. When a dimension has multiple reviewers (dim_split > 1, e.g. D3 TOPO:2 / D4 TOPO:2), each writes its OWN slot file findings-{DIMENSION_CODE}-r{k}.json with reviewer_slot/reviewer_count set. See templates/runs-layout.md and rules/two-tier-artifacts.md.",
|
||||
"type": "object",
|
||||
"required": ["dimension", "dimension_label", "verdict", "quantitative_summary", "findings", "reviewed_files"],
|
||||
"properties": {
|
||||
"dimension": {
|
||||
"type": "string",
|
||||
"description": "Dimension code (e.g. COR, SEC, PERF, TRC)"
|
||||
},
|
||||
"dimension_label": {
|
||||
"type": "string",
|
||||
"description": "Human-readable label"
|
||||
},
|
||||
"reviewer_slot": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"description": "1-based slot of this reviewer WITHIN its dimension, when dim_split assigns >1 reviewer to the dimension (e.g. review-dag D3 TOPO:2, D4 TOPO:2 鈥?dim_split per `workflow-routing.yaml` dag.size_derivation.review_depth). Multi-reviewer dimensions write one file per slot: findings-{DIMENSION_CODE}-r{k}.json. Omitted for single-reviewer dimensions (file stays findings-{DIMENSION_CODE}.json). Preserves per-reviewer attribution for the retrospective overlap analysis ([org-internal #2996] step 1a)."
|
||||
},
|
||||
"reviewer_count": {
|
||||
"type": "integer",
|
||||
"minimum": 2,
|
||||
"description": "Total reviewers assigned to this dimension in this round (set together with reviewer_slot)."
|
||||
},
|
||||
"verdict": {
|
||||
"type": "string",
|
||||
"enum": ["PASS", "WARN", "FAIL", "UNRESOLVED"],
|
||||
"description": "PASS: pass_rate = 1.0 (no failed checks). WARN: pass_rate >= 0.7 and no BLOCKER findings. FAIL: any BLOCKER finding OR pass_rate < 0.7."
|
||||
},
|
||||
"quantitative_summary": {
|
||||
"type": "object",
|
||||
"required": ["total_checks", "passed", "failed", "pass_rate"],
|
||||
"properties": {
|
||||
"total_checks": { "type": "integer", "minimum": 1 },
|
||||
"passed": { "type": "integer", "minimum": 0 },
|
||||
"failed": { "type": "integer", "minimum": 0 },
|
||||
"pass_rate": { "type": "number", "minimum": 0, "maximum": 1 },
|
||||
"na_count": { "type": "integer", "minimum": 0, "description": "Count of not-applicable items" },
|
||||
"blocker_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Count of BLOCKER findings (consumed by synthesis_task.md aggregation)"
|
||||
},
|
||||
"major_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Count of MAJOR findings (consumed by synthesis_task.md aggregation)"
|
||||
},
|
||||
"minor_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Count of MINOR findings (consumed by synthesis_task.md aggregation)"
|
||||
},
|
||||
"info_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Count of INFO findings (consumed by synthesis_task.md aggregation)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"findings": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["id", "severity", "checklist_ref", "location", "summary", "detail", "evidence", "suggestion"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Z0-9]+-F\\d{3,}$",
|
||||
"description": "Unique finding ID within the dimension file, form {OWN_DIMENSION_CODE}-F{NNN}, e.g. COR-F001. In a grouped dispatch the id uses the finding's OWN dimension code (e.g. TST-F001 inside the COR+TST+DBT group), never the joined group code. NOTE: uniqueness of the id VALUE is NOT enforced by findings[] uniqueItems (JSON Schema uniqueItems compares whole objects by deep equality); the synthesizer MUST post-check that id values are unique across all findings-{DIM}*.json files of the round (wired as step 1b of _shared/synthesis_task.md). Multi-slot dimensions (reviewer_slot set): prefix the slot in the id, e.g. TOPOR1-F001 / TOPOR2-F001, so ids stay unique across a dimension's slot files while still matching this pattern."
|
||||
},
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"enum": ["BLOCKER", "MAJOR", "MINOR", "INFO"]
|
||||
},
|
||||
"checklist_ref": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Z0-9]{2,6} \\d+(\\.\\d+)+(\\s*\\([^\\)]*\\))?(\\s*/\\s*[A-Z0-9]{2,6} \\d+(\\.\\d+)+(\\s*\\([^\\)]*\\))?)*$",
|
||||
"description": "Checklist item reference, e.g. COR 1.1, INV 1.3, SEC 3.7.1, STD 8.4 鈥?dimension/section code followed by item number(s)."
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "File:line or file:section where the issue occurs"
|
||||
},
|
||||
"summary": {
|
||||
"type": "string",
|
||||
"description": "One-line description"
|
||||
},
|
||||
"detail": {
|
||||
"type": "string",
|
||||
"description": "Full explanation of the finding"
|
||||
},
|
||||
"evidence": {
|
||||
"type": "string",
|
||||
"description": "Quote or excerpt from the document that supports this finding"
|
||||
},
|
||||
"suggestion": {
|
||||
"type": "string",
|
||||
"description": "Actionable recommendation to resolve the finding"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"reviewed_files": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "List of files actually reviewed"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/runs-bundle.schema.json",
|
||||
"title": "Tier 1 Run Archive Bundle",
|
||||
"description": "Composite archive bundle committed at close: runs/archive/{slug}.json = {meta, index}. Dual-profile by schema_version: >= 2 strict (closed core field set + free-form context bag), 1 or absent legacy (structural typing only; historical drift normalized by repair-run-bundles.ts, see [org-internal #2588]). Validated by validate-run-bundles.ts; CI wiring: .gitea/workflows/octopus-schemas.yml. Authoritative layout: templates/runs-layout.md. Boundary rule: rules/two-tier-artifacts.md.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["meta", "index"],
|
||||
"properties": {
|
||||
"meta": true,
|
||||
"index": true,
|
||||
"working_notes": {
|
||||
"type": "string",
|
||||
"description": "Final content of runs/{slug}/working-notes.md ([org-internal #2600]), inlined at archive time when the file exists. The one Tier 1 artifact whose CONTENT (not just digest) rides in the bundle 鈥?the run's narrative memory (decisions, dead ends, reviewer feedback), consumed by later retro probes and the notes-injection degradation path ([org-internal #2601]). Absent when the run kept no working notes."
|
||||
},
|
||||
"review_history": {
|
||||
"type": "array",
|
||||
"description": "Offline-verifiability digest of the run's review rounds (TD/TRC-F006, [org-internal #2688]): one entry per review stage the run executed, each derived from that stage's live reviews/{stage}/status.json history[] at archive time. Lets closed-run probes reconstruct round counts and verdicts from the bundle alone, without the (removed) live workspace or path reconstruction ([org-internal #2591]). Absent when the run kept no review rounds.",
|
||||
"items": { "$ref": "#/$defs/reviewHistoryEntry" }
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"meta": {
|
||||
"properties": { "schema_version": { "const": 2 } },
|
||||
"required": ["schema_version"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": { "properties": { "meta": { "$ref": "#/$defs/metaStrict" } } },
|
||||
"else": { "properties": { "meta": { "$ref": "#/$defs/metaLegacy" } } }
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"index": {
|
||||
"properties": { "schema_version": { "const": 2 } },
|
||||
"required": ["schema_version"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": { "properties": { "index": { "$ref": "#/$defs/indexStrict" } } },
|
||||
"else": { "properties": { "index": { "$ref": "#/$defs/indexLegacy" } } }
|
||||
}
|
||||
],
|
||||
"$defs": {
|
||||
"reviewHistoryEntry": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["stage", "rounds", "final_verdict"],
|
||||
"description": "Per-stage summary of one review stage's rounds. Shapes mirror review-status.schema.json (stage dir name; history[] item fields).",
|
||||
"properties": {
|
||||
"stage": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Review stage identifier matching the reviews/{stage}/ directory (e.g. code, review-dag, audit-process 鈥?the live values; design-space is a retired historical value)."
|
||||
},
|
||||
"rounds": { "type": "integer", "minimum": 1, "description": "Number of executed rounds." },
|
||||
"final_verdict": { "type": "string", "enum": ["PASS", "WARN", "FAIL"] },
|
||||
"per_round": {
|
||||
"type": "array",
|
||||
"description": "One entry per executed round, mirroring the live status.json history[] items.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["round", "overall_verdict", "blockers", "majors"],
|
||||
"properties": {
|
||||
"round": { "type": "integer", "minimum": 1 },
|
||||
"overall_verdict": { "type": "string", "enum": ["PASS", "WARN", "FAIL"] },
|
||||
"blockers": { "type": "integer", "minimum": 0 },
|
||||
"majors": { "type": "integer", "minimum": 0 },
|
||||
"minors": { "type": "integer", "minimum": 0, "description": "Count of MINOR findings for this round." }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ticket": {
|
||||
"oneOf": [
|
||||
{ "type": "null" },
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["owner", "repo", "number"],
|
||||
"properties": {
|
||||
"owner": { "type": "string", "minLength": 1 },
|
||||
"repo": { "type": "string", "minLength": 1 },
|
||||
"number": { "type": "integer", "minimum": 1 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "Source issue reference, or null for an ad-hoc run (no ticket)."
|
||||
},
|
||||
"parentEpic": {
|
||||
"oneOf": [
|
||||
{ "type": "null" },
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["number"],
|
||||
"properties": { "number": { "type": "integer", "minimum": 1 } }
|
||||
}
|
||||
],
|
||||
"description": "Parent Epic issue number, or null if the run is not under an Epic."
|
||||
},
|
||||
"metaLegacy": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"required": ["slug", "state", "created_at"],
|
||||
"description": "Legacy profile (schema_version 1 or absent): historical bundles AND current live-workspace runs 鈥?runs-meta.schema.json / runs-index.schema.json pin schema_version 1 today. Core fields type-checked when present; extra context properties (route/pr/node/kind/...) tolerated as-is. The v2 strict profile (below) is the migration target: it becomes the profile for new runs once the live-workspace schemas are bumped to emit schema_version 2 with the context bag.",
|
||||
"properties": {
|
||||
"schema_version": { "type": "integer", "minimum": 1 },
|
||||
"slug": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
||||
"branch": { "type": "string" },
|
||||
"state": { "type": "string", "enum": ["active", "archived"] },
|
||||
"created_at": { "type": "string", "anyOf": [{ "format": "date-time" }, { "format": "date" }] },
|
||||
"updated_at": { "type": "string", "anyOf": [{ "format": "date-time" }, { "format": "date" }] },
|
||||
"closed_at": { "type": ["string", "null"], "anyOf": [{ "format": "date-time" }, { "format": "date" }] },
|
||||
"close_commit_sha": {
|
||||
"type": ["string", "null"],
|
||||
"pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$",
|
||||
"description": "Full git SHA, or null when unresolvable (legacy runs whose landing commit is unknown)."
|
||||
},
|
||||
"ticket": { "$ref": "#/$defs/ticket" },
|
||||
"parent_epic": { "$ref": "#/$defs/parentEpic" },
|
||||
"worktree": { "type": ["string", "null"] }
|
||||
}
|
||||
},
|
||||
"metaStrict": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema_version", "slug", "state", "created_at", "updated_at", "ticket"],
|
||||
"description": "Strict profile (schema_version 2): closed core field set. Run-specific context (route/pr/node/task_node/...) goes in the context bag 鈥?the ONLY place for non-core fields.",
|
||||
"allOf": [
|
||||
{
|
||||
"if": { "properties": { "state": { "const": "archived" } }, "required": ["state"] },
|
||||
"then": {
|
||||
"properties": {
|
||||
"closed_at": { "type": "string", "format": "date-time" },
|
||||
"close_commit_sha": { "type": "string", "pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$" }
|
||||
},
|
||||
"required": ["closed_at", "close_commit_sha"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"schema_version": { "type": "integer", "const": 2 },
|
||||
"slug": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
||||
"branch": { "type": "string", "minLength": 1 },
|
||||
"worktree": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Absolute path of the worktree hosting this run, or null for ad-hoc runs without a worktree."
|
||||
},
|
||||
"state": { "type": "string", "enum": ["active", "archived"] },
|
||||
"created_at": { "type": "string", "format": "date-time" },
|
||||
"updated_at": { "type": "string", "format": "date-time" },
|
||||
"closed_at": { "type": ["string", "null"], "format": "date-time" },
|
||||
"close_commit_sha": { "type": ["string", "null"], "pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$" },
|
||||
"ticket": { "$ref": "#/$defs/ticket" },
|
||||
"parent_epic": { "$ref": "#/$defs/parentEpic" },
|
||||
"context": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"description": "Free-form run context (route, pr, node, task_node, mode, kind, ...). In v2 every non-core field MUST live here, not at meta top level."
|
||||
}
|
||||
}
|
||||
},
|
||||
"artifactLegacy": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"required": ["path"],
|
||||
"properties": {
|
||||
"path": { "type": "string", "minLength": 1 },
|
||||
"sha": {
|
||||
"type": ["string", "null"],
|
||||
"pattern": "^[0-9a-f]{12,64}$",
|
||||
"description": "Legacy digests may be truncated (12/16-hex; source files no longer exist, cannot be re-computed). v2 requires full 64-hex."
|
||||
}
|
||||
}
|
||||
},
|
||||
"artifactStrict": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["path", "type", "tier"],
|
||||
"properties": {
|
||||
"path": { "type": "string", "minLength": 1 },
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"meta",
|
||||
"index",
|
||||
"design",
|
||||
"plan",
|
||||
"review-findings",
|
||||
"review-status",
|
||||
"review-synthesis",
|
||||
"synthesis-prompt",
|
||||
"precondition-gate",
|
||||
"working-notes",
|
||||
"analysis",
|
||||
"browser-evidence",
|
||||
"other"
|
||||
]
|
||||
},
|
||||
"schema": { "type": ["string", "null"], "format": "uri" },
|
||||
"tier": { "type": "integer", "const": 1 },
|
||||
"stage": {
|
||||
"type": ["string", "null"],
|
||||
"enum": [
|
||||
"requirements",
|
||||
"design",
|
||||
"plan-iterations",
|
||||
"roadmap",
|
||||
"implement",
|
||||
"review-roadmap",
|
||||
"review-design-space",
|
||||
"review-iteration-plan",
|
||||
"review-dag",
|
||||
"review-code",
|
||||
"verify",
|
||||
"audit-process",
|
||||
"merge-pr",
|
||||
null
|
||||
],
|
||||
"description": "Producing pipeline stage. Live stage ids (per schemas/workflow-routing.schema.json): review-dag, review-code, verify, audit-process, merge-pr. Retired/phase names retained only so historical archived bundles keep validating."
|
||||
},
|
||||
"produced_by": {
|
||||
"type": ["string", "null"],
|
||||
"enum": [
|
||||
"Producer",
|
||||
"Reviewer",
|
||||
"Verifier",
|
||||
"Tool",
|
||||
"Coordinator",
|
||||
"Analyst",
|
||||
"Architect",
|
||||
"Planner",
|
||||
"Developer",
|
||||
"Synthesizer",
|
||||
"Orchestrator",
|
||||
"Remediator",
|
||||
"Auditor",
|
||||
null
|
||||
]
|
||||
},
|
||||
"sha": { "type": ["string", "null"], "pattern": "^[0-9a-f]{64}$" },
|
||||
"tier2_ref": { "type": ["string", "null"] }
|
||||
}
|
||||
},
|
||||
"indexLegacy": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"required": ["artifacts"],
|
||||
"properties": {
|
||||
"schema_version": { "type": "integer", "minimum": 1 },
|
||||
"slug": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
||||
"artifacts": { "type": "array", "items": { "$ref": "#/$defs/artifactLegacy" } }
|
||||
}
|
||||
},
|
||||
"indexStrict": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema_version", "slug", "artifacts"],
|
||||
"properties": {
|
||||
"schema_version": { "type": "integer", "const": 2 },
|
||||
"slug": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
||||
"artifacts": { "type": "array", "items": { "$ref": "#/$defs/artifactStrict" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/runs-index.schema.json",
|
||||
"title": "Tier 1 Run Artifact Index",
|
||||
"description": "Schema for runs/{slug}/index.json 鈥?the artifact manifest for a Tier 1 local structured run. This is the compact-recovery entry point after compaction (compact never deletes local files). Parallel to, not a replacement for, the Tier 2 '## 宸ヤ欢绱㈠紩' issue comment. Authoritative layout: templates/runs-layout.md. Boundary rule: rules/two-tier-artifacts.md.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema_version", "slug", "artifacts"],
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"description": "Optional JSON Schema $self reference for editor validation."
|
||||
},
|
||||
"schema_version": {
|
||||
"type": "integer",
|
||||
"const": 1,
|
||||
"description": "Index schema version."
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9][a-z0-9-]*$",
|
||||
"description": "Run slug; must match meta.json.slug."
|
||||
},
|
||||
"artifacts": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/artifact" },
|
||||
"description": "One entry per Tier 1 artifact in this run. Tier 2 artifacts (Gitea issue comments, wiki pages, commit status, PRs) are NEVER listed here 鈥?this index is Tier 1 only."
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"artifact": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["path", "type", "tier"],
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Artifact path relative to {active-slug}/. Use forward slashes."
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"meta",
|
||||
"index",
|
||||
"design",
|
||||
"plan",
|
||||
"review-findings",
|
||||
"review-status",
|
||||
"review-synthesis",
|
||||
"synthesis-prompt",
|
||||
"precondition-gate",
|
||||
"working-notes",
|
||||
"analysis",
|
||||
"browser-evidence",
|
||||
"other"
|
||||
],
|
||||
"description": "Artifact category. 'meta'/'index' for the manifest files themselves; stage categories for produced artifacts. 'review-synthesis' marks a synthesis result artifact (round verdict) as distinct from 'synthesis-prompt' (the Phase B task prompt) and 'review-status' (the lifecycle file). 'precondition-gate' marks the review-code mechanical-green gate log. 'working-notes' is the cross-stage narrative memory file ([org-internal #2600]) 鈥?one file per run, appended by every role at close, registered once (stage/produced_by null: multi-stage, multi-role). 'browser-evidence' marks a browser evidence pack manifest ([org-internal #4497] N-03, browser/{session-id}/manifest.json 鈥?shared/pack-manifest-v1)."
|
||||
},
|
||||
"schema": {
|
||||
"type": ["string", "null"],
|
||||
"format": "uri",
|
||||
"description": "$id of the validating JSON Schema, or null for schema-less artifacts (e.g. a prompt .md passed via prompt_file). Accepts $ids from the current namespace https://eightarms.net/octopus-workflow/schemas/*.schema.json as well as legacy $ids from the historical namespace https://eightarms.net/fourbroad/octopus/ (archived live runs' index.json carry the old values); legacy values resolve via schemas/id-aliases.json."
|
||||
},
|
||||
"tier": {
|
||||
"type": "integer",
|
||||
"const": 1,
|
||||
"description": "Tier marker. This index lists only Tier 1 artifacts, so the value is always 1."
|
||||
},
|
||||
"stage": {
|
||||
"type": ["string", "null"],
|
||||
"enum": [
|
||||
"requirements",
|
||||
"design",
|
||||
"plan-iterations",
|
||||
"roadmap",
|
||||
"implement",
|
||||
"review-roadmap",
|
||||
"review-design-space",
|
||||
"review-iteration-plan",
|
||||
"review-dag",
|
||||
"review-code",
|
||||
"verify",
|
||||
"audit-process",
|
||||
"merge-pr",
|
||||
null
|
||||
],
|
||||
"description": "Producing pipeline stage, or null for meta/index manifest entries. Live stage ids (per schemas/workflow-routing.schema.json): review-dag, review-code, verify, audit-process, merge-pr. The production-phase / retired names (requirements, design, plan-iterations, roadmap, implement, review-roadmap, review-design-space, review-iteration-plan) are retained ONLY so historical archived rows keep validating; new artifacts MUST use the live ids."
|
||||
},
|
||||
"produced_by": {
|
||||
"type": ["string", "null"],
|
||||
"enum": [
|
||||
"Producer",
|
||||
"Reviewer",
|
||||
"Verifier",
|
||||
"Tool",
|
||||
"Coordinator",
|
||||
"Analyst",
|
||||
"Architect",
|
||||
"Planner",
|
||||
"Developer",
|
||||
"Synthesizer",
|
||||
"Orchestrator",
|
||||
"Remediator",
|
||||
"Auditor",
|
||||
null
|
||||
],
|
||||
"description": "Role that produced the artifact, or null if not role-attributed. Canonical role names are Producer / Reviewer / Verifier / Tool / Coordinator (the `name` field of skills/_shared/roles/*.yaml). The remaining values are fine-grained sub-roles mapped to canonical roles via ROLE_ALIASES in role config: Analyst / Architect / Planner / Developer / Synthesizer / Orchestrator / Remediator map to `Producer`; Auditor maps to `Reviewer`. They are retained so historical index rows validate and so the audit target's revision role (Remediator) and reviewer sub-role (Auditor) can be attributed if ever written to a Tier 1 artifact."
|
||||
},
|
||||
"sha": {
|
||||
"type": ["string", "null"],
|
||||
"pattern": "^[0-9a-f]{64}$",
|
||||
"description": "sha256 content digest, or null if not yet computed."
|
||||
},
|
||||
"tier2_ref": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Cross-link to a Tier 2 mirror of this artifact (issue comment URL, wiki page path, commit status context) when one exists; null when the artifact is Tier 1 only."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/runs-meta.schema.json",
|
||||
"title": "Tier 1 Run Metadata",
|
||||
"description": "Schema for runs/{slug}/meta.json 鈥?run metadata for a Tier 1 local structured run. Authoritative layout: templates/runs-layout.md. Boundary rule: rules/two-tier-artifacts.md. State transitions active -> archived at close (archive-at-close).",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"slug",
|
||||
"state",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"ticket",
|
||||
"parent_epic",
|
||||
"closed_at",
|
||||
"close_commit_sha"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"if": { "properties": { "state": { "const": "archived" } }, "required": ["state"] },
|
||||
"then": {
|
||||
"properties": {
|
||||
"closed_at": { "type": "string", "format": "date-time" },
|
||||
"close_commit_sha": { "type": "string", "pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$" }
|
||||
},
|
||||
"required": ["closed_at", "close_commit_sha"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"description": "Optional JSON Schema $self reference for editor validation."
|
||||
},
|
||||
"schema_version": {
|
||||
"type": "integer",
|
||||
"const": 1,
|
||||
"description": "Layout schema version. Bumped on breaking changes to this shape."
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9][a-z0-9-]*$",
|
||||
"description": "Run slug. Matches the workflow branch tail and the Gitea wiki namespace."
|
||||
},
|
||||
"ticket": {
|
||||
"oneOf": [
|
||||
{ "type": "null" },
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["owner", "repo", "number"],
|
||||
"properties": {
|
||||
"owner": { "type": "string", "minLength": 1 },
|
||||
"repo": { "type": "string", "minLength": 1 },
|
||||
"number": { "type": "integer", "minimum": 1 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "Source issue reference, or null for an ad-hoc run (no ticket)."
|
||||
},
|
||||
"branch": {
|
||||
"type": "string",
|
||||
"description": "Workflow branch, e.g. workflow/enhancement/1968-two-tier-artifacts. Omitted for ad-hoc runs without a branch (both runs-bundle profiles tolerate omission)."
|
||||
},
|
||||
"worktree": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Absolute path of the worktree hosting this run, or null for ad-hoc runs without a worktree (aligned with runs-bundle.schema.json metaStrict)."
|
||||
},
|
||||
"parent_epic": {
|
||||
"oneOf": [
|
||||
{ "type": "null" },
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["number"],
|
||||
"properties": {
|
||||
"number": { "type": "integer", "minimum": 1 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "Parent Epic issue number, or null if the run is not under an Epic."
|
||||
},
|
||||
"state": {
|
||||
"type": "string",
|
||||
"enum": ["active", "archived"],
|
||||
"description": "active: run in progress (workspace gitignored). archived: closed; bundle committed under runs/archive/{slug}.json."
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "RFC 3339 timestamp 鈥?run creation."
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "RFC 3339 timestamp 鈥?last metadata mutation."
|
||||
},
|
||||
"closed_at": {
|
||||
"type": ["string", "null"],
|
||||
"format": "date-time",
|
||||
"description": "RFC 3339 timestamp set at archive-at-close, or null while active."
|
||||
},
|
||||
"close_commit_sha": {
|
||||
"type": ["string", "null"],
|
||||
"pattern": "^([0-9a-f]{40}|[0-9a-f]{64})$",
|
||||
"description": "git SHA of the commit that landed archive/{slug}.json; null while active. Must be non-null when state=archived (enforced by the if/then invariant)."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/synthesis.schema.json",
|
||||
"title": "Synthesis Output",
|
||||
"description": "Schema for the orchestrator's synthesis of all reviewer outputs for a single round.",
|
||||
"type": "object",
|
||||
"required": ["round", "overall_verdict", "dimensions", "aggregated_stats", "top_findings"],
|
||||
"properties": {
|
||||
"round": { "type": "integer", "minimum": 1 },
|
||||
"overall_verdict": {
|
||||
"type": "string",
|
||||
"enum": ["PASS", "WARN", "FAIL"],
|
||||
"description": "PASS: all dimensions PASS. WARN: no BLOCKER, no dimension FAIL. FAIL: otherwise."
|
||||
},
|
||||
"dimensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"required": ["verdict", "pass_rate", "blocker_count", "major_count", "minor_count", "info_count"],
|
||||
"properties": {
|
||||
"verdict": { "type": "string", "enum": ["PASS", "WARN", "FAIL", "UNRESOLVED"] },
|
||||
"pass_rate": { "type": "number" },
|
||||
"blocker_count": { "type": "integer" },
|
||||
"major_count": { "type": "integer" },
|
||||
"minor_count": { "type": "integer" },
|
||||
"info_count": { "type": "integer" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"aggregated_stats": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"total_dimensions",
|
||||
"passed_dimensions",
|
||||
"warn_dimensions",
|
||||
"failed_dimensions",
|
||||
"total_findings",
|
||||
"blockers",
|
||||
"majors",
|
||||
"minors",
|
||||
"infos"
|
||||
],
|
||||
"properties": {
|
||||
"total_dimensions": { "type": "integer" },
|
||||
"passed_dimensions": { "type": "integer" },
|
||||
"warn_dimensions": { "type": "integer" },
|
||||
"failed_dimensions": { "type": "integer" },
|
||||
"total_findings": { "type": "integer" },
|
||||
"blockers": { "type": "integer" },
|
||||
"majors": { "type": "integer" },
|
||||
"minors": { "type": "integer" },
|
||||
"infos": { "type": "integer" },
|
||||
"unresolved_dimensions": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Count of dimensions marked UNRESOLVED (auditor crash/timeout) and excluded from aggregation"
|
||||
}
|
||||
}
|
||||
},
|
||||
"top_findings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["id", "dimension", "severity", "checklist_ref", "summary"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Z0-9]+-F\\d{3,}$",
|
||||
"description": "The source reviewer finding id (from the Tier 1 findings file; slot-prefixed for multi-slot dimensions), so each synthesized headline traces back to its raw finding."
|
||||
},
|
||||
"dimension": { "type": "string" },
|
||||
"severity": { "type": "string", "enum": ["BLOCKER", "MAJOR", "MINOR", "INFO"] },
|
||||
"checklist_ref": { "type": "string" },
|
||||
"summary": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://eightarms.net/octopus-workflow/schemas/workflow-routing.schema.json",
|
||||
"title": "Workflow Routing Table",
|
||||
"description": "Schema for workflow-routing.yaml 鈥?type-driven (Kind/*) process selection. Referenced by rules/workflow-routing.md and the auto-approve rule. When this schema and the rules markdown disagree, the YAML (validated by this schema) is authoritative.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["version", "default", "routes"],
|
||||
"properties": {
|
||||
"$schema": { "type": "string" },
|
||||
"version": { "type": "integer", "const": 1 },
|
||||
"default": { "$ref": "#/$defs/route" },
|
||||
"routes": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#/$defs/route" },
|
||||
"description": "Keys are Kind/* label names, e.g. Kind/Bug, Kind/Feature."
|
||||
},
|
||||
"preflight": { "$ref": "#/$defs/preflightPolicy" },
|
||||
"dag": { "$ref": "#/$defs/dag" },
|
||||
"filing": { "$ref": "#/$defs/filing" },
|
||||
"review": { "$ref": "#/$defs/review" }
|
||||
},
|
||||
"$defs": {
|
||||
"stage": {
|
||||
"type": "string",
|
||||
"description": "A pipeline stage identifier 鈥?the 5 live gates (post-[org-internal #3072] phase 3, 2026-08-21): review-dag, review-code, verify, audit-process, merge-pr. Production-phase names (requirements, design, roadmap, implement) and the 7 retired stage ids (roadmap-exit, review-roadmap, requirements-exit, design-exit, review-design-space, review-iteration-plan, plan-iterations) are no longer valid anywhere 鈥?the sizing block that consumed the phase names was retired the same ruling.",
|
||||
"enum": ["review-dag", "review-code", "verify", "audit-process", "merge-pr"]
|
||||
},
|
||||
"entry": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"skill": {
|
||||
"type": ["string", "null"],
|
||||
"enum": ["implement", "review-artifact", "review-code", "verify", "analyze-dag", null],
|
||||
"description": "Skill name to enter, or null for natural-language routing. The legacy roadmap / requirements-elicitation / design / plan-iterations entries were removed when those skills were archived ([org-internal #3072] phase 3, 2026-08-21 - the instance archive)."
|
||||
},
|
||||
"mode": {
|
||||
"type": ["string", "null"],
|
||||
"enum": ["bugfix", "refactor", "port", null],
|
||||
"description": "Skill mode, or null."
|
||||
}
|
||||
}
|
||||
},
|
||||
"stageList": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/stage" }
|
||||
},
|
||||
"route": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"entry": { "$ref": "#/$defs/entry" },
|
||||
"skip": { "$ref": "#/$defs/stageList" },
|
||||
"keep_gates": { "$ref": "#/$defs/stageList" },
|
||||
"preflight": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/preflightItem" },
|
||||
"description": "Evidence-based producer pre-flight checklist ([org-internal #2599]) 鈥?landed by a human from retrospective PRE-FLIGHT PROPOSAL rows. Absent/empty = no injection. Cap is governed by preflight.max_items (policy), not the schema."
|
||||
},
|
||||
"notes": { "type": "string" },
|
||||
"effort": {
|
||||
"type": "string",
|
||||
"enum": ["low", "high", "max"],
|
||||
"description": "Reasoning-effort hint for this route (llm-latency phase 2): maps onto the active model's variant table when it exposes one (GLM-5.2+ low/high/max); fail-open otherwise. Reserve `low` for genuinely lightweight routes (direct-edit, no pipeline) 鈥?reasoning dominates GLM-5.3 generation latency."
|
||||
}
|
||||
}
|
||||
},
|
||||
"preflightPolicy": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"description": "Thresholds for the retro 鈫?routes.{Kind}.preflight evidence loop ([org-internal #2599]). Authoritative values live here; the retrospective consults this block, it does not restate the numbers.",
|
||||
"properties": {
|
||||
"max_items": { "type": "integer", "minimum": 1 },
|
||||
"min_sample": { "type": "integer", "minimum": 0 },
|
||||
"consecutive_recur": { "type": "integer", "minimum": 1 },
|
||||
"aging_consecutive_clean": { "type": "integer", "minimum": 1 }
|
||||
}
|
||||
},
|
||||
"preflightItem": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["id", "item", "evidence"],
|
||||
"properties": {
|
||||
"id": { "type": "string", "description": "Stable slug, e.g. cor-unhandled-null." },
|
||||
"item": { "type": "string", "description": "One checklist line the Producer verifies before implementing." },
|
||||
"evidence": {
|
||||
"type": "string",
|
||||
"description": "Retro reference justifying the item, e.g. 'retro [org-internal #2599]: COR 脳 unhandled-null, 5/8 first-round FAILs on Kind/Bug'."
|
||||
},
|
||||
"added_cycle": {
|
||||
"type": "string",
|
||||
"description": "Cycle id when landed, e.g. 2026-08. Drop-oldest beyond preflight.max_items sorts on this."
|
||||
}
|
||||
}
|
||||
},
|
||||
"filing": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"description": "Filing policy (绔嬫/鐧昏绛栫暐). sprint-mode ([org-internal #3007]): when true, verify Phase 5.5 promotes registry rows to tickets in sprint mode only. work-ticket-wip-cap ([org-internal #3005] G3): open non-TD work tickets beyond this count require scheduling evidence, else they stay registry rows.",
|
||||
"properties": {
|
||||
"sprint-mode": {
|
||||
"type": "boolean",
|
||||
"description": "Sprint-mode filing downgrade switch ([org-internal #3007])."
|
||||
},
|
||||
"work-ticket-wip-cap": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "WIP cap on open non-TD work tickets (Kind/* without assignee) before new filings require scheduling evidence ([org-internal #3005] G3)."
|
||||
}
|
||||
}
|
||||
},
|
||||
"review": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"description": "Review round budget (process-diet P0b, [org-internal #3567]/TD-564): a tail cut, not the norm 鈥?at budget without convergence, STOP; residual findings flow to the TD registry. High risk = a BLOCKER survived a round or the diff touches a frozen cross-session contract.",
|
||||
"required": ["max_rounds"],
|
||||
"properties": {
|
||||
"max_rounds": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["default", "high_risk"],
|
||||
"properties": {
|
||||
"default": { "type": "integer" },
|
||||
"high_risk": { "type": "integer" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dag": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["route", "task_route", "ticket_creation", "verify_milestone", "size_derivation", "review_depth"],
|
||||
"properties": {
|
||||
"route": { "$ref": "#/$defs/dagRoute" },
|
||||
"task_route": { "$ref": "#/$defs/dagTaskRoute" },
|
||||
"ticket_creation": { "$ref": "#/$defs/dagTicketCreation" },
|
||||
"verify_milestone": { "$ref": "#/$defs/dagVerifyMilestone" },
|
||||
"size_derivation": { "$ref": "#/$defs/dagSizeDerivation" },
|
||||
"review_depth": { "$ref": "#/$defs/dagReviewDepth" }
|
||||
}
|
||||
},
|
||||
"dagRoute": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["entry", "skip", "keep_gates", "single_gate"],
|
||||
"properties": {
|
||||
"entry": { "$ref": "#/$defs/entry" },
|
||||
"skip": { "$ref": "#/$defs/stageList" },
|
||||
"keep_gates": { "$ref": "#/$defs/stageList" },
|
||||
"single_gate": { "$ref": "#/$defs/dagSingleGate" },
|
||||
"preflight": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/preflightItem" },
|
||||
"description": "Evidence-based pre-flight checklist for the DAG route ([org-internal #2905] 鏂规 3) 鈥?same item shape as routes.{Kind}.preflight ([org-internal #2599]). Landed by a human from retrospective PRE-FLIGHT PROPOSAL rows, including the 'REQ 脳 late-discovery' root cause (requirements found only after the DAG froze: DAG oversize signals, demo-period feedback). Consumed by analyze-dag (the route's entry skill) before decomposition. Absent/empty = no injection. Optional 鈥?deliberately NOT in dagRoute.required (spec-02 搂2.8 frozen required array unchanged). Cap governed by preflight.max_items (policy), not the schema."
|
||||
}
|
||||
}
|
||||
},
|
||||
"dagTaskRoute": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["entry", "skip", "keep_gates", "inherit"],
|
||||
"properties": {
|
||||
"entry": { "$ref": "#/$defs/entry" },
|
||||
"skip": { "$ref": "#/$defs/stageList" },
|
||||
"keep_gates": { "$ref": "#/$defs/stageList" },
|
||||
"inherit": { "type": "string", "enum": ["parent_dag"] }
|
||||
}
|
||||
},
|
||||
"dagSingleGate": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["id", "dimensions", "replaces", "depth_from", "never_trim"],
|
||||
"properties": {
|
||||
"id": { "type": "string", "enum": ["review-dag"] },
|
||||
"dimensions": {
|
||||
"type": "array",
|
||||
"items": { "type": "string", "enum": ["TOPO", "REQMAP", "RELEASE"] },
|
||||
"uniqueItems": true,
|
||||
"minItems": 3,
|
||||
"maxItems": 3
|
||||
},
|
||||
"replaces": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Historical documentation of which legacy gates the single gate replaced ([org-internal #3072] phase 3 retired them) 鈥?free-form strings, not the live stage vocabulary."
|
||||
},
|
||||
"depth_from": { "type": "string", "enum": ["size_derivation"] },
|
||||
"never_trim": { "type": "boolean" }
|
||||
}
|
||||
},
|
||||
"dagTicketCreation": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["per_node", "milestone_no_ticket", "inherit_parent", "parent_aggregator"],
|
||||
"properties": {
|
||||
"per_node": { "type": "boolean" },
|
||||
"milestone_no_ticket": { "type": "boolean" },
|
||||
"inherit_parent": {
|
||||
"type": "array",
|
||||
"items": { "type": "string", "enum": ["Kind/Epic", "Kind/Feature"] },
|
||||
"uniqueItems": true,
|
||||
"minItems": 1,
|
||||
"description": "Ticket kinds whose DAG children reference them as ## 鐖剁骇 / Parent ([org-internal #3061] Phase 2 / TD-388: Kind/Feature added alongside Kind/Epic)."
|
||||
},
|
||||
"parent_aggregator": { "type": "boolean" }
|
||||
}
|
||||
},
|
||||
"dagVerifyMilestone": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["weld_at_convergence", "milestone_skill", "integration_trigger"],
|
||||
"properties": {
|
||||
"weld_at_convergence": { "type": "boolean" },
|
||||
"milestone_skill": { "type": "string", "enum": ["verify"] },
|
||||
"integration_trigger": { "type": "string", "enum": ["all_incoming_done_and_green"] }
|
||||
}
|
||||
},
|
||||
"dagSizeDerivation": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["enabled", "thresholds", "size_labels", "oversize_signals"],
|
||||
"properties": {
|
||||
"enabled": { "type": "boolean" },
|
||||
"formula": { "type": "string" },
|
||||
"thresholds": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"node_count": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"d1": { "type": "integer" },
|
||||
"d2": { "type": "integer" },
|
||||
"d3": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"cross_session_edge_count": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"d1": { "type": "integer" },
|
||||
"d3": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"contract_change_surface": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"none": { "type": "string" },
|
||||
"additive": { "type": "string" },
|
||||
"breaking": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"size_labels": { "type": "string" },
|
||||
"oversize_signals": { "type": "array", "items": { "$ref": "#/$defs/dagOversizeSignal" } }
|
||||
}
|
||||
},
|
||||
"dagOversizeSignal": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["id", "trigger"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"node_split",
|
||||
"new_cross_session_edge",
|
||||
"draft_contract_breaking",
|
||||
"frozen_contract_breaking",
|
||||
"node_scope_drift"
|
||||
]
|
||||
},
|
||||
"trigger": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"dagReviewDepth": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["D1", "D2", "D3", "D4"],
|
||||
"properties": {
|
||||
"D1": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["reviewers", "rounds", "dim_split"],
|
||||
"properties": {
|
||||
"reviewers": { "type": "integer" },
|
||||
"rounds": { "type": "integer" },
|
||||
"dim_split": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"D2": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["reviewers", "rounds", "dim_split"],
|
||||
"properties": {
|
||||
"reviewers": { "type": "integer" },
|
||||
"rounds": { "type": "integer" },
|
||||
"dim_split": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"D3": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["reviewers", "rounds", "dim_split"],
|
||||
"properties": {
|
||||
"reviewers": { "type": "integer" },
|
||||
"rounds": { "type": "integer" },
|
||||
"dim_split": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"D4": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["reviewers", "rounds", "dim_split"],
|
||||
"properties": {
|
||||
"reviewers": { "type": "integer" },
|
||||
"rounds": { "type": "integer" },
|
||||
"dim_split": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Shared Rule — Round-Boundary Context Compaction
|
||||
|
||||
> **Canonical source of truth.** This file is the single authoritative copy of
|
||||
> the round-boundary compaction rule. It is injected **directly** (via
|
||||
> `<!-- inject -->`) by two skills:
|
||||
>
|
||||
> - `review-code/SKILL.md`
|
||||
> - `review-artifact/SKILL.md`
|
||||
>
|
||||
> Both inject tags are resolved by `injectShared`
|
||||
> (`<harness-package>/src/tool/skill.ts`) at skill-load time. Do not place this
|
||||
> inject inside a file that is *itself* injected (a nested inject) —
|
||||
> `injectShared` is single-pass and does not resolve nested inject tags; they
|
||||
> survive as literal `<!-- inject -->` comments and the procedure is silently
|
||||
> lost.
|
||||
>
|
||||
> **When editing:** update this file only. Every consumer picks up the change
|
||||
> automatically at load time — do not copy the text into any skill.
|
||||
>
|
||||
> This is a reference document, not a skill (no `SKILL.md` frontmatter), so the
|
||||
> skill loader does not register it. It exists purely so maintainers have one
|
||||
> place to edit.
|
||||
|
||||
## Why a canonical copy exists
|
||||
|
||||
Every multi-round review skill (`review-artifact`, `review-code`) needs the model to
|
||||
read this rule inline at the point it dispatches rounds — a cross-file
|
||||
reference would not be loaded into the model's context. So the text is
|
||||
injected directly into both skills at load time. The injection is intentional;
|
||||
this file exists to keep the two consumers on identical wording without a
|
||||
copy-paste mirror.
|
||||
|
||||
## Canonical text
|
||||
|
||||
The block below is what each consumer receives at the inject site. The guard
|
||||
referenced is the agent-initiated `compact` tool's minimum-turns guard,
|
||||
configured at `compaction.agent.min_turns_since_compaction` (default **3**).
|
||||
|
||||
> **Context compaction (round ≥ 2)**: The Orchestrator is the only long-lived
|
||||
> context across rounds; per-round dimension outputs, synthesis, and
|
||||
> convergence tracking must not accumulate unbounded. Once Phase B has posted
|
||||
> the commit status (all round state persisted in the synthesis comment + commit status)
|
||||
> and before dispatching the next
|
||||
> round in Phase A, the Orchestrator MUST run `compact` to summarize the
|
||||
> conversation through the current round, then re-read
|
||||
> the latest synthesis comment via `工单评论 API(见 TERMINOLOGY)` to restore
|
||||
> `current_round` and convergence tracking. The
|
||||
> Synthesizer's return summary (verdict, per-dimension results, re-audit
|
||||
> targets) is carried in the compacted conversation; if it was lost, re-read
|
||||
> the synthesis comment's fenced JSON block as fallback before proceeding.
|
||||
> Skip on round 1
|
||||
> (single-pass reviews never loop). Each round then starts from a bounded
|
||||
> context anchored on file state, not conversation memory.
|
||||
>
|
||||
> This round-boundary compaction is mandatory, but it is NOT exempt from the
|
||||
> `compact` tool's `min_turns_since_compaction` guard
|
||||
> (`compaction.agent.min_turns_since_compaction`, default 3): no such exemption
|
||||
> exists in the tool implementation. If the tool rejects with
|
||||
> `insufficient_turns`, accept the rejection and continue into the next round
|
||||
> without compacting — do NOT retry or work around the guard. A round with too
|
||||
> few turns since the last compaction simply keeps its context; the synthesis
|
||||
> comment + commit status still bound what accumulates.
|
||||
|
||||
## Canonical note line
|
||||
|
||||
There is no `*(Shared rule …)*` italic note line in any consumer — the inject
|
||||
directive resolves to this file's full content (this header included) at load
|
||||
time. Older revisions of this document described a two-step "copy the
|
||||
canonical text into each mirror" sync procedure; that procedure no longer
|
||||
exists and must not be reintroduced.
|
||||
@@ -0,0 +1,35 @@
|
||||
# Shared Rule — Large Prompts via `prompt_file`
|
||||
|
||||
> **Canonical source of truth.** This file is the single authoritative copy of
|
||||
> the large-prompts-via-prompt_file rule. The following skills reference this
|
||||
> rule via an `<!-- inject -->` comment and MUST NOT maintain their own copies:
|
||||
>
|
||||
> - `review-code/SKILL.md`
|
||||
> - `implement/SKILL.md`
|
||||
> - `frontend/SKILL.md`
|
||||
>
|
||||
> (The `roadmap`, `requirements-elicitation`, `design`, and `plan-iterations`
|
||||
> referencing skills were archived 2026-08-21, [org-internal #3072] phase 3 — see
|
||||
> `.octopus/archive/skills/`; their copies of this directive moved with them.)
|
||||
>
|
||||
> **When editing:** update this file only. Each skill references this file via a
|
||||
> one-line `<!-- inject -->` directive — there are no verbatim mirrors to sync.
|
||||
>
|
||||
> This is a reference document, not a skill (no `SKILL.md` frontmatter), so the
|
||||
> skill loader does not register it.
|
||||
|
||||
## Why a canonical copy exists
|
||||
|
||||
The `task-tool-discipline` rule in `AGENTS.md` requires large prompts to be
|
||||
written to a file and passed via `prompt_file`. This guidance applies to every
|
||||
skill that dispatches sub-agents with potentially large prompts. A single
|
||||
canonical source prevents drift and ensures the rule, file path convention
|
||||
(`/tmp/octopus/<slug>.md`), and justification stay consistent.
|
||||
|
||||
## Canonical text
|
||||
|
||||
> **Large Prompts via `prompt_file`**
|
||||
>
|
||||
> Follow the `task-tool-discipline` rule in `AGENTS.md`: any prompt expected to
|
||||
> exceed ~1 KB MUST be written to a file first and passed via the `task` tool's
|
||||
> `prompt_file` parameter (e.g. `/tmp/octopus/<slug>.md`), never inlined.
|
||||
@@ -0,0 +1,75 @@
|
||||
# Shared Final Report Template
|
||||
|
||||
> Canonical final-report markdown template shared across review skills. Read
|
||||
> on-demand by the Orchestrator when writing the final report in Phase E.
|
||||
|
||||
```markdown
|
||||
# {REVIEW_TYPE} Final Report
|
||||
|
||||
- **Project**: {slug}
|
||||
- **Rounds completed**: {N}
|
||||
- **Final verdict**: {PASS | WARN | FAIL}
|
||||
- **Converged**: {true | false — reason}
|
||||
|
||||
## Dimension Summary
|
||||
|
||||
| Dimension | Final Verdict | Pass Rate | R1→RN |
|
||||
| --------- | ------------- | --------- | ----- |
|
||||
| ... | | | |
|
||||
|
||||
## Outstanding Findings
|
||||
|
||||
### Accepted Risks (for tech-debt issue creation in verify Phase 5.5)
|
||||
|
||||
> verify Phase 5.5 会将下表每项 Accepted Risk 创建为 Gitea issue
|
||||
> (label `tech-debt`,标题含 `TD-NNN`)。issue 集即技术债登记表——
|
||||
> 不再有 `tech-debt.md` 文件或 `{slug}/tech-debt` wiki 页。
|
||||
|
||||
| Finding ID | Severity | Description | Recommended Reactivation Trigger |
|
||||
|-----------|----------|-------------|--------------------------------|
|
||||
| {ID} | HIGH/MEDIUM/LOW | {描述} | {可客观判断的条件} |
|
||||
|
||||
> Note: the Severity column uses the **TD-registry severity scale**
|
||||
> (HIGH/MEDIUM/LOW → Priority/High|Medium|Low labels, feeding verify Phase
|
||||
> 5.5's `## TD 登记` rows and promotion labels), NOT the review-finding
|
||||
> severity scale (BLOCKER/MAJOR/MINOR/INFO) — reviewers assign the TD
|
||||
> severity directly when accepting a risk.
|
||||
|
||||
### Other Open Items
|
||||
|
||||
...
|
||||
|
||||
## Review History
|
||||
|
||||
| Round | Verdict | Blockers | Majors | Minors |
|
||||
| ----- | ------- | -------- | ------ | ------ |
|
||||
| ... | | | | |
|
||||
|
||||
## Round-1 Findings Summary
|
||||
|
||||
> Mandatory for `review-code` (Phase E step 2.55): one row per round-1
|
||||
> finding, produced by the round-1 Synthesizer as
|
||||
> `.octopus/runs/{slug}/reviews/{stage}/round1/findings-summary.md`
|
||||
> (`_shared/review-pipeline-phases.md` Phase B step 3 "Round-1 findings
|
||||
> digest") and copied verbatim by the Orchestrator — the Orchestrator does
|
||||
> NOT read the findings JSONs — before they are deleted at archive-at-close.
|
||||
> This table is the durable record retro M7 reads. Other targets MAY include
|
||||
> it when round-1 findings data exists.
|
||||
|
||||
| Dim | Finding ID | Severity | Summary |
|
||||
| --- | ---------- | -------- | ------- |
|
||||
| ... | | | |
|
||||
```
|
||||
|
||||
## Placeholders
|
||||
|
||||
- `{REVIEW_TYPE}` — the value the skill's Phase Bindings declares, used
|
||||
verbatim in the title (`# {REVIEW_TYPE} Final Report`): "Code Review"
|
||||
(review-code — human label; its commit-status context is separately fixed
|
||||
to `pipeline/review-code`), or the machine ids `audit-process` /
|
||||
`review-dag` (review-artifact targets — same id feeds the
|
||||
`pipeline/{REVIEW_TYPE}` commit-status context; see review-artifact Phase
|
||||
Bindings). (The design-space/plan targets were archived 2026-08-21,
|
||||
[org-internal #3072] phase 3.)
|
||||
- `{DIMENSION_COUNT}` — 10 (code review; 9 in standalone-bugfix mode), 3 (review-dag single gate), or 10 (process audit); used in
|
||||
the skill's SKILL.md reference text, not inside this template body
|
||||
@@ -0,0 +1,168 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Shared Orchestrator Rules (Mandatory)
|
||||
|
||||
> Canonical Orchestrator behavior rules shared across review skills. Read
|
||||
> on-demand by the Orchestrator. These rules constrain the Orchestrator's behavior;
|
||||
> violating any of them invalidates the review.
|
||||
|
||||
1. **No scope narrowing**: The Orchestrator MUST NOT add qualifiers to the
|
||||
reviewer prompt that discourage thoroughness. Forbidden phrases include:
|
||||
"Focus on the most important issues", "Prioritize critical findings",
|
||||
"Limit to top N items", "Be concise", or any wording that suggests the
|
||||
reviewer should self-censor.
|
||||
|
||||
2. **Standardized prompts only**: Every reviewer receives the prompt from the
|
||||
template field-for-field. The Orchestrator MUST NOT modify the template
|
||||
structure, add constraints, or remove required sections.
|
||||
|
||||
3. **Quantitative verdicts only**: A PASS verdict REQUIRES pass_rate = 1.0
|
||||
with quantitative_summary populated. The Orchestrator MUST NOT promote a
|
||||
WARN to PASS based on subjective judgment. If pass_rate < 1.0, the
|
||||
dimension is at best WARN; if pass_rate < 0.7 or any BLOCKER finding exists,
|
||||
the dimension MUST be FAIL. INFO findings do NOT reduce `pass_rate` and do
|
||||
NOT affect the dimension verdict — a dimension whose only findings are INFO
|
||||
is PASS. INFO is dispositioned (`TD-candidate` / `DISCARDED`) at synthesis
|
||||
and never enters the revision loop (see
|
||||
`_shared/review-pipeline-phases.md` Phase D, "Severity handling — INFO
|
||||
disposition").
|
||||
|
||||
4. **Synthesis is aggregation, not reinterpretation**: The Orchestrator MUST NOT
|
||||
soften, rephrase, or downgrade reviewer findings during synthesis.
|
||||
If a reviewer writes "BLOCKER", it stays "BLOCKER" in synthesis.
|
||||
|
||||
5. **Anonymous findings preservation**: If two reviewers independently flag
|
||||
the same issue from different dimensions, preserve both entries. Do not
|
||||
deduplicate by discarding one — cross-dimension agreement is itself a
|
||||
signal.
|
||||
|
||||
6. **File-based communication**: Do not inline large text blocks in prompts.
|
||||
Use wiki page paths. Reviewer output goes to the skill's configured findings
|
||||
sink (see `_shared/review-pipeline-phases.md` "Output model — two findings
|
||||
sinks"): either a Tier 1 local file
|
||||
`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`
|
||||
(validated by `reviewer-output.schema.json`) for migrated skills like
|
||||
`review-code`, or an issue comment via `工单评论 API(见 TERMINOLOGY)` (Pattern
|
||||
5) for unmigrated skills. Tier boundary: `core/rules/two-tier-artifacts.md`.
|
||||
Either way, reviewer output does NOT travel as agent reply text.
|
||||
|
||||
7. **Auditor independence**: Reviewers are read-only Explorers. Only the
|
||||
{REVISION_ROLE} (a separate Worker, run in a different phase) may edit files.
|
||||
The Orchestrator MUST NOT let a reviewer both judge and fix.
|
||||
|
||||
8. **Subagent dispatch contract** (binding — omitting `subagent_type` is the
|
||||
#1 review-skill dispatch failure): every `task` tool call MUST resolve an
|
||||
agent type. The skill prose uses role names ("Orchestrator", "Architect",
|
||||
"Reviewer", "Developer", "Analyst", "Planner", "Verifier", "Remediator", "Synthesizer") — those are
|
||||
`role` values, NOT agent types. Map them to the `task` tool parameters as
|
||||
follows:
|
||||
|
||||
| Skill role | `role` param | `subagent_type` param |
|
||||
| ----------------- | -------------- | --------------------- |
|
||||
| Reviewer | `Reviewer` | `explorer` |
|
||||
| Orchestrator | `Orchestrator` | `worker` |
|
||||
| Architect | `Architect` | `worker` |
|
||||
| Developer / Tester| `Developer` | `worker` |
|
||||
| Analyst | `Analyst` | `worker` |
|
||||
| Planner | `Planner` | `worker` |
|
||||
| Verifier | `Verifier` | `worker` |
|
||||
| Remediator | `Remediator` | `worker` |
|
||||
| Synthesizer | `Synthesizer` | `worker` |
|
||||
|
||||
The `agent` type is declared in `roles/*.yaml` for the five roles that
|
||||
have a role YAML — `producer.yaml` (worker), `reviewer.yaml` (explorer),
|
||||
`tool.yaml` (explorer), `verifier.yaml` (worker), and `coordinator.yaml`
|
||||
(worker; the pool coordinator session — not a task-dispatch role, so it
|
||||
does not appear in the table above). The task tool derives
|
||||
`subagent_type` from `role` automatically when `subagent_type` is omitted
|
||||
AND the role's `agent` field is defined. All other roles in the table above
|
||||
(Orchestrator, Architect, Developer, Analyst, Planner, Remediator,
|
||||
Synthesizer) have NO role YAML and therefore MUST pass `subagent_type`
|
||||
explicitly — never pass only `role` + `description` and assume a default.
|
||||
|
||||
Concrete example (dispatching a reviewer):
|
||||
|
||||
```
|
||||
task(
|
||||
description: "Review CMP dimension",
|
||||
role: "Reviewer",
|
||||
subagent_type: "explorer", // optional here (derivable from role), shown for clarity
|
||||
permissions: [{ permission: "read", pattern: "*", action: "allow" }, ...],
|
||||
temperature: 0.1,
|
||||
prompt: <filled reviewer template>
|
||||
)
|
||||
```
|
||||
|
||||
Forbidden: a `task` call that names the role in prose ("Spawn the
|
||||
Architect") but omits BOTH `subagent_type` and a defined-`agent` `role` —
|
||||
it fails with `TaskMissingSubagentType`.
|
||||
|
||||
## Default Explorer Permissions
|
||||
|
||||
All review and audit skills that spawn Explorer sub-agents use the same
|
||||
read-only BASE permission set. The canonical base template is:
|
||||
|
||||
```
|
||||
permissions: [
|
||||
{ permission: "read", pattern: "*", action: "allow" },
|
||||
{ permission: "glob", pattern: "*", action: "allow" },
|
||||
{ permission: "grep", pattern: "*", action: "allow" }
|
||||
]
|
||||
```
|
||||
|
||||
Each skill references this base template via `core/skills/_shared/review-orchestrator-rules.md`
|
||||
rather than inlining the JSON array. This centralization ensures the base
|
||||
permission surface stays consistent across all review/audit skills.
|
||||
|
||||
**Tier-1 local exception** ([org-internal #1988]): migrated skills that write raw per-dimension
|
||||
findings to `<runs-root>/**` (e.g. `review-code`) add ONE scoped `edit` allow
|
||||
to the base set — `{ permission: "edit", pattern: "<runs-root>/**", action:
|
||||
"allow" }` — so the reviewer can write its OWN finding file. This is a
|
||||
documented exception to the read-only base, not a divergence: it does NOT grant
|
||||
edit on the code under review, so rule #7 (auditor independence) holds. See
|
||||
`_shared/review-pipeline-phases.md` Phase A step 4 and `review-code/SKILL.md`
|
||||
Phase A step 3 for the inline extended array.
|
||||
|
||||
Residual risk — accepted trade-off (TD/STD-F004, [org-internal #2688]): the grant is scoped
|
||||
to `<runs-root>/**`, wider than the reviewer's own findings file. Per-file
|
||||
narrowing was considered and rejected: findings paths are round- and
|
||||
dimension-dependent (`{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`),
|
||||
which makes per-file permission patterns brittle at spawn time, and `runs/**`
|
||||
holds only transient Tier 1 artifacts (digest-summarized at archive). The
|
||||
residual exposure — a misbehaving reviewer could overwrite a sibling stage's
|
||||
Tier 1 files — is recorded here as accepted; auditor independence is
|
||||
unaffected (code under review lives outside `runs/`).
|
||||
|
||||
### No bash for Explorer sub-agents (deliberate boundary, [org-internal #2115] RC4 / [org-internal #2126])
|
||||
|
||||
Explorer sub-agents (Reviewer / auditor / Tool roles) have **no bash**. The
|
||||
`explorer` agent is built deny-by-default at
|
||||
`<harness-package>/src/agent/agent.ts` (`"*": "deny"` + explicit `read`/`glob`/
|
||||
`grep` allows + `bash: "deny"`), so the `permissions` array above is read-only
|
||||
by construction — a reviewer cannot run commands, mutate state, or escape its
|
||||
read surface even if a prompt tried to grant bash. This is a deliberate
|
||||
security boundary that upholds rule #7 (auditor independence): a reviewer must
|
||||
never alter the artifact under review or its environment.
|
||||
|
||||
**Reviewer guidance**: do everything with `read`, `glob`, `grep`, and the
|
||||
`gitea-rest` skill (curl against `/api/v1`; `gitea-mcp` is retired). If a
|
||||
review dimension seems to require running a command
|
||||
(executing a build, running a test, computing a metric), that is the
|
||||
Synthesizer / Verifier's job (Worker sub-agents, which DO have bash via the
|
||||
`worker` agent defaults) — not the Reviewer's. Report the need in findings
|
||||
instead of attempting bash.
|
||||
|
||||
> **[org-internal #2126] Task 2 conclusion — keep + document.** The `deny **` pushed per-tool
|
||||
> at `<harness-package>/src/session/prompt.ts` is the **session tool-enable /
|
||||
> disable map** (it disables `todowrite` / `task` / `experimental.primary_tools`
|
||||
> for subagents and allows forwarded MCP tools). It is NOT the source of the
|
||||
> Explorer bash deny — that comes from the `explorer` agent config above, which
|
||||
> is the correct place. Relaxing it would break auditor independence; worker-
|
||||
> class sub-agents already have bash wherever the pipeline needs to run
|
||||
> commands. Decision: **keep the deny, document it here** (this section).
|
||||
|
||||
## Placeholder
|
||||
|
||||
- `{REVISION_ROLE}` — "Developer" (code review), "Analyst" (review-dag single
|
||||
gate), or "Remediator" (process audit). The Worker role that revises
|
||||
artifacts between rounds. (The Architect/Planner revision roles belonged to
|
||||
the archived design-space / iteration-plan targets, [org-internal #3072] phase 3.)
|
||||
@@ -0,0 +1,493 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Review Pipeline Phases (Canonical)
|
||||
|
||||
This document is the **single source of truth** for the shared Phase A→E
|
||||
review/audit pipeline flow. Individual review and audit skills bind their own
|
||||
parameters and reference this doc rather than duplicating the shared mechanics.
|
||||
|
||||
**Terminology**: "reviewer" and "auditor" are interchangeable — `audit-process`
|
||||
legitimately uses "auditor"/"Re-audit" terminology. All generic descriptions
|
||||
use `{REVIEWER_OR_AUDITOR}`.
|
||||
|
||||
**Output model — two findings sinks** ([org-internal #1988] Tier 1 migration): raw per-dimension
|
||||
reviewer findings have ONE of two sinks, chosen per skill. The **synthesis**
|
||||
(human gate) and **commit status** are ALWAYS Tier 2 (Gitea) regardless of the
|
||||
findings sink.
|
||||
|
||||
| Sink | Skills | Reviewer writes findings to | Synthesizer reads findings from |
|
||||
|---|---|---|---|
|
||||
| `tier1-local` (migrated) | `review-code`, `review-artifact` (live targets: review-dag, audit-process; the roadmap/design-space/plan targets were archived 2026-08-21, [org-internal #3072] phase 3) | `<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json` (validated by `core/schemas/reviewer-output.schema.json`) | the local finding files (no Gitea call) |
|
||||
| `tier2-comment` (fallback) | _(none — all targets migrated)_ | Gitea PR/issue comment via `工单评论 API(见 TERMINOLOGY)` (Pattern 5) | `工单评论 API(见 TERMINOLOGY)` (Pattern 3) |
|
||||
|
||||
Migration complete — every review/audit skill now outputs `tier1-local`; the
|
||||
`tier2-comment` row remains as the documented fallback (boundary rule:
|
||||
`core/rules/two-tier-artifacts.md`; a skill without `<runs-root>/`
|
||||
write access falls back to comment-based output unchanged). A `tier1-local`
|
||||
reviewer additionally
|
||||
appends an `index.json` row per finding file (Tier 1 manifest, see
|
||||
`core/templates/runs-layout.md`).
|
||||
|
||||
**Tier 2 outputs (always)**: synthesis is posted as a **Gitea PR/issue comment**
|
||||
with a fenced ` ```json ` block — except for the `audit-process` target, which
|
||||
has no source issue and posts the synthesis as a **wiki page**
|
||||
`audit/{date}/round{N}/synthesis` instead (see `review-artifact/SKILL.md` process target
|
||||
Output Artifacts); status tracking uses **REST commit status**
|
||||
(`pipeline/{REVIEW_TYPE}` context). The full Gitea output pattern is documented
|
||||
in `_shared/gitea-write-patterns.md`.
|
||||
|
||||
---
|
||||
|
||||
## Phase A — Parallel {REVIEWER_OR_AUDITOR_NAME}
|
||||
|
||||
**Orchestrator actions**:
|
||||
|
||||
0. **Move the source issue to the `review` column** on the Pipeline Stages
|
||||
board per `_shared/gitea-write-patterns.md` Pattern 7.5. Skip if no
|
||||
source issue exists. The review stage is a single board column covering
|
||||
all review targets (DAG single gate / code review / audit-process).
|
||||
|
||||
0.5. **Initialize the review-status file** (round 1 only). The Orchestrator
|
||||
creates `<runs-root>/{slug}/reviews/{stage}/status.json` before dispatching
|
||||
round 1, with the full schema-required field set
|
||||
(`core/schemas/review-status.schema.json`): `slug`, `started_at`
|
||||
(RFC 3339 timestamp), `current_round: 1`, `max_rounds` (per-skill binding —
|
||||
`review-artifact` = 3, `review-code` = 2 default / 3 high-risk —
|
||||
`<instance-root>/workflow-routing.yaml` `review.max_rounds`), `converged: false`,
|
||||
`history: []`. This
|
||||
step is skipped on rounds ≥ 2 — the file already exists and the Synthesizer
|
||||
appends to it in Phase B. Initialization is the Orchestrator's sole
|
||||
responsibility; the Synthesizer never creates the file. The Orchestrator
|
||||
also appends an `index.json` row for the file (`type` = `review-status`,
|
||||
`stage` = `{REVIEW_TYPE}`, `produced_by` = `Orchestrator`, `schema` = the
|
||||
review-status `$id`).
|
||||
|
||||
1. **Tool-availability probe** (defense-in-depth, [org-internal #2115] RC1 / [org-internal #2126]). Before
|
||||
dispatching any {REVIEWER_OR_AUDITOR}, verify reviewers will be able to read
|
||||
wiki artifacts (requirements / design / plan / prior-review reports). A
|
||||
reviewer that cannot read its inputs aborts or hallucinates — catch this ONCE
|
||||
in the Orchestrator instead of letting {DIMENSION_COUNT} parallel subagents
|
||||
each rediscover the gap:
|
||||
|
||||
- **Primary path**: confirm `gitea_wiki__*` tools are registered (the
|
||||
Orchestrator checks its own tool catalog; these tools are forwarded to
|
||||
reviewer subagents when registered — [org-internal #2116] raised the tool cap 50→100 so
|
||||
they are present in normal runs). If available → continue to step 2.
|
||||
- **Fallback path** (when `gitea_wiki__*` is NOT registered, e.g. a
|
||||
cross-repo scenario [org-internal #2115]): verify the **local wiki clone** at
|
||||
`<wiki-clone-root>/{repo}-wiki/` is readable —
|
||||
`glob(pattern="*.md", path="<wiki-clone-root>/{repo}-wiki/")`. If the clone
|
||||
is readable → continue to step 2; reviewers use the local clone (see
|
||||
`_shared/gitea-read-patterns.md` Pattern 1 fallback note and the "Tool
|
||||
Availability & Path Resolution" section of this skill).
|
||||
- **Both unavailable → FAIL FAST**: post a `failure` commit status (state
|
||||
`"failure"`, context `pipeline/{REVIEW_TYPE}`, description naming the
|
||||
missing capability) and abort — do NOT enter parallel dispatch. Report the
|
||||
gap so the environment can be fixed (register the wiki MCP server, or
|
||||
populate `<wiki-clone-root>/{repo}-wiki/`).
|
||||
|
||||
This probe is belt-and-suspenders: RC1 (tool cap, [org-internal #2116]) already fixed the
|
||||
primary cause of missing `gitea_wiki__*` in subagents. It exists so a future
|
||||
environment regression fails gracefully with one clear message instead of N
|
||||
silent subagent aborts.
|
||||
|
||||
2. Post an initial pending commit status via REST:
|
||||
|
||||
```
|
||||
POST /api/v1/repos/Octopus/octopus/statuses/{SHA}
|
||||
state: "pending"
|
||||
context: "pipeline/{REVIEW_TYPE}"
|
||||
description: "Round {N}: {DIMENSION_COUNT} reviewers dispatched"
|
||||
```
|
||||
|
||||
(See `_shared/gitea-write-patterns.md` Pattern 8.)
|
||||
|
||||
3. Spawn **all {DIMENSION_COUNT} {REVIEWER_OR_AUDITOR}s in parallel**. Each
|
||||
receives:
|
||||
- The standardized prompt with dimension-specific values filled in.
|
||||
- Agent type: Explorer.
|
||||
- Permission: Explorer read-only (see
|
||||
`core/skills/_shared/review-orchestrator-rules.md` — Default
|
||||
Explorer Permissions).
|
||||
- Temperature: 0.1 (deterministic output).
|
||||
|
||||
4. Wait for all {REVIEWER_OR_AUDITOR}s to complete. Each reviewer writes its
|
||||
dimension result to the skill's configured findings sink:
|
||||
- `tier1-local` (e.g. `review-code`, `review-artifact`, `audit-process`): write the JSON to
|
||||
`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`
|
||||
(validated by `core/schemas/reviewer-output.schema.json`). The reviewer
|
||||
is granted scoped write to `<runs-root>/**` (read-only everywhere else —
|
||||
this emits the reviewer's OWN output, it does not touch the artifact under
|
||||
review, so it does not violate rule #7 auditor independence).
|
||||
- `tier2-comment` (fallback): post an issue comment via
|
||||
`工单评论 API(见 TERMINOLOGY)` (Pattern 5, `_shared/gitea-write-patterns.md`)
|
||||
with a fenced ` ```json ` block on the target PR (pipeline mode) or issue
|
||||
(standalone mode). The comment is labeled `**{DIMENSION_CODE} review — round {N}**`.
|
||||
After all reviewers complete, for `tier1-local` the Orchestrator appends one
|
||||
`index.json` row per finding file (type `review-findings`, stage
|
||||
`{REVIEW_TYPE}`, produced_by `Reviewer`, schema = the reviewer-output `$id`).
|
||||
|
||||
5. If a {REVIEWER_OR_AUDITOR} fails (crash / timeout), retry once. If still
|
||||
failed, mark that dimension as `UNRESOLVED` in the synthesis and continue.
|
||||
Record it for deferred retry at the start of the next round.
|
||||
|
||||
---
|
||||
|
||||
## Phase B — Synthesis (Delegated)
|
||||
|
||||
> **Context optimization**: Phase B is delegated to a Synthesizer (Worker) to
|
||||
> prevent the Orchestrator's context from accumulating N dimension finding
|
||||
> Read results (up to N × 50KB per round). The Synthesizer reads all dimension
|
||||
> findings in its own context, posts the synthesis comment + commit status,
|
||||
> and returns a compact summary (≤ 2KB). The Orchestrator never reads
|
||||
> dimension findings directly.
|
||||
|
||||
**Orchestrator actions**:
|
||||
|
||||
1. Fill the synthesis task template
|
||||
(`core/skills/_shared/synthesis_task.md`) with:
|
||||
- Round number. A round-1 dispatch carries the template's step-8
|
||||
"Round-1 only — findings digest" duty (the Synthesizer writes
|
||||
`round1/findings-summary.md`, consumed verbatim at Phase E — see the
|
||||
digest bullet in step 3 below); verify it is present in the filled
|
||||
template.
|
||||
- Target PR number (pipeline mode) or issue number (standalone mode).
|
||||
- List of UNRESOLVED dimensions (reviewers that crashed/timed out).
|
||||
- Commit SHA and review type.
|
||||
- For `tier1-local` skills (all review/audit): write the filled template to
|
||||
`<runs-root>/{slug}/reviews/{stage}/round{N}/task-synthesizer.md` (Tier 1
|
||||
synthesis prompt; append an `index.json` row, type `synthesis-prompt`). The
|
||||
`/tmp/octopus/` embryo is NOT used — Tier 1 owns the prompt.
|
||||
- For `tier2-comment` skills: write the filled template to
|
||||
`/tmp/octopus/synthesis_task_{slug}_round{N}.md` for `prompt_file` use; the
|
||||
authoritative copy is published as a Gitea wiki page at
|
||||
`{slug}/reviews/{stage}/round{N}/task-synthesizer`.
|
||||
|
||||
2. Dispatch the **Synthesizer** (Worker) via the `task` tool:
|
||||
- `role`: `Synthesizer`
|
||||
- `subagent_type`: `worker`
|
||||
- `prompt_file`: the synthesis-prompt path from step 1 (Tier 1 local path for
|
||||
`tier1-local` skills; `/tmp/octopus/...` for `tier2-comment` skills)
|
||||
- `permissions`: read + write + edit + bash + glob + grep (all allow)
|
||||
- `temperature`: 0.1
|
||||
- Foreground (the Orchestrator waits for completion).
|
||||
|
||||
3. The Synthesizer:
|
||||
- Reads all dimension findings from the skill's configured sink:
|
||||
- `tier1-local`: read the finding files at
|
||||
`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-*.json` (in its
|
||||
OWN context, not the Orchestrator's — no Gitea call).
|
||||
- `tier2-comment`: read all dimension comments via `工单评论 API(见 TERMINOLOGY)`
|
||||
(Pattern 3, `_shared/gitea-read-patterns.md`) on the target PR/issue.
|
||||
- **Post-check finding-id uniqueness (MANDATORY)**: verifies that `id`
|
||||
values are unique across ALL findings files of the round (including
|
||||
every `-r{k}` slot file) — the schema's `uniqueItems` does NOT enforce
|
||||
this. On a duplicate id, keeps both findings, notes the collision in
|
||||
the digest and marks the affected dimension WARN (per step 1b of
|
||||
`_shared/synthesis_task.md`).
|
||||
- Computes synthesis following `core/schemas/synthesis.schema.json`.
|
||||
- Posts synthesis as a PR/issue comment via `工单评论 API(见 TERMINOLOGY)`
|
||||
(Pattern 5, `_shared/gitea-write-patterns.md`) — markdown summary + fenced
|
||||
` ```json ` block matching `core/schemas/synthesis.schema.json`. (This
|
||||
is the Tier 2 human-gate output — it stays on Gitea for both sinks.) For
|
||||
the `audit-process` target (no source issue), post the synthesis as a wiki
|
||||
page `audit/{date}/round{N}/synthesis` instead of a PR/issue comment.
|
||||
- Posts a commit status via REST with context
|
||||
`pipeline/{REVIEW_TYPE}` (Pattern 8, `_shared/gitea-write-patterns.md`):
|
||||
`state: "success"` for PASS/WARN, `"failure"` for FAIL.
|
||||
- Appends one `history[]` entry for this round to the run's review-status
|
||||
file `<runs-root>/{slug}/reviews/{stage}/status.json` (validated by
|
||||
`core/schemas/review-status.schema.json`) — `round`,
|
||||
`overall_verdict`, `blockers`, `majors`, `minors` — the durable per-round
|
||||
audit history (defined by `core/schemas/review-status.schema.json`
|
||||
`history[]`; mirrors the audit-process checklist TRC 10.4), and updates
|
||||
the file's `current_round` to this round number. (The Orchestrator initializes `status.json` before
|
||||
round 1 — see Phase A step 0.5 — with the full required field set `slug` /
|
||||
`started_at` / `current_round` / `max_rounds` / `converged` / `history`.
|
||||
The Synthesizer only appends `history[]` and updates `current_round`; it
|
||||
never re-creates the file.)
|
||||
- **Round-1 findings digest (round 1 only)**: for skills whose final report
|
||||
carries a Round-1 Findings Summary (`review-code` Phase E step 2.55 and
|
||||
any target adopting the template section), the round-1 Synthesizer is the
|
||||
PRODUCER of that table: it writes
|
||||
`<runs-root>/{slug}/reviews/{stage}/round1/findings-summary.md` — the
|
||||
rendered `## Round-1 Findings Summary` table (columns
|
||||
`| Dim | Finding ID | Severity | Summary |`, one row per round-1 finding;
|
||||
Summary = one-line 摘由/root cause) — distilled from the round-1 findings
|
||||
it just read in its own context (append an `index.json` row, type
|
||||
`review-synthesis` — Synthesizer-produced synthesis artifacts; the
|
||||
round-verdict return summary `synthesis-return.md` keeps the generic
|
||||
worker-report type `other` per
|
||||
`_shared/worker-report-persistence.md`). This duty is wired into the
|
||||
Synthesizer's operative prompt as step 8 of
|
||||
`_shared/synthesis_task.md` ("Round 1 only — findings digest"). The
|
||||
Orchestrator at Phase E copies this file
|
||||
verbatim into the final report; it does NOT read the findings JSONs
|
||||
(step 4 below) — the digest file is the wired producer→consumer path.
|
||||
- Returns a compact summary (≤ 2KB) with verdict, per-dimension results,
|
||||
and re-audit targets.
|
||||
|
||||
4. The Orchestrator receives the summary and proceeds to Phase C or Phase E
|
||||
based on `overall_verdict`. The Orchestrator does NOT read dimension
|
||||
comments or the synthesis comment directly — all decision-relevant data
|
||||
is in the Synthesizer's return summary.
|
||||
|
||||
5. Present the Synthesizer's return summary to the user in chat.
|
||||
|
||||
---
|
||||
|
||||
## Phase C — {REVISION_ROLE} Revision
|
||||
|
||||
If `overall_verdict` is PASS, skip to Phase E.
|
||||
|
||||
Otherwise:
|
||||
|
||||
**Orchestrator actions**:
|
||||
|
||||
1. Prepare the {REVISION_ROLE}'s input as a structured task, published as a
|
||||
Gitea wiki page at `{slug}/reviews/{stage}/round{N}/task-{ROLE}` (Pattern 1,
|
||||
`_shared/gitea-write-patterns.md`). For the `audit-process` target, the
|
||||
process target's Phase Bindings override the wiki namespace to
|
||||
`audit/{date}/` — publish to `audit/{date}/round{N}/task-{ROLE}` instead
|
||||
(see `review-artifact/SKILL.md` process target).
|
||||
|
||||
This must contain:
|
||||
- Paths to all files that need revision.
|
||||
- Reference to the synthesis comment on the PR/issue (posted in Phase B).
|
||||
- References to the dimensions that have open BLOCKER, MAJOR, or MINOR
|
||||
findings, via the skill's findings sink:
|
||||
- `tier1-local`: paths to the Tier 1 finding files
|
||||
(`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`).
|
||||
- `tier2-comment`: references to the individual dimension issue comments.
|
||||
Dimensions whose only findings are INFO are excluded — INFO is
|
||||
dispositioned at synthesis and never revised (see the "Severity handling —
|
||||
INFO disposition" rule in Phase D).
|
||||
- The {REVISION_ROLE} MUST NOT receive the full text of all findings inline —
|
||||
use finding-file path / comment references only to avoid context explosion.
|
||||
- Convergence status: blockers/majors remaining vs. previous round.
|
||||
- **Revision survival ([org-internal #2601]) — primary path**: the task tool's completion
|
||||
output includes a `task_id`; passing it back resumes the SAME
|
||||
{REVISION_ROLE} subagent session instead of creating a fresh one. Round 1:
|
||||
persist the returned `task_id` with the round's working-notes entry (it
|
||||
must survive round-boundary compaction). Round ≥ 2: dispatch with
|
||||
`task_id` set to the previous revision round's — the reviser continues
|
||||
in-session with its prior context. {REVIEWER_OR_AUDITOR}s are NEVER
|
||||
resumed — per-round freshness is their independence.
|
||||
- **Revision survival — fallback** (task_id lost to compaction before it
|
||||
was persisted): for round ≥ 2, include the current
|
||||
`<runs-root>/{slug}/working-notes.md` content (run narrative memory:
|
||||
prior decisions, dead ends, earlier reviewer feedback) and a reference to
|
||||
the previous round's revision summary. State explicitly that this revises
|
||||
work a prior round already produced — re-derive from upstream artifacts
|
||||
ONLY when a finding requires it.
|
||||
|
||||
2. Spawn the {REVISION_ROLE} (Worker) with revision instructions from
|
||||
`core/skills/_shared/review-revision-prompt.md`. For round ≥ 2, pass
|
||||
the previous reviser `task_id` to resume the same session (see Revision
|
||||
survival above); reviewer/auditor dispatches are always fresh.
|
||||
|
||||
---
|
||||
|
||||
## Phase D — {REVIEWER_OR_AUDIT_LOOP} Loop
|
||||
|
||||
After the {REVISION_ROLE} completes revision:
|
||||
|
||||
1. Increment the round counter, and update `current_round` in
|
||||
`<runs-root>/{slug}/reviews/{stage}/status.json` to match. Post the round
|
||||
transition as a progress update to the commit status (Pattern 8):
|
||||
```
|
||||
state: "pending"
|
||||
context: "pipeline/{REVIEW_TYPE}"
|
||||
description: "Round {N+1}: re-reviewing after {REVISION_ROLE} revision"
|
||||
```
|
||||
2. Go back to Phase A (spawn a new round against the revised files).
|
||||
|
||||
**Convergence criteria** (stop the loop when ANY of these is true):
|
||||
|
||||
| Condition | Action |
|
||||
| -------------------------------------- | ---------------------------------------------- |
|
||||
| `overall_verdict` = PASS | Stop. Proceed to Phase E. |
|
||||
| `current_round` > `{MAX_ROUNDS}` | Stop. Proceed to Phase E with final status. |
|
||||
| No improvement in 2 consecutive rounds | Stop. (Same blocker count AND same major count |
|
||||
| (compared to 2 rounds ago) | without reduction). Proceed to Phase E. |
|
||||
| User explicitly approves current state | Stop. Proceed to Phase E. |
|
||||
|
||||
**`{MAX_ROUNDS}` binding** (per skill):
|
||||
- `review-artifact` skill (process target): **3**.
|
||||
- `review-artifact` skill (`review-dag` single-gate target): **per depth** from
|
||||
`dag_metrics.review_depth` — D1 ≤2, D2 ≤3, D3 ≤3, D4 ≤4 (D4 reaches round 4,
|
||||
exceeding the shared `review-artifact` default; the per-depth cap takes precedence —
|
||||
see `review-artifact/SKILL.md` review-dag Phase D and
|
||||
`core/schemas/review-status.schema.json`).
|
||||
- `review-code` skill: **2** default / **3** high-risk (a BLOCKER survived a
|
||||
round, or the diff touches a frozen cross-session contract) —
|
||||
`<instance-root>/workflow-routing.yaml` `review.max_rounds` ([org-internal #3567], process-diet
|
||||
P0b). At budget without convergence: STOP, register the remaining
|
||||
BLOCKER/MAJOR findings as TD registry rows, verdict FAIL if any BLOCKER
|
||||
remains else PASS — the debt is carried by those TD registry rows
|
||||
(see `review-code/SKILL.md` Phase D). This
|
||||
supersedes the 2026-08-25 [org-internal #3380] calibration (p50=2, p95=5, n=66): the runs
|
||||
past round 3 are the marathon tail this budget exists to cut; their residue
|
||||
flows to the TD registry instead of reviewer rounds. Ledger:
|
||||
`docs/workflow-refactor/thresholds-ledger.md`.
|
||||
|
||||
Rationale for `review-artifact` = 3 (evaluated in [org-internal #1969], replacing the prior blanket
|
||||
default of 5): (1) the Round-3 Escalation Guardrail below already detects
|
||||
non-convergence at the start of round 3; (2) the INFO-disposition rule below
|
||||
removes spurious WARN rounds previously caused by non-blocking INFO findings;
|
||||
(3) the "no improvement in 2 consecutive rounds" stop condition naturally
|
||||
fires by round 3; (4) the marginal value of rounds 4-5 is low once the
|
||||
guardrail has fired and they burn reviewer budget. (`review-code` no longer
|
||||
reaches round 4 — its round budget is 2/3, see the binding above.)
|
||||
|
||||
**Provisional, not empirically validated**: the four safeguards make `review-artifact`=3
|
||||
defensible, but it has no fresh run-data yet. Treat it as a hypothesis to
|
||||
confirm via the retrospective signal ([org-internal #1834]; gate-trim's landing machinery was
|
||||
retired, [org-internal #3072] phase 3 — the check is observational, not a trim proposal): if
|
||||
live `review-artifact` runs frequently hit round 3 still improving yet are
|
||||
forced to stop at WARN/FAIL, bump it back to 4.
|
||||
|
||||
**Severity handling — INFO disposition** (non-blocking, non-revising): INFO
|
||||
findings are observations/recommendations beyond the checklist. They NEVER
|
||||
block convergence and NEVER drive a reviser round.
|
||||
- A dimension whose ONLY open findings are INFO has effective verdict **PASS**
|
||||
— INFO does not reduce `pass_rate` and does not cause WARN/FAIL (see
|
||||
orchestrator rule #3). Such a dimension does NOT count against
|
||||
`overall_verdict`.
|
||||
- At synthesis, the Synthesizer dispositions EVERY INFO finding as one of:
|
||||
- `TD-candidate` — actionable observation worth a tech-debt item; record a
|
||||
suggested REGISTRY ROW (one-line summary + reactivation trigger,
|
||||
`core/rules/ticket-lifecycle.md`). Actual `TD-NNN` registration is
|
||||
deferred to the `verify` skill's Phase 5.5 registry filing — a
|
||||
TD-candidate does NOT open an independent ticket at review time
|
||||
(consistent with how `review-code` Accepted Risks flow to verify).
|
||||
- `DISCARDED` — not worth tracking; record a one-line reason.
|
||||
Dispositions are listed in the synthesis comment (markdown). INFO counts
|
||||
carry in `info_count` / `infos` for reporting only.
|
||||
- Phase C reviser task (step 1 above) references ONLY dimensions with open
|
||||
BLOCKER/MAJOR/MINOR findings. INFO findings are NEVER passed to the
|
||||
{REVISION_ROLE}.
|
||||
|
||||
**Round-3 Escalation Guardrail** (applies to the `review-artifact` skill's
|
||||
live targets — the process audit target AND the review-dag single-gate
|
||||
target; the roadmap / design / plan targets were archived 2026-08-21,
|
||||
[org-internal #3072] phase 3). `review-code` does NOT use this guardrail; it uses its round
|
||||
budget ([org-internal #3567], see the `{MAX_ROUNDS}` binding above).
|
||||
|
||||
At the start of round 3, if the combined blocker+major count is still >50% of
|
||||
round 1's count, STOP the loop and surface to the user:
|
||||
|
||||
> Review not converging: round 1 had {N1} blockers+majors, round 2 still has
|
||||
> {N2} ({pct}% of round 1). This suggests the artifact under review needs a
|
||||
> substantive rewrite, not incremental patching. Options:
|
||||
> (a) Return to upstream — for the `process` audit target, escalate to the
|
||||
> octopus maintainer for a process-level redesign (the legacy producer skills
|
||||
> `roadmap` / `requirements-elicitation` / `design` / `plan-iterations` were
|
||||
> archived 2026-08-21, [org-internal #3072] phase 3 — `<instance-root>/archive/`), then restart this
|
||||
> review at round 1.
|
||||
> (b) Continue iterating — accept that more rounds may be needed (capped by
|
||||
> `{MAX_ROUNDS}`).
|
||||
|
||||
Choosing (a) returns control to the upstream skill (or maintainer); the
|
||||
review's commit status is marked with description `"escalated"` and
|
||||
`converged: false`.
|
||||
|
||||
**Round-4 user-choice escalation point (shared)**: a multi-round review skill
|
||||
MUST offer the user an explicit choice before entering round 4 — (a) stop the
|
||||
review and escalate to a human, or (b) continue into round 4 — via the
|
||||
`question` tool. The guard means "escalate with a choice", NOT "force-stop".
|
||||
The choice is UNCONDITIONAL at the round-3→4 transition — instance skills may
|
||||
narrow when an escalation *variant* (e.g. a design-level decision) fires, but
|
||||
never narrow the choice gate itself. The call MUST be optionized ([org-internal #3378]):
|
||||
two options — `Stop & escalate to a human` (first, when recommended) /
|
||||
`Continue into round 4` — with `stage` set to the running skill's stage so
|
||||
auto-approval applies in autonomous mode; free-text asking is a spec
|
||||
violation.
|
||||
Instance: review-dag D4 option-b (`review-artifact/SKILL.md` §"Round-3
|
||||
Escalation Guardrail interaction") — the sole live instance, its D4 round cap
|
||||
(≤4) being the only budget that reaches round 4 (`review-code` is capped 2/3,
|
||||
[org-internal #3567]).
|
||||
Rationale (retro 2026-08-21 / [org-internal #3050]): unattended late-round loops drift into
|
||||
review marathons — the choice forces the design-level stall to surface to a
|
||||
human at round 4 instead of round 6.
|
||||
|
||||
**Efficiency optimization**: From round 2 onward ([org-internal #2598]), only spawn
|
||||
{REVIEWER_OR_AUDITOR}s for dimensions that had FAIL, WARN, or UNRESOLVED in
|
||||
the previous round. Every 2 rounds, run a full {DIMENSION_COUNT}-reviewer
|
||||
sweep to catch regressions. From round 2 onward reviewer input is also
|
||||
delta-scoped: the file list handed to each reviewer covers only the files
|
||||
changed since the previous round plus their convention-comparison neighbors,
|
||||
not the round-1 full set.
|
||||
|
||||
---
|
||||
|
||||
## Phase E — Final Report
|
||||
|
||||
**Orchestrator actions**:
|
||||
|
||||
1. Write the final report as a Gitea wiki page at
|
||||
`{slug}/reviews/{stage}/final/report` (Pattern 1,
|
||||
`_shared/gitea-write-patterns.md`), using the template at
|
||||
`core/skills/_shared/review-final-report-template.md`. For the
|
||||
`audit-process` target, write to `audit/{date}/final/report` instead (wiki
|
||||
namespace override `audit/{date}/`). Also set the lifecycle `converged` flag
|
||||
in `<runs-root>/{slug}/reviews/{stage}/status.json`: `true` when the final
|
||||
verdict is PASS (or WARN with no unresolved BLOCKERs), else `false`. This is
|
||||
the ONLY place `converged` is written — the Orchestrator at Phase E; the
|
||||
Synthesizer never sets it.
|
||||
|
||||
2. Update the `## 工件索引` comment on the source issue per
|
||||
`_shared/gitea-write-patterns.md` Pattern 10: add/update the
|
||||
`REVIEW-{stage}` row (位置 = the final report page, 重读 = CORE —
|
||||
review conclusions are part of the compaction re-read set).
|
||||
Skip if no source issue exists (Pattern 10 step 1).
|
||||
|
||||
2.5. **Inline-ization (skills that bind it)** — when the review target is a
|
||||
PR and the skill's Phase E binds the inline-comment step (currently
|
||||
`review-code` only, as its Phase E step 2.56), execute it now: post the
|
||||
final round's findings as PR inline comments via
|
||||
`<harness-package>/script/post-inline-comments.ts` (consumer of frozen
|
||||
contract `shared/review-inline-v1`): noise findings (INFO / DISCARDED /
|
||||
`WAIVED-{id}`) are never inlined (contract behavior 2), multi-round
|
||||
posts delete the identity's previous inline comments first (behavior 5),
|
||||
stale lines degrade to the file's first diff hunk (behavior 4; caveat:
|
||||
this server build returns HTTP 500 rather than the contract's 422 for
|
||||
stale lines — TD-611 — so stale items currently surface under `failed`
|
||||
with `fallback` empty), and partial failures are accounted — never
|
||||
blocking the verdict (behavior 6) — in the synthesis comment and final
|
||||
report's `inline: X posted / Y file-level / Z fallback / W failed` line.
|
||||
Skills whose target has no PR (e.g. `audit-process`) skip this step. The
|
||||
skill-specific binding (runner invocation, findings paths, identity,
|
||||
cleanup) is normative in the binding skill's own Phase E section.
|
||||
|
||||
3. Post final commit status via REST:
|
||||
|
||||
```
|
||||
POST /api/v1/repos/Octopus/octopus/statuses/{SHA}
|
||||
state: "success"|"failure"
|
||||
context: "pipeline/{REVIEW_TYPE}"
|
||||
description: "Final: {PASS|WARN|FAIL} after {N} rounds — converged: {true|false}"
|
||||
target_url: "{wiki page link}"
|
||||
```
|
||||
|
||||
3.5. **Gitea approval timing (mandatory when the PR needs a human approval
|
||||
gate)**: if the flow requests approval from a human reviewer on Gitea,
|
||||
register the reviewer FIRST via `gitea_pull__add_reviewer`, wait for the
|
||||
`REQUEST_REVIEW` registration to be recorded, and only THEN ask the human
|
||||
to approve. Approving before the reviewer is registered produces
|
||||
`official: false` on the approval — the gate treats the PR as unapproved
|
||||
and the approval must be re-done. ([org-internal #1931]: audit R2→R3 re-loop + re-approve,
|
||||
~30min wasted on exactly this ordering.)
|
||||
|
||||
4. Present the final report to the user and obtain approval via the `question`
|
||||
tool, passing `stage: "{STAGE}"`.
|
||||
|
||||
The `question` tool auto-approves per `core/rules/auto-approve.md` (stage `"{STAGE}"`).
|
||||
|
||||
Do NOT advance without approval (auto or explicit).
|
||||
|
||||
---
|
||||
|
||||
## Orchestrator Rules (Mandatory)
|
||||
|
||||
See `core/skills/_shared/review-orchestrator-rules.md` for the mandatory
|
||||
Orchestrator behavior rules. Substitute `{REVISION_ROLE}`: {REVISION_ROLE}.
|
||||
@@ -0,0 +1,135 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Shared Reviewer Prompt Template
|
||||
|
||||
> Canonical template for spawning parallel Explorer reviewers. Read on-demand
|
||||
> by the Orchestrator when dispatching reviewers. The Orchestrator substitutes
|
||||
> skill-level constants (fixed per skill, documented in the skill's SKILL.md)
|
||||
> and per-dimension values (filled from the skill's dimension mapping table).
|
||||
|
||||
The prompt text below is sent to each reviewer. Substitute all `{PLACEHOLDER}`
|
||||
values before dispatch.
|
||||
|
||||
```
|
||||
You are a {REVIEWER_ROLE}. Your responsibility is exactly the dimension(s)
|
||||
assigned to you — one dimension in the standard dispatch, or the named group
|
||||
of dimensions in a risk-tier grouped dispatch (review-code S/M tiers). Do NOT
|
||||
expand or narrow your scope beyond the assignment. Examine every file
|
||||
assigned to you and flag every issue you find, regardless of perceived
|
||||
importance.
|
||||
|
||||
{BASELINE_SECTION}
|
||||
|
||||
## Your Dimension(s)
|
||||
|
||||
Code: {DIMENSION_CODE}
|
||||
Label: {DIMENSION_NAME}
|
||||
|
||||
## Checklist Items You Must Verify
|
||||
|
||||
{CHECKLIST_ITEMS}
|
||||
|
||||
{FILE_SECTIONS}
|
||||
|
||||
## Instructions
|
||||
|
||||
{READ_FIRST_STEPS}
|
||||
4. For each checklist item, determine whether the {ARTIFACT_NOUN} satisfies it.
|
||||
Every checklist item must receive a PASS or FAIL verdict.
|
||||
Items that are genuinely not applicable must be marked NA with a reason.
|
||||
5. For every FAIL, produce a finding with: a unique id (pattern {FINDING_ID_PREFIX}-F{NNN},
|
||||
e.g. {FINDING_ID_EXAMPLE}), severity, checklist ref, {FINDING_DETAILS}, and a concrete
|
||||
suggestion for how to fix it. {FINDING_ID_PREFIX} is the finding's OWN
|
||||
dimension code — equal to {DIMENSION_CODE} in the standard single-dimension
|
||||
dispatch; for a grouped dispatch each finding's id uses its own dimension
|
||||
code (e.g. COR-F001, TST-F001), NOT the joined group code
|
||||
(never `COR+TST+DBT-F001`). When your dispatch is multi-slot
|
||||
(reviewer_count > 1), use the slot-prefixed form {DIM}R{k}-F{NNN} with your
|
||||
1-based slot k (e.g. TOPOR1-F001) so parallel slot files never collide on id.
|
||||
{CONTRADICTION_RULE}
|
||||
7. Assign severity strictly:
|
||||
{SEVERITY_DEFINITIONS}
|
||||
8. Count every PASS and FAIL. Compute pass_rate = passed / total_checks.
|
||||
Verdict rules:
|
||||
- PASS: pass_rate = 1.0 (no failed checks)
|
||||
- WARN: pass_rate >= 0.7 and no BLOCKER findings
|
||||
- FAIL: any BLOCKER finding OR pass_rate < 0.7
|
||||
9. Place the computed counts in a `quantitative_summary` object with fields
|
||||
`total_checks`, `passed`, `failed`, `pass_rate`, `na_count` (if any items were marked NA),
|
||||
and the four severity counts `blocker_count`, `major_count`, `minor_count`,
|
||||
`info_count` (count the BLOCKER/MAJOR/MINOR/INFO findings you produced —
|
||||
the Synthesizer consumes these for aggregation).
|
||||
10. Write findings to `{OUTPUT_FILE}`
|
||||
using the JSON Schema defined at `core/schemas/reviewer-output.schema.json`.
|
||||
Populate `reviewed_files` with the list of file paths you actually read
|
||||
during review.
|
||||
|
||||
## What You Must NOT Do
|
||||
|
||||
- Do NOT fabricate results. If a tool you need (e.g. `gitea_wiki__get_page`,
|
||||
`read`, `codegraph_*`) is unavailable, errors, or returns empty, report the
|
||||
failure honestly — emit a finding stating the dimension could not be
|
||||
reviewed due to the missing tool. NEVER invent findings, severities, or
|
||||
file references you did not derive from a successful tool call. Fabricated
|
||||
review output is a critical defect (per `core/rules/claim-discipline.md`
|
||||
§ "Tool-failure honesty").
|
||||
- Do NOT narrow your scope because "this seems minor".
|
||||
- Do NOT skip checklist items because "{LOOKS_FINE_PHRASE}".
|
||||
- Do NOT produce a PASS verdict to save effort. Base verdicts on data.
|
||||
- Do NOT add findings for dimensions you are not assigned to.
|
||||
- Do NOT read files outside your assigned list.
|
||||
- Do NOT edit any file EXCEPT your own findings file at `{OUTPUT_FILE}`. You are read-only for all other files.
|
||||
```
|
||||
|
||||
## Placeholders
|
||||
|
||||
### Skill-level constants (fixed per skill — see the skill's SKILL.md)
|
||||
|
||||
- `{REVIEWER_ROLE}` — reviewer identity (e.g. "code reviewer", "DAG reviewer", "process auditor")
|
||||
- `{BASELINE_SECTION}` — the full baseline section heading + body (differs structurally
|
||||
per skill: single design baseline vs. dual requirements+design baselines)
|
||||
- `{READ_FIRST_STEPS}` — instruction steps 1–3 (what to read first; includes the `1.` `2.`
|
||||
`3.` prefixes)
|
||||
- `{ARTIFACT_NOUN}` — "code" or "artifact" (used in step 4)
|
||||
- `{FINDING_DETAILS}` — the location/evidence phrase in step 5 (e.g. "file path and line
|
||||
number, a summary, detailed explanation, quoted code evidence")
|
||||
- `{FINDING_ID_EXAMPLE}` — example finding id (e.g. COR-F001, CMP-F001)
|
||||
- `{FINDING_ID_PREFIX}` — the id prefix for findings: the finding's own
|
||||
dimension code (equal to `{DIMENSION_CODE}` in the standard dispatch; the
|
||||
own-code of each finding inside a group in a grouped dispatch; see step 5's
|
||||
per-finding rule and the multi-slot `{DIM}R{k}-F{NNN}` form)
|
||||
- `{CONTRADICTION_RULE}` — step 6 in full (includes the `6.` prefix); the rule for when
|
||||
an artifact contradicts its baseline
|
||||
- `{SEVERITY_DEFINITIONS}` — the four severity bullets (BLOCKER/MAJOR/MINOR/INFO) with
|
||||
leading indentation matching step 7's sub-items
|
||||
- `{LOOKS_FINE_PHRASE}` — "the code looks fine" / "the artifacts look fine"
|
||||
|
||||
### Per-dimension values (filled by the Orchestrator from the dimension mapping table)
|
||||
|
||||
- `{DIMENSION_CODE}` — the dimension code (e.g. COR, ARCH). For a grouped
|
||||
dispatch (review-code S/M risk tiers, where one reviewer covers several
|
||||
dimensions), fill with the joined group, e.g. `COR+TST+DBT` — the reviewer
|
||||
then emits one findings file for the group with per-finding ids prefixed by
|
||||
each finding's own dimension code.
|
||||
- `{DIMENSION_NAME}` — the dimension label (e.g. Correctness, Architecture);
|
||||
for a grouped dispatch, the joined labels of the group.
|
||||
- `{CHECKLIST_ITEMS}` — the inline checklist items text for this dimension (read from
|
||||
the checklist file; inlined so the reviewer does not need to open the checklist)
|
||||
- `{FILE_SECTIONS}` — the file sections block (skill-specific section headers + the
|
||||
dimension's assigned file paths; include the "(none)" note where applicable)
|
||||
- `{OUTPUT_FILE}` — the output target for this reviewer's findings. The skill
|
||||
picks the findings sink (see `_shared/review-pipeline-phases.md` "Output model
|
||||
— two findings sinks"):
|
||||
- Tier 1 local (migrated skills, e.g. `review-code`): the absolute path inside
|
||||
the worktree, e.g.
|
||||
`<worktrees-root>/<repo>-<slug>/<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`
|
||||
— a JSON file validated by `core/schemas/reviewer-output.schema.json`.
|
||||
The reviewer is granted scoped write to `<runs-root>/**` for this.
|
||||
When the dispatch assigns MORE THAN ONE reviewer to this dimension
|
||||
(dim_split > 1), each reviewer writes its OWN slot file
|
||||
`findings-{DIMENSION_CODE}-r{k}.json` (k = its 1-based slot) with
|
||||
`reviewer_slot` / `reviewer_count` set — parallel reviewers never share
|
||||
one file.
|
||||
- Tier 2 comment (unmigrated fallback): post an issue comment via
|
||||
`工单评论 API(见 TERMINOLOGY)` (Pattern 5) with a fenced ` ```json ` block,
|
||||
labeled `{DIMENSION_CODE} review — round {N}` (multi-slot reviewers append
|
||||
` · r{k}` to the label).
|
||||
@@ -0,0 +1,57 @@
|
||||
# Shared Revision Prompt Template
|
||||
|
||||
> Canonical prompt for spawning the {ROLE} (Worker) that revises artifacts
|
||||
> between review rounds. Read on-demand by the Orchestrator in Phase C. The
|
||||
> Orchestrator substitutes skill-level constants before dispatch.
|
||||
|
||||
```
|
||||
Read `{TASK_FILE}` for your input files. {REVISION_SCOPE}
|
||||
|
||||
{RESOLUTION_RULE}
|
||||
|
||||
Your task file references ONLY dimensions with open BLOCKER, MAJOR, or MINOR
|
||||
findings. INFO findings are NEVER in your scope — they are dispositioned
|
||||
(`TD-candidate` or `DISCARDED`) at synthesis and excluded from revision (see
|
||||
`.octopus/skills/_shared/review-pipeline-phases.md` Phase D, "Severity
|
||||
handling — INFO disposition"). Do NOT act on or chase INFO observations.
|
||||
|
||||
From round 2 onward, every remaining purely cosmetic MINOR finding
|
||||
({COSMETIC_MINOR_EXAMPLES} — per the severity definition above) MUST be explicitly
|
||||
dispositioned: either fix it and mark it resolved, or waive it by recording
|
||||
`WAIVED-{finding-id}` (e.g. `{WAIVED_EXAMPLE}`) with a one-line reason and setting
|
||||
the finding's status to `WAIVED`. Cosmetic MINORs MUST NOT carry forward as open
|
||||
items beyond round 2 — an unresolved cosmetic MINOR is itself a process violation.
|
||||
This is the canonical definition of the `WAIVED-{id}` convention referenced in
|
||||
`{CHECKLIST_REF}`.
|
||||
|
||||
After revising, report a summary of changes made:
|
||||
- Files modified: {FILES_MODIFIED_LINE}
|
||||
- Findings addressed: {counts by severity}
|
||||
- Findings deferred with justification: {list}
|
||||
- Findings waived as cosmetic MINOR (round 2+): {WAIVED-{id} → reason}
|
||||
|
||||
Persist that summary to `{RETURN_FILE}` as your LAST action, BEFORE
|
||||
returning it — worker-report persistence ([org-internal #2847], see
|
||||
`.octopus/skills/_shared/worker-report-persistence.md`). The persisted copy
|
||||
is the report of record; if your completion notification is lost, the
|
||||
Orchestrator recovers the revision outcome from this file.
|
||||
```
|
||||
|
||||
## Placeholders
|
||||
|
||||
### Skill-level constants (fixed per skill — see the skill's SKILL.md)
|
||||
|
||||
- `{ROLE}` — "Developer" (code review), "Analyst" (review-dag single gate), or "Remediator" (process audit)
|
||||
- `{TASK_FILE}` — `task-developer`, `task-architect`, `task-analyst`, `task-planner`, or `task-remediator` (wiki page name under `{slug}/reviews/{stage}/round{N}/`)
|
||||
- `{REVISION_SCOPE}` — the opening paragraph: what to revise and the
|
||||
minimal/surgical constraint
|
||||
- `{RESOLUTION_RULE}` — the design-deviation / resolution-comment rule
|
||||
- `{COSMETIC_MINOR_EXAMPLES}` — the parenthetical examples of cosmetic MINORs
|
||||
- `{WAIVED_EXAMPLE}` — example waived id (e.g. `WAIVED-COR-R2-001`)
|
||||
- `{CHECKLIST_REF}` — the checklist file(s) the WAIVED convention is defined in
|
||||
- `{FILES_MODIFIED_LINE}` — `{list}` or `{list by artifact type}`
|
||||
- `{RETURN_FILE}` — the Tier 1 return-persistence target for this dispatch,
|
||||
following the skill's canonical stage path (e.g. review-code:
|
||||
`.octopus/runs/{slug}/reviews/code/round{N}/revision-return.md`); default
|
||||
per convention: `.octopus/runs/{slug}/workers/{chunk-id}-worker-{seq}.md`,
|
||||
lightweight fallback `/tmp/octopus/{chunk-id}-worker-{seq}.md`
|
||||
@@ -0,0 +1,86 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
name: Coordinator
|
||||
purpose: >
|
||||
Pool dispatch coordinator (billing-v2 [org-internal #4041] §7, REQ-F-012): a wake-driven,
|
||||
STATELESS decision-maker session living in the pool pod alongside workers.
|
||||
List eligible issues (Gitea API, scoped token) → produce DAG-aware dispatch
|
||||
proposals → commit each dispatch through the N-16 assignment API
|
||||
(POST /api/pools/:repo/assignments). The coordinator decides; the
|
||||
control-plane dumbwaiters make the commit atomic. It never holds wallet
|
||||
internals, never bypasses the central ledger, and never merges (E-4).
|
||||
|
||||
# Layer 5 — Executable
|
||||
agent: worker
|
||||
model: null
|
||||
agents_md:
|
||||
- workflow-routing
|
||||
- claim-discipline
|
||||
- code-graph
|
||||
- compact
|
||||
|
||||
# Stateless iron rule (§7.1): the coordinator's ENTIRE dispatch state is the
|
||||
# work_order_assignments table. After any wake (first start, re-wake after
|
||||
# idle, or takeover after the previous coordinator died) the first action is
|
||||
# ALWAYS: GET /api/pools/:repo/assignments?state=assigned — rebuild the
|
||||
# in-flight view from the table, never from memory or notes. Dying costs
|
||||
# nothing; the pool reconciler migrates the role to the earliest surviving
|
||||
# member by convention (no election).
|
||||
#
|
||||
# Decision procedure (AC-1) — full text: docs/runbooks/pool-coordinator.md.
|
||||
# 1. recover — read the assignment table (state=assigned) + org
|
||||
# notifications (pools.assignment.expired = re-dispatch inbox)
|
||||
# 2. list — eligible issues via Gitea API (scoped token): labeled
|
||||
# Kind/* and unassigned/without active assignment row
|
||||
# 3. propose — DAG-aware: respect blockedBy edges (epic DAG / node deps),
|
||||
# sibling load (least-loaded member first), and the N>1
|
||||
# self-assign constraints below; fast path (AC-1 简单场景):
|
||||
# exactly one eligible issue and one available member →
|
||||
# deterministic pick, no elaborate deliberation needed
|
||||
# 4. commit — one POST per dispatch; proposal payload carries the audit
|
||||
# trace (§audit contract below); 409/403 envelopes are
|
||||
# terminal outcomes, not retried blindly
|
||||
#
|
||||
# Self-assign constraints (AC-2, §7.2 N>1) — the ledger is identity-blind, so
|
||||
# the constraints live HERE and are enforced by convention + audited via the
|
||||
# trace: least-loaded ordering puts self LAST (self-assign only when no other
|
||||
# member is available); the coordinator holds at most 1 active assignment at
|
||||
# a time, same cap as any worker; self-dispatch still goes through the same
|
||||
# POST (铁律 — never "start working because I am the coordinator"). N=1:
|
||||
# self-dispatch is the only form and is NOT a special branch.
|
||||
#
|
||||
# Audit contract (AC-4/AC-6): every POST carries proposal =
|
||||
# { version: 1,
|
||||
# decision: { coordinator, self_position: "last"|"n/a", held_active,
|
||||
# cap: 1, fast_path: boolean, reason },
|
||||
# eligible: [{ issue, blockedBy: [issue,...] }...],
|
||||
# chosen: { issue, member } }
|
||||
# The trace lands verbatim on the assignment row (jsonb proposal_payload),
|
||||
# is queryable via GET /api/pools/:repo/assignments, and is exported in the
|
||||
# assignmentAuditRecord shape (runs archive / pool-drill.sh). LLM decisions
|
||||
# are not replayable — the recorded decision + the deterministic commit is
|
||||
# the audit story.
|
||||
#
|
||||
# Security posture (AC-5, see docs/security/threat-model.md §自治批处理):
|
||||
# the coordinator authenticates with a workspace-level API token
|
||||
# (TD-687/[org-internal #3898] owner-minted, jti-revocable, role claim org_role). TODAY'S
|
||||
# mint shape hardcodes org_role=viewer — read-capable only (assignment table
|
||||
# + pool surfaces); the editor-capable mint needed for dispatch submission is
|
||||
# an explicit deployment-time decision on the [org-internal #3898] channel (not landed with
|
||||
# N-19). Either way the token can NOT mint tokens, touch wallet internals, or
|
||||
# merge PRs — the merge gate stays human (E-4).
|
||||
#
|
||||
# Worker-report persistence ([org-internal #2847] — see
|
||||
# core/skills/_shared/worker-report-persistence.md): a Coordinator
|
||||
# sub-agent persists its FINAL RETURN REPORT to disk as its LAST action,
|
||||
# before returning — <runs-root>/{slug}/workers/{chunk-id}-worker-{seq}.md
|
||||
# (Tier 1 preferred; else /tmp/octopus/{chunk-id}-worker-{seq}.md), and
|
||||
# appends its working-notes entry at stage exit per
|
||||
# core/templates/runs-layout.md. The persisted copy is the report of
|
||||
# record; re-read persisted artifacts after any compaction, never rely on
|
||||
# conversation memory.
|
||||
compact:
|
||||
preserve:
|
||||
- "docs/runbooks/pool-coordinator.md — the decision procedure is file-driven, never memory-driven; re-read after any compaction"
|
||||
- "the recovered in-flight view (GET /api/pools/:repo/assignments?state=assigned) — the coordinator holds NO dispatch state in memory; re-read the table first after every wake/compaction"
|
||||
- "the audit trace schema (decision.self_position / decision.held_active / decision.cap / eligible[].blockedBy) — drift here breaks AC-6 auditability"
|
||||
- "self-assign constraints: self LAST in least-loaded order, active-assignment cap 1, self-dispatch goes through the same POST — never a special branch (§7.2 铁律)"
|
||||
@@ -0,0 +1,41 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
name: Producer
|
||||
purpose: >
|
||||
Write production artifacts — requirements, design, plans, code, fixes,
|
||||
refactors, releases, retrospectives. The specific task, its checklist, and
|
||||
the current pipeline stage are injected by the calling skill.
|
||||
|
||||
# Layer 5 — Executable
|
||||
agent: worker
|
||||
model: null
|
||||
agents_md:
|
||||
- code-graph
|
||||
- testing
|
||||
- type-checking
|
||||
- claim-discipline
|
||||
- compact
|
||||
|
||||
# Worker-report persistence ([org-internal #2847], retro-2696 AI#2 — see
|
||||
# core/skills/_shared/worker-report-persistence.md): every Producer
|
||||
# sub-agent persists its FINAL RETURN REPORT to disk as its LAST action,
|
||||
# before returning — <runs-root>/{slug}/workers/{chunk-id}-worker-{seq}.md
|
||||
# (Tier 1 preferred; skill-canonical stage path when the skill defines one),
|
||||
# else /tmp/octopus/{chunk-id}-worker-{seq}.md. The persisted copy is the
|
||||
# report of record; the task notification is a convenience copy.
|
||||
#
|
||||
# Working-notes contract ([org-internal #2600] — see core/templates/runs-layout.md §
|
||||
# working-notes.md): at stage exit the Producer appends ONE entry (≤30 lines:
|
||||
# decisions and why, dead ends, reviewer feedback that changed the work, hints
|
||||
# for the next stage) to <runs-root>/{slug}/working-notes.md, and registers
|
||||
# its index.json row (type working-notes) once if absent.
|
||||
#
|
||||
# Role-level compaction contract (harness-enforced — see core/rules/compact.md
|
||||
# § "Role-level re-read set"). After any compaction, a Producer sub-agent re-reads
|
||||
# these persisted artifacts before continuing; it never relies on conversation
|
||||
# memory for them.
|
||||
compact:
|
||||
preserve:
|
||||
- "<runs-root>/{slug}/working-notes.md (re-read FIRST, before formal artifacts — carries prior stages' tacit context: decisions, dead ends, reviewer feedback)"
|
||||
- "the work item / node spec being implemented (issue body or the frozen DAG copy {epic-slug}/dag + {epic-slug}/dag-nodes/{node-id} subpages)"
|
||||
- "the node's design baseline: node spec + cross-session edge contracts in {epic-slug}/dag (design detail is folded into node AC + contracts)"
|
||||
- "the acceptance criteria for the current node (node acceptance_criteria in {epic-slug}/dag, or the task ticket body)"
|
||||
@@ -0,0 +1,30 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
name: Reviewer
|
||||
purpose: >
|
||||
Review one dimension of an artifact against its checklist, read-only.
|
||||
Find issues that would block merge. One reviewer per dimension, all
|
||||
running in parallel.
|
||||
|
||||
# Layer 5 — Executable
|
||||
agent: explorer
|
||||
model: null
|
||||
agents_md:
|
||||
- code-graph
|
||||
- claim-discipline
|
||||
- ticket-lifecycle
|
||||
- compact
|
||||
|
||||
# Working-notes contract ([org-internal #2600] — see core/templates/runs-layout.md §
|
||||
# working-notes.md): the Reviewer side appends ONE distilled entry via the
|
||||
# Synthesizer at review convergence — never per dimension dispatch (9
|
||||
# dimension dispatches = 1 entry) — to <runs-root>/{slug}/working-notes.md.
|
||||
#
|
||||
# Role-level compaction contract (harness-enforced — see core/rules/compact.md
|
||||
# §"Role-level re-read set"). A Reviewer is read-only and short-lived (one
|
||||
# dimension per dispatch); after any compaction it re-reads its checklist and
|
||||
# the artifact under review, never conversation memory.
|
||||
compact:
|
||||
preserve:
|
||||
- "<runs-root>/{slug}/working-notes.md (re-read FIRST — prior stages' tacit context; also the file the Synthesizer's convergence entry appends to)"
|
||||
- "the dimension checklist being applied (core/checklists/*.md)"
|
||||
- "the artifact under review ({slug}/ wiki page or PR diff)"
|
||||
@@ -0,0 +1,26 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
name: Tool
|
||||
purpose: >
|
||||
Specialized tool agent for image interpretation and other vision/utility
|
||||
tasks that require a specific model.
|
||||
|
||||
# Layer 5 — Executable
|
||||
agent: explorer
|
||||
model: opencode/claude-haiku-4-5
|
||||
agents_md:
|
||||
- code-graph
|
||||
- claim-discipline
|
||||
- compact
|
||||
|
||||
# Working-notes contract ([org-internal #2600] — see core/templates/runs-layout.md §
|
||||
# working-notes.md): Tool agents append to <runs-root>/{slug}/working-notes.md
|
||||
# ONLY when the task produced a load-bearing finding (e.g. image evidence that
|
||||
# changed a decision) — not for routine single-shot output.
|
||||
#
|
||||
# Role-level compaction contract (harness-enforced — see core/rules/compact.md
|
||||
# §"Role-level re-read set"). Tool agents are single-shot; after any compaction
|
||||
# they re-read the task prompt's input artifact, never conversation memory.
|
||||
compact:
|
||||
preserve:
|
||||
- "<runs-root>/{slug}/working-notes.md (re-read FIRST — prior stages' tacit context)"
|
||||
- "the input artifact named in the task prompt (image path, file, or URL)"
|
||||
@@ -0,0 +1,40 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
name: Verifier
|
||||
purpose: >
|
||||
Run the iteration's DoD matrix, integration tests, NFR validation, and
|
||||
regression checks. The iteration is not Done until all DoD items pass.
|
||||
|
||||
# Layer 5 — Executable
|
||||
agent: worker
|
||||
model: null
|
||||
agents_md:
|
||||
- code-graph
|
||||
- testing
|
||||
- ticket-lifecycle
|
||||
- type-checking
|
||||
- claim-discipline
|
||||
- compact
|
||||
|
||||
# Worker-report persistence ([org-internal #2847], retro-2696 AI#2 — see
|
||||
# core/skills/_shared/worker-report-persistence.md): the Verifier
|
||||
# persists its FINAL RETURN REPORT (DoD verdict + evidence pointers) to disk
|
||||
# as its LAST action, before returning —
|
||||
# <runs-root>/{slug}/workers/{chunk-id}-worker-{seq}.md (Tier 1 preferred),
|
||||
# else /tmp/octopus/{chunk-id}-worker-{seq}.md. The persisted copy is the
|
||||
# report of record; the task notification is a convenience copy.
|
||||
#
|
||||
# Working-notes contract ([org-internal #2600] — see core/templates/runs-layout.md §
|
||||
# working-notes.md): the Verifier appends ONE entry at verify exit (what the
|
||||
# DoD sweep caught, flaky/deferral context the next run should know). The
|
||||
# Phase 5.5 archive step additionally inlines the final working-notes.md
|
||||
# content into the archive bundle's working_notes field.
|
||||
#
|
||||
# Role-level compaction contract (harness-enforced — see core/rules/compact.md
|
||||
# §"Role-level re-read set"). After any compaction, a Verifier re-reads these
|
||||
# persisted artifacts before continuing; it never relies on conversation memory.
|
||||
compact:
|
||||
preserve:
|
||||
- "<runs-root>/{slug}/working-notes.md (re-read FIRST, before formal artifacts — carries prior stages' tacit context: decisions, dead ends, reviewer feedback)"
|
||||
- "the DoD matrix for the current iteration ({slug}/05-verify-*)"
|
||||
- "the acceptance criteria (node acceptance_criteria in the frozen DAG copy {epic-slug}/dag, or the task ticket body)"
|
||||
- "the final code-review report ({slug}/reviews/code/final/report)"
|
||||
@@ -0,0 +1,236 @@
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
|
||||
# Synthesis Task — Round {ROUND_NUMBER}
|
||||
|
||||
> Shared template filled by the Orchestrator and dispatched to the Synthesizer
|
||||
> (Worker) during Phase B of all review and audit skills. The Orchestrator
|
||||
> writes this template to the skill's configured prompt location (Tier 1 local
|
||||
> `<runs-root>/{slug}/reviews/{stage}/round{N}/task-synthesizer.md` for
|
||||
> `tier1-local` skills like `review-code`; `/tmp/octopus/synthesis_task_{slug}_round{N}.md`
|
||||
> + a Gitea wiki page for `tier2-comment` skills) for `prompt_file` dispatch.
|
||||
>
|
||||
> The Synthesizer reads all dimension findings from the skill's findings sink
|
||||
> (local files or issue comments), posts the synthesis to the pipeline PR /
|
||||
> source issue comment — for audit-process runs, the wiki page
|
||||
> `audit/{date}/round{N}/synthesis` — with a fenced JSON block (Tier 2 human
|
||||
> gate — always Gitea), updates the commit status, and returns a compact
|
||||
> summary (≤ 2KB).
|
||||
|
||||
## Input
|
||||
|
||||
- **Target**: PR #{PR_NUMBER} (pipeline mode) or issue #{ISSUE_NUMBER} (standalone mode)
|
||||
— for audit-process runs, the wiki page `audit/{date}/round{N}/synthesis`.
|
||||
Post the synthesis to this target.
|
||||
- **Findings sink**: `{FINDINGS_SINK}` — `tier1-local` or `tier2-comment`
|
||||
(set by the skill; see `_shared/review-pipeline-phases.md`).
|
||||
- **Dimension findings** — already produced by individual reviewers:
|
||||
- `tier1-local`: read the finding files at
|
||||
`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`,
|
||||
each a JSON document matching `core/schemas/reviewer-output.schema.json`.
|
||||
Read every `findings-*.json` in that round directory. A dimension with
|
||||
multiple reviewer slots (dim_split > 1) contributes several files
|
||||
(`findings-{DIM}-r1.json`, `-r2.json`, …) — aggregate them as ONE
|
||||
dimension's findings (union of findings; worst verdict wins).
|
||||
- `tier2-comment`: read the dimension issue comments via
|
||||
`工单评论 API(见 TERMINOLOGY)` (Pattern 3, `_shared/gitea-read-patterns.md`).
|
||||
Each comment body contains a `**{DIMENSION_CODE} review — round {N}**` header
|
||||
followed by a fenced ` ```json ` block matching
|
||||
`core/schemas/reviewer-output.schema.json`.
|
||||
- **Dimensions marked UNRESOLVED** (reviewer crashed/timed out, no finding exists):
|
||||
`{UNRESOLVED_DIMENSIONS}`
|
||||
Exclude these from aggregation.
|
||||
- **Synthesis schema**: `core/schemas/synthesis.schema.json`
|
||||
- **Commit SHA**: `{SHA}` (the HEAD commit being reviewed).
|
||||
- **Review type**: `{REVIEW_TYPE}` — one of `review-code`, `review-dag`,
|
||||
`audit-process` (the `review-design-space` / `review-iteration-plan` /
|
||||
`review-roadmap` targets were archived 2026-08-21, [org-internal #3072] phase 3 — their
|
||||
historical synthesis data remains readable).
|
||||
|
||||
## Your Job
|
||||
|
||||
1. Read all dimension findings from the configured sink. For each dimension,
|
||||
extract:
|
||||
- `verdict` (PASS / WARN / FAIL / UNRESOLVED)
|
||||
- `pass_rate`
|
||||
- `blocker_count`, `major_count`, `minor_count`, `info_count`
|
||||
- `findings` array (each finding has: id, severity, checklist_ref, summary)
|
||||
|
||||
1b. **Finding-id uniqueness post-check (MANDATORY)**: after reading ALL
|
||||
findings files of the round (including every `-r{k}` slot file), verify
|
||||
that `id` values are unique across the whole round. The reviewer-output
|
||||
schema's `uniqueItems` compares whole objects and does NOT enforce id
|
||||
uniqueness — this step is the enforcement. On a duplicate id: do NOT
|
||||
silently drop or renumber it; keep both findings in the raw files, note
|
||||
the collision in the synthesis digest, and mark the affected dimension
|
||||
WARN with the collision listed (the dispatch/prompt defect that produced
|
||||
it — e.g. a missing slot prefix — is the fix target).
|
||||
|
||||
2. Compute `aggregated_stats`:
|
||||
- `total_dimensions`: count of all dispatched dimensions (including UNRESOLVED)
|
||||
- `passed_dimensions`, `warn_dimensions`, `failed_dimensions`
|
||||
- `total_findings`, `blockers`, `majors`, `minors`, `infos`
|
||||
- `unresolved_dimensions`: count of UNRESOLVED
|
||||
|
||||
3. Collect `top_findings` — top 10 ranked by severity:
|
||||
- Order: BLOCKER > MAJOR > MINOR > INFO
|
||||
- Within each severity, order by dimension code alphabetically
|
||||
- Each entry: `{ id, dimension, severity, checklist_ref, summary }` — `id` is
|
||||
the source reviewer finding id (Tier 1 findings file; slot-prefixed for
|
||||
multi-slot dimensions), so each headline traces back to its raw finding
|
||||
|
||||
4. Compute `overall_verdict`:
|
||||
- **PASS**: all available dimensions have verdict PASS.
|
||||
- **WARN**: no FAIL dimensions AND no BLOCKER findings.
|
||||
- **FAIL**: any FAIL dimension OR any BLOCKER finding.
|
||||
|
||||
5. Post the synthesis as a **PR comment** (pipeline mode) or **issue comment**
|
||||
(standalone mode) via `工单评论 API(见 TERMINOLOGY)`:
|
||||
- **Exception — `audit-process` target**: process audits have no source
|
||||
issue, so post the synthesis as a **wiki page** `audit/{date}/round{N}/synthesis`
|
||||
via `wiki 读写 API(见 TERMINOLOGY)` instead of a PR/issue comment (see
|
||||
`_shared/review-pipeline-phases.md` Phase B step 3 and review-artifact/SKILL.md
|
||||
process target Output Artifacts). The commit-status `target_url` then
|
||||
carries that wiki page's `html_url`.
|
||||
- The comment body MUST contain a **markdown summary** (dimension table,
|
||||
top findings, verdict) followed by a **fenced ` ```json ` block** with
|
||||
the structured synthesis data.
|
||||
- Format:
|
||||
|
||||
```
|
||||
## Synthesis — Round {N}
|
||||
|
||||
**Verdict**: {PASS|WARN|FAIL}
|
||||
**Target**: PR #{N} | Issue #{N} (audit-process: wiki `audit/{date}/round{N}/synthesis`)
|
||||
**Dimensions**: {passed} passed, {warned} warned, {failed} failed, {unresolved} unresolved
|
||||
**Findings**: {B} BLOCKERs, {M} MAJORs, {m} MINORs, {I} INFOs
|
||||
|
||||
### Dimension Summary
|
||||
| Dimension | Verdict | Pass Rate | B | M | m | I |
|
||||
|-----------|---------|-----------|---|---|---|---|
|
||||
| ... | ... | ... | | | | |
|
||||
|
||||
### Top Findings
|
||||
1. [{DIM_CODE}] [{SEVERITY}] {checklist_ref}: {summary}
|
||||
2. ...
|
||||
|
||||
```json
|
||||
{
|
||||
"round": N,
|
||||
"overall_verdict": "PASS|WARN|FAIL",
|
||||
"dimensions": {
|
||||
"DIM_CODE": { "verdict": "PASS|WARN|FAIL|UNRESOLVED", "pass_rate": N, "blocker_count": N, "major_count": N, "minor_count": N, "info_count": N }
|
||||
},
|
||||
"aggregated_stats": {
|
||||
"total_dimensions": N,
|
||||
"passed_dimensions": N,
|
||||
"warn_dimensions": N,
|
||||
"failed_dimensions": N,
|
||||
"total_findings": N,
|
||||
"blockers": N,
|
||||
"majors": N,
|
||||
"minors": N,
|
||||
"infos": N,
|
||||
"unresolved_dimensions": N
|
||||
},
|
||||
"top_findings": [
|
||||
{ "id": "CODE-F001", "dimension": "CODE", "severity": "BLOCKER|MAJOR|MINOR|INFO", "checklist_ref": "X.Y", "summary": "..." }
|
||||
]
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
6. Post a **commit status** via REST:
|
||||
|
||||
```
|
||||
POST /api/v1/repos/Octopus/octopus/statuses/{SHA}
|
||||
Authorization: token {GITEA_TOKEN}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"state": "success"|"failure",
|
||||
"context": "pipeline/{REVIEW_TYPE}",
|
||||
"description": "Round {N}: {PASS|WARN|FAIL} — {passed}/{total} dims passed, {B}B {M}M",
|
||||
"target_url": "{link to synthesis comment}"
|
||||
}
|
||||
```
|
||||
|
||||
- `state` is `"success"` for PASS or WARN, `"failure"` for FAIL (any BLOCKER).
|
||||
- `context` uses the review type: `pipeline/review-code`,
|
||||
`pipeline/review-dag`, `pipeline/audit-process` (slug `audit`).
|
||||
|
||||
(See `_shared/gitea-write-patterns.md` Pattern 8 for the full commit-status recipe.)
|
||||
|
||||
7. Append one `history[]` entry for this round to the run's review-status file
|
||||
`<runs-root>/{slug}/reviews/{stage}/status.json` (validated by
|
||||
`core/schemas/review-status.schema.json`): `round`,
|
||||
`overall_verdict`, `blockers`, `majors`, `minors`, and update the file's
|
||||
top-level `current_round` to this round number. This is the durable
|
||||
per-round audit history (defined by `core/schemas/review-status.schema.json`
|
||||
`history[]`; mirrors the audit-process checklist TRC 10.4). Initialization is the **Orchestrator**'s
|
||||
job (Phase A step 0.5 of `_shared/review-pipeline-phases.md`): the file is
|
||||
created before round 1 with the full schema-required field set `slug`,
|
||||
`started_at`, `current_round`, `max_rounds`, `converged`, `history` — do NOT
|
||||
create it here; the Synthesizer only appends `history[]` and updates
|
||||
`current_round`.
|
||||
|
||||
8. **Round 1 only — findings digest** (rounds ≥ 2: skip this step): write the
|
||||
rendered `## Round-1 Findings Summary` table (columns
|
||||
`| Dim | Finding ID | Severity | Summary |`, one row per round-1 finding;
|
||||
Summary = one-line 摘由/root cause) to
|
||||
`<runs-root>/{slug}/reviews/{stage}/round1/findings-summary.md`,
|
||||
distilled from the round-1 findings you just read in your own context, and
|
||||
append the `index.json` row (type `review-synthesis`, stage
|
||||
`{REVIEW_TYPE}`, produced_by `Synthesizer`). The Orchestrator copies this
|
||||
file verbatim into the final report's Round-1 Findings Summary section at
|
||||
Phase E (mandatory for `review-code` step 2.55; other targets MAY include
|
||||
it — `_shared/review-final-report-template.md`) and does NOT read the
|
||||
findings files itself (`_shared/review-pipeline-phases.md` Phase B step 3
|
||||
"Round-1 findings digest").
|
||||
|
||||
9. Persist your return summary (the exact text from the Return Format
|
||||
below) to `<runs-root>/{slug}/reviews/{stage}/round{N}/synthesis-return.md`
|
||||
(Tier 1) as your LAST action, BEFORE returning it — worker-report
|
||||
persistence ([org-internal #2847], `_shared/worker-report-persistence.md`). The persisted
|
||||
copy is the report of record; if your completion notification is lost, the
|
||||
Orchestrator recovers the round verdict from this file.
|
||||
|
||||
## Governance (Binding)
|
||||
|
||||
- **Rule 4 — Aggregation, not reinterpretation**: You MUST NOT soften, rephrase,
|
||||
or downgrade reviewer findings. If a reviewer writes "BLOCKER", it stays
|
||||
"BLOCKER" in synthesis.
|
||||
- **Rule 5 — Anonymous findings preservation**: If two reviewers independently
|
||||
flag the same issue from different dimensions, preserve both entries. Do not
|
||||
deduplicate by discarding one — cross-dimension agreement is itself a signal.
|
||||
- **Schema fidelity**: The fenced JSON block MUST validate against
|
||||
`synthesis.schema.json`. Read the schema file first if unsure of field names.
|
||||
|
||||
## Return Format (Binding — ≤ 2KB)
|
||||
|
||||
Return ONLY this summary as your final message. Do NOT include the full
|
||||
synthesis JSON — it is already posted to the pipeline PR / source issue
|
||||
comment (for audit-process runs, the wiki page `audit/{date}/round{N}/synthesis`).
|
||||
Persist this summary verbatim to
|
||||
`<runs-root>/{slug}/reviews/{stage}/round{N}/synthesis-return.md` before
|
||||
returning (step 9 above — worker-report persistence [org-internal #2847]).
|
||||
|
||||
```
|
||||
## Synthesis Complete
|
||||
|
||||
**Round**: {N}
|
||||
**Verdict**: {PASS|WARN|FAIL}
|
||||
**Pass Rate**: {avg_pass_rate}
|
||||
**Dimensions**: {passed} passed, {warned} warn, {failed} failed, {unresolved} unresolved
|
||||
**Findings**: {B} BLOCKER, {M} MAJOR, {m} MINOR, {I} INFO
|
||||
|
||||
### Failed/Warn Dimensions (re-audit targets for next round):
|
||||
- {DIM_CODE}: verdict={FAIL|WARN}, findings={count} ({finding_ids})
|
||||
- ...
|
||||
|
||||
### Top Findings (for revision task):
|
||||
1. [{DIM_CODE}] [{SEVERITY}] {checklist_ref}: {summary}
|
||||
2. ...
|
||||
|
||||
### Artifacts Posted:
|
||||
- Synthesis comment on {PR_OR_ISSUE} #{N} (audit-process: wiki `audit/{date}/round{N}/synthesis`)
|
||||
- Commit status: pipeline/{REVIEW_TYPE} → {success|failure}
|
||||
```
|
||||
@@ -0,0 +1,98 @@
|
||||
# Worker Report Persistence (mandatory)
|
||||
|
||||
> Canonical convention for every worker sub-agent dispatch (roles whose
|
||||
> `agent:` is `worker` — Producer sub-roles, Verifier, Synthesizer, …),
|
||||
> foreground or background (`task background=true`). Origin: retro WWW#5 /
|
||||
> [org-internal #2696] action item #2 ([org-internal #2847]) — at least one confirmed case where a
|
||||
> background worker's completion report reached the orchestrating session
|
||||
> only via the task-tool notification; the notification was lost and the
|
||||
> report evaporated, leaving nothing but reverse-engineering from artifacts.
|
||||
|
||||
## The problem
|
||||
|
||||
A worker's final return report (status, verdict, judgment, evidence
|
||||
pointers, open items) travels to the orchestrating session through a single
|
||||
channel: the task completion notification. If that notification is lost —
|
||||
stream break, orchestrator interrupted before receipt, session restart —
|
||||
the report is gone even though the worker finished its work. Conclusions
|
||||
that exist only in a notification are not durable.
|
||||
|
||||
## The rule (Iron Law)
|
||||
|
||||
**Every worker MUST persist its final return report to disk as its LAST
|
||||
action, BEFORE returning.** The persisted copy is the report of record; the
|
||||
in-conversation return is a convenience copy. No worker return may exist
|
||||
only in the task notification.
|
||||
|
||||
## Where to persist (path selection, in order)
|
||||
|
||||
1. **Tier 1 run workspace (preferred when it exists)** — if
|
||||
`.octopus/runs/{slug}/` exists (see `.octopus/templates/runs-layout.md`),
|
||||
write the report to:
|
||||
- Default: `.octopus/runs/{slug}/workers/{chunk-id}-worker-{seq}.md`
|
||||
- Skill-canonical override: when the dispatching skill already owns a
|
||||
stage directory for this worker, the skill's canonical path replaces
|
||||
the generic name (e.g. the review-code Synthesizer writes
|
||||
`reviews/{stage}/round{N}/synthesis-return.md` next to its task file).
|
||||
- Register one `index.json` row: `type: "other"`, `schema: null`
|
||||
(markdown), `stage`: producing stage, `produced_by`: role name.
|
||||
2. **Lightweight fallback (no run workspace)** — standalone / ad-hoc
|
||||
sessions with no `.octopus/runs/{slug}/`: write to
|
||||
`/tmp/octopus/{chunk-id}-worker-{seq}.md`. Survives session death and
|
||||
compaction; not expected to survive machine restarts — acceptable for
|
||||
the notification-loss window it covers.
|
||||
|
||||
### Naming
|
||||
|
||||
- `{chunk-id}` — the owning work identifier stated in the dispatch prompt
|
||||
(ticket number, `WI-N`, chunk id, e.g. `2847`, `WI-3`).
|
||||
- `{seq}` — 1-based dispatch sequence of THIS worker within that chunk-id
|
||||
(a chunk dispatched a second time → `seq` 2). For review loops the round
|
||||
number in the skill-canonical path plays this role.
|
||||
- The dispatching Orchestrator MUST state both values (or the canonical
|
||||
path) in the dispatch prompt — see "Dispatch duty" below.
|
||||
|
||||
## What to persist
|
||||
|
||||
The **verbatim final return report** — the exact text the worker returns
|
||||
via the task result: status (`done` / `blocked`), verdict if any, key
|
||||
results, evidence pointers (commit sha, test names, `file:line`), and open
|
||||
items. Markdown, no schema. Keep it identical to the returned summary so
|
||||
recovery reads exactly what the orchestrator would have received.
|
||||
|
||||
## Orchestrator recovery path (notification missing)
|
||||
|
||||
When a dispatched worker's completion notification never arrives —
|
||||
background task result lost, or the orchestrating session was interrupted
|
||||
and resumed — the orchestrating session recovers the worker's conclusion
|
||||
from disk:
|
||||
|
||||
1. Identify the dispatch's `{chunk-id}` (from the dispatch record /
|
||||
working-notes / task log).
|
||||
2. Read, in order, the newest match by `{seq}` / mtime:
|
||||
- `.octopus/runs/{slug}/workers/{chunk-id}-worker-*.md`
|
||||
- the skill-canonical return file when the dispatch named one (e.g.
|
||||
`reviews/{stage}/round{N}/synthesis-return.md`)
|
||||
- `/tmp/octopus/{chunk-id}-worker-*.md`
|
||||
3. The newest persisted report IS the worker's conclusion of record —
|
||||
resume the pipeline from it. Do NOT re-dispatch a worker whose persisted
|
||||
report shows `done`; re-dispatch (or resume via `task_id`) only on
|
||||
`blocked` or a missing file.
|
||||
|
||||
## Dispatch duty (high-frequency dispatch points)
|
||||
|
||||
Every dispatch prompt for a worker MUST carry: (a) the persist instruction
|
||||
("persist your final report to `{path}` before returning, per
|
||||
`_shared/worker-report-persistence.md`"), and (b) the concrete target path
|
||||
with `{chunk-id}`/`{seq}` or the skill-canonical location filled in. The
|
||||
high-frequency templates carry it inline:
|
||||
|
||||
- `implement/SKILL.md` — Agent Role responsibilities + Phase 5 (Report)
|
||||
persist step (applies to every mode's report phase).
|
||||
- `review-code/SKILL.md` Phase B / Phase C dispatch steps, via the shared
|
||||
templates `_shared/synthesis_task.md` and
|
||||
`_shared/review-revision-prompt.md`.
|
||||
|
||||
Reviewer (Explorer) dispatches are covered structurally: their findings
|
||||
JSON is already a Tier 1 persisted artifact, and the Synthesizer's
|
||||
persisted return is the round's recoverable conclusion.
|
||||
@@ -0,0 +1,503 @@
|
||||
---
|
||||
name: analyze-dag
|
||||
description: >
|
||||
Use ONLY when an Epic is routed to the DAG ticket pipeline (Kind/Epic or
|
||||
Kind/Feature — both resolve to analyze-dag via the routes table directly,
|
||||
[org-internal #3072] phase 3 / [org-internal #3061] Phase 2). The
|
||||
Analyst decomposes the Epic into a single task-DAG artifact — merging
|
||||
roadmap+requirements+design+plan into one graph (nodes = acceptance
|
||||
criteria, edges = contracts, topology = plan).
|
||||
triggers:
|
||||
- analyze-dag
|
||||
- "dag pipeline"
|
||||
- "DAG 管线"
|
||||
- "任务 DAG"
|
||||
- "task DAG"
|
||||
- "DAG decomposition"
|
||||
- "DAG 分解"
|
||||
# `stage` is intentionally omitted: analyze-dag is the DAG route's entry skill,
|
||||
# replacing roadmap+requirements+design+plan — it has no single stage identifier
|
||||
# in the stage registry (see `<instance-root>/workflow-routing.yaml` dag.route and
|
||||
# core/schemas/workflow-routing.schema.json stage enum). Phase-boundary
|
||||
# signalling for the DAG route is the review-dag single gate and verify
|
||||
# milestone, not a StageDone event.
|
||||
role: Producer
|
||||
---
|
||||
|
||||
# Analyze DAG — Task-DAG Decomposition (DAG route entry skill)
|
||||
|
||||
## Purpose
|
||||
|
||||
analyze-dag is the **entry skill** of the DAG ticket pipeline
|
||||
(`dag.route.entry.skill`, spec-07 §2). For a DAG-routed Epic
|
||||
(`Kind/Epic`, or `Kind/Feature` — both resolve here via the routes table
|
||||
directly, [org-internal #3072] phase 3 / [org-internal #3061] Phase 2 / TD-388 —
|
||||
including BACKFILL mode for MVP graduates and big-bug relabels), it
|
||||
**replaces** `roadmap` + `requirements-elicitation` + `design` +
|
||||
`plan-iterations` and produces **ONE artifact**: the task DAG, published as
|
||||
wiki page `{epic-slug}/dag` (Tier 2 — human-visible, cross-worktree stable).
|
||||
|
||||
The task DAG merges three views into a single graph:
|
||||
|
||||
| View | Where it lives in the DAG |
|
||||
| ---- | ------------------------- |
|
||||
| requirements | node `acceptance_criteria` (each node is its own spec) |
|
||||
| design | edge `contract_ref` (inter-node interface contract) |
|
||||
| plan | topology (edge direction = dependency / execution order) |
|
||||
|
||||
`{epic-slug}` = the slug of the routed Epic — **not** this design's own slug
|
||||
`dag-pipeline`.
|
||||
|
||||
## Agent Role
|
||||
|
||||
The **Analyst** (Producer) owns and produces the task DAG. The Analyst
|
||||
decomposes the Epic into nodes (acceptance criteria), edges (contracts), and
|
||||
a topological order (the plan), then publishes the single graph.
|
||||
|
||||
## Route Disqualifiers — 适用性预检(分解前必查)
|
||||
|
||||
分解前,Analyst 必须对照下表预检。任一命中 → **不分解**,将具体未决问题
|
||||
列回 Epic 并 bounce(在不稳定需求上分解会产出一个即刻腐烂的冻结图;
|
||||
"先出个小 DAG" 不是降级选项——不进管线,先稳定需求):
|
||||
|
||||
| Disqualifier | 判定 | Bounce 动作 |
|
||||
| --- | --- | --- |
|
||||
| 需求未稳 | scope item 含糊、互相矛盾、或只能以 "视情况" 回答;关键干系人问题仍开放 | 在 Epic 上列出澄清问题清单;问题解决后重入 analyze-dag |
|
||||
| 设计主导 | 主要风险是一个有 ≥2 个可行选项的架构/平台决策,而非任务分解;需要先做一次探索 | 将开放问题转 `prototype`(THROWAWAY spike)或设计探索;决策记录后重入 |
|
||||
| 外部契约未决 | scope 依赖未决的外部契约(第三方 schema、上游 API 未定) | 按 External-System Rule(见下文)解析或围栏;不可拆的残余隔离后再重入 |
|
||||
|
||||
**反向拦截(Kind/MVP 降级建议,[org-internal #3061])**:一个 `Kind/Feature` 工单在分解预检中
|
||||
暴露"概念未稳"(scope 仍在探索、成功标准未定、决策日志会推翻节点划分)时,
|
||||
正确出口不是硬分解,而是建议改标签 `Kind/MVP` 先行交互式验证——概念稳定后
|
||||
经 backfill 模式(见下文)带着决策日志重入。
|
||||
|
||||
### Phase 0 需求澄清(disqualifier 的结构化扩展,[org-internal #3061])
|
||||
|
||||
预检不止于"能否分解",还须证明"需求已澄清到可分解"。两项强制检查:
|
||||
|
||||
**1. Scope item 可证伪性预检**:逐条检查 Epic 的 scope item。每条必须能改写为
|
||||
一个可观察的通过条件(给定输入/操作 → 可观测的结果);写不出来的条目就是
|
||||
澄清问题,列入 bounce 清单。禁止把不可证伪条目直接塞进 node AC。
|
||||
|
||||
**2. 横向完备性三态清单**:对下表每个维度做三态判定——**已覆盖**(scope 有
|
||||
对应条目)/ **不适用**(一句话理由)/ **待澄清**(列具体问题)。任一维度停在
|
||||
"待澄清" → bounce(澄清问题清单随 bounce 一起给出,不让用户猜缺什么):
|
||||
|
||||
| 维度 | 检查 |
|
||||
| --- | --- |
|
||||
| 外部系统 | 沿用 External-System Rule(见下文):每个外部交互有明确协议与失败语义 |
|
||||
| 错误路径 | 关键流程的失败分支有定义(不是只写 happy path) |
|
||||
| 数据生命周期 | 数据的创建/保留/删除策略已明确 |
|
||||
| 权限/安全 | 访问边界与敏感数据处理已明确(或显式声明"无安全面") |
|
||||
| NFR | 性能/容量/兼容性约束已枚举(见下) |
|
||||
| 边界条件 | 值域/并发/时序等边界有约定(或声明"无特殊边界") |
|
||||
|
||||
**NFR 输入来源明确化**:NFR 不允许"分解时脑补"。Epic 必须自带 NFR 输入——
|
||||
scope item 中带 `NFR:` 前缀的条目,或一行显式声明"无 NFR 约束"。两者皆缺 →
|
||||
作为澄清问题 bounce,而不是默认无约束。node schema 的 `NFR:` 条目只能来自
|
||||
该枚举,不得新增臆造。
|
||||
|
||||
## Node Schema (spec-03 §1)
|
||||
|
||||
```yaml
|
||||
node:
|
||||
id: "N-01" # task node N-{nn}; milestone node M-{nn}
|
||||
title: "契约骨架" # one line
|
||||
type: task # task | milestone
|
||||
acceptance_criteria: # "requirements" view — one falsifiable AC per line
|
||||
- "AC-1: <可证伪的验收条件, 映射 test_id>"
|
||||
- "NFR: <产品 NFR 条目>" # NFR:-prefixed entry — enumerated storage, no REQ-F number
|
||||
req_refs: # functional-requirement references
|
||||
- "REQ-F-001"
|
||||
status: pending # pending | ready | in_progress | done | blocked | green(仅里程碑)
|
||||
owner_session: null # assigned session id, null when unassigned
|
||||
size_attrs: # decomposition-time measured attributes (spec-06)
|
||||
cross_session_in: 1
|
||||
cross_session_out: 1
|
||||
contract_change: additive # none | additive | breaking (out-edge aggregation)
|
||||
estimated_hours: 8 # task nodes only; milestone nodes omitted
|
||||
estimated_sessions: 1 # task nodes only; milestone nodes omitted
|
||||
```
|
||||
|
||||
Field semantics:
|
||||
|
||||
- `acceptance_criteria` — the node **is** its spec. Each AC is falsifiable and
|
||||
maps a `test_id` ∈ `{ file-path::test-name | MANUAL | BENCH:<script> }`
|
||||
(the plan-iterations §6 SDD→TDD handshake; no other values). Entries MAY be
|
||||
`NFR:`-prefixed (product NFRs — written into node AC as enumerated storage;
|
||||
they carry no `REQ-F` number and are not functional ACs).
|
||||
- `req_refs` — functional-requirement ids this node covers, derived from the
|
||||
requirement registry (see below). Drives the REQMAP "every requirement ≥1
|
||||
node / every node ≥1 requirement" mechanical check.
|
||||
- `status` — drives rolling release (spec-04 RELEASE). `ready` = all
|
||||
cross-session in-edge source nodes reached their terminal state (task `done`,
|
||||
milestone `green`; same-session edges do not block). `green` is **milestone
|
||||
only** (a task node's terminal state is `done`).
|
||||
- `size_attrs` — decomposition-time measured inputs; the DAG-level `dag_metrics`
|
||||
aggregates them. `estimated_hours` / `estimated_sessions` are **required only
|
||||
for `type: task` nodes** (milestone nodes have no implementation work and
|
||||
omit them). The two fields are two units of the same estimate; the
|
||||
consistency invariant is `|estimated_hours − 8 × estimated_sessions| ≤ 2`,
|
||||
and **granularity is always judged by `estimated_sessions`** (1 session ≈ 8h).
|
||||
|
||||
## Edge Schema (spec-03 §2)
|
||||
|
||||
```yaml
|
||||
edge:
|
||||
from: "N-01" # source node id
|
||||
to: "N-02" # target node id
|
||||
contract_ref: "shared/types-v1" # contract pointer (wiki {epic-slug}/shared/ or schema $id)
|
||||
cross_session: true # from/to owned by different sessions?
|
||||
contract_state: draft # draft | frozen (only meaningful for task-to-task cross-session edges)
|
||||
change_type: additive # none | additive | breaking (only cross-session edges)
|
||||
```
|
||||
|
||||
Field semantics:
|
||||
|
||||
- `contract_ref` — the "design" view: where the contract lives. Cross-session
|
||||
task edges point at wiki `{epic-slug}/shared/` or a schema `$id`; same-session
|
||||
contracts may be inlined.
|
||||
- `cross_session` — contract freezing applies **only** to `true` edges (spec-02 §1 D-05).
|
||||
Same-session edges are `cross_session: false` (zero freeze cost).
|
||||
- `contract_state` — `draft` (initial, freely revisable) → `frozen` (after the
|
||||
single-gate PASS). Only meaningful for task-to-task cross-session edges.
|
||||
- `change_type` — the contract change surface, feeding `contract_change_surface`.
|
||||
|
||||
**Milestone edge exception (frozen, spec-03 §2 / spec-02 §2.1):**
|
||||
a milestone is a session-less integration point, so every edge connected to a
|
||||
milestone (in + out) is `cross_session: true` but carries **NO `contract_ref`,
|
||||
NO `change_type`**, and `contract_state` is **not applicable** (never frozen).
|
||||
Milestone edges do not participate in `contract_change_surface` aggregation
|
||||
(treated as `none`).
|
||||
|
||||
## Topology Constraints (spec-03 §3 — must satisfy, or TOPO fails)
|
||||
|
||||
1. **Acyclicity** — the directed graph must have no cycle. A cycle = BLOCKER.
|
||||
2. **Milestone weld rule** — any node with `cross_session_in ≥ 2` (≥2
|
||||
cross-session in-edges converging on it) MUST have a milestone `M` welded at
|
||||
the convergence point, such that:
|
||||
- (a) all cross-session in-edges terminate at `M` (M **is** the convergence
|
||||
point — fan-in edges end at M, no separate original convergence task node);
|
||||
- (b) M has no implementation work (`type: milestone`, no
|
||||
`acceptance_criteria`, only a DoD);
|
||||
- (c) M's DoD = integration verification of the converged scope (spec-05);
|
||||
- (d) M is the **必经 (must-pass) node** for all downstream consumers —
|
||||
downstream nodes may not become `ready` before M is `green`.
|
||||
A convergence point with no downstream consumer is a sink milestone (in-edges
|
||||
only).
|
||||
3. **Granularity floor** — every `type: task` node's implementation estimate is
|
||||
carried by `size_attrs.estimated_hours` / `estimated_sessions` (milestones
|
||||
omit both). Judged by `estimated_sessions` (1 session ≈ 8h). The SINGLE
|
||||
SOURCE of the judging criteria — floor/ceiling bounds, the severity
|
||||
mapping (field-inconsistency, below-floor, above-ceiling, giant node), and
|
||||
the folded checklist rows — is `core/checklists/dag-single-gate.md`
|
||||
§TOPO: recalibrate there, not here. (One-line summary: sessions far below
|
||||
the floor → merge candidates; far above → split/giant; hours-sessions
|
||||
mismatch → field inconsistency.)
|
||||
|
||||
## Requirement Registry (需求登记表, spec-02 §2.9)
|
||||
|
||||
Maintained at the DAG root. Each row:
|
||||
|
||||
```yaml
|
||||
- id: "REQ-F-001" # derived from the Epic scope-item number
|
||||
title: "<需求标题>"
|
||||
source: "<来源 scope item>"
|
||||
refs_by: ["N-01", "N-02"] # node ids that reference this requirement
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- **Only functional requirements** (Epic scope items) get `REQ-F-{NNN}` numbers
|
||||
and rows in the registry.
|
||||
- **Product NFRs do NOT get `REQ-F` numbers** and are NOT registered as rows —
|
||||
they are written into node `acceptance_criteria` as `NFR:`-prefixed entries
|
||||
(enumerated storage), so "every NFR ≥1 node" is mechanically checkable via
|
||||
node-AC enumeration.
|
||||
- `refs_by` is derived from the REQMAP "every requirement ≥1 node" criterion
|
||||
(missing coverage = a requirement with no node).
|
||||
|
||||
## Breadth Coverage — 类目覆盖矩阵 + 排除账本 ([org-internal #2905] 方案 1)
|
||||
|
||||
The four-stage fold that produced the single DAG artifact kept requirement
|
||||
**depth** (REQMAP three-path coverage + falsifiable AC) but dropped legacy
|
||||
elicitation's **breadth** scanning ("还有什么别的需求?"-style category
|
||||
sweep). The registry above only registers what the proposer wrote — a
|
||||
requirement nobody wrote does not exist in the system, and REQMAP's
|
||||
bidirectional mapping checks *in-table* consistency, never `表 ↔ 现实`
|
||||
completeness. The coverage matrix is the mechanical, enumerable part of the
|
||||
compensation (three layers: artifact = matrix, graph = probe nodes, data =
|
||||
`dag.route.preflight` learning loop; this section is the artifact layer).
|
||||
|
||||
analyze-dag MUST emit both sections in `{epic-slug}/dag` (template
|
||||
`core/templates/dag.md` §2.1/§2.2):
|
||||
|
||||
1. **类目覆盖矩阵 (coverage matrix)** — the fixed category set (≤10
|
||||
categories, extracted from legacy
|
||||
`<instance-root>/archive/checklists/requirements-analysis.md`
|
||||
CMP/SAF dimensions) × state ∈ {已覆盖, 明确排除, 待确认}. The category
|
||||
list is FIXED in the template — do not invent or drop categories per
|
||||
Epic; state is per-Epic.
|
||||
- **已覆盖** — the category has registered demand (registry `REQ-F-{NNN}`
|
||||
row or a node `NFR:` entry); evidence cell cites the id(s).
|
||||
- **明确排除** — out of scope, with a ledger row (below). An exclusion
|
||||
without a ledger row is NOT valid.
|
||||
- **待确认** — intermediate state; MUST carry (向{确认人}确认{什么}) so it
|
||||
is distinguishable from 漏了. 待确认 rows SHOULD be driven to a terminal
|
||||
state (已覆盖/明确排除) before the draft is submitted to review-dag.
|
||||
2. **排除账本 (exclusion ledger)** — one `E-n` row per exclusion:
|
||||
条目 — out of scope, 理由, 确认人. Exclusions are reversible (delete the
|
||||
row + flip the matrix state in the same revision).
|
||||
|
||||
**明确排除 ≠ 漏了 (distinguishability)**: excluded = matrix row 明确排除 +
|
||||
ledger `E-n` row with reason and confirmer; 漏了 = blank state, or an "N/A"
|
||||
without a ledger row. The single gate does NOT gain criteria for this —
|
||||
REQMAP's three tables are frozen (spec-04 §1) and `表 ↔ 现实`
|
||||
completeness is a **production-side obligation** of analyze-dag; reviewers
|
||||
may still raise findings on the matrix as ordinary artifact content.
|
||||
|
||||
**Relation to the Product-Epic External-System Rule**: that rule is the
|
||||
external-integration category applied to product Epics — the matrix
|
||||
generalizes its shape from 1 category to N. For a product-type Epic the
|
||||
外部系统集成 category remains mandatory-covered (task node or cross-session
|
||||
contract edge, see the rule below). Process-infrastructure Epics will
|
||||
typically 明确排除 most product-facing categories — that is legitimate;
|
||||
the ledger records why.
|
||||
|
||||
**Page-size budget**: the matrix + ledger participate in the ≤800-line
|
||||
budget (spec-02 §2.6). If the page exceeds, sink category DETAIL to
|
||||
sub-page `{epic-slug}/dag-coverage` using the same mechanics as AC sinking
|
||||
(in-page pointer, return link above the status line, version stamp rides the
|
||||
frozen copy) — purely organizational, NOT an oversize signal, no
|
||||
re-derivation.
|
||||
|
||||
### Breadth probe nodes (P2, [org-internal #2905] 方案 2)
|
||||
|
||||
When a 待确认 category is cheapest to resolve empirically, a **probe node**
|
||||
is a legal task node at the granularity floor:
|
||||
|
||||
```yaml
|
||||
- id: "N-07"
|
||||
title: "探查:{类目} 存量/可行性核对"
|
||||
type: task
|
||||
acceptance_criteria:
|
||||
- "AC-1: 产出 {类目} 结论(进/不进登记表)— MANUAL"
|
||||
req_refs: ["REQ-F-00n"] # 指向被其澄清的需求;探查本身不派生新 REQ-F
|
||||
status: pending
|
||||
size_attrs:
|
||||
cross_session_in: 0
|
||||
cross_session_out: 0
|
||||
contract_change: none
|
||||
estimated_hours: 2
|
||||
estimated_sessions: 0.25 # 粒度下限(Topology Constraints 3)——合法,不得更低
|
||||
```
|
||||
|
||||
- `MANUAL` is a legal `test_id` value — a probe's deliverable is a human
|
||||
verdict (matrix flip), not an automated test.
|
||||
- A probe node anchors to an **existing** requirement it clarifies
|
||||
(`req_refs` non-empty — REQMAP 节点溯源 requires it). A 待确认 category
|
||||
with NO anchor requirement is resolved by confirmation (向确认人问询),
|
||||
not by a probe node.
|
||||
- The probe's conclusion flips the category: → 已覆盖 (new registry row +
|
||||
covering nodes) or → 明确排除 (new ledger row). A conclusion landing
|
||||
BEFORE freeze is ordinary draft revision (no signal).
|
||||
- **登记表单独加行 ≡ node_split**(显式化):post-freeze 新增登记表行(新需求)
|
||||
按 `node_split` oversize 信号处理——触发重派生 + 无条件重跑 review-dag、修订
|
||||
冻结副本(新增需求必然改变节点集合或 `req_refs`;需要新节点时全额按
|
||||
node_split 建单传播)。
|
||||
|
||||
### Data layer — read `dag.route.preflight` BEFORE decomposition ([org-internal #2905] 方案 3)
|
||||
|
||||
Before starting decomposition, analyze-dag MUST read
|
||||
`<instance-root>/workflow-routing.yaml` `dag.route.preflight` and, when the list is
|
||||
non-empty, verify each item as a producer self-check (the same injection
|
||||
contract `implement` applies for `routes.{Kind}.preflight`, [org-internal #2599] — landed
|
||||
items carry their retro evidence in `evidence`):
|
||||
|
||||
1. Read the `dag:` block's `route.preflight` array (live yaml, repo root).
|
||||
2. For each item: run its `item` self-check against the Epic's scope /
|
||||
breadth matrix before writing the DAG (items are typically recurring
|
||||
late-discovery categories, e.g. the retro-derived
|
||||
`REQ × late-discovery` class — requirements that surfaced only after
|
||||
previous DAGs froze).
|
||||
3. An item that fires becomes a 待确认 matrix row (or a probe node, above)
|
||||
— never silently dropped.
|
||||
4. Absent/empty array → no injection, proceed (current state: the list is
|
||||
empty until a retro cycle lands evidence).
|
||||
|
||||
## dag_metrics (spec-03 §4 / spec-06)
|
||||
|
||||
Stored at the DAG root / Epic aggregator. Keys are frozen (spec-02 §2.6):
|
||||
|
||||
```yaml
|
||||
dag_metrics:
|
||||
node_count: 6 # task + milestone nodes total
|
||||
cross_session_edge_count: 7 # total cross_session:true edges (milestone edges counted too)
|
||||
contract_change_surface: additive # none | additive | breaking
|
||||
review_depth: D4 # derived tier D1–D4
|
||||
```
|
||||
|
||||
- `contract_change_surface` — **worst-value aggregation** of `change_type` across
|
||||
cross-session **task edges only** (`breaking > additive > none`). Milestone
|
||||
edges are excluded (treated as `none` — no contract, no `change_type`).
|
||||
- `review_depth` — derived (decomposition **is** the sizing, 分解即定档):
|
||||
|
||||
```
|
||||
review_depth = max(
|
||||
depth_by(node_count),
|
||||
depth_by(cross_session_edge_count),
|
||||
depth_by(contract_change_surface)
|
||||
)
|
||||
```
|
||||
|
||||
Thresholds (spec-06 §2 — authoritative):
|
||||
|
||||
| Measured attribute | D1 | D2 | D3 | D4 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `node_count` | ≤3 | ≤8 | ≤15 | >15 |
|
||||
| `cross_session_edge_count` | 0 | — | 1–3 | ≥4 |
|
||||
| `contract_change_surface` | none | — | additive | breaking |
|
||||
|
||||
- `cross_session_edge_count`: 0 → D1; 1–3 → D3; ≥4 → D4. **D2 is not derivable
|
||||
from this dimension.**
|
||||
- `contract_change_surface`: none → D1; additive → D3; breaking → D4 (D2 not
|
||||
derivable from this dimension).
|
||||
- `review_depth` is the review-dag review-resource parameter (reviewers / round
|
||||
cap, spec-06 §3) — analyze-dag computes it; it does not assign `Size/*`
|
||||
labels (DAG-routed tickets ignore `Size/*`).
|
||||
|
||||
## Page-Size Budget (spec-02 §2.6)
|
||||
|
||||
> Threshold single source: the page-size budget and the `> 页尺寸自检: 超限`
|
||||
> signal are judged by `core/checklists/dag-single-gate.md` (TOPO 页尺寸自检信号
|
||||
> row) — recalibrate there, not here. (One-line summary: the `{epic-slug}/dag`
|
||||
> body has a line budget (~800 lines at calibration time); on breach, sink
|
||||
> node detail to sub-pages and emit the self-check signal per the checklist
|
||||
> row.)
|
||||
|
||||
The `{epic-slug}/dag` page body — node table + edge table + requirement registry
|
||||
+ breadth coverage matrix (§2.1) + exclusion ledger (§2.2) + `dag_metrics` —
|
||||
must fit the checklist's page-size budget (see the single-source note above;
|
||||
~800 lines at calibration time). On breach, analyze-dag MUST:
|
||||
|
||||
1. Sink node AC detail into sub-pages `{epic-slug}/dag-nodes/{node-id}`
|
||||
(sub-page naming `{epic-slug}/dag-nodes/{node-id}` is **frozen**);
|
||||
2. Keep an in-page pointer `{node-id} → {epic-slug}/dag-nodes/{node-id}` for
|
||||
each sunk node;
|
||||
3. Put a return-link line on each sub-page, **above** its status line:
|
||||
`> 返回 [{epic-slug}/dag](…)`;
|
||||
4. Write a page-size self-check signal into the DAG page header, **parallel to**
|
||||
the `> DAG 工件状态` line:
|
||||
`> 页尺寸自检: 超限({node-id} 已下沉子页)`.
|
||||
|
||||
Category detail (matrix/ledger 细目) may likewise sink to
|
||||
`{epic-slug}/dag-coverage` (same mechanics — see Breadth Coverage). This is
|
||||
**NOT** an `oversize_signal` and does **NOT** trigger re-derivation —
|
||||
page overflow does not change the three derivation inputs
|
||||
(`node_count` / `cross_session_edge_count` / `contract_change_surface`); sub-page
|
||||
sinking is purely organizational. (Re-derivation is triggered only by the four
|
||||
spec-06 §5 oversize signals.)
|
||||
|
||||
## Product-Epic External-System Rule (spec-04 §2 ARCH 1.1 alternative)
|
||||
|
||||
For a **product-type** Epic, external systems and integration protocols MUST
|
||||
explicitly appear in the DAG — **either** as task nodes (with acceptance
|
||||
criteria) **or** as cross-session contract edges (`contract_ref` pointing at an
|
||||
external contract). This is what lets the REQMAP "需求覆盖" (requirement
|
||||
coverage) check mechanically verify that external-integration requirements map
|
||||
to nodes. (The `ARCH 1.1` system-context diagram is discarded only for
|
||||
process-infrastructure-type Epics; product Epics get this alternative rule
|
||||
instead.) [org-internal #2905]: this rule is the 外部系统集成 category of the breadth
|
||||
coverage matrix held at its original strength — see Breadth Coverage above.
|
||||
|
||||
## Exec-Resource Configuration (spec-07 §4 — mechanical destination of folded checklist rows)
|
||||
|
||||
This clause is the **mechanically checkable destination** of the folded plan
|
||||
checklist rows `RISK 3.7–3.10` / `EST 6.3–6.12` / `EXE 8.7–8.11` (single-gate
|
||||
does not re-audit these — they live here, in the skill). A analyze-dag run MUST
|
||||
observe:
|
||||
|
||||
| Resource | Configuration |
|
||||
| --- | --- |
|
||||
| Model tier assignment | reasoning-tier for decomposition; fast-tier for mechanical transcription (node/edge/registry/dag_metrics table assembly). reviewer-tier — i.e. the `Reviewer` Explorer role (`_shared/roles/reviewer.yaml`; the vocabulary the review skills use for reviewer dispatch is agent type Explorer) — is used **only** via the review-dag gate (see `review-artifact` target review-dag "Reviewer Constants": Model tier) — **never** inside analyze-dag. |
|
||||
| Token budget | a single analyze-dag agent call's estimated token consumption must stay within the model's context-window capacity, and the `{epic-slug}/dag` page body ≤ 800 lines (page-size budget) bounds the artifact's token footprint. |
|
||||
| Context-window ceiling | keep single-call consumption ≤ 70% of the model's context window. |
|
||||
| RAG supplement | if the Epic's scope touches knowledge newer than the model's training cutoff, configure RAG supplementation before decomposition. |
|
||||
| API-unavailable fallback | define a fallback path (retry / degrade to a lower tier / pause and report) when the model API is unavailable. |
|
||||
| Deterministic tasks | deterministic tasks (mechanical transcription) run at `temperature ≤ 0.1`. |
|
||||
| AI-artifact origin annotation | the DAG artifact is annotated with its origin — model + temperature + timestamp. |
|
||||
|
||||
## Output & Lifecycle
|
||||
|
||||
1. analyze-dag writes wiki page `{epic-slug}/dag` with first line:
|
||||
`> DAG 工件状态: draft`.
|
||||
2. After the single-gate (`review-dag`) PASS, the **aggregator agent** (main
|
||||
session / Orchestrator — not analyze-dag) asks the freeze confirmation
|
||||
question ([org-internal #3379]), then on confirmation flips the page to
|
||||
`> DAG 工件状态: frozen` and stamps version `v1`. The confirmation is an
|
||||
OPTIONIZED `question` call (stage `review-dag`, never `destructive` —
|
||||
freeze is a process gate, not a business decision): options
|
||||
`Freeze as v1 (Recommended)` / `Hold — back to review`, plus a one-line
|
||||
summary of the gate verdict (rounds, worst severity, open waivers) in the
|
||||
question text. Choosing hold = do NOT flip — leave the page `draft`,
|
||||
record the hold and the reason as a comment on the source Epic, and route
|
||||
back to `review-artifact`. An unconfirmed flip is a spec violation: the
|
||||
flip MUST be preceded by the question call in the same session, and the
|
||||
confirmation leaves a trace — post a comment on the source Epic
|
||||
(`DAG frozen: {epic-slug}/dag v1 — confirmed via question, {date}`) before
|
||||
flipping. In autonomous runs the question auto-approves via its stage
|
||||
(choosing the recommended freeze) — the gate guarantees the decision
|
||||
SURFACES with a defined shape at a defined moment, not that a human
|
||||
always attends it.
|
||||
3. On each post-freeze re-review PASS (an oversize-signal re-run, spec-06
|
||||
§5), the aggregator agent updates the frozen copy in place and increments
|
||||
the version stamp `vN → vN+1` (no re-confirmation — only the initial
|
||||
freeze gates on a decision).
|
||||
|
||||
## Backfill Input Mode (Kind/MVP graduation & big-bug relabels, [org-internal #3061])
|
||||
|
||||
When a `Kind/MVP` ticket graduates (relabel → `Kind/Feature` → Step 0 → this
|
||||
skill), the DAG is derived from **what was actually built**, not from greenfield
|
||||
decomposition:
|
||||
|
||||
- **Inputs**: the ticket body's `## 决策日志` (one line per decision: what /
|
||||
why / expiry) + `## Debt Register` section + codegraph reverse call graph
|
||||
of the implemented surface.
|
||||
- **Nodes** follow the code's actual module/contract structure; each node's AC
|
||||
traces to a decision-log line or an existing test. The decision log is the
|
||||
REQMAP baseline for the single gate — decisions still marked deferred/expires
|
||||
become explicit open questions on the Epic, not silent assumptions.
|
||||
- **Debt register items** map to node ACs when they describe unfinished
|
||||
behavior; remaining shortcuts stay debt (prototype/EVOLUTIONARY path →
|
||||
verify Phase 5.5 registration).
|
||||
- **Big-bug relabel inputs** (a `Kind/Bug` relabeled `Kind/Feature` per the
|
||||
big-bug rule, `rules/workflow-routing.md` §"Runtime growth signals
|
||||
(post-sizing)" — big-bug relabel rule): the repro
|
||||
+ root-cause notes (wiki page `{slug}/repro-notes`) carry over as node
|
||||
input — node ACs trace to the reproduction steps and root-cause findings,
|
||||
and the root-cause analysis (not a decision log) serves as the REQMAP
|
||||
baseline for the single gate.
|
||||
- Disqualifiers STILL apply in backfill mode: an MVP whose decision log shows
|
||||
unresolved conflicting deferred decisions bounces (stabilize first) instead
|
||||
of freezing a contradictory graph.
|
||||
|
||||
## What analyze-dag does NOT do
|
||||
|
||||
- **No synthesis.** analyze-dag does not produce a synthesis — the review-artifact skill
|
||||
Phase E produces the single-gate synthesis after review-dag converges.
|
||||
- **No `## 工件索引` issue comment.** analyze-dag is a sub-agent; it does NOT
|
||||
create the Epic's `## 工件索引` comment. The aggregator agent / main session
|
||||
creates and maintains that comment (issue-cross-linking Pattern 10), and the
|
||||
review / verify stages report their rows to it — analyze-dag only publishes the
|
||||
`{epic-slug}/dag` artifact.
|
||||
|
||||
## References
|
||||
|
||||
- `core/templates/dag.md` — the DAG artifact structure contract (this skill writes `{epic-slug}/dag` against it; node/edge/milestone/dag_metrics shape + §6 topology constraints)
|
||||
- `dag-pipeline/spec-02` — frozen contracts (§2.1 node/edge, §2.6 dag_metrics + page-size budget, §2.9 requirement registry + test_id)
|
||||
- `dag-pipeline/spec-03` — node/edge schema + topology constraints
|
||||
- `dag-pipeline/spec-04` — single-gate TOPO/REQMAP/RELEASE dimensions (folded-row destinations)
|
||||
- `dag-pipeline/spec-06` — dag_metrics derivation + thresholds
|
||||
- `dag-pipeline/spec-07` — dag.route landing row (entry skill, skip set, single-gate) + §4 landing list
|
||||
- `dag-pipeline/spec-08` — DAG artifact lifecycle / version stamp
|
||||
- `<instance-root>/archive/skills/roadmap/SKILL.md` — legacy route (archived 2026-08-21, [org-internal #3072] phase 3; this skill replaces it for all Epics)
|
||||
@@ -0,0 +1,297 @@
|
||||
---
|
||||
name: browser-debug
|
||||
description: >
|
||||
Use ONLY when an agent needs to interact with a live browser to debug, verify,
|
||||
or iterate on Web UI work in packages/app — navigate to the dev server, snapshot
|
||||
the DOM/accessibility tree, capture console and network errors, screenshot
|
||||
component states, and exercise user flows end-to-end. Triggers on UI bugs,
|
||||
visual regressions, layout/positioning issues, keyboard-focus problems,
|
||||
console-error reports, and any "open the page and check X" task. Loads the
|
||||
Playwright MCP toolset (browser_navigate, browser_snapshot,
|
||||
browser_take_screenshot, browser_console_messages, browser_evaluate, etc.).
|
||||
Use ONLY for interactive browser work; for writing Playwright *test files*
|
||||
use the existing e2e/ suites under packages/app, not this skill.
|
||||
triggers:
|
||||
- browser debug
|
||||
- open the page
|
||||
- check the page
|
||||
- screenshot the
|
||||
- inspect element
|
||||
- console error
|
||||
- visual bug
|
||||
- visual regression
|
||||
- layout broken
|
||||
- alignment off
|
||||
- focus ring
|
||||
- tab order
|
||||
- 浏览器调试
|
||||
- 打开页面
|
||||
- 截图
|
||||
- 视觉
|
||||
- 布局错乱
|
||||
- 对齐
|
||||
- 焦点
|
||||
- 控制台报错
|
||||
role: Producer
|
||||
---
|
||||
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatimDir)。机制、结构与 frontmatter 保持;实例术语(工具名、路径、工单号)按 `core/adapters/TERMINOLOGY.md` 绑定到具体实例。
|
||||
|
||||
# Browser Debug
|
||||
|
||||
Interactive browser automation for Web UI work in `packages/app`. This skill
|
||||
teaches the *recipe* for driving the Playwright MCP toolset — when to reach
|
||||
for it, which tool for which job, and how to capture evidence a reviewer can
|
||||
trust.
|
||||
|
||||
## When this skill applies
|
||||
|
||||
Reach for `browser-debug` when a task requires **observing** the live
|
||||
application, not just reasoning about code:
|
||||
|
||||
- Reproducing a UI bug (focus lost, dropdown won't close, scroll jumps).
|
||||
- Verifying a visual change actually landed (computed style, layout box,
|
||||
dark-mode token).
|
||||
- Capturing console errors or failed network requests during a user flow.
|
||||
- Recording a screenshot / trace as evidence for a review or DoD entry.
|
||||
- Exercising a keyboard / screen-reader flow end-to-end.
|
||||
|
||||
Do **not** reach for it when:
|
||||
|
||||
- You are writing Playwright *test files* — those live in
|
||||
`packages/app/e2e/...` and run via `cd packages/app && bun run test:e2e`
|
||||
(variant suites: `test:e2e:perf`, `test:e2e:a11y`, `test:e2e:smoke`,
|
||||
`test:e2e:visual`). This skill is for
|
||||
ad-hoc, agent-driven sessions, not for adding tests.
|
||||
- The question can be answered by reading code or running a unit test.
|
||||
Browser time is expensive; spend it only when the live DOM is the oracle.
|
||||
|
||||
## Preconditions
|
||||
|
||||
1. **Playwright MCP is configured.** The project `octopus.jsonc` carries a
|
||||
`mcp.playwright` block launching `@playwright/mcp`. If the MCP tools are
|
||||
not visible, run `/kickoff` or follow the `codegraph-setup` skill's
|
||||
recipe (the closest analog for debugging missing MCP servers).
|
||||
2. **Dev server is running.** The app under test must be reachable. For
|
||||
`packages/app`, run `bash script/dev-fresh.sh --full` for the dev server
|
||||
(Windows workstations: `bash script/dev-fresh-windows.sh` — dev-fresh.sh's
|
||||
stop path depends on setsid/ss, silently ineffective under Git Bash; see
|
||||
local-workstation-quirks.md §5);
|
||||
frontend is at `http://localhost:4444` and backend at `http://localhost:4096`
|
||||
(see AGENTS.md Dev services section).
|
||||
3. **Chromium is installed.** First run of `@playwright/mcp` downloads it
|
||||
automatically; if the tool reports a missing browser, run
|
||||
`bunx playwright install chromium` once in any shell.
|
||||
|
||||
## Tool selection recipe
|
||||
|
||||
The Playwright MCP exposes many tools. Pick by *what you need to observe*,
|
||||
not by familiarity:
|
||||
|
||||
| Need | Tool | Why |
|
||||
| --- | --- | --- |
|
||||
| Open a URL | `browser_navigate` | Always the first call. |
|
||||
| See the page structure (roles, names) | `browser_snapshot` | Returns the accessibility tree — stable across CSS refactors. Prefer this over `browser_take_screenshot` when asserting "does element X exist / is it labelled Y?". |
|
||||
| See the page *visually* | `browser_take_screenshot` | For layout, spacing, color, overflow. Attach the PNG to your report. |
|
||||
| Read console errors / warnings | `browser_console_messages` | Filter by level (`error`, `warn`). The first stop for any "page is blank" bug. |
|
||||
| Read failed network calls | `browser_network_requests` | Locate 4xx/5xx, CORS failures, hanging fetches. |
|
||||
| Click, type, hover, select | `browser_click`, `browser_type`, `browser_hover`, `browser_select_option` | Drive the flow. Always `browser_snapshot` first to get the `ref` for the target element. |
|
||||
| Assert computed style / DOM state | `browser_evaluate` | Last resort. Snapshots cover 90%; reach for `evaluate` only when you need `getComputedStyle`, scroll positions, or arbitrary JS state. |
|
||||
| Keyboard navigation | `browser_press_key` | For tab order, focus rings, escape-to-close. |
|
||||
| Record a video / trace | `browser_start_tracing`, `browser_stop_tracing` | Heavy; use only when a bug is timing-sensitive and console+snapshot can't catch it. |
|
||||
|
||||
## Standard recipe — reproduce a UI bug
|
||||
|
||||
Follow these steps in order. Do not skip the snapshot step: the `ref` it
|
||||
returns is the stable handle every subsequent tool needs.
|
||||
|
||||
1. `browser_navigate` to the page where the bug lives.
|
||||
2. `browser_console_messages` — record any errors *before* touching anything.
|
||||
Many "interaction bugs" are actually load-time errors.
|
||||
3. `browser_snapshot` — locate the target element; capture its `ref` and
|
||||
accessible name.
|
||||
4. Drive the interaction with `browser_click` / `browser_type` /
|
||||
`browser_press_key` until the bug reproduces.
|
||||
5. Re-read `browser_console_messages` and `browser_network_requests` —
|
||||
capture the *delta* introduced by the interaction.
|
||||
6. `browser_take_screenshot` (or a second `browser_snapshot`) to freeze the
|
||||
broken state.
|
||||
7. Report: URL, the minimal interaction sequence, console delta, network
|
||||
delta, screenshot, and the suspected component path in
|
||||
`packages/app/src/...`.
|
||||
|
||||
## Standard recipe — verify a UI fix
|
||||
|
||||
1. `browser_navigate` to the affected page.
|
||||
2. `browser_snapshot` — confirm the fixed element is present with the
|
||||
expected accessible name.
|
||||
3. `browser_take_screenshot` — full page **and** element-cropped if the fix
|
||||
is local.
|
||||
4. If the fix touched layout, `browser_evaluate` a `getBoundingClientRect()`
|
||||
or `getComputedStyle()` on the key node and paste the value into your
|
||||
report. Numbers beat adjectives.
|
||||
5. If the fix touched interaction, replay the original bug reproduction
|
||||
steps and assert the previous failure no longer fires.
|
||||
|
||||
## Standard recipe — capture evidence for verify / DoD
|
||||
|
||||
When a `verify` work item has an E2E or UI DoD entry:
|
||||
|
||||
1. Run the recipe above for each DoD line.
|
||||
2. Save each screenshot as `.playwright-mcp/dod-{item-id}-{state}.png` —
|
||||
the worktree's gitignored output dir. See "Screenshot output paths"
|
||||
below.
|
||||
3. In the verification report, cite the file names and tell the reviewer
|
||||
where to look: open the worktree as an octopus project → session side
|
||||
panel "Screenshots" tab (or the standalone screenshots page). Verify
|
||||
with `GET /screenshots?directory={worktree}` before citing. A screenshot
|
||||
the reviewer can open beats a paragraph of "I checked it".
|
||||
4. When the DoD row will cite the evidence as a `BROWSER:{pack-ref}`
|
||||
reference (UI-touching PRs, `verify` skill Phase 2.7 / contract
|
||||
`browser-evidence-4486/shared/evidence-ref-v1`), promote the captures
|
||||
into an evidence pack first — feed COPIES through
|
||||
`writeEvidencePack` (`<harness-package>/src/browser/evidence-pack.ts`,
|
||||
the single mandatory sanitize boundary; the `.playwright-mcp/` layer
|
||||
keeps its current semantics, additive not replaced) and cite the pack
|
||||
directory. Evidence ladder: raw capture → `.playwright-mcp/` working
|
||||
layer (gitignored) → sanitized Tier-1 pack → `BROWSER:` reference the
|
||||
verifier validates mechanically.
|
||||
|
||||
## Rationalizations to refuse
|
||||
|
||||
| Excuse | Reality |
|
||||
| --- | --- |
|
||||
| "The unit test already covers it." | Unit tests assert component logic, not what Chromium actually renders. Visual and focus bugs live in the gap. |
|
||||
| "I can tell from the JSX that it's aligned." | Computed layout depends on parent containers, fonts, and viewport. Screenshot the real page. |
|
||||
| "Browser automation is overkill for a small change." | A `browser_navigate` + `browser_take_screenshot` pair is ~2 seconds. The cost is in *not* looking. |
|
||||
| "I'll just run the whole e2e suite." | The suite answers "did anything break?" — it does not answer "does this specific change look right?". |
|
||||
| "Snapshot is enough, skip the screenshot." | Accessibility trees hide overflow, clipping, and z-index bugs. Take both for visual changes. |
|
||||
|
||||
## Boundaries
|
||||
|
||||
- **Read-only by default.** This skill never writes files under
|
||||
`packages/app/src/` — it observes. Edits flow through `implement`
|
||||
(bugfix/refactor/port mode).
|
||||
- **One page at a time.** Close tabs (`browser_close`) between scenarios so
|
||||
state doesn't leak.
|
||||
- **Evidence lives in the worktree's `.playwright-mcp/` dir.** Any screenshot
|
||||
or trace you reference in a PR comment, verification report, or DoD entry
|
||||
must be copied into the workflow worktree's `.playwright-mcp/` directory
|
||||
(gitignored) — never left in `/tmp/`, `playwright-report/`, or any
|
||||
machine-local path the reviewer cannot reach through octopus. Cite file
|
||||
names + the worktree project path; the reviewer opens the octopus
|
||||
screenshots UI (session panel tab or screenshots page) to view them.
|
||||
|
||||
## Session cleanup (mandatory)
|
||||
|
||||
A Playwright MCP browser is a real Chrome with a `/tmp` user-data-dir — its
|
||||
mode follows `mcp.playwright.headless` (`headless-field-v1`, [org-internal #4393]): the repo
|
||||
default `auto` spawns headed on desktop (win32/darwin; linux only with
|
||||
DISPLAY/WAYLAND_DISPLAY set) and headless otherwise; an explicit `true`/`false`
|
||||
or `OCTOPUS_MCP_HEADLESS=1|0` pins it. The mode is fixed per new session —
|
||||
switching the config does not hot-switch a running browser. It
|
||||
stays alive as long as its MCP server runs, and the MCP server stays alive as
|
||||
long as its parent octopus backend runs. When a browser-debug session ends
|
||||
without an explicit `browser_close`, OR the octopus session times out mid-task,
|
||||
the Chrome + its MCP server are orphaned and accumulate across sessions —
|
||||
burning CPU and leaving dead profile dirs (issue [org-internal #1315]); in headed mode the
|
||||
orphan is a visible stray window.
|
||||
|
||||
**Iron rule: every browser-debug session MUST close its browser before ending.**
|
||||
|
||||
1. **Always close when done.** The last action of any browser-debug task is
|
||||
`browser_close`. Treat it like a `return` — if you opened a browser, you
|
||||
close it. Do not rely on session teardown to do it for you; it doesn't.
|
||||
2. **Close on error too.** If the task aborts (bug not reproduced, tool error,
|
||||
user redirect), still call `browser_close` before moving on. An abandoned
|
||||
debugging session is the most common orphan source.
|
||||
3. **If a browser was already orphaned** (you spot a stale Chrome in `ps`, or
|
||||
a `/tmp/playwright_chromiumdev_profile-*` dir with no live session), do not
|
||||
leave it. `bash script/dev-fresh.sh --full` (Windows workstations:
|
||||
`dev-fresh-windows.sh`, same stop-path caveat as Preconditions §2) reaps
|
||||
orphan dev sessions and
|
||||
init-adopted browsers before restarting; run it to clean up. For a targeted
|
||||
clean without restarting the dev server, kill the Chrome by its profile:
|
||||
`pkill -f 'playwright_chromiumdev_profile'` (and any `agent-browser` harness
|
||||
reparented to init).
|
||||
|
||||
**Why this matters:** a single forgotten `browser_close` leaves a Chrome
|
||||
subtree (8+ processes) running indefinitely. Over days this is hundreds of
|
||||
orphaned processes and wasted CPU. The MCP server does not auto-close its
|
||||
browser on disconnect — only an explicit `browser_close` (or process reaping)
|
||||
releases it.
|
||||
|
||||
## Screenshot output paths
|
||||
|
||||
The Playwright MCP server is launched WITHOUT `--output-dir` (see
|
||||
`octopus.jsonc` → `mcp.playwright.command`): its default output location is
|
||||
`.playwright-mcp/` in the process cwd (the worktree root), gitignored and
|
||||
auto-evicted at 100 MB via `--output-max-size`. Auto-named output (no
|
||||
`filename`) lands there — but an explicit `filename` can resolve against the
|
||||
cwd and drop the PNG into the repo root, where the `/*.png` gitignore
|
||||
backstop hides it from `git status` and nothing ever cleans it up.
|
||||
|
||||
**Iron rule: 截图文件名必须写进 `.playwright-mcp/`。** Every `filename`
|
||||
passed to `browser_take_screenshot` (or any tool that writes a file) MUST
|
||||
resolve into `.playwright-mcp/` — write it as `.playwright-mcp/<name>.png`,
|
||||
or use an absolute path under `/tmp/octopus/`. NEVER a bare relative name
|
||||
like `2202-after-models.png`: it leaks a root-level PNG that gitignore masks
|
||||
but nobody deletes (the 2026-08 repo-root buildup — 40+ orphan PNGs — came
|
||||
from exactly this).
|
||||
|
||||
Two valid destinations for screenshots, by purpose:
|
||||
|
||||
| Purpose | Destination | Why |
|
||||
| --- | --- | --- |
|
||||
| **Ad-hoc / throwaway** (debug a layout, confirm a fix landed, never cited in any report) | `.playwright-mcp/<name>.png`, or omit `filename` to auto-save into the same dir | Survives across the session, auto-evicted at 100 MB, never pollutes the repo. |
|
||||
| **Evidence cited in a PR / report / DoD entry** | `.playwright-mcp/<name>.png` — the cwd is the worktree root, so no copy step is needed | Zero repo bloat (gitignored, never committed); viewable by the reviewer through octopus's own screenshots UI (session panel "Screenshots" tab / standalone screenshots page, served by `GET /screenshots?directory={worktree}`); lifecycle bounded to the worktree = the review window. |
|
||||
|
||||
**Evidence publishing ladder** (verified 2026-08-14 on this Gitea version):
|
||||
|
||||
1. **Copy into the workflow worktree's `.playwright-mcp/`** — primary path.
|
||||
Cite file names + worktree project path; reviewer views via the octopus
|
||||
screenshots UI. Verify reachability with
|
||||
`GET /screenshots?directory={worktree}` before citing. Never commit
|
||||
evidence binaries to git — screenshots accumulate per PR and git history
|
||||
is unreclaimable (measured ~80KB/PR today, unbounded growth). Do not put
|
||||
routine debug screenshots on the wiki either — base64-in-page bloats the
|
||||
wiki git repo equivalently; the wiki evidence namespace is reserved for
|
||||
the durable verify-stage evidence channel (rung 3).
|
||||
2. **Issue attachments API** (`POST /api/v1/repos/{o}/{r}/issues/{n}/assets`)
|
||||
— alternative when the reviewer cannot reach the worktree through the
|
||||
octopus UI. The endpoint exists but has returned 500 on this instance;
|
||||
verify before relying on it.
|
||||
3. **Publish to the wiki evidence namespace `{slug}/verify/evidence/{name}`
|
||||
via `wiki 读写 API(见 TERMINOLOGY)`** — last resort only, when no other channel
|
||||
works AND the evidence must outlive the worktree (this is the same durable
|
||||
channel `verify` mandates for DoD evidence entries — see
|
||||
`core/skills/verify/SKILL.md` References). Keep it small:
|
||||
≤5 images per PR, each ≤200KB (compress/downscale, prefer element
|
||||
crops over full-page).
|
||||
|
||||
**Iron rule: never cite a machine-local path (`/tmp/...`, `~`, project
|
||||
root) as evidence in a Tier-2 output** (PR comment, issue comment,
|
||||
verification report). The reviewer must be able to open the evidence
|
||||
through octopus (`.playwright-mcp/` + screenshots UI) or a repo/wiki URL
|
||||
(worktree files, or the `{slug}/verify/evidence/{name}` wiki pages). A
|
||||
citation they cannot reach is a fabricated citation.
|
||||
|
||||
Never write screenshots to the project root. The `.gitignore` rejects
|
||||
`/*.png`, `/.playwright-mcp/`, and `/playwright-report/` only as a backstop —
|
||||
an ignored file is still an orphan on disk. The `.playwright-mcp/` filename
|
||||
prefix is the real fix; rely on that, not on the backstop.
|
||||
|
||||
## References
|
||||
|
||||
- `core/skills/frontend/SKILL.md` — where UI changes are planned; invoke
|
||||
this skill during Phase 3 / Phase 4 to verify each state visually.
|
||||
- `core/skills/implement/SKILL.md` — Frontend Mode; pair with
|
||||
`browser-debug` when the work item touches rendering.
|
||||
- `core/skills/implement/SKILL.md` (Mode: bugfix) — Phase 1 reproduction; use this skill
|
||||
to capture the failing state before isolating root cause.
|
||||
- `core/skills/verify/SKILL.md` — Phase 2 / DoD matrix; use this skill
|
||||
to generate screenshot evidence for UI-tagged DoD entries.
|
||||
- `packages/app/e2e/` — permanent Playwright test suites; this skill is the
|
||||
ad-hoc complement, not a replacement.
|
||||
- `rules/dev-server` on the wiki — how to run the dev server for TUI work;
|
||||
consult `packages/app/README.md` for the web equivalent.
|
||||
@@ -0,0 +1,213 @@
|
||||
---
|
||||
name: codegraph-setup
|
||||
description: >
|
||||
Use ONLY when installing, configuring, initializing, or troubleshooting CodeGraph
|
||||
(@colbymchenry/codegraph — https://github.com/colbymchenry/codegraph) in the
|
||||
octopus environment. Covers global CLI install, per-project `.codegraph/`
|
||||
indexing, and maintenance commands (status, sync, upgrade, uninstall). MCP
|
||||
wiring into octopus.jsonc is handled by the kickoff pipeline
|
||||
(`octopus kickoff` or `/kickoff`). Also use when a project session reports a
|
||||
missing/stale `.codegraph/` index.
|
||||
triggers:
|
||||
- codegraph
|
||||
- code graph
|
||||
- codegraph setup
|
||||
- codegraph install
|
||||
- codegraph init
|
||||
- .codegraph
|
||||
- codegraph_explore
|
||||
- codegraph_node
|
||||
- codegraph_callers
|
||||
- codegraph_status
|
||||
- install codegraph
|
||||
# Chinese — bare nouns for code-graph concepts. The English "codegraph" brand
|
||||
# name above already covers mixed-language phrasings; these cover pure Chinese.
|
||||
- 代码图
|
||||
- 代码图谱
|
||||
- 代码索引
|
||||
- 索引代码
|
||||
- 装代码图
|
||||
- 初始化代码图
|
||||
role: Producer
|
||||
---
|
||||
|
||||
# CodeGraph Setup in the Octopus Environment
|
||||
|
||||
CodeGraph is a local-first code intelligence layer (https://github.com/colbymchenry/codegraph)
|
||||
that octopus consumes as a **stdio MCP server**. It exposes the `codegraph_explore`,
|
||||
`codegraph_node`, `codegraph_callers`, `codegraph_search`, and `codegraph_status`
|
||||
tools, letting agents query a pre-indexed knowledge graph instead of scanning files.
|
||||
|
||||
Per `.octopus/rules/code-graph.md` (the L1 code-graph rule): **Code Graph First is mandatory.**
|
||||
Structural questions MUST use `codegraph_explore` / `codegraph_search` /
|
||||
`codegraph_callers`; grep is the fallback for fuzzy text only. So a working
|
||||
CodeGraph install is a prerequisite for every cross-file task.
|
||||
|
||||
Setup has two independent layers. Each can be installed/reinstalled without
|
||||
touching the others:
|
||||
|
||||
1. **Global CLI** — the `codegraph` binary, installed once per machine.
|
||||
2. **Per-project index** — the `.codegraph/` directory, built once per repo.
|
||||
|
||||
MCP wiring into octopus.jsonc is handled by the kickoff pipeline
|
||||
(`octopus kickoff` or `/kickoff`). See the `project-kickoff` skill for details.
|
||||
|
||||
---
|
||||
|
||||
## 1. Install the global CLI
|
||||
|
||||
The package is `@colbymchenry/codegraph` on npm. It bundles its own runtime and
|
||||
ships platform-specific binaries as optional dependencies (e.g.
|
||||
`@colbymchenry/codegraph-linux-x64`), so there is nothing to compile.
|
||||
|
||||
Pick one method (all equivalent; they only differ in how the binary lands on
|
||||
`$PATH`):
|
||||
|
||||
```bash
|
||||
# Via bun global (what this environment currently uses)
|
||||
bun add -g @colbymchenry/codegraph
|
||||
|
||||
# Via npm global
|
||||
npm i -g @colbymchenry/codegraph
|
||||
|
||||
# Run-once, no install
|
||||
npx @colbymchenry/codegraph
|
||||
|
||||
# Via the official install scripts (puts `codegraph` on PATH, no shell change)
|
||||
curl -fsSL https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh | sh # macOS / Linux
|
||||
irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex # Windows (PowerShell)
|
||||
```
|
||||
|
||||
**In this environment**, the install is bun-global:
|
||||
|
||||
| Item | Value |
|
||||
|------|-------|
|
||||
| Binary on PATH | `~/.bun/bin/codegraph` (symlink) |
|
||||
| Real package dir | `~/.bun/install/global/node_modules/@colbymchenry/codegraph/` |
|
||||
| Entry point | `npm-shim.js` → resolves to the platform binary |
|
||||
| Platform binary | `@colbymchenry/codegraph-linux-x64` |
|
||||
| Version | `1.0.0` |
|
||||
|
||||
Verify the install:
|
||||
|
||||
```bash
|
||||
codegraph --version # prints the installed version
|
||||
which codegraph # should resolve under ~/.bun/bin or ~/.npm-global/bin
|
||||
```
|
||||
|
||||
Upgrade any time — it auto-detects how you installed:
|
||||
|
||||
```bash
|
||||
codegraph upgrade # upgrade to latest
|
||||
codegraph upgrade --check # only report if an update exists
|
||||
codegraph upgrade 1.2.3 # pin a specific version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Initialize the per-project index
|
||||
|
||||
Run once from inside each project you want indexed. `-i` builds the initial
|
||||
graph in the same step; omit it to create the `.codegraph/` dir and build
|
||||
later with `codegraph index`.
|
||||
|
||||
```bash
|
||||
codegraph init -i # create .codegraph/ AND build the initial graph
|
||||
codegraph init # create .codegraph/ only; run `codegraph index` afterwards
|
||||
```
|
||||
|
||||
This creates (relative to the project root):
|
||||
|
||||
```
|
||||
.codegraph/
|
||||
├── .gitignore # ignores itself; safe to leave uncommitted
|
||||
├── codegraph.db # the SQLite knowledge graph (full WAL mode)
|
||||
├── codegraph.db-shm # SQLite shared-memory (WAL)
|
||||
├── codegraph.db-wal # SQLite write-ahead log
|
||||
├── daemon.log # watcher/MCP daemon log
|
||||
├── daemon.pid # daemon process id
|
||||
└── daemon.sock # daemon IPC socket
|
||||
```
|
||||
|
||||
`codegraph init` writes a `.gitignore` inside `.codegraph/`, so the index dir
|
||||
is normally left uncommitted (it is machine- and version-specific). If you
|
||||
choose to commit it anyway, add `.codegraph/*.db*` to the repo `.gitignore`
|
||||
instead and treat the index as a build artifact.
|
||||
|
||||
**In this environment** the index is already built:
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Files indexed | 1,868 |
|
||||
| Nodes | 32,592 |
|
||||
| Edges | 118,912 |
|
||||
| DB size | ~85 MB |
|
||||
| Backend | `node:sqlite` — built-in (full WAL) |
|
||||
|
||||
Check any project's own stats at any time:
|
||||
|
||||
```bash
|
||||
codegraph status
|
||||
```
|
||||
|
||||
### Auto-sync — you do not normally run `codegraph sync`
|
||||
|
||||
Once the MCP server (`codegraph serve --mcp`) is running, three layers keep the
|
||||
index in step with your code without manual sync:
|
||||
|
||||
1. **Debounced file watcher.** A native FSEvents (macOS) / inotify (Linux) /
|
||||
ReadDirectoryChangesW (Windows) watcher captures every create/modify/delete
|
||||
and re-indexes after a debounce. Default `2000 ms`; tune via
|
||||
`CODEGRAPH_WATCH_DEBOUNCE_MS` (clamped to `[100ms, 60s]`).
|
||||
2. **Connect-time catch-up.** On (re)connect the server reconciles
|
||||
`(size, mtime)` + content-hash against the working tree, so edits made while
|
||||
no MCP server was running (a `git pull` from the terminal, another editor, a
|
||||
previous session that exited) are absorbed before the first query.
|
||||
3. **On-demand sync.** `codegraph sync` is available as an escape hatch.
|
||||
|
||||
Disable the watcher on slow filesystems (WSL2 `/mnt` drives, network mounts):
|
||||
|
||||
```bash
|
||||
codegraph serve --mcp --no-watch
|
||||
```
|
||||
|
||||
### Workflow worktrees get their own index ([org-internal #2561] / TD-006)
|
||||
|
||||
The main checkout's `.codegraph/` does NOT cover git worktrees — they live
|
||||
outside the repo path, so nearest-`.codegraph/` resolution finds nothing under
|
||||
them and graph queries degrade to grep+read. Both worktree lifecycle scripts
|
||||
hook a per-worktree init into their `create` flow:
|
||||
|
||||
- `script/session-worktree.sh create <slug>` and
|
||||
`script/scratch-worktree.sh create <slug> <base>` run
|
||||
`codegraph init -i <worktree-path>` in the background. Best-effort: a
|
||||
missing CLI, an existing index, or an init failure never blocks worktree
|
||||
creation — the ready/failed signal lands in `<worktree>/.codegraph/init.log`
|
||||
(one line). The background subshell never holds the caller's stdout pipe, so
|
||||
output-capturing callers (CI, nested scripts) are not blocked either.
|
||||
- `CODEGRAPH_SKIP=1` disables the hook (selftests / CI).
|
||||
- Worktrees created before this hook landed have no index — run
|
||||
`codegraph init -i` once from inside them.
|
||||
- After create, the index builds asynchronously (a full repo takes ~minutes).
|
||||
Verify readiness with `codegraph status` INSIDE the worktree (`Files > 0`,
|
||||
`Nodes > 0`) before relying on graph queries.
|
||||
|
||||
---
|
||||
|
||||
## 3. Verification checklist
|
||||
|
||||
After a fresh setup, confirm each layer is working:
|
||||
|
||||
1. **CLI** — `codegraph --version` prints a version; `which codegraph` resolves.
|
||||
2. **Index** — `codegraph status` from the project root shows `Files > 0` and
|
||||
`Nodes > 0`; `.codegraph/codegraph.db` exists.
|
||||
|
||||
---
|
||||
|
||||
## 4. How octopus uses CodeGraph (usage rules)
|
||||
|
||||
This skill covers **setup** only. Usage conventions (query the graph before
|
||||
reading; `codegraph_explore` for flows, `codegraph_search`/`codegraph_callers`
|
||||
for symbols; `grep` is the fallback) are normative in `.octopus/rules/code-graph.md` — refer there. The short version: run `codegraph_status`
|
||||
on session start, and prefer graph tools over grep+read for any structural
|
||||
question.
|
||||
@@ -0,0 +1,322 @@
|
||||
---
|
||||
name: frontend
|
||||
description: >
|
||||
Use ONLY when doing frontend development work — building or modifying UI
|
||||
components, pages, styles, and client-side interactions. Detects the
|
||||
project's framework and styling approach, then implements with a plan
|
||||
template, mandatory loading/empty/error/success state coverage, and an
|
||||
accessibility + self-check pass against core/checklists/frontend.md.
|
||||
For full pipeline features (a new page/route, ≥3 new components, new API
|
||||
contracts or a new data model, a new state-management pattern, or work
|
||||
spanning ≥5 files), use the `implement` skill and apply these templates
|
||||
within it.
|
||||
triggers:
|
||||
# English — phrases + UI-element nouns (short nouns are safe here: they
|
||||
# rarely embed in non-frontend words, and the description gates false fires).
|
||||
- create component
|
||||
- build ui
|
||||
- implement frontend
|
||||
- add page
|
||||
- add screen
|
||||
- implement page
|
||||
- frontend
|
||||
- UI component
|
||||
- style this
|
||||
- add styles
|
||||
- make it responsive
|
||||
- component
|
||||
- button
|
||||
- modal
|
||||
- dropdown
|
||||
- tooltip
|
||||
- navbar
|
||||
- checkbox
|
||||
- toggle
|
||||
# Chinese — high-frequency bare nouns only. Because matching is substring,
|
||||
# a bare noun (组件) subsumes every verb-noun combo (写组件 / 做个组件 /
|
||||
# 创建组件 / 改组件), so listing phrases is redundant. Chinese substrings
|
||||
# do not collide with English words, so bare nouns are safe + maximal-recall.
|
||||
- 前端
|
||||
- 组件
|
||||
- 页面
|
||||
- 样式
|
||||
- 按钮
|
||||
- 表单
|
||||
- 弹窗
|
||||
- 模态框
|
||||
- 输入框
|
||||
- 导航
|
||||
- 菜单
|
||||
- 卡片
|
||||
- 下拉
|
||||
- 标签页
|
||||
- 界面
|
||||
- 交互
|
||||
- 切图
|
||||
- 响应式
|
||||
|
||||
role: Producer
|
||||
---
|
||||
|
||||
# Frontend
|
||||
|
||||
Surgically implement frontend UI — components, pages, styles, and client-side
|
||||
interactions — guided by framework conventions, with mandatory state-coverage
|
||||
and accessibility checks before code review.
|
||||
|
||||
## Scope & Routing
|
||||
|
||||
This skill covers **standalone** frontend work: one or two components, a style
|
||||
tweak, a single page or UI element. The user's description plus the existing
|
||||
codebase is the specification.
|
||||
|
||||
Escalate to the `implement` skill (full pipeline: analyze-dag → review-dag →
|
||||
implement → review-code → verify) when the work involves ANY of:
|
||||
|
||||
- ≥ 3 new components, or a new page/route
|
||||
- new API contracts or a new data model
|
||||
- a new state-management pattern
|
||||
- ≥ 5 files changed
|
||||
|
||||
For pipeline work, still apply this skill's **Framework Detection**, **plan &
|
||||
report templates**, and **self-check** within the `implement` workflow.
|
||||
|
||||
## Agent Role
|
||||
|
||||
Owned by the **Developer** (Worker). Responsibilities:
|
||||
|
||||
- Detect framework + styling from the existing code; match conventions exactly.
|
||||
- Read ≥ 3 neighbor components before writing (brownfield).
|
||||
- Implement all UI states: loading, empty, error, success, and edge cases.
|
||||
- Ensure accessibility: semantic HTML, ARIA, keyboard nav, focus, contrast.
|
||||
- Self-check against `core/checklists/frontend.md` before handoff.
|
||||
- Write no more and no less than the scope — no opportunistic refactoring.
|
||||
|
||||
The Builder validates output and passes it to code review; it MUST NOT write
|
||||
implementation code.
|
||||
|
||||
<!-- inject: ../_shared/large-prompts.md -->
|
||||
|
||||
> **Context compaction**: this skill is a pipeline stage boundary. The main
|
||||
> session (orchestrator) compacts at this clean boundary ONLY when a
|
||||
> capacity/projection trigger holds, per the L1 rule `core/rules/compact.md`
|
||||
> §"Stage-boundary compaction" (long multi-stage runs — DAG Epic orchestration — keep the legacy
|
||||
> every-boundary compaction; short runs — bugfix / DAG task — and standalone
|
||||
> runs default to NOT compacting). The
|
||||
> sub-agent this skill dispatches persists its artifacts to the Gitea wiki
|
||||
> under `{slug}/` as it goes, so a mid-run compaction loses nothing — re-read
|
||||
> the stage's wiki index to resume.
|
||||
|
||||
## Framework Detection
|
||||
|
||||
Before writing any code, detect the project's frontend framework:
|
||||
|
||||
| Signal | Framework |
|
||||
| --------------------------------- | ---------- |
|
||||
| `package.json` has `react` | React |
|
||||
| `package.json` has `vue` | Vue |
|
||||
| `package.json` has `svelte` | Svelte |
|
||||
| `package.json` has `solid-js` | SolidJS |
|
||||
| `package.json` has `@angular/core`| Angular |
|
||||
| `.tsx`/`.jsx` files present | React or SolidJS (check package.json) |
|
||||
| `.vue` files present | Vue |
|
||||
| `.svelte` files present | Svelte |
|
||||
|
||||
Detect the styling approach:
|
||||
|
||||
| Signal | Approach |
|
||||
| ----------------------------------------- | ----------------- |
|
||||
| `tailwind.config.*` or `postcss.config.*` with tailwind | Tailwind CSS |
|
||||
| `.module.css` or `.module.scss` files | CSS Modules |
|
||||
| `styled-components` in package.json | styled-components |
|
||||
| `@emotion/*` in package.json | Emotion |
|
||||
| `uno.config.*` | UnoCSS |
|
||||
| Plain `.css` or `.scss` imports | Plain CSS/SCSS |
|
||||
|
||||
Follow the detected convention exactly. Do NOT introduce a new styling
|
||||
approach unless the work item explicitly requires it.
|
||||
|
||||
## Greenfield vs. Brownfield
|
||||
|
||||
**Greenfield** (new project): Create new files following the design. Use the
|
||||
framework's standard conventions (`create-vite`, `create-next-app`, etc.) as
|
||||
the baseline. Prefer functional components, TypeScript, and the framework's
|
||||
current recommended patterns.
|
||||
|
||||
**Brownfield** (existing project + new UI):
|
||||
|
||||
- **Read neighbors first.** Before writing code, read at least 3 existing
|
||||
files in the same module (component files for UI work) to absorb the
|
||||
project's patterns: component structure, prop
|
||||
typing, styling approach, state management, and file organization.
|
||||
(Shared brownfield rule — canonical statement:
|
||||
`core/skills/review-code/SKILL.md` §"Greenfield vs. Brownfield".)
|
||||
- Match existing conventions exactly: component declaration style, export
|
||||
pattern, file naming, directory structure, import ordering, and CSS
|
||||
organization.
|
||||
- New code MUST follow existing conventions consistently — no style drift.
|
||||
- No opportunistic refactoring of unrelated components. If you see a pattern
|
||||
violation, log it in the implementation report — do not fix it.
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Preconditions
|
||||
|
||||
- [ ] Task description exists (component name, props, behavior).
|
||||
- [ ] Existing codebase is accessible for convention discovery.
|
||||
- [ ] `core/checklists/frontend.md` is accessible.
|
||||
|
||||
If the task is vague ("make it look better"), ask for specifics:
|
||||
|
||||
1. What component/page needs work? (name or path)
|
||||
2. What should it look like or do? (screenshot, description, or reference)
|
||||
3. What states should it handle? (loading, empty, error, success)
|
||||
4. Any accessibility requirements?
|
||||
|
||||
### Phase 1 — Parse Context
|
||||
|
||||
1. Detect framework and styling approach (see Framework Detection).
|
||||
2. Read ≥ 3 neighbor components to absorb patterns.
|
||||
3. Check existing tests — follow the same test pattern.
|
||||
4. Check existing routes if adding a page.
|
||||
5. Check design tokens (theme, spacing, palette) if the project has them.
|
||||
|
||||
### Phase 2 — Plan UI Implementation
|
||||
|
||||
Before writing code, produce a brief implementation plan:
|
||||
|
||||
```markdown
|
||||
## Frontend Implementation Plan
|
||||
|
||||
**Component(s)**: {ComponentName} at {path}
|
||||
**Framework**: {React / Vue / Svelte / SolidJS / Angular}
|
||||
**Styling**: {Tailwind / CSS Modules / styled-components / plain CSS}
|
||||
**State variants**: loading | empty | error | success | {edge case}
|
||||
|
||||
**Files to create**:
|
||||
- `path/to/Component.tsx` — {purpose}
|
||||
|
||||
**Files to modify**:
|
||||
- `path/to/existing.tsx` — {what changes, why}
|
||||
|
||||
**Component API**:
|
||||
- Props: {prop}: {type} — {description}
|
||||
- Events/Callbacks: {onX}: {signature} — {description}
|
||||
- Slots/Children: {description if applicable}
|
||||
|
||||
**Accessibility checklist**:
|
||||
- [ ] Semantic HTML elements used
|
||||
- [ ] ARIA labels for icon-only buttons/images
|
||||
- [ ] Keyboard navigation (Tab order, Enter/Space for actions)
|
||||
- [ ] Focus management (auto-focus, focus trapping for modals)
|
||||
- [ ] Color contrast ≥ 4.5:1 for text, ≥ 3:1 for large text
|
||||
```
|
||||
|
||||
### Phase 3 — Implement
|
||||
|
||||
Implement in layers:
|
||||
|
||||
1. **Structure first** — scaffold the component with correct HTML semantics
|
||||
and prop types. No styling yet.
|
||||
2. **Add styling** — apply styles following the project's convention. Handle
|
||||
responsive breakpoints if the design specifies them.
|
||||
3. **Add state variants** — implement loading, empty, error, and edge case
|
||||
states before the happy path. This forces you to handle all conditions.
|
||||
4. **Wire interactivity** — add event handlers, form validation, keyboard
|
||||
shortcuts, and focus management.
|
||||
5. **Accessibility pass** — audit every interactive element for keyboard
|
||||
access, every image/icon for alt text, every form control for labels.
|
||||
|
||||
**Rules**:
|
||||
|
||||
- Use the project's existing component library (Kobalte, Radix, Headless UI,
|
||||
etc.) where applicable — do not reinvent accessible primitives.
|
||||
- Every component MUST render gracefully in all four states: loading, empty,
|
||||
error, and success.
|
||||
- If the project uses TypeScript, all props must be typed — no `any`.
|
||||
- Image `alt` text must be meaningful, not decorative if the image conveys
|
||||
information.
|
||||
- Form inputs must have associated `<label>` elements (not just placeholders).
|
||||
|
||||
### Phase 4 — Self-Check
|
||||
|
||||
Run the project's verification commands:
|
||||
|
||||
1. **Typecheck**: `bun typecheck` (or project equivalent). Fix all errors.
|
||||
2. **Lint**: `bun oxlint --deny-warnings` (repo root; `bun lint` is the package-script alias). Fix all errors.
|
||||
3. **Tests**: `bun run test:changed` (or project equivalent). All affected tests pass.
|
||||
|
||||
Then self-check against `core/checklists/frontend.md`:
|
||||
|
||||
- Verify every PRE item was satisfied before coding.
|
||||
- Verify every POST item is satisfied now.
|
||||
- For any failed item, fix before reporting.
|
||||
|
||||
### Phase 5 — Report
|
||||
|
||||
```markdown
|
||||
## Frontend Implementation Report
|
||||
|
||||
**Component(s)**: {ComponentName}
|
||||
**Framework**: {framework}
|
||||
**Styling**: {approach}
|
||||
|
||||
### Files Changed
|
||||
|
||||
| File | Action | Purpose |
|
||||
| ------------------ | -------- | -------------- |
|
||||
| `path/to/file.tsx` | created | {purpose} |
|
||||
| `path/to/file.css` | created | {purpose} |
|
||||
|
||||
### State Coverage
|
||||
|
||||
| State | Handled | How |
|
||||
| ------- | ------- | -------------------------------- |
|
||||
| loading | ✅ | Skeleton/spinner while fetching |
|
||||
| empty | ✅ | "No items" message with CTA |
|
||||
| error | ✅ | Error message with retry button |
|
||||
| success | ✅ | Renders data as designed |
|
||||
|
||||
### Accessibility
|
||||
|
||||
| Check | Status | Notes |
|
||||
| --------------------------- | ------ | -------------- |
|
||||
| Semantic HTML | ✅ | |
|
||||
| Keyboard navigation | ✅ | |
|
||||
| Focus management | ✅ | |
|
||||
| Color contrast | ✅ | |
|
||||
| Screen reader labels | ✅ | |
|
||||
|
||||
### Verification
|
||||
|
||||
- Typecheck: {pass / fail}
|
||||
- Lint: {pass / fail}
|
||||
- Tests: {N} passed, {M} failed
|
||||
|
||||
### Open Items
|
||||
|
||||
{anything incomplete with reason, or "None"}
|
||||
|
||||
---
|
||||
|
||||
**Handoff**: {if > 20 lines or ≥ 3 files → run review-code | else → complete}
|
||||
```
|
||||
|
||||
### Phase 6 — Approval
|
||||
|
||||
Present the report to the user. Route to code review if > 20 lines or ≥ 3 files.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- `core/checklists/frontend.md` — Frontend self-check checklist
|
||||
- `core/skills/implement/SKILL.md` — General + pipeline implementation workflow
|
||||
- `core/skills/review-code/SKILL.md` — Code review (next step)
|
||||
- `<instance-root>/archive/skills/design/SKILL.md` — Design document production (legacy upstream, archived [org-internal #3072] phase 3)
|
||||
- `core/skills/browser-debug/SKILL.md` — Interactive browser verification (invoke during Phase 3 / 4 for visual + interaction evidence)
|
||||
- `core/rules/testing.md`, `core/rules/type-checking.md` — Repository conventions (test commands, typecheck)
|
||||
- `core/rules/code-graph.md` — Code graph first
|
||||
- `core/adapters/gitea/reading.md` — Read SDLC artifacts from wiki
|
||||
@@ -0,0 +1,183 @@
|
||||
---
|
||||
name: gitea-rest
|
||||
description: Use ONLY when an agent must call the Gitea REST API over HTTP (curl / fetch / HttpClient) 鈥?not for other gitea tooling (the retired gitea-mcp wrapper / gitea_* MCP tools). Covers auth schemes + token scopes, pagination, error format, this fork's specifics, copy-paste recipes for the daily loop (issues, PRs, comments, labels, releases, raw files), and the on-demand recipe for extracting any endpoint's definition from the auto-generated swagger spec (`/swagger.v1.json`) instead of keeping an endpoint catalog in context. This is the only supported Gitea API path 鈥?the gitea-mcp wrapper is retired.
|
||||
triggers:
|
||||
# Direct Gitea HTTP/REST. Composite "gitea rest"/"gitea api" to avoid
|
||||
# colliding with other gitea tooling.
|
||||
- gitea rest
|
||||
- gitea api
|
||||
- gitea http
|
||||
- call gitea api
|
||||
- /api/v1
|
||||
- gitea swagger
|
||||
- swagger.v1.json
|
||||
- 璋?gitea api
|
||||
- gitea 鎺ュ彛
|
||||
- gitea rest api
|
||||
role: Producer
|
||||
---
|
||||
|
||||
> Core 涓珛鐗堬紙Increment 6a 鏀瑰啓锛夈€傛湰鏂囦欢鍚屾椂浣滀负 gitea adapter 鐨?**reference implementation** 绀轰緥锛坒rontmatter 鍘熸牱锛夛紱瀹炰緥鏈瀵圭収 `core/adapters/TERMINOLOGY.md`銆?
|
||||
# Gitea REST 鈥?conventions + on-demand endpoint lookup
|
||||
|
||||
Gitea ships a JSON REST API under **`/api/v1`**. The instance also publishes an
|
||||
**auto-generated OpenAPI spec at `/swagger.v1.json`** (~889KB, 316 endpoints) 鈥?
|
||||
never load it whole into context; extract single endpoint definitions with jq
|
||||
(see "Endpoint lookup"). The spec is generated from this fork's code, so it is
|
||||
always current 鈥?no manually maintained catalog can drift.
|
||||
|
||||
## Quick start
|
||||
|
||||
```sh
|
||||
BASE=<instance-base-url>
|
||||
TOKEN=$(cat <config-home>/octopus/gitea-token) # or $WORKSPACE_GIT_TOKEN inside a workspace container
|
||||
AUTH="Authorization: token $TOKEN"
|
||||
curl -fsS -H "$AUTH" $BASE/api/v1/version # => {"version":"1.22.0"}
|
||||
```
|
||||
|
||||
Inside a workspace container: `WORKSPACE_GIT_TOKEN` already carries the
|
||||
provisioned token, and the instance host resolves to the VPC-internal address
|
||||
(no EIP bandwidth cap) 鈥?use it as `TOKEN` directly.
|
||||
|
||||
## Authentication
|
||||
|
||||
| Scheme | Header | Notes |
|
||||
| --------------------- | ------------------------------------------ | ----------------------------------------------- |
|
||||
| **PAT (recommended)** | `Authorization: token <PAT>` | Also accepts `bearer`. 40-char hex. |
|
||||
| OAuth2 JWT | `Authorization: bearer <jwt>` | OAuth2-app token. |
|
||||
| Basic | `Authorization: Basic <base64(user:pass)>` | Server must enable it; 2FA needs `X-Gitea-OTP`. |
|
||||
|
||||
- Query-param token (`?token=`) is deprecated 鈥?avoid.
|
||||
- `Sudo: <username>` header acts as another user (admin only).
|
||||
- Keep tokens out of git and out of process argv where possible: read from a
|
||||
file/env into a shell var at call time, not in a committed script.
|
||||
|
||||
## Token scopes (write implies read)
|
||||
|
||||
Categories: `issue`, `repository`, `organization`, `user`, `notification`,
|
||||
`package`, `admin`, `misc`, `activitypub` 鈥?each as `read:X` / `write:X`.
|
||||
HTTP method sets the level: **GET 鈫?read**, **POST/PUT/PATCH/DELETE 鈫?write**.
|
||||
A scope miss returns `403` with `token does not have at least one of required
|
||||
scope(s)...` 鈥?re-issue the token rather than widening other permissions.
|
||||
|
||||
The full agent loop (push commits, create/merge PRs, comment) needs at minimum
|
||||
**`write:repository` + `write:issue`**. Workspace container tokens are
|
||||
provisioned `read:repository` by default 鈥?write operations fail with 403
|
||||
until the operator widens the scope.
|
||||
|
||||
## Pagination
|
||||
|
||||
- `page` (1-based, default 1), `limit` (default 30, **hard cap 50**).
|
||||
- `X-Total-Count` response header = total results.
|
||||
- `Link: <url?page=2>; rel="next"` 鈥?walk `next` links until absent.
|
||||
- Some endpoints (commits, pulls) also set `X-Page` / `X-PerPage` /
|
||||
`X-PageCount` / `X-HasMore`.
|
||||
|
||||
## Errors
|
||||
|
||||
Every error is `{"message":"...","url":".../api/swagger"}` with the matching
|
||||
status: `400` bad arg, `403` permission/scope, `404` not found or wrong BASE,
|
||||
`409` already exists, `413` too large, `422` malformed JSON / missing field.
|
||||
There is **no rate limiting** on `/api/v1` 鈥?don't expect `X-RateLimit-*`.
|
||||
|
||||
## Endpoint lookup (swagger, on demand)
|
||||
|
||||
For any endpoint not covered by the recipes below, pull the spec once per
|
||||
session and jq out just the definition you need (50鈥?00 tokens each):
|
||||
|
||||
```sh
|
||||
curl -s $BASE/swagger.v1.json -o /tmp/gitea-sw.json
|
||||
jq '.paths["/repos/{owner}/{repo}/pulls"].post' /tmp/gitea-sw.json # create PR
|
||||
jq '.paths["/repos/{owner}/{repo}/issues"].get.parameters' /tmp/gitea-sw.json
|
||||
jq '.definitions.CreateIssueOption' /tmp/gitea-sw.json # a body model
|
||||
jq -r '.paths | keys[]' /tmp/gitea-sw.json | grep actions # discover endpoints
|
||||
```
|
||||
|
||||
Definitions include parameter names/types/required flags and `$ref` response
|
||||
models (under `.definitions` on this Gitea version). Prefer discovering via
|
||||
the spec over guessing paths.
|
||||
|
||||
## Daily-loop recipes
|
||||
|
||||
`BASE`/`TOKEN`/`AUTH` as in Quick start; `OWNER=Octopus REPO=octopus` as
|
||||
the example. All bodies are JSON.
|
||||
|
||||
**Create an issue**
|
||||
|
||||
```sh
|
||||
curl -fsS -X POST "$BASE/api/v1/repos/$OWNER/$REPO/issues" -H "$AUTH" -H 'Content-Type: application/json' \
|
||||
-d '{"title":"Bug: X fails","body":"steps...","labels":[12]}'
|
||||
```
|
||||
|
||||
**Comment on an issue / PR** (same path for both 鈥?PRs are issues by index)
|
||||
|
||||
```sh
|
||||
curl -fsS -X POST "$BASE/api/v1/repos/$OWNER/$REPO/issues/42/comments" -H "$AUTH" \
|
||||
-H 'Content-Type: application/json' -d '{"body":"LGTM"}'
|
||||
```
|
||||
|
||||
**Create a PR**
|
||||
|
||||
```sh
|
||||
curl -fsS -X POST "$BASE/api/v1/repos/$OWNER/$REPO/pulls" -H "$AUTH" -H 'Content-Type: application/json' \
|
||||
-d '{"head":"workflow/fix/x","base":"main","title":"fix: X"}'
|
||||
```
|
||||
|
||||
**Merge a PR**
|
||||
|
||||
```sh
|
||||
curl -fsS -X POST "$BASE/api/v1/repos/$OWNER/$REPO/pulls/7/merge" -H "$AUTH" -H 'Content-Type: application/json' \
|
||||
-d '{"Do":"merge","MergeTitleField":"feat: add X","MergeMessageField":"closes #7"}'
|
||||
```
|
||||
|
||||
**Merge returned 405? Read the response body 鈥?two distinct causes:**
|
||||
|
||||
- `{"message":"head branch is behind the base branch"}` 鈥?the keep-mergeable
|
||||
bot advanced main after your push, or Gitea's mergeable cache is stale.
|
||||
Fix: `git fetch origin main && git merge origin/main && git push` the head
|
||||
branch, wait ~2s, retry the merge. (Recurring on this server 鈥?seen 3 PRs
|
||||
in a row on 2026-08-23.)
|
||||
- `{"message":"The PR is already merged"}` 鈥?**treat as success**. The merge
|
||||
landed during a previous "failed" attempt (racy async recompute). Confirm
|
||||
with `GET .../pulls/7 | jq '.merged, .merge_commit_sha'` before assuming
|
||||
failure. Never re-push/re-create the PR on this signal.
|
||||
|
||||
**Auth hygiene** 鈥?always pass the token via the `Authorization` header
|
||||
(`-H "Authorization: token $TOKEN"`), never embedded in a remote URL
|
||||
(`https://user:token@host/...`): it persists into `.git/config` and shell
|
||||
history. If a token leaks into a remote URL, rewrite the remote
|
||||
(`git remote set-url`) and rotate.
|
||||
|
||||
**List PRs / changed files** 鈥?`GET .../pulls?state=open`, `GET .../pulls/7/files`
|
||||
|
||||
**Search issues across repos** 鈥?`GET $BASE/api/v1/repos/issues/search?q=&type=pulls`
|
||||
|
||||
**List labels** 鈥?`GET .../labels?limit=50 | jq '.[] | {name,color}'` (label create/update via POST/PATCH on the same path)
|
||||
|
||||
**Raw file contents** 鈥?`GET .../raw/README.md` (plain text; the `contents/{path}` variant returns base64 in `.content`)
|
||||
|
||||
**Create a release** 鈥?`POST .../releases {"tag_name":"v1.2.0","target":"main",...}`
|
||||
|
||||
**Create a wiki page** 鈥?`POST .../wiki/new {"title":"My Page","content_base64":"<b64>","message":"add page"}`. **`content_base64` is the ONLY field that writes body text** 鈥?this fork **silently ignores** the upstream-style `content` field on wiki create/update (no error, `201` returned, page saved 0 bytes; evidence [org-internal #3944]: commits f5eaf18/4f0217d/bad5880). `content` is valid only on the file API, never on wiki.
|
||||
|
||||
**Read / edit / delete a wiki page** 鈥?`GET|PATCH|DELETE .../wiki/page/{pageName}`. **Read by the mangled name, not the logical title**: this fork rewrites stored filenames for titles containing `/`, spaces, or `:` (slash percent-encoded into the filename + a `.-` suffix 鈥?see [org-internal #3218]), so `GET /wiki/page/bugfix-3204/bugfix-report` 404s. Always `GET .../wiki/pages` first and use the returned `sub_url` **verbatim** (it already carries the `%2F` encoding and `.-` suffix). PATCH/DELETE take the same mangled `pageName`. For PATCH, **omit `title` to keep the page name** and send only `content_base64`+`message` (fixed in gitea `dev-421-g7ff56aec08`, [org-internal #3510] 鈥?before that a title-less PATCH silently renamed the page to `unnamed.md` and later edits deleted target pages). Same trap as create: a PATCH carrying `content` instead of `content_base64` returns `200` with a **0-byte page** ([org-internal #3944]) 鈥?always base64-encode the body and verify non-empty via a follow-up `GET`.
|
||||
|
||||
## This fork's specifics
|
||||
|
||||
- **Projects are repo-level only** 鈥?no `/orgs/.../projects` or `/users/.../projects` REST endpoints.
|
||||
- **Aggregated inline review comments**: no single list-all endpoint 鈥?list
|
||||
reviews, then fetch each review's `/comments`.
|
||||
- Wiki REST = 6 method脳path combos on 4 paths (`new`, `page/{pageName}`
|
||||
GET/PATCH/DELETE, `pages`, `revisions/{pageName}`). This fork has **no**
|
||||
upstream `raw/{pageName}` or `pages/{pageName}` single-page endpoints, and
|
||||
`page/{pageName}` only matches the mangled filename (see the wiki recipe
|
||||
above and [org-internal #3218]) 鈥?clone the wiki git repo (default branch `main`) only if
|
||||
you need history beyond `/wiki/revisions` or bulk filename surgery.
|
||||
- The authoritative route table is `routers/api/v1/api.go` in the Gitea
|
||||
source; the human-readable docs UI is `<BASE>/api/swagger`.
|
||||
|
||||
## In Effect code
|
||||
|
||||
Prefer `HttpClient` from `@effect/platform` with the same headers (see
|
||||
`rules/effect-rules` on the wiki). Outside Effect, `curl` / `Bun.fetch` are
|
||||
fine 鈥?the wire format is identical.
|
||||
@@ -0,0 +1,547 @@
|
||||
---
|
||||
name: headless-session-ops
|
||||
description: >
|
||||
Use ONLY when an agent must autonomously launch a headless main session 鈥?
|
||||
create a session and drive agent generation over the HTTP `prompt_async`
|
||||
endpoint with NO persistent client attached (Web UI / TUI absent) 鈥?OR
|
||||
lifecycle-manage such a session afterwards: health-check polling, hung-stream
|
||||
diagnosis (state=generating + frozen message count), abort + re-wake rescue,
|
||||
provider quota-wall recovery (worker killed mid-task by a usage limit 鈥?
|
||||
detect, re-wake on a known-good provider, inject facts), and fact-baseline
|
||||
injection to correct a stale worldview in a woken worker.
|
||||
This is the path used by in-session agents spawning sibling sessions,
|
||||
`octopus run --attach` non-interactive mode, and cron / CI drivers. Covers
|
||||
the 3-step flow (create session 鈫?POST prompt_async WITH a known-good
|
||||
`model` 鈫?poll `/session/:id/message` to verify generation), how to REUSE
|
||||
the current session's model for the new session, the model-field
|
||||
requirement, the directory default (current session's directory unless
|
||||
targeting another repo), and the ticket-driven recipe (launch a session
|
||||
seeded from issue #N). Do NOT use when a persistent client (Web UI / TUI)
|
||||
is driving the session 鈥?those use the synchronous `prompt` endpoint and
|
||||
always send `model`.
|
||||
triggers:
|
||||
# English 鈥?the brand phrases for headless launch.
|
||||
- headless session
|
||||
- launch session
|
||||
- prompt_async
|
||||
- prompt async
|
||||
- autonomous session
|
||||
# English 鈥?the failure mode (agent sees a stalled session and needs to know why).
|
||||
- session not generating
|
||||
- session generation stuck
|
||||
# English 鈥?ticket-driven launch.
|
||||
- launch session for issue
|
||||
- launch session for ticket
|
||||
- spawn session for ticket
|
||||
# English 鈥?patrol mode (delayed self-wake polling loops, [org-internal #3937]).
|
||||
- patrol loop
|
||||
- patrol mode
|
||||
- delayed wake
|
||||
# Chinese 鈥?bare noun phrases (matches() is a contiguous substring).
|
||||
- 鎷夎捣涓讳細璇?
|
||||
- 鎷夎捣浼氳瘽
|
||||
- 鑷不浼氳瘽
|
||||
- 鏃犲ご浼氳瘽
|
||||
# Chinese 鈥?patrol mode ([org-internal #3937]).
|
||||
- 宸℃妯″紡
|
||||
- 寤惰繜鍞ら啋
|
||||
- 浼氳瘽涓嶇敓鎴?
|
||||
- 浼氳瘽鍗℃
|
||||
- 涓哄伐鍗曟媺璧蜂細璇?
|
||||
- 宸ュ崟鎷夎捣浼氳瘽
|
||||
# English 鈥?lifecycle (hung rescue, stale-worldview correction, [org-internal #2459]).
|
||||
- abort session
|
||||
- revive session
|
||||
- session rescue
|
||||
- fact baseline
|
||||
# English 鈥?quota-wall recovery ([org-internal #3669]): worker killed by provider usage limit.
|
||||
- quota wall
|
||||
- usage limit reached
|
||||
- quota exhausted
|
||||
- switch provider
|
||||
# Chinese 鈥?lifecycle ops ([org-internal #2459]).
|
||||
- 鍞ら啋浼氳瘽
|
||||
- 浼氳瘽鎸傛
|
||||
- 浜嬪疄鍩虹嚎
|
||||
# Chinese 鈥?quota-wall recovery ([org-internal #3669]).
|
||||
- 閰嶉澧?
|
||||
- 閰嶉鑰楀敖
|
||||
- 鐢ㄩ噺涓婇檺
|
||||
- 鎹緵搴斿晢
|
||||
role: Producer
|
||||
---
|
||||
|
||||
> Core 涓珛鐗堬紙Increment 6a 鏀瑰啓锛屽師 deferHard verbatimDir锛夈€傛満鍒躲€佺粨鏋勪笌 frontmatter 淇濇寔锛涘疄渚嬫湳璇紙宸ュ叿鍚嶃€佽矾寰勩€佸伐鍗曞彿锛夋寜 `core/adapters/TERMINOLOGY.md` 缁戝畾鍒板叿浣撳疄渚嬨€?
|
||||
# Headless Main Session Launch (prompt_async)
|
||||
|
||||
Launch a **main session for a specific issue / work item from a headless
|
||||
context** 鈥?no Web UI, no TUI, just HTTP. This is the "fire-and-forget" path: an
|
||||
agent inside another session, a cron job, a CI script, or `octopus run --attach`
|
||||
non-interactive mode all land here when they need to spin up a session that
|
||||
actually generates.
|
||||
|
||||
## Directory convention 鈥?independent session in the current workspace, no worktree
|
||||
|
||||
The default is an **independent session sharing the current session's
|
||||
directory** 鈥?the repo you are already working in. No new worktree, no
|
||||
checkout, no extra `bun install` or `.codegraph/` rebuild. Session creation
|
||||
and worktree discipline are **orthogonal**: this skill only opens a session
|
||||
record pointed at a directory; whether the launched session later creates its
|
||||
own worktree is a _workflow_ decision (per the worktree-discipline rule) it
|
||||
makes when it starts actual work 鈥?not something the launcher does. One
|
||||
exception: the **ticket-driven recipe** below, where claim-first ([org-internal #2297])
|
||||
requires the launcher to push the workflow branch before launch, making a
|
||||
pre-built worktree the claim carrier + ready workspace (see Pitfall #10
|
||||
carve-out). So, unless a different repo / path is specifically targeted,
|
||||
`?directory=` defaults to the current session's directory and is NOT mandatory:
|
||||
|
||||
- **You are an in-session agent** (most common): set `DIR` to your own
|
||||
session's `directory` (you already know it from context, or read `.directory`
|
||||
from your own session record via `GET /session/$LAUNCHER_SID`). The new
|
||||
session runs independently in that same workspace. Pass an explicit
|
||||
different `?directory=` only when the new session must work in another
|
||||
**repo** 鈥?never a worktree of the same repo. The Web UI sidebar groups
|
||||
sessions by directory (`sidebar-project.tsx` per-workspace
|
||||
`workspaceSessions(directory)`, fetched via `session.list({ directory })`),
|
||||
so a session pointed at a worktree disappears from the workspace the creator
|
||||
manages. A pre-built ticket worktree is handed to the launched session via
|
||||
the claim comment + seed prompt, NOT via `?directory=`.
|
||||
- **No current session** (cron / CI / bare script): the server-side fallback
|
||||
when `?directory=` is omitted is the **server process's `process.cwd()`**
|
||||
(`server/routes/instance/httpapi/middleware/workspace-routing.ts:76`), which
|
||||
is wherever the server was started 鈥?unreliable. In that context `?directory=`
|
||||
remains effectively required.
|
||||
- **Dispatch convention 鈥?directory = code location, not tracker repo.** When
|
||||
launching a session for a ticket, set `?directory=` to the **main workspace**
|
||||
checkout that holds the **code the ticket edits**, which may differ from the
|
||||
repo where the ticket is tracked. Example: `<owner>/<backend-repo>#<n>` is filed in
|
||||
the ticket-tracker repo but edits the main repo's workflow files 鈫?the
|
||||
session's `?directory=` is the **code checkout root**
|
||||
(`<workspace-root>/<org>/<repo>`), not the tracker repo checkout. Never point
|
||||
`?directory=` at a ticket worktree 鈥?the worktree goes in the claim comment +
|
||||
seed prompt, not the directory field (sidebar visibility, see bullet 1). The
|
||||
owner-check (`GET /session?directory=$DIR`) is scoped per directory, so
|
||||
rooting the session at the correct code checkout is also what makes the
|
||||
duplicate-session guard meaningful. Server-side backstop ([org-internal #3190]): creating a
|
||||
session whose directory IS a linked git worktree answers with an
|
||||
`X-Session-Directory-Warning` response header and a server warn log naming
|
||||
the owning main repo 鈥?if you see that header, the session will be invisible
|
||||
to `GET /session?directory=<mainRepo>` owner-checks; re-check your launch
|
||||
parameters before proceeding.
|
||||
|
||||
> **Authoritative reference:** `rules/headless-session-ops` wiki page
|
||||
> (<<instance-base-url>/Octopus/octopus/wiki/rules%2Fheadless-session-ops>).
|
||||
> This skill is its runtime carrier. Provenance: issue **[org-internal #1695]** (which
|
||||
> corrected the misdiagnosed [org-internal #1691], see comment 9205). Code citations below were
|
||||
> verified against `<harness-package>` at HEAD.
|
||||
|
||||
## The `model` field 鈥?always provide a known-good model (read this first)
|
||||
|
||||
`prompt_async` is **fire-and-forget**: after HTTP returns 204 (or 202 when the
|
||||
message is queued behind a wedge/zombie session 鈥?no live runner in that
|
||||
process), the server runs the full agent loop in a separate fiber. **Always
|
||||
include an explicit `model`
|
||||
in the payload, and reuse the current session's model for it** (see the next
|
||||
section). Two reasons:
|
||||
|
||||
1. **Current code does not hard-require `model`.** The HTTP boundary marks
|
||||
`model` as optional (`<harness-package>/src/session/prompt.ts:1860`), and
|
||||
`createUserMessage` fills a missing model via a fallback chain
|
||||
(`prompt.ts:858`):
|
||||
|
||||
```ts
|
||||
const model = input.model ?? ag.model ?? yield * currentModel(input.sessionID)
|
||||
// ^^^^^^^^^ agent ^^^^^^^^^^^^^^^^^ session/default
|
||||
```
|
||||
|
||||
So omission does **not** produce a clean validation error 鈥?it silently
|
||||
resolves to the agent's model, the session's last-used model, or the
|
||||
provider default (`currentModel`, `prompt.ts:824-838`).
|
||||
|
||||
2. **But the fallback is not safe in the autonomous context.** If it resolves
|
||||
to a model that is unusable here (region-blocked, wrong subscription tier,
|
||||
not configured), the runLoop's `getModel` raises `ModelNotFoundError`
|
||||
(`prompt.ts:1399`, `:804-822`). In the **async** handler that error is caught
|
||||
by the delivery fork's failure handling (`forkPromptDelivery` in
|
||||
`handlers/session.ts`) and never reaches the HTTP
|
||||
caller 鈥?so the session looks healthy (HTTP 204, user message persisted) yet
|
||||
**never generates**. This is exactly the "silent stall" observed empirically
|
||||
in [org-internal #1695]: a session with a 1-message transcript (user only) and frozen
|
||||
`time.updated`.
|
||||
|
||||
| payload | result |
|
||||
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `{agent, parts}` 鈥?no `model` | 204/202; model filled by fallback; if the resolved model is unusable 鈫?**apparent silent stall** (no error surfaced, no generation). |
|
||||
| `{agent, model:{providerID,modelID}, parts}` | 204/202; deterministic generation within seconds (verified: "1+1 绛変簬 2銆? in ~6s). |
|
||||
|
||||
> **Iron Law:** every `prompt_async` payload MUST carry a **known-good** `model`,
|
||||
> and the safest known-good model is **the one driving this session right now**
|
||||
> (it is, by definition, generating). Never rely on the fallback chain in a
|
||||
> headless/autonomous launch.
|
||||
|
||||
**Field-name gotcha when copying a model:** the prompt payload's `ModelRef` uses
|
||||
`{ providerID, modelID }` (`prompt.ts:1852-1855`), but the **session record's**
|
||||
model uses `{ id, providerID, variant }` (`session.ts:256-260`) 鈥?i.e. `modelID`
|
||||
is called `id`. When you reuse a model read from a session record, remap
|
||||
`model.id 鈫?modelID` (see recipe below). User-message `info.model` already uses
|
||||
`modelID` (`message.ts:403-407`).
|
||||
|
||||
## Reuse the current session's model (recommended default)
|
||||
|
||||
The model powering the launching session is proven to work 鈥?reuse it. Discover
|
||||
it by whichever path fits your context:
|
||||
|
||||
- **You are an in-session agent** (most common): read it straight from your own
|
||||
system prompt, which states verbatim:
|
||||
|
||||
> "You are powered by the model named **{modelID}** (provider: **{providerID}**)."
|
||||
|
||||
e.g. `modelID = "glm-5.2"`, `providerID = "zai-coding-plan"`锛堟浘鍚?
|
||||
zhipuai-coding-plan锛?026-08 鏇村悕锛?
|
||||
|
||||
- **Programmatic / no system prompt** (cron, CI, sibling caller): `GET` the
|
||||
launching session's record and read `.model`:
|
||||
|
||||
```sh
|
||||
curl -s "$BASE/session/$LAUNCHER_SID?directory=$DIR" \
|
||||
| jq '.model' # 鈫?{ "id": "glm-5.2", "providerID": "zai-coding-plan", "variant": ... }
|
||||
```
|
||||
|
||||
Then **remap** `model.id 鈫?modelID` when building the payload.
|
||||
|
||||
- **Verify the provider is actually connected** before launching (cheap
|
||||
insurance 鈥?the model only generates if its provider is live):
|
||||
```sh
|
||||
curl -s "$BASE/provider?directory=$DIR" | jq '.connected | index("<current providerID>")'
|
||||
# 鈫?null = provider NOT connected (do NOT launch); 0..n = connected
|
||||
```
|
||||
or call the `list-models` tool. Note: model-level availability is only
|
||||
knowable at runtime 鈥?a _configured_ model can still fail (subscription-tier
|
||||
or region errors); `GET /config/providers` lists _configured_ models, not
|
||||
usable ones. The safest pre-flight is "provider connected" + reusing the
|
||||
current session's model, which is generating by definition. There is **no
|
||||
`GET /model` HTTP endpoint** (it 404s); do not use it.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A reachable octopus HTTP instance (e.g. `http://127.0.0.1:4096`).
|
||||
- The target directory (passed as `?directory=`). Defaults to the current
|
||||
session's directory for in-session launches 鈥?see the Directory convention
|
||||
above; only specify a different one explicitly when targeting another repo.
|
||||
- A known-good `model` (reuse the current session's 鈥?see above). Model IDs
|
||||
must match the provider config in `<config-home>/octopus/octopus.jsonc`;
|
||||
credentials live in the octopus account keyring (not env), so an isolated
|
||||
HOME cannot reuse them.
|
||||
|
||||
## The 3-step flow
|
||||
|
||||
Set the base URL and directory once:
|
||||
|
||||
```sh
|
||||
BASE=http://127.0.0.1:4096
|
||||
DIR=/data/octopus
|
||||
```
|
||||
|
||||
### Step 1 鈥?Create the session
|
||||
|
||||
```sh
|
||||
SID=$(curl -s -X POST "$BASE/session?directory=$DIR" \
|
||||
-H 'content-type: application/json' \
|
||||
-d '{"title":"#N 鈥?<short description>","agent":"builder"}' | jq -r .id)
|
||||
echo "session=$SID"
|
||||
```
|
||||
|
||||
- `POST /session` returns `Session.Info` with `.id` (`groups/session.ts:207-211`).
|
||||
- `?directory=` points at the repo root. For in-session launches it defaults to
|
||||
the **current session's directory** (Directory convention above) 鈥?keep it
|
||||
that way so the new session stays visible in the creator's sidebar; pass an
|
||||
explicit path only when targeting a different repo (a worktree of the same
|
||||
repo is NOT a directory target 鈥?hand it over via the seed prompt), and
|
||||
always pass it from cron / CI where no current session exists (server falls
|
||||
back to its own `process.cwd()` otherwise).
|
||||
- `agent` is `builder` (the main agent). Sub-agents (explorer/worker) are
|
||||
spawned within a session 鈥?never named here.
|
||||
|
||||
### Step 2 鈥?Deliver the seed prompt (CRITICAL: include a known-good `model`)
|
||||
|
||||
```sh
|
||||
curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
|
||||
-H 'content-type: application/json' \
|
||||
-d '{
|
||||
"agent": "builder",
|
||||
"model": { "providerID": "<current providerID>", "modelID": "<current modelID>" },
|
||||
"parts": [ { "type": "text", "text": "<seed prompt: restate goal, constraints, acceptance criteria, cite wiki artifact paths>" } ]
|
||||
}'
|
||||
```
|
||||
|
||||
- Endpoint is `POST /session/:sessionID/prompt_async`; it answers **204, no
|
||||
body** when processing starts now (idle session, live runner, noReply
|
||||
injection, or waiting-question preemption), **202** when the message is
|
||||
queued behind a session that is generating with no live runner in this
|
||||
process (wedge/zombie shape 鈥?stored and re-driven on restart drain or the
|
||||
next runner), **202** when `delay_sec` defers delivery to an in-memory
|
||||
delayed wake ([org-internal #3937] A 鈥?see "宸℃妯″紡 / Patrol mode" below), and **409**
|
||||
when the session is paused (message NOT stored) or the maintenance-mode
|
||||
hold queue is full.
|
||||
- **Global route alternative ([org-internal #4307]):** when the client only knows the
|
||||
sessionID (no `?directory=` routing), `POST /prompt_async` (no path prefix)
|
||||
resolves the instance from the app database by the body's `sessionID` and
|
||||
delegates to the same delivery core 鈥?same 204/202/409 semantics, same
|
||||
`delay_sec` contract, body is the session-scoped payload plus a required
|
||||
`sessionID` field. Unknown sessionID 鈫?404 naming the session-scoped
|
||||
route. This is the natural fit for the completion-report POST (fixed port,
|
||||
no directory context).
|
||||
- `delay_sec` (optional, integer 0..86400, else 400) schedules the prompt for
|
||||
a one-shot delayed self-wake: the server holds the prompt in memory and
|
||||
fires it exactly once after `delay_sec` seconds, only for an idle session
|
||||
at intake. **Precedence ([org-internal #3937] review):** `delay_sec` applies only when the
|
||||
intake would otherwise start processing now (immediate class). For the
|
||||
queued-behind-wedge 202 member and under maintenance queue mode it is
|
||||
stripped 鈥?retention semantics win (the server logs the drop; the delay is
|
||||
NOT honored). Pending wakes are **in-memory only 鈥?a server restart abandons
|
||||
them**; a patrol loop must therefore tolerate a missed wake and re-arm.
|
||||
Never use `sleep <big>; curl ...` in a bash tool call to wait 鈥?that blocks
|
||||
the turn with zero output (the [org-internal #3937] incident shape); the bash tool now
|
||||
warns on bare sleeps > 120s (long-sleep guard, [org-internal #3937] C).
|
||||
- `model` is `{ providerID, modelID }` or the shorthand string
|
||||
`"providerID/modelID"` (split on the FIRST `/`; accepted since [org-internal #4307] 鈥?
|
||||
e.g. `"zai-coding-plan/glm-5.2"`). **Do not omit it.** Fill the values from
|
||||
your own session's model (see "Reuse the current session's model" above) 鈥?
|
||||
never hardcode a specific model. A string without a `/` is rejected with
|
||||
400 naming both accepted forms.
|
||||
- When the seed prompt contains newlines/quotes, build the payload from a file
|
||||
to avoid shell-escaping errors:
|
||||
```sh
|
||||
jq -n --rawfile p seed.txt '{agent:"builder",model:{providerID:"<current providerID>",modelID:"<current modelID>"},parts:[{type:"text",text:$p}]}'
|
||||
```
|
||||
|
||||
### Step 3 鈥?Verify the session actually generated (do NOT assume success)
|
||||
|
||||
HTTP 204/202 鈮?generation success. Poll until an `assistant` message appears. The
|
||||
endpoint is **`/session/:id/message` 鈥?SINGULAR**, and it returns a bare array
|
||||
whose elements are `{ info, parts }` with `role` at `.info.role`
|
||||
(`groups/session.ts:88,183-194`). Poll the **tail with `limit=1`** 鈥?the
|
||||
no-`limit` form loads the ENTIRE transcript server-side (O(messages脳parts) per
|
||||
poll; pitfall #14's 478-message session paid it on every health check), while
|
||||
`limit=1` returns just the newest message (REQ-F-010 windowing, `message.ts`
|
||||
`page()`):
|
||||
|
||||
```sh
|
||||
# wait 5鈥?0s, then poll the TAIL 鈥?never the full transcript
|
||||
curl -s "$BASE/session/$SID/message?directory=$DIR&limit=1" \
|
||||
| jq '{latest_role: .[0].info.role, latest_id: .[0].info.id}'
|
||||
```
|
||||
|
||||
Verdict: `latest_role == "assistant"` 鈫?the first generation completed (the
|
||||
seed is `user`; the newest message flips to `assistant` only once the model
|
||||
answered 鈥?an errored turn also persists an assistant message carrying the 鈿狅笍
|
||||
usage-limit part, [org-internal #2912], so check the tail text before declaring success).
|
||||
Stays `user` across 2鈥? polls spaced 10鈥?0 s 鈫?**apparent silent stall**;
|
||||
almost certainly the model did not resolve to a usable one (fallback landed
|
||||
badly, or you omitted `model`). Redeliver Step 2 with an explicit known-good
|
||||
`model`. Need everything newer than a known point? Page forward with
|
||||
`after=<cursor>&limit=N` and follow the `X-Next-Cursor` response header 鈥?
|
||||
just never omit `limit`.
|
||||
|
||||
> 鈿狅笍 Do NOT write `/messages` (plural) 鈥?that path does not exist for listing.
|
||||
> (`POST /session/:id/message`, same singular path but POST, is the synchronous
|
||||
> prompt endpoint; don't confuse the two.)
|
||||
|
||||
## Launch a session for a specific ticket (#N)
|
||||
|
||||
The common case: an agent (or cron/CI) spins up a fresh main session to work a
|
||||
tracked issue 鈥?seed prompt composed **from the issue body**, launched on the
|
||||
current session's model. **Step-0 owner check is MANDATORY ([org-internal #1803])**: four data
|
||||
sources (session-title scan, assignee/claim, open PRs, remote branches); any
|
||||
live claim 鈫?ABORT the launch (`session-scope-guard.md` points here for that
|
||||
pre-step). Full recipe 鈥?owner-check scan script with `X-Total-Count` /
|
||||
`X-Has-More` pagination handling ([org-internal #3190]), claim-first atomic 3-step ([org-internal #2297],
|
||||
+ one-command provisioning via `script/claim-provision.sh`, [org-internal #3642]),
|
||||
seed-prompt MUSTs, backend duplicate-ticket hard guard ([org-internal #1989], on by default
|
||||
[org-internal #2350]), launch + poll commands: `reference/ticket-recipe.md` (read BEFORE
|
||||
creating any ticket-driven session).
|
||||
|
||||
## Completion-report protocol (瀹屾垚鍥炴姤鍗忚, [org-internal #2374])
|
||||
|
||||
`prompt_async` is fire-and-forget **for the launcher too**: without this
|
||||
protocol the orchestrator has NO push channel and must poll forever ([org-internal #2366]).
|
||||
The worker actively reports `status=done|blocked|handoff` back into the
|
||||
orchestrator's session via `prompt_async`, with an issue-comment fallback.
|
||||
Default report format is `branch=<ref>`-based 鈥?workers never open PRs
|
||||
(TD-678/[org-internal #4425]); `pr=#N` appears only on `uncoordinated` self-opens.
|
||||
Hard constraints: a headless worker MUST NOT call the `question` tool
|
||||
([org-internal #2378] 鈥?directional/irreversible decisions are `status=blocked` reports,
|
||||
then STOP), and MUST NOT self-merge its PR / self-close the issue / touch
|
||||
`main` ([org-internal #2386] 鈥?keep the PR open; the orchestrator merges and closes). Full
|
||||
protocol 鈥?orchestrator-side seed block (verbatim template), worker-side
|
||||
steps, decision-authority layering: `reference/completion-report.md` (read
|
||||
BEFORE composing a seed prompt for any worker you need to hear back from).
|
||||
|
||||
## Post-launch lifecycle: health check, hung rescue, stale-worldview correction ([org-internal #2459])
|
||||
|
||||
Launching is half the job 鈥?a headless worker can hang silently or wake with
|
||||
a stale worldview. Health check = two polls 30鈥?0 s apart (`.state` + `progress`
|
||||
+ message count); `state=generating` with ALL counters frozen across BOTH
|
||||
samples = hung 鈫?abort FIRST (`POST /session/:id/abort`), then re-wake with a
|
||||
known-good model + the anti-re-hang clause. Stale worldview 鈫?fact-baseline
|
||||
injection (only the CURRENT authority injects; if YOU might be the stale one,
|
||||
verify identity against the durable record first). Full runbooks 鈥?verdict
|
||||
table + [org-internal #3215] dual-sample rule, abort + re-wake sequence, fact-baseline
|
||||
structure, authorization asymmetry: `reference/lifecycle-ops.md` (read when
|
||||
managing a launched session). Quota wall 鈥?the worker's turn killed by a
|
||||
terminal usage-limit error (looks like silent completion: no report, ticket
|
||||
stalled) 鈥?has its own runbook: detection signals (finish=error 鈿狅笍 tail,
|
||||
provider quota markers), provider-switch re-wake, fact-baseline template,
|
||||
pre-dispatch provider check: `reference/quota-wall-recovery.md` ([org-internal #3669], evidence
|
||||
[org-internal #3627]; read when a worker goes silent mid-task).
|
||||
|
||||
## 宸℃妯″紡 / Patrol mode (delayed self-wake, [org-internal #3937])
|
||||
|
||||
A patrol loop periodically nudges a headless session without holding a
|
||||
connection or burning a turn on a blocking sleep. Since [org-internal #3937] A the server
|
||||
supports this natively via `delay_sec` on `prompt_async`.
|
||||
|
||||
```sh
|
||||
curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
|
||||
-H 'content-type: application/json' \
|
||||
-d '{
|
||||
"agent": "builder",
|
||||
"model": { "providerID": "<current providerID>", "modelID": "<current modelID>" },
|
||||
"parts": [ { "type": "text", "text": "<patrol instruction>" } ],
|
||||
"delay_sec": 3600
|
||||
}'
|
||||
```
|
||||
|
||||
Rules of the road:
|
||||
|
||||
- **202 + `delay_sec` 鈮?queued-behind-wedge 202.** Both are 202, but the
|
||||
delayed wake holds the prompt in memory and delivers it exactly once when
|
||||
the delay elapses (session must have been idle at intake; `0` equals
|
||||
omission 鈫?immediate 204 path). If the session was NOT idle at intake, the
|
||||
intake keeps the queued semantics and `delay_sec` is ignored (logged
|
||||
server-side) 鈥?the same precedence as maintenance queue mode, where the
|
||||
intake is retained and `delay_sec` never delays the flush.
|
||||
- **Paused at wake 鈫?dropped, not stored.** The wake's `prompt()` rejects and
|
||||
the server publishes an error event; the message is never stored. A patrol
|
||||
driver should check session state before assuming delivery.
|
||||
- **Restart abandons pending wakes.** They are in-memory server fibers 鈥?no
|
||||
persistence, no drain. After any server restart the patrol driver owns
|
||||
re-arming the next wake; treat a missed wake as expected, not as an error.
|
||||
- **Latest wake wins, per session ([org-internal #4069]).** Arming `delay_sec` on a session
|
||||
that already has a pending wake SUPERSEDES it: the previous countdown is
|
||||
cancelled, only the latest wake fires. Re-arming each turn is the intended
|
||||
patrol pattern 鈥?a superseded wake never delivers its prompt and publishes
|
||||
no error. (Pre-[org-internal #4069] stacks both FIFO 鈥?on old binaries, re-arm only after
|
||||
the previous wake fired.)
|
||||
- **Never emulate a delay with the bash tool.** `sleep 900; curl ...` blocks
|
||||
the turn with zero output and gets manually aborted as a hang (the [org-internal #3937]
|
||||
incident). The bash tool now prepends a `<shell_warning>` on bare sleeps
|
||||
beyond 120s ([org-internal #3937] C) 鈥?treat that warning as a redirect to this section.
|
||||
|
||||
## Do NOT use this skill when
|
||||
|
||||
- A **persistent client** (Web UI / interactive TUI) is attached 鈥?those use the
|
||||
synchronous `prompt` endpoint (it blocks until generation completes) and send
|
||||
`model` automatically. Using `prompt_async` there gains nothing and loses the
|
||||
in-line response. This includes the rescue runbook: a hung session driven by
|
||||
a persistent client is the client user's to handle (refresh / re-prompt from
|
||||
the UI) 鈥?abort + re-wake targets headless / managed sessions only.
|
||||
- You want the response **inline** in the same HTTP call 鈥?`prompt_async` is
|
||||
fire-and-forget; use the synchronous `prompt` endpoint instead.
|
||||
|
||||
## Common pitfalls
|
||||
|
||||
| # | Pitfall | Fix |
|
||||
| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 1 | Omitting `model` / relying on the fallback 鈫?apparent silent stall (most common) | Always send a known-good `model:{providerID,modelID}` 鈥?reuse the current session's |
|
||||
| 2 | Listing endpoint written `/messages` (plural) 鈫?404 | Use `/session/:id/message` (singular); returns a bare array, role at `.info.role` |
|
||||
| 3 | Copying the session record's model verbatim into the payload | Remap field names: session `.model.id` 鈫?payload `modelID` (session uses `id`, payload uses `modelID`) |
|
||||
| 4 | Stale `octopus` binary rejects newer config schema (`Unrecognized key`) | Use the source CLI `bun run ./src/index.ts ...`; the config itself is valid |
|
||||
| 5 | Missing `?directory=` query param | In-session launch: default to the current session's directory. Cron/CI (no current session): pass the repo root explicitly 鈥?server falls back to its own `process.cwd()` |
|
||||
| 6 | Using synchronous `prompt` for headless launch | `prompt` blocks until done 鈥?wrong for fire-and-forget; use `prompt_async` |
|
||||
| 7 | Shell-escaping errors in a multi-line seed prompt | Build the payload with `jq -n --rawfile` from a file |
|
||||
| 8 | Verifying models via `GET /model` 鈥?no such endpoint (404) | Use `GET /provider` 鈫?`.connected` (provider live check); `list-models` tool also works. `GET /config/providers` lists _configured_ models only |
|
||||
| 9 | Launching a session for a ticket another live session already owns 鈫?conflicting diffs, duplicate work ([org-internal #1744]/[org-internal #1753]) | Run the Step-0 owner check (`GET /session` title scan + worktree/branch/PR scan); ABORT and report on conflict |
|
||||
| 10 | Creating a worktree / fresh checkout for the new session (unnecessary `bun install` + `.codegraph/` rebuild cost) | Don't 鈥?for _general_ launches: a session is a conversation pointed at a directory, so reuse the current workspace; worktree creation is a separate _workflow_ decision the launched session makes if/when it starts a workflow. **Ticket-recipe carve-out ([org-internal #2297]):** the ticket's work happens in a dedicated worktree anyway (worktree discipline), and claim-first pushes the workflow branch before launch 鈥?so the launcher pre-builds the worktree as the claim carrier + ready workspace, and the session reuses it (cost is front-loaded, not wasted). The worktree path goes in the claim comment + seed prompt; `?directory=` stays on the creator's workspace |
|
||||
| 11 | Launching a worker and never hearing back 鈥?orchestrator polls a few times, stops, loses visibility ([org-internal #2374]) | Append the completion-report block to the seed prompt (Completion-report protocol section): worker `prompt_async`s a one-line status back into the orchestrator session on done/blocked/handoff, with issue-comment fallback |
|
||||
| 12 | Headless worker calls the `question` tool 鈫?no attached client: the question deadlocks in a queue, or `auto_approve` silently auto-approves a direction-setting decision ([org-internal #2378]) | NEVER call `question` from a headless worker. Decision boundary (Completion-report protocol section): directional/irreversible decision 鈫?`status=blocked` report + one-line decision point, then STOP; the orchestrator is the sole human-decision entry point |
|
||||
| 13 | Headless worker self-merges its PR / self-closes the issue once the PR looks ready, despite a seed instruction to keep it open ([org-internal #2386]; N-02 [org-internal #2367] / N-03 [org-internal #2368], commits `d1565c99` / `040f21b7`) | Self-merge / self-close / touching `main` are irreversible directional actions 鈫?blocked by the Decision boundary. PR ready 鈫?keep it OPEN, report `status=done branch=<ref>`; the orchestrator opens/admits the PR, merges and closes uniformly |
|
||||
| 14 | Trusting `state=generating` as "alive" 鈥?a hung stream goes unrescued (session A, 2026-08-16: `generating` 6+ min, count frozen at 478) | Poll state AND message count twice 30鈥?0 s apart; frozen count = hung 鈫?abort + re-wake (Post-launch lifecycle section) |
|
||||
| 15 | Re-sending a wake prompt to a hung session and waiting 鈥?queued prompts are never consumed while the stream is dead | Abort FIRST (`POST /session/:id/abort` 鈫?200, verify `idle`), THEN re-send; a prompt queued before the abort may still never fire 鈥?always re-deliver |
|
||||
| 16 | A worker woken from restart / long idle acts on its stale worldview 鈥?re-dispatches superseded work or claims authority it no longer holds (2026-08-16: revived worker re-ran an already-published FAIL verify; ops session announced an inherited "orchestrator" takeover) | Fact-baseline injection (Post-launch lifecycle section): authoritative facts + invalidated assumptions + one concrete re-assignment. Only the CURRENT authority injects; a session that suspects IT is stale verifies identity against the durable record first (`rules/compact.md` identity clause) |
|
||||
| 17 | Worker goes silent mid-task: provider quota wall killed the turn (usage limit / 429) 鈥?no completion-report, no blocked report; from the issue side indistinguishable from silent completion (2026-08-29 [org-internal #3627]: opencode-go `weekly usage limit reached` after the 13:18 Round-1 FAIL; ticket stalled until manually re-woken) | Detect via tail probe (`finish=error` + 鈿狅笍 usage-limit part, `metadata.reason` in the quota family) + `GET /provider` quota markers; re-wake on a known-good provider with the fact-baseline template 鈥?`reference/quota-wall-recovery.md` ([org-internal #3669]). Prevent: pre-dispatch provider check + the seed's 閰嶉鑷姤 clause (`reference/completion-report.md`, [org-internal #3669]) 鈥?quota exhaustion is a `status=blocked reason=quota-exhausted` report, not a silent death |
|
||||
|
||||
## Known limitation (separate from this workaround)
|
||||
|
||||
A missing `model` producing an **apparent silent stall** (no surfaced error, no
|
||||
generation) is an observability / robustness gap. Current code does NOT
|
||||
hard-require `model` 鈥?it fills it via a fallback chain (`prompt.ts:858`) and any
|
||||
resolution failure surfaces as a published `Session.Event.Error` on the
|
||||
synchronous path (`prompt.ts:804-822`), but the **async** `prompt_async` handler
|
||||
catches that cause (`forkPromptDelivery` in `handlers/session.ts`) so the HTTP
|
||||
caller never sees it. The proper fix is to surface the error (or reject unusable
|
||||
models at the boundary) so a stalled session is diagnosable. Until then, **always
|
||||
provide an explicit known-good `model`** 鈥?this skill is the documented
|
||||
workaround (see [org-internal #1695] "閬楃暀 minor").
|
||||
|
||||
**Update ([org-internal #2912], 2026-08-20)**: usage-limit turn failures (HTTP 429 /
|
||||
FreeUsageLimitError / GoUsageLimitError) are no longer silent shells. When such
|
||||
a turn terminates (retry schedule ends or the spin is aborted), the errored
|
||||
assistant message persists `finish=error` plus a human-readable 鈿狅笍 text part
|
||||
carrying provider, limit reason, and reset hint (`metadata: { error: true,
|
||||
reason, provider }`) 鈥?visible via `GET /session/:id/message` and rendered in
|
||||
the web UI like any assistant text. Pitfall #14's double-poll is still the
|
||||
liveness check while a 429 retry spin is IN progress (the spin itself retries
|
||||
with backoff until interrupted); the explicit error part only appears once the
|
||||
turn ends. The general async-path observability gap above remains tracked by
|
||||
[org-internal #1695].
|
||||
|
||||
## See also
|
||||
|
||||
- Source issue / full manual: **[org-internal #1695]** (this skill adds the ticket-driven recipe
|
||||
- current-model reuse, and corrects the endpoint/claim against current code).
|
||||
- Completion-report protocol: **[org-internal #2374]** (worker-side active report back to the
|
||||
launching orchestrator session; fallback to issue comment).
|
||||
- Quota-wall recovery recipe: **[org-internal #3669]** (evidence instance [org-internal #3627]) 鈥?detect a
|
||||
worker killed by a provider usage limit, re-wake on a known-good provider,
|
||||
pre-dispatch prevention.
|
||||
- Decision boundary: **[org-internal #2378]** (headless worker MUST NOT call `question`;
|
||||
directional decisions go through `status=blocked` reports to the
|
||||
orchestrator) and **[org-internal #2386]** (the worker MUST NOT self-merge its PR,
|
||||
self-close the issue, or touch `main` 鈥?the orchestrator merges and closes
|
||||
uniformly).
|
||||
- Correction of the prior misdiagnosis: **[org-internal #1691]** (closed Invalid), comment 9205.
|
||||
- Synchronous client always sends `model`:
|
||||
`packages/app/src/components/prompt-input/submit.ts:158-165`.
|
||||
- Code citations: `<harness-package>/src/session/prompt.ts:858,804-822,1399`,
|
||||
`handlers/session.ts` `forkPromptDelivery` (async failure handling),
|
||||
`server/routes/instance/httpapi/groups/session.ts`.
|
||||
|
||||
## References
|
||||
|
||||
**On-demand references** (NOT injected 鈥?read at the declared timing):
|
||||
|
||||
- `reference/ticket-recipe.md` 鈥?ticket-driven launch (`Launch a session for a
|
||||
specific ticket (#N)`): read BEFORE creating any session for issue #N 鈥?
|
||||
Step-0 owner check, seed-prompt composition, launch + poll commands.
|
||||
- `reference/completion-report.md` 鈥?completion-report protocol (瀹屾垚鍥炴姤鍗忚,
|
||||
[org-internal #2374]): read BEFORE composing a seed prompt for a worker you need to hear
|
||||
back from 鈥?orchestrator block template, worker-side report steps, decision
|
||||
boundary ([org-internal #2378] / [org-internal #2386]), quota self-report clause ([org-internal #3669]).
|
||||
- `reference/lifecycle-ops.md` 鈥?post-launch lifecycle ([org-internal #2459]): read when
|
||||
managing a launched session 鈥?health-check polling, hung-stream diagnosis
|
||||
(state=generating + frozen counters), abort + re-wake rescue,
|
||||
fact-baseline injection, authorization asymmetry.
|
||||
- `reference/quota-wall-recovery.md` 鈥?quota-wall recovery ([org-internal #3669], evidence
|
||||
[org-internal #3627]): read when a worker goes silent mid-task (suspected provider usage
|
||||
limit) or BEFORE dispatching a long-running worker 鈥?detection signals
|
||||
(finish=error 鈿狅笍 tail, provider quota markers, log grep patterns),
|
||||
provider-switch re-wake + fact-baseline template, pre-dispatch provider
|
||||
check, quota self-report clause.
|
||||
|
||||
- `rules/headless-session-ops` wiki page 鈥?authoritative reference (this
|
||||
skill is its runtime carrier; link in the Directory-convention note above).
|
||||
- `core/rules/session-scope-guard.md` 鈥?owner-check + claim-first rule
|
||||
(the ticket recipe's Step 0 is its launch-time application).
|
||||
- `core/rules/compact.md` 鈥?identity-verification clause backing the
|
||||
authorization-asymmetry rule (`reference/lifecycle-ops.md`).
|
||||
@@ -0,0 +1,173 @@
|
||||
> Extracted from headless-session-ops/SKILL.md (Completion-report protocol (完成回报协议, [org-internal #2374])) — moved verbatim 2026-08-26, ticket [org-internal #3480].
|
||||
|
||||
## Completion-report protocol (完成回报协议, [org-internal #2374])
|
||||
|
||||
`prompt_async` is fire-and-forget **for the launcher too**: once the worker
|
||||
session starts generating, the orchestrator receives NO event when it finishes
|
||||
or blocks. The `task` tool auto-returns `task_result` for subagents; a
|
||||
`prompt_async`-launched independent main session has no equivalent — the
|
||||
orchestrator's only built-in channel is bare polling of
|
||||
`GET /session/:id/message?limit=1` (tail probe — the no-`limit` form loads the
|
||||
whole transcript server-side), which it eventually stops doing ([org-internal #2366]: worker ran
|
||||
~1h past the orchestrator's last poll, merged PR [org-internal #2371] and closed [org-internal #2366] while
|
||||
the orchestrator had zero visibility).
|
||||
|
||||
The protocol below closes that gap WITHOUT changing server semantics: the
|
||||
**worker** actively posts a completion/block message back into the
|
||||
orchestrator's session via `prompt_async`. Fire-and-forget launch is
|
||||
unchanged; the report is a worker-side obligation, not a new server dependency.
|
||||
|
||||
### Orchestrator side — pass your SID in the seed prompt
|
||||
|
||||
When launching a worker that you need to hear back from (DAG node execution,
|
||||
parallel chunk work, any long-running delegated ticket), append a
|
||||
completion-report block to the seed prompt. It MUST state:
|
||||
|
||||
- the orchestrator's **session id** (`$LAUNCHER_SID`) and **directory**;
|
||||
- the report endpoint: `POST $BASE/session/$LAUNCHER_SID/prompt_async?directory=$DIR` (or, when the directory is unknown to the worker, the global route `POST $BASE/prompt_async` with the sessionID in the body — [org-internal #4307]);
|
||||
- the exact report format (one-line, machine-greppable);
|
||||
- the fallback (issue comment) if the report call fails.
|
||||
|
||||
Template (include verbatim in `seed.txt` when you are the launcher):
|
||||
|
||||
```text
|
||||
## 完成回报协议 (mandatory, [org-internal #2374])
|
||||
|
||||
你由编排会话 (orchestrator) 启动。完成、阻塞、或将工单移交他人时,必须向编排会话回报:
|
||||
POST <BASE>/session/<LAUNCHER_SID>/prompt_async?directory=<DIR>
|
||||
body: {"agent":"builder","model":{...},"parts":[{"type":"text","text":"<report>"}]}
|
||||
<report> 格式(单行,machine-greppable):
|
||||
[#<ticket>] [<node-or-slug>] status=done|blocked|handoff branch=<ref> [pr=#<N>] verify=<changed+typecheck> risk=<high|low> summary=<one line>
|
||||
默认(TD-678 / [org-internal #4425]):worker 只推分支不开 PR——交付推送前置本地验证
|
||||
(<harness-package> 下 test:changed 全绿 + typecheck 0 error,verify 字段回执);
|
||||
risk 按 risk-classifier 冻结表(HIGH_RISK_GLOBS)自报。PR 由编排按容量串行开
|
||||
1:1(正文携 worker 报告),或 batch 模式 epic 经 land-batch 技能按迭代统一合成
|
||||
(见 core/skills/land-batch/)。pr=#<N> 仅限编排不可达自开(uncoordinated)。
|
||||
若回报调用失败(HTTP 非 2xx / 连接拒绝 — orchestrator 会话可能已结束或实例重启),
|
||||
退而在源 issue(或父 Epic)发同内容评论作为兜底(fallback, 非主通道)。
|
||||
回报是你的 mandatory step,与 issue-checklist-sync 并列;不得静默结束。
|
||||
决策边界([org-internal #2378]):headless worker 禁止调用 `question` 工具 —— 遇方向性/不可逆
|
||||
决策点 → `status=blocked` + 一行决策点描述后 STOP,勿调 question(无人应答死锁 /
|
||||
被 auto_approve 静默自动批准)。
|
||||
配额自报([org-internal #3669]):若收到配额/用量上限错误(usage limit / 429 / 402
|
||||
insufficient_quota),立即回报 status=blocked reason=quota-exhausted
|
||||
summary=<provider 与 ⚠️ 部分中的重置提示>(branch=<ref> 必带)后停止
|
||||
——勿自行换模型重试;换供应商重唤醒由编排者执行
|
||||
(reference/quota-wall-recovery.md)。
|
||||
PR 就绪后保持 open:不自行合并 PR、不关闭 issue、不动 main([org-internal #2386]);
|
||||
合并与 issue 关闭由编排者统一执行。
|
||||
```
|
||||
|
||||
The orchestrator discovers its own `$LAUNCHER_SID` from context (it is the
|
||||
session the orchestrator IS — e.g. the SID it already used for the
|
||||
`GET /session/$LAUNCHER_SID` model lookup in the recipe above) or from its
|
||||
session record.
|
||||
|
||||
### Worker side — report on completion / block / handoff
|
||||
|
||||
A session whose seed prompt contains the completion-report block MUST, as its
|
||||
final step (before idling):
|
||||
|
||||
1. **Report** by `prompt_async` into the orchestrator session, with the
|
||||
one-line format above. Include `branch=<ref>` (plus `verify=`/`risk=` on
|
||||
done); `pr=#N` only for an `uncoordinated` self-open; use
|
||||
`status=blocked` + the blocker description when stuck; use
|
||||
`status=handoff` when deliberately transferring the ticket.
|
||||
1. **Quota exhaustion is a blocked report, not a silent death ([org-internal #3669]).** A
|
||||
worker whose turn is killed by a usage-limit error (429 / 402 quota
|
||||
family) cannot finish the turn — but per the seed's 配额自报 clause it
|
||||
reports `status=blocked reason=quota-exhausted summary=<provider +
|
||||
reset hint>` (with `branch=<ref>`) BEFORE stopping
|
||||
(issue-comment fallback if the orchestrator session is unreachable).
|
||||
The orchestrator then runs the provider-switch recovery in
|
||||
`reference/quota-wall-recovery.md` instead of discovering the wall by
|
||||
polling.
|
||||
2. **On failure** of that POST (non-2xx / connection refused — the
|
||||
orchestrator may have ended or the instance restarted), fall back to a
|
||||
`工单评论 API(见 TERMINOLOGY)` on the source issue / parent Epic carrying
|
||||
the same one-line report. The issue comment is the durable record of last
|
||||
resort, NOT the primary channel — the orchestrator does not watch issues in
|
||||
real time.
|
||||
3. The report is **additive** to existing obligations (issue checklist sync,
|
||||
`## 当前状态` updates, archive-at-close) — it never replaces them. The issue
|
||||
remains the stakeholder-facing record; the report is the
|
||||
orchestrator-facing wake-up signal.
|
||||
|
||||
### Orchestrator side — serial PR admission on done reports ([org-internal #4425])
|
||||
|
||||
On a `status=done branch=<ref> verify=… risk=…` report:
|
||||
|
||||
1. Enqueue the ticket; admit PRs serially per `ticket-lifecycle.md` §PR
|
||||
准入 — one open PR at a time, next only after double-green merge. Branch
|
||||
rot is bounded by the existing keep-mergeable/syncMain machinery, not new
|
||||
code; keep the queue shallow.
|
||||
2. Open the 1:1 PR (title from the ticket node, body carrying the worker
|
||||
report: branch / files / self-test / verify), apply the `Risk/*` label
|
||||
from the report's `risk=` hint, merge via `script/pr-merge.sh` ([org-internal #3864]).
|
||||
Batch-mode epics: one `land-batch` PR per iteration instead ([org-internal #3731]).
|
||||
3. A first red on the opened PR goes back through the report fallback
|
||||
channel (issue comment) — the worker may already be idle; the issue
|
||||
comment is the durable re-entry point.
|
||||
|
||||
Prose discipline on the orchestrator session only — no new daemon, no
|
||||
mergeable-state automation (merge-coordinator retired, [org-internal #4385]).
|
||||
|
||||
### Decision boundary (决策边界, [org-internal #2378])
|
||||
|
||||
The report channel defines how the worker *answers*; this subsection defines
|
||||
the boundary of what it may *ask*. A headless worker session has NO attached
|
||||
client (no Web UI / TUI), so calling the `question` tool has exactly two
|
||||
outcomes, both structurally broken:
|
||||
|
||||
- the question sits in a queue nobody answers → the session deadlocks;
|
||||
- or it is silently auto-approved by `auto_approve` → a direction-setting
|
||||
decision gets decided by a default value, violating the human-gate
|
||||
semantics of the review gates.
|
||||
|
||||
**Rule: a headless worker MUST NOT call the `question` tool.** Decision
|
||||
authority is layered instead:
|
||||
|
||||
- **Directional / irreversible decisions** (scope changes, mid-stream
|
||||
reclassification — big-bug relabel / DAG re-derivation, merge timing,
|
||||
contract changes, approach selection) → the
|
||||
worker reports `status=blocked` with a one-line description of the decision
|
||||
point, then STOPs. The orchestrator is the sole human-decision entry point:
|
||||
it interacts with the user, then replies to the worker (or acts on its
|
||||
behalf).
|
||||
- **No self-merge / no self-close / never touch main ([org-internal #2386])**: a headless
|
||||
worker MUST NOT merge its own PR, close the source issue, or rebase /
|
||||
force-push `main`. These are irreversible directional actions of exactly
|
||||
the class this boundary blocks — even when the seed prompt's instruction
|
||||
to "keep the PR open" is absent or the worker judges the PR ready. When
|
||||
the PR is ready the worker keeps it OPEN and reports
|
||||
`status=done pr=#N` to the orchestrator; the orchestrator performs the
|
||||
merge and the issue close uniformly. (Counter-example: N-02 [org-internal #2367] /
|
||||
N-03 [org-internal #2368] self-merged their PRs — commits `d1565c99` / `040f21b7` —
|
||||
despite a seed instruction to keep them open.)
|
||||
- **Local implementation decisions within the AC scope** (pattern choice,
|
||||
helper extraction, test shape) → the worker decides autonomously and notes
|
||||
the choice in its report / PR body.
|
||||
|
||||
Rule of thumb: the worker's only way to "ask" is a `status=blocked` report.
|
||||
A genuine ambiguity left by the seed prompt that would change scope is a
|
||||
blocked report, NOT a `question` call. (Field sample: N-04 [org-internal #2369] — the
|
||||
worker spontaneously did NOT merge its own PR and left merging to the
|
||||
orchestrator; this subsection codifies that behavior.)
|
||||
|
||||
### Semantics preserved
|
||||
|
||||
- **Fire-and-forget launch is unchanged**: Step 1–3 above are identical; the
|
||||
orchestrator still gets no server push at launch time.
|
||||
- **No server change**: the report reuses the existing `prompt_async` endpoint
|
||||
against the orchestrator's SID. Nothing new is required of the backend.
|
||||
- **Polling stays as fallback**: an orchestrator that never got a report can
|
||||
still poll `/session/:id/message`; the protocol removes the *need* to poll
|
||||
forever, not the ability.
|
||||
|
||||
### (Optional / future) server-side callback
|
||||
|
||||
Longer-term, `prompt_async` could accept a `callback_session_id` so the server
|
||||
itself posts an event to the callback session when the run loop terminates —
|
||||
removing the prompt-level manual protocol. That is a separate platform
|
||||
enhancement and intentionally NOT in this ticket's scope; the manual protocol
|
||||
above works today and remains compatible with a future automatic callback.
|
||||
@@ -0,0 +1,106 @@
|
||||
> Extracted from headless-session-ops/SKILL.md (Post-launch lifecycle: health check, hung rescue, stale-worldview correction ([org-internal #2459])) — moved verbatim 2026-08-26, ticket [org-internal #3480].
|
||||
|
||||
## Post-launch lifecycle: health check, hung rescue, stale-worldview correction ([org-internal #2459])
|
||||
|
||||
Launching is half the job — a headless worker can hang silently or wake up
|
||||
with an outdated worldview. All three failure modes below were observed in
|
||||
production on 2026-08-16 during Epic [org-internal #2459] (ops-restart window); the runbooks
|
||||
are extracted from that incident record.
|
||||
|
||||
### Health check — three states, one poll pattern
|
||||
|
||||
Poll two signals together — `GET /session?directory=` for `.state` (plus the
|
||||
per-session `progress` object when present), and `GET
|
||||
/session/:id/message?limit=1` for the newest message's `.info.id` — **twice,
|
||||
spaced 30–60 s**. `MessageID` is monotonic (`MessageID.ascending`,
|
||||
`message.ts`), so a frozen newest-id across both samples is exactly the old
|
||||
"message count frozen" signal at O(1) per poll instead of O(transcript) —
|
||||
never poll the no-`limit` form for liveness, it loads the whole transcript
|
||||
server-side:
|
||||
|
||||
| Verdict | Signals | Action |
|
||||
|---|---|---|
|
||||
| healthy | `state=generating` AND (`progress.stepCount` growing OR newest message id advancing) | leave it alone |
|
||||
| idle | `state=idle` AND `progress` absent-or-stale across both polls | turn ended — read the LAST message (same `limit=1` fetch): task unfinished → wake prompt; `status=done` report → harvest |
|
||||
| **hung** | `state=generating` AND `progress.stepCount`/`lastStepAt` AND newest message id ALL frozen across both polls | the generation stream is dead; queued prompts will NEVER be consumed — rescue required |
|
||||
|
||||
`state` alone lies in BOTH directions ([org-internal #3215]): a hung session still reports
|
||||
`generating` (field sample: session A, 2026-08-16 — `state=generating` for 6+
|
||||
minutes with the message count frozen at 478), and a healthy mid-step worker
|
||||
can read `idle`/empty message tails in a single snapshot (2026-08-23 W3 wave:
|
||||
4 healthy workers aborted off one snapshot). **The dual-sample delta is the
|
||||
discriminator; a single snapshot is NEVER an abort basis.**
|
||||
|
||||
`progress` (`stepCount` cumulative LLM-round counter, `lastStepAt` epoch-ms
|
||||
heartbeat — [org-internal #3215]) moves on every round even when message tails are
|
||||
transiently empty or `state` flickers; it is absent for sessions that never
|
||||
ran since instance start (treat absent = no signal, fall back to count
|
||||
deltas). `lastStepAt` freshness alone does NOT prove liveness (a long tool
|
||||
call inside one round keeps it stale for minutes) — always compare TWO
|
||||
samples spaced ≥30 s.
|
||||
|
||||
### Hung rescue — abort, then re-wake (in this order)
|
||||
|
||||
```sh
|
||||
# 1. Abort the dead stream (queued-but-unconsumed prompts do NOT unblock it)
|
||||
curl -s -X POST "$BASE/session/$SID/abort?directory=$DIR" -o /dev/null -w "%{http_code}\n" # → 200
|
||||
# 2. Verify idle
|
||||
curl -s "$BASE/session?directory=$DIR" | jq -r --arg s "$SID" '.[] | select(.id==$s) | .state' # → idle
|
||||
# 3. Check the worktree — the hung turn may have left uncommitted files (NOT lost)
|
||||
git -C <worktree> status --short
|
||||
# 4. Re-deliver the wake prompt (same Iron Law: known-good model).
|
||||
# A prompt queued BEFORE the abort may still never fire — always re-send.
|
||||
```
|
||||
|
||||
Endpoint: `POST /session/:sessionID/abort` (`groups/session.ts:113`,
|
||||
`handlers/session.ts:311`). Abort stops the run loop; filesystem writes the
|
||||
hung turn already made survive — inspect the worktree and list any recovered
|
||||
files IN the wake prompt so the worker re-validates them instead of redoing
|
||||
work (field sample: session A's hung turn had produced 3 src + 1 test file
|
||||
that its revived self adopted).
|
||||
|
||||
**Anti-re-hang clause — include in every wake prompt.** The trigger for the
|
||||
observed hang was a system-injected "请在适当的时机压缩当前会话" (compact at an
|
||||
appropriate time): the worker ended its turn after compacting, leaving the
|
||||
task half-done and idle. A wake prompt MUST carry, verbatim:
|
||||
|
||||
```text
|
||||
若系统再注入「请在适当的时机压缩当前会话」:执行压缩后立即在后续 turn 继续任务,
|
||||
绝不在任务未完成时以 idle 结束。
|
||||
```
|
||||
|
||||
### Stale worldview — fact-baseline injection (correct BEFORE it acts)
|
||||
|
||||
A worker woken after an ops restart / long idle carries the worldview it went
|
||||
to sleep with. It may re-dispatch superseded work, overwrite newer state, or
|
||||
claim authority it does not hold (field samples, 2026-08-16: a revived worker
|
||||
re-ran an M-01 verify another session had already published as FAIL —
|
||||
deduplicated via flag #comment-21590; an ops-notification session inherited an
|
||||
"orchestrator" identity from a compaction summary and announced a takeover —
|
||||
corrected via 勘误 #comment-21654/[org-internal #21668]).
|
||||
|
||||
When you detect a stale-worldview session, do not wait for it to finish being
|
||||
wrong — inject a fact-baseline prompt immediately (regular `prompt_async`),
|
||||
structured as:
|
||||
|
||||
1. **You were woken; your worldview is stale** — name the event (restart /
|
||||
maintenance window) and the current time.
|
||||
2. **Authoritative state** — numbered facts with artifact links (wiki page,
|
||||
issue comment), each with its timestamp; state explicitly which of the
|
||||
recipient's standing assumptions are now INVALID.
|
||||
3. **Your actual assignment now** — one concrete task (or explicit standby).
|
||||
4. **Evidence rule** — verify each fact at its cited source before acting;
|
||||
never act on this baseline alone.
|
||||
|
||||
(Field sample: the 2026-08-16 injection to session B pivoted it from the
|
||||
superseded M-01 verify to the N-04b fix within one turn — the format works.)
|
||||
|
||||
### Authorization asymmetry — read this BEFORE "correcting" anyone
|
||||
|
||||
Only the session that CURRENTLY holds the authority may inject a baseline or
|
||||
re-task a worker. If YOU might be the stale one — you woke from a restart,
|
||||
your context came from a compaction summary, you cannot find your claim in
|
||||
the durable record — assume YOU are stale: verify your identity/authority
|
||||
against the record (issue assignee, claim comment, orchestrator session id)
|
||||
BEFORE issuing any instruction. See the identity-verification clause in
|
||||
`core/rules/compact.md` (recovery contract).
|
||||
@@ -0,0 +1,226 @@
|
||||
> Added from headless-session-ops/SKILL.md (triggers `quota wall` / `配额墙`,
|
||||
> Post-launch lifecycle section, pitfall #17, References) — new runbook
|
||||
> 2026-08-29, ticket [org-internal #3669] (evidence instance [org-internal #3627]). Companion runbooks:
|
||||
> `lifecycle-ops.md` (hung rescue, fact-baseline structure) and
|
||||
> `completion-report.md` §"Orchestrator side" (the quota self-report clause);
|
||||
> §Prevention is this file's own closing section.
|
||||
|
||||
## Quota wall — worker killed mid-task by a provider usage limit
|
||||
|
||||
A **quota wall** is when a headless worker's turn is killed by a terminal
|
||||
provider-quota error (usage limit / 429 family) mid-task. From the issue side
|
||||
it looks EXACTLY like silent completion: no completion-report, no
|
||||
`status=blocked` report, the ticket just stops moving. The stalled window
|
||||
equals the orchestrator's polling interval, and without this runbook the
|
||||
recovery was improvised.
|
||||
|
||||
**Evidence instance ([org-internal #3627], 2026-08-29)** — the shapes below are field-verified:
|
||||
|
||||
|时刻 (+08)| 事件 |
|
||||
|---|---|
|
||||
| 08-29 13:18 | review-code Round-1 synthesis (FAIL) posted on [org-internal #3627]; worker session `ses_fb46e705bffe9kYbSoHayoJ1OQ` (builder, opencode-go model, branch `workflow/session/3627-wedge-promptasync`) entered its revision loop |
|
||||
| ~13:18–16:00 | worker hit the provider's `weekly usage limit reached` mid-revision — turn killed, **no** completion-report, **no** blocked report; issue side silent |
|
||||
| detection | orchestrator noticed the `/session` state stall, then confirmed via the quota error in the session log (message tail carries the ⚠️ usage-limit part, [org-internal #2912]) |
|
||||
| recovery | re-wake via `prompt_async` on a known-good provider (`zhipuai-coding-plan`/glm-5.3 — since renamed `zai-coding-plan`) **plus a fact-baseline injection** (what happened / what is done / where to resume) |
|
||||
| 16:26 / 16:51 | Round-2 synthesis PASS; `status=done pr=[org-internal #3675]` report received |
|
||||
|
||||
## Detection — three signals, in this order
|
||||
|
||||
Set the usual variables first:
|
||||
|
||||
```sh
|
||||
BASE=http://127.0.0.1:4096 # dev backend (:4180 prod)
|
||||
DIR=/data/octopus
|
||||
SID=<worker session id>
|
||||
```
|
||||
|
||||
**Signal 1 — tail probe (primary; survives restarts, lives in the DB).** Since
|
||||
[org-internal #2912]/[org-internal #3190] a quota-killed turn persists an assistant message with
|
||||
`finish=error` plus a human-readable ⚠️ text part whose metadata names the
|
||||
quota family. Probe the TAIL (`limit=1`, never the no-`limit` full transcript):
|
||||
|
||||
```sh
|
||||
curl -s "$BASE/session/$SID/message?directory=$DIR&limit=1" | jq '
|
||||
{role: .[0].info.role, finish: .[0].info.finish,
|
||||
error_parts: [.[0].parts[]? | select(.type=="text" and .metadata?.error == true)
|
||||
| {text: .text[0:160], metadata: .metadata}]}'
|
||||
```
|
||||
|
||||
Quota-wall verdict: `role=assistant` AND `finish=error` AND an error part with
|
||||
`metadata.reason` in `{account_rate_limit, free_tier_limit, account_usage_limit,
|
||||
rate_limit}` (metadata also carries `provider`). The ⚠️ text is one of — all
|
||||
strings observed in this repo's runtime (`session/retry.ts`):
|
||||
|
||||
- `` `weekly usage limit` reached. It will reset in … `` / `Go limit reached`
|
||||
(GoUsageLimitError — the [org-internal #3627] shape; `weekly` is the body's `limitName`)
|
||||
- `Free limit reached` (FreeUsageLimitError)
|
||||
- `Account-level usage limit — switching API keys does not reset it. You can
|
||||
continue after HH:mm.` ([org-internal #3407], zhipu/zai code 1308 — account-level)
|
||||
- `Provider <id> rate limited (HTTP 429)` (generic 429)
|
||||
- `Generation failed — …` with a 402 `insufficient_quota` body (balance, not
|
||||
window — recovery is top-up, not provider switch)
|
||||
|
||||
**Signal 2 — state (two shapes; use the lifecycle-ops dual-sample rule).**
|
||||
|
||||
```sh
|
||||
curl -s "$BASE/session?directory=$DIR" | jq -r --arg s "$SID" \
|
||||
'.[] | select(.id==$s) | .state'
|
||||
```
|
||||
|
||||
- **idle** + Signal-1 tail → the turn already died on the quota error. No
|
||||
abort needed; go straight to Recovery.
|
||||
- **generating** with `progress`/newest-message-id frozen across two polls
|
||||
30–60 s apart → the 429 retry spin is still running (it retries with backoff
|
||||
until the schedule ends) OR the stream is hung — treat exactly like the hung
|
||||
shape in `lifecycle-ops.md`: abort FIRST, then re-wake. A single snapshot is
|
||||
never an abort basis ([org-internal #3215]).
|
||||
|
||||
**Signal 3 — provider-level confirmation ([org-internal #2911] quota markers).** `GET
|
||||
/provider` overlays active usage-limit cooldowns, so the dead provider is
|
||||
visible without reading logs:
|
||||
|
||||
```sh
|
||||
curl -s "$BASE/provider?directory=$DIR" | jq '[.all[] | select(.quota) | {id, quota}]'
|
||||
# quota: {markedAt: <epoch-ms>, resetAt?: <epoch-ms>} — resetAt absent = reset unknown
|
||||
date -d @$(($(curl -s "$BASE/provider?directory=$DIR" | jq '[.all[].quota.resetAt // 0] | max') / 1000)) # earliest full-reset wall clock
|
||||
```
|
||||
|
||||
**Log grep (fallback; the dev log is restart-truncated).** Patterns actually
|
||||
seen in the wild — [org-internal #3627] produced the first:
|
||||
|
||||
```sh
|
||||
grep -E "usage limit reached|Go limit reached|Free limit reached|Account-level usage limit|GoUsageLimitError|FreeUsageLimitError|rate limited \(HTTP 429\)|insufficient_quota" \
|
||||
/tmp/octopus-backend.log | tail -20
|
||||
# the pool-failover WARN precedes the wall when same-name pools exist:
|
||||
# "Usage limit reached on <provider> — retrying <model> on <provider>"
|
||||
```
|
||||
|
||||
The wall is only terminal after failover has exhausted the same-name pools
|
||||
([org-internal #2911] failover); the `retrying … on …` WARNs above tell you the pool was
|
||||
already draining before it died.
|
||||
|
||||
## Recovery — abort (only if needed), re-wake on a known-good provider, inject facts
|
||||
|
||||
**Step 0 — classify the state (Signal 2).** `idle` → skip abort.
|
||||
`generating` + frozen across dual samples → abort first, exactly per
|
||||
`lifecycle-ops.md` (queued prompts are never consumed by a dead stream):
|
||||
|
||||
```sh
|
||||
curl -s -X POST "$BASE/session/$SID/abort?directory=$DIR" -o /dev/null -w "%{http_code}\n" # → 200
|
||||
curl -s "$BASE/session?directory=$DIR" | jq -r --arg s "$SID" '.[] | select(.id==$s) | .state' # → idle
|
||||
```
|
||||
|
||||
**Step 1 — inventory surviving work.** The killed turn's filesystem writes
|
||||
survive; list them so the worker re-validates instead of redoing:
|
||||
|
||||
```sh
|
||||
git -C <worktree> status --short
|
||||
git -C <worktree> log --oneline -3
|
||||
```
|
||||
|
||||
**Step 2 — pick a known-good provider/model.** The re-wake payload MUST carry
|
||||
an explicit `model` (SKILL.md Iron Law), and after a quota wall the CURRENT
|
||||
provider is by definition NOT known-good. Pick a provider that is connected
|
||||
AND not quota-marked:
|
||||
|
||||
```sh
|
||||
curl -s "$BASE/provider?directory=$DIR" | jq -r \
|
||||
'.connected as $c | [.all[] | select(.id as $id | $c | index($id)) | select(.quota | not) | .id] | join("\n")'
|
||||
# then pick a modelID you know works on that provider (e.g. the orchestrator's
|
||||
# own current model — it is generating by definition).
|
||||
```
|
||||
|
||||
([org-internal #3627]: opencode-go dead → re-woken on zhipuai-coding-plan/glm-5.3, since
|
||||
renamed `zai-coding-plan`. Do NOT "wait for reset" as the default — weekly
|
||||
windows mean days.)
|
||||
|
||||
**Empty list — every connected provider is quota-marked.** The loop needs an
|
||||
explicit exit; never re-wake into a wall you can see. Find the earliest reset
|
||||
among the marked connected providers:
|
||||
|
||||
```sh
|
||||
curl -s "$BASE/provider?directory=$DIR" | jq -r \
|
||||
'.connected as $c
|
||||
| [.all[] | select(.id as $id | $c | index($id)) | select(.quota)]
|
||||
| min_by(.quota.resetAt // 9007199254740991) # resetAt absent → sorts last
|
||||
| "\(.id): markedAt=\(.quota.markedAt) resetAt=\(.quota.resetAt // "unknown")"'
|
||||
# resetAt is epoch-ms → wall clock: date -d @$((<resetAt> / 1000))
|
||||
```
|
||||
|
||||
- Known `resetAt` within reach → suspend the re-wake (leave the worker down)
|
||||
and re-run this Step after the reset; weekly windows mean days — schedule
|
||||
the retry, do not poll in a tight loop.
|
||||
- `resetAt` unknown, or the earliest window is unacceptable (402
|
||||
`insufficient_quota` is balance — reset means top-up, not time) →
|
||||
**escalate to the human orchestrator**: report every marked provider's
|
||||
`id`/`markedAt`/`resetAt` and stop. Top-up or onboarding a new provider is
|
||||
a human decision, not another re-wake.
|
||||
|
||||
**Step 3 — re-wake with a fact-baseline prompt.** Build the payload from a file
|
||||
(`jq --rawfile`, SKILL.md pitfall #7), same wake shape as
|
||||
`lifecycle-ops.md` but with the quota facts. The template also restates the
|
||||
completion-report protocol ([org-internal #2374]) so the recovery is self-contained: it
|
||||
assumes the original seed still contains the protocol block — if you are
|
||||
unsure it does (e.g. the worker was dispatched before [org-internal #3669] added the quota
|
||||
clause), the template's own block below carries it; fill
|
||||
`<BASE>`/`<LAUNCHER_SID>`/`<DIR>` with the orchestrator session's values:
|
||||
|
||||
```sh
|
||||
cat > wake.txt <<'EOF'
|
||||
## 事实基线(配额墙恢复)
|
||||
你上一回合被供应商配额错误杀死,世界观停留在被杀时刻。先读以下事实再行动:
|
||||
1. 发生了什么:<HH:mm> 你的回合因 <provider> 配额耗尽终止(usage limit,
|
||||
预计重置 <HH:mm 或 未知>);本消息由已知良好供应商重新唤醒,非你自行恢复。
|
||||
2. 已做什么:<已完成步骤/commit/PR 状态>;worktree 未提交变更:
|
||||
<git status --short 摘要>——先 re-validate 这些文件,勿重做已完成工作。
|
||||
3. 从哪继续:<一件具体的下一步任务>。
|
||||
4. 证据规则:逐条到引用来源(issue 评论 / PR / 工件)核实后再行动,
|
||||
勿仅凭本基线行动。
|
||||
## 完成回报协议(重申 [org-internal #2374]/[org-internal #3669];不确定原始 seed 是否含协议块时以本节为准)
|
||||
完成、阻塞或移交时,必须回报编排会话(你的唤醒者):
|
||||
POST <BASE>/session/<LAUNCHER_SID>/prompt_async?directory=<DIR>
|
||||
body: {"agent":"builder","model":{...},"parts":[{"type":"text","text":"<report>"}]}
|
||||
<report> 单行格式:[#<ticket>] [<node-or-slug>] status=done|blocked|handoff pr=#<N> summary=<一行>
|
||||
约束:若再遇配额/用量上限错误,立即回报 status=blocked
|
||||
reason=quota-exhausted summary=<provider 与重置提示>(pr=#<N> 有 PR 时带上)
|
||||
后停止,勿自行换模型重试;回报失败 → 源 issue 评论兜底。
|
||||
EOF
|
||||
jq -n --rawfile p wake.txt '{agent:"builder",
|
||||
model:{providerID:"<known-good providerID>",modelID:"<known-good modelID>"},
|
||||
parts:[{type:"text",text:$p}]}' \
|
||||
| curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
|
||||
-H 'content-type: application/json' -d @- -o /dev/null -w "%{http_code}\n" # → 204
|
||||
```
|
||||
|
||||
Then poll the tail (`limit=1`) until an assistant message appears, and keep
|
||||
expecting the completion-report per protocol — the re-woken worker owes you
|
||||
`status=done|blocked` like any other (the restated block in the template
|
||||
makes this hold even when the original seed predates [org-internal #3669]).
|
||||
|
||||
## Prevention — before dispatch, and in the seed
|
||||
|
||||
**Pre-dispatch provider check (30 seconds, catches most walls):**
|
||||
|
||||
```sh
|
||||
# 1) no active quota markers on the dispatch provider (else pick another)
|
||||
curl -s "$BASE/provider?directory=$DIR" | jq '[.all[] | select(.quota) | .id]'
|
||||
# 2) dispatch provider is connected AND ≥1 other connected provider exists as fallback
|
||||
curl -s "$BASE/provider?directory=$DIR" | jq '{connected, fallbacks: (.connected | length > 1)}'
|
||||
```
|
||||
|
||||
If the intended provider is already quota-marked, dispatch on a different one
|
||||
— do not launch into a wall you can see coming.
|
||||
|
||||
**Quota self-report clause (turns a silent death into a harvestable
|
||||
blocked).** The canonical clause text lives in `completion-report.md`
|
||||
§"Orchestrator side" (配额自报, [org-internal #3669]) — append it VERBATIM to the seed
|
||||
prompt's completion-report block. Single source of truth: do not fork or
|
||||
restate the clause here; its blocked payload already follows the one-line
|
||||
key=value spec (`status=blocked reason=quota-exhausted summary=<provider +
|
||||
reset hint> pr=#N`, completion-report.md `<report>` format).
|
||||
|
||||
With the clause in the seed, a worker that hits the wall reports `blocked`
|
||||
instead of dying silently — the orchestrator harvests the report and runs the
|
||||
Recovery section directly. The clause is also why the re-wake prompt's
|
||||
constraint (Step 3) repeats it: the re-woken worker must know the rule still
|
||||
holds on the new provider.
|
||||
@@ -0,0 +1,182 @@
|
||||
> Extracted from headless-session-ops/SKILL.md (Launch a session for a specific ticket (#N)) — moved verbatim 2026-08-26, ticket [org-internal #3480].
|
||||
|
||||
## Launch a session for a specific ticket (#N)
|
||||
|
||||
The common case: an agent (or cron/CI) needs to spin up a fresh main session to
|
||||
work a tracked issue. Compose the seed prompt **from the issue itself** so the
|
||||
new session starts with real context, and launch it on **the current session's
|
||||
model**.
|
||||
|
||||
0. **Owner check (MANDATORY pre-step, [org-internal #1803]).** Before creating anything,
|
||||
verify no other live session already owns this ticket or code area —
|
||||
duplicate ownership is how [org-internal #1744]/[org-internal #1753] collided. The check spans four
|
||||
data sources; any live claim → **ABORT the launch and report the
|
||||
conflict**.
|
||||
|
||||
**Mechanized ([org-internal #3667])**: `bash script/session-conflicts.sh <N>` runs the
|
||||
session-title scan (a), the branch scan (d), and the open-PR check (c)
|
||||
in one read-only pass (exit 4 = conflict, `--json` for a machine
|
||||
summary); the assignee/claim check (b) is `claim.sh`'s compare-and-swap
|
||||
at claim time. The manual recipes below remain the fallback and the
|
||||
normative definition (`core/rules/session-scope-guard.md`).
|
||||
|
||||
**a. Session-title scan (live session check).** The listing endpoint
|
||||
returns at most `limit` (default 100) sessions — on busy repos (300+ live
|
||||
sessions in one directory) that silently truncates and the scan misses
|
||||
owners ([org-internal #3190]). Non-range listing responses carry `X-Total-Count` (full
|
||||
filter population, untruncated) and `X-Has-More: true` (only when the
|
||||
page is truncated). Scan procedure: fetch page 1, and when
|
||||
`X-Has-More: true` is present re-fetch once with `limit=$TOTAL` — treat
|
||||
any failure to obtain the full population as "more owners may exist"
|
||||
(ABORT or widen the scan — never assume the first page is the whole
|
||||
population).
|
||||
|
||||
```sh
|
||||
# N must be the digits-only issue number (e.g. N=1803) — never interpolate
|
||||
# raw issue text here; the regex below assumes digits.
|
||||
[[ "$N" =~ ^[0-9]+$ ]] || { echo "N must be digits only"; exit 1; }
|
||||
HDR=$(mktemp)
|
||||
BATCH=$(curl -sD "$HDR" "$BASE/session?directory=$DIR")
|
||||
TOTAL=$(awk -F': ' 'tolower($1)=="x-total-count"{print $2}' "$HDR" | tr -d '\r')
|
||||
if [[ "$TOTAL" =~ ^[0-9]+$ ]] && awk -F': ' 'tolower($1)=="x-has-more"{print $2}' "$HDR" | grep -q true; then
|
||||
# truncated page + known population — refetch the full set in one request
|
||||
BATCH=$(curl -s "$BASE/session?limit=$TOTAL&directory=$DIR")
|
||||
elif awk -F': ' 'tolower($1)=="x-has-more"{print $2}' "$HDR" | grep -q true; then
|
||||
# truncated page but no usable X-Total-Count (older server) — the
|
||||
# population is unknown; do NOT re-fetch with the same default limit
|
||||
# (it would silently re-truncate). Escalate instead.
|
||||
echo "ERROR: session listing truncated but X-Total-Count unavailable — cannot establish full owner population" >&2
|
||||
rm -f "$HDR"; exit 1
|
||||
fi
|
||||
rm -f "$HDR"
|
||||
echo "scanned $(echo "$BATCH" | jq 'length') of ${TOTAL:-?} sessions in $DIR" >&2
|
||||
echo "$BATCH" | jq -r --arg n "$N" '.[] | select(.title | test("#" + $n + "([^0-9]|$)")) | "\(.id)\t\(.title)\t\(.time.updated)"'
|
||||
```
|
||||
|
||||
- Any hit whose `time.updated` is recent (session still active) → **ABORT
|
||||
the launch and report the conflict** (issue comment naming the owning
|
||||
session id). Do not launch a second session for the same ticket.
|
||||
|
||||
**b. Assignee / claim check ([org-internal #2297]).** Read the issue via
|
||||
`工单 API(见 TERMINOLOGY)get(owner, repo, index: N)` (or `GET /api/v1/repos/$OWNER/$REPO/issues/$N`):
|
||||
- If `assignee` is set and is **not** the launching agent → **ABORT** and
|
||||
report (the ticket is already claimed).
|
||||
- If a claim comment names a **different branch / session id** → **ABORT**
|
||||
and coordinate on the issue before proceeding.
|
||||
|
||||
**c. Open-PR check.** List open PRs referencing `#N` —
|
||||
`工单 API(见 TERMINOLOGY)search(q: "#N", type: "pulls", state: "open")` (API source)
|
||||
or `gitea_pull__list` — and **ABORT** if an open PR already covers the ticket.
|
||||
|
||||
**d. Remote branch check ([org-internal #2297]).** A local branch is invisible to other
|
||||
sessions — check the remote too:
|
||||
`git ls-remote origin 'workflow/*'` and scan for a branch tail covering `#N`.
|
||||
Also check the repo side locally: `git worktree list` +
|
||||
`git branch --list 'workflow/*'` for a branch / worktree already covering #N.
|
||||
|
||||
- A stale hit (session idle for hours / clearly abandoned) → do NOT
|
||||
hand-post a takeover comment (retired admin workaround — unauditable);
|
||||
dead-claim takeover is mechanized ([org-internal #3668]): `bash script/claim.sh
|
||||
takeover <ticket> <branch> --session-id <id>`. It enforces the
|
||||
evidence gate (branch ABSENT on origin AND (session 404 OR comment at
|
||||
least CLAIM_TAKEOVER_STALE_DAYS old)) and exits 10 when the evidence
|
||||
does not hold — a valid claim is never superseded; an idle-but-alive
|
||||
session is NOT provably dead, coordinate on the issue instead (exit 4
|
||||
= owned/conflict, in claim.sh and session-conflicts.sh alike).
|
||||
Evidence rules: `core/rules/session-scope-guard.md`
|
||||
§"Dead-claim takeover" (claim.sh header is normative).
|
||||
The runtime counterpart of this check is `core/rules/session-scope-guard.md`
|
||||
("One task, one owner"), injected into every session's prompt.
|
||||
- **Backend hard guard ([org-internal #1989]):** `POST /session` hard-rejects duplicate-ticket
|
||||
session creation with HTTP 400. The backend guard is **on by default ([org-internal #2350])**;
|
||||
set `enabled: false` in config to opt out. It fails open ONLY on defect
|
||||
paths (DB / config errors), never as a configured disable. The front-end
|
||||
soft check above is now backed by this backend hard check for defense in
|
||||
depth.
|
||||
- **Claim-first ([org-internal #2297]).** Claiming a ticket is one atomic 3-step action:
|
||||
(1) set the issue assignee to the working account, (2) post a claim comment
|
||||
naming the workflow branch and session id, (3) push the workflow branch to
|
||||
remote (`git push -u origin workflow/...`). The claim is valid ONLY when all
|
||||
three steps complete — an incomplete claim is NOT a claim. Do NOT launch the
|
||||
session until all three steps are done (see
|
||||
`core/rules/session-scope-guard.md` §"Claim-first"). If a collision HAS
|
||||
already happened, follow `rules/ownership-collision-runbook` (wiki, L2 on-demand).
|
||||
- **Provisioning after claim ([org-internal #3642]) is ONE command** — claim + worktree +
|
||||
Tier-1 runs scaffold:
|
||||
`bash script/claim-provision.sh <N> workflow/<branch>` (runs claim.sh with
|
||||
exit codes passed through, attaches the session worktree to the claimed
|
||||
branch via `session-worktree.sh create <slug> --branch <branch>`, then
|
||||
scaffolds `<runs-root>/{slug}/` via `<harness-package>/script/runs-init.ts`
|
||||
with kind-aware exemptions; prints one JSON summary; idempotent re-runs).
|
||||
Pitfall #10's pre-built worktree is exactly what it produces — put the
|
||||
returned worktree path in the claim comment + seed prompt.
|
||||
|
||||
1. **Read the issue.** Use the REST API (`gitea-rest` skill; `gitea-mcp` is retired):
|
||||
|
||||
```sh
|
||||
curl -s "$GITEA/api/v1/repos/$OWNER/$REPO/issues/$N" | jq '{title, body}'
|
||||
```
|
||||
|
||||
or `工单 API(见 TERMINOLOGY)get(owner, repo, index: N)`.
|
||||
|
||||
2. **Compose the seed prompt** from the issue body. The seed MUST, at minimum:
|
||||
- restate the **goal** in one sentence;
|
||||
- list the **constraints** and **acceptance criteria**;
|
||||
- cite every **wiki artifact path** / related issue referenced in the body
|
||||
(e.g. `{epic-slug}/dag`, `{epic-slug}/dag-nodes/{node-id}`, `[org-internal #1691]`);
|
||||
- tell the new agent to follow the right pipeline skill for the work type
|
||||
(implement / bugfix / design / …).
|
||||
Write it to a file (handles newlines):
|
||||
|
||||
```sh
|
||||
cat > seed.txt <<'EOF'
|
||||
Work issue #N: <one-line title>.
|
||||
Goal: <…>.
|
||||
Constraints: <…>.
|
||||
Acceptance: <…>.
|
||||
Artifacts: <wiki paths from the issue body>.
|
||||
Follow the `implement` skill (Mode: bugfix) for this.
|
||||
Session scope guard (mandatory): if a test fails and it was NOT caused by
|
||||
your change — classify (pre-existing/flaky), file a BF/FT issue per
|
||||
core/rules/session-scope-guard.md, and continue this task. Do NOT fix
|
||||
unrelated failing tests in place.
|
||||
|
||||
## 完成回报协议 (mandatory, [org-internal #2374])
|
||||
|
||||
完成、阻塞、或移交时,向编排会话回报:
|
||||
POST <BASE>/session/<LAUNCHER_SID>/prompt_async?directory=<DIR>
|
||||
(编排目录未知时可用全局路由 POST <BASE>/prompt_async,body 携 sessionID,[org-internal #4307])
|
||||
body: {"agent":"builder","model":{...},"parts":[{"type":"text","text":"<report>"}]}
|
||||
<report>: [#N] [<node-or-slug>] status=done|blocked|handoff branch=<ref> [pr=#<PR>] verify=<changed+typecheck> risk=<high|low> summary=<one line>
|
||||
约束:worker 只推分支不开 PR(PR 由编排串行开);交付推送前 test:changed+typecheck 须绿。若再遇配额/用量上限错误,立即回报 status=blocked reason=quota-exhausted summary=<provider 与重置提示>(branch=<ref> 必带)后停止,勿自行换模型重试。
|
||||
回报失败(非 2xx / 连接拒绝)→ 在本 issue(或父 Epic)发同内容评论兜底。
|
||||
决策边界([org-internal #2378]):遇方向性决策点 → status=blocked 回报,勿调 question。
|
||||
EOF
|
||||
```
|
||||
|
||||
3. **Determine the known-good model** (reuse the current session's — see the
|
||||
"Reuse the current session's model" section).
|
||||
|
||||
4. **Run the 3-step flow** with `title:"#N — <short>"` and the file-built
|
||||
payload. Read the model from the launcher session's own record — this also
|
||||
applies the `model.id → modelID` remap from the "Reuse" section:
|
||||
|
||||
```sh
|
||||
MODEL=$(curl -s "$BASE/session/$LAUNCHER_SID?directory=$DIR" \
|
||||
| jq -c '.model | {providerID, modelID: .id}')
|
||||
SID=$(curl -s -X POST "$BASE/session?directory=$DIR" \
|
||||
-H 'content-type: application/json' \
|
||||
-d "{\"title\":\"#$N — <short>\",\"agent\":\"builder\"}" | jq -r .id)
|
||||
PAYLOAD=$(jq -n --argjson m "$MODEL" --rawfile p seed.txt \
|
||||
'{agent:"builder",model:$m,parts:[{type:"text",text:$p}]}')
|
||||
curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
|
||||
-H 'content-type: application/json' -d "$PAYLOAD"
|
||||
```
|
||||
|
||||
5. **Poll** Step 3 until an `assistant` message appears — bounded tail poll
|
||||
(`&limit=1`), never the no-`limit` full-transcript form. Record `$SID` somewhere
|
||||
durable (issue comment, CI log) so the session is traceable to the ticket.
|
||||
|
||||
6. **Add the completion-report block to the seed prompt** (see next section) so
|
||||
the worker reports back when it finishes or blocks — without it, this
|
||||
orchestrator has no push channel and must poll forever ([org-internal #2374]).
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
name: image-interpret
|
||||
description: >
|
||||
Use ONLY when the agent needs to read or analyze an image (screenshot,
|
||||
design mockup, UI capture, .png/.jpg/.webp file) but the active model lacks
|
||||
vision capability. Delegates to the Image Interpreter role (vision-capable
|
||||
model) which returns a structured text spec — layout regions, colors (hex),
|
||||
spacing (px), typography, component hierarchy, and responsive breakpoints.
|
||||
Do not use for non-image tasks or when the active model already supports
|
||||
vision.
|
||||
triggers:
|
||||
- screenshot
|
||||
- mockup
|
||||
- design mockup
|
||||
- UI capture
|
||||
- .png
|
||||
- .jpg
|
||||
- .jpeg
|
||||
- .webp
|
||||
- image
|
||||
- 读图
|
||||
- 设计稿
|
||||
- 截图
|
||||
- 视觉
|
||||
role: Tool
|
||||
---
|
||||
|
||||
# Image Interpret — delegate image reading to a vision-capable role
|
||||
|
||||
## When to use
|
||||
|
||||
You (the calling agent) cannot read images — your model has no vision. Any
|
||||
task that requires *seeing* a visual artifact must be delegated to the
|
||||
**Image Interpreter** role, which runs on a vision-capable model and returns a
|
||||
structured text description you can act on.
|
||||
|
||||
Trigger situations:
|
||||
|
||||
- The user references a screenshot, design mockup, or image file.
|
||||
- A task requires comparing rendered UI against a design.
|
||||
- You need layout/color/spacing specs extracted from a picture.
|
||||
- A file path ending in `.png` / `.jpg` / `.jpeg` / `.webp` must be understood.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Identify the image.** Confirm the file path (or paths) that must be read.
|
||||
Resolve it to an absolute path the subagent can access.
|
||||
|
||||
2. **Frame the question.** Decide exactly what the caller needs — full layout
|
||||
spec, a specific region, color tokens, component list, diff vs. existing UI.
|
||||
|
||||
3. **Delegate via the Task tool with `role: "Tool"`.** Do NOT pass
|
||||
`subagent_type` — it is derived automatically from the role.
|
||||
|
||||
```
|
||||
Task(
|
||||
role: "Tool",
|
||||
description: "Extract header layout spec",
|
||||
prompt: "Read the image at /abs/path/mockup.png. Extract: (1) header
|
||||
layout regions with bounding positions, (2) all colors as hex,
|
||||
(3) spacing in px, (4) typography (family/size/weight). Return
|
||||
a structured text spec the caller can implement without seeing
|
||||
the image."
|
||||
)
|
||||
```
|
||||
|
||||
4. **Consume the returned text spec.** The Image Interpreter returns a
|
||||
structured description only — it cannot write code or artifacts. Use the
|
||||
returned spec to drive your own work (code generation, design review,
|
||||
planning).
|
||||
|
||||
5. **Do not attempt to read the image yourself.** If you are a non-vision
|
||||
model, opening the image yields nothing. Always delegate.
|
||||
|
||||
## Notes
|
||||
|
||||
- The Image Interpreter is **read-only** — it describes images, it does not edit
|
||||
files or write code.
|
||||
- One delegation per distinct visual question. Batch multiple images into a
|
||||
single delegation only when they must be compared.
|
||||
- If the returned spec is ambiguous, delegate again with a sharper question
|
||||
rather than guessing.
|
||||
@@ -0,0 +1,350 @@
|
||||
---
|
||||
name: implement
|
||||
description: >
|
||||
Use ONLY when implementing a work item from an approved iteration plan,
|
||||
OR fixing a bug (bugfix mode), OR refactoring code (refactor mode),
|
||||
OR porting a feature (port mode). The Developer (Worker) reads the work
|
||||
item, design sections, and acceptance criteria, writes code, and
|
||||
self-checks against the relevant checklist before handing off to code
|
||||
review.
|
||||
# `stage` is intentionally omitted: `implement` is a production-phase name the
|
||||
# stage registry rules invalid post-[org-internal #3072] phase 3 (see
|
||||
# core/schemas/workflow-routing.schema.json stage enum — production-phase
|
||||
# names are "no longer valid anywhere"). The implement skill has no registry
|
||||
# gate id; the gates it feeds into are `review-code` and `verify`.
|
||||
triggers:
|
||||
- implement
|
||||
- implement work item
|
||||
- implement WI-
|
||||
- work item
|
||||
- 实现工作项
|
||||
- 开始写代码
|
||||
- implement the iteration
|
||||
- bugfix
|
||||
- fix a bug
|
||||
- fix bug
|
||||
- fix this bug
|
||||
- 修复
|
||||
- 修复bug
|
||||
- help me fix
|
||||
- doesn't work
|
||||
- does not work
|
||||
- not working
|
||||
- is broken
|
||||
- stack trace
|
||||
- throws an error
|
||||
- crashes
|
||||
- refactor
|
||||
- 重构
|
||||
- restructure
|
||||
- 删除死代码
|
||||
- dead code
|
||||
- improve code structure
|
||||
- 重新组织代码
|
||||
- graduate experimental
|
||||
- remove experimental guard
|
||||
- 移除实验性
|
||||
- port
|
||||
- porting
|
||||
- port feature
|
||||
- port this feature
|
||||
- migrate feature
|
||||
- 移植
|
||||
- 迁移功能
|
||||
- code change
|
||||
- write code
|
||||
role: Producer
|
||||
---
|
||||
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatimDir)。机制、结构与 frontmatter 保持;实例术语(工具名、路径、工单号)按 `core/adapters/TERMINOLOGY.md` 绑定到具体实例。
|
||||
|
||||
# Skill: implement
|
||||
|
||||
## Mode Selection
|
||||
|
||||
This skill handles four work modes. The main session identifies the mode from the work item's `type` field or the user's request:
|
||||
|
||||
| Mode | When to use | Key difference |
|
||||
|---|---|---|
|
||||
| **implement** (default) | Work item from an approved iteration plan | Standard implementation per acceptance criteria |
|
||||
| **bugfix** | User reports a bug / fix request | Reproduce → root cause → regression test (must FAIL first) → minimal fix |
|
||||
| **refactor** | User asks to restructure code (no behavior change) | Establish baseline → transform in ≤10 steps → verify zero regression |
|
||||
| **port** | User asks to port a feature from another project | Read source → map concepts → implement in target conventions → verify fidelity |
|
||||
|
||||
In **standalone mode** (user-initiated, no iteration plan), skip preconditions about plan/approval and go directly to the mode-specific workflow.
|
||||
|
||||
In **pipeline mode** (work item from iteration plan), follow the standard preconditions then the mode-specific phases.
|
||||
|
||||
**On-demand references**: each mode's phase-by-phase detail (templates, prompts, orchestration flows, historical notes) lives in `reference/{mode}-mode.md`, NOT injected — read it at mode entry. Full read-timing table: References section.
|
||||
|
||||
> **Progressive disclosure ([org-internal #3734])**: when dispatching a Developer scoped to
|
||||
> one mode, pass `skills: ["implement:mode-bugfix"]` (preamble + that
|
||||
> mode's workflow only) instead of the full body; pipeline-mode dispatches
|
||||
> should also carry `pipeline-detection`
|
||||
> (`"implement:mode-implement,pipeline-detection"`). Mode Selection, Agent
|
||||
> Role, Greenfield vs. Brownfield, … are unmarked preamble and always
|
||||
> inject. Full-body remains the default when no selector is passed.
|
||||
|
||||
## Agent Role
|
||||
|
||||
The implementation is owned and produced by the **Developer** (Worker). The Builder delegates each work item to a dedicated Developer sub-agent with the full design and plan context.
|
||||
|
||||
> **Role naming**: throughout this skill, "Builder", "Orchestrator", and "main session" name the same coordinating role (the main session that dispatches Developer sub-agents and validates output) — not a distinct role from the "Orchestrator" used by `review-code` and the shared review pipeline.
|
||||
|
||||
The Developer is responsible for:
|
||||
|
||||
- Reading the assigned work item, design sections, and acceptance criteria.
|
||||
- Writing code that faithfully implements the design.
|
||||
- Running typecheck, lint, and tests to self-verify.
|
||||
- Self-checking against the relevant checklist before handoff.
|
||||
- Persisting the final report to disk before returning — worker-report persistence ([org-internal #2847], see `../_shared/worker-report-persistence.md`).
|
||||
- Writing no more and no less than the work item scope — no opportunistic refactoring of unrelated code.
|
||||
|
||||
The Builder's role is to validate the implementation output and pass it to code review. The Builder MUST NOT write or revise implementation code.
|
||||
|
||||
### Pre-flight checklist injection ([org-internal #2599])
|
||||
|
||||
Before dispatching ANY Developer (or Tester) sub-agent — in every mode (implement / bugfix / refactor / port; pipeline or standalone) — the orchestrator resolves the ticket's route (the Process Assessment Gate Step 0 already resolves the Kind/* route) and reads `<instance-root>/workflow-routing.yaml` `routes.{Kind}.preflight`. When the list is non-empty, prepend it to the sub-agent's task prompt as:
|
||||
|
||||
```
|
||||
Pre-flight self-check (evidence-based, from retrospective — verify each
|
||||
BEFORE writing code; if one is already satisfied, note why in impl-notes):
|
||||
1. {item} (evidence: {evidence})
|
||||
2. ...
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- The list is a human-landed checklist (retro proposes, a human lands it) — NEVER synthesize or extend items at dispatch time.
|
||||
- Cap at `preflight.max_items` entries; beyond it, drop oldest by `added_cycle`.
|
||||
- Items are self-checks, not gates: an unsatisfied item means the Developer addresses it in the implementation (and says how), not that dispatch aborts.
|
||||
|
||||
<!-- inject: ../_shared/large-prompts.md -->
|
||||
|
||||
> **Context compaction**: this skill is a pipeline stage boundary. The main session (orchestrator) compacts at this clean boundary ONLY when a capacity/projection trigger holds, per the L1 rule `core/rules/compact.md` §"Stage-boundary compaction" (long multi-stage runs — DAG Epic orchestration — keep the legacy every-boundary compaction; short runs — bugfix / DAG task — and standalone runs default to NOT compacting). The sub-agent this skill dispatches persists its artifacts to the Gitea wiki under `{slug}/` as it goes, so a mid-run compaction loses nothing — re-read the stage's wiki index to resume.
|
||||
|
||||
## Role Split: Developer vs Tester
|
||||
|
||||
Each execution mode below defines its own Tester focus and any mode-specific orchestration overrides.
|
||||
|
||||
## Greenfield vs. Brownfield
|
||||
|
||||
**Greenfield** (new project): Create new files following the design. Project conventions are defined by the design document.
|
||||
|
||||
**Brownfield** (existing project + new feature):
|
||||
|
||||
- **Read neighbors first.** Before writing code, read at least 3 existing files in the same module to absorb the project's code patterns. (Shared brownfield rule — canonical statement: `core/skills/review-code/SKILL.md` §"Greenfield vs. Brownfield"; the threshold and same-module scope are defined there.)
|
||||
- Match existing conventions exactly: error handling style, logging format, naming, file structure, import ordering, type declaration placement.
|
||||
- New code MUST follow existing conventions consistently — no style drift.
|
||||
- No opportunistic refactoring of unrelated existing code. If you see a bug or improvement opportunity in unmodified files, log it in the implementation report as a separate observation — do not fix it in this work item.
|
||||
- Phase 1 (Parse Context) includes reading neighboring code files to establish the project's conventions. Phase 4 (Self-Check) compares new code against these conventions.
|
||||
|
||||
---
|
||||
|
||||
## Execution Modes
|
||||
|
||||
<!-- section:mode-implement -->
|
||||
### Mode: implement (default)
|
||||
|
||||
Standard workflow for work items from an approved iteration plan. Full phase detail, templates, prompts: `reference/implement-mode.md` (read at mode entry).
|
||||
|
||||
#### Preconditions
|
||||
|
||||
> **DAG-mode input path** (DAG ticket pipeline — `Kind/Epic` / `Kind/Feature` DAG parent, routes-table direct): DAG-routed tickets **ignore `Size/*`** — the tiered Preconditions below are replaced by the node spec: work item + acceptance criteria resolve from the **frozen DAG copy** wiki page `{epic-slug}/dag` (+ `{epic-slug}/dag-nodes/{node-id}` subpages when AC detail is sunk) and the node ticket's issue body — no legacy `{slug}/04-plan-*` page, no `Size/*`-tiered req/design page. The design-space + iteration-plan convergence preconditions are replaced by the **review-dag single-gate convergence**: `octopus review status --stage review-dag` must show `success` before the node is implemented.
|
||||
|
||||
> **DAG-route read map** (every legacy `{slug}/04-plan-*` / `{slug}/03-design-*` reference below resolves from the frozen DAG copy instead — mirror of `verify/SKILL.md`'s DAG branch; full map: `reference/implement-mode.md` § Preconditions):
|
||||
>
|
||||
> - Work item (`04-plan-04-iteration-assignment` / issue body) → node spec in `{epic-slug}/dag` + node ticket body.
|
||||
> - Acceptance criteria (`04-plan-05-acceptance-criteria` / issue body) → node `acceptance_criteria` (+ sunk subpages) + node ticket body.
|
||||
> - `test_id` (測試用例 ID) → the `test_id` declared on the node AC in `{epic-slug}/dag`.
|
||||
> - Design sections / interface design (`03-design-**`) → node spec + cross-session edge contracts (no design page).
|
||||
> - Component mapping (`03-design-08-traceability`) → node `req_refs` + component field.
|
||||
>
|
||||
> At Phase 6 handoff, pass `mode: "dag-task"` to review-code (its DAG Task Mode keys off the same frozen-DAG-copy detection).
|
||||
|
||||
> **Legacy pipeline preconditions retired ([org-internal #3072] phase 3, 2026-08-21)**: tiered artifact-existence and review-convergence checks belonged to the archived legacy pipeline. Live input modes: DAG task mode (above) and standalone modes (the request itself is the spec). Historical tiered publish targets: `reference/implement-mode.md` § Legacy notes.
|
||||
|
||||
Before starting implementation, confirm:
|
||||
|
||||
- [ ] Work item is specified (DAG node ticket `{node-id}`, or a clear task description in standalone modes).
|
||||
- [ ] `core/checklists/implementation.md` is accessible.
|
||||
- [ ] `slug` matches the run's slug (DAG: `{epic-slug}`).
|
||||
- [ ] 跨阶段门控清单: `core/checklists/pipeline-gate.md` accessible and its DAG 路由变体 section confirmed — frozen DAG copy exists, single gate converged, upstream dependencies at terminal state (`ready`); else abort, listing the blocked nodes.
|
||||
|
||||
**If any precondition is unmet, abort and inform the user** — list every missing artifact, un-converged review, and blocked dependency (complete gate checklist + Recovery Protocol: `core/checklists/pipeline-gate.md`). When no work item is specified, resolve the ready/pending task nodes from `{epic-slug}/dag` and present them for selection (prompt: `reference/implement-mode.md` § Preconditions — work-item selection).
|
||||
|
||||
#### Phases skeleton
|
||||
|
||||
Every gate below is hard — agents rationalize skipping exactly these. Full detail: `reference/implement-mode.md`.
|
||||
|
||||
1. **Phase 1 — Parse Context**: read the work item (node spec + node ticket), acceptance criteria (every falsifiable `AC-{n}` / `NFR:` entry and its declared `test_id`), design context (cross-session edge contracts), and the existing codebase; resolve inputs per the DAG-route read map (standalone: the request). Read every referenced design file before writing code.
|
||||
2. **Phase 2 — Plan Implementation**: brief plan (template: `reference/implement-mode.md` § Phase 2). GATES: **≤ 3 files per work item** (more → the Builder/user MUST split it); every file must map to a design component (else flag the design gap and abort); do NOT invent design decisions. Present the plan and ask: proceed? (yes / no / revise).
|
||||
3. **Phase 3 — Implement**: design-exact code; tests cover every AC.
|
||||
- **Design discipline**: component interfaces, method signatures, return types, data model fields, API endpoints/schemas/status codes MUST match the design exactly; an impossible design decision → stop and report the gap, never silently deviate.
|
||||
- **Test discipline (Red → Green, declared test_ids)**: write each declared test FIRST and confirm it fails for the intended reason (Red) before writing the implementation (Green); the test's `file-path :: test-name` MUST match the declared `test_id` exactly — the implement-side handshake with `verify` (DOD-1.6). `MANUAL` / `BENCH:<script>` test_ids are exempt from the Red step; an already-passing test is noted in the Phase 5 report, not forced to fail.
|
||||
- Code quality + incremental commitments (conventions, no unjustified dependencies, public-API docs; shared types → data access → logic → handlers, typecheck per unit): `reference/implement-mode.md` § Code Quality.
|
||||
4. **Phase 4 — Self-Check** (all mandatory):
|
||||
1. `bun typecheck` (or project-equivalent) — zero errors.
|
||||
2. `bun oxlint --deny-warnings` (repo root — the review-code mechanical gate's canonical lint invocation; `bun lint` is the package-script alias) — zero errors.
|
||||
3. `bun run test:changed` (or project-equivalent; full suite `bun run test:parallel` is verify's job, not a per-revision gate, [org-internal #2598]) — all tests pass.
|
||||
4. Post-deletion cleanup (when any code was removed): re-run lint + typecheck to catch orphaned imports/variables/type references.
|
||||
Then self-check `core/checklists/implementation.md`; every new function/method/exported API has ≥ 1 test; interface promises cross-checked against the node's edge contracts. **Review-readiness GATE**: self-attest `core/checklists/code-review.md` (COR, DGN, SEC, PERF, TST, STY, DBT, A11Y, DOC, TRC), record pass/fail per dimension in the Phase 5 report; handoff requires **0 BLOCKERs and 0 MAJORs** — if you can find a MAJOR, the formal review will too; fix it now.
|
||||
5. **Phase 4.5 — Iteration Completion Commit**: after ALL work items in the iteration pass Phase 4, commit with format `[{chunk-id}][{iteration}] {summary}`; commit body REQUIRED for non-trivial commits (> 1 file or > 20 LOC): What (files + purpose + the `WI-{NNN}` ID — code-review TRC 10.1), Why (design/REQ motivation), Evidence (test names / verification commands). Full rules: `reference/implement-mode.md` § Phase 4.5.
|
||||
6. **Phase 4.6 — Issue Checklist Sync (progressive)**: after committing, mark items this iteration delivered `- [x]` + `_(commit {sha}: file/component)_` per the `issue-checklist-sync` L1 rule; do NOT touch items outside this iteration's scope.
|
||||
7. **Phase 4.7 — PR-Creation Sync**: the session pushes its branch and reports `status=done branch=<ref> verify=… risk=…` — the orchestrator opens the PR (serially per TD-678/[org-internal #4425]) and applies the `Risk/*` label from the report's `risk=` hint, computed per the risk-classifier frozen table (`HIGH_RISK_GLOBS` in `.gitea/scripts/risk-classifier.ts`: core/migrations/deploy/`core/rules/**`/`.gitea/workflows/**` → `Risk/High`, else `Risk/Low`); `Risk/High` PRs merge manually by design. Once the PR exists, ensure `## 当前状态` exists (PR / 代码评审 / CI rows are written by the `status-sync` poller, NOT by hand); append the PR reference to the matching Epic task-list row. **Never hand-sync main into the PR branch** — that is the keep-mergeable workflow's job. Detail: `reference/implement-mode.md` § Phase 4.6 / § Phase 4.7.
|
||||
8. **Phase 5 — Report**: implementation report with AC → test traceability (template: `reference/implement-mode.md` § Phase 5). **Persist before returning ([org-internal #2847])** — the Developer's LAST action before returning the report: write it to `<runs-root>/{slug}/workers/{chunk-id}-worker-{seq}.md` (Tier 1 run workspace) else `/tmp/octopus/{chunk-id}-worker-{seq}.md` (`../_shared/worker-report-persistence.md`). The persisted copy is the report of record — applies to EVERY mode's report phase.
|
||||
9. **Phase 6 — Handoff to Code Review**: present the report; signal readiness via `signal_stage_done`. Do NOT mark the work item complete until code review passes.
|
||||
|
||||
#### Tester focus & Common Rationalizations
|
||||
|
||||
Boundary + contract tests — the cases the Developer is structurally biased to miss; every acceptance criterion MUST map to ≥ 1 test. Implementation fails far more from **pressure** than from ignorance — full detail (11-row Excuse → Reality table): `reference/implement-mode.md` § Tester focus for implement / § Common Rationalizations.
|
||||
|
||||
---
|
||||
|
||||
<!-- section:mode-bugfix -->
|
||||
### Mode: bugfix
|
||||
|
||||
Reproduce, isolate, and fix a bug with a regression test that MUST fail before the fix. Small localized bugs → standalone (existing system behavior is the specification; review gate optional — only when > 20 lines or ≥ 3 files); large/complex bugs → pipeline (review + verify mandatory). Phase detail, templates, prompts: `reference/bugfix-mode.md` (read at mode entry).
|
||||
|
||||
> **Routing override (ticket-seeded)**: the Optional/None gate above applies to *user-initiated* standalone mode. A `Kind/*` route whose `keep_gates` includes `review-code` / `verify` (e.g. `Kind/Bug`, `Kind/Testing`) makes those gates MANDATORY regardless of size (a gate is mandatory if EITHER the route OR the skill requires it; skipping is valid only when BOTH agree).
|
||||
|
||||
#### Phases skeleton
|
||||
|
||||
The regression test MUST fail before the fix — the Tester dispatches between Phase 2 and Phase 4, not after the fix (dispatch flow: `reference/bugfix-mode.md` § Role & Responsibilities; single-Developer invocation only for trivial single-file fixes — force the split when the fix touches ≥ 2 files or the root cause spans ≥ 2 levels of indirection).
|
||||
|
||||
1. **Phase 1 — Understand & Reproduce**: read relevant code; check existing tests (a passing test on this path → the bug is in the test or an uncovered branch); reproduce and document (template: `reference/bugfix-mode.md` § Phase 1). **If the bug CANNOT be reproduced, stop and report — do not guess-patch.**
|
||||
2. **Phase 2 — Isolate Root Cause**: trace symptom → proximate cause → root cause (RCA template: `reference/bugfix-mode.md` § Phase 2); fixing a symptom → stop, go deeper; not found after 3 levels of indirection → pause and report, no surface-level patch. Then evaluate routing (below).
|
||||
3. **Phase 3 — Write a Regression Test**: exercise the exact bug path with the failing inputs; it MUST fail with the bug's symptom NOW, before the fix (already passing → the test does not cover the bug, rewrite it; no failing test possible → most targeted test, marked `[flaky]`).
|
||||
4. **Phase 4 — Fix**: minimum change resolving the root cause; one conceptual change per fix — no bundled refactoring, style changes, or "while I'm here" improvements (root cause in a different file → fix it there). Run the regression test (MUST pass) + relevant unit tests.
|
||||
5. **Phase 5 — Self-Check & Report**: `bun typecheck`; `bun oxlint --deny-warnings`; `bun run test:changed` (all pass; full suite belongs to verify); verify `core/checklists/bugfix.md`; publish the bugfix report as wiki page `{slug}/bugfix-report` (template: `reference/bugfix-mode.md` § Bugfix Report); persist per the Phase 5 persistence rule (Mode: implement, [org-internal #2847]).
|
||||
6. **Phase 5.5 — Issue Checklist Sync (standalone bugfix)**: sync the source issue at each transition (commit / PR / review / CI / close) per the `issue-checklist-sync` L1 rule and its standalone-flow table.
|
||||
7. **Phase 6 — Approval**: present the report (prompt: `reference/bugfix-mode.md` § Phase 6 — Approval).
|
||||
|
||||
#### Routing Decision (after Phase 2)
|
||||
|
||||
Full escalate/stay criteria: `reference/bugfix-mode.md` § Routing Decision. In short — **escalate to pipeline** on ANY of: ≥ 5 files · ≥ 2 modules/components · design-level root cause · data migration / schema change · public-API / contract change · dependency change · > 50 lines · user requests full-process; **stay standalone** only when ALL the opposites hold. If uncertain, escalate — a false escalation costs review rounds; a false standalone decision skips quality gates.
|
||||
|
||||
**Big-bug relabel rule ([org-internal #3061])** — before the generic escalation above, split the triggers by kind:
|
||||
|
||||
- **Design-level triggers** (root cause is a design decision — protocol / schema / architecture; shared-contract or public-API change; data migration): do NOT push through bugfix — **relabel the ticket `Kind/Feature`** and reroute via Step 0 (DAG route; 1–3 node small DAG expected); repro + root-cause notes become node input.
|
||||
- **Mechanical size triggers only** (many files / lines, same design): stay in bugfix — batch into iterations, keep review-code + verify. Scale alone never justifies a relabel.
|
||||
|
||||
#### Pipeline Mode (bugfix)
|
||||
|
||||
> **Legacy path retired ([org-internal #3072] phase 3)**: the requirements → design → review → plan front-end was archived; a big bug needing a design-level decision relabels `Kind/Feature` into the DAG route (big-bug relabel rule above).
|
||||
|
||||
When a bugfix escalates beyond standalone scope, the bug report becomes a pipeline input; the bugfix phases (reproduce, root cause, regression test, fix) are embedded within the implement stage, and review-code + verify remain mandatory. On the DAG route the node spec lives in `{epic-slug}/dag`; the Developer follows bugfix Phases 1–6 as the implementation method, then produces the standard implementation report (Mode: implement, Phase 5).
|
||||
|
||||
**Pipeline abort criteria** — before any code is written in pipeline mode, abort if ANY of: (1) bug no longer reproduces; (2) root-cause hypothesis falsified during re-isolation; (3) resolved by external change; (4) reproduction confidence < 3/5 after one re-isolation iteration. Abort procedure (`{slug}/ABORT` wiki page, no commit/merge, retrospective, archive): `reference/bugfix-mode.md` § Pipeline Abort Criteria. Once code is written, abort is no longer valid — the run proceeds review-code → verify.
|
||||
|
||||
**Common Rationalizations (bugfix) & Incident Triage** — full 9-row Excuse → Reality table + Incident Triage Carve-Out (under active incident pressure the Phase 2→3 ordering MAY be relaxed, never skipped — a stop-gap may ship first, BUT the full root-cause trace + failing regression test + proper fix MUST land in the same incident window): `reference/bugfix-mode.md` § Common Rationalizations (bugfix) / § Incident Triage Carve-Out.
|
||||
|
||||
---
|
||||
|
||||
<!-- section:mode-refactor -->
|
||||
### Mode: refactor
|
||||
|
||||
Restructure existing code without changing observable behavior — the existing test suite is the safety net; every step MUST be verified before proceeding. Templates and prompts: `reference/refactor-mode.md` (read at mode entry). **Execution modes**: standalone (user says "refactor {X}"; review gate optional — only when > 50 lines or ≥ 5 files) vs pipeline (refactoring WI / DAG node; scope from the node spec, review mandatory).
|
||||
|
||||
#### Preconditions
|
||||
|
||||
- [ ] Scope is specified (which file, module, or pattern to refactor).
|
||||
- [ ] An existing test suite covers the scope (if unknown, run with coverage first).
|
||||
- [ ] No uncommitted changes (`git status` is clean); `core/checklists/refactoring.md` is accessible.
|
||||
|
||||
**No test coverage? Stop.** Refactoring without tests is rewriting with unknown side effects — write characterization tests first or skip this module (prompt: `reference/refactor-mode.md` § No Test Coverage? Stop.).
|
||||
|
||||
#### Phases skeleton
|
||||
|
||||
1. **Phase 1 — Scope & Baseline**: map exact files + dependents; run `bun run test:parallel` (the refactor baseline legitimately needs the full suite) — ANY pre-existing failure → stop ("Cannot begin refactoring with failing tests. Fix them first."). Capture baseline: test count, coverage, optional complexity (template: `reference/refactor-mode.md` § Baseline).
|
||||
2. **Phase 2 — Define Target Pattern**: Extract / Inline / Rename / Move / Replace / Simplify / Upgrade, with one-sentence goal + success criteria (all tests pass unchanged; structural goal met; coverage does not decrease). Pipeline mode: align with the node spec + contracts, or justify in the report.
|
||||
3. **Phase 3 — Decompose into Steps**: smallest individually-verifiable steps (each reversible, suite-passing, ONE conceptual transformation); present the plan before executing (user may approve / reorder / reject). **If > 10 steps, the scope is too large — split into multiple sessions.**
|
||||
4. **Phase 4 — Incremental Execution**: per step — transform, run `bun run test:changed` (ALL pass; per-step scoped reruns — full suite is verify's job, [org-internal #2598]), commit `refactor: {what} from {where}`. **If FAIL: revert immediately** — do NOT fix the test or code within the same step; find a smaller decomposition (exception: fix a flaky test first as a prerequisite step, then retry).
|
||||
5. **Phase 5 — Final Validation**: `bun run test:parallel` all pass; `bun typecheck` zero errors; `bun oxlint --deny-warnings` zero errors; coverage MUST NOT decrease vs baseline (±1% noise).
|
||||
6. **Phase 6 — Report**: before/after metrics, steps executed, verification results, design deviations (template: `reference/refactor-mode.md` § Refactor Report).
|
||||
7. **Phase 7 — Approval**: present the report (prompt: `reference/refactor-mode.md` § Phase 7 — Approval). Route to code review if > 50 lines or ≥ 5 files.
|
||||
|
||||
---
|
||||
|
||||
<!-- section:mode-port -->
|
||||
### Mode: port
|
||||
|
||||
Port a feature from a source project into the current (target) project, preserving the source's behavioral contract while adapting every implementation detail to the target's tech stack and conventions. Orchestration, per-phase processes, and templates live in the existing `reference/*.md` port files (indexed in References); read `reference/port-mode.md` at mode entry.
|
||||
|
||||
**Tester focus (port)**: behavioral-fidelity tests — every `FID-*` item in `port-{name}/source-analysis/10-fidelity-baseline` MUST map to ≥ 1 ported test (rewrite the source test in the target framework's style, preserving the contract); port edge/error/boundary tests, not just the happy path (missing FID coverage is the #1 cause of incomplete porting); every `[FIDELITY DEVIATION]` gets a test locking the divergent behavior.
|
||||
|
||||
#### Phase 0 — Scope Assessment
|
||||
|
||||
Measure the source scope — file count and LOC alone are insufficient: (1) source files; (2) source LOC (exclude tests); (3) source modules / feature areas; (4) source packages touched; (5) implicit dependency artifacts (schema/migrations, config, env vars, CLI flags, theme files, routes, providers, build config — full list: Phase A1.8). Then:
|
||||
|
||||
- **Standalone** (Part 1): ≤ 5 files, ≤ 200 LOC, 1 package, 0 implicit deps.
|
||||
- **Pipeline** (Part 2): > 5 files or > 200 LOC · spans ≥ 3 source modules · spans ≥ 2 packages · or ≥ 3 implicit dependency artifacts (implicit deps require capability boundary analysis).
|
||||
|
||||
Report the assessment and do NOT proceed without explicit approval (prompt: `reference/port-mode.md` § Phase 0 — Scope Assessment).
|
||||
|
||||
#### Preconditions
|
||||
|
||||
- [ ] Source project path/reference and source feature scope specified; target project structure accessible (all monorepo packages).
|
||||
- [ ] Target is the current working directory, clean workspace, existing test framework (none → warn: behavioral fidelity cannot be guaranteed — `reference/port-mode.md` § Preconditions).
|
||||
- [ ] Target's existing capabilities documented or discoverable (else run the structural survey, Phase A1.7, first); `core/checklists/port.md` is accessible.
|
||||
|
||||
#### Part 1 — Standalone Mode (Small Port)
|
||||
|
||||
The Developer executes the full port lifecycle directly; no DAG decomposition; **code review is mandatory regardless of size** (Phase A8). Phases: A1 source analysis → A1.5 review GATE → A1.7 target surface → A1.8 capability boundary GATE (`reference/source-analysis.md`) · A2 concept mapping → A3 gap analysis → A4 adaptation design (`reference/mapping-and-design.md`) · A5 implement + A5.5 self-check GATE (rules below) · A6 port tests & fidelity (`reference/fidelity-verification.md`) · A7 report (`reference/port-report-template.md`) · A8 approval → code review (`reference/port-mode.md` § Phase A8).
|
||||
|
||||
**Phase A5 — Implement (rules)**: one file at a time (port completely, verify, then move on); **target conventions are law**; **fidelity over aesthetics** — do not "improve" the source logic (port the exact validation, log `[NOTE: weak validation in source]`; stricter behavior is a separate feature item); port comments from source (translated); `bun typecheck` after each file; no new dependencies (use the adaptation-design alternative or reimplement the subset inline).
|
||||
|
||||
**Phase A5.5 — Self-Check GATE (MANDATORY)**: complete `core/checklists/port.md` in full — every item ☑ (pass) or ☐ (fail) with written justification; every ☐ item documents an inline fix plan; known deferrals include a reactivation path (chunk + trigger); publish as wiki page `port-{name}/self-check`. Do NOT proceed to Phase A6 until all items are ☑ OR all ☐ items have documented fix plans AND the Developer has printed `SELF-CHECK COMPLETE — {X} items passing, {Y} items deferred with plan`.
|
||||
|
||||
#### Part 2 — Pipeline Mode (Large Port)
|
||||
|
||||
The Developer produces a source analysis report as the foundation artifact, then the port flows through the pipeline with peer-review gates: `port (source analysis) → analyze-dag (decompose by source module) → review-dag single gate → per node (dag.task_route): implement → review-code → verify (integration + fidelity)`. Phases: B1 12-document source analysis → B1.5 10-dimensional review GATE → B1.7+B1.8 target surface & capability boundary artifacts → B2 checklist self-check → B3 handoff to analyze-dag (slug `port-{name}`) → B4 per-node DAG task route (`reference/large-port-pipeline.md`) · B5 verify — integration + fidelity, every `FID-*` traced to a passing test (`reference/fidelity-verification.md`) · B6 final approval (rules below).
|
||||
|
||||
**Phase B6 — Final Approval**: verify (1) CI is configured (absent → warn `[GAP: no CI — no automated gate before merge]`, flag in report); (2) all review gates passed (review-dag single gate + every node's code review `converged: true`); (3) typecheck + lint + tests pass fresh. Present the consolidated report (prompt: `reference/port-mode.md` § Phase B6). Post-merge cleanup: `reference/port-mode.md` § Phase B7.
|
||||
|
||||
---
|
||||
|
||||
<!-- section:pipeline-detection -->
|
||||
## Pipeline Work Item Detection
|
||||
|
||||
Not all work items involve writing new code — some are refactoring, bugfix, or frontend work items. They use the workflows above (or the frontend skill) but flow through the same pipeline gates (review-code → verify; DAG-routed work resolves its spec from `{epic-slug}/dag`). When detected, read `reference/work-item-detection.md` BEFORE Phase 1 for the per-phase pipeline adaptations. Detection triggers (full keyword lists in the reference):
|
||||
|
||||
| Work item | Detected by (examples) | Route |
|
||||
|---|---|---|
|
||||
| **Refactoring** | "Refactor" / "重构" / "Extract" / "Rename" … prefixes; `[REFACTOR]` tag; behavior-preserving structural node spec; REQ-REFACTOR-* | Mode: refactor + adaptations — code review mandatory regardless of size, no "no review needed" bypass |
|
||||
| **Bugfix** | "Bugfix" / "Fix" / "修复" / "Hotfix" prefixes; `[BUGFIX]` tag; correction-of-behavior node spec; bug report / stack trace / RCA reference | Mode: bugfix + adaptations — code review mandatory regardless of size |
|
||||
| **Frontend** | "Frontend" / "UI" / "组件" / "页面" prefixes; `[FRONTEND]` tag; UI-layer node spec; components in `components/` `pages/` `views/` `ui/`; exclusively `.tsx`/`.jsx`/`.vue`/`.svelte`/`.astro`/`.css`/`.scss` files | `core/skills/frontend/SKILL.md` + adaptations — code review mandatory regardless of size |
|
||||
|
||||
## References
|
||||
|
||||
**On-demand mode references** (NOT injected — read at the declared timing):
|
||||
|
||||
- `reference/implement-mode.md` — Mode: implement: mode entry (phase detail); Phase 2 / Phase 5 templates; Tester focus; Common Rationalizations; legacy notes.
|
||||
- `reference/bugfix-mode.md` — Mode: bugfix: mode entry; Phase 1–3 templates; Phase 5 report + publish; routing escalation; abort procedure; orchestration.
|
||||
- `reference/refactor-mode.md` — Mode: refactor: mode entry; Phase 1 baseline; Phase 6 report; no-coverage stop prompt.
|
||||
- `reference/port-mode.md` — Mode: port: mode entry; Phase 0 prompt; preconditions warnings; A8/B6 approval prompts; B7 cleanup.
|
||||
- `reference/work-item-detection.md` — detected refactoring / bugfix / frontend work item: BEFORE Phase 1 of the matched mode.
|
||||
|
||||
- `core/checklists/implementation.md` — Implementation self-check checklist
|
||||
- `core/checklists/bugfix.md` — Bugfix self-check checklist
|
||||
- `core/checklists/refactoring.md` — Refactoring self-check checklist
|
||||
- `core/checklists/port.md` — Porting self-check checklist
|
||||
- `core/checklists/frontend.md` — Frontend self-check checklist
|
||||
- `core/checklists/code-review.md` — Code review checklist (self-attestation)
|
||||
- `core/checklists/pipeline-gate.md` — Cross-stage pipeline gate checklist
|
||||
- `core/skills/frontend/SKILL.md` — Frontend workflow (for frontend work items)
|
||||
- `core/skills/browser-debug/SKILL.md` — Interactive browser verification (for frontend work items) and UI bug reproduction (bugfix Phase 1)
|
||||
- `core/skills/review-code/SKILL.md` — Code review (next step after implementation)
|
||||
- `core/skills/verify/SKILL.md` — Integration + fidelity verification (pipeline-mode bugfix, large port)
|
||||
- `<instance-root>/archive/skills/` — Legacy requirements/design/plan/roadmap skills (archived [org-internal #3072] phase 3; their templates moved to `<instance-root>/archive/templates/`)
|
||||
- `reference/source-analysis.md` — Port Phase A1, A1.5, A1.7, A1.8 detailed processes & templates
|
||||
- `reference/mapping-and-design.md` — Port Phase A2, A3, A4 detailed processes & templates
|
||||
- `reference/large-port-pipeline.md` — Port Phase B1–B4 detailed processes
|
||||
- `reference/fidelity-verification.md` — Port Phase A6, B5 detailed processes & templates
|
||||
- `reference/source-analysis-templates.md` — Port B1 document format templates
|
||||
- `reference/capability-boundary-template.md` — Port A1.8/B1.8 13-dimension table
|
||||
- `reference/target-surface-template.md` — Port A1.7/B1.7 output format
|
||||
- `reference/port-report-template.md` — Port A7/B6 report format
|
||||
- Martin Fowler, _Refactoring: Improving the Design of Existing Code_ (2nd ed.)
|
||||
- `core/rules/` — Project engineering conventions (test commands, typecheck)
|
||||
- L2 wiki pages for style-guide, effect-rules, and database conventions (see the "L2 on-demand reference" section of AGENTS.md)
|
||||
@@ -0,0 +1,494 @@
|
||||
> Extracted from implement/SKILL.md (Mode: bugfix) — moved verbatim 2026-08-25, ticket [org-internal #3381].
|
||||
|
||||
### Mode: bugfix
|
||||
|
||||
Reproduce, isolate, and fix a bug with a regression test to prevent
|
||||
recurrence. For small, localized bugs, use standalone mode — the existing
|
||||
system behavior is the specification. For large, complex bugs, route through
|
||||
the full quality pipeline.
|
||||
|
||||
#### Process Overview
|
||||
|
||||
Every diamond below is a gate Developers rationalize skipping — especially
|
||||
under "the bug is obvious" pressure.
|
||||
|
||||
```dot
|
||||
digraph bugfix {
|
||||
rankdir=TB;
|
||||
node [shape=box, fontname="Helvetica"];
|
||||
|
||||
repro [shape=diamond, label="Bug reproduces?"];
|
||||
norepro [label="STOP: report cannot-reproduce\n(do not guess-patch)"];
|
||||
rc [shape=diamond, label="Root cause found\n(not just symptom)?"];
|
||||
symptom [label="Go deeper — do NOT\npatch the symptom"];
|
||||
route [shape=diamond, label="Routing:\nstandalone vs pipeline?"];
|
||||
escalate [label="Uncertain → escalate\nto pipeline mode"];
|
||||
rtest [shape=diamond, label="Regression test\nFAILS before fix?"];
|
||||
notest [label="Test does not cover\nthe bug — rewrite it"];
|
||||
fix [label="Phase 4: Fix\n(one change, root cause only)"];
|
||||
green [shape=diamond, label="Regression test PASS\n+ full suite green?"];
|
||||
done [shape=doublecircle, label="Phase 5/6:\nSelf-Check + Report"];
|
||||
|
||||
repro -> norepro [label="no"];
|
||||
repro -> rc [label="yes"];
|
||||
rc -> symptom [label="no"];
|
||||
rc -> route [label="yes"];
|
||||
route -> escalate [label="uncertain"];
|
||||
route -> rtest [label="standalone"];
|
||||
rtest -> notest [label="passes already"];
|
||||
rtest -> fix [label="fails (confirmed)"];
|
||||
fix -> green;
|
||||
green -> fix [label="no: fix + re-run"];
|
||||
green -> done [label="yes"];
|
||||
}
|
||||
```
|
||||
|
||||
#### Execution Modes
|
||||
|
||||
| Mode | Entry Point | Scope Source | Review Gate | Verify Gate |
|
||||
| ---------- | -------------------------------------- | ------------------ | ------------ | ----------- |
|
||||
| Standalone | User says "fix this bug" | Bug report + code | Optional (>20 lines or ≥3 files) | None |
|
||||
| Pipeline | User requests full-process bugfix, or auto-escalation | Bug report → requirements → design → plan → implement | Mandatory | Mandatory |
|
||||
|
||||
> **Routing override (ticket-seeded)**: the Optional/None gate declarations
|
||||
> above apply to *user-initiated* standalone mode. When the ticket carries a
|
||||
> `Kind/*` route whose `keep_gates` includes `review-code` / `verify` (e.g.
|
||||
> `Kind/Bug`, `Kind/Testing` — see `<instance-root>/workflow-routing.yaml`), those
|
||||
> gates are MANDATORY regardless of size. Per `core/rules/workflow-routing.md`,
|
||||
> a gate is mandatory if EITHER the route OR the skill requires it; skipping is
|
||||
> valid only when BOTH agree it is skippable.
|
||||
|
||||
In pipeline mode, the bug report becomes a bugfix work item that flows through
|
||||
the full requirements → design → plan → implement → review-code → verify
|
||||
pipeline.
|
||||
|
||||
#### Role & Responsibilities
|
||||
|
||||
The bugfix is owned and executed by the **Developer** (Worker). The
|
||||
Developer owns both implementation and bugfix — same role, same skill set.
|
||||
|
||||
The Developer is responsible for:
|
||||
|
||||
- Reproducing the bug from the description.
|
||||
- Identifying the root cause (not just patching the symptom).
|
||||
- Writing a regression test that fails before the fix and passes after.
|
||||
- Applying the minimal surgical fix — one change, one purpose.
|
||||
- Running the full test suite to confirm no regressions.
|
||||
|
||||
The Builder's role is to present the bugfix report and route it to code
|
||||
review if the change is non-trivial (> 20 lines or touches ≥ 3 files).
|
||||
|
||||
#### Tester focus for bugfix
|
||||
|
||||
The Tester role in bugfix writes **regression tests** and, uniquely,
|
||||
intervenes BEFORE the fix (a regression test must fail before the fix to
|
||||
prove the bug exists):
|
||||
|
||||
- **Failing regression test** — read `repro-notes.md` (the Developer's
|
||||
reproduction + root-cause analysis from Phases 1–2), write a test that
|
||||
exercises the exact bug path and FAILS with the bug's symptom. This MUST
|
||||
happen before Phase 4 (Fix), not after — it is Phase 3.
|
||||
- **Passing confirmation** — after the Developer's fix, the same test MUST
|
||||
pass (the Developer's green run in Phase 4 verifies this).
|
||||
- **Boundary regression tests** — inputs adjacent to the bug trigger,
|
||||
similar conditions that must NOT trigger the fix (guards against
|
||||
over-fixing), and error paths near the root cause.
|
||||
|
||||
**Bugfix-specific orchestration** (overrides the standard role-split flow):
|
||||
|
||||
Because a regression test must fail BEFORE the fix, the bugfix role split
|
||||
inverts the standard orchestration — the Tester dispatches between
|
||||
Phase 2 and Phase 4, not after the fix:
|
||||
|
||||
```
|
||||
[Worker: developer] Phase 1 reproduce + Phase 2 root cause
|
||||
→ write repro-notes (reproduction steps, root cause, bug path,
|
||||
expected behavior)
|
||||
↓ persist: wiki page `{slug}/repro-notes` (gitea_wiki__create_page)
|
||||
[Worker: tester] read repro-notes → Phase 3 write failing regression test
|
||||
(confirms FAIL before fix)
|
||||
↓ persist: wiki page `{slug}/test-report` (gitea_wiki__create_page, failing test confirmed)
|
||||
[Worker: developer] Phase 4 fix → run test:changed to green
|
||||
→ write impl-notes (post-fix behavior contract)
|
||||
↓ persist: wiki page `{slug}/impl-notes` (gitea_wiki__create_page, post-fix behavior contract)
|
||||
[Worker: tester] supplement boundary regression tests → run test:changed
|
||||
↓ persist: update wiki page `{slug}/test-report` (gitea_wiki__update_page, final)
|
||||
— consumed by the human stakeholder / next iteration planning for DoD regression-test evidence
|
||||
```
|
||||
|
||||
For small, single-file bugfixes with an obvious fix, a single Developer
|
||||
Worker may write the failing test, fix, and confirm green in one invocation
|
||||
— the split is optional for trivial fixes (Phase 3 + Phase 4 in one
|
||||
session). Force the split when the fix touches ≥ 2 files or the root cause
|
||||
spans ≥ 2 levels of indirection.
|
||||
|
||||
Pipeline-mode bugfixes route through the implement pipeline (see ### Mode:
|
||||
implement (default)), with the bugfix-specific Tester focus above layered
|
||||
on top of the standard role-split orchestration.
|
||||
|
||||
---
|
||||
|
||||
#### Preconditions (standalone)
|
||||
|
||||
Before starting the bugfix, confirm:
|
||||
|
||||
- [ ] Bug description exists (user's message, issue tracker link, or error log).
|
||||
- [ ] Existing codebase is accessible.
|
||||
- [ ] `core/checklists/bugfix.md` is accessible.
|
||||
|
||||
If the user describes a symptom without specifics, ask for:
|
||||
|
||||
```
|
||||
To fix this bug, I need:
|
||||
1. What is the expected behavior? (what should happen)
|
||||
2. What is the actual behavior? (what happens instead)
|
||||
3. Steps to reproduce.
|
||||
4. Any error messages, logs, or stack traces.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Routing Decision
|
||||
|
||||
After Phase 1 (reproduce) and Phase 2 (root cause), the Developer evaluates
|
||||
whether the fix qualifies for standalone or pipeline mode:
|
||||
|
||||
**Escalate to pipeline mode when ANY of:**
|
||||
|
||||
| Condition | Reason |
|
||||
| --------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| Fix touches ≥ 5 files | Cross-file changes need design review and integration tests |
|
||||
| Fix spans ≥ 2 modules / components | Multi-module fixes need architectural validation |
|
||||
| Root cause is in a design-level decision (protocol, schema, architecture) | Design change needs requirements + design review |
|
||||
| Fix requires data migration or schema change | Schema changes need data design review and migration plan |
|
||||
| Fix changes a public API or interface contract | API changes need interface design review and compatibility check |
|
||||
| Fix introduces a new dependency or changes an existing one | Dependency changes need review (DGN dimension, code review) |
|
||||
| Estimated lines changed > 50 | Large change carries high regression risk |
|
||||
| User explicitly requests full-process bugfix | User wants quality gates |
|
||||
|
||||
**Stay in standalone mode when ALL of:**
|
||||
|
||||
| Condition |
|
||||
| ---------------------------------------------- |
|
||||
| Fix is ≤ 4 files |
|
||||
| Fix is ≤ 1 module / component |
|
||||
| Fix is a logic error, not a design error |
|
||||
| No data migration or schema change |
|
||||
| No API or interface contract change |
|
||||
| No dependency change |
|
||||
| Estimated lines changed ≤ 50 |
|
||||
|
||||
If the Developer is uncertain, escalate. A false pipeline escalation costs a few
|
||||
extra review rounds. A false standalone decision risks missing a quality gate on
|
||||
a complex change.
|
||||
|
||||
**Big-bug relabel rule ([org-internal #3061])** — before the generic escalation below, split
|
||||
the triggers by kind:
|
||||
|
||||
- **Design-level triggers** (root cause is a design decision — protocol /
|
||||
schema / architecture; shared-contract or public-API change; data
|
||||
migration): do NOT push through bugfix and do NOT run the legacy pipeline
|
||||
escalation — **relabel the ticket `Kind/Feature`** and reroute via Step 0
|
||||
(DAG route; a 1–3 node small DAG is the expected shape for a single
|
||||
design-level fix). The fix work already done (repro notes, root cause)
|
||||
becomes node input, not wasted work.
|
||||
- **Mechanical size triggers only** (many files / many lines, same design):
|
||||
stay in bugfix — batch the change into iterations and keep the
|
||||
review-code + verify gates. Scale alone never justifies a relabel.
|
||||
|
||||
When escalating, the Developer pauses after Phase 2, reports the routing
|
||||
decision, and asks the user to confirm pipeline escalation:
|
||||
|
||||
```
|
||||
This bugfix qualifies for pipeline mode:
|
||||
- {N} files across {M} modules
|
||||
- Root cause: {design-level issue}
|
||||
- Estimated lines: {N}
|
||||
|
||||
→ Route through requirements → design → plan → implement → review → verify?
|
||||
(yes / no — proceed with standalone)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 1 — Understand & Reproduce
|
||||
|
||||
1. **Read relevant code** — find the module/component likely responsible for
|
||||
the bug. Use `grep` for error messages, `glob` for related files.
|
||||
|
||||
2. **Check existing tests** — do existing tests cover this code path? If a
|
||||
test exists but passes, the bug is in the test or in an uncovered branch.
|
||||
|
||||
3. **Reproduce** — run the relevant test(s) or manually trigger the bug.
|
||||
Confirm the actual behavior matches the bug report. Document the
|
||||
reproduction:
|
||||
|
||||
```markdown
|
||||
## Reproduction
|
||||
|
||||
**Steps**:
|
||||
|
||||
1. {step}
|
||||
2. {step}
|
||||
**Expected**: {what should happen}
|
||||
**Actual**: {what happens}
|
||||
```
|
||||
|
||||
4. If the bug CANNOT be reproduced, stop and report:
|
||||
|
||||
```
|
||||
Cannot reproduce the bug. Here's what I tried:
|
||||
- {step 1}
|
||||
- {step 2}
|
||||
|
||||
→ Is the environment different? Are there missing steps? Does a specific
|
||||
data state trigger it?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 2 — Isolate Root Cause
|
||||
|
||||
Trace from the symptom to the root cause:
|
||||
|
||||
1. **Symptom**: surface-level error (e.g. "500 on login").
|
||||
2. **Proximate cause**: the code that throws or returns wrong (e.g. "password
|
||||
hash comparison returns false for valid password").
|
||||
3. **Root cause**: the underlying defect (e.g. "password hashing config changed
|
||||
in commit abc123 but the stored hashes were not re-hashed").
|
||||
|
||||
```markdown
|
||||
## Root Cause Analysis
|
||||
|
||||
**Symptom**: {error message or wrong behavior}
|
||||
**Proximate cause**: {file}:{line} — {what the code does wrong}
|
||||
**Root cause**: {underlying defect — config, data, logic, or assumption}
|
||||
|
||||
**Introduced in**: {commit hash or version if known}
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
|
||||
- If you're fixing a symptom (e.g. adding a null check where the real bug is
|
||||
that null should never reach that line), stop and go deeper.
|
||||
- If you can't find the root cause after examining 3 levels of indirection,
|
||||
pause and report findings. Do NOT apply a surface-level patch.
|
||||
- **After Phase 2, evaluate the routing decision** (see Routing Decision table
|
||||
above). If the fix qualifies for pipeline mode, pause and present the
|
||||
escalation prompt before proceeding to Phase 3.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 3 — Write a Regression Test
|
||||
|
||||
Before fixing, write a test that proves the bug exists:
|
||||
|
||||
1. Write a test that exercises the bug path with the failing inputs.
|
||||
2. Run the test — it MUST fail with the bug's symptom.
|
||||
3. The test must be specific: test the exact condition that was broken, not
|
||||
a general "endpoint returns 200" test.
|
||||
|
||||
```markdown
|
||||
## Regression Test
|
||||
|
||||
- **File**: {path to test file}
|
||||
- **Test name**: {test function name}
|
||||
- **What it verifies**: {the expected behavior that was broken}
|
||||
- **Fails before fix**: ✅ (confirmed)
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
|
||||
- If you cannot write a test that fails (bug is non-deterministic, environment-
|
||||
specific), write the most targeted test you can and mark it `[flaky]`.
|
||||
- The test must fail NOW, before you apply the fix. If it passes already, the
|
||||
test does not cover the bug.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 4 — Fix
|
||||
|
||||
Apply the minimum change that resolves the root cause:
|
||||
|
||||
1. **One conceptual change per fix** — do not bundle a bugfix with refactoring,
|
||||
style changes, or "while I'm here" improvements.
|
||||
2. **Fix the root cause**, not the symptom. If the root cause is in a different
|
||||
file than the symptom, fix it there.
|
||||
3. **Update only what's necessary** — if fixing a null-safety bug requires
|
||||
adding a null check in one place, add one null check, not a comprehensive
|
||||
null-safety overhaul of the entire module.
|
||||
4. Run the regression test — it MUST pass.
|
||||
5. Run the relevant unit tests — all existing tests must still pass.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 5 — Self-Check & Report
|
||||
|
||||
##### Self-Check
|
||||
|
||||
1. **Typecheck**: `bun typecheck` — zero errors.
|
||||
2. **Lint**: `bun oxlint --deny-warnings` — zero errors.
|
||||
3. **Full test suite**: `bun run test:parallel` — all tests pass (new + existing).
|
||||
4. **Checklist**: verify every item in `core/checklists/bugfix.md`.
|
||||
5. **Publish bugfix report**: write the bugfix report as a wiki page via `wiki 读写 API(见 TERMINOLOGY)` with page_name `{slug}/bugfix-report` (`_shared/gitea-write-patterns.md` Pattern 1).
|
||||
|
||||
##### Report
|
||||
|
||||
```markdown
|
||||
## Bugfix Report
|
||||
|
||||
**Bug**: {one-line description}
|
||||
**Root cause**: {file}:{line} — {explanation}
|
||||
**Fix**: {file} — {single-sentence description of change}
|
||||
**Lines changed**: {N}
|
||||
**Regression test**: {test file}:{test name}
|
||||
|
||||
### Verification
|
||||
|
||||
- Regression test: {PASS | FAIL}
|
||||
- Full test suite: {N} passed, 0 failed
|
||||
- Typecheck: ✅
|
||||
- Lint: ✅
|
||||
|
||||
### Files Changed
|
||||
|
||||
| File | Lines | Purpose |
|
||||
| ------------------- | ------ | ------------------------------------------------------ |
|
||||
| `src/auth/login.ts` | +3, -1 | Fix password hash comparison when salt version changes |
|
||||
|
||||
---
|
||||
|
||||
**Handoff**: {if changes > 20 lines or ≥ 3 files → run `core/skills/review-code/SKILL.md`
|
||||
| otherwise → fix complete, no review needed}
|
||||
|
||||
> **Routing override**: when the ticket carries a `Kind/*` route whose
|
||||
> `keep_gates` includes `review-code` / `verify` (e.g. `Kind/Bug`,
|
||||
> `Kind/Testing`), those gates are MANDATORY even for small fixes — the
|
||||
> "no review needed" branch above does not apply (see the Execution Modes
|
||||
> routing-override note above).
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 5.5 — Issue Checklist Sync (standalone bugfix)
|
||||
|
||||
In standalone-bugfix mode there are no skill-exit boundaries between commit,
|
||||
PR, review, and CI — without explicit sync points the issue goes stale. Per
|
||||
the `issue-checklist-sync` L1 rule, sync at each externally visible
|
||||
transition (skip any step if no source issue exists):
|
||||
|
||||
| When | Sync action |
|
||||
|------|-------------|
|
||||
| After the fix commit | Check off fix-delivered ACs with `_(commit {sha}: file)_` |
|
||||
| After PR creation | Ensure the `## 当前状态` section exists (the PR row is auto-written by the status-sync poller — see `issue-checklist-sync.md` § Automated sync) |
|
||||
| After review convergence | Review-related ACs get `_(reviewed: round N PASS)_` (done by review-code Phase E 2.7) |
|
||||
| On CI state transitions | Update process-AC progress (e.g. "N consecutive green") with run number |
|
||||
| At verify PASS / close | Final sweep per `verify` Phase 5.6 |
|
||||
|
||||
Bugfix mode delegates the "after commit" step to the same mechanics as Phase
|
||||
4.6 above (fetch issue body → map `- [ ]` items → `工单 API(见 TERMINOLOGY)update`), and
|
||||
the PR-creation step to Phase 4.7.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 6 — Approval
|
||||
|
||||
Present the report:
|
||||
|
||||
```
|
||||
Bug fixed: {one-line description}
|
||||
- Root cause: {file}:{line}
|
||||
- {N} lines changed in {M} files
|
||||
- Regression test added: {test name}
|
||||
- Full test suite: ✅
|
||||
|
||||
→ {if review needed: "Run code review?" | else: "Fix complete. Approve?"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Common Rationalizations (bugfix)
|
||||
|
||||
Bugfixes fail from **pressure and false confidence** far more than from
|
||||
ignorance — "the bug is obvious" is the thought that precedes a symptom patch,
|
||||
a bundled diff, or a regression that surfaces weeks later. These are the
|
||||
excuses that precede every reopened bug. If you catch yourself thinking any
|
||||
row's "Excuse", stop: the "Reality" column is the exact rule you are about to
|
||||
break.
|
||||
|
||||
| Excuse | Reality (the rule being broken) |
|
||||
|--------|---------------------------------|
|
||||
| "Just add a null check where it crashes" | Symptom-patching. Phase 2: if you are fixing a symptom, stop and go deeper — the real defect is whatever let null reach that line. |
|
||||
| "Can't reproduce, but I'm sure it's X" | Phase 1: if the bug cannot be reproduced, stop and report. Guess-patching a non-reproduced bug fixes nothing verifiable. |
|
||||
| "3 levels deep, can't find it, patch the symptom" | Phase 2: after 3 levels of indirection with no root cause, pause and report — do NOT apply a surface patch. |
|
||||
| "Bug's obvious, I'll fix then add the test" | Phase 3: the regression test MUST fail before the fix. Fix-first means you test your fix, not the bug. |
|
||||
| "Test passed immediately, ship it" | Phase 3 Rules: a test that passes before the fix does not cover the bug — rewrite it until it fails. |
|
||||
| "While I'm in this file, also clean up…" | Phase 4 rule 1: one conceptual change per fix. Bundling refactors/style/other-fixes pollutes the regression signal. |
|
||||
| "Make the whole module null-safe while I'm here" | Phase 4 rule 3: update only what is necessary. Over-fixing turns a 3-line surgical fix into a high-risk diff. |
|
||||
| "Fix is isolated, skip the full suite" | Phase 4 rule 5 + Phase 5: the full suite catches regressions your isolated view cannot. |
|
||||
| "4 files but one module, standalone's fine" | Routing Decision: escalate when uncertain. A false-standalone call skips quality gates on a complex change. |
|
||||
|
||||
##### Incident Triage Carve-Out
|
||||
|
||||
When the bugfix occurs under **active production incident** pressure
|
||||
(user-facing outage, on-call escalation), the Phase 2→3 ordering can be
|
||||
**temporarily relaxed** — but never skipped:
|
||||
|
||||
1. A stop-gap (symptom patch) MAY ship first to restore service.
|
||||
2. BUT the full root-cause trace + failing regression test + proper
|
||||
root-cause fix MUST land in the **same incident window** — never deferred
|
||||
to "tomorrow" or "a follow-up ticket".
|
||||
3. If you defer, you have not fixed the bug — you have shipped a symptom patch
|
||||
with a promise. Promises are not regression tests.
|
||||
|
||||
This carve-out exists because the rationalization table above cannot resolve a
|
||||
*legitimate* priority conflict (service down vs process discipline). It
|
||||
resolves it by permitting triage but forbidding deferral.
|
||||
|
||||
---
|
||||
|
||||
#### Pipeline Mode (bugfix)
|
||||
|
||||
> **Legacy path retired ([org-internal #3072] phase 3, 2026-08-21)**: the full
|
||||
> requirements-elicitation → design → review-artifact(design-space) →
|
||||
> plan-iterations → review-artifact(plan) front-end was archived
|
||||
> (`<instance-root>/archive/skills/`). A big bug that needs a design-level decision
|
||||
> now relabels `Kind/Feature` and enters the DAG route (see the big-bug
|
||||
> relabel rule above) — repro + root-cause notes carry over as node input.
|
||||
> The abort criteria below still apply to any multi-stage bug run before
|
||||
> code is written.
|
||||
|
||||
When a bugfix escalates beyond standalone scope, the bug report becomes a
|
||||
pipeline input; the original bugfix phases (reproduce, root cause, regression
|
||||
test, fix) are embedded within the implement stage, and review-code + verify
|
||||
remain mandatory gates.
|
||||
|
||||
##### Pipeline Abort Criteria
|
||||
|
||||
Before any code is written in pipeline mode, abort the pipeline if ANY of:
|
||||
|
||||
| # | Condition | Action |
|
||||
|---|-----------|--------|
|
||||
| 1 | Bug no longer reproduces after environment change (strace re-isolation returns 0 reproductions, user confirms symptom resolved) | Write ABORT to wiki page `{slug}/ABORT` (wiki 读写 API(见 TERMINOLOGY)), preserve all completed artifacts, run retrospective |
|
||||
| 2 | Root cause hypothesis is falsified during re-isolation (e.g., strace shows suspected git spawn is NOT hanging) | Write ABORT.md, escalate to Architect for design revision OR abort pipeline |
|
||||
| 3 | Bug is resolved by external change (new binary build, dependency update, OS/kernel patch) | Write ABORT.md with resolution evidence, close without code changes |
|
||||
| 4 | Reproduction confidence < 3/5 after re-isolation attempt | Write ABORT.md if confidence cannot be improved within 1 re-isolation iteration |
|
||||
|
||||
**Abort procedure**:
|
||||
1. Write ABORT to wiki page `{slug}/ABORT` (wiki 读写 API(见 TERMINOLOGY)) documenting the reason, evidence, and which artifacts are preserved.
|
||||
2. Do NOT commit or merge the bugfix branch (no code was written).
|
||||
3. Run retrospective to extract process improvements.
|
||||
4. Archive artifacts to wiki page `_archive/{slug}/` (wiki 读写 API(见 TERMINOLOGY)) after retrospective.
|
||||
|
||||
**Scope**: these criteria apply before the implement stage. Once code is written, the pipeline proceeds through review-code → verify — abort is no longer valid.
|
||||
|
||||
##### Stage: Implement → Code Review → Verify
|
||||
|
||||
On the DAG route a bug-fix node's spec (ACs tracing to the repro + root cause)
|
||||
lives in `{epic-slug}/dag`; the Developer follows the bugfix Phases 1–6 (from
|
||||
standalone mode above) as the implementation method, then produces the
|
||||
standard implementation report (see ### Mode: implement (default), Phase 5).
|
||||
Code review runs all 10 dimensions against the bugfix changes. Verify runs
|
||||
the full DoD matrix including regression tests, integration tests, and NFR
|
||||
validation. Output pages: code review → `{slug}/reviews/code/final/report`;
|
||||
verification → `{slug}/05-verify-iteration-1`.
|
||||
@@ -0,0 +1,76 @@
|
||||
# Capability Boundary — Dimension Table & Output Template
|
||||
|
||||
> Used by Phase A1.8 (standalone) and Phase B1.8 (pipeline).
|
||||
> Read this file when executing the Capability Boundary Definition phase.
|
||||
> The 13-dimension table defines WHAT to analyze; the output template defines
|
||||
> HOW to record it.
|
||||
|
||||
## Artifact Dimensions
|
||||
|
||||
Every capability MUST be analyzed across ALL of these dimensions. A dimension
|
||||
with no artifacts is explicitly marked "N/A — none required" (not silently
|
||||
skipped):
|
||||
|
||||
| # | Dimension | What to list | Why it matters |
|
||||
|---|-----------|-------------|----------------|
|
||||
| 1 | **Source code files** | Every .ts/.tsx/.js file in the feature scope | The obvious one — but not the only one |
|
||||
| 2 | **Type definitions / interfaces** | Shared types, branded types, schemas (Zod/Schema.Class) | Types are consumed across files; missing types break compilation silently |
|
||||
| 3 | **Database schema / migrations** | Table definitions, column additions, migration SQL | Data layer changes are invisible in code diffs but block runtime |
|
||||
| 4 | **Configuration entries** | Config keys, settings entries, default values | Missing config = silent runtime failures |
|
||||
| 5 | **Environment variables** | Env vars read by the feature, VITE_* vars | Missing env vars = undefined behavior at runtime |
|
||||
| 6 | **CLI flags / commands** | CLI commands, flags, option definitions | CLI surface changes are easily forgotten |
|
||||
| 7 | **Theme / style files** | CSS files, theme JSON, tailwind config, token files | Styling is per-component and easily orphaned |
|
||||
| 8 | **Route definitions** | New routes, modified redirects, route guards | Routes are defined in a central file far from the feature code |
|
||||
| 9 | **Provider / context hierarchy** | New providers, insertion points in provider tree, context keys | Provider ordering bugs are silent and hard to debug |
|
||||
| 10 | **Build config changes** | vite.config, tsconfig, webpack, tailwind.config | Build config gates whether the feature compiles/bundles |
|
||||
| 11 | **Package.json dependencies** | New npm deps, version changes, workspace dep additions | Missing deps = import errors at runtime |
|
||||
| 12 | **Test files** | Unit tests, integration tests, test fixtures, test helpers | Tests are the fidelity contract — missing tests = unverified behavior |
|
||||
| 13 | **Shared package changes** | Changes to SDK, UI, core packages that the feature depends on | Cross-package deps are the #1 source of incomplete ports |
|
||||
|
||||
## Output Template
|
||||
|
||||
Publish to wiki page `port-{name}/source-analysis/capability-boundary` (standalone) or
|
||||
`port-{name}/source-analysis/12-capability-boundary` (pipeline) via `wiki 读写 API(见 TERMINOLOGY)`.
|
||||
|
||||
```markdown
|
||||
## Capability Boundary: {feature name}
|
||||
|
||||
### Dimension 1 — Source Code Files
|
||||
| Source File | Target Location | Status | Notes |
|
||||
| ----------- | --------------- | ------ | ----- |
|
||||
| src/context/tabs.tsx | src/context/tabs.tsx | ☐ | New file |
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
### Dimension 2 — Type Definitions / Interfaces
|
||||
| Source Type | Target Location | Status | Notes |
|
||||
| ----------- | --------------- | ------ | ----- |
|
||||
| Tab interface | src/context/tabs.tsx | ☐ | Co-located |
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
### Dimension 3 — Database Schema / Migrations
|
||||
| Source Schema | Target Migration | Status | Notes |
|
||||
| ------------- | ---------------- | ------ | ----- |
|
||||
| N/A — none required | — | ⏭ | Feature uses in-memory state only |
|
||||
|
||||
### Dimension 4 — Configuration Entries
|
||||
| Source Config Key | Target Config Key | Status | Notes |
|
||||
| ----------------- | ----------------- | ------ | ----- |
|
||||
| tabs.enabled | tabs.enabled | ☐ | New setting |
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
### Dimension 5 — Environment Variables
|
||||
| Source Env Var | Target Env Var | Status | Notes |
|
||||
| -------------- | -------------- | ------ | ----- |
|
||||
| VITE_TABS_LIMIT | VITE_TABS_LIMIT | ☐ | New |
|
||||
| N/A | — | ⏭ | No env vars required |
|
||||
|
||||
(... repeat for all 13 dimensions ...)
|
||||
|
||||
### Completeness Cross-Check
|
||||
| Cross-Check | Result |
|
||||
| ----------- | ------ |
|
||||
| Every A1 Source Function Inventory item appears in D1 or D2? | ✅ / ❌ |
|
||||
| Every A1.7 Structural Diff gap has a capability boundary entry? | ✅ / ❌ |
|
||||
| All 13 dimensions filled in? | ✅ / ❌ |
|
||||
| All ☐ items have deferral + reactivation path or are pre-implementation? | ✅ / ❌ |
|
||||
```
|
||||
@@ -0,0 +1,77 @@
|
||||
# Fidelity Verification — Detailed Processes
|
||||
|
||||
> Extracted from `implement/SKILL.md` (Mode: port) Phase A6 and B5.
|
||||
> Read this file when executing the test porting and fidelity verification phases.
|
||||
|
||||
---
|
||||
|
||||
## Phase A6 — Port Tests & Verify Fidelity
|
||||
|
||||
1. **Port every test** from the source — not just the happy path. Edge cases,
|
||||
error paths, and boundary tests must all be ported.
|
||||
2. **Rewrite assertions** to match the target test framework's assertion style.
|
||||
3. **Run ported tests** — they must pass. If a test fails:
|
||||
- Behavior mismatch: fix the implementation to match source behavior.
|
||||
- Test logic error (e.g. wrong assertion library syntax): fix the test.
|
||||
- Infrastructure gap (e.g. test tried to connect to Redis): adapt the test
|
||||
to the alternative from Phase A4.
|
||||
4. **Full test suite** — run the target project's existing tests + ported
|
||||
tests. No regression in existing tests.
|
||||
5. **Fidelity checklist** — for each source behavior, verify manually or
|
||||
automatically:
|
||||
|
||||
### Fidelity Verification Template
|
||||
|
||||
```markdown
|
||||
## Fidelity Verification
|
||||
|
||||
| Source Behavior | Tested? | Result | Notes |
|
||||
| --------------------------------- | ------------- | ------ | -------------------------------------------- |
|
||||
| User login with valid credentials | ✅ ported | PASS | |
|
||||
| User login with invalid password | ✅ ported | PASS | |
|
||||
| Session expiry at 1h | ✅ ported | PASS | Adapted to DB session store |
|
||||
| Rate limiting: 5 attempts / min | ⚠️ not ported | — | Target has no rate limiting infra → deferred |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase B5 — Verify (Integration + Fidelity)
|
||||
|
||||
After all chunks are implemented and reviewed, run
|
||||
`core/skills/verify/SKILL.md` with the fidelity baseline
|
||||
(wiki page `port-{name}/source-analysis/10-fidelity-baseline`, read via `wiki 读写 API(见 TERMINOLOGY)`) as the
|
||||
acceptance criteria:
|
||||
|
||||
- Every `FID-*` item must be traced to a passing test in the target project.
|
||||
- Full test suite (existing + ported) must pass with no regressions.
|
||||
- Typecheck and lint must be clean.
|
||||
- Fidelity deviations from any chunk are consolidated into a final
|
||||
fidelity report.
|
||||
- **Reverse coverage (Ported? audit)**: The Source Function Inventory
|
||||
(produced in Phase A1 / B1) "Ported?" column must have **zero** unexplained
|
||||
☐ entries. Any residual ☐ MUST carry a matching `[DEFER]` row — with a
|
||||
reactivation trigger — in the Port Fidelity Report. A bare ☐ is a FAIL,
|
||||
not a deferral. This closes the #1 port-completeness gap: functions that
|
||||
were never ported and never consciously deferred.
|
||||
- **Symbol-level completeness (SRC-CMP)**: Run an automated export-symbol
|
||||
diff between source and target packages. Every source symbol absent from
|
||||
the target MUST appear as `[DEFER]` in the report; a silent gap is a FAIL.
|
||||
|
||||
```bash
|
||||
diff <(codegraph exports <source-pkg>) <(codegraph exports <target-pkg>)
|
||||
# Each left-only symbol must be DEFER'd or ported — silent gaps fail B5.
|
||||
```
|
||||
|
||||
### Port Fidelity Report Template
|
||||
|
||||
```markdown
|
||||
## Port Fidelity Report
|
||||
|
||||
| FID-* | Behavior | Chunk | Target Test | Status |
|
||||
| ------- | ------------------ | ----------- | -------------------- | ------ |
|
||||
| FID-001 | Login valid creds | chunk-auth | auth/login.test.ts | PASS |
|
||||
| FID-002 | Login invalid pw | chunk-auth | auth/login.test.ts | PASS |
|
||||
| FID-004 | Rate limiting | chunk-auth | — | DEFER |
|
||||
|
||||
**Summary**: {X}/{Y} behaviors verified, {Z} deferred
|
||||
```
|
||||
@@ -0,0 +1,592 @@
|
||||
> Extracted from implement/SKILL.md (Mode: implement) — moved verbatim 2026-08-25, ticket [org-internal #3381].
|
||||
|
||||
### Mode: implement (default)
|
||||
|
||||
The standard implementation workflow for work items from an approved
|
||||
iteration plan. Implement a single work item, guided by the approved design,
|
||||
and self-verify before passing to code review.
|
||||
|
||||
#### Pre-flight
|
||||
|
||||
The pre-flight self-check prompt format ([org-internal #2599]), prepended to the Developer
|
||||
sub-agent's task prompt when `routes.{Kind}.preflight` is non-empty:
|
||||
|
||||
```
|
||||
Pre-flight self-check (evidence-based, from retrospective — verify each
|
||||
BEFORE writing code; if one is already satisfied, note why in impl-notes):
|
||||
1. {item} (evidence: {evidence})
|
||||
2. ...
|
||||
```
|
||||
|
||||
#### Process Overview
|
||||
|
||||
Every diamond below is a gate agents rationalize skipping. None are optional.
|
||||
|
||||
```dot
|
||||
digraph implement {
|
||||
rankdir=TB;
|
||||
node [shape=box, fontname="Helvetica"];
|
||||
|
||||
pre [shape=diamond, label="Preconditions\n(artifacts + reviews\nconverged)?"];
|
||||
abort [label="ABORT: list every\nmissing item"];
|
||||
p1 [label="Phase 1: Parse Context"];
|
||||
p2 [label="Phase 2: Plan\n(≤3 files per WI)"];
|
||||
scope [shape=diamond, label="Scope ≤3 files\nAND maps to a\ndesign component?"];
|
||||
gap [label="Flag design gap,\nDO NOT invent decisions"];
|
||||
p3 [label="Phase 3: Implement\n(design-exact, tests cover AC)"];
|
||||
p4 [label="Phase 4: Self-Check\n(typecheck + lint +\ntest:changed + review checklist)"];
|
||||
clean [shape=diamond, label="0 BLOCKERs\nand 0 MAJORs?"];
|
||||
p5 [label="Phase 5: Report\n(AC → test traceability)"];
|
||||
p6 [shape=doublecircle, label="Phase 6: Handoff\nto review-code"];
|
||||
|
||||
pre -> abort [label="no"];
|
||||
pre -> p1 [label="yes"];
|
||||
p1 -> p2;
|
||||
p2 -> scope;
|
||||
scope -> gap [label="no"];
|
||||
scope -> p3 [label="yes"];
|
||||
p3 -> p4;
|
||||
p4 -> clean;
|
||||
clean -> p4 [label="no: fix + re-run"];
|
||||
clean -> p5 [label="yes"];
|
||||
p5 -> p6;
|
||||
}
|
||||
```
|
||||
|
||||
#### Tester focus for implement
|
||||
|
||||
The Tester role in implement writes **boundary + contract tests**:
|
||||
|
||||
- **Contract tests** — for every public API signature in `impl-notes.md`,
|
||||
verify the documented inputs/outputs, error paths, and side effects.
|
||||
Each acceptance criterion (node `acceptance_criteria` in `{epic-slug}/dag`;
|
||||
historically `04-plan-05-acceptance-criteria`) MUST map to at least one
|
||||
test.
|
||||
- **Boundary tests** — empty values, malformed input, permission
|
||||
boundaries, concurrency edges, and the edge cases the node spec's decision
|
||||
tables / state machines imply.
|
||||
- **Failure-path tests** — every error scenario the node's cross-session
|
||||
edge contracts (historically the interface design,
|
||||
`03-design-04-interface-design`) specify.
|
||||
|
||||
The Developer's Phase 4 self-check (`bun run test:changed` to green) covers
|
||||
the happy path and existing tests; the Tester's job is the cases the
|
||||
Developer is structurally biased to miss.
|
||||
|
||||
#### Preconditions
|
||||
|
||||
> **Publish target (tiered targeting retired, [org-internal #3072] phase 3)**: the
|
||||
> `Size/*`-tiered publish rule (`rules/workflow-routing.md` §"Publish target
|
||||
> by Size/* tier — RETIRED") was retired with the legacy producer skills.
|
||||
> Artifacts publish where the live mode puts them: DAG task mode → node spec
|
||||
> in the frozen `{epic-slug}/dag` copy (see the DAG-mode input path below);
|
||||
> standalone bugfix → `{slug}/bugfix-report` + issue body per bugfix Phase 5.
|
||||
> Legacy tiered locations (`{slug}/02-03-req-design`, `{slug}/04-plan-*`, …)
|
||||
> stay readable for historical runs via `_shared/gitea-read-patterns.md`.
|
||||
|
||||
> **DAG-mode input path** (DAG ticket pipeline — `Kind/Epic` / `Kind/Feature`
|
||||
> DAG parent, routes-table direct):
|
||||
> DAG-routed tickets **ignore `Size/*`** (`core/skills/analyze-dag/SKILL.md`).
|
||||
> When the ticket routes through the DAG pipeline, the tiered Preconditions
|
||||
> below are replaced by the node spec: the work item and its acceptance
|
||||
> criteria resolve from the **frozen DAG copy** wiki page `{epic-slug}/dag`
|
||||
> (and the `{epic-slug}/dag-nodes/{node-id}` subpages when AC detail is sunk)
|
||||
> plus the node ticket's issue body — there is no `{slug}/04-plan-*` page and
|
||||
> no `Size/*`-tiered req/design page. The design-space + iteration-plan review
|
||||
> convergence preconditions are replaced by the **review-dag single-gate
|
||||
> convergence**: `octopus review status --stage review-dag` must show state
|
||||
> `success` before the node is implemented.
|
||||
|
||||
> **DAG-route read map** (applies to Phase 1 read inputs and the Phase 3/4
|
||||
> artifact references below — mirror `verify/SKILL.md`'s DAG branch): when
|
||||
> DAG-routed, resolve each legacy tiered artifact reference (any mention below
|
||||
> of `{slug}/04-plan-*` / `{slug}/03-design-*` pages) from the frozen
|
||||
> DAG copy instead:
|
||||
>
|
||||
> - Work item — `{slug}/04-plan-04-iteration-assignment` / issue body → the
|
||||
> node spec in `{epic-slug}/dag` + the node ticket's issue body.
|
||||
> - Acceptance criteria — `{slug}/04-plan-05-acceptance-criteria` / issue body
|
||||
> → the node `acceptance_criteria` in `{epic-slug}/dag` (+
|
||||
> `{epic-slug}/dag-nodes/{node-id}` subpages when AC detail is sunk) + the
|
||||
> node ticket's issue body.
|
||||
> - `test_id` (測試用例 ID) declared in `04-plan-05-acceptance-criteria` → the
|
||||
> `test_id` declared on the node AC in `{epic-slug}/dag`.
|
||||
> - Design sections — `{slug}/03-design-**` / `{slug}/02-03-req-design` → the
|
||||
> node spec + cross-session edge contracts in the frozen DAG copy (design
|
||||
> detail is folded into node AC + contracts; there is no `{slug}/03-design-*`
|
||||
> page).
|
||||
> - Interface design — `03-design-04-interface-design` → the node's
|
||||
> cross-session edge contracts in `{epic-slug}/dag`.
|
||||
> - Component mapping — `{slug}/03-design-08-traceability` → the node
|
||||
> `req_refs` + component field in `{epic-slug}/dag`.
|
||||
>
|
||||
> At Phase 6 handoff, pass `mode: "dag-task"` to review-code (its DAG Task
|
||||
> Mode keys off the same frozen-DAG-copy detection).
|
||||
|
||||
Before starting implementation, confirm:
|
||||
|
||||
> **Legacy pipeline preconditions retired ([org-internal #3072] phase 3, 2026-08-21)**: the
|
||||
> tier-dependent requirements/design/plan artifact-existence checks and the
|
||||
> design-space / iteration-plan review-convergence checks that used to head
|
||||
> this list belonged to the archived legacy pipeline (`<instance-root>/archive/`).
|
||||
> Live input modes: DAG task mode (node spec from the frozen
|
||||
> `{epic-slug}/dag` copy — see the DAG-mode input path above; convergence
|
||||
> precondition = `octopus review status --stage review-dag` shows `success`)
|
||||
> and standalone modes (bugfix / refactor / port — the request itself is the
|
||||
> spec). Historical req/design/plan pages stay readable via
|
||||
> `_shared/gitea-read-patterns.md`.
|
||||
|
||||
- [ ] Work item is specified (DAG node ticket `{node-id}`, or a clear task
|
||||
description in standalone modes).
|
||||
- [ ] `core/checklists/implementation.md` is accessible.
|
||||
- [ ] `slug` matches the run's slug (DAG: `{epic-slug}`).
|
||||
- [ ] 跨阶段门控清单: `core/checklists/pipeline-gate.md` is accessible and
|
||||
its DAG 路由变体 section has been confirmed item by item. Specifically:
|
||||
the frozen DAG copy exists and the single gate has converged; the
|
||||
node's cross-session upstream dependencies are at terminal state
|
||||
(`ready`). If any dependency is not complete → abort, listing the
|
||||
blocked nodes.
|
||||
|
||||
**If any precondition is unmet, abort and inform the user.** Refer to
|
||||
`core/checklists/pipeline-gate.md` for the complete gate checklist. List
|
||||
every missing artifact, every un-converged review, and every blocked dependency
|
||||
explicitly so the user knows exactly what upstream work remains before
|
||||
implementation can begin. Refer to the Recovery Protocol in
|
||||
`core/checklists/pipeline-gate.md` to determine the recovery action for
|
||||
each missing item.
|
||||
|
||||
#### Work-item selection
|
||||
|
||||
When the user requests implementation without specifying a work item, resolve
|
||||
the work-item list from the frozen DAG copy: the ready/pending task nodes in
|
||||
`{epic-slug}/dag` (via `wiki 读写 API(见 TERMINOLOGY)`), cross-checked against the
|
||||
node tickets on the Epic's `## DAG 状态` table. (Legacy tier-based resolution
|
||||
via `{slug}/04-plan-04-iteration-assignment` was archived 2026-08-21,
|
||||
[org-internal #3072] phase 3.) Present the current ready nodes for selection:
|
||||
|
||||
```
|
||||
Current iteration: Iteration {N}: {Goal}
|
||||
Available work items:
|
||||
| Work Item | Description | Complexity | Status |
|
||||
|-----------|-------------|------------|--------|
|
||||
| WI-001 | ... | 3 | PENDING |
|
||||
| WI-002 | ... | 2 | PENDING |
|
||||
|
||||
→ Which work item should be implemented?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 1 — Parse Context
|
||||
|
||||
> **Pipeline stage**: if the source issue exists, move it to the `implement`
|
||||
> column on the Pipeline Stages board per `_shared/gitea-write-patterns.md`
|
||||
> Pattern 7.5. Skip if no source issue exists.
|
||||
|
||||
Read the upstream artifacts to build a complete implementation context.
|
||||
Resolve inputs per the DAG-route read map (Preconditions above); standalone
|
||||
modes read the request/bug report instead:
|
||||
|
||||
1. **Work item** — the node spec in `{epic-slug}/dag` (+ the
|
||||
`{epic-slug}/dag-nodes/{node-id}` subpage when detail is sunk) and the
|
||||
node ticket's issue body:
|
||||
- Node id, title, complexity (`size_attrs`).
|
||||
- Requirements covered (`req_refs`).
|
||||
- Component(s) involved (node component field).
|
||||
|
||||
2. **Acceptance criteria** — the node `acceptance_criteria` in
|
||||
`{epic-slug}/dag` (+ sunk subpages) and the node ticket's issue body:
|
||||
- Every falsifiable AC (`AC-{n}`) and `NFR:` entry.
|
||||
- The declared 测试用例 ID (`test_id`) for each criterion — these drive the
|
||||
Red → Green test-first order in Phase 3 and are the handshake with `verify`
|
||||
(DOD-1.6).
|
||||
|
||||
3. **Design context** — the node spec + the node's cross-session edge
|
||||
contracts in the frozen DAG copy (design detail is folded into node AC +
|
||||
contracts; there is no separate design page). Historical
|
||||
`{slug}/03-design-*` pages from legacy runs stay readable.
|
||||
|
||||
4. **Existing codebase** — use `glob` and `grep` to locate:
|
||||
- Existing files in the component's directory.
|
||||
- Existing tests.
|
||||
- Existing type definitions, schemas, configuration files the work item
|
||||
touches.
|
||||
|
||||
**Output**: internal only. The Developer MUST have read every referenced
|
||||
design file before writing a single line of code.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 2 — Plan Implementation
|
||||
|
||||
Before writing code, produce a brief implementation plan:
|
||||
|
||||
```markdown
|
||||
## Implementation Plan: {WI-ID}
|
||||
|
||||
**Work item**: {description}
|
||||
**Files to create**:
|
||||
|
||||
- `path/to/new/file.ts` — {purpose}
|
||||
|
||||
**Files to modify**:
|
||||
|
||||
- `path/to/existing/file.ts` — {what changes, why}
|
||||
|
||||
**Design compliance**:
|
||||
|
||||
- Component: {COMP-XXX} from {design-file}
|
||||
- Interface: {iface-name} from {design-file}
|
||||
- Data entity: {entity-name} from {design-file}
|
||||
|
||||
**Acceptance criteria to satisfy**:
|
||||
|
||||
- [ ] {criterion 1}
|
||||
- [ ] {criterion 2}
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
|
||||
- If the implementation plan reveals that the work item touches > 3 files,
|
||||
pause and ask: "This work item spans {N} files. Is the scope correct, or
|
||||
should it be split?" The Builder (or user) MUST split it into smaller
|
||||
work items each touching ≤ 3 files before proceeding.
|
||||
- If the work item requires a file that doesn't map to any design component,
|
||||
flag a design gap and abort. Do NOT invent design decisions.
|
||||
|
||||
Present the plan to the user:
|
||||
|
||||
```
|
||||
Implementation plan for {WI-ID}:
|
||||
- {N} files to create, {M} files to modify
|
||||
- {K} acceptance criteria
|
||||
|
||||
→ Proceed? (yes / no / revise)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 3 — Implement
|
||||
|
||||
Write code following these rules:
|
||||
|
||||
##### Design Discipline
|
||||
|
||||
- Component interfaces, method signatures, and return types MUST match the
|
||||
design document exactly.
|
||||
- Data model fields, types, and relationships MUST match the data design.
|
||||
- API endpoints, request/response schemas, and status codes MUST match the
|
||||
interface design.
|
||||
- If a design decision proves impossible in practice, stop and report the gap
|
||||
to the Builder. Do NOT silently deviate.
|
||||
|
||||
##### Code Quality
|
||||
|
||||
- Follow existing project conventions (read neighbor files first to
|
||||
understand patterns).
|
||||
- Use existing libraries and utilities already in the codebase — do not
|
||||
introduce new dependencies without explicit justification.
|
||||
- Keep functions small and single-purpose — but per `rules/style-guide`, do NOT
|
||||
preemptively extract single-use helpers; inline at the call site unless the
|
||||
helper is reused, hides a genuinely complex boundary, or has a clear
|
||||
independent name that improves the caller.
|
||||
- Handle errors at the appropriate layer (matching the design's error
|
||||
handling strategy).
|
||||
- Write self-documenting code; add comments only for genuinely non-obvious
|
||||
logic.
|
||||
- Document all new/modified public APIs inline (JSDoc/TSDoc/pydoc/etc.)
|
||||
with parameter descriptions, return types, and thrown errors.
|
||||
- If the project has an API documentation file (e.g. OpenAPI spec, API.md),
|
||||
update it to reflect the new endpoints, schemas, or behavior changes.
|
||||
|
||||
##### Test Discipline
|
||||
|
||||
- **Test-first (Red → Green) for declared test_ids.** For every acceptance
|
||||
criterion (node AC in `{epic-slug}/dag`, whose `test_id` mapping is declared
|
||||
inline; historically the `04-plan-05-acceptance-criteria` table) that
|
||||
declares a `test_id`, write that test FIRST and confirm it fails for the
|
||||
intended reason (Red) before writing the implementation that satisfies it
|
||||
(Green). The test's `file-path :: test-name` MUST match the declared
|
||||
`test_id` exactly — this is the implement-side handshake with `verify`
|
||||
(DOD-1.6). A `test_id` marked `MANUAL` or `BENCH:<script>` is implemented
|
||||
per its method and is exempt from the Red step. If a test already passes
|
||||
against existing code (the behavior is already present), note it in the
|
||||
Phase 5 report rather than forcing an artificial failure.
|
||||
- Write tests that verify the acceptance criteria.
|
||||
- Tests must be independent (no shared mutable state).
|
||||
- Test edge cases identified in the acceptance criteria.
|
||||
- Test failure paths that the design specifies.
|
||||
|
||||
##### Incremental Commitments
|
||||
|
||||
- Implement in dependency order within the work item: shared types first,
|
||||
then data access, then business logic, then API handlers.
|
||||
- After each coherent unit, run typecheck to catch errors early.
|
||||
|
||||
---
|
||||
|
||||
#### Common Rationalizations
|
||||
|
||||
Implementation fails far more often from **pressure** than from ignorance — the
|
||||
Developer knows the rules and rationalizes skipping them under context or time
|
||||
pressure. These are the excuses that precede every review blocker and silent
|
||||
defect. If you catch yourself thinking any row's "Excuse", stop: the "Reality"
|
||||
column is the exact rule you are about to break, and breaking it is what turns
|
||||
a one-pass implementation into a multi-round review.
|
||||
|
||||
| Excuse | Reality (the rule being broken) |
|
||||
|--------|---------------------------------|
|
||||
| "Design says X, but Y is simpler/better" | Silent deviation is a hidden design gap. Phase 3 Design Discipline: stop and report to the Builder — never silently deviate. |
|
||||
| "Small change, a test is overkill" | A one-line edit can break a contract. Every acceptance criterion maps to ≥1 test (Phase 4 Brownfield check). 30 seconds now vs. a review blocker later. |
|
||||
| "I'll write tests after it works" | Tests-after verify what you built, not what was required — you test your own bias, not the spec. |
|
||||
| "Typecheck passed, lint is cosmetic" | Lint is a Phase 4 gate, not optional polish. Failing lint is an automatic review blocker. |
|
||||
| "Self-check passed, I'll trust it" | Rubber-stamping misses the MAJORs the formal review will catch. Rule: if YOU can find a MAJOR, fix it now — the first review should never discover what you could have. |
|
||||
| "This neighbor looks buggy, I'll fix it too" | Scope creep. Log it as an observation in the report; do not fix unrelated code (Greenfield/Brownfield rule). |
|
||||
| "Spans 5 files but it's one logical change" | The ≤3-files rule is structural, not aesthetic. Split the work item via the Builder (Phase 2 rule). |
|
||||
| "Design is ambiguous here, I'll pick the obvious option" | Inventing a design decision is a Phase 2 abort condition. Flag the gap; do not guess. |
|
||||
| "Already manually verified it works" | Manual ≠ systematic — no record, can't re-run, can't bisect. `bun run test:changed` is the evidence the report demands. |
|
||||
| "Report is busywork, the diff speaks for itself" | No report → review-code cannot trace AC→test. Phase 5 is the handoff contract; skip it and the review stalls. |
|
||||
| "X× improvement — assumed, no measurement" | Quick-measure before it becomes an AC. Unverified assumptions in ACs waste framing cost ([org-internal #1932]: YAML token density assumed 2-3×, measured 0.95 — hypothesis rejected by data). |
|
||||
|
||||
---
|
||||
|
||||
#### Phase 4 — Self-Check
|
||||
|
||||
After writing all code, run the project's verification commands:
|
||||
|
||||
1. **Typecheck**: `bun typecheck` (or project-equivalent). Fix all type errors.
|
||||
2. **Lint**: `bun oxlint --deny-warnings` (repo root — the review-code
|
||||
mechanical gate's canonical lint invocation; `bun lint` is the package-script
|
||||
alias). Fix all lint errors.
|
||||
3. **Tests**: `bun run test:changed` (or project-equivalent). All affected tests must pass.
|
||||
4. **Post-deletion cleanup** (mandatory when any code was removed): If files or code blocks were deleted (dead code, test cleanup, refactored-out modules), re-run `bun oxlint --deny-warnings` specifically to catch orphaned imports and unused variables — these are the most common post-deletion regressions. Re-run `bun typecheck` to catch orphaned type references to deleted modules.
|
||||
|
||||
Then self-check against `core/checklists/implementation.md`:
|
||||
|
||||
- Verify every checklist item marked PRE (pre-implementation) was satisfied
|
||||
before coding.
|
||||
- Verify every checklist item marked POST (post-implementation) is satisfied
|
||||
now.
|
||||
- For any failed checklist item, fix the code before reporting.
|
||||
|
||||
##### Brownfield Self-Check (additional)
|
||||
|
||||
For brownfield work items, additionally:
|
||||
|
||||
1. **Design spec cross-check**: Re-read the node's cross-session edge
|
||||
contracts in the frozen DAG copy (historically the design's interface
|
||||
design section, `03-design-04-interface-design`). Verify every interface
|
||||
promise — method signatures, return types, output formats, error messages,
|
||||
config field names, param descriptions — is satisfied exactly as specified.
|
||||
Schema annotations MUST match actual code behavior.
|
||||
2. **Test coverage**: For each new function, method, or exported API added,
|
||||
confirm at least one test exercises it. If `bun run test:changed` reports zero new
|
||||
tests, add them before handoff.
|
||||
|
||||
##### Review Readiness Self-Check (mandatory before handoff)
|
||||
|
||||
Before submitting to code review, the Developer MUST self-attest against the
|
||||
code review checklist. This reduces round-trips by catching common defects
|
||||
before the first review submission. **The self-check must achieve 0 BLOCKERs
|
||||
and 0 MAJORs before handoff** — if the Developer can find a MAJOR issue during
|
||||
self-check, the formal reviewers will find it too.
|
||||
|
||||
1. **Run the code review checklist**: Read `core/checklists/code-review.md`
|
||||
and self-attest that the code likely passes, for each of its 10 dimensions
|
||||
(COR, DGN, SEC, PERF, TST, STY, DBT, A11Y, DOC, TRC — the authoritative
|
||||
dimension set lives in the checklist's section headers and
|
||||
`review-code/reference/code-review-dimensions.md`; do NOT hand-maintain a
|
||||
copy here).
|
||||
2. Record the self-attestation in the Phase 5 report under "Review Readiness"
|
||||
as a pass/fail per dimension. Any FAIL dimension MUST be fixed before handoff.
|
||||
3. **Hard gate**: self-check MUST find 0 BLOCKERs and 0 MAJORs. If the
|
||||
Developer finds even one MAJOR, fix it and re-run self-check before handoff.
|
||||
The first formal code review should never discover issues the Developer
|
||||
could have caught themselves.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 4.5 — Iteration Completion Commit
|
||||
|
||||
After ALL work items in the current iteration have been implemented and passed
|
||||
Self-Check (Phase 4), create a git commit BEFORE proceeding to the next
|
||||
iteration. This preserves per-iteration traceability and enables `git bisect`
|
||||
per iteration.
|
||||
|
||||
##### Commit Rules
|
||||
|
||||
1. Commit after the last WI of the iteration is done and self-checked.
|
||||
2. Commit message format: `[{chunk-id}][{iteration}] {summary}`.
|
||||
- Example: `[chunk-resolution][iter-1] feat: add two-pass chain resolution engine`
|
||||
3. **Commit body is REQUIRED for non-trivial commits** (any commit touching > 1 file
|
||||
or > 20 LOC). The body MUST contain:
|
||||
- **What**: a 1-3 line summary of the changes (files + purpose), including
|
||||
the work item ID (`WI-{NNN}`) the commit delivers — code-review TRC 10.1
|
||||
requires the commit/PR description to carry the work item ID.
|
||||
- **Why**: the design/requirement motivation (cite REQ-ID or ADR if applicable).
|
||||
- **Evidence**: test names or verification commands run (e.g. `90 compaction
|
||||
tests pass`).
|
||||
- Subject-only commits are acceptable only for single-line fixes or doc tweaks.
|
||||
4. Include all source + test files from the iteration.
|
||||
5. After commit, proceed to Phase 4.6 (Issue Checklist Sync), then Phase 5
|
||||
(Report) for the iteration, then start the next iteration's WIs.
|
||||
|
||||
##### Multi-Iteration Workflow
|
||||
|
||||
```
|
||||
Iteration 1 WIs → Self-Check → Commit [iter-1] → Checklist Sync → Code Review →
|
||||
Iteration 2 WIs → Self-Check → Commit [iter-2] → Checklist Sync → Code Review → Merge
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 4.6 — Issue Checklist Sync (progressive)
|
||||
|
||||
After committing the iteration, sync the source issue's checklist so
|
||||
stakeholders see progress in real time. This is mandated by the
|
||||
`issue-checklist-sync` L1 rule — follow its "How to sync (each point)"
|
||||
procedure (identify source issue → fetch body → map → update, preserving
|
||||
non-checklist content); this phase adds only the implement-specific annotation:
|
||||
|
||||
- **Stage-specific row**: for each `- [ ]` item the iteration's work satisfies,
|
||||
mark `- [x]` and append `_(commit {sha}: file/component)_` or
|
||||
`_(PR #NNN: file)_`.
|
||||
- **Do NOT touch items outside this iteration's scope** — they will be caught
|
||||
at a later sync point (next iteration, DAG-freeze aggregation sync, or
|
||||
verify Phase 5.6). Only check off what this iteration actually delivered.
|
||||
|
||||
This is a **progressive** sync: the checklist fills in incrementally as
|
||||
iterations complete, giving stakeholders a live view of progress without
|
||||
waiting for the final verify gate.
|
||||
|
||||
#### Phase 4.7 — PR-Creation Sync
|
||||
|
||||
The session pushes its branch and reports `status=done branch=<ref> verify=…
|
||||
risk=…`; the orchestrator admits the PR (serially, one open at a time) —
|
||||
workers never open PRs (TD-678/[org-internal #4425]; `uncoordinated` self-open only when
|
||||
the orchestrator is unreachable). Once that PR exists, update the source
|
||||
issue so stakeholders see the mergeable state without waiting for code
|
||||
review. Mandated by the `issue-checklist-sync` L1 rule; skip if no source
|
||||
issue exists.
|
||||
|
||||
> PR shape per mode: default = one 1:1 PR per task (body carries the worker
|
||||
> report); batch-mode epics ([org-internal #3731], per-epic opt-in) = the orchestrator
|
||||
> composes ONE batch PR per iteration via the `land-batch` skill. This phase
|
||||
> then runs per member issue as usual (N times), each pointing at its PR
|
||||
> (batch: the single batch PR); the poller writes the PR/CI/review rows
|
||||
> against every member issue (multi-close-ref fan-out).
|
||||
|
||||
1. Re-fetch the issue body via `工单 API(见 TERMINOLOGY)get`.
|
||||
2. **Ensure the `## 当前状态` live-status section exists** (create it if
|
||||
absent — MANDATORY for incident / standalone-bugfix flows; for quiet
|
||||
pipeline flows, create it only if it already exists, otherwise skip). The
|
||||
`PR` row itself is written by the `status-sync` poller
|
||||
(`.gitea/scripts/status-sync-poll.ts`), NOT this skill — do NOT manually
|
||||
`工单 API(见 TERMINOLOGY)update` the PR / 代码评审 / CI rows (per
|
||||
`issue-checklist-sync.md` § Automated sync).
|
||||
3. If this is an Epic task list, append the PR reference to the row that this
|
||||
iteration's work corresponds to.
|
||||
4. Preserve all non-checklist content.
|
||||
5. **Never hand-sync main into the PR branch.** Keeping the PR mergeable is
|
||||
the keep-mergeable workflow's job: once review converges the orchestrator
|
||||
labels the PR `ready-to-merge` and the server-side keep-mergeable cron
|
||||
(`.gitea/scripts/keep-mergeable.ts`, driven by
|
||||
`script/keep-mergeable-cron.sh` under a systemd timer) fetches the PR head,
|
||||
probes `merge-tree --write-tree`, and pushes a non-force `commit-tree` merge
|
||||
into the head branch (the retired `POST /pulls/{n}/update-branch` API path
|
||||
returned 405 on this instance — see AGENTS.md "PR keep-mergeable").
|
||||
Hand-written `chore: merge origin/main (keep PR mergeable)` commits are
|
||||
retired — each one re-triggered the full CI surface for near-zero re-tested
|
||||
risk.
|
||||
|
||||
> **Kanban column lifecycle**: automated (`工单 API(见 TERMINOLOGY)create` → Backlog,
|
||||
> `gitea_pull__create` → Review; no manual moves). Single shared reference:
|
||||
> `_shared/gitea-write-patterns.md` Pattern 7.5; column semantics: wiki
|
||||
> `kanban-lifecycle`.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 5 — Report
|
||||
|
||||
Produce an implementation report:
|
||||
|
||||
```markdown
|
||||
## Implementation Report: {WI-ID}
|
||||
|
||||
**Work item**: {description}
|
||||
**Iteration**: {iteration number}: {goal}
|
||||
|
||||
### Files Changed
|
||||
|
||||
| File | Action | Purpose |
|
||||
| ------------------ | -------- | -------------- |
|
||||
| `path/to/file.ts` | created | {purpose} |
|
||||
| `path/to/other.ts` | modified | {what changed} |
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
| Criterion | Status | Evidence |
|
||||
| ------------- | ------ | ---------------------------------- |
|
||||
| {criterion 1} | ✅ | {test name or manual verification} |
|
||||
| {criterion 2} | ✅ | {test name or manual verification} |
|
||||
|
||||
### Verification Results
|
||||
|
||||
- Typecheck: {pass / fail + error count}
|
||||
- Lint: {pass / fail + warning count}
|
||||
- Tests: {N} passed, {M} failed, {K} skipped
|
||||
|
||||
### Design Deviations
|
||||
|
||||
{list any intentional deviations from design with rationale, or "None"}
|
||||
|
||||
### Open Items
|
||||
|
||||
{anything incomplete with reason, or "None"}
|
||||
|
||||
---
|
||||
|
||||
**Handoff**: Ready for `core/skills/review-code/SKILL.md`
|
||||
```
|
||||
|
||||
**Persist before returning ([org-internal #2847])**: the Developer MUST write the final
|
||||
report above to disk as its LAST action, BEFORE returning it —
|
||||
`<runs-root>/{slug}/workers/{chunk-id}-worker-{seq}.md` when the Tier 1
|
||||
run workspace exists, else `/tmp/octopus/{chunk-id}-worker-{seq}.md`
|
||||
(`{chunk-id}`/`{seq}` come from the dispatch prompt — see
|
||||
`../_shared/worker-report-persistence.md`). The persisted copy is the
|
||||
report of record; the task notification is a convenience copy. The same
|
||||
step applies to EVERY mode's report phase (bugfix Phase 5, refactor
|
||||
Phase 6, port report) — no worker return may exist only in the task
|
||||
notification.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 6 — Handoff to Code Review
|
||||
|
||||
Present the report to the user and signal readiness for review:
|
||||
|
||||
```
|
||||
Implementation of {WI-ID} complete.
|
||||
- {N} files changed ({C} created, {M} modified)
|
||||
- {T} tests passing
|
||||
- All acceptance criteria satisfied
|
||||
- Typecheck + lint clean
|
||||
|
||||
→ Run code review? (yes / no)
|
||||
```
|
||||
|
||||
Do NOT mark the work item as complete until code review passes.
|
||||
|
||||
To notify workflow completion, call the `signal_stage_done` tool.
|
||||
|
||||
#### Legacy notes
|
||||
|
||||
> **Publish target (tiered targeting retired, [org-internal #3072] phase 3)**: the
|
||||
> `Size/*`-tiered publish rule (`rules/workflow-routing.md` §"Publish target
|
||||
> by Size/* tier — RETIRED") was retired with the legacy producer skills.
|
||||
> Artifacts publish where the live mode puts them: DAG task mode → node spec
|
||||
> in the frozen `{epic-slug}/dag` copy (see the DAG-mode input path above);
|
||||
> standalone bugfix → `{slug}/bugfix-report` + issue body per bugfix Phase 5.
|
||||
> Legacy tiered locations (`{slug}/02-03-req-design`, `{slug}/04-plan-*`, …)
|
||||
> stay readable for historical runs via `_shared/gitea-read-patterns.md`.
|
||||
@@ -0,0 +1,252 @@
|
||||
# Large Port Pipeline — Detailed Processes
|
||||
|
||||
> Extracted from `implement/SKILL.md` (Mode: port) Phase B1 through B4.
|
||||
> Read this file when executing the Pipeline Mode for large ports.
|
||||
|
||||
---
|
||||
|
||||
## Phase B1 — Source Analysis
|
||||
|
||||
Produce a comprehensive source analysis under
|
||||
wiki page namespace `port-{name}/source-analysis/`. This is the authoritative
|
||||
behavioral contract that every downstream stage references.
|
||||
|
||||
### Directory Structure
|
||||
|
||||
```
|
||||
port-{name}/source-analysis/
|
||||
├── index # Summary & reading guide (wiki page)
|
||||
├── 01-source-overview # Source project context, tech stack
|
||||
├── 02-public-api.md # Every public endpoint / method / interface
|
||||
├── 03-data-model.md # Entities, fields, relationships, schemas
|
||||
├── 04-business-logic.md # Validation, rules, edge cases, state machines
|
||||
├── 05-error-handling.md # Error types, codes, messages, recovery paths
|
||||
├── 06-dependencies.md # Libraries, infrastructure, external services
|
||||
├── 07-test-coverage.md # Test inventory: happy path, edge cases, errors
|
||||
├── 08-concept-mapping.md # Source → target concept mapping (Phase A2)
|
||||
├── 09-gap-analysis.md # Gaps, alternatives, decisions (Phase A3)
|
||||
├── 10-fidelity-baseline.md # Full behavioral inventory for end-to-end verify
|
||||
├── 11-target-surface.md # Target project receiving surface analysis (Phase B1.7)
|
||||
└── 12-capability-boundary.md # Complete artifact inventory per capability (Phase B1.8)
|
||||
```
|
||||
|
||||
### Document Templates
|
||||
|
||||
The format templates for documents 01–05 and 10 are in
|
||||
`reference/source-analysis-templates.md` (read it when producing these
|
||||
documents). Documents 06–09 follow the same table-based format as their
|
||||
Phase A1/A2/A3 counterparts in standalone mode. The `10-fidelity-baseline.md`
|
||||
document is the master inventory — every source behavior is listed as a
|
||||
checkable `FID-*` item with a `Chunk` column that drives DAG node decomposition.
|
||||
|
||||
---
|
||||
|
||||
## Phase B1.5 — Source Analysis Review (GATE)
|
||||
|
||||
Before proceeding to self-check, ALL source analysis documents MUST pass
|
||||
peer review. This is the single highest-leverage quality gate in the port
|
||||
pipeline — errors in source analysis propagate to every downstream stage.
|
||||
|
||||
### Review Process
|
||||
|
||||
1. **Spawn 10 parallel reviewer sub-agents** (Explorer), one per dimension.
|
||||
Each reviewer receives:
|
||||
- The relevant source analysis documents (as listed per dimension below).
|
||||
- The source project files (or accessible copies).
|
||||
- The target project files (for TGT-SURF and CAP-BOUND dimensions).
|
||||
- Review instructions for that dimension.
|
||||
|
||||
2. **Review dimensions**:
|
||||
|
||||
| Dimension | Documents Reviewed | Key Question |
|
||||
| --------- | ------------------ | ------------ |
|
||||
| SRC-CMP | 01-source-overview, 03-data-model, 04-business-logic, 10-fidelity-baseline | Is every source behavior/entity/rule captured? |
|
||||
| SRC-API | 02-public-api, 05-error-handling | Is every public endpoint/method/error documented accurately? |
|
||||
| SRC-DATA | 03-data-model | Are entities, fields, types, constraints, and relations correct? |
|
||||
| SRC-BIZ | 04-business-logic | Are every validation rule, edge case, and state transition documented? |
|
||||
| SRC-ERR | 05-error-handling | Are all error types, codes, messages, and recovery paths captured? |
|
||||
| SRC-DEP | 06-dependencies | Are all libraries, infrastructure, and external services listed? |
|
||||
| SRC-TST | 07-test-coverage, 10-fidelity-baseline | Does every source test case map to a FID item? Are file:line references correct? |
|
||||
| SRC-MAP | 08-concept-mapping | Is every source concept mapped to a target equivalent or [GAP]? Are mappings correct? |
|
||||
| TGT-SURF | 11-target-surface | Is the target project's receiving surface fully analyzed? Are all integration points, structural diffs, and readiness items captured? Does every source gap in the structural diff have a plan? |
|
||||
| CAP-BOUND | 12-capability-boundary | Are all 13 artifact dimensions filled in? Does every Source Function Inventory item appear in D1/D2? Does every structural diff gap have a boundary entry? Are all N/A dimensions justified? |
|
||||
|
||||
3. **Output**: Each reviewer writes a findings JSON conforming to
|
||||
`core/schemas/port-analysis.schema.json` as a
|
||||
wiki page `port-{name}/source-analysis/reviews/{dimension}`
|
||||
with format:
|
||||
```json
|
||||
{
|
||||
"dimension": "SRC-CMP",
|
||||
"findings": [
|
||||
{
|
||||
"id": "SRC-CMP-001",
|
||||
"severity": "BLOCKER|MAJOR|MINOR",
|
||||
"description": "...",
|
||||
"source_evidence": "file:line",
|
||||
"recommendation": "..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
4. **Synthesis**: An Orchestrator (Worker) collects all 10 dimension reports,
|
||||
deduplicates, and produces a synthesis:
|
||||
wiki page `port-{name}/source-analysis/reviews/synthesis`
|
||||
with summary counts per dimension and consolidated action items.
|
||||
|
||||
5. **Iterate until convergence**:
|
||||
- Developer addresses all BLOCKER and MAJOR findings.
|
||||
- Reviewer re-checks affected dimensions.
|
||||
- Repeat until all dimensions show 0 BLOCKER and 0 MAJOR.
|
||||
|
||||
6. **GATE**: Phase B2 cannot start until synthesis shows ALL dimensions
|
||||
converged (0 BLOCKER, 0 MAJOR). Developer prints:
|
||||
`SOURCE ANALYSIS REVIEW CONVERGED — 10/10 dimensions pass`
|
||||
|
||||
---
|
||||
|
||||
## Phase B1.7 + B1.8 — Target Surface & Capability Boundary (Pipeline)
|
||||
|
||||
In pipeline mode, Phase A1.7 (Target Surface Analysis) and Phase A1.8
|
||||
(Capability Boundary Definition) produce formal artifacts in the
|
||||
source-analysis directory for peer review:
|
||||
|
||||
- **`11-target-surface.md`** — same process and format as Phase A1.7, using
|
||||
`reference/target-surface-template.md`. Reviewed by the **TGT-SURF**
|
||||
dimension in Phase B1.5.
|
||||
- **`12-capability-boundary.md`** — same process and format as Phase A1.8,
|
||||
using `reference/capability-boundary-template.md`. Reviewed by the
|
||||
**CAP-BOUND** dimension in Phase B1.5.
|
||||
|
||||
### Roadmap decomposition link
|
||||
|
||||
The capability boundary directly feeds the DAG decomposition (Phase B3):
|
||||
- Chunks are defined by grouping capability boundary artifacts by source
|
||||
module / feature area.
|
||||
- Cross-chunk dependencies are derived from dimension 13 (shared package
|
||||
changes).
|
||||
- The fidelity baseline (`10-fidelity-baseline.md`) is partitioned across
|
||||
chunks based on which capability boundary artifacts implement each FID item.
|
||||
|
||||
**GATE**: Do NOT proceed to Phase B2 until the B1.5 review converges on ALL
|
||||
10 dimensions including TGT-SURF and CAP-BOUND.
|
||||
|
||||
---
|
||||
|
||||
## Phase B2 — Self-Check Source Analysis
|
||||
|
||||
Run the port checklist (`core/checklists/port.md`) against the source
|
||||
analysis:
|
||||
|
||||
- Section 0.5 (SRV — Source Analysis Review): 10-dimension review converged.
|
||||
- Section 0.7 (TGT — Target Surface Analysis): target receiving surface
|
||||
fully mapped, structural diff complete, integration points identified.
|
||||
- Section 0.8 (CAP — Capability Boundary): all 13 artifact dimensions filled,
|
||||
cross-checks passed.
|
||||
- Section 1 (SRC — Source Understanding): every source file and test read.
|
||||
- Section 2 (MAP — Concept Mapping): every source concept has a target
|
||||
equivalent or `[GAP]`.
|
||||
- Section 3 (GAP — Gap Analysis): all gaps have alternatives and decisions.
|
||||
- Sections 4–7 (ADAPT, FID, TST, BEH): deferred to downstream stages —
|
||||
marked as `[DEFERRED TO DESIGN]`, `[DEFERRED TO IMPLEMENT]`, etc.
|
||||
|
||||
---
|
||||
|
||||
## Phase B3 — Handoff to DAG Decomposition
|
||||
|
||||
> Legacy note ([org-internal #3072] phase 3, 2026-08-21): this handoff used to target the
|
||||
> archived `roadmap` skill; it now targets `analyze-dag`.
|
||||
|
||||
Present the source analysis and request task-DAG decomposition:
|
||||
|
||||
```
|
||||
Source analysis complete for port-{name}.
|
||||
- {N} source files ({L} LOC) across {M} modules
|
||||
- {K} public API endpoints / methods documented
|
||||
- {B} business rules captured
|
||||
- {F} fidelity baseline items (for end-to-end verify)
|
||||
- {G} gaps identified with alternatives
|
||||
|
||||
Artifacts: wiki pages under `port-{name}/source-analysis/`
|
||||
|
||||
→ Approve and begin DAG decomposition? (yes / no / revise)
|
||||
```
|
||||
|
||||
Upon approval, the Builder routes to
|
||||
`core/skills/analyze-dag/SKILL.md` with:
|
||||
- **Slug**: `port-{name}`
|
||||
- **Scope**: the source modules and fidelity baseline from the source analysis.
|
||||
|
||||
analyze-dag decomposes the port into a task DAG by source module / feature
|
||||
area. Each node is a self-contained porting unit (e.g. `N-auth`, `N-api`,
|
||||
`N-models`); node ACs derive from the source analysis (`FID-*` items become
|
||||
ACs tracing to source tests; concept-mapping and gap decisions become the
|
||||
node spec; porting order follows source-file dependency order as edge
|
||||
topology). After the `review-dag` single gate PASSes, each node ticket flows
|
||||
`dag.task_route`:
|
||||
|
||||
```
|
||||
core/skills/implement/SKILL.md
|
||||
→ core/skills/review-code/SKILL.md
|
||||
→ core/skills/verify/SKILL.md
|
||||
```
|
||||
|
||||
(The legacy per-chunk `requirements-elicitation` → `design` →
|
||||
`review-artifact(design-space)` → `plan-iterations` → `review-artifact(plan)`
|
||||
front-end was archived 2026-08-21, [org-internal #3072] phase 3 — `<instance-root>/archive/`.)
|
||||
|
||||
### implement
|
||||
|
||||
The Developer ports code following the rules from Phase A5 (target
|
||||
conventions, fidelity over aesthetics, no new dependencies, port comments).
|
||||
|
||||
Additionally:
|
||||
- Each implementation report references the corresponding `FID-*` items
|
||||
from the fidelity baseline.
|
||||
- Ported tests reference source test file and line number.
|
||||
|
||||
### review-code
|
||||
|
||||
Standard code review. Additionally:
|
||||
- Reviewer checks fidelity: does the ported code match the source behavior
|
||||
as documented in the chunk's requirements?
|
||||
- Reviewer checks convention compliance: does the new code look native to
|
||||
the target project?
|
||||
|
||||
### Target-Side Refactoring in Port Pipeline
|
||||
|
||||
Large ports often require restructuring the target codebase to accommodate
|
||||
ported code — extracting interfaces, renaming conflicting modules, removing
|
||||
dead code, or adapting existing abstractions. These refactoring needs MUST
|
||||
flow through the pipeline's quality gates, not as ad hoc changes.
|
||||
|
||||
**Identification**: The Architect identifies target-side refactoring needs
|
||||
during design and documents them as design decisions. Each refactoring
|
||||
decision references the gap that requires it (e.g. "Target's UserService must
|
||||
be extracted to an interface before porting source AuthService to avoid
|
||||
tight coupling").
|
||||
|
||||
**Planning**: The Planner creates refactoring work items alongside
|
||||
implementation work items. A refactoring work item's description starts with
|
||||
"Refactor" (or 重构) and its requirements coverage maps to a refactoring
|
||||
requirement derived from the design decision. Dependencies are enforced:
|
||||
- Refactoring work items that unblock port work items appear earlier in the
|
||||
iteration order.
|
||||
- No port work item depends on an incomplete refactoring.
|
||||
|
||||
**Execution**: The `implement` skill handles refactoring work items via its
|
||||
Refactoring Mode (see
|
||||
`core/skills/implement/SKILL.md` — Refactoring Mode). The refactoring
|
||||
produces a standard implementation report and proceeds to code review.
|
||||
|
||||
**Quality**: Refactoring work items go through the full review gate —
|
||||
code review is mandatory regardless of change size. The reviewer verifies:
|
||||
- Behavioral fidelity: did the refactoring preserve existing behavior?
|
||||
- Test baseline: did any existing tests break or change?
|
||||
- Design alignment: does the refactoring match the design decision?
|
||||
|
||||
```
|
||||
pipeline for refactoring work items (DAG node):
|
||||
implement (refactoring mode) → review-code → verify
|
||||
```
|
||||
@@ -0,0 +1,102 @@
|
||||
# Concept Mapping, Gap Analysis & Adaptation Design — Detailed Processes
|
||||
|
||||
> Extracted from `implement/SKILL.md` (Mode: port) Phase A2, A3, A4.
|
||||
> Read this file when executing the Mapping and Design phases in standalone mode.
|
||||
|
||||
---
|
||||
|
||||
## Phase A2 — Map Concepts
|
||||
|
||||
For every source concept, identify the target project's equivalent. This is a
|
||||
bidirectional mapping — every source entity, every source API call, every
|
||||
source pattern must have a corresponding target concept.
|
||||
|
||||
### Mapping Table
|
||||
|
||||
| Source Concept | Target Equivalent | Notes |
|
||||
| ------------------ | ------------------------------------------- | ---------------------------------------------------- |
|
||||
| Express middleware | Fastify hook | Different signature — adapt order and error handling |
|
||||
| Sequelize model | Drizzle schema | Different migration strategy — adapt CLI |
|
||||
| bcrypt hash | argon2 | Target project's existing auth module uses argon2 |
|
||||
| Redis cache | Memory cache (no Redis) | Compromise — simplify to in-memory with TTL |
|
||||
| Pino logger | Existing logger module in `src/util/log.ts` | Reuse target's logger |
|
||||
|
||||
### Rules
|
||||
|
||||
- If a source concept has no clear target equivalent, pause and log `[GAP]`.
|
||||
- If the target has a different pattern for the same concept (e.g. callbacks
|
||||
vs. async/await), prefer the TARGET pattern, not the source's.
|
||||
- If the source uses a library that exists in the target's ecosystem, use the
|
||||
version already in the target's `package.json` — do not introduce a different
|
||||
version.
|
||||
|
||||
---
|
||||
|
||||
## Phase A3 — Gap Analysis
|
||||
|
||||
For every `[GAP]` from Phase A2, analyze the impact:
|
||||
|
||||
### Gap Analysis Table
|
||||
|
||||
| Gap | Impact | Alternatives | Decision | Deferred To (slug) | Reactivation Trigger |
|
||||
| ------------------ | ------------------------------------ | ------------------------------------------------------------------------------------------- | ---------- | ------------------ | -------------------- |
|
||||
| No Redis in target | Source uses Redis for session store | 1. Add Redis to target, 2. Use DB for sessions, 3. Use in-memory (not for production) | {decision} | | |
|
||||
| No message queue | Source uses RabbitMQ for async tasks | 1. Add queue to target, 2. Make synchronous, 3. Use a simpler queue (e.g. database polling) | {decision} | | |
|
||||
|
||||
### Rules
|
||||
|
||||
- Do NOT add infrastructure to the target unless absolutely necessary — prefer
|
||||
alternatives that use existing target infrastructure.
|
||||
- If a gap forces a behavior change, mark it as `[FIDELITY DEVIATION]` — the
|
||||
port will not behave identically. This must be explicitly approved.
|
||||
|
||||
### Deferral Rules
|
||||
|
||||
- A gap marked `Deferred` MUST populate both "Deferred To" and "Reactivation Trigger" columns.
|
||||
- "Deferred To" must reference a concrete artifact slug (e.g. `port-{name}/chunk-http`).
|
||||
- "Reactivation Trigger" must specify a condition (e.g. "After chunk-auth verification passes").
|
||||
- Gaps without a reactivation path are treated as `[PORT GAP]` — a blocker for the current port.
|
||||
|
||||
---
|
||||
|
||||
## Phase A4 — Adaptation Design
|
||||
|
||||
Design how the source feature will fit into the target project:
|
||||
|
||||
1. **File structure** — where in the target project will the ported code live?
|
||||
2. **Interface adaptations** — source API signatures must adapt to target
|
||||
conventions (e.g. source uses `snake_case`, target uses `camelCase`).
|
||||
3. **Dependency replacements** — for each source dependency, use the target
|
||||
equivalent or the Gap decision from Phase A3.
|
||||
4. **Test adaptation** — source test framework → target test framework mapping
|
||||
(e.g. `describe`/`it` → `describe`/`it` if both use the same pattern, or
|
||||
map to target's test DSL).
|
||||
|
||||
### Adaptation Design Template
|
||||
|
||||
````markdown
|
||||
## Adaptation Design
|
||||
|
||||
### File Structure
|
||||
```
|
||||
src/
|
||||
{module}/
|
||||
{ported_file}.ts — (from source/src/{module}/{file}.js)
|
||||
...
|
||||
test/
|
||||
{module}/
|
||||
{ported_test}.test.ts — (from source/test/{module}/{file}.test.js)
|
||||
```
|
||||
|
||||
### Interface Adaptations
|
||||
| Source | Target | Reason |
|
||||
|--------|--------|--------|
|
||||
| `req.body.created_at` | `req.body.createdAt` | Target convention: camelCase |
|
||||
| `throw new AppError(400, '...')` | `yield* new BadRequest('...')` | Target uses Effect errors |
|
||||
|
||||
### Fidelity Deviations
|
||||
| What Changes | Why | Impact |
|
||||
|-------------|-----|--------|
|
||||
| Session store: Redis → DB | Target has no Redis | Slightly higher latency (~5ms), CAP consistency trade |
|
||||
| Async queue: RabbitMQ → DB polling | Target has no queue broker | Higher latency, lower throughput — acceptable for < 100 ops/min |
|
||||
````
|
||||
@@ -0,0 +1,389 @@
|
||||
> Extracted from implement/SKILL.md (Mode: port) — moved verbatim 2026-08-25, ticket [org-internal #3381].
|
||||
|
||||
### Mode: port
|
||||
|
||||
Port a feature from a source project into the current (target) project. Unlike
|
||||
greenfield (no existing code), brownfield (new feature in same project), or
|
||||
bugfix (restore intended behavior), porting requires preserving the source's
|
||||
behavioral contract while adapting every implementation detail to the target's
|
||||
tech stack and conventions.
|
||||
|
||||
#### Role & Responsibilities
|
||||
|
||||
The port is owned by the **Developer** (Worker).
|
||||
|
||||
The Developer is responsible for:
|
||||
|
||||
- Reading and understanding the source feature end-to-end.
|
||||
- Mapping source concepts to target equivalents.
|
||||
- Identifying gaps (missing infrastructure, incompatible libraries).
|
||||
- Designing adaptations that preserve behavior.
|
||||
- For small ports: implementing in the target project following target
|
||||
conventions end-to-end.
|
||||
- For large ports: producing a source analysis report, then handing off to
|
||||
the DAG pipeline (analyze-dag → review-dag → per-node implement →
|
||||
review-code → verify) with the single-gate review.
|
||||
- Porting source tests to the target test framework.
|
||||
- Verifying behavioral fidelity (same inputs → same outputs).
|
||||
|
||||
The Builder's role is to validate the port output and route to the next stage
|
||||
(analyze-dag for large ports, code review for non-trivial small ports).
|
||||
|
||||
#### Tester focus for port
|
||||
|
||||
The Tester role in port writes **behavioral-fidelity tests** — verifying
|
||||
that ported code behaves identically to the source, not just that it passes
|
||||
its own assertions:
|
||||
|
||||
- **Fidelity-anchored tests** — every `FID-*` item in
|
||||
`port-{name}/source-analysis/10-fidelity-baseline` (or `port-{name}/source-analysis/fid-raw` in standalone mode) MUST map
|
||||
to at least one ported test. The Tester reads the source test for each
|
||||
FID and rewrites it in the target test framework's assertion style,
|
||||
preserving the behavioral contract (same inputs → same outputs).
|
||||
- **Source-test porting** — port edge cases, error paths, and boundary
|
||||
tests from the source, not just the happy path. Missing FID coverage is
|
||||
the #1 cause of incomplete porting.
|
||||
- **Fidelity-deviation tests** — for every `[FIDELITY DEVIATION]` in the
|
||||
adaptation design (Phase A4), write a test that documents and locks the
|
||||
divergent behavior so the deviation is intentional, not accidental drift.
|
||||
|
||||
The port orchestration follows the standard role-split flow (implement
|
||||
before test): the Developer ports code in Phase A5 and runs test:changed
|
||||
to green; the Tester then ports source tests and verifies fidelity in
|
||||
Phase A6. In pipeline mode (Part 2), the role split applies within each
|
||||
chunk's implement stage.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 0 — Scope Assessment
|
||||
|
||||
Before starting, measure the source scope across **four dimensions** — file
|
||||
count and LOC alone are insufficient because a 3-file port that spans 3
|
||||
packages with implicit dependencies (schema, config, routes) is far more
|
||||
complex than a 10-file port within a single self-contained module.
|
||||
|
||||
1. Count source files in the feature scope.
|
||||
2. Count total source lines of code (exclude tests).
|
||||
3. Count source modules / feature areas (distinct functional areas).
|
||||
4. **Count source packages touched** — how many monorepo packages does the
|
||||
feature span? (e.g. `packages/app`, `packages/sdk`, `packages/ui`,
|
||||
`packages/core`).
|
||||
5. **Count implicit dependency artifacts** — schema/migration files, config
|
||||
entries, env vars, CLI flags, theme/style files, route definitions,
|
||||
Provider/context hierarchy changes, build config changes. These are the
|
||||
artifacts that are NOT source code files but are required for the feature
|
||||
to function. See Phase A1.8 for the full artifact dimension list.
|
||||
|
||||
Determine the port path:
|
||||
|
||||
| Scope | Mode | Pipeline |
|
||||
| ---------------------------------- | ------------- | ------------------------------------------------------ |
|
||||
| ≤ 5 files, ≤ 200 LOC, 1 package, 0 implicit deps | **Standalone**| Standalone Developer flow (Phases A1–A8, mandatory code review) |
|
||||
| > 5 files or > 200 LOC | **Pipeline** | Full pipeline: source analysis → analyze-dag → per-node |
|
||||
| Spans ≥ 3 source modules | **Pipeline** | Full pipeline (regardless of file count / LOC) |
|
||||
| Spans ≥ 2 packages | **Pipeline** | Full pipeline (cross-package ports have hidden coupling) |
|
||||
| ≥ 3 implicit dependency artifacts | **Pipeline** | Full pipeline (implicit deps require capability boundary analysis) |
|
||||
|
||||
Report the assessment:
|
||||
|
||||
```
|
||||
Port scope assessment:
|
||||
- Source files: {N}
|
||||
- Source LOC: {L}
|
||||
- Source modules: {M}
|
||||
- Source packages touched: {P}
|
||||
- Implicit dependency artifacts: {I}
|
||||
- Path: A (standalone) / B (full pipeline)
|
||||
|
||||
→ Proceed? (yes / no / revise)
|
||||
```
|
||||
|
||||
Do NOT proceed without explicit approval.
|
||||
|
||||
---
|
||||
|
||||
#### Preconditions
|
||||
|
||||
- [ ] Source project path or reference is specified.
|
||||
- [ ] Source feature scope is specified (which files, module, or feature).
|
||||
- [ ] Target project is the current working directory and has a clean
|
||||
workspace.
|
||||
- [ ] Target project has an existing test framework.
|
||||
- [ ] **Target project structure is accessible** — the Developer can read all
|
||||
target project packages, config files, and build configs. If the target
|
||||
is a monorepo, all packages must be accessible.
|
||||
- [ ] **Target project's existing capabilities are documented or discoverable**
|
||||
— the Developer must be able to identify what the target already has
|
||||
(existing modules, routes, providers, schemas) to avoid redundant porting
|
||||
and to identify integration points. If not documented, the Developer
|
||||
must run a structural survey (Phase A1.7) before proceeding.
|
||||
- [ ] `core/checklists/port.md` is accessible.
|
||||
|
||||
If the target project has no test framework, warn:
|
||||
|
||||
```
|
||||
Target project has no test framework. Porting without tests cannot verify
|
||||
behavioral fidelity. Options:
|
||||
1. Add a test framework to the target project first.
|
||||
2. Proceed without tests — behavioral fidelity cannot be guaranteed.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Part 1 — Standalone Mode (Small Port)
|
||||
|
||||
For small ports (≤ 5 files, ≤ 200 LOC), the Developer executes the full
|
||||
port lifecycle directly. No DAG decomposition, no per-stage review gates.
|
||||
Code review is mandatory (regardless of size, per Phase A8).
|
||||
|
||||
| Phase | Summary | Detail |
|
||||
|-------|---------|--------|
|
||||
| A1 | Deeply analyze source: public API, data model, dependencies, function inventory, test-to-FID extraction | `reference/source-analysis.md` |
|
||||
| A1.5 | Source Analysis Review GATE — 4-dimensional peer review of A1 deliverables | `reference/source-analysis.md` |
|
||||
| A1.7 | Target Surface Analysis — map target receiving surface, structural diffs, integration points | `reference/source-analysis.md` |
|
||||
| A1.8 | Capability Boundary Definition GATE — 13-dimension artifact inventory | `reference/source-analysis.md` |
|
||||
| A2 | Map every source concept to a target equivalent or mark as `[GAP]` | `reference/mapping-and-design.md` |
|
||||
| A3 | Gap Analysis — alternatives, decisions, deferral paths for every `[GAP]` | `reference/mapping-and-design.md` |
|
||||
| A4 | Adaptation Design — file structure, interface adaptations, fidelity deviations | `reference/mapping-and-design.md` |
|
||||
| A5 | **Implement** — port code following target conventions | *(inline below)* |
|
||||
| A5.5 | **Self-Check Gate** — complete port checklist | *(inline below)* |
|
||||
| A6 | Port Tests & Verify Fidelity — port every test, fidelity checklist | `reference/fidelity-verification.md` |
|
||||
| A7 | **Report** — produce port report | `reference/port-report-template.md` |
|
||||
| A8 | **Approval** — present report, route to code review | *(inline below)* |
|
||||
|
||||
##### Phase A1 — Understand Source
|
||||
|
||||
Deeply analyze the source feature: public API, data model, dependencies, and
|
||||
function inventory. Extract every test case as a `FID-*` entry in `port-{name}/source-analysis/fid-raw` (wiki page).
|
||||
See `reference/source-analysis.md` for the full process, templates, and
|
||||
Function Inventory format.
|
||||
|
||||
##### Phase A1.5 — Source Analysis Review (GATE)
|
||||
|
||||
A reviewer (Explorer sub-agent) cross-checks all A1 deliverables against source
|
||||
files across 4 dimensions (SRC-CMP, SRC-API, SRC-TST, SRC-DEP). All BLOCKER
|
||||
findings must be resolved before Phase A2. See `reference/source-analysis.md`.
|
||||
|
||||
##### Phase A1.7 — Target Surface Analysis
|
||||
|
||||
Analyze the target project's receiving surface: directory tree, existing
|
||||
capabilities, automated structural diffs, integration points, and readiness.
|
||||
See `reference/source-analysis.md` for the full process.
|
||||
Output follows `reference/target-surface-template.md`.
|
||||
|
||||
##### Phase A1.8 — Capability Boundary Definition (GATE)
|
||||
|
||||
Define the complete artifact boundary across all 13 dimensions (code, types,
|
||||
schema, config, env, CLI, theme, routes, providers, build, deps, tests, shared
|
||||
packages). Cross-reference with A1 inventory and A1.7 diffs.
|
||||
See `reference/source-analysis.md` for the full process.
|
||||
Output follows `reference/capability-boundary-template.md`.
|
||||
|
||||
##### Phase A2 — Map Concepts
|
||||
|
||||
Map every source concept to a target equivalent — bidirectional, complete.
|
||||
Mark missing equivalents as `[GAP]`. See `reference/mapping-and-design.md`.
|
||||
|
||||
##### Phase A3 — Gap Analysis
|
||||
|
||||
Analyze every `[GAP]`: impact, alternatives, decision, deferral path with
|
||||
reactivation trigger. See `reference/mapping-and-design.md`.
|
||||
|
||||
##### Phase A4 — Adaptation Design
|
||||
|
||||
Design file structure, interface adaptations, dependency replacements, and
|
||||
document `[FIDELITY DEVIATION]` items. See `reference/mapping-and-design.md`.
|
||||
|
||||
##### Phase A5 — Implement
|
||||
|
||||
Port the code file by file, following these rules:
|
||||
|
||||
1. **One file at a time** — port completely, verify, then move to the next.
|
||||
2. **Target conventions are law** — the ported code must follow target
|
||||
conventions exactly. Use target's naming, patterns, and idioms.
|
||||
3. **Fidelity over aesthetics** — do not "improve" the source logic. If the
|
||||
source validates email with `/^.+@.+$/`, port that exact validation (then
|
||||
log a `[NOTE: weak validation in source]`). If you want stricter validation,
|
||||
that's a separate feature item, not part of the port.
|
||||
4. **Port comments from source** (translated to target language) — they capture
|
||||
the original author's intent.
|
||||
5. **After each file** — run `bun typecheck` to catch type errors early.
|
||||
6. **Do not introduce new dependencies** — if the source uses a library not in
|
||||
the target's lockfile, use the alternative from the adaptation design or
|
||||
reimplement the needed subset inline.
|
||||
|
||||
##### Phase A5.5 — Self-Check Gate (MANDATORY)
|
||||
|
||||
Before proceeding to test porting, the Developer MUST complete the port
|
||||
checklist (`core/checklists/port.md`) in full:
|
||||
|
||||
1. **Run every checklist section** — all 12 sections, all items.
|
||||
2. **Mark every item** — ☑ (pass) or ☐ (fail) with written justification.
|
||||
3. **For any ☐ item** — document a fix plan inline in the remarks column.
|
||||
If the item is a known deferral (e.g. infrastructure gap), the deferral
|
||||
must include a reactivation path (chunk + trigger).
|
||||
4. **Publish the completed checklist** as a Gitea wiki page:
|
||||
`wiki 读写 API(见 TERMINOLOGY)(owner="Octopus", repo="octopus", title="port-{name}/self-check", content="{checklist body}", message="Publish port self-check for {name}")`.
|
||||
5. **GATE** — do NOT proceed to Phase A6 until:
|
||||
- All items are ☑, OR
|
||||
- All ☐ items have documented fix plans with reactivation paths,
|
||||
AND the Developer has printed: `SELF-CHECK COMPLETE — {X} items passing, {Y} items deferred with plan`
|
||||
|
||||
##### Phase A6 — Port Tests & Verify Fidelity
|
||||
|
||||
Port every source test to the target test framework. Run all tests (ported +
|
||||
existing), verify behavioral fidelity, and produce a fidelity verification
|
||||
table. See `reference/fidelity-verification.md` for the full process and
|
||||
fidelity checklist template.
|
||||
|
||||
##### Phase A7 — Report
|
||||
|
||||
Produce a port report following the template in
|
||||
`reference/port-report-template.md`. The report covers: fidelity assessment
|
||||
(fully ported / adapted / deferred / N/A), portfolio map (source → target file
|
||||
mapping), gaps & deferred items, and verification results.
|
||||
|
||||
##### Phase A8 — Approval
|
||||
|
||||
Present the report:
|
||||
|
||||
```
|
||||
Port complete: {one-line summary}
|
||||
- {N} files ported ({L} lines)
|
||||
- {M} tests ported, all pass
|
||||
- Fidelity: {X}% fully ported, {Y}% adapted, {Z}% deferred
|
||||
|
||||
→ Run code review? (mandatory)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Part 2 — Pipeline Mode (Large Port)
|
||||
|
||||
For large ports, the Developer produces a source analysis report as the
|
||||
foundation artifact, then the port flows through the full pipeline with
|
||||
peer-review gates at every stage:
|
||||
|
||||
```
|
||||
port (source analysis)
|
||||
→ analyze-dag (decompose by source module into DAG nodes)
|
||||
→ review-dag single gate (replaces the legacy design-space + plan reviews)
|
||||
→ per node (dag.task_route):
|
||||
implement (port code, target conventions) — see ### Mode: implement (default)
|
||||
→ review-code
|
||||
→ verify (integration + fidelity)
|
||||
```
|
||||
|
||||
> Legacy diagram (archived [org-internal #3072] phase 3): the former per-chunk pipeline ran
|
||||
> requirements-elicitation → design → review (target: design-space) →
|
||||
> plan-iterations → review (target: plan) → implement → review-code. Those
|
||||
> skills/targets are archived (`<instance-root>/archive/`); the live path is the
|
||||
> analyze-dag → review-dag route above (see Phase B3/B4 below).
|
||||
|
||||
| Phase | Summary | Detail |
|
||||
|-------|---------|--------|
|
||||
| B1 | Produce comprehensive 12-document source analysis | `reference/large-port-pipeline.md` |
|
||||
| B1.5 | **GATE** — 10-dimensional peer review of source analysis | `reference/large-port-pipeline.md` |
|
||||
| B1.7+B1.8 | Target surface & capability boundary as formal artifacts for peer review | `reference/large-port-pipeline.md` |
|
||||
| B2 | Self-check source analysis against port checklist | `reference/large-port-pipeline.md` |
|
||||
| B3 | Handoff to analyze-dag — decompose by source module into DAG nodes (legacy: roadmap skill, archived [org-internal #3072] phase 3) | `reference/large-port-pipeline.md` |
|
||||
| B4 | Per-node DAG task route: implement → review-code (the review-dag single gate replaces the legacy design-space + plan reviews at the Epic level) | `reference/large-port-pipeline.md` |
|
||||
| B5 | Verify — integration + fidelity, every `FID-*` traced to a passing test | `reference/fidelity-verification.md` |
|
||||
| B6 | **Final Approval** — consolidated port report | *(inline below)* |
|
||||
|
||||
##### Phase B1 — Source Analysis
|
||||
|
||||
Produce a comprehensive 12-document source analysis as Gitea wiki pages under
|
||||
`port-{name}/source-analysis/`. Each document is a separate wiki page.
|
||||
See `reference/large-port-pipeline.md` for the full document list, templates (in
|
||||
`reference/source-analysis-templates.md`), and production process.
|
||||
|
||||
##### Phase B1.5 — Source Analysis Review (GATE)
|
||||
|
||||
Spawn 10 parallel Explorer reviewers across all dimensions (SRC-CMP, SRC-API,
|
||||
SRC-DATA, SRC-BIZ, SRC-ERR, SRC-DEP, SRC-TST, SRC-MAP, TGT-SURF, CAP-BOUND),
|
||||
synthesize findings, and iterate until convergence (0 BLOCKER, 0 MAJOR).
|
||||
See `reference/large-port-pipeline.md` for the full process.
|
||||
|
||||
##### Phase B1.7 + B1.8 — Target Surface & Capability Boundary
|
||||
|
||||
Same processes as A1.7/A1.8, producing formal artifacts (`11-target-surface.md`,
|
||||
`port-{name}/source-analysis/12-capability-boundary`) for peer review. The capability boundary feeds
|
||||
directly into DAG node decomposition.
|
||||
See `reference/large-port-pipeline.md`.
|
||||
|
||||
##### Phase B2 — Self-Check Source Analysis
|
||||
|
||||
Run the port checklist against the source analysis (sections 0.5, 0.7, 0.8, 1,
|
||||
2, 3; sections 4–7 deferred to downstream stages).
|
||||
See `reference/large-port-pipeline.md`.
|
||||
|
||||
##### Phase B3 — Handoff to DAG Decomposition
|
||||
|
||||
Present source analysis summary and request task-DAG decomposition. The
|
||||
Builder routes to `core/skills/analyze-dag/SKILL.md` with slug
|
||||
`port-{name}` (nodes per source module; the source analysis supplies node
|
||||
ACs and edge contracts). (Legacy: this handed off to the archived `roadmap`
|
||||
skill — `<instance-root>/archive/skills/roadmap/`, [org-internal #3072] phase 3.) See
|
||||
`reference/large-port-pipeline.md` for the handoff format.
|
||||
|
||||
##### Phase B4 — Per-Node Pipeline
|
||||
|
||||
Each node ticket flows `dag.task_route`: `implement` → `review-code`
|
||||
(the `review-dag` single gate replaces the legacy design-space + plan
|
||||
reviews at the Epic level). Includes rules for target-side refactoring work
|
||||
items routed through the refactor workflow (see Mode: refactor above).
|
||||
See `reference/large-port-pipeline.md` for full per-stage details.
|
||||
|
||||
##### Phase B5 — Verify (Integration + Fidelity)
|
||||
|
||||
Run `core/skills/verify/SKILL.md` with the fidelity baseline as acceptance
|
||||
criteria. Every `FID-*` must trace to a passing test. Produce a consolidated
|
||||
Port Fidelity Report. See `reference/fidelity-verification.md`.
|
||||
|
||||
##### Phase B6 — Final Approval
|
||||
|
||||
Before declaring the port complete, verify:
|
||||
|
||||
1. **CI is configured** — Check `.gitea/workflows/` (this repo's CI location),
|
||||
`.github/workflows/ci.yml`, or equivalent.
|
||||
If absent, warn: `[GAP: no CI — no automated gate before merge]`.
|
||||
The PR may be merged, but flag the gap in the port report.
|
||||
2. **All review gates passed** — The `review-dag` single gate and every
|
||||
node's code review have `converged: true`.
|
||||
3. **Final typecheck + lint + tests pass** — Run all three commands fresh.
|
||||
|
||||
Present the consolidated port report:
|
||||
|
||||
```
|
||||
Port complete: port-{name}
|
||||
- {N} chunks implemented
|
||||
- {F} files ported ({L} lines)
|
||||
- {T} tests ported, all pass
|
||||
- Fidelity: {X}/{Y} behaviors verified, {Z} deferred
|
||||
- All peer-review gates passed
|
||||
- CI: {configured / absent — manual gate required}
|
||||
|
||||
Artifacts: wiki pages under `port-{name}/`
|
||||
|
||||
→ Approve port? (yes / no)
|
||||
```
|
||||
|
||||
##### Phase B7 — Post-Merge Cleanup
|
||||
|
||||
After the PR is merged and the port branch is no longer needed:
|
||||
|
||||
1. **Delete the remote branch**:
|
||||
```
|
||||
git push origin --delete workflow/port/{name}
|
||||
```
|
||||
2. **Delete the local branch**:
|
||||
```
|
||||
git branch -d workflow/port/{name}
|
||||
```
|
||||
3. **Remove associated worktrees**:
|
||||
```
|
||||
git worktree list | grep "workflow/port/{name}" | awk '{print $1}' | xargs git worktree remove
|
||||
```
|
||||
4. **Verify cleanup**: `git branch -a | grep workflow/port/{name}` should
|
||||
return empty.
|
||||
@@ -0,0 +1,43 @@
|
||||
# Port Report Template
|
||||
|
||||
```markdown
|
||||
# Port Report
|
||||
|
||||
**Source**: {project name} — {feature name}
|
||||
**Target**: {current project}
|
||||
**Files ported**: {N}
|
||||
**Tests ported**: {M}
|
||||
|
||||
## Fidelity Assessment
|
||||
|
||||
| Category | Status | Count |
|
||||
| ------------------------------------- | ------ | ------------- |
|
||||
| Fully ported | ✅ | {N} behaviors |
|
||||
| Adapted (minor change) | ⚠️ | {N} behaviors |
|
||||
| Deferred (not ported) | ❌ | {N} behaviors |
|
||||
| Not applicable (different tech stack) | N/A | {N} behaviors |
|
||||
|
||||
## Portfolio Map
|
||||
|
||||
| Source File | Target File | Lines | Status |
|
||||
| -------------------------------- | -------------------------------- | --------- | ------ |
|
||||
| `source/src/auth/login.js` | `target/src/auth/login.ts` | 45 → 52 | ✅ |
|
||||
| `source/test/auth/login.test.js` | `target/test/auth/login.test.ts` | 120 → 118 | ✅ |
|
||||
|
||||
## Gaps & Deferred
|
||||
|
||||
| Item | Reason | Deferred to |
|
||||
| ------------- | -------------------------- | ------------------------------------- |
|
||||
| Rate limiting | Target has no rate limiter | Separate feature: "Add rate limiting" |
|
||||
|
||||
## Verification
|
||||
|
||||
- Ported tests: {M} passed, 0 failed
|
||||
- Existing tests: {K} passed, 0 failed
|
||||
- Typecheck: ✅
|
||||
- Lint: ✅
|
||||
|
||||
---
|
||||
|
||||
**Handoff**: → Run `core/skills/review-code/SKILL.md` (mandatory for all ports, regardless of size)
|
||||
```
|
||||
@@ -0,0 +1,258 @@
|
||||
> Extracted from implement/SKILL.md (Mode: refactor) — moved verbatim 2026-08-25, ticket [org-internal #3381].
|
||||
|
||||
### Mode: refactor
|
||||
|
||||
Restructure existing code to improve maintainability, readability, or
|
||||
performance without changing observable behavior. The existing test suite
|
||||
is the safety net — every refactoring step MUST be verified before proceeding.
|
||||
|
||||
#### Execution Modes
|
||||
|
||||
| Mode | Entry Point | Scope Source | Review Gate |
|
||||
| ---------- | -------------------------------------------- | --------------------------- | ---------------------- |
|
||||
| Standalone | User says "refactor {X}" | User specifies scope | Optional (>50 lines or ≥5 files) |
|
||||
| Pipeline | `implement` skill dispatches refactoring WI | Node spec (DAG) or request | Mandatory |
|
||||
|
||||
In pipeline mode, the scope and target pattern come from the design document
|
||||
and iteration plan, not from user input. The Developer must read the design
|
||||
sections referenced by the work item before starting Phase 1. After completing
|
||||
Phases 1–6, the Developer produces the standard implementation report (see
|
||||
Mode: implement (default), Phase 5) and hands off to code review.
|
||||
|
||||
#### Role & Responsibilities
|
||||
|
||||
The refactoring is owned and executed by the **Developer** (Worker). The
|
||||
Developer owns both implementation and refactoring — same role, same skill set.
|
||||
|
||||
The Developer is responsible for:
|
||||
|
||||
- Establishing a passing test baseline before any code change.
|
||||
- Decomposing the refactoring into small, reversible, verifiable steps.
|
||||
- Running the full test suite after every step — never skip a verification.
|
||||
- Reverting immediately if any step causes a test failure.
|
||||
- Comparing before/after coverage and complexity metrics.
|
||||
|
||||
The Builder's role is to present the refactor report and route to code review
|
||||
if the change is non-trivial (> 50 lines or touches ≥ 5 files).
|
||||
|
||||
---
|
||||
|
||||
#### Preconditions
|
||||
|
||||
Before starting the refactoring, confirm:
|
||||
|
||||
- [ ] Scope is specified (which file, module, or pattern to refactor).
|
||||
- [ ] An existing test suite covers the scope. If test coverage is unknown,
|
||||
run the test suite with coverage first.
|
||||
- [ ] No uncommitted changes in the working tree (`git status` is clean).
|
||||
- [ ] `core/checklists/refactoring.md` is accessible.
|
||||
|
||||
##### No Test Coverage? Stop.
|
||||
|
||||
If the scope has **no existing tests**:
|
||||
|
||||
```
|
||||
Cannot safely refactor {scope} — no existing test coverage.
|
||||
|
||||
Refactoring without tests is not restructuring, it's rewriting with unknown
|
||||
side effects. Options:
|
||||
1. Write characterization tests first (tests that capture current behavior),
|
||||
then refactor.
|
||||
2. Skip this module — refactor only modules with test coverage.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 1 — Scope & Baseline
|
||||
|
||||
1. **Identify scope** — confirm the exact files, classes, or modules to
|
||||
refactor. Use `glob` and `grep` to map all files and their dependents.
|
||||
|
||||
2. **Establish baseline**:
|
||||
- Run `bun run test:parallel` (or project-equivalent) — all tests must pass.
|
||||
- If any test fails before you start, stop and report: "Cannot begin
|
||||
refactoring with failing tests. Fix them first."
|
||||
- Capture test count as baseline: `tests: {N} total, {N} passed`.
|
||||
- Capture coverage if available: run the package's coverage script from
|
||||
the package dir (e.g. `cd <harness-package> && bun run test:coverage`) —
|
||||
`bun run test:parallel` does not emit coverage (its runner script drops
|
||||
positional args, so `test:parallel --coverage` silently ignores the
|
||||
flag); use the package's `test:coverage` script instead.
|
||||
|
||||
3. **Capture complexity metrics** (optional but recommended):
|
||||
- Lines of code in scope.
|
||||
- Cyclomatic complexity or equivalent (if tooling exists).
|
||||
- Dependencies (fan-in / fan-out).
|
||||
|
||||
```markdown
|
||||
## Baseline
|
||||
|
||||
**Scope**: {list of files}
|
||||
**Tests**: {N} passed, 0 failed
|
||||
**Coverage**: {X}% lines, {Y}% branches
|
||||
**LOC**: {N}
|
||||
**Complexity**: {measured or "no metrics tool available"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 2 — Define Target Pattern
|
||||
|
||||
Define what "done" looks like. A refactoring without a target pattern is
|
||||
code churn, not improvement.
|
||||
|
||||
The target pattern must be one of:
|
||||
|
||||
| Category | Examples |
|
||||
| ------------ | -------------------------------------------------------------------------------------------------------------- |
|
||||
| **Extract** | Extract class, extract function, extract module |
|
||||
| **Inline** | Inline function, inline variable, inline class |
|
||||
| **Rename** | Rename function, variable, class, file, module |
|
||||
| **Move** | Move function/class to a more appropriate module |
|
||||
| **Replace** | Replace callback with Promise/await, replace loop with functional style, replace conditional with polymorphism |
|
||||
| **Simplify** | Remove dead code, collapse redundant logic, flatten nested conditionals |
|
||||
| **Upgrade** | Migrate to new API, adopt new library version patterns |
|
||||
|
||||
```markdown
|
||||
## Target Pattern
|
||||
|
||||
**Category**: {Extract | Inline | Rename | Move | Replace | Simplify | Upgrade}
|
||||
**Goal**: {one sentence — e.g. "Extract UserRepository from UserController to
|
||||
separate persistence logic from HTTP handling"}
|
||||
**Success criteria**:
|
||||
|
||||
1. All existing tests pass unchanged.
|
||||
2. {specific structural goal — e.g. "UserController no longer imports Database"}.
|
||||
3. Coverage does not decrease.
|
||||
4. {additional criteria if applicable}.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 3 — Decompose into Steps
|
||||
|
||||
Break the refactoring into the smallest individually-verifiable steps.
|
||||
Each step must:
|
||||
|
||||
- Be reversible (if tests break, revert and reassess).
|
||||
- Pass the full test suite independently.
|
||||
- Be one conceptual transformation (not "rename + extract + inline" in one step).
|
||||
- Take ≤ 5 minutes to write.
|
||||
|
||||
```markdown
|
||||
## Refactoring Steps
|
||||
|
||||
| Step | Action | Files Affected | Expected Outcome |
|
||||
| ---- | ----------------------------------------------- | ------------------------------------------------ | ---------------------------------- |
|
||||
| 1 | Extract `findById` method from controller | `user.controller.ts`, `user.repository.ts` (new) | Controller delegates to repository |
|
||||
| 2 | Extract `create` method | `user.controller.ts`, `user.repository.ts` | Same pattern as step 1 |
|
||||
| 3 | Inline `formatUser` helper (used once) | `user.controller.ts` | Remove one-line helper |
|
||||
| 4 | Rename `user.controller.ts` → `user.handler.ts` | `user.controller.ts`, 3 imports | Naming consistency |
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
|
||||
- Present the step plan to the user before executing.
|
||||
- If > 10 steps, the scope is too large — split into multiple refactoring
|
||||
sessions.
|
||||
- The user may approve, reorder, or reject individual steps.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 4 — Incremental Execution
|
||||
|
||||
For each step, in order:
|
||||
|
||||
1. **Transform**: apply the single conceptual change.
|
||||
2. **Verify**: run `bun run test:changed` — ALL affected tests must pass.
|
||||
3. **If PASS**: commit the step with a message describing the transformation:
|
||||
```
|
||||
refactor: extract {what} from {where}
|
||||
```
|
||||
4. **If FAIL**: revert the change. Do NOT fix the test or the code. Assess
|
||||
whether the step decomposition is wrong or the test was already flaky.
|
||||
- If the test was flaky (fails non-deterministically), fix the test first
|
||||
as a prerequisite step, then retry.
|
||||
- If the step decomposition is wrong, re-decompose from Phase 3.
|
||||
|
||||
**Revert policy**: revert immediately on failure. Do not attempt to fix
|
||||
within the same step — a failing test during refactoring means the step is
|
||||
not behavior-preserving, and you must find a smaller decomposition.
|
||||
|
||||
---
|
||||
|
||||
#### Phase 5 — Final Validation
|
||||
|
||||
After all steps are complete:
|
||||
|
||||
1. **Full test suite**: `bun run test:parallel` — all tests must pass.
|
||||
2. **Typecheck**: `bun typecheck` — zero errors.
|
||||
3. **Lint**: `bun oxlint --deny-warnings` — zero errors.
|
||||
4. **Coverage comparison**: compare post-refactor coverage to baseline.
|
||||
Coverage MUST NOT decrease (within ±1% for measurement noise).
|
||||
5. **Complexity comparison** (optional): confirm the refactoring improved
|
||||
the target metric (e.g. lower cyclomatic complexity).
|
||||
|
||||
---
|
||||
|
||||
#### Phase 6 — Report
|
||||
|
||||
```markdown
|
||||
# Refactor Report
|
||||
|
||||
**Scope**: {module/pattern}
|
||||
**Target**: {one-sentence goal}
|
||||
**Steps executed**: {N}
|
||||
|
||||
## Before / After
|
||||
|
||||
| Metric | Before | After | Delta |
|
||||
| ---------------- | ------ | ----- | --------- |
|
||||
| LOC in scope | {N} | {N} | {N} |
|
||||
| Coverage (lines) | {X}% | {Y}% | {delta} |
|
||||
| Complexity | {N} | {N} | {delta} |
|
||||
| Files touched | — | {N} | — |
|
||||
| Tests | {N} | {N} | 0 changed |
|
||||
|
||||
## Steps
|
||||
|
||||
| # | Action | Outcome |
|
||||
| --- | ------------------ | ------------- |
|
||||
| 1 | Extract `findById` | ✅ tests pass |
|
||||
| 2 | Extract `create` | ✅ tests pass |
|
||||
| ... | ... | ... |
|
||||
|
||||
## Verification
|
||||
|
||||
- `bun run test:parallel`: {N} passed, 0 failed
|
||||
- `bun typecheck`: ✅
|
||||
- `bun oxlint --deny-warnings`: ✅
|
||||
- Coverage delta: {delta}
|
||||
|
||||
## Design Deviation
|
||||
|
||||
{If the refactoring changes the internal architecture in a way that merits an
|
||||
ADR, reference the ADR. **Deprecated:** `.artifacts/{slug}/design/adr/{NNNN}-*.md`
|
||||
→ ADRs now live on the Gitea wiki at page `{slug}/03-adr-{NNNN}-{title}`,
|
||||
readable via `gitea_wiki__get_page`. Or "None".}
|
||||
|
||||
---
|
||||
|
||||
**Handoff**: {if > 50 lines or ≥ 5 files → run `core/skills/review-code/SKILL.md`
|
||||
| otherwise → refactor complete, no review needed}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Phase 7 — Approval
|
||||
|
||||
Present the report:
|
||||
|
||||
```
|
||||
Refactor complete: {one-line summary}
|
||||
- {N} steps executed, all tests pass
|
||||
- {before} → {after} ({delta} LOC)
|
||||
- Coverage: {before}% → {after}%
|
||||
|
||||
→ {if review needed: "Run code review?" | else: "Refactor complete. Approve?"}
|
||||
```
|
||||
@@ -0,0 +1,105 @@
|
||||
# Source Analysis — Document Templates
|
||||
|
||||
> Used by Phase B1 (pipeline mode).
|
||||
> Read this file when producing the source-analysis document set.
|
||||
> Each section below is the template for the corresponding numbered file
|
||||
> under `port-{name}/source-analysis/` (wiki pages via `wiki 读写 API(见 TERMINOLOGY)`).
|
||||
|
||||
## 01-source-overview.md
|
||||
|
||||
```markdown
|
||||
## Source Overview
|
||||
|
||||
- **Project**: {name}
|
||||
- **Language / Runtime**: {e.g. Python 3.11}
|
||||
- **Framework**: {e.g. FastAPI}
|
||||
- **Feature scope**: {description of what's being ported}
|
||||
- **Source files**: {N}
|
||||
- **Source LOC**: {L}
|
||||
- **Source modules**: {list of distinct functional areas}
|
||||
```
|
||||
|
||||
## 02-public-api.md
|
||||
|
||||
Document every public interface:
|
||||
|
||||
```markdown
|
||||
## Public API
|
||||
|
||||
| Method / Endpoint | Input Schema | Output Schema | Errors | Notes |
|
||||
| ----------------- | -------------- | --------------- | --------------------- | --------------- |
|
||||
| POST /auth/login | `{email, pw}` | `{token, user}` | 400, 401, 429, 500 | Rate limited |
|
||||
| GET /users/:id | path param | `User` object | 401, 403, 404 | Auth required |
|
||||
```
|
||||
|
||||
## 03-data-model.md
|
||||
|
||||
```markdown
|
||||
## Data Model
|
||||
|
||||
### Entity: User
|
||||
| Field | Type | Constraints |
|
||||
| ----------- | ---------- | ------------------- |
|
||||
| id | UUID | PK, not null |
|
||||
| email | string | unique, not null |
|
||||
| password | string | hashed, not null |
|
||||
| created_at | datetime | not null |
|
||||
|
||||
### Relationships
|
||||
- User 1—N Session
|
||||
- User N—M Role
|
||||
```
|
||||
|
||||
## 04-business-logic.md
|
||||
|
||||
Capture every business rule, validation, edge case, and state transition
|
||||
from the source. Write each rule as an executable assertion:
|
||||
|
||||
```markdown
|
||||
## Business Logic
|
||||
|
||||
### Login
|
||||
- RULE-01: Valid credentials → return JWT + user object
|
||||
- RULE-02: Invalid password → 401 "Invalid credentials"
|
||||
- RULE-03: Non-existent email → 401 "Invalid credentials" (same message, no enumeration)
|
||||
- RULE-04: 5 failed attempts in 1 min → 429 + lock for 15 min
|
||||
- RULE-05: Locked account + valid password → 423 "Account locked"
|
||||
|
||||
### Edge Cases
|
||||
- Empty email → 400 "Email is required"
|
||||
- Email > 254 chars → 400 "Email too long"
|
||||
- Password < 8 chars → 400 "Password too short"
|
||||
```
|
||||
|
||||
## 05-error-handling.md
|
||||
|
||||
```markdown
|
||||
## Error Handling
|
||||
|
||||
| Error Code | HTTP Status | Message | Source Condition |
|
||||
| ---------- | ----------- | -------------------- | --------------------- |
|
||||
| AUTH_001 | 400 | Email is required | empty email |
|
||||
| AUTH_002 | 401 | Invalid credentials | wrong email or pw |
|
||||
| AUTH_003 | 429 | Too many attempts | rate limit exceeded |
|
||||
| AUTH_004 | 423 | Account locked | locked out |
|
||||
```
|
||||
|
||||
## 10-fidelity-baseline.md
|
||||
|
||||
This is the master inventory used by `verify` at the end. Every source
|
||||
behavior is listed as a checkable item:
|
||||
|
||||
```markdown
|
||||
## Fidelity Baseline
|
||||
|
||||
| ID | Behavior | Type | Source Test | Chunk |
|
||||
| ---------- | ----------------------------------- | ------------ | -------------------- | ------------ |
|
||||
| FID-001 | Login with valid credentials | happy path | test_login_ok | chunk-auth |
|
||||
| FID-002 | Login with invalid password | error path | test_login_bad_pw | chunk-auth |
|
||||
| FID-003 | Login with empty email | edge case | test_login_empty | chunk-auth |
|
||||
| FID-004 | Rate limiting after 5 attempts | error path | test_rate_limit | chunk-auth |
|
||||
```
|
||||
|
||||
Each `FID-*` item maps to a `Chunk` column — this drives the DAG node
|
||||
decomposition. Behaviors in the same chunk are ported together. The
|
||||
`Source Test` column traces back to the original test for the verify stage.
|
||||
@@ -0,0 +1,301 @@
|
||||
# Source Analysis & Review — Detailed Processes
|
||||
|
||||
> Extracted from `implement/SKILL.md` (Mode: port) Phase A1, A1.5, A1.7, A1.8.
|
||||
> Read this file when executing the Source Analysis phases in standalone mode.
|
||||
|
||||
---
|
||||
|
||||
## Phase A1 — Understand Source
|
||||
|
||||
Read the source feature thoroughly — you must understand it well enough to
|
||||
reimplement it from scratch in a different tech stack.
|
||||
|
||||
### Process
|
||||
|
||||
1. **Source code** — read every file in the source scope. Understand:
|
||||
- Public API (method signatures, request/response schemas).
|
||||
- Data model (entities, fields, relationships).
|
||||
- Business logic (validation, business rules, edge cases).
|
||||
- Error handling (exception types, error codes, error messages).
|
||||
- Configuration (environment variables, feature flags, constants).
|
||||
|
||||
2. **Source tests** — read all tests for the source feature. Tests are the
|
||||
authoritative specification of behavior. Pay attention to:
|
||||
- Happy path assertions.
|
||||
- Edge case and boundary condition tests.
|
||||
- Error path tests.
|
||||
- Mock/stub setup (external dependencies).
|
||||
|
||||
After reading all source tests, run an automated extraction to seed the
|
||||
fidelity baseline:
|
||||
|
||||
1. **For each test file**, extract every test case name (e.g. `describe`/`it`
|
||||
blocks, function names in test files).
|
||||
2. **Generate a raw FID list** — one `FID-*` entry per test case:
|
||||
| FID-* | Test Name | Source File:Line | Type |
|
||||
| ----- | --------- | ---------------- | ---- |
|
||||
3. **Do NOT skip** — every test case becomes a FID item. Missing FID items
|
||||
are the #1 cause of incomplete porting.
|
||||
4. **Save** the raw FID list to wiki page `port-{name}/source-analysis/fid-raw` (via `wiki 读写 API(见 TERMINOLOGY)`).
|
||||
|
||||
In pipeline mode (Phase B1), this raw FID list feeds into `10-fidelity-baseline.md`.
|
||||
|
||||
3. **Source dependencies** — list every library, service, and infrastructure
|
||||
the source feature depends on:
|
||||
- Language runtime and version.
|
||||
- Framework (web framework, ORM, etc.).
|
||||
- Libraries (auth, logging, data parsing, etc.).
|
||||
- Infrastructure (database, cache, message queue, file storage).
|
||||
- External services (APIs, SaaS).
|
||||
|
||||
4. **Source Function Inventory** — produce a function-level catalog of every
|
||||
public API, private helper, and configuration constant in the source scope.
|
||||
This is the completeness audit trail — every unported function is visible.
|
||||
|
||||
### Output Templates
|
||||
|
||||
#### Source Analysis Document
|
||||
|
||||
```markdown
|
||||
## Source Analysis: {source feature name}
|
||||
|
||||
### Public API
|
||||
|
||||
| Endpoint / Method | Input | Output | Error Cases |
|
||||
| ----------------- | ----- | ------ | ----------- |
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
### Data Model
|
||||
|
||||
| Entity | Fields | Relations |
|
||||
| ------ | ------ | --------- |
|
||||
| ... | ... | ... |
|
||||
|
||||
### Dependencies
|
||||
|
||||
| Dep | Purpose | Available in Target? |
|
||||
| ------ | --------- | ------------------------ |
|
||||
| {name} | {purpose} | {yes / no / alternative} |
|
||||
```
|
||||
|
||||
#### Source Function Inventory
|
||||
|
||||
```markdown
|
||||
### Source Function Inventory
|
||||
|
||||
| Source File | Function / Symbol | Line | Type (public/private/config) | Ported? | Target Location |
|
||||
| ----------- | ----------------- | ---- | ---------------------------- | ------- | --------------- |
|
||||
| ... | ... | ... | ... | ☐ | |
|
||||
```
|
||||
|
||||
After Phase A5 (or at the end), require the Developer to backfill the "Ported?"
|
||||
and "Target Location" columns. Add a note: "Any ☐ remaining in the 'Ported?'
|
||||
column is a port gap."
|
||||
|
||||
---
|
||||
|
||||
## Phase A1.5 — Source Analysis Review (GATE)
|
||||
|
||||
Before proceeding to concept mapping, a reviewer (Explorer sub-agent) MUST
|
||||
cross-check the source analysis deliverables against the original source files.
|
||||
This is a lightweight but mandatory gate — source misunderstandings are the #1
|
||||
root cause of incomplete porting.
|
||||
|
||||
### Review Process
|
||||
|
||||
1. **Spawn a reviewer** (Explorer sub-agent) with access to:
|
||||
- All source files in the original project (or their copies if offline).
|
||||
- All A1 deliverables: Source Analysis doc, Source Function Inventory,
|
||||
`fid-raw.md`.
|
||||
|
||||
2. **Reviewer checks (4 dimensions)**:
|
||||
- **SRC-CMP** (Completeness — **automated, not manual**): Does the Source
|
||||
Function Inventory list every public/private function, symbol, and config
|
||||
constant found in source files? This dimension MUST be verified by an
|
||||
automated symbol diff (see step 2.5), not by the reviewer reading source
|
||||
files one-by-one. Manual "looks complete" judgments are the dominant
|
||||
failure mode for port completeness — they are the reason functions get
|
||||
silently dropped. Any symbol present in source but absent from the
|
||||
inventory is a BLOCKER gap.
|
||||
- **SRC-API** (API Accuracy): Does the Public API table correctly capture
|
||||
every endpoint/method, its input/output schema, and all documented error
|
||||
cases? Compare against source route/method definitions and error handling
|
||||
code.
|
||||
- **SRC-TST** (Test Coverage): Does `fid-raw.md` contain one FID entry for
|
||||
every `describe`/`it`/`test` block in the source test files? Any test case
|
||||
without a FID is a gap. Does every FID reference the correct source
|
||||
file:line?
|
||||
- **SRC-DEP** (Dependency Accuracy): Are all libraries, infrastructure
|
||||
services, and external APIs the source depends on listed? Check source
|
||||
package manager files (`package.json`, `Cargo.toml`, `requirements.txt`,
|
||||
etc.) and imports.
|
||||
|
||||
2.5. **SRC-CMP automated symbol verification (mandatory)** — Enumerate every
|
||||
symbol the source actually exports, then diff against the Source Function
|
||||
Inventory. This converts "is the inventory complete?" from a subjective
|
||||
judgment into an objective set difference. **Do NOT skip even if codegraph
|
||||
is unavailable** — fall back to `grep`, never to a manual glance.
|
||||
|
||||
```bash
|
||||
# Preferred: codegraph symbol enumeration (one call per source file in scope)
|
||||
codegraph_node --symbolsOnly <source-file>
|
||||
|
||||
# Fallback: grep for declarations in the source language
|
||||
grep -rEn '^\s*(export (async )?(function|const|class|interface|type|enum)|export \{|def |class |fn |public )' <source-dir>
|
||||
|
||||
# Then diff the enumerated source-symbol set against the inventory's
|
||||
# "Function / Symbol" column. Every source-only symbol is a BLOCKER.
|
||||
```
|
||||
|
||||
Record the command used and the resulting symbol-set delta under dimension
|
||||
`SRC-CMP` in `source-analysis-review.md`. A review that omits this
|
||||
automated delta is itself a BLOCKER — the gate was bypassed, not passed.
|
||||
|
||||
3. **Output**: Reviewer writes findings to wiki page `port-{name}/source-analysis/review` (via `wiki 读写 API(见 TERMINOLOGY)`) with format:
|
||||
|
||||
| Dimension | Finding | Severity (BLOCKER/MAJOR/MINOR) | Source Evidence |
|
||||
| --------- | ------- | ------------------------------ | --------------- |
|
||||
| SRC-CMP | Missing function `validateSession` in `auth/middleware.js:45` | MAJOR | Source file line 45 |
|
||||
|
||||
4. **GATE**: All BLOCKER findings MUST be resolved (add missing items to
|
||||
inventory/fid list) before proceeding to Phase A2. MAJOR findings require
|
||||
documented justification if deferred.
|
||||
|
||||
5. **Pass condition**: Developer prints:
|
||||
`SOURCE ANALYSIS REVIEW COMPLETE — {N} BLOCKER items fixed, {M} MAJOR items documented`
|
||||
|
||||
---
|
||||
|
||||
## Phase A1.7 — Target Surface Analysis
|
||||
|
||||
> **Root cause addressed**: Ports fail when the target project's receiving
|
||||
> surface is not analyzed. The Developer knows the source inside-out but has
|
||||
> no systematic picture of what the target already has, what it lacks, and
|
||||
> what structural changes are needed to receive the port. This phase closes
|
||||
> that gap.
|
||||
|
||||
Analyze the **target project's current state** to establish the receiving
|
||||
surface for the port. This is the mirror image of Phase A1 — instead of
|
||||
understanding the source, you understand the target.
|
||||
|
||||
### Process
|
||||
|
||||
1. **Target directory tree** — map the target project's package structure,
|
||||
especially the packages that will receive ported code or that the source
|
||||
feature depends on. For monorepos, list every package and its role.
|
||||
|
||||
2. **Target existing capabilities** — identify what the target project
|
||||
already has that overlaps with or relates to the source feature:
|
||||
- Existing modules, components, services in the same domain.
|
||||
- Existing routes, providers, context hierarchy.
|
||||
- Existing schemas, migrations, config entries.
|
||||
- Existing CLI commands, flags.
|
||||
- Existing theme/style files.
|
||||
|
||||
3. **Automated structural diff** — run a source vs target comparison across
|
||||
multiple dimensions to surface gaps that manual reading misses:
|
||||
|
||||
```bash
|
||||
# Directory structure diff (source feature scope vs target equivalent)
|
||||
diff <(cd /source && find packages/app/src -name '*.tsx' | sort) \
|
||||
<(cd /target && find packages/app/src -name '*.tsx' | sort)
|
||||
|
||||
# Dependency diff (package.json)
|
||||
diff <(jq '.dependencies | keys' /source/packages/app/package.json) \
|
||||
<(jq '.dependencies | keys' /target/packages/app/package.json)
|
||||
|
||||
# Export symbol diff (if codegraph is available)
|
||||
diff <(codegraph exports @source-ai/app) \
|
||||
<(codegraph exports @target-ai/app)
|
||||
```
|
||||
|
||||
If `codegraph` is not available, use `grep` for exported symbols or
|
||||
`glob` for file presence. The goal is **systematic, not manual** —
|
||||
never rely on reading files one by one to discover what the target has.
|
||||
|
||||
4. **Integration point identification** — where in the target project will
|
||||
the ported code connect?
|
||||
- Route table changes (new routes, modified redirects).
|
||||
- Provider/context hierarchy changes (new providers, insertion points).
|
||||
- Schema/migration additions (new tables, new columns).
|
||||
- Config/settings additions (new config entries, new setting keys).
|
||||
- CLI command additions or flag additions.
|
||||
- Build config changes (vite/webpack/tsconfig).
|
||||
- Package.json dependency additions.
|
||||
|
||||
5. **Target readiness assessment** — does the target need structural
|
||||
refactoring before it can receive the port?
|
||||
- Does the target need a new package? (e.g. a new `packages/timeline/`)
|
||||
- Does the target need an interface extraction? (e.g. extract
|
||||
`ServerService` to an interface before porting a new implementation)
|
||||
- Does the target need a migration to add tables/columns?
|
||||
- Does the target need config schema changes?
|
||||
|
||||
### Output
|
||||
|
||||
Publish to wiki page `port-{name}/source-analysis/target-surface` (via `wiki 读写 API(见 TERMINOLOGY)`) using the format
|
||||
in `reference/target-surface-template.md` (read it when executing this phase).
|
||||
|
||||
---
|
||||
|
||||
## Phase A1.8 — Capability Boundary Definition (GATE)
|
||||
|
||||
> **Root cause addressed**: Ports fail because the porting unit is "files"
|
||||
> rather than "capabilities". A single capability (e.g. "draft/tab system")
|
||||
> spans code files, type definitions, schemas, config, routes, providers,
|
||||
> themes, and tests. When the Developer ports only the files they see and
|
||||
> misses the implicit artifacts, the port is incomplete. This phase enforces
|
||||
> a complete artifact inventory per capability before any implementation.
|
||||
|
||||
Define the **complete boundary** of the capability being ported. A capability
|
||||
is not a file — it is the full set of artifacts required for the feature to
|
||||
function in the target project.
|
||||
|
||||
### Artifact Dimensions
|
||||
|
||||
Every capability MUST be analyzed across ALL 13 dimensions listed in
|
||||
`reference/capability-boundary-template.md` (read it when executing this
|
||||
phase). The 13 dimensions are: source code files, type definitions/interfaces,
|
||||
database schema/migrations, configuration entries, environment variables, CLI
|
||||
flags/commands, theme/style files, route definitions, provider/context
|
||||
hierarchy, build config changes, package.json dependencies, test files, and
|
||||
shared package changes. A dimension with no artifacts is explicitly marked
|
||||
"N/A — none required" (not silently skipped).
|
||||
|
||||
### Process
|
||||
|
||||
1. **For each dimension**, list every artifact:
|
||||
- **Source has**: what exists in the source project for this dimension.
|
||||
- **Target already has**: what the target project already has (from
|
||||
Phase A1.7 Target Surface Analysis).
|
||||
- **Needs creation / modification**: what must be created or changed in
|
||||
the target.
|
||||
- **Status**: ☐ not ported / ☑ ported / ⏭ N/A (none required)
|
||||
|
||||
2. **Cross-reference with Phase A1 Source Function Inventory** — every
|
||||
function/symbol in the inventory MUST appear in dimension 1 (source code
|
||||
files) or dimension 2 (type definitions). Any orphan is a gap.
|
||||
|
||||
3. **Cross-reference with Phase A1.7 Target Surface Analysis** — every
|
||||
"Gap" in the structural diff table MUST have a corresponding entry in
|
||||
the capability boundary. Any orphan is a gap.
|
||||
|
||||
4. **GATE**: All 13 dimensions MUST be filled in. A dimension with artifacts
|
||||
marked "☐ not ported" is acceptable ONLY if there is a documented deferral
|
||||
with a reactivation path (same rules as Phase A3 Gap Analysis). Dimensions
|
||||
that are "N/A — none required" must include a one-line justification.
|
||||
|
||||
### Output
|
||||
|
||||
Publish to wiki page `port-{name}/source-analysis/capability-boundary` (via `wiki 读写 API(见 TERMINOLOGY)`) using the format in
|
||||
`reference/capability-boundary-template.md` (includes the full 13-dimension
|
||||
table and output template).
|
||||
|
||||
### Pass condition
|
||||
|
||||
Developer prints:
|
||||
`CAPABILITY BOUNDARY COMPLETE — {N}/{13} dimensions have artifacts, {M} dimensions N/A, {K} items deferred with reactivation path`
|
||||
|
||||
**Do NOT proceed to Phase A2 until this gate passes.** The capability boundary
|
||||
is the single source of truth for "what must be ported" — every downstream
|
||||
phase references it.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Target Surface Analysis — Output Template
|
||||
|
||||
> Used by Phase A1.7 (standalone) and Phase B1.7 (pipeline).
|
||||
> Read this file when executing the Target Surface Analysis phase, then
|
||||
> produce the output document following this format.
|
||||
|
||||
Publish the output to wiki page `port-{name}/source-analysis/target-surface`
|
||||
(standalone) or `port-{name}/source-analysis/11-target-surface`
|
||||
(pipeline) via `wiki 读写 API(见 TERMINOLOGY)`.
|
||||
|
||||
```markdown
|
||||
## Target Surface Analysis
|
||||
|
||||
### Target Project Structure
|
||||
- Package map (package name → role)
|
||||
- Relevant directory trees
|
||||
|
||||
### Existing Capabilities (overlapping with source)
|
||||
| Target Module | Overlap with Source | Action (reuse / replace / extend) |
|
||||
| ------------- | ------------------- | --------------------------------- |
|
||||
| ... | ... | ... |
|
||||
|
||||
### Structural Diff Summary
|
||||
| Dimension | Source has | Target has | Gap |
|
||||
| --------------- | ---------- | ---------- | --- |
|
||||
| Files (.tsx) | {N} files | {M} files | {N-M} new |
|
||||
| Dependencies | {list} | {list} | {diff} |
|
||||
| Export symbols | {list} | {list} | {diff} |
|
||||
| Routes | {list} | {list} | {diff} |
|
||||
| Providers | {list} | {list} | {diff} |
|
||||
| Schemas | {list} | {list} | {diff} |
|
||||
| CLI commands | {list} | {list} | {diff} |
|
||||
| Theme files | {list} | {list} | {diff} |
|
||||
| Config entries | {list} | {list} | {diff} |
|
||||
| Env vars | {list} | {list} | {diff} |
|
||||
| Build config | {list} | {list} | {diff} |
|
||||
|
||||
### Integration Points
|
||||
| Integration Point | Change Required | Affected Target Files |
|
||||
| ----------------- | --------------- | --------------------- |
|
||||
| Route table | Add /new-session route | src/app.tsx |
|
||||
| Provider hierarchy | Insert TabsProvider | src/app.tsx |
|
||||
| ... | ... | ... |
|
||||
|
||||
### Target Readiness
|
||||
| Readiness Item | Required? | Complexity | Blocking? |
|
||||
| -------------- | --------- | ---------- | --------- |
|
||||
| New package | No | — | No |
|
||||
| Interface extraction | Yes | Medium | Yes |
|
||||
| Migration | Yes | Low | Yes |
|
||||
| Config schema | No | — | No |
|
||||
```
|
||||
@@ -0,0 +1,123 @@
|
||||
> Extracted from implement/SKILL.md (Pipeline Work Item Detection) — moved verbatim 2026-08-25, ticket [org-internal #3381].
|
||||
|
||||
## Pipeline Work Item Detection
|
||||
|
||||
Not all work items in an iteration plan involve writing new code. Some require
|
||||
fixing bugs, restructuring existing code, porting features, or building
|
||||
frontend UI. These specialized work items use different execution workflows
|
||||
(defined above) but flow through the same pipeline gates (review-code →
|
||||
verify; DAG-routed work resolves its spec from `{epic-slug}/dag`).
|
||||
|
||||
### Refactoring Work Items
|
||||
|
||||
A work item is a refactoring work item when:
|
||||
- Its description starts with "Refactor", "重构", "Restructure", "Extract",
|
||||
"Inline", "Move", "Rename", "Simplify", "Upgrade", or "Remove dead code".
|
||||
- It is explicitly tagged `[REFACTOR]` in the node ticket / request.
|
||||
- The node spec identifies it as a structural change that preserves
|
||||
behavior (no new capabilities, no bug fixes).
|
||||
- Requirements coverage is a refactoring requirement (REQ-REFACTOR-*).
|
||||
|
||||
When a work item is a refactoring work item, follow the workflow defined in
|
||||
Mode: refactor above with these adaptations:
|
||||
|
||||
1. **Scope & Baseline** (refactor Phase 1): The scope is the node spec /
|
||||
work-item description, not free-form user input.
|
||||
2. **Define Target Pattern** (refactor Phase 2): The target pattern must align
|
||||
with the baseline's architecture decisions (node spec + contracts). If the
|
||||
baseline does not prescribe a pattern, justify the choice in the refactor
|
||||
report.
|
||||
3. **Decompose into Steps** (refactor Phase 3): Present steps to the user for
|
||||
approval per the refactor workflow. If the design document decides the target
|
||||
pattern, the steps are not negotiable — they are derived from that decision.
|
||||
4. **Incremental Execution** (refactor Phase 4): Same as standalone refactor.
|
||||
Commit each step separately.
|
||||
5. **Final Validation** (refactor Phase 5): Run `bun run test:parallel`, `bun typecheck`,
|
||||
`bun oxlint --deny-warnings`. Coverage must not decrease.
|
||||
6. **Produce the implementation report** using the standard Phase 5 (Report)
|
||||
from ### Mode: implement (default). Embed the refactor report as the
|
||||
report body. The file change table and acceptance criteria table follow the
|
||||
standard format so the handoff to code review is seamless.
|
||||
|
||||
After the refactoring is complete, proceed to the standard Phase 5 (Report)
|
||||
and Phase 6 (Handoff to Code Review) exactly as a standard implementation
|
||||
work item would. The code review gate is mandatory for all refactoring work
|
||||
items regardless of size — there is no "no review needed" bypass.
|
||||
|
||||
### Bugfix Work Items
|
||||
|
||||
A work item is a bugfix work item when:
|
||||
- Its description starts with "Bugfix", "Fix", "修复", "Bug", or "Hotfix".
|
||||
- It is explicitly tagged `[BUGFIX]` in the node ticket / request.
|
||||
- The node spec identifies it as a correction of existing behavior
|
||||
(no new capabilities).
|
||||
- Its description references a bug report, stack trace, or root cause analysis
|
||||
from the bugfix workflow's Phases 1–2.
|
||||
|
||||
When a work item is a bugfix work item, follow the workflow defined in
|
||||
Mode: bugfix above with these adaptations:
|
||||
|
||||
1. **Understand & Reproduce** (bugfix Phase 1): The scope is the work item
|
||||
description and the bug reproduction steps captured in requirements.
|
||||
2. **Isolate Root Cause** (bugfix Phase 2): The root cause may already be
|
||||
documented in the requirements; verify it against the current codebase.
|
||||
If the root cause differs, flag a design gap and abort.
|
||||
3. **Write Regression Test** (bugfix Phase 3): Before fixing, write a test
|
||||
that fails with the bug's symptom. The test must exercise the exact
|
||||
condition described in the acceptance criteria.
|
||||
4. **Fix** (bugfix Phase 4): Apply the minimal surgical fix. The fix MUST
|
||||
match the design document's component and interface decisions.
|
||||
5. **Self-Check & Report** (bugfix Phase 5): Run `bun run test:changed`, `bun typecheck`,
|
||||
`bun oxlint --deny-warnings`. Verify every item in `core/checklists/bugfix.md`.
|
||||
6. **Produce the implementation report** using the standard Phase 5 (Report)
|
||||
from ### Mode: implement (default). Embed the bugfix report as the report
|
||||
body. The file change table and acceptance criteria table follow the
|
||||
standard format so the handoff to code review is seamless.
|
||||
|
||||
After the bugfix is complete, proceed to the standard Phase 5 (Report) and
|
||||
Phase 6 (Handoff to Code Review) exactly as a standard implementation work
|
||||
item would. The code review gate is mandatory for all bugfix work items
|
||||
regardless of size — there is no "no review needed" bypass for pipeline
|
||||
bugfixes.
|
||||
|
||||
### Frontend Work Items
|
||||
|
||||
A work item is a frontend work item when:
|
||||
- Its description starts with "Frontend", "UI", "Component", "Page", "Style",
|
||||
"前端", "UI", "组件", "页面", or "样式".
|
||||
- It is explicitly tagged `[FRONTEND]` in the node ticket / request.
|
||||
- The node spec identifies it as a UI-layer change.
|
||||
- The work item's component mapping (node `req_refs` + component field in
|
||||
`{epic-slug}/dag`; historically `{slug}/03-design-08-traceability`) shows
|
||||
components in `components/`, `pages/`, `views/`, `ui/`, or frontend
|
||||
framework directories.
|
||||
- The work item involves `.tsx`, `.jsx`, `.vue`, `.svelte`, `.astro`, `.css`,
|
||||
or `.scss` files exclusively (no backend or data-layer files).
|
||||
|
||||
When a work item is a frontend work item, follow the workflow defined in
|
||||
`core/skills/frontend/SKILL.md` with these adaptations:
|
||||
|
||||
1. **Parse Context** (frontend Phase 1): The scope is the work item
|
||||
description, the design sections it references, and the project's
|
||||
framework/styling conventions. Read neighboring frontend files to absorb
|
||||
patterns before writing code.
|
||||
2. **Plan UI Implementation** (frontend Phase 2): Produce a brief
|
||||
implementation plan covering component structure, props, state variants
|
||||
(loading/empty/error/edge), and accessibility requirements. Cross-
|
||||
reference every design specification.
|
||||
3. **Implement** (frontend Phase 3): Implement in layers — structure → style
|
||||
→ state variants → interactivity → accessibility pass. Every component
|
||||
MUST render gracefully in all states.
|
||||
4. **Self-Check** (frontend Phase 4): Run `bun typecheck`, `bun oxlint --deny-warnings`,
|
||||
`bun run test:changed`. Verify every item in `core/checklists/frontend.md`.
|
||||
5. **Produce the implementation report** using the standard Phase 5 (Report)
|
||||
from ### Mode: implement (default). Embed the frontend report as the report
|
||||
body. Include state coverage and accessibility verification tables. The file
|
||||
change table and acceptance criteria table follow the standard format so the
|
||||
handoff to code review is seamless.
|
||||
|
||||
After the frontend implementation is complete, proceed to the standard Phase 5
|
||||
(Report) and Phase 6 (Handoff to Code Review) exactly as a standard
|
||||
implementation work item would. The code review gate is mandatory for all
|
||||
frontend work items regardless of size — there is no "no review needed" bypass
|
||||
for pipeline frontend work.
|
||||
@@ -0,0 +1,203 @@
|
||||
---
|
||||
name: land-batch
|
||||
description: >
|
||||
Use ONLY when the orchestrator lands the finished task branches of one DAG
|
||||
iteration as a single batch PR ([org-internal #3731] batch-landing pilot). Composes
|
||||
path-disjoint member branches via the deterministic batch-compose script,
|
||||
pre-validates locally, opens ONE PR for N tasks (one CI fan-out instead of
|
||||
N on the single-runner pool), pastes each member's review synthesis into
|
||||
the PR conversation, runs one delta-scoped review-code on the batch head,
|
||||
then hands the labeled PR to the merge coordinator. NOT for hotfixes /
|
||||
CI-infra fixes (fast lane: 1:1 PR), single tasks, or single-session
|
||||
mono-branch batches (topology A — one session owns every ticket on one
|
||||
branch from inception; hand-compose those, see Applicability).
|
||||
stage: merge-pr
|
||||
triggers:
|
||||
- land batch
|
||||
- batch land
|
||||
- batch-compose
|
||||
- 批量归并
|
||||
- 批量提交 pr
|
||||
role: Producer
|
||||
---
|
||||
|
||||
# land-batch — batch PR composition for one DAG iteration
|
||||
|
||||
**Orchestrator-only.** The composing session is the epic's orchestrator
|
||||
(single writer at iteration close — the same role that already owns uniform
|
||||
merge/close). `workflow/batch/*` branches are NOT claim branches: no
|
||||
`claim.sh` interaction, no assignee; the orchestrator is the only composer.
|
||||
Pilot charter: the decision comment on [org-internal #3731] (scope, opt-in protocol, cap,
|
||||
success criteria) — this skill implements that charter, not the epic's
|
||||
frozen DAG v1 (which covers only the merge-coordinator nodes N-01..04).
|
||||
|
||||
## Applicability: post-hoc composition only (topology B)
|
||||
|
||||
Two batch shapes exist ([org-internal #3731] charter addendum, comment 39347):
|
||||
|
||||
- **Topology A — single-session mono-branch batch**: one session owns every
|
||||
ticket from inception and writes them on ONE branch (instance: PR [org-internal #3861],
|
||||
audit-h1). Hand composition is natural there — do NOT force this skill on
|
||||
it; record the outcome on [org-internal #3731] as a manual-batch datum instead.
|
||||
- **Topology B — parallel workers, post-hoc composition**: workers finish on
|
||||
SEPARATE branches at different times; the orchestrator composes at
|
||||
iteration close. This is the skill's ONLY applicability — cross-branch
|
||||
conflict attribution, lockfile convergence, and composition ordering are
|
||||
exactly the error-prone parts it mechanizes (path-disjoint admission,
|
||||
fail-closed attribution, `--convergent` surgery, per-member commit
|
||||
retention for revert granularity).
|
||||
|
||||
Quick discriminator: if every "member" would be a commit on a branch you are
|
||||
already writing, that is topology A — keep going, skip this skill. If you
|
||||
are landing OTHER workers' branches you never owned, that is topology B —
|
||||
this skill applies.
|
||||
|
||||
## Preconditions (all mandatory, fail-closed)
|
||||
|
||||
1. **Pilot opt-in**: the epic runs in batch-landing mode — its workers were
|
||||
seeded to report `status=done branch=<ref>` WITHOUT opening PRs (see the
|
||||
pilot note in `headless-session-ops/reference/completion-report.md`).
|
||||
Non-pilot epics keep the per-task 1:1 PR flow.
|
||||
2. **Every member is done**: review-code PASS on its own branch, run bundle
|
||||
archived (`gen-run-bundle.ts --slug <slug> --ticket <N>` — the bundle is
|
||||
committed on the member branch, so composition carries it; the
|
||||
merge-gate's presence check is per close-ref and expects it).
|
||||
3. **2..7 members** — a single done task rides its own 1:1 PR; more than 7
|
||||
(`batch-compose --max-members`, the pilot cap) splits into multiple
|
||||
batches along the path-overlap report.
|
||||
4. **No fast-lane cargo**: prod hotfixes and CI-infra fixes bypass batching
|
||||
entirely (1:1 PR, land immediately) — batching latency must never gate
|
||||
urgent work.
|
||||
|
||||
## Procedure
|
||||
|
||||
### 1. Compose (deterministic core)
|
||||
|
||||
```bash
|
||||
bun .gitea/scripts/batch-compose.ts \
|
||||
--branch workflow/task/<a> --branch workflow/task/<b> … \
|
||||
--out workflow/batch/<epic-slug>-iter<N> --dry-run
|
||||
```
|
||||
|
||||
For batches whose members ALL rewrite a regenerable shared file — the
|
||||
rule, not the exception, for dependency-bump batches (every `bun install`
|
||||
rewrites the root `bun.lock`) — pass `--convergent bun.lock`: the file is
|
||||
exempt from the overlap admission, and a merge that conflicts ONLY on it is
|
||||
surgically resolved to the running head's version (dropped from the index
|
||||
outright when the head no longer carries the path), with the JSON
|
||||
reporting `converged: ["bun.lock"]` and a regeneration hint. A conflict
|
||||
report carrying shapes the tool cannot attribute (rename/delete etc.)
|
||||
fails CLOSED — convergent exemption never widens into a blind pass.
|
||||
|
||||
Exit codes / handling: `1 usage / no-changes-over-base` → drop the named
|
||||
member or fix the invocation; `3 path-overlap` → split the batch along the
|
||||
reported pairs (or land the overlapping member 1:1) and re-run per group;
|
||||
`4 merge conflict` / `5 transport/git error` → fall
|
||||
back to 1:1 for this batch; `0` → drop `--dry-run`
|
||||
and push the composed branch. The script never touches the worktree or
|
||||
index (keeper-style plumbing) and each member keeps its original commits —
|
||||
revert granularity stays per-task.
|
||||
|
||||
### 2. Pre-validate locally (never enter CI red)
|
||||
|
||||
```bash
|
||||
git fetch origin workflow/batch/<slug>
|
||||
git switch --detach origin/workflow/batch/<slug>
|
||||
cd packages/octopus && bun run test:changed
|
||||
cd ../.. && git switch - # back to the orchestrator branch
|
||||
```
|
||||
|
||||
If the compose output carried a `regenerate convergent files (…)` hint —
|
||||
regenerate them FIRST, on the detached batch branch, and push the result
|
||||
(the composed branch deliberately carries a stale convergent file: either
|
||||
the surgically-resolved running-head version, or a member's clean-merged
|
||||
one; either way it is deterministic only after regeneration):
|
||||
|
||||
```bash
|
||||
git switch --detach origin/workflow/batch/<slug>
|
||||
bun install # regenerates bun.lock
|
||||
git add bun.lock # NEVER `commit -am` here: -a stages the
|
||||
# worktree's symlink stubs and trips the
|
||||
# pre-commit symlink guard (core.symlinks=false)
|
||||
git commit -m "[<epic-slug>][iter-N] chore(deps): regenerate bun.lock for batch <slug>"
|
||||
git push origin HEAD:refs/heads/workflow/batch/<slug>
|
||||
```
|
||||
|
||||
(On detached HEAD the commit-msg hook's format check is inert — the
|
||||
`[slug][iter-N]` prefix above keeps the corpus contract anyway.)
|
||||
|
||||
Failure → fix on the offending member branch (it is the only place the
|
||||
fault can live — paths are disjoint), re-compose, re-validate. Do NOT push
|
||||
a known-red batch.
|
||||
|
||||
### 3. Open the batch PR (gitea-rest recipes)
|
||||
|
||||
Title: `[<epic-slug>][iter-N] batch: <task-a> / <task-b> / …`
|
||||
|
||||
Body constraints (each is a merge-gate requirement, not style):
|
||||
|
||||
- **English close keywords, one per member issue**: `Closes #A`, `Closes #B`,
|
||||
… — Gitea auto-closes all of them; Chinese 关闭/修复 never auto-close.
|
||||
- **Zero unchecked top-level `- [ ]` lines in the body** — the gate's regex
|
||||
(`^-\s+\[ \]`) blocks on any unindented unchecked box, with no fence
|
||||
stripping. Render deferred/pending items as
|
||||
plain text (`· deferred: …`), never as checkboxes; the per-task live
|
||||
checklists stay on their issues.
|
||||
- Per-member summary (ticket, branch, one line) + bundle index.
|
||||
|
||||
### 4. Paste member syntheses into the PR conversation
|
||||
|
||||
Post one comment per member carrying its review-code verdict block
|
||||
(`**Verdict**: PASS|WARN` + the fenced JSON synthesis, from the member
|
||||
issue's review thread). **Why**: the merge-gate hunts FAIL verdicts only in
|
||||
the PR's own comment stream — a batch PR without these comments passes
|
||||
vacuously (a fake-green hole). Pasting restores FAIL visibility; a member
|
||||
synthesis of FAIL blocks the gate exactly as a 1:1 PR's would.
|
||||
|
||||
### 5. One review-code pass on the batch head
|
||||
|
||||
Run review-code against the batch PR. Members already PASS'd individually —
|
||||
scope the review delta to what composition added (merge seams, cross-member
|
||||
integration the per-task reviews could not see). This posts the required
|
||||
per-sha `pipeline/review-code` status; member statuses do NOT transfer to
|
||||
the composed head.
|
||||
|
||||
### 6. Label and hand off
|
||||
|
||||
Label the PR `ready-to-merge`. From here the flow is stock: the merge
|
||||
coordinator's double-green queue serially merges (CI/check +
|
||||
pipeline/merge-gate on the batch head, bot approve, main-account merge).
|
||||
Do NOT hand-merge, do NOT hand-sync main into the batch branch.
|
||||
|
||||
### 7. Post-merge expectations
|
||||
|
||||
Member issues auto-close via the English keywords; their `## 当前状态`
|
||||
PR/代码评审/CI rows fan out to EVERY close ref (status-sync poller Wide
|
||||
behavior — requires [org-internal #3867], merged 2026-08-31). `task done ≡ PR merged`
|
||||
still holds, transitively
|
||||
through the batch PR; verify milestones fire unchanged.
|
||||
|
||||
## Fallback matrix
|
||||
|
||||
| Failure | Action |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| path-overlap (exit 3) | split batch along overlap pairs / 1:1 for the odd member |
|
||||
| merge conflict (exit 4) | 1:1 for the whole batch |
|
||||
| local pre-validation red | fix on the offending member, re-compose |
|
||||
| batch CI red after mergeable | coordinator dequeues with trail; fix on member branches, re-compose as a new batch PR |
|
||||
|
||||
Any fallback preserves today's behavior — batch landing is an optimization,
|
||||
never a dependency.
|
||||
|
||||
## Measurement (pilot duty — revised per [org-internal #3731] charter addendum 39347)
|
||||
|
||||
Per-iteration attribution only; the global 7-day ratio is trend context,
|
||||
never a pass/fail judge (same-window confounders: test-slim [org-internal #3855] /
|
||||
test-dedupe [org-internal #3854] / the pilot's own PRs). On the server,
|
||||
`bun .gitea/scripts/ci-run-stats.ts --days 7 --json /tmp/<slug>-ci-stats.json`
|
||||
(the `--json` flag takes a filename — bare, it is a silent no-op; the human
|
||||
summary still prints). Same methodology as the checklist-merge-gate.yml
|
||||
2026-08-27 note. Record on [org-internal #3731]: (a) this batch iteration's per-workflow
|
||||
trigger counts vs a historically similar 1:1 iteration, and (b) one
|
||||
fallback drill (construct a path-overlap group → exit-3 split executed).
|
||||
Baseline, trend only: 2026-08-31, 5,081 runs / TOTAL 2.75x single runner.
|
||||
@@ -0,0 +1,132 @@
|
||||
---
|
||||
name: project-kickoff
|
||||
description: >
|
||||
Use ONLY when the user asks about project setup, kickoff, or project
|
||||
initialization ("kickoff", "project setup", "椤圭洰鍒濆鍖?, "椤圭洰閰嶇疆").
|
||||
Provides guidance on the octopus project kickoff workflow: checking readiness,
|
||||
running /kickoff or octopus kickoff, configuring codegraph, and understanding
|
||||
project scaffolding. Do not use for application-level configuration 鈥?
|
||||
this skill covers the octopus project lifecycle, not user application code.
|
||||
triggers:
|
||||
# High-discrimination phrases only ([org-internal #4525]): bare "setup"/"init"/"閰嶇疆" fire
|
||||
# on any configuration mention, and the skill gate folds the previous
|
||||
# assistant turn on approval turns 鈥?one stray "閰嶇疆" in a reply re-fires
|
||||
# the gate every turn. Keep triggers to phrases that name THIS workflow;
|
||||
# generic setup/init questions still route via the description in the
|
||||
# skills listing.
|
||||
- kickoff
|
||||
- project setup
|
||||
- 椤圭洰鍒濆鍖?
|
||||
- 椤圭洰閰嶇疆
|
||||
role: Producer
|
||||
---
|
||||
|
||||
> Core 涓珛鐗堬紙Increment 6a 鏀瑰啓锛屽師 deferHard verbatimDir锛夈€傛満鍒躲€佺粨鏋勪笌 frontmatter 淇濇寔锛涘疄渚嬫湳璇紙宸ュ叿鍚嶃€佽矾寰勩€佸伐鍗曞彿锛夋寜 `core/adapters/TERMINOLOGY.md` 缁戝畾鍒板叿浣撳疄渚嬨€?
|
||||
# Project Kickoff
|
||||
|
||||
## 1. Overview
|
||||
|
||||
Project kickoff is the octopus project initialization workflow that gets a project from zero to fully configured in one command. It covers readiness detection 鈫?setup 鈫?scaffold 鈫?config 鈫?codegraph init 鈫?skills overlay pull, producing a project that is ready for the full SDLC pipeline without manual configuration steps.
|
||||
|
||||
## 2. When This Skill Activates
|
||||
|
||||
This skill activates when the user's text matches any of the trigger keywords:
|
||||
|
||||
- `kickoff` 鈥?asking about the `/kickoff` command or the kickoff workflow
|
||||
- `project setup` 鈥?asking how to set up a project in octopus
|
||||
- `椤圭洰鍒濆鍖朻 / `椤圭洰閰嶇疆` 鈥?asking how to initialize or configure an octopus project (Chinese)
|
||||
|
||||
Generic "setup"/"init"/"閰嶇疆" mentions are deliberately NOT triggers ([org-internal #4525]): they collide with everyday configuration talk, and the hard skill gate folds the previous assistant turn into its match text on approval turns 鈥?a passing "鎴栭厤缃?x: false" in a reply would re-fire the gate every turn. `/init` note: `/init` generates AGENTS.md via LLM; kickoff uses the managed template from `octopus setup` 鈥?the Builder distinguishes based on context.
|
||||
|
||||
Typical user intents include: encountering a readiness prompt at session start, wondering how to initialize a project, or wanting to understand the kickoff workflow steps.
|
||||
|
||||
## 3. Prerequisites
|
||||
|
||||
Before kickoff can run:
|
||||
|
||||
- `codegraph` CLI must be installed and on `$PATH` (covered by the `codegraph-setup` skill)
|
||||
- Global octopus config (`<config-home>/octopus/octopus.jsonc`) must be merged and valid
|
||||
- The project root must be identified (the directory octopus is running in)
|
||||
|
||||
### CI Readiness
|
||||
|
||||
After kickoff completes, check for CI configuration:
|
||||
|
||||
```bash
|
||||
ls .gitea/workflows/ .github/workflows/ 2>/dev/null || echo "[GAP: no CI workflows]"
|
||||
```
|
||||
|
||||
If no workflows exist, recommend creating `.gitea/workflows/ci.yml` (this repo
|
||||
uses Gitea Actions) 鈥?or `.github/workflows/ci.yml` for a GitHub-hosted project
|
||||
鈥?with at minimum: typecheck, lint, and test gates. Without CI, every merge is a
|
||||
manual gate 鈥?type errors, lint regressions, and test failures can land on
|
||||
main and go undetected until the next local checkout.
|
||||
|
||||
## 4. Workflow
|
||||
|
||||
The kickoff workflow runs in four automated steps:
|
||||
|
||||
1. **Check readiness** 鈥?Determine if the project needs setup. Run `checkReadinessWithGitea()` or `octopus kickoff --check-only` to inspect the current state.
|
||||
2. **Run kickoff** 鈥?Execute `/kickoff` from the TUI or `octopus kickoff` from the terminal.
|
||||
3. **What happens** 鈥?A Phase 0 gate, then five sequential phases:
|
||||
- Phase 0 鈥?**Gitea auth check**: derives the `gitea` block from the git remote origin (persisting missing fields), then verifies the token (`GET /user`) and runs the auth wizard on failure; never aborts kickoff
|
||||
- [1/5] **Setup** 鈥?Creates project directories and writes `AGENTS.md` from the managed template
|
||||
- [2/5] **Scaffold** 鈥?Creates the `<instance-root>/` skeleton (`core/skills/` directory + default `octopus.jsonc` template if absent); domain skills content arrives via the [5/5] overlay pull
|
||||
- [3/5] **Config stub + MCP wiring** 鈥?Writes the `mcp.codegraph` block into `octopus.jsonc`
|
||||
- [4/5] **Codegraph init** 鈥?Initializes and builds the per-project `.codegraph/` index
|
||||
- [5/5] **Skills remote + overlay pull** 鈥?Opt-in semantics ([org-internal #4370]); branches by flags when no `overlay.remotes` entry is configured:
|
||||
- no flags (interactive run) 鈥?**safe default**: nothing is written; a remediation hint is printed (pass `--skills-remote`, or edit `overlay.remotes`), and the readiness skills item stays not-ready
|
||||
- `--yes` 鈥?legacy behavior kept: writes the default skills remote, then pulls domain skills content (`octopus skills pull` equivalent)
|
||||
- `--skills-remote <owner/repo>` (+ optional `--skills-remote-base-url <url>`) 鈥?writes the specified remote instead (ref `main`; base URL defaults to `<instance-base-url>`), then pulls from it
|
||||
- `overlay.remotes` already configured 鈥?the write is skipped (idempotent; an explicit `--skills-remote` is ignored with a warning)
|
||||
4. **Post-kickoff verification** 鈥?Run `octopus kickoff --check-only` again. After a `--yes` or `--skills-remote` run it reports all 5 items as ready; after a no-flag interactive run it reports 4 of 5 鈥?the skills item stays not-ready until a remote is configured (`--skills-remote`, `--yes`, or a manual `overlay.remotes` edit + `octopus skills pull`).
|
||||
|
||||
### First-ticket hint ([org-internal #3061])
|
||||
|
||||
For a brand-new project still validating its concept, open the FIRST ticket as
|
||||
`Kind/MVP` 鈥?the interactive no-pipeline mode (decision log + debt register in
|
||||
the ticket body). It graduates by relabeling to `Kind/Feature` (analyze-dag
|
||||
backfill mode picks up the decision log) when a second session, an external
|
||||
contract, or a second consumer of a shared type appears. Do not route early
|
||||
concept work into the full pipeline "to be safe" 鈥?the pipeline's coordination
|
||||
machinery is cost, not quality, at zero coordination.
|
||||
|
||||
## 5. Re-kickoff / Idempotency
|
||||
|
||||
Re-running kickoff on an already-configured project is safe 鈥?every step is idempotent:
|
||||
|
||||
- **Setup** skips existing directories
|
||||
- **Scaffold** skips existing files
|
||||
- **Config mutation** is a no-op if the `mcp.codegraph` block already matches
|
||||
- **Codegraph** re-indexes (rebuilds the graph, which is a safe refresh)
|
||||
- **Skills remote** ([5/5]) skips the write when `overlay.remotes` is already configured
|
||||
|
||||
**Idempotent silence** (REQ-F-005): A fully configured project produces zero startup prompts. Users can run `/kickoff` or `octopus kickoff --check-only` at any time for explicit readiness checks without side effects.
|
||||
|
||||
## 6. Troubleshooting
|
||||
|
||||
Common issues and their resolutions:
|
||||
|
||||
- **`codegraph: command not found`** 鈥?Run the `codegraph-setup` skill, or install via `bun add -g @colbymchenry/codegraph`
|
||||
- **Permission denied** 鈥?Check project directory ownership and write permissions
|
||||
- **Partially complete state** 鈥?Re-run `/kickoff`; idempotent steps will complete the missing parts without affecting already-completed steps
|
||||
- **MCP tools not available** 鈥?Verify `mcp.codegraph` exists in `octopus.jsonc`; re-run `octopus kickoff` to write it
|
||||
|
||||
## 7. Related Commands
|
||||
|
||||
- `/init` 鈥?Generates `AGENTS.md` using an LLM (custom, context-aware); kickoff uses the managed template from `octopus setup`
|
||||
- `octopus setup` 鈥?Creates directories + `AGENTS.md` from the managed template; kickoff calls this internally
|
||||
- `octopus scaffold` 鈥?Creates the `<instance-root>/` skeleton (`core/skills/` directory + default `octopus.jsonc` template if absent); run `octopus skills pull` to fetch domain skills content; kickoff calls this internally
|
||||
- `octopus kickoff --check-only` 鈥?Readiness check only, no pipeline execution
|
||||
- `octopus kickoff --skills-remote <owner/repo>` 鈥?Explicit opt-in: writes the given skills remote to `overlay.remotes` (ref `main`) and pulls it instead of the default
|
||||
- `octopus kickoff --skills-remote-base-url <url>` 鈥?Base URL for `--skills-remote` (requires it); defaults to `<instance-base-url>` when omitted
|
||||
- `octopus kickoff --yes` 鈥?Skips the opt-in confirmation and keeps the legacy behavior of writing the default skills remote (`Octopus/octopus-skills`)
|
||||
- `codegraph-setup` skill 鈥?For installing the codegraph CLI and initializing the per-project index
|
||||
|
||||
## 8. Reference
|
||||
|
||||
- `<harness-package>/src/project-kickoff/readiness.ts` 鈥?`checkReadiness()` implementation (sync 4-item base)
|
||||
- `<harness-package>/src/project-kickoff/readiness-gitea.ts` 鈥?`checkReadinessWithGitea()` composed 5-item check (adds gitea auth reachable)
|
||||
- `<harness-package>/src/cli/cmd/kickoff.ts` 鈥?`/kickoff` command handler
|
||||
- `core/skills/codegraph-setup/SKILL.md` 鈥?CodeGraph setup skill
|
||||
- Project roadmap at `.artifacts/project-kickoff/` (deprecated 鈥?now stored as wiki page `project-kickoff/roadmap/` via `wiki 璇诲啓 API锛堣 TERMINOLOGY锛塦)
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
name: prototype
|
||||
description: >
|
||||
Use ONLY when the user asks to build a prototype, proof-of-concept, spike, or
|
||||
exploratory implementation where the code's ultimate fate (keep vs discard)
|
||||
is not yet decided by the standard pipeline. The Developer (Worker) first
|
||||
classifies the work as THROWAWAY (spike) or EVOLUTIONARY (high-fidelity
|
||||
prototype that will be promoted to production), then applies the matching
|
||||
disposition contract. Do NOT use for standard feature work (use implement),
|
||||
bug fixes (use implement, Mode: bugfix), or refactoring (use implement, Mode: refactor).
|
||||
triggers:
|
||||
- prototype
|
||||
- spike
|
||||
- proof of concept
|
||||
- poc
|
||||
- 原型
|
||||
- 高保真原型
|
||||
- 演进式原型
|
||||
- 技术探索
|
||||
- 技术验证
|
||||
- throwaway
|
||||
- evolutionary prototype
|
||||
role: Producer
|
||||
---
|
||||
|
||||
# Prototype / Spike
|
||||
|
||||
Classify the work BEFORE writing code, then apply the disposition contract.
|
||||
The classification is a one-line decision recorded in the artifact header.
|
||||
|
||||
## Phase 0 — Disposition Decision (MANDATORY)
|
||||
|
||||
Answer this question in one sentence, citing the user's words as evidence:
|
||||
|
||||
> **Will this exact code be promoted to production (EVOLUTIONARY), or will it
|
||||
> be discarded after we learn from it (THROWAWAY)?**
|
||||
|
||||
| Signal from user | Disposition |
|
||||
|---|---|
|
||||
| "spike", "validate", "prove it works", "throwaway", "探索", "验证" | THROWAWAY |
|
||||
| "prototype then keep iterating", "evolve", "no rewrite", "MVP from this", "演进", "不重写" | EVOLUTIONARY |
|
||||
| Conditional evolution: "if it works, we'll keep iterating", "if users like it, we'll build on it" | **ASK the user**: "Do you intend to evolve this exact codebase (EVOLUTIONARY), or rewrite cleanly if validated (THROWAWAY)?" |
|
||||
| Ambiguous ("let's prototype and see") | **ASK the user.** Do not guess. |
|
||||
|
||||
Record the decision in the artifact you produce:
|
||||
|
||||
```
|
||||
@disposition: THROWAWAY | EVOLUTIONARY
|
||||
@evidence: "<the user's exact phrase that decided it>"
|
||||
```
|
||||
|
||||
If you cannot cite the user's exact phrase, you have not decided — ask.
|
||||
|
||||
---
|
||||
|
||||
## THROWAWAY contract (spike)
|
||||
|
||||
Goal = **learning**, not code. Code is a means to a decision.
|
||||
|
||||
- **Time-box**: state a deadline (hours/days) up front. Stop when the box ends, even mid-sentence.
|
||||
- **Artifacts**:
|
||||
1. Throwaway code (no tests, no lint, no polish — label `@ai-artifact: spike`)
|
||||
2. **Learning report** (wiki page `{slug}/spike-report`): what was validated, what failed, decision (go / pivot / stop)
|
||||
- **Fencing**: spike code MUST NOT be importable from production code. Put it in `spike/` or a scratch worktree; delete it after the report is accepted.
|
||||
- **Exit gate**: the learning report is the deliverable. Code without a report = incomplete.
|
||||
- **Terminal artifact**: wiki page `{slug}/spike-report` is consumed by the human decision-maker (go/pivot/stop), not by a downstream skill. This is an intentional pipeline terminal, analogous to `release` and `retrospective` outputs.
|
||||
|
||||
## EVOLUTIONARY contract (high-fidelity prototype)
|
||||
|
||||
Goal = **seed code that becomes production**. Code is the product.
|
||||
|
||||
- **Debt register** (wiki page `{slug}/prototype-debt`): every shortcut is a line item with an owner and a promotion criterion. Example:
|
||||
| Shortcut | Why | Promotion criterion |
|
||||
|---|---|---|
|
||||
| Hardcoded auth token | unblock UI flow | Replace with real auth before any external user |
|
||||
| No tests on prototype UI | speed | Add source-verification tests before merging to main |
|
||||
- **Quality floors that apply from day 1**: typecheck passes, no `any` without justification, no `console.log` left in committed code.
|
||||
- **Quality floors explicitly deferred** (and tracked in the debt register): full test coverage, error states, observability, performance budgets.
|
||||
- **Promotion gate**: the prototype is "production" only when the debt register is empty OR each remaining item has an explicit waiver from the reviewer. Route through `review-code` with `mode: "prototype-promotion"` before promotion; the debt register is a required review input in that mode. Waived items become `ACCEPTED_RISK` findings in the review report, which `verify` Phase 5.5 registers as `## TD 登记` rows — promoted to `tech-debt` labeled Gitea issues on claim (`.octopus/rules/ticket-lifecycle.md`); the register lives in the issue tracker, not a parallel file.
|
||||
|
||||
## Rationalization table
|
||||
|
||||
| Excuse | Reality |
|
||||
|---|---|
|
||||
| "The user said 'no rewrite', so I'll just skip design forever" | EVOLUTIONARY defers design DETAILS, not design ITSELF. The debt register IS the design surface; promotion requires review-code. |
|
||||
| "This is just a spike, so no need to record anything" | The learning report IS the spike's deliverable. No report = no learning = wasted spike. |
|
||||
| "The prototype works, let's ship it and clean up later" | "Later" never comes. The debt register is the cleanup plan; promote only when it's empty or waived. |
|
||||
| "I'll decide throwaway vs evolutionary after I see how it goes" | Deciding after = throwaway by default (no debt register, no promotion path). If it might evolve, declare EVOLUTIONARY up front. |
|
||||
| "'If it works, we'll keep iterating' — so EVOLUTIONARY" | "If it works" is conditional. The user has NOT committed to evolving this exact codebase. ASK: evolve-this-code or validate-then-rewrite? |
|
||||
|
||||
## Anti-patterns (do not do these)
|
||||
|
||||
- ❌ Building a "prototype" with full production quality "just in case" — that's not a prototype, that's slow feature work.
|
||||
- ❌ Building a "spike" then quietly merging it because it happened to work — that's evolutionary without the debt register.
|
||||
- ❌ Freezing an evolutionary prototype and rewriting it in `src/` — that's a throwaway; if the user said "no rewrite", honor it.
|
||||
|
||||
## References
|
||||
|
||||
- `.octopus/checklists/prototype.md` — Prototype self-check checklist
|
||||
@@ -0,0 +1,447 @@
|
||||
---
|
||||
name: release
|
||||
description: >
|
||||
Use ONLY when ready to cut a release. The Release Manager (Worker) inspects
|
||||
project state (git log, version files, build) to determine the semver bump,
|
||||
generate the changelog, tag, and produce a release artifact. Works on any
|
||||
project state 鈥?no pipeline artifacts required.
|
||||
triggers:
|
||||
- release
|
||||
- cut release
|
||||
- 鍙戠増
|
||||
- publish release
|
||||
- ship release
|
||||
role: Producer
|
||||
---
|
||||
|
||||
> Core 涓珛鐗堬紙Increment 6a 鏀瑰啓锛屽師 deferHard verbatimDir锛夈€傛満鍒躲€佺粨鏋勪笌 frontmatter 淇濇寔锛涘疄渚嬫湳璇紙宸ュ叿鍚嶃€佽矾寰勩€佸伐鍗曞彿锛夋寜 `core/adapters/TERMINOLOGY.md` 缁戝畾鍒板叿浣撳疄渚嬨€?
|
||||
# Release
|
||||
|
||||
Prepare and tag a release based on project facts 鈥?git history since the last
|
||||
tag, current version, build status, and dependency health. No dependency on `.artifacts/{slug}/`
|
||||
(**deprecated** 鈥?SDLC artifacts now live in the Gitea wiki at `{slug}/...`;
|
||||
see `_shared/gitea-read-patterns.md`) or any pipeline skill outputs.
|
||||
|
||||
**This SKILL does not deploy.** Deployment is project-specific (K8s, Docker,
|
||||
npm publish, etc.) and varies too widely to template. The release artifact
|
||||
produced here is the input to project-specific deployment workflows.
|
||||
|
||||
## Agent Role
|
||||
|
||||
The release is owned and executed by the **Release Manager** (Worker).
|
||||
|
||||
**Context compaction**: release is a pipeline stage boundary. The main session
|
||||
compacts at this clean boundary ONLY when a capacity/projection trigger holds,
|
||||
per `core/rules/compact.md` 搂"Stage-boundary compaction" (long multi-stage
|
||||
runs 鈥?DAG Epic orchestration 鈥?keep the legacy every-boundary compaction;
|
||||
short runs 鈥?bugfix / DAG task 鈥?and standalone runs default to NOT
|
||||
compacting). The Release Manager itself is single-phase and
|
||||
artifact-driven (version files, changelog, tag); a mid-run compaction loses
|
||||
nothing 鈥?re-read the release checklist state and git log to resume.
|
||||
|
||||
---
|
||||
|
||||
## Preconditions
|
||||
|
||||
Before starting the release:
|
||||
|
||||
- [ ] Working tree is clean (`git status` has no uncommitted changes).
|
||||
- [ ] `core/checklists/release.md` is accessible.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 鈥?Pre-release Gate
|
||||
|
||||
Run each check against the live project. Stop and report failures.
|
||||
|
||||
1. **Clean workspace**: `git status` must show nothing to commit.
|
||||
2. **Branch**: confirm the current branch. Default assumption: release from
|
||||
`main` or `master`. If on another branch, note it.
|
||||
3. **Build**: run the project's build command. Must pass.
|
||||
4. **Typecheck + Lint**: run the project's typecheck and lint. Must pass.
|
||||
5. **Tests**: run the project's test suite. Must pass.
|
||||
6. **Dependency audit**: run the project's vulnerability scanner
|
||||
(e.g. `npm audit`, `bun audit`, `pip-audit`, `cargo audit`).
|
||||
- No new HIGH or CRITICAL CVEs block the release.
|
||||
- Pre-existing HIGH/CRITICAL CVEs do NOT block but **MUST be filed as
|
||||
tech-debt** before the release lands. File them in TWO TIERS (batching
|
||||
rule, [org-internal #3846] 鈥?a single release audit once fanned out to 14 TDs 鈫?14 PRs 鈫?
|
||||
56-84 CI runs on a saturated runner pool): Tier A below is the general
|
||||
mechanical-change batching rule; Tier B and the escape hatch follow it.
|
||||
|
||||
<a id="phase-1-tier-a"></a>
|
||||
### Tier A 鈥?閫氱敤鏈烘鍙樻洿骞跺崟瑙勫垯 / Universal mechanical-change batching rule
|
||||
|
||||
Origin [org-internal #3846] (audit-batch pilot, generalized in place): the rule covers ANY
|
||||
single-source fan-out of mechanical changes, not just dependency audits.
|
||||
Two source types today: (a) dependency bump audits; (b) docs 鎵归噺淇
|
||||
(batch docs revisions).
|
||||
|
||||
#### 瑙勫垯姝f枃 / Rule text
|
||||
|
||||
- **Core**: mechanical changes of ONE class fanning out from a single
|
||||
source are filed as ONE batch issue + ONE batch PR 鈥?never N脳issue +
|
||||
N脳PR. The batch body keeps one row per item (per-CVE / per-doc mapping),
|
||||
so per-item traceability is unchanged; work the batch as a single PR.
|
||||
- **(a) Dependency bump audits** (the [org-internal #3846] origin case): advisories whose
|
||||
remediation is a plain version bump (lockfile-only diff, no semver-major
|
||||
jump, no overrides/catalog surgery, no API or adapter fallout) are filed
|
||||
together via `宸ュ崟 API锛堣 TERMINOLOGY锛塩reate` with
|
||||
`title="[{origin}] audit-batch@{version}: mechanical bumps for {N} advisories (TD-{NNN})"`
|
||||
and labels `[tech_debt_label_id, severity_label_id, kind_bug_label_id]`
|
||||
(severity = highest in the batch). One row per advisory 鈥?CVE/GHSA ID,
|
||||
affected package + from鈫抰o version, severity (CVSS), advisory URL,
|
||||
recommended remediation, and a per-advisory Reactivation Trigger
|
||||
("resolved when `npm audit` / `bun audit` reports no HIGH/CRITICAL for
|
||||
this advisory") 鈥?so the release-notes `TD-NNN 鈫?#NNNN` mapping stays
|
||||
per-advisory.
|
||||
- **(b) Docs 鎵归噺淇 (batch docs revisions)**: many small same-class docs
|
||||
corrections discovered in one pass (e.g. a terminology sweep) file as ONE
|
||||
issue via the same flow,
|
||||
`title="[{origin}] docs-batch: {class} revisions for {N} files"`, with one
|
||||
row per file (path, correction, reason); same ONE-batch-PR landing.
|
||||
- **Landing**: when members live on separate branches, compose the single
|
||||
batch PR via the `land-batch` skill (`core/skills/land-batch/SKILL.md`
|
||||
鈥?cross-branch batch composition, topology B). For dependency-bump
|
||||
batches pass `batch-compose --convergent bun.lock` (script
|
||||
`.gitea/scripts/batch-compose.ts`): the regenerable lockfile is exempt
|
||||
from path-overlap admission and lockfile-only conflicts are surgically
|
||||
resolved to the running head's version.
|
||||
|
||||
#### 閫傜敤杈圭晫 / Applicability boundary
|
||||
|
||||
- **闈?mechanical锛堝惈鍒ゆ柇鎴愬垎鐨勫彉鏇达紝涓嶅苟鍗曪級** 鈥?items requiring judgment
|
||||
(wording decisions, behavior/API changes, review-dependent edits) are
|
||||
never batched; file each as its own issue.
|
||||
- **璺緞閲嶅彔锛坆atch 鎴愬憳瑙︾鐩稿悓鏂囦欢 鈫?涓嶅苟鍗曪紱鍞竴璞佸厤 =
|
||||
鍙啀鐢熷叡浜枃浠?`bun.lock` 缁?batch-compose `--convergent` 鏀舵暃锛?* 鈥?the sole
|
||||
path-overlap exemption is the regenerable shared lockfile under
|
||||
`--convergent`; every other same-file collision stays un-batched.
|
||||
- **semver-major 璺冲彉 / overrides路catalog 鎵嬫湳 / adapter路peer 鑱斿姩锛堚啋
|
||||
Tier B 鐙珛鍗曪紝缁存寔 per-package锛?* 鈥?these stay per-package Tier B
|
||||
issues. Tier B and the escape hatch (below) survive this generalization
|
||||
unchanged.
|
||||
|
||||
#### 闄嶇骇璺緞 / Degradation path
|
||||
|
||||
- **骞跺崟 PR CI 澶辫触 鈫?鎸夐攣鏂囦欢 hunk 鎷嗗寘鍥為€€锛坆isect锛?*: for dependency
|
||||
batches, bisect by splitting lockfile hunks per package back into
|
||||
per-package PRs 鈥?the original Tier A bisect semantics carried over
|
||||
verbatim (鍚岃涔夛紝娉涘寲鎺緸: every mechanical batch degrades the same way).
|
||||
- **Docs batches**: split per file back into per-file PRs.
|
||||
- **land-batch composition fallback** (exit codes per
|
||||
`.gitea/scripts/batch-compose.ts`): `3` path-overlap 鈫?split the batch
|
||||
along the reported pairs (or land the overlapping member 1:1) and re-run
|
||||
per group; `4` merge conflict / `5` transport/git error 鈫?fall back to
|
||||
1:1 PRs for the whole batch.
|
||||
- **Preflight hedge ([org-internal #3846])**: run `bun install --dry-run` before opening
|
||||
a dependency-batch PR 鈥?the resolver accepting the composed version set
|
||||
is a cheap pre-CI rejection of impossible bump combinations.
|
||||
|
||||
Tier B and the escape hatch (unchanged by the generalization):
|
||||
|
||||
- **Tier B 鈥?surgery, one issue per package**: semver-major jumps,
|
||||
adapter/peer fallout, or overrides/catalog surgery keep the
|
||||
per-package issue: `title="[{origin}] {CVE-ID/GHSA-ID} in {package} (TD-{NNN})"`
|
||||
with the same labels; body requirements match the Tier A rows.
|
||||
- Escape hatch: either tier may carve a single advisory into its own
|
||||
issue when same-day remediation is required (urgent HIGH/CRITICAL).
|
||||
|
||||
De-duplicate against open `tech-debt` issues (match by
|
||||
CVE/GHSA ID) before creating. Record the `TD-NNN 鈫?#NNNN` mapping in the
|
||||
release notes. See `verify` Phase 5.5 for the tech-debt promotion body
|
||||
template and `_shared/gitea-write-patterns.md` Pattern 3.
|
||||
|
||||
```markdown
|
||||
## Pre-release Gate
|
||||
|
||||
| Check | Status |
|
||||
| ----------------- | ----------------------------------------------------- |
|
||||
| Clean workspace | 鉁?/ 鉂? |
|
||||
| Branch | {branch name} |
|
||||
| Build | 鉁?/ 鉂? |
|
||||
| Typecheck + Lint | 鉁?/ 鉂? |
|
||||
| Tests | 鉁?/ 鉂?(N passed, M failed) |
|
||||
| Dependency audit | 鉁?/ 鈿狅笍 N known CVEs (pre-existing) / 鉂?N new CVEs |
|
||||
```
|
||||
|
||||
If any gate fails except known CVEs, stop and report what failed.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 鈥?Version Bump
|
||||
|
||||
Determine the new version by inspecting git history since the last tag.
|
||||
|
||||
1. **Find last tag**: `git describe --tags --abbrev=0` (or `git tag --sort=-v:refname | head -1`).
|
||||
- **No previous tag?** This is the first tracked release. Use the initial commit as
|
||||
baseline: `git rev-list --max-parents=0 HEAD`. After tagging this release, create
|
||||
a retroactive baseline tag (`v{base-version}`) on the initial commit so future
|
||||
cycles have a clean `{tag}..{tag}` range. Document the gap in the release report.
|
||||
2. **Read commits since last tag**: `git log <last-tag>..HEAD --oneline`.
|
||||
3. **Read current version** from the project's version file (`package.json`
|
||||
`version`, `Cargo.toml`, `VERSION`, etc.).
|
||||
4. **Categorize commits** by change type:
|
||||
|
||||
| Conventional prefix | Semver | Examples |
|
||||
| -------------------- | ------- | ----------------------------- |
|
||||
| `BREAKING CHANGE:` / `!:` | MAJOR | API removal, schema change |
|
||||
| `feat:` | MINOR | New feature, new endpoint |
|
||||
| `fix:` | PATCH | Bug fix only |
|
||||
| `perf:` | PATCH | Performance improvement |
|
||||
| `refactor:` | PATCH | Internal restructuring |
|
||||
| `docs:` / `chore:` | (skip) | Not user-visible |
|
||||
|
||||
If no conventional prefix found, infer from the subject line:
|
||||
- "add", "implement", "introduce" 鈫?MINOR
|
||||
- "fix", "resolve", "correct" 鈫?PATCH
|
||||
- "remove", "drop", "rename" (public API) 鈫?MAJOR
|
||||
|
||||
5. **Compute bump**:
|
||||
- If any MAJOR commit 鈫?bump MAJOR.
|
||||
- Else if any MINOR commit 鈫?bump MINOR.
|
||||
- Else 鈫?bump PATCH.
|
||||
|
||||
```markdown
|
||||
## Version Bump
|
||||
|
||||
**Last tag**: {tag}
|
||||
**Current version**: {old version}
|
||||
**New version**: {new version}
|
||||
**Type**: MAJOR / MINOR / PATCH
|
||||
**Commits since last tag**: {N}
|
||||
**Reason**: {justification 鈥?e.g. "2 feat + 3 fix 鈫?MINOR"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 鈥?Changelog
|
||||
|
||||
Generate the changelog from `git log <last-tag>..HEAD`:
|
||||
|
||||
1. **List all commits**. For each, extract:
|
||||
- Type (from prefix or inferred).
|
||||
- Scope (if present, e.g. `feat(auth):`).
|
||||
- Description (the subject line, past tense, human-readable).
|
||||
2. **Group by type**:
|
||||
- **Added** 鈥?`feat:` commits.
|
||||
- **Changed** 鈥?modifications to existing behavior (non-breaking).
|
||||
- **Fixed** 鈥?`fix:` commits.
|
||||
- **Breaking** 鈥?`BREAKING CHANGE:` or `!:` commits.
|
||||
3. **Deduplicate**: squash multiple commits for the same change into one entry
|
||||
where it makes narrative sense.
|
||||
4. Read the existing root `CHANGELOG.md` (if any) and prepend this release.
|
||||
|
||||
Write to root `CHANGELOG.md` (prepend section).
|
||||
|
||||
```markdown
|
||||
## [{version}] 鈥?{YYYY-MM-DD}
|
||||
|
||||
### Added
|
||||
|
||||
- {feature} ({commit hash short})
|
||||
|
||||
### Changed
|
||||
|
||||
- {change} ({hash})
|
||||
|
||||
### Fixed
|
||||
|
||||
- {bugfix} ({hash})
|
||||
|
||||
### Breaking
|
||||
|
||||
- {breaking change} ({hash}) 鈥?see migration notes above
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 鈥?Tag & Finalize
|
||||
|
||||
1. **Update version file** 鈥?write the new version to the project's version
|
||||
manifest.
|
||||
2. **Commit**: `git add` version file + changelog file, commit with message:
|
||||
```
|
||||
release: {version}
|
||||
```
|
||||
3. **Tag**: `git tag v{version}` (adjust prefix per project convention 鈥?
|
||||
check existing tags with `git tag -l`).
|
||||
4. **Verify tag**: `git tag -l v{version}` confirms the tag exists.
|
||||
|
||||
**Do not push** 鈥?the user must explicitly request pushing to remote.
|
||||
|
||||
```markdown
|
||||
## Release Artifact
|
||||
|
||||
- **Version**: {version}
|
||||
- **Commit**: {commit hash}
|
||||
- **Tag**: v{version}
|
||||
- **Changelog**: CHANGELOG.md updated
|
||||
|
||||
### Files Changed
|
||||
|
||||
| File | Change |
|
||||
| -------------- | ------------------------- |
|
||||
| {version file} | {old} 鈫?{new} |
|
||||
| CHANGELOG.md | Prepended {version} |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4b 鈥?Publish Release Artifacts (octopus project, manual)
|
||||
|
||||
> Octopus-specific. Other projects: substitute your own artifact pipeline 鈥?
|
||||
> the goal is identical (turn the tag into downloadable assets).
|
||||
|
||||
The CI publish pipeline (`.gitea/workflows/publish.yml`) was **retired by
|
||||
[org-internal #2003]** (138/138 historical runs cancelled; the pipeline sat unused for 35+
|
||||
days). Releasing octopus is now a **manual local process**. All former CI
|
||||
steps live in repo scripts, runnable from a maintainer machine with the right
|
||||
credentials in the environment.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Clean checkout of the release commit (tag pushed or about to be pushed).
|
||||
- Credentials in env:
|
||||
- `GITEA_TOKEN` 鈥?PAT with `write:repository` (release create/undraft,
|
||||
tag push) and `write:package` (container registry). The `CI_PAT` secret
|
||||
value is the canonical token.
|
||||
- `NODE_AUTH_TOKEN` 鈥?only if publishing to npm.
|
||||
- `AUR_KEY` 鈥?only if pushing the AUR package.
|
||||
- Docker logged in to the Gitea container registry:
|
||||
`echo "$GITEA_TOKEN" | docker login <instance-registry-host> -u <user> --password-stdin`.
|
||||
|
||||
### Steps
|
||||
|
||||
1. **Version + draft release** (idempotent 鈥?skips if already published):
|
||||
```bash
|
||||
GITEA_TOKEN=<pat> GH_REPO=Octopus/octopus bun script/version.ts
|
||||
```
|
||||
Creates (or refines) the draft Gitea release for `v{version}`.
|
||||
|
||||
2. **Build the 12 platform binaries + archives** (longest step):
|
||||
```bash
|
||||
OCTOPUS_VERSION={version} OCTOPUS_RELEASE={release-id} \
|
||||
GH_REPO=Octopus/octopus GITEA_TOKEN=<pat> \
|
||||
bun <harness-package>/script/build.ts
|
||||
```
|
||||
`build.ts` attaches the 12 release assets to the draft release.
|
||||
|
||||
3. **Publish npm / docker / AUR + git sync**:
|
||||
```bash
|
||||
OCTOPUS_VERSION={version} OCTOPUS_RELEASE={release-id} \
|
||||
GITEA_TOKEN=<pat> GH_REPO=Octopus/octopus \
|
||||
NODE_AUTH_TOKEN=<npm-token> \
|
||||
bun script/publish.ts
|
||||
```
|
||||
Set `OCTOPUS_GITEA_ONLY=true` to skip npm/docker/AUR/homebrew.
|
||||
|
||||
4. **Undraft the release** (makes it public):
|
||||
```bash
|
||||
curl -fsS -X PATCH \
|
||||
"<instance-base-url>/api/v1/repos/Octopus/octopus/releases/{release-id}" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" -d '{"draft": false}'
|
||||
```
|
||||
|
||||
5. **Verify assets** 鈥?the release must carry 12 assets:
|
||||
```bash
|
||||
curl -sS "<instance-base-url>/api/v1/repos/Octopus/octopus/releases/tags/v{version}" \
|
||||
-H "Authorization: token $GITEA_TOKEN" | jq '.assets | length'
|
||||
```
|
||||
|
||||
6. **Sync the public mirror repo** (`Octopus/octopus-release`):
|
||||
```bash
|
||||
CI_PAT=<pat> VERSION={version} bash script/sync-public-install.sh
|
||||
CI_PAT=<pat> VERSION={version} bash script/sync-public-assets.sh
|
||||
CI_PAT=<pat> VERSION={version} bash script/verify-public-sync.sh
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
- **Windows code-signing** is not provisioned (no Windows runner / Azure
|
||||
Trusted Signing); the CLI ships unsigned, as before ([org-internal #252]).
|
||||
- **Rollback**: if a step fails mid-release, the draft release + tag can be
|
||||
deleted and re-run; every script above is idempotent or safely re-runnable.
|
||||
- If a future CI pipeline replaces this manual flow, update this section and
|
||||
reference [org-internal #2003] for the retirement rationale.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 鈥?Rollback Plan
|
||||
|
||||
Document how to undo this release:
|
||||
|
||||
1. **Git rollback**:
|
||||
- `git tag -d v{version}` (if not yet pushed).
|
||||
- `git revert {commit_hash}` (if already merged).
|
||||
- If the release includes DB migrations, confirm the `down` migration exists.
|
||||
2. **Data rollback** (if applicable):
|
||||
- For each migration, confirm the `down` migration exists and has been tested.
|
||||
- If the release changes data format without a reversible migration, mark
|
||||
`鈿狅笍 IRREVERSIBLE DATA CHANGE`.
|
||||
|
||||
```markdown
|
||||
## Rollback Plan
|
||||
|
||||
### Git Rollback
|
||||
|
||||
git tag -d v{version}
|
||||
# or: git revert {hash}
|
||||
|
||||
### Data Rollback
|
||||
- Migration `{name}`: down {exists / NOT FOUND}
|
||||
- {additional risks}
|
||||
|
||||
### Rollback Triggers
|
||||
| Condition | Threshold | Duration |
|
||||
| ------------------ | ------------ | -------- |
|
||||
| P99 latency spike | 2x baseline | 5 min |
|
||||
| Error rate spike | 1% | 1 min |
|
||||
| Critical bug | Data loss / security breach | immediate |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 6 鈥?Post-release Smoke Test
|
||||
|
||||
1. Run the project's build command on the tagged commit. Must pass.
|
||||
2. Run the test suite. Must pass.
|
||||
3. Return to the branch: `git checkout <original-branch>`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 7 鈥?Report
|
||||
|
||||
```markdown
|
||||
## Release Report
|
||||
|
||||
**Version**: {old} 鈫?{new} (MAJOR / MINOR / PATCH)
|
||||
**Tag**: v{version}
|
||||
**Commits**: {N} since last tag ({feat} features, {fix} fixes)
|
||||
**Typecheck**: 鉁?| Lint: 鉁?| Tests: 鉁?
|
||||
|
||||
### Changelog
|
||||
|
||||
{paste changelog section}
|
||||
|
||||
### Release Artifacts
|
||||
|
||||
- Commit: {hash} 鈥?`release: {version}`
|
||||
- Tag: v{version}
|
||||
- Changelog: CHANGELOG.md updated
|
||||
|
||||
---
|
||||
|
||||
**Deploy**: {manual step 鈥?"merge to main triggers CI", etc.}
|
||||
**Rollback**: see Phase 5 above
|
||||
**Next**: `core/skills/retrospective/SKILL.md` (optional 鈥?run on any project state)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- `core/checklists/release.md` 鈥?Release checklist
|
||||
- Semver spec: https://semver.org
|
||||
- Conventional Commits: https://www.conventionalcommits.org
|
||||
@@ -0,0 +1,762 @@
|
||||
---
|
||||
name: retrospective
|
||||
description: >
|
||||
Use ONLY when running a retrospective at the end of any work cycle
|
||||
(release, feature, bugfix, port). The Retrospective Lead (Worker) inspects project
|
||||
facts — git log, file churn, test history, commit patterns — and generates
|
||||
actionable improvement items. No pipeline artifacts required.
|
||||
triggers:
|
||||
- retrospective
|
||||
- 复盘
|
||||
- lessons learned
|
||||
- post-mortem
|
||||
- postmortem
|
||||
role: Producer
|
||||
---
|
||||
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatimDir)。机制、结构与 frontmatter 保持;实例术语(工具名、路径、工单号)按 `core/adapters/TERMINOLOGY.md` 绑定到具体实例。
|
||||
|
||||
# Retrospective
|
||||
|
||||
Inspect the project's recent work cycle to extract patterns and generate
|
||||
concrete action items. Based purely on project facts: git history, code churn,
|
||||
commit quality, test results, and build health.
|
||||
|
||||
**Purpose**: Continuous improvement of BOTH the project AND the SDLC pipeline
|
||||
itself. Every retrospective may modify templates, checklists, or SKILLs.
|
||||
|
||||
## Agent Role
|
||||
|
||||
The retrospective is owned and executed by the **Retrospective Lead** (Worker).
|
||||
|
||||
**Context compaction**: retrospective is a pipeline stage boundary. The main
|
||||
session compacts at this clean boundary ONLY when a capacity/projection trigger
|
||||
holds, per `core/rules/compact.md` §"Stage-boundary compaction" (long multi-stage runs
|
||||
— DAG Epic orchestration — keep the legacy every-boundary compaction; short
|
||||
runs — bugfix / DAG task — and standalone runs default to NOT compacting). The Retrospective Lead itself is single-phase and
|
||||
data-driven: all findings are written to the retrospective report as they are
|
||||
produced, so a mid-run compaction loses nothing — re-read the report artifact
|
||||
to resume.
|
||||
|
||||
---
|
||||
|
||||
## Preconditions
|
||||
|
||||
Before starting the retrospective:
|
||||
|
||||
- [ ] A work cycle has been completed (release was cut, feature merged,
|
||||
bug fixed, or port landed).
|
||||
- [ ] The project is a git repository with recent commits.
|
||||
- [ ] `core/checklists/retrospective.md` is accessible.
|
||||
|
||||
No `.artifacts/{slug}/` files are required. (**Deprecated**: `.artifacts/` file-system access is being migrated to Gitea wiki. SDLC artifacts now live as wiki pages under `{slug}/`; use `wiki 读写 API(见 TERMINOLOGY)` and `wiki 读写 API(见 TERMINOLOGY)` to read them.) The retrospective reads the project's own state and git history.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — Scope the Cycle
|
||||
|
||||
Determine the time range to analyze:
|
||||
|
||||
1. If a **release was just done**: use `git log <last-tag>..<new-tag>`.
|
||||
2. If **no tag exists**: prompt the user for a time range or revision range
|
||||
(e.g. `HEAD~20..HEAD`, or `--since="last work cycle"`).
|
||||
3. If the user specifies a range, use that.
|
||||
|
||||
```markdown
|
||||
## Cycle Scope
|
||||
|
||||
**Range**: {commit range or "last work cycle"}
|
||||
**Date**: {start} → {end}
|
||||
**Commits analyzed**: {N}
|
||||
**Authors**: {names}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — Collect Data
|
||||
|
||||
Run project-inspection commands and summarize findings:
|
||||
|
||||
### 2.1 Commit patterns
|
||||
|
||||
```bash
|
||||
git log {range} --oneline --no-merges
|
||||
git log {range} --format='%s' | grep -cE '^(BREAKING|feat|fix|perf|refactor|docs|chore)'
|
||||
```
|
||||
|
||||
- Count commits by type (BREAKING, feat, fix, refactor, etc.).
|
||||
- Flag commits with no conventional prefix — these are opaque and hard to
|
||||
trace → log as `[SIGNAL: low commit hygiene]`.
|
||||
- Count revert commits (`git log {range} --grep="Revert" --oneline`).
|
||||
- High revert count → likely insufficient testing or review before merge.
|
||||
|
||||
### 2.2 File churn
|
||||
|
||||
```bash
|
||||
git diff --stat {range}
|
||||
git diff --numstat {range} | sort -k1 -rn | head -20
|
||||
```
|
||||
|
||||
- Identify the most-churned files (top 10 by lines added + deleted).
|
||||
- High churn in a single file (> 200 lines in one cycle) → possible
|
||||
monolithic module, design issue, or scope creep.
|
||||
- List new files vs. deleted files.
|
||||
|
||||
### 2.3 Test health
|
||||
|
||||
1. Run the test suite: confirm pass/fail count and duration.
|
||||
2. Check if any test files changed during the cycle:
|
||||
```bash
|
||||
git diff --name-only {range} | grep -E 'test|spec|__tests__'
|
||||
```
|
||||
3. If test files were NOT changed but source files were → `[SIGNAL: untested changes]`.
|
||||
**Architecture-A exemption**: If the cycle touches ONLY `<instance-root>/` and
|
||||
`.gitea/` files (config, skills, templates, rules, checklists) with zero
|
||||
`packages/*` source changes, the "untested changes" signal does NOT apply
|
||||
— `<instance-root>/` files are validated by the audit-process review gate, not
|
||||
by unit tests. Record this as `[NOTE: Architecture A — config-only cycle,
|
||||
unit-test exemption applies]` in the test summary.
|
||||
4. Check for skipped/flaky tests if the framework reports them.
|
||||
|
||||
### 2.4 Build health
|
||||
|
||||
1. Run `bun typecheck` (or project equivalent). Note first-time errors.
|
||||
2. Run `bun oxlint --deny-warnings` (repo root; `bun lint` is the package-script alias). Note any first-time warnings.
|
||||
3. If the project has CI, check the latest run status.
|
||||
|
||||
### 2.5 Dependency health
|
||||
|
||||
1. Run `bun audit` (or equivalent). Flag any new HIGH/CRITICAL CVEs.
|
||||
2. Check if any dependency was added/removed/upgraded:
|
||||
```bash
|
||||
git diff {range} -- package.json bun.lockb
|
||||
```
|
||||
|
||||
### 2.6 Process quality
|
||||
|
||||
Inspect the SDLC infrastructure around the project — not just the code, but
|
||||
the factory that produces it. These checks are all file-existence and
|
||||
configuration reads; they require zero prior pipeline artifacts.
|
||||
|
||||
1. **Pre-commit guards**:
|
||||
- Does the project have pre-commit hooks? (Check `.husky/`, `lefthook.yml`,
|
||||
`.pre-commit-config.yaml`, `package.json` `"lint-staged"` key.)
|
||||
- If absent → `[GAP: no pre-commit guard — bad code can land]`.
|
||||
- If present → what commands do they run? (lint? typecheck? test?)
|
||||
|
||||
2. **CI/CD pipeline**:
|
||||
- Does CI exist? (Check `.gitea/workflows/` — this repo's CI location —
|
||||
then `.github/workflows/`, `.gitlab-ci.yml`, `Jenkinsfile`, etc.)
|
||||
- If absent → `[GAP: no CI — no automated gate before merge]`.
|
||||
- If present → does it block merge on failure? Check branch protection
|
||||
(GitHub: `gh api repos/{owner}/{repo}/branches/main/protection`).
|
||||
|
||||
3. **Code review practice**:
|
||||
- Does the project have a review template or guideline? (Check
|
||||
`.github/PULL_REQUEST_TEMPLATE.md`, `CONTRIBUTING.md`, etc.)
|
||||
- Are PRs used? Look at merge commits: do they reference PR numbers?
|
||||
- If direct pushes to main → `[SIGNAL: no review gate]`.
|
||||
|
||||
4. **SDLC artifacts**:
|
||||
- Use `wiki 读写 API(见 TERMINOLOGY)(owner="Octopus", repo="octopus")` to check
|
||||
for `{slug}/` wiki pages (`.artifacts/` file-system access is retired —
|
||||
SDLC artifacts now live as wiki pages under `{slug}/`).
|
||||
- If no `{slug}/` pages exist → `[NOTE: no structured SDLC artifacts — decisions not traceable]`.
|
||||
- If present but stale → `[SIGNAL: artifacts not maintained — process drift]`.
|
||||
|
||||
5. **Tooling consistency**:
|
||||
- Does the project have a unified formatter config? (`.prettierrc`,
|
||||
`biome.json`, etc.)
|
||||
- Does it have a unified typecheck/lint/test command? (Check `package.json`
|
||||
scripts.)
|
||||
- Are there multiple competing tools for the same concern? (e.g. both
|
||||
prettier AND biome, both jest AND vitest.)
|
||||
|
||||
6. **Documentation health**:
|
||||
- Does `README.md` include setup, build, and test instructions?
|
||||
- Does `AGENTS.md` or `<instance-root>/AGENTS.md` exist?
|
||||
- Are there any outdated docs? (Check for files referencing removed
|
||||
commands or directories.)
|
||||
|
||||
### 2.7 Token telemetry
|
||||
|
||||
Collect LLM token-consumption signals to assess workflow quality. Five
|
||||
metrics: M1 from an inline check; M2–M5 from the token-telemetry probe.
|
||||
|
||||
1. **M1 — Review convergence.** The number of rounds each review dimension
|
||||
needed to converge is the strongest signal of upstream-stage quality.
|
||||
|
||||
Discover review rounds via `wiki 读写 API(见 TERMINOLOGY)(owner="Octopus", repo="octopus")`
|
||||
with prefix `{slug}/reviews/` (`.artifacts/` file-system listing is retired).
|
||||
For each review, `max(roundN)` is its convergence round count.
|
||||
- 🟢 1–2 / 🟡 3–4 / 🔴 ≥5.
|
||||
- ≥5 rounds → `[SIGNAL: review convergence ≥5 — upstream stage quality
|
||||
insufficient, rework deferred to review]`.
|
||||
|
||||
The remaining four metrics come from a single probe run:
|
||||
|
||||
```bash
|
||||
bun run core/skills/retrospective/scripts/token-telemetry.ts
|
||||
```
|
||||
|
||||
It scans `~/.local/share/octopus/octopus-*.db` (token usage) and review
|
||||
rounds from two sources — the Tier 1 review-status file
|
||||
`<runs-root>/{slug}/reviews/{stage}/status.json` (canonical — reads the
|
||||
`history[]` field (legacy alias `rounds[]`) and `current_round`) with the
|
||||
legacy `.artifacts/**/reviews/*/status.json` tree fallback, plus the
|
||||
committed archive bundles `<runs-root>/archive/{slug}.json` for closed
|
||||
runs — and prints M2–M5.
|
||||
|
||||
2. **M2 — Stage distribution.** Token spend per pipeline stage (design,
|
||||
review, implement, …). The probe reconstructs a per-session stage
|
||||
timeline from `~/.local/share/octopus/token-stage-ledger.jsonl` —
|
||||
written by the auto-discovered `<instance-root>/plugin/token-stage-ledger.ts`
|
||||
plugin, which hooks `tool.execute.after` on the `skill` tool — and
|
||||
attributes each message's tokens to the stage active at its creation.
|
||||
- **Ledger-gated.** If the plugin was not active during the cycle the
|
||||
ledger is absent and the probe prints
|
||||
`[NOTE: token-stage-ledger.jsonl absent — M2 skipped]`. That is
|
||||
"unavailable", not "failed" — proceed.
|
||||
- Review-stage share: 🟢 <35% / 🟡 35–60% / 🔴 >60%.
|
||||
- > 60% → `[SIGNAL: review stage >60% of token spend — over-reviewing,
|
||||
review findings not actionable upstream]`.
|
||||
|
||||
3. **M3 — Review rework.** Rework fraction — review rounds beyond the first
|
||||
as a share of total review rounds.
|
||||
The probe merges two sources, deduped by `{slug}/reviews/{stage}` (the
|
||||
ACTIVE status.json wins): (a) the Tier 1 review-status file
|
||||
`<runs-root>/{slug}/reviews/{stage}/status.json` (canonical — `history[]`
|
||||
field, legacy alias `rounds[]`, and `current_round`; skips `_archive/`)
|
||||
with the legacy `.artifacts/**/reviews/*/status.json` tree fallback —
|
||||
in-flight runs only, since the active workspace is deleted at
|
||||
archive-at-close; (b) the committed archive bundles
|
||||
`<runs-root>/archive/{slug}.json`, where per-review round counts are
|
||||
reconstructed from the documented `reviews/{stage}/round{N}/` layout in
|
||||
`index.artifacts[].path` ([org-internal #2591] — the archive source is what makes M3
|
||||
durable instead of structurally emptying as runs close). Available once
|
||||
any run has closed.
|
||||
- Rework fraction: 🟢 <15% / 🟡 15–30% / 🔴 >30%.
|
||||
- > 30% → `[SIGNAL: rework fraction >30% — review findings not actionable
|
||||
or upstream design unclear]`.
|
||||
|
||||
4. **M4 — Context hygiene.** Per-session input-token growth and cache
|
||||
efficiency, read from the octopus session database. The cache
|
||||
read/input ratio measures context reuse.
|
||||
- 🟢 cache_read/input > 10:1 / 🟡 3–10:1 / 🔴 < 3:1.
|
||||
- < 3:1 → `[SIGNAL: cache hit <3:1 — context re-read, code-graph-first
|
||||
not followed]`.
|
||||
- **Compact frequency ([org-internal #2601] pilot data)**: report the token-telemetry
|
||||
"Compactor Activity" line (compactor messages/tokens in window) and the
|
||||
zero-compact share of short runs (bugfix / DAG task) — the capacity-driven compaction
|
||||
pilot metrics. A rising zero-compact share with NO late-stage
|
||||
degradation signal is the evidence that retires the pilot gate in
|
||||
`rules/compact.md` § Stage-boundary compaction.
|
||||
|
||||
5. **M5 — Explore/execute ratio.** Token spend by agent type
|
||||
(`data.agent` per message). A low ratio means workers are doing
|
||||
explorers' job — context-gathering that should be delegated.
|
||||
- 🟢 > 2:1 / 🟡 1–2:1 / 🔴 < 1:1.
|
||||
- < 1:1 → `[SIGNAL: explore/execute <1:1 — workers doing explorers' job,
|
||||
exploration skipped or under-delegated]`.
|
||||
|
||||
### 2.8 Gate defect-escape analysis (single metric, [org-internal #3061])
|
||||
|
||||
Gates earn their cost by what they CATCH, and the honest test is what slips
|
||||
past them: a gate that runs clean while the same defect resurfaces downstream
|
||||
is under-powered — the opposite of redundant. This probe computes ONE metric
|
||||
per `gate_id` (the legacy per-(Kind × Size × gate) keying is retired with the
|
||||
sizing subsystem — sub-5 cell counts produced noise, not evidence):
|
||||
|
||||
**`escape_rate` = clean runs with a downstream escape / clean runs**
|
||||
|
||||
- **Clean run**: the gate ran on a cycle ticket and passed round 1 with zero
|
||||
INFO-or-worse findings.
|
||||
- **Downstream escape**: within 14 days after the clean gate, either
|
||||
(a) a verify FAIL/WARN finding in the same area (module/dimension) on the
|
||||
same ticket, or (b) a post-merge `Kind/Bug` ticket whose body references
|
||||
the area the gate covered.
|
||||
|
||||
**Data sources** (all read-only):
|
||||
|
||||
- **Review status**: commit-status context `pipeline/{review_type}`
|
||||
(`_shared/gitea-write-patterns.md` Pattern 8), read via `octopus review
|
||||
status` CLI or commit-status inspection. Final reports at
|
||||
`{slug}/reviews/{stage}/final/report` record round count and highest
|
||||
severity.
|
||||
- **Downstream (a)**: verify reports `{slug}/05-verify-iteration-{N}` for the
|
||||
same slug.
|
||||
- **Downstream (b)**: `工单 API(见 TERMINOLOGY)list(labels="Kind/Bug", state=closed)`
|
||||
within the window; match by module/path references in the body.
|
||||
- **slug ↔ issue map**: the issue's `## 工件索引` comment (legacy:
|
||||
`## Pipeline 工件追踪表`) or `Closes #N` in the PR body.
|
||||
|
||||
**Procedure**:
|
||||
|
||||
1. Enumerate closed tickets in the cycle with any review run; resolve slugs.
|
||||
2. Per gate run: classify clean / not-clean; for clean runs, search both
|
||||
downstream sources for an escape.
|
||||
3. Tally per `gate_id`: `clean_runs`, `escapes`, `escape_rate`.
|
||||
4. Reading (thresholds are this skill's own policy — the `gate_trim:` block
|
||||
they once deferred to was retired 2026-08-21, [org-internal #3072] phase 3; nothing
|
||||
trims gates anymore, so the trim-candidate branch below is gone):
|
||||
- `escape_rate ≥ 0.3` → the gate is
|
||||
**UNDER-POWERED**: route the escape causes into §2.9 pre-flight
|
||||
(producer-side self-checks) and note the gate in the report. Do NOT
|
||||
propose trimming it.
|
||||
- `escape_rate < 0.3` → healthy; report the numbers, no further verdict.
|
||||
|
||||
**DAG single-gate exclusion ([org-internal #2267])**: `review-dag` is structurally
|
||||
non-trimmable (`dag.route.single_gate.never_trim: true` — self-contained in
|
||||
the `dag:` block); `verify` and `merge-pr` were `never_trim` in the retired
|
||||
`gate_trim` block and stay untouchable by convention.
|
||||
|
||||
**keep_gates exclusion (TD-390, [org-internal #3061]) — RETIRED with gate_trim**: the
|
||||
rule that a trim candidate's gate must not be a member of the target route's
|
||||
`keep_gates` guarded a landing field (`sizing.tiers.*.additional_skip`)
|
||||
that no longer exists. Historical record: wiki `rules/gate-trim`.
|
||||
|
||||
**Sample-sufficiency gate** (structural, not optional): fewer than 5 clean
|
||||
runs for a gate → report the raw numbers with
|
||||
`[NOTE: insufficient sample for gate {id}]` and emit no verdict for it.
|
||||
|
||||
```markdown
|
||||
### Gate defect-escape (M6)
|
||||
|
||||
| gate | clean runs | escapes | escape_rate | verdict |
|
||||
| ----------------------------------- | ---------- | ------- | ----------- | ------------------------- |
|
||||
| review-code | 12 | 1 | 0.08 | healthy |
|
||||
| review-design-space (sticky legacy) | 6 | 3 | 0.50 | UNDER-POWERED → feed §2.9 |
|
||||
| ... | ... | ... | ... | ... |
|
||||
|
||||
- [NOTE: insufficient sample for gate X] where clean runs < 5.
|
||||
- UNDER-POWERED gates (escape_rate ≥ 0.3) feed §2.9 pre-flight — never a
|
||||
unilateral trim: the only exit is a furlough entry in
|
||||
`<instance-root>/gate-ledger.yaml` (evidence + reopen condition, [org-internal #3607]).
|
||||
```
|
||||
|
||||
### 2.9 Pre-flight defect-prevention analysis ([org-internal #2599])
|
||||
|
||||
First-round FAIL/WARN findings are the pipeline's cost multiplier: every
|
||||
multi-round review pays for them twice (review round + revision round).
|
||||
This probe tallies their root causes and proposes producer-side self-checks —
|
||||
the defensive complement of the retired gate-trim meta-process: instead of
|
||||
removing a gate that never catches anything, inject the causes that keep
|
||||
costing rounds as a pre-flight checklist the Producer verifies BEFORE writing
|
||||
code.
|
||||
|
||||
**Data sources** (read-only, same as 2.8): review status pages / final
|
||||
reports for the cycle's tickets (`{slug}/reviews/{stage}/final/report` —
|
||||
round-1 findings with severity FAIL/WARN, their dimension codes, and the
|
||||
finding text for root-cause categorization) + the source issues'
|
||||
`Kind/*` labels. For DAG-routed Epics ([org-internal #2905] 方案 3), two further
|
||||
read-only sources feed the `REQ × late-discovery` root cause (step 1):
|
||||
(a) **DAG oversize-signal events** — `node_split` / registry-row additions
|
||||
recorded on the Epic's issue timeline / `## DAG 状态` whose trigger is a NEW
|
||||
requirement rather than a refactor; (b) **demo-period feedback** —
|
||||
stakeholder comments naming functionality the frozen DAG never covered. For browser-evidence
|
||||
cycles ([org-internal #4499], contract `browser-evidence-4486/shared/evidence-ref-v1` §3), a
|
||||
third read-only
|
||||
source: (c) **browser session rows** — per browser-debug session
|
||||
`{session_id, outcome: evidence-captured | env-unavailable | replay-failed,
|
||||
replay summary, env.mode}`, derived from Tier-1 pack manifests (sanitized
|
||||
transitively by the N-03 write boundary — this probe never touches raw
|
||||
captures). Replay failure attribution rides the FIXED
|
||||
`ReplayFailureCategory` enum (`browser-evidence-4486/shared/pack-manifest-v1`
|
||||
§4); `env-unavailable` sessions default to the enum's own environment slot
|
||||
(`env-binary-missing`) unless the row carries an explicit attribution;
|
||||
browser-class
|
||||
causes enter the step-2 route-class tally as `BROWSER × {category}` and a
|
||||
qualifying cause becomes a PRE-FLIGHT PROPOSAL candidate landing on
|
||||
`routes.{Kind}.preflight` (human-landed in Phase 6, never auto-applied).
|
||||
Rows are currently Task-DAG cycle products by default (the helper's
|
||||
`--route-class` override / row-level `route_class` field re-keys a future
|
||||
Bug- or Feature-cycle session to its own landing slot). The replay
|
||||
success-rate baseline (N-03 BENCH) is exposed as a consumable metric — a SEEDED CONVENTION (fixture-authored outcomes, channel health),
|
||||
NOT a live-browser trend baseline. Mechanical helper:
|
||||
`<harness-package>/scripts/browser-retro-tally.ts`. Boundary rules ([org-internal #4499]
|
||||
AC-3): out-of-enum or empty categories fall into an explicit `other` bucket
|
||||
with provenance (recorded, never silently swallowed, never a crash); rows
|
||||
missing replay/attribution fields yield structured errors in the tally
|
||||
output; a cycle with no browser session rows keeps this probe's current
|
||||
behavior (empty tally, no synthetic proposal rows).
|
||||
|
||||
**Procedure**:
|
||||
|
||||
1. For each ticket with a multi-round review in the cycle, read the round-1
|
||||
findings with severity FAIL or WARN. Categorize each by
|
||||
(dimension × root-cause) — the dimension code is checklist-qualified
|
||||
(checklist-local namespace; e.g. `code-review.md TST × missing-boundary-test`,
|
||||
`code-review.md COR × unhandled-null`,
|
||||
`code-review.md STY × wrong-import-order`).
|
||||
Canonical root-cause category for DAG-routed Epics: `REQ ×
|
||||
late-discovery` — 冻结后才被发现的需求 (a requirement that surfaced only
|
||||
after the DAG froze). A late discovery usually manifests as a round-1
|
||||
REQMAP FAIL on the re-run review, but the underlying event is an
|
||||
oversize signal or demo comment — count it from sources (a)/(b) above,
|
||||
not only from review findings.
|
||||
2. Tally per **route-class** — `Bug`, `Feature-DAG`, `Epic-DAG-route`,
|
||||
`Task-DAG` (Kind/Feature routes to the DAG pipeline since [org-internal #3061] Phase 2 /
|
||||
TD-388 — `Feature-legacy` survives only as a historical bucket for tickets
|
||||
closed before 2026-08-20; the Size dimension is retired from retro keying,
|
||||
[org-internal #3061]: DAG-routed tickets derive depth instead of carrying `Size/*`
|
||||
(spec-06), and `Kind/Bug` carries no ladder). First-round hit count
|
||||
and distinct-ticket count per cause per class.
|
||||
3. A cause is a **pre-flight candidate** when ALL hold (thresholds from
|
||||
`workflow-routing.yaml` `preflight` — do NOT restate values here):
|
||||
- hit count ≥ `preflight.min_sample`
|
||||
- distinct tickets ≥ `preflight.consecutive_recur`
|
||||
A qualifying `Epic × DAG-route` cause (e.g. `REQ × late-discovery`) lands
|
||||
on `<instance-root>/workflow-routing.yaml` `dag.route.preflight` — NOT
|
||||
`routes.Kind/Epic.preflight` (legacy-roadmap-era landing; the legacy route
|
||||
was archived [org-internal #3072] phase 3) ([org-internal #2905] 方案 3). The analyze-dag skill reads
|
||||
`dag.route.preflight` before decomposition.
|
||||
4. Aging: for causes ALREADY landed in `routes.{Kind}.preflight` or
|
||||
`dag.route.preflight`, count
|
||||
consecutive clean first rounds since landing (from this and prior retro
|
||||
data); at ≥ `preflight.aging_consecutive_clean`, emit a REMOVAL row.
|
||||
|
||||
Sample-sufficiency guard mirrors 2.8: zero qualifying tickets → emit
|
||||
`[NOTE: insufficient sample for pre-flight proposals]` and skip this probe.
|
||||
|
||||
```markdown
|
||||
### Pre-flight defect prevention (M7)
|
||||
|
||||
| route-class | Cause (checklist-qualified dim × root-cause) | 1st-round hits | tickets | qualify? |
|
||||
| -------------- | ------------------------------------------- | -------------- | --------------------------- | -------------------------------- |
|
||||
| Bug | code-review.md COR × unhandled-null | 5 | 4 ([org-internal #2400] [org-internal #2429] [org-internal #2471] [org-internal #2488]) | ✅ PROPOSE |
|
||||
| Bug | code-review.md STY × wrong-import-order | 6 | 2 | ⛔ tickets <3 |
|
||||
| Feature-DAG | code-review.md TST × missing-boundary-test | 3 | 3 | ⛔ hits <5 |
|
||||
| Epic-DAG-route | REQ × late-discovery | 5 | 3 (#27xx #28xx #29xx) | ✅ PROPOSE → dag.route.preflight |
|
||||
|
||||
- [NOTE: no pre-flight proposals] if the table is empty or nothing clears threshold.
|
||||
- ✅ PROPOSE rows feed Phase 5 PRE-FLIGHT PROPOSAL action items.
|
||||
- Landed-item aging: `cor-unhandled-null clean streak 5 ≥ aging 5` → REMOVAL row.
|
||||
```
|
||||
|
||||
### 2.10 Derived-ticket health ([org-internal #3061])
|
||||
|
||||
Derived tickets (TD promotions, BF umbrellas, FT tickets) are the pipeline's
|
||||
exhaust. Unmanaged they accumulate into flush cycles (2026-08 evidence: 122
|
||||
open tech-debt tickets, ~9.4/day creation, zero pre-August closures, a
|
||||
115-ticket bulk flush). Under the registry-first regime (verify Phase 5.5)
|
||||
this probe checks whether the system DIGESTS what it records:
|
||||
|
||||
**Metrics** (cycle window):
|
||||
|
||||
- **TD flow**: registry rows created / rows promoted to tickets / promoted
|
||||
tickets closed-as-fixed vs closed-as-wontfix.
|
||||
- **Median open age**: open TD rows + promoted tickets, by module/origin.
|
||||
- **BF triage compliance**: % of verify-Phase-5.55 BF umbrellas triaged
|
||||
(assigned or scheduled) within 1 day of filing.
|
||||
- **FT expiry compliance**: % of verify-Phase-5.56 FT tickets fixed-or-
|
||||
isolated within their N-day window.
|
||||
- **Per-module open count** vs the promotion quota (verify Phase 5.5).
|
||||
|
||||
**Actions**:
|
||||
|
||||
- A category (module/origin) with creation ≥ fix across ≥2 consecutive
|
||||
retros → mark it **register-only**: verify Phase 5.5 stops promoting rows
|
||||
to tickets there until one retro shows net-negative backlog.
|
||||
- Open TD rows unclaimed for >3 retro cycles → mark the row `[COLD]`
|
||||
(revivable — a pull event clears the mark). Cold rows are excluded from
|
||||
adjacency-quota pressure and do not count against the module quota.
|
||||
- BF/FT compliance < 100% → name the untriaged/expired items in the report
|
||||
(they are SLA breaches, not statistics).
|
||||
|
||||
```markdown
|
||||
### Derived-ticket health (M8)
|
||||
|
||||
| metric | this cycle | last cycle | trend |
|
||||
| ----------------------------------------- | ---------- | ---------- | ----- |
|
||||
| TD rows created / promoted / closed-fixed | | | |
|
||||
| median open-TD age (days) | | | |
|
||||
| BF same-day triage % | | | |
|
||||
| FT fix-or-isolate % | | | |
|
||||
|
||||
- register-only categories: {list or "none"}
|
||||
- rows marked [COLD] this cycle: {list or "none"}
|
||||
```
|
||||
|
||||
### 2.11 Threshold calibration tally ([org-internal #3380])
|
||||
|
||||
Process prose constants (round caps, D1–D4 thresholds, preflight knobs,
|
||||
escape-rate cutoffs, quotas — full index:
|
||||
`docs/workflow-refactor/thresholds-ledger.md`) are only as good as the data
|
||||
behind them. This probe keeps them honest, mirroring §2.9's propose-and-human-
|
||||
lands pattern:
|
||||
|
||||
1. **Round-cap distribution** (per stage): read `{slug}/reviews/*/final/report`
|
||||
headers (`**Rounds completed**: N`) for the cycle's slugs; tally per stage
|
||||
(code / dag), report n / p50 / p90 / p95 / max, and how often the cap was
|
||||
the binding stop (`Rounds completed` == cap with `Converged: false`).
|
||||
2. **CALIBRATION PROPOSAL rows**: for any ledger constant whose data source
|
||||
this retro tallied, emit a proposal row — `constant | current | observed |
|
||||
proposed | evidence` — when the data contradicts the current value (cap
|
||||
never binding AND p95 ≪ cap → propose lowering; cap binding with
|
||||
converged-improving runs → propose raising). Proposals are **never
|
||||
auto-landed**: a human lands them by editing the definition site AND the
|
||||
ledger row in one PR citing this retro (preflight evidence-field pattern).
|
||||
3. **Ledger sync**: if any constant's definition site changed since the last
|
||||
retro (value or location), update the ledger row — drift between the two
|
||||
is a TD-480-class double-source failure.
|
||||
|
||||
```markdown
|
||||
### Threshold calibration (M9)
|
||||
|
||||
| stage | n | p50 | p90 | p95 | max | cap-binding runs |
|
||||
| --------------------------------- | ------- | -------- | -------- | ------------------- | --- | ---------------- |
|
||||
| code | | | | | | |
|
||||
| dag | | | | | | |
|
||||
| - CALIBRATION PROPOSAL: {constant | current | observed | proposed | evidence} or "none" |
|
||||
|
||||
- ledger sync: {rows updated or "none"}
|
||||
```
|
||||
|
||||
```markdown
|
||||
## Data Summary
|
||||
|
||||
### Commits
|
||||
|
||||
| Type | Count |
|
||||
| ------------- | ------ |
|
||||
| BREAKING | {N} |
|
||||
| feat | {N} |
|
||||
| fix | {N} |
|
||||
| refactor | {N} |
|
||||
| docs/chore | {N} |
|
||||
| **no prefix** | {N} ⚠️ |
|
||||
| **reverts** | {N} |
|
||||
|
||||
### Churn Top 10
|
||||
|
||||
| File | +lines | -lines | Concern |
|
||||
| --------------------- | ------ | ------ | ----------- |
|
||||
| src/{module}/large.ts | 200 | 150 | Monolithic? |
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
### Tests
|
||||
|
||||
- Suite: {N} passed, {M} failed, {S} skipped — {duration}s
|
||||
- Test files changed: {N} / {M}
|
||||
- Untested source changes: {list or "none"}
|
||||
|
||||
### Build
|
||||
|
||||
- Typecheck: ✅ / ❌ (N errors)
|
||||
- Lint: ✅ / ⚠️ (N warnings)
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Added: {list}
|
||||
- Removed: {list}
|
||||
- Upgraded: {list}
|
||||
- Audit: ✅ / ⚠️ N CVEs
|
||||
|
||||
### Process
|
||||
|
||||
| Check | Status | Detail |
|
||||
| ------------------- | ------------ | ---------------------------------- |
|
||||
| Pre-commit hooks | ✅ / ❌ | {what runs / "none"} |
|
||||
| CI/CD | ✅ / ❌ | {provider / "none"} |
|
||||
| PR / review gate | ✅ / ❌ | {PR # pattern / direct push} |
|
||||
| SDLC artifacts | ✅ / ⚠️ / ❌ | {present & fresh / stale / absent} |
|
||||
| Formatter config | ✅ / ❌ | {tool / "none"} |
|
||||
| Typecheck+Lint+Test | ✅ / ⚠️ | {unified scripts?} |
|
||||
| README / AGENTS.md | ✅ / ⚠️ | {present / stale / absent} |
|
||||
|
||||
### Token
|
||||
|
||||
- M1 review convergence: max {N} rounds ({review name}) — 🟢/🟡/🔴
|
||||
- M2 stage distribution: design {X}% / review {X}% / implement {X}% (review share {X}% — 🟢/🟡/🔴)
|
||||
- M3 rework: {X}% rework fraction ({rework}/{total} rounds across {N} reviews) — 🟢/🟡/🔴
|
||||
- M4 context: p50={N} / p90={N} / max={N} input tokens; cache {ratio}:1 — 🟢/🟡/🔴
|
||||
- M5 explore/execute: {ratio}:1 (explorer {N} / worker {M} tokens) — 🟢/🟡/🔴
|
||||
- M6 gate defect-escape: {N} gates scanned, {M} UNDER-POWERED (escape_rate at/above threshold, top: {gate}) — 🟢 all healthy / 🟡 {M} under-powered → feed §2.9 / 🔴 broad under-powering across gates
|
||||
- [NOTE: M2 skipped if token-stage-ledger.jsonl absent]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — What Went Well?
|
||||
|
||||
Identify positive patterns — things to repeat or amplify:
|
||||
|
||||
1. **High-quality commits**: commits with clear prefixes, atomic scope, good
|
||||
descriptions → name specific examples.
|
||||
2. **Low-churn modules**: files that were changed but had low +/− counts
|
||||
(well-factored, easy to modify).
|
||||
3. **Tests that caught bugs**: if any test was added before the fix commit,
|
||||
that's TDD → highlight it.
|
||||
4. **Fast turnaround**: if any commit → production cycle was unusually fast.
|
||||
5. **Process safeguards active**: pre-commit hooks catching errors before push,
|
||||
CI blocking broken builds, PR review catching design issues early.
|
||||
6. **Fresh documentation**: README and AGENTS.md are up to date and referenceable.
|
||||
|
||||
```markdown
|
||||
## What Went Well
|
||||
|
||||
1. {finding} — {evidence from data} — {why it worked, do again}
|
||||
2. {finding}
|
||||
3. {finding}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — What Went Wrong?
|
||||
|
||||
Identify problems — focus on patterns in the data, not blame:
|
||||
|
||||
| Data signal | Root cause pattern | Example |
|
||||
| ------------------------------------------- | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
||||
| Revert commits > 0 | Bug slipped through review / testing | "Revert 'fix auth' — original fix broke login" |
|
||||
| High churn in a single file (> 300) | Monolithic module, hard to change safely | `src/handler.ts` +400/-350 in one cycle |
|
||||
| feat commits without test changes | New features landed untested | 3 feat commits, 0 test files changed |
|
||||
| No conventional commit prefix | Low commit discipline, harder to auto-changelog | 8 of 12 commits have no prefix |
|
||||
| Test suite growing slower | Test debt accumulating | Source +200 lines, tests +10 lines |
|
||||
| Dependency added without audit check | Supply chain risk | New dep added, `audit` not run |
|
||||
| Typecheck broke mid-cycle | No pre-commit / pre-push hooks | Type error landed on main, fixed later |
|
||||
| No pre-commit hooks | Every developer must remember to run checks manually | Type errors and lint violations land on main |
|
||||
| No CI/CD | No automated gate before merge | Broken build merged, discovered later |
|
||||
| No PR template / direct pushes | No structured review process | Design flaws not caught until production |
|
||||
| Stale SDLC artifacts | Process was followed once then abandoned | `.artifacts/` exists but empty for last 3 cycles (**deprecated**: check Gitea wiki `{slug}/` pages instead) |
|
||||
| Multiple formatters / test frameworks | Tooling inconsistency slows onboarding | Both prettier and biome configured |
|
||||
| Review rounds ≥5 | Upstream stage quality low — rework deferred to review | design converged only at round 5 |
|
||||
| cache_read/input < 3:1 | Context re-read repeatedly — code-graph-first not followed | 2.8:1 across design stage |
|
||||
| explore/execute < 1:1 | Workers doing explorers' job — exploration skipped or under-delegated | 0.7:1 — worker tokens exceed explorer |
|
||||
| review stage >60% of token spend | Over-reviewing — review findings not actionable upstream | design 20% / review 65% / implement 15% |
|
||||
| rework fraction >30% | Review findings not actionable or upstream design unclear | 62% rework — 30 of 48 rounds beyond first |
|
||||
| retrospective skill modified in cycle range | Retrospective reviewing its own modification — potential self-review bias | `core/skills/retrospective/SKILL.md` changed in `git diff {range}` |
|
||||
|
||||
```markdown
|
||||
## What Went Wrong
|
||||
|
||||
1. **{signal}** — {root cause} — {impact: what broke / slowed down}
|
||||
2. **{signal}** — {root cause} — {impact}
|
||||
3. **{signal}** — {root cause} — {impact}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 — Action Items
|
||||
|
||||
For each "what went wrong", generate a concrete, measurable action item.
|
||||
Each item MUST specify:
|
||||
|
||||
- **What** — the change to make.
|
||||
- **Where** — which template, checklist, SKILL, or project config to modify.
|
||||
- **Who** — which role or agent is responsible.
|
||||
- **When** — effective immediately or next cycle.
|
||||
|
||||
```markdown
|
||||
## Action Items
|
||||
|
||||
| # | What | Where | Who | When |
|
||||
| --- | -------------------------------------------------- | --------------------------------------- | ------------------ | ---------- |
|
||||
| 1 | Add pre-commit hook: typecheck + lint on staged | `.husky/pre-commit` | Developer | immediate |
|
||||
| 2 | Require test file changes for every feat commit | `core/checklists/implementation.md` | Retrospective Lead | next cycle |
|
||||
| 3 | Add commit message template (conventional commits) | `.gitmessage` or `CONTRIBUTING.md` | Retrospective Lead | next cycle |
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
|
||||
- Maximum 5 action items per retrospective. If you have more, group by root
|
||||
cause and take the top 5.
|
||||
- Every action item MUST reference a specific file to modify.
|
||||
- Action items that modify SKILLs or templates are applied immediately (the
|
||||
Retrospective Lead can edit `<instance-root>/` files).
|
||||
- Action items MUST NOT be vague ("try harder", "be more careful").
|
||||
- **Layering question** (injection-budget ruling item 3, [org-internal #3547]): every action
|
||||
item that encodes a lesson as a process constraint MUST answer **"which
|
||||
layer carries this lesson?"** — options in ascending per-turn cost, pick
|
||||
the cheapest that actually enforces it:
|
||||
1. **L0 tool-enforced** — hook / bash guard / CI validation (zero prompt
|
||||
bytes);
|
||||
2. **Skill step** — inline in the phase skill that executes the work
|
||||
(bytes paid only when that skill is loaded);
|
||||
3. **L2 on-demand** — wiki / rule doc fetched when a task needs it;
|
||||
4. **L1 per-turn injection** — an `core/rules/*.md` whitelist entry
|
||||
(bytes paid by EVERY role on EVERY turn; item must cite the current
|
||||
corpus bytes vs `bun run check:rule-budget` cap headroom).
|
||||
Option 4 is **default-deny**: choosing it requires stating why 1–3 cannot
|
||||
carry the lesson (2026-08 batch-1 cut builder -41% / explorer -69% per-turn
|
||||
corpus — do not casually re-grow it; the rule-GC report in that same check
|
||||
flags aging L1 rules for L2 retirement).
|
||||
|
||||
**TRIM PROPOSAL rows — RETIRED ([org-internal #3072] phase 3, 2026-08-21)**: the
|
||||
gate-trim landing machinery (`gate_trim.action` →
|
||||
`sizing.tiers.{Size}.additional_skip` / `routes.{Kind}.skip` /
|
||||
`auto_approve.stages`) was removed with the `gate_trim:` and `sizing:`
|
||||
blocks — nothing trims gates anymore, so Phase 5 emits NO trim proposals.
|
||||
Gate health findings flow exclusively through §2.8's escape-rate verdict
|
||||
(UNDER-POWERED → §2.9 pre-flight) and ordinary action items. Historical
|
||||
spec: wiki `rules/gate-trim` (L2).
|
||||
|
||||
**PRE-FLIGHT PROPOSAL rows** (from Phase 2.9 defect-prevention, [org-internal #2599]): format
|
||||
the What cell as
|
||||
`Add pre-flight '{id}' to {Kind} ({hits} first-round FAIL/WARN hits, {tickets} tickets — {evidence})`
|
||||
and the Where cell as
|
||||
`<instance-root>/workflow-routing.yaml` `routes.{Kind}.preflight` — or, for causes
|
||||
keyed `Epic × DAG-route` (e.g. `REQ × late-discovery`, [org-internal #2905] 方案 3),
|
||||
`<instance-root>/workflow-routing.yaml` `dag.route.preflight` (consumed by
|
||||
analyze-dag before decomposition).
|
||||
Aging removal rows: `Remove pre-flight '{id}' from {Kind} (clean streak {N} ≥ aging threshold)`.
|
||||
|
||||
Same doctrine the retired TRIM proposals used: a PRE-FLIGHT PROPOSAL is a
|
||||
recommendation, not an
|
||||
auto-apply — landing (and removal) happens in Phase 6 only when the action
|
||||
item survives the retrospective's own review. Landed checklists must stay
|
||||
within `preflight.max_items` entries per route (drop-oldest by `added_cycle`).
|
||||
|
||||
---
|
||||
|
||||
## Phase 6 — Apply Improvements
|
||||
|
||||
For each action item that modifies a `<instance-root>/` or project-config file:
|
||||
|
||||
1. Read the current file.
|
||||
2. Apply the change.
|
||||
3. Note the change in the retrospective report.
|
||||
|
||||
```markdown
|
||||
## Applied Improvements
|
||||
|
||||
1. Modified `{.file}`: {what was changed} — {commit hash}
|
||||
2. Modified `{.file}`: {what was changed} — {commit hash}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 7 — Report
|
||||
|
||||
Publish the retrospective report as a Gitea wiki page `_retrospectives/{cycle-name}` via `wiki 读写 API(见 TERMINOLOGY)`. The `_retrospectives/` namespace is an intentional cross-cycle, slug-less exception to the `{slug}/...` artifact-path convention (retrospectives aggregate multiple slugs and outlive any one pipeline run) — analogous to the audit `audit/{date}/` date-slug exception documented in the NAM 4.4 checklist item.
|
||||
|
||||
```markdown
|
||||
# Retrospective: {cycle description}
|
||||
|
||||
**Date**: {YYYY-MM-DD}
|
||||
**Range**: {commit range or time range}
|
||||
**Commits**: {N}
|
||||
**Overall**: 🟢 GREEN / 🟡 YELLOW / 🔴 RED
|
||||
|
||||
## What Went Well
|
||||
|
||||
{list}
|
||||
|
||||
## What Went Wrong
|
||||
|
||||
{list}
|
||||
|
||||
## Action Items
|
||||
|
||||
{table}
|
||||
|
||||
## Applied Improvements
|
||||
|
||||
{list}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- `core/checklists/retrospective.md` — Retrospective self-check
|
||||
@@ -0,0 +1,830 @@
|
||||
import { Database } from "bun:sqlite"
|
||||
import { readdirSync, readFileSync, existsSync, statSync } from "node:fs"
|
||||
import { join, relative } from "node:path"
|
||||
import { homedir } from "node:os"
|
||||
|
||||
const DBS_DIR = join(homedir(), ".local/share/octopus")
|
||||
const LEDGER_PATH = join(DBS_DIR, "token-stage-ledger.jsonl")
|
||||
|
||||
// #2628: workspace containers (octopus-ws-*) persist their session store in
|
||||
// named volumes octopus-sessions-<id> (bind added in
|
||||
// packages/containers/src/runtime/docker.ts). Scan those alongside the host
|
||||
// dir so telemetry no longer depends on which machine/container ran a session.
|
||||
const SESSION_VOLUMES_ROOT = "/data/docker/volumes"
|
||||
|
||||
function collectSessionVolumeDbs(): string[] {
|
||||
let vols: string[] = []
|
||||
try {
|
||||
vols = readdirSync(SESSION_VOLUMES_ROOT).filter((d) => d.startsWith("octopus-sessions-"))
|
||||
} catch {
|
||||
return [] // not on the docker host (e.g. a dev workstation) — fine
|
||||
}
|
||||
const out: string[] = []
|
||||
for (const v of vols) {
|
||||
const dir = join(SESSION_VOLUMES_ROOT, v, "_data")
|
||||
try {
|
||||
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
||||
if (e.isFile() && e.name.endsWith(".db") && (e.name === "octopus.db" || e.name.startsWith("octopus-"))) {
|
||||
out.push(join(dir, e.name))
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// unreadable volume — skip it
|
||||
}
|
||||
}
|
||||
return out.sort()
|
||||
}
|
||||
|
||||
function dbLabel(dbPath: string): string {
|
||||
const base = dbPath.split(/[/\\]/).pop()!.replace(".db", "")
|
||||
const vol = dbPath.match(/octopus-sessions-([a-zA-Z0-9-]+)[/\\]_data/)
|
||||
return vol ? `${base}@${vol[1]!.slice(0, 8)}` : base
|
||||
}
|
||||
|
||||
// SINCE_DAYS=<N> env var scopes queries to messages from the last N days,
|
||||
// avoiding full-table scans on multi-GB databases.
|
||||
// message.time_created is MILLISECONDS (verified: raw values ~1.78e12). Keep
|
||||
// `since` in ms — a seconds-based value is always smaller than every ms
|
||||
// timestamp, so the filter would silently match everything (#2599).
|
||||
const sinceDays = Number(process.env.SINCE_DAYS ?? "30")
|
||||
const since = sinceDays > 0 ? Date.now() - sinceDays * 86_400_000 : 0
|
||||
|
||||
// Cycle-window filters (retro #4034 quick-wins): `--since <ISO-date>` and
|
||||
// `--slug <slug>` constrain the cycle-window metrics (M2/M3/M5) to the
|
||||
// window / matching run. M1/M4 keep the SINCE_DAYS env semantics. A filter
|
||||
// that yields no data prints an explicit "no data in window" line for the
|
||||
// metric — never a silent fallback to all-time numbers.
|
||||
const argv = process.argv.slice(2)
|
||||
const arg = (name: string): string | undefined => {
|
||||
const i = argv.indexOf(`--${name}`)
|
||||
return i >= 0 ? argv[i + 1] : undefined
|
||||
}
|
||||
const sinceArg = arg("since")
|
||||
const slugArg = arg("slug")
|
||||
const windowSince = sinceArg !== undefined ? Date.parse(sinceArg) : undefined
|
||||
if (sinceArg !== undefined && Number.isNaN(windowSince)) {
|
||||
console.error(`invalid --since "${sinceArg}" — use an ISO date (e.g. 2026-09-02)`)
|
||||
process.exit(1)
|
||||
}
|
||||
const filtersActive = sinceArg !== undefined || slugArg !== undefined
|
||||
const windowOrSince = windowSince ?? since
|
||||
const m5Filter = filtersActive ? { since: windowOrSince, slug: slugArg } : undefined
|
||||
|
||||
function percentile(sorted: number[], p: number): number {
|
||||
if (sorted.length === 0) return 0
|
||||
const idx = Math.min(Math.floor((sorted.length * p) / 100), sorted.length - 1)
|
||||
return sorted[idx] ?? 0
|
||||
}
|
||||
|
||||
type AgentStats = Map<string, { msgCount: number; totalTokens: number }>
|
||||
|
||||
type SessionRow = {
|
||||
id: string
|
||||
parent_id: string | null
|
||||
slug: string | null
|
||||
title: string | null
|
||||
directory: string | null
|
||||
}
|
||||
|
||||
function loadSessionRows(db: Database): Map<string, SessionRow> | null {
|
||||
const hasTable = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='session'").get()
|
||||
if (!hasTable) return null
|
||||
const rows = db.prepare("SELECT id, parent_id, slug, title, directory FROM session").all() as SessionRow[]
|
||||
return new Map(rows.map((r) => [r.id, r]))
|
||||
}
|
||||
|
||||
// Session→run matching for --slug: a session matches when its slug, title,
|
||||
// or directory contains the run slug (workflow sessions live in worktrees
|
||||
// named after the run). A match propagates to the whole subtree (subagents),
|
||||
// so every message of the run's sessions is included.
|
||||
function slugSessionIncludeSet(sessions: Map<string, SessionRow>, slug: string): Set<string> {
|
||||
const needle = slug.toLowerCase()
|
||||
const children = new Map<string, string[]>()
|
||||
for (const r of sessions.values()) {
|
||||
if (!r.parent_id) continue
|
||||
const arr = children.get(r.parent_id) ?? []
|
||||
arr.push(r.id)
|
||||
children.set(r.parent_id, arr)
|
||||
}
|
||||
const include = new Set<string>()
|
||||
const markSubtree = (id: string) => {
|
||||
if (include.has(id)) return
|
||||
include.add(id)
|
||||
for (const c of children.get(id) ?? []) markSubtree(c)
|
||||
}
|
||||
for (const r of sessions.values()) {
|
||||
const hay = [r.slug, r.title, r.directory].filter((x): x is string => typeof x === "string")
|
||||
if (hay.some((x) => x.toLowerCase().includes(needle))) markSubtree(r.id)
|
||||
}
|
||||
return include
|
||||
}
|
||||
|
||||
function processDb(dbPath: string, since = 0, m5Filter?: { since: number; slug?: string }) {
|
||||
const dbName = dbLabel(dbPath)
|
||||
const sessions: {
|
||||
sessionId: string
|
||||
msgCount: number
|
||||
inputs: number[]
|
||||
totalInput: number
|
||||
totalCacheRead: number
|
||||
}[] = []
|
||||
const agents: AgentStats = new Map()
|
||||
|
||||
let db: Database | null = null
|
||||
try {
|
||||
db = new Database(dbPath, { readonly: true })
|
||||
db.exec("PRAGMA busy_timeout = 5000")
|
||||
} catch {
|
||||
return { dbName, sessionCount: 0, messageCount: 0, sessions, agents }
|
||||
}
|
||||
|
||||
try {
|
||||
const hasTable = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='message'").get()
|
||||
if (!hasTable) return { dbName, sessionCount: 0, messageCount: 0, sessions, agents }
|
||||
|
||||
// Single query replaces the former N+1 pattern (one query per session).
|
||||
// Grouping in JS avoids N full-table scans with json_extract.
|
||||
const rows = db
|
||||
.prepare(
|
||||
`
|
||||
SELECT
|
||||
session_id,
|
||||
CAST(json_extract(data, '$.tokens.input') AS REAL) as inp,
|
||||
CAST(json_extract(data, '$.tokens.cache.read') AS REAL) as cr
|
||||
FROM message
|
||||
WHERE json_extract(data, '$.role') = 'assistant'
|
||||
AND json_extract(data, '$.tokens.input') IS NOT NULL
|
||||
${since > 0 ? "AND time_created >= ?" : ""}
|
||||
ORDER BY session_id, time_created
|
||||
`,
|
||||
)
|
||||
.all(...(since > 0 ? [since] : [])) as {
|
||||
session_id: string
|
||||
inp: number | null
|
||||
cr: number | null
|
||||
}[]
|
||||
|
||||
const sessionMap = new Map<string, { inputs: number[]; totalInput: number; totalCacheRead: number }>()
|
||||
|
||||
for (const r of rows) {
|
||||
const inp = Number(r.inp ?? 0)
|
||||
const cr = Number(r.cr ?? 0)
|
||||
if (inp <= 0) continue
|
||||
let s = sessionMap.get(r.session_id)
|
||||
if (!s) {
|
||||
s = { inputs: [], totalInput: 0, totalCacheRead: 0 }
|
||||
sessionMap.set(r.session_id, s)
|
||||
}
|
||||
s.inputs.push(inp)
|
||||
s.totalInput += inp
|
||||
s.totalCacheRead += cr
|
||||
}
|
||||
|
||||
for (const [sessionId, s] of sessionMap) {
|
||||
if (s.inputs.length === 0) continue
|
||||
sessions.push({
|
||||
sessionId,
|
||||
msgCount: s.inputs.length,
|
||||
inputs: s.inputs,
|
||||
totalInput: s.totalInput,
|
||||
totalCacheRead: s.totalCacheRead,
|
||||
})
|
||||
}
|
||||
|
||||
if (m5Filter) {
|
||||
let include: Set<string> | undefined
|
||||
if (m5Filter.slug !== undefined) {
|
||||
const sessions = loadSessionRows(db)
|
||||
include = sessions ? slugSessionIncludeSet(sessions, m5Filter.slug) : new Set<string>()
|
||||
}
|
||||
// include.size === 0 (or a slug-less filter) → contribute nothing;
|
||||
// the merged M5 output prints the explicit "no data in window" line.
|
||||
if (include === undefined || include.size > 0) {
|
||||
const rows = db
|
||||
.prepare(
|
||||
`
|
||||
SELECT
|
||||
session_id,
|
||||
COALESCE(json_extract(data, '$.agent'), 'unknown') as agent,
|
||||
CAST(json_extract(data, '$.tokens.input') AS REAL) as inp,
|
||||
CAST(json_extract(data, '$.tokens.output') AS REAL) as outp,
|
||||
CAST(json_extract(data, '$.tokens.reasoning') AS REAL) as rea
|
||||
FROM message
|
||||
WHERE json_extract(data, '$.role') = 'assistant'
|
||||
AND json_extract(data, '$.tokens.input') IS NOT NULL
|
||||
${m5Filter.since > 0 ? "AND time_created >= ?" : ""}
|
||||
`,
|
||||
)
|
||||
.all(...(m5Filter.since > 0 ? [m5Filter.since] : [])) as {
|
||||
session_id: string
|
||||
agent: string
|
||||
inp: number | null
|
||||
outp: number | null
|
||||
rea: number | null
|
||||
}[]
|
||||
for (const r of rows) {
|
||||
if (include !== undefined && !include.has(r.session_id)) continue
|
||||
const cur = agents.get(r.agent) ?? { msgCount: 0, totalTokens: 0 }
|
||||
cur.msgCount++
|
||||
cur.totalTokens += Number(r.inp ?? 0) + Number(r.outp ?? 0) + Number(r.rea ?? 0)
|
||||
agents.set(r.agent, cur)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const agentRows = db
|
||||
.prepare(
|
||||
`
|
||||
SELECT
|
||||
COALESCE(json_extract(data, '$.agent'), 'unknown') as agent,
|
||||
COUNT(*) as msg_count,
|
||||
CAST(TOTAL(json_extract(data, '$.tokens.input')) AS REAL) as inp,
|
||||
CAST(TOTAL(json_extract(data, '$.tokens.output')) AS REAL) as outp,
|
||||
CAST(TOTAL(json_extract(data, '$.tokens.reasoning')) AS REAL) as rea
|
||||
FROM message
|
||||
WHERE json_extract(data, '$.role') = 'assistant'
|
||||
AND json_extract(data, '$.tokens.input') IS NOT NULL
|
||||
${since > 0 ? "AND time_created >= ?" : ""}
|
||||
GROUP BY agent
|
||||
`,
|
||||
)
|
||||
.all(...(since > 0 ? [since] : [])) as {
|
||||
agent: string
|
||||
msg_count: number
|
||||
inp: number
|
||||
outp: number
|
||||
rea: number
|
||||
}[]
|
||||
|
||||
for (const r of agentRows) {
|
||||
agents.set(r.agent, {
|
||||
msgCount: Number(r.msg_count ?? 0),
|
||||
totalTokens: Number(r.inp ?? 0) + Number(r.outp ?? 0) + Number(r.rea ?? 0),
|
||||
})
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
db.close()
|
||||
}
|
||||
|
||||
const messageCount = sessions.reduce((s, se) => s + se.msgCount, 0)
|
||||
return { dbName, sessionCount: sessions.length, messageCount, sessions, agents }
|
||||
}
|
||||
|
||||
function dbSummary(db: ReturnType<typeof processDb>) {
|
||||
if (db.sessionCount === 0) return null
|
||||
return ` ${db.dbName}: ${db.sessionCount} sessions, ${db.messageCount.toLocaleString()} messages`
|
||||
}
|
||||
|
||||
function computeSessionMedians(dbs: ReturnType<typeof processDb>[]) {
|
||||
const medians: number[] = []
|
||||
for (const db of dbs) {
|
||||
for (const s of db.sessions) {
|
||||
const sorted = [...s.inputs].sort((a, b) => a - b)
|
||||
medians.push(percentile(sorted, 50))
|
||||
}
|
||||
}
|
||||
return medians
|
||||
}
|
||||
|
||||
function computeSessionMaxes(dbs: ReturnType<typeof processDb>[]) {
|
||||
const maxes: number[] = []
|
||||
for (const db of dbs) {
|
||||
for (const s of db.sessions) {
|
||||
maxes.push(Math.max(...s.inputs))
|
||||
}
|
||||
}
|
||||
return maxes
|
||||
}
|
||||
|
||||
function computeSessionP90s(dbs: ReturnType<typeof processDb>[]) {
|
||||
const p90s: number[] = []
|
||||
for (const db of dbs) {
|
||||
for (const s of db.sessions) {
|
||||
const sorted = [...s.inputs].sort((a, b) => a - b)
|
||||
p90s.push(percentile(sorted, 90))
|
||||
}
|
||||
}
|
||||
return p90s
|
||||
}
|
||||
|
||||
function computeOverallCacheRatio(dbs: ReturnType<typeof processDb>[]) {
|
||||
let totalInput = 0
|
||||
let totalCacheRead = 0
|
||||
for (const db of dbs) {
|
||||
for (const s of db.sessions) {
|
||||
totalInput += s.totalInput
|
||||
totalCacheRead += s.totalCacheRead
|
||||
}
|
||||
}
|
||||
return totalInput > 0 ? totalCacheRead / totalInput : 0
|
||||
}
|
||||
|
||||
function trafficLight(ratio: number): string {
|
||||
if (ratio > 10) return "🟢"
|
||||
if (ratio >= 3) return "🟡"
|
||||
return "🔴"
|
||||
}
|
||||
|
||||
const dbFiles = [
|
||||
...readdirSync(DBS_DIR, { withFileTypes: true })
|
||||
.filter((e) => e.isFile() && e.name.endsWith(".db") && (e.name === "octopus.db" || e.name.startsWith("octopus-")))
|
||||
.map((e) => join(DBS_DIR, e.name)),
|
||||
...collectSessionVolumeDbs(), // #2628
|
||||
].sort()
|
||||
|
||||
for (const f of dbFiles) {
|
||||
const size = statSync(f).size
|
||||
if (size > 1_000_000_000 && since === 0) {
|
||||
console.error(
|
||||
`[WARN] ${f.split("/").pop()} is ${(size / 1e9).toFixed(1)} GB — query may be slow. Set SINCE_DAYS=<N> to scope to recent sessions.`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const allDbs = dbFiles.map((p) => processDb(p, since, m5Filter))
|
||||
|
||||
console.log("# Token Telemetry: Context Hygiene (M4)")
|
||||
console.log()
|
||||
if (filtersActive) {
|
||||
const parts = [sinceArg ? `--since ${sinceArg}` : "", slugArg ? `--slug ${slugArg}` : ""].filter(Boolean)
|
||||
console.log(`Cycle-window filters active (${parts.join(" ")}): M2/M3/M5 constrained to the window; M1/M4 unchanged`)
|
||||
console.log()
|
||||
}
|
||||
|
||||
console.log("## Per-Database Summaries")
|
||||
console.log()
|
||||
for (const db of allDbs) {
|
||||
const s = dbSummary(db)
|
||||
if (s) console.log(s)
|
||||
}
|
||||
|
||||
const totalSessions = allDbs.reduce((s, d) => s + d.sessionCount, 0)
|
||||
const totalMessages = allDbs.reduce((s, d) => s + d.messageCount, 0)
|
||||
console.log()
|
||||
console.log(
|
||||
`Total across ${allDbs.filter((d) => d.sessionCount > 0).length} databases: ${totalSessions} sessions, ${totalMessages.toLocaleString()} messages`,
|
||||
)
|
||||
|
||||
const sessionMedians = computeSessionMedians(allDbs)
|
||||
const sessionP90s = computeSessionP90s(allDbs)
|
||||
const sessionMaxes = computeSessionMaxes(allDbs)
|
||||
const cacheRatio = computeOverallCacheRatio(allDbs)
|
||||
|
||||
const sortedMedians = [...sessionMedians].sort((a, b) => a - b)
|
||||
const sortedP90s = [...sessionP90s].sort((a, b) => a - b)
|
||||
const sortedMaxes = [...sessionMaxes].sort((a, b) => a - b)
|
||||
|
||||
console.log()
|
||||
console.log("## Aggregate Input Token Stats (per-session metrics)")
|
||||
console.log()
|
||||
console.log("| Metric | p50 | p90 | max |")
|
||||
console.log("| ------ | --- | --- | --- |")
|
||||
console.log(
|
||||
`| Per-session median input | ${percentile(sortedMedians, 50).toLocaleString()} | ${percentile(sortedMedians, 90).toLocaleString()} | ${percentile(sortedMedians, 100).toLocaleString()} |`,
|
||||
)
|
||||
console.log(
|
||||
`| Per-session p90 input | ${percentile(sortedP90s, 50).toLocaleString()} | ${percentile(sortedP90s, 90).toLocaleString()} | ${percentile(sortedP90s, 100).toLocaleString()} |`,
|
||||
)
|
||||
console.log(
|
||||
`| Per-session max input | ${percentile(sortedMaxes, 50).toLocaleString()} | ${percentile(sortedMaxes, 90).toLocaleString()} | ${percentile(sortedMaxes, 100).toLocaleString()} |`,
|
||||
)
|
||||
|
||||
console.log()
|
||||
const ratioLabel = cacheRatio >= 1 ? `${cacheRatio.toFixed(1)}:1` : `1:${(1 / cacheRatio).toFixed(1)}`
|
||||
const light = trafficLight(cacheRatio)
|
||||
console.log(`## Cache Read / Input Ratio: ${ratioLabel} ${light}`)
|
||||
console.log()
|
||||
|
||||
const desc =
|
||||
cacheRatio > 10
|
||||
? "Excellent — context reuse is very high, indicating effective caching"
|
||||
: cacheRatio >= 3
|
||||
? "Moderate — reasonable cache hits, room for improvement"
|
||||
: "Low — consider strategies to increase context cache reuse"
|
||||
console.log(` ${desc}`)
|
||||
|
||||
const mergedAgents: Map<string, { msgCount: number; totalTokens: number }> = new Map()
|
||||
for (const db of allDbs) {
|
||||
for (const [agent, stats] of db.agents) {
|
||||
const existing = mergedAgents.get(agent)
|
||||
if (existing) {
|
||||
existing.msgCount += stats.msgCount
|
||||
existing.totalTokens += stats.totalTokens
|
||||
} else {
|
||||
mergedAgents.set(agent, { ...stats })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let exploreTokens = 0
|
||||
let workerTokens = 0
|
||||
for (const [agent, stats] of mergedAgents) {
|
||||
const lower = agent.toLowerCase()
|
||||
if (lower.includes("explorer")) exploreTokens += stats.totalTokens
|
||||
else if (lower.includes("worker")) workerTokens += stats.totalTokens
|
||||
}
|
||||
|
||||
const m5Ratio = workerTokens > 0 ? exploreTokens / workerTokens : 0
|
||||
|
||||
function m5TrafficLight(ratio: number): string {
|
||||
if (ratio > 2) return "🟢"
|
||||
if (ratio >= 1) return "🟡"
|
||||
return "🔴"
|
||||
}
|
||||
|
||||
console.log()
|
||||
console.log("## Explore / Execute Ratio (M5)")
|
||||
console.log()
|
||||
console.log("| Agent | Messages | Total tokens |")
|
||||
console.log("| --------- | -------- | ------------ |")
|
||||
|
||||
const sortedAgents = [...mergedAgents.entries()].sort((a, b) => b[1].totalTokens - a[1].totalTokens)
|
||||
for (const [agent, stats] of sortedAgents) {
|
||||
console.log(
|
||||
`| ${agent.padEnd(9)} | ${stats.msgCount.toLocaleString().padStart(7)} | ${stats.totalTokens.toLocaleString().padStart(12)} |`,
|
||||
)
|
||||
}
|
||||
|
||||
console.log()
|
||||
if (filtersActive && mergedAgents.size === 0) {
|
||||
console.log("[NOTE: no data in window for M5 — no messages match the requested window/slug]")
|
||||
} else {
|
||||
const m5Light = m5TrafficLight(m5Ratio)
|
||||
console.log(`Explore/Execute: ${m5Ratio.toFixed(2)}:1 ${m5Light}`)
|
||||
console.log()
|
||||
|
||||
const m5Desc =
|
||||
m5Ratio > 2
|
||||
? "Explorer-heavy — exploration dominates execution, good for discovery but may need more synthesis"
|
||||
: m5Ratio >= 1
|
||||
? "Balanced — reasonable split between exploration and execution"
|
||||
: "Execution-heavy — workers are spending tokens on discovery work that explorers should handle"
|
||||
console.log(` ${m5Desc}`)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Compactor activity — compact-frequency proxy (#2601 pilot data).
|
||||
// The compactor agent runs once per agent-initiated compaction, so its
|
||||
// message count in the window approximates how often compaction fired.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
console.log()
|
||||
console.log("## Compactor Activity (#2601 compact-frequency proxy)")
|
||||
console.log()
|
||||
const compactor = mergedAgents.get("compactor")
|
||||
if (compactor) {
|
||||
console.log(
|
||||
`compactor: ${compactor.msgCount.toLocaleString()} messages, ${compactor.totalTokens.toLocaleString()} tokens in window`,
|
||||
)
|
||||
console.log(" (per-run distribution = the #2601 pilot metric; rising zero-compact")
|
||||
console.log(" share for short runs (bugfix / DAG task) with no late-stage degradation retires the pilot gate)")
|
||||
} else {
|
||||
console.log("[NOTE: no compactor messages in window — zero agent-initiated compactions recorded]")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// M2 — Stage token distribution (ledger-gated).
|
||||
// Reads token-stage-ledger.jsonl (written by .octopus/plugin/token-stage-ledger.ts),
|
||||
// reconstructs a per-root-session stage timeline, and attributes every
|
||||
// assistant message's tokens to the stage that was active when the message
|
||||
// was created. Skips cleanly when no ledger exists.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type LedgerEntry = { sessionID: string; stage: string; t: number }
|
||||
|
||||
function loadLedger(): Map<string, { stage: string; t: number }[]> | null {
|
||||
if (!existsSync(LEDGER_PATH)) return null
|
||||
const bySession = new Map<string, { stage: string; t: number }[]>()
|
||||
let any = false
|
||||
for (const line of readFileSync(LEDGER_PATH, "utf8").split("\n")) {
|
||||
const trimmed = line.trim()
|
||||
if (!trimmed) continue
|
||||
try {
|
||||
const e = JSON.parse(trimmed) as LedgerEntry
|
||||
const arr = bySession.get(e.sessionID) ?? []
|
||||
arr.push({ stage: e.stage, t: e.t })
|
||||
bySession.set(e.sessionID, arr)
|
||||
any = true
|
||||
} catch {
|
||||
// skip malformed lines
|
||||
}
|
||||
}
|
||||
if (!any) return null
|
||||
for (const arr of bySession.values()) arr.sort((a, b) => a.t - b.t)
|
||||
return bySession
|
||||
}
|
||||
|
||||
function stageTokensForDb(
|
||||
dbPath: string,
|
||||
ledger: Map<string, { stage: string; t: number }[]>,
|
||||
since = 0,
|
||||
slug?: string,
|
||||
): Map<string, number> | null {
|
||||
let db: Database | null = null
|
||||
try {
|
||||
db = new Database(dbPath, { readonly: true })
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
try {
|
||||
const sessions = loadSessionRows(db)
|
||||
if (!sessions) return null
|
||||
|
||||
// timelines keyed by root sessions present in this db
|
||||
const timelines = new Map<string, { stage: string; t: number }[]>()
|
||||
for (const [sid, entries] of ledger) {
|
||||
if (sessions.has(sid)) timelines.set(sid, entries)
|
||||
}
|
||||
if (timelines.size === 0) return null
|
||||
|
||||
let include: Set<string> | undefined
|
||||
if (slug !== undefined) {
|
||||
include = slugSessionIncludeSet(sessions, slug)
|
||||
if (include.size === 0) return null // no session matches the slug
|
||||
}
|
||||
|
||||
const rootOf = (id: string): string => {
|
||||
let cur = id
|
||||
let guard = 0
|
||||
while (guard++ < 100) {
|
||||
const parent = sessions.get(cur)?.parent_id
|
||||
if (!parent) break
|
||||
cur = parent
|
||||
}
|
||||
return cur
|
||||
}
|
||||
const stageAt = (rootId: string, time: number): string | null => {
|
||||
const tl = timelines.get(rootId)
|
||||
if (!tl) return null
|
||||
let stage: string | null = null
|
||||
for (const e of tl) {
|
||||
if (e.t <= time) stage = e.stage
|
||||
else break
|
||||
}
|
||||
return stage
|
||||
}
|
||||
|
||||
const byStage = new Map<string, number>()
|
||||
const rows = db
|
||||
.prepare(
|
||||
`
|
||||
SELECT session_id, time_created,
|
||||
CAST(json_extract(data, '$.tokens.input') AS REAL) as inp,
|
||||
CAST(json_extract(data, '$.tokens.output') AS REAL) as outp,
|
||||
CAST(json_extract(data, '$.tokens.reasoning') AS REAL) as rea
|
||||
FROM message
|
||||
WHERE json_extract(data, '$.role') = 'assistant'
|
||||
AND json_extract(data, '$.tokens.input') IS NOT NULL
|
||||
${since > 0 ? "AND time_created >= ?" : ""}
|
||||
`,
|
||||
)
|
||||
.all(...(since > 0 ? [since] : [])) as {
|
||||
session_id: string
|
||||
time_created: number
|
||||
inp: number | null
|
||||
outp: number | null
|
||||
rea: number | null
|
||||
}[]
|
||||
|
||||
for (const r of rows) {
|
||||
if (include !== undefined && !include.has(r.session_id)) continue
|
||||
const stage = stageAt(rootOf(r.session_id), r.time_created)
|
||||
if (!stage) continue
|
||||
const tokens = Number(r.inp ?? 0) + Number(r.outp ?? 0) + Number(r.rea ?? 0)
|
||||
byStage.set(stage, (byStage.get(stage) ?? 0) + tokens)
|
||||
}
|
||||
return byStage
|
||||
} finally {
|
||||
db.close()
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// M3 — Review rework fraction (#2591).
|
||||
// Two sources, merged with dedup by review identity `{slug}/reviews/{stage}`
|
||||
// (the ACTIVE status.json wins when both exist — it is canonical):
|
||||
// 1. ACTIVE runs — status.json under the Tier 1 location
|
||||
// .octopus/runs/{slug}/reviews/{stage}/status.json (reads history[],
|
||||
// legacy alias rounds[], current_round) and the legacy
|
||||
// .artifacts/**/reviews/*/status.json tree. In-flight runs only: the
|
||||
// active workspace is deleted at archive-at-close, so this source alone
|
||||
// structurally empties as runs close.
|
||||
// 2. ARCHIVED runs — the committed archive bundle
|
||||
// .octopus/runs/archive/{slug}.json. Bundles store digests, not
|
||||
// status.json content, so the per-review round count is reconstructed
|
||||
// from the documented Tier 1 layout `reviews/{stage}/round{N}/…`
|
||||
// (templates/runs-layout.md) by counting distinct roundN path segments
|
||||
// per stage across index.artifacts[].path. This archive source is what
|
||||
// makes M3 durable.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type ReviewRounds = Map<string, number> // `${slug}/reviews/${stage}` -> rounds
|
||||
|
||||
function collectActiveReviewRounds(roots: string[]): { rounds: ReviewRounds; startedMs: Map<string, number> } {
|
||||
const out: ReviewRounds = new Map()
|
||||
const startedMs = new Map<string, number>()
|
||||
const walk = (dir: string, top: string) => {
|
||||
let entries: ReturnType<typeof readdirSync>
|
||||
try {
|
||||
entries = readdirSync(dir, { withFileTypes: true })
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
for (const e of entries) {
|
||||
if (e.name === "_archive" || e.name === "archive") continue
|
||||
const full = join(dir, e.name)
|
||||
if (e.isDirectory()) walk(full, top)
|
||||
else if (e.name === "status.json" && dir.includes("/reviews/")) {
|
||||
try {
|
||||
const data = JSON.parse(readFileSync(full, "utf8")) as {
|
||||
rounds?: unknown[]
|
||||
history?: unknown[]
|
||||
current_round?: number
|
||||
started_at?: string
|
||||
}
|
||||
// Canonical field is `history[]` (per review-status.schema.json);
|
||||
// `rounds[]` is a legacy alias that maps to it. Prefer the array
|
||||
// forms; fall back to current_round.
|
||||
let rounds = 0
|
||||
if (Array.isArray(data.rounds) && data.rounds.length > 0) rounds = data.rounds.length
|
||||
else if (Array.isArray(data.history) && data.history.length > 0) rounds = data.history.length
|
||||
else if (typeof data.current_round === "number" && data.current_round > 0) rounds = data.current_round
|
||||
if (rounds > 0) {
|
||||
const key = relative(top, dir).split("\\").join("/")
|
||||
out.set(key, rounds)
|
||||
if (typeof data.started_at === "string") {
|
||||
const t = Date.parse(data.started_at)
|
||||
if (!Number.isNaN(t)) startedMs.set(key, t)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// skip unreadable / malformed status files
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const root of roots) walk(root, root)
|
||||
return { rounds: out, startedMs }
|
||||
}
|
||||
|
||||
function collectBundleReviewRounds(archiveDir: string): { rounds: ReviewRounds; closedMs: Map<string, number> } {
|
||||
let files: string[] = []
|
||||
try {
|
||||
files = readdirSync(archiveDir).filter((f) => f.endsWith(".json"))
|
||||
} catch {
|
||||
return { rounds: new Map(), closedMs: new Map() } // no archive dir (e.g. a fresh checkout) — fine
|
||||
}
|
||||
const out: ReviewRounds = new Map()
|
||||
const closedMs = new Map<string, number>()
|
||||
for (const f of files) {
|
||||
try {
|
||||
const bundle = JSON.parse(readFileSync(join(archiveDir, f), "utf8")) as {
|
||||
index?: { artifacts?: { path?: unknown }[] }
|
||||
meta?: { closed_at?: string; updated_at?: string; created_at?: string }
|
||||
}
|
||||
// Identity = the bundle filename stem (= the archived run's workspace
|
||||
// dir name). meta.slug is NOT unique — epic task-node bundles carry the
|
||||
// parent epic slug while filenames stay per-node.
|
||||
const slug = f.replace(/\.json$/, "")
|
||||
const closedRaw = bundle.meta?.closed_at ?? bundle.meta?.updated_at ?? bundle.meta?.created_at
|
||||
// Distinct roundN segments per review stage across artifact paths
|
||||
// (paths may or may not carry the slug prefix — match the segment).
|
||||
const byStage = new Map<string, Set<string>>()
|
||||
for (const a of bundle.index?.artifacts ?? []) {
|
||||
if (typeof a?.path !== "string") continue
|
||||
const hit = a.path.match(/reviews\/([^/]+)\/(round\d+)\//)
|
||||
if (!hit?.[1] || !hit[2]) continue
|
||||
const set = byStage.get(hit[1]) ?? new Set<string>()
|
||||
set.add(hit[2])
|
||||
byStage.set(hit[1], set)
|
||||
}
|
||||
for (const [stage, rounds] of byStage) {
|
||||
if (rounds.size > 0) out.set(`${slug}/reviews/${stage}`, rounds.size)
|
||||
if (closedRaw !== undefined) {
|
||||
const t = Date.parse(closedRaw)
|
||||
if (!Number.isNaN(t)) closedMs.set(`${slug}/reviews/${stage}`, t)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// skip unreadable / malformed bundles
|
||||
}
|
||||
}
|
||||
return { rounds: out, closedMs }
|
||||
}
|
||||
|
||||
// --- M2 output ---
|
||||
const ledger = loadLedger()
|
||||
console.log()
|
||||
console.log("## Stage Token Distribution (M2)")
|
||||
console.log()
|
||||
if (!ledger) {
|
||||
console.log("[NOTE: token-stage-ledger.jsonl absent — M2 skipped]")
|
||||
console.log(" (enable the .octopus/plugin/token-stage-ledger plugin to populate)")
|
||||
} else {
|
||||
const mergedStages = new Map<string, number>()
|
||||
for (const dbPath of dbFiles) {
|
||||
const byStage = stageTokensForDb(dbPath, ledger, windowOrSince, slugArg)
|
||||
if (!byStage) continue
|
||||
for (const [stage, tokens] of byStage) mergedStages.set(stage, (mergedStages.get(stage) ?? 0) + tokens)
|
||||
}
|
||||
const grandTotal = [...mergedStages.values()].reduce((a, b) => a + b, 0)
|
||||
if (grandTotal === 0) {
|
||||
if (filtersActive) {
|
||||
console.log("[NOTE: no data in window for M2 — no attributed tokens match the requested window/slug]")
|
||||
} else {
|
||||
console.log("[NOTE: ledger present but no sessions matched — M2 has no attributed data yet]")
|
||||
}
|
||||
} else {
|
||||
const sortedStages = [...mergedStages.entries()].sort((a, b) => b[1] - a[1])
|
||||
console.log("| Stage | Tokens | Share |")
|
||||
console.log("| ------------- | ------ | ----- |")
|
||||
for (const [stage, tokens] of sortedStages) {
|
||||
const pct = ((tokens / grandTotal) * 100).toFixed(1)
|
||||
console.log(`| ${stage.padEnd(13)} | ${tokens.toLocaleString().padStart(13)} | ${pct.padStart(5)}% |`)
|
||||
}
|
||||
const reviewTokens = mergedStages.get("review") ?? 0
|
||||
const reviewShare = (reviewTokens / grandTotal) * 100
|
||||
const m2Light = reviewShare > 60 ? "🔴" : reviewShare >= 35 ? "🟡" : "🟢"
|
||||
console.log()
|
||||
console.log(`Review-stage share: ${reviewShare.toFixed(1)}% ${m2Light}`)
|
||||
console.log(
|
||||
` ${
|
||||
reviewShare > 60
|
||||
? "Review dominates token spend — possible over-reviewing"
|
||||
: reviewShare >= 35
|
||||
? "Moderate review spend"
|
||||
: "Review spend is proportionate"
|
||||
}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// --- M3 output ---
|
||||
const runsDir = join(process.cwd(), ".octopus", "runs")
|
||||
const artifactsDir = join(process.cwd(), ".artifacts")
|
||||
const archiveDir = join(runsDir, "archive")
|
||||
const activeRoots = [runsDir, artifactsDir].filter((d) => existsSync(d))
|
||||
console.log()
|
||||
console.log("## Review Rework (M3)")
|
||||
console.log()
|
||||
const active = collectActiveReviewRounds(activeRoots)
|
||||
const archived = collectBundleReviewRounds(archiveDir)
|
||||
const activeRounds = active.rounds
|
||||
const archivedRounds = archived.rounds
|
||||
const mergedRounds: ReviewRounds = new Map(activeRounds)
|
||||
let archivedOnly = 0
|
||||
for (const [key, rounds] of archivedRounds) {
|
||||
if (mergedRounds.has(key)) continue // active status.json is canonical
|
||||
mergedRounds.set(key, rounds)
|
||||
archivedOnly++
|
||||
}
|
||||
// Cycle-window filters: constrain to reviews whose run identity contains the
|
||||
// slug and whose start (active) / close (archived) time falls in the window.
|
||||
// Reviews without a parseable timestamp are excluded when a window is set —
|
||||
// strict, so filtered numbers never silently fall back to all-time totals.
|
||||
const windowRounds: ReviewRounds = new Map()
|
||||
for (const [key, rounds] of mergedRounds) {
|
||||
const identity = key.split("/reviews/")[0] ?? key
|
||||
if (slugArg !== undefined && !identity.includes(slugArg)) continue
|
||||
if (windowSince !== undefined) {
|
||||
const t = active.startedMs.get(key) ?? archived.closedMs.get(key)
|
||||
if (t === undefined || t < windowSince) continue
|
||||
}
|
||||
windowRounds.set(key, rounds)
|
||||
}
|
||||
if (mergedRounds.size === 0) {
|
||||
console.log(
|
||||
activeRoots.length === 0 && archivedRounds.size === 0
|
||||
? "[NOTE: no .octopus/runs or .artifacts directory in cwd — M3 skipped]"
|
||||
: "[NOTE: no review rounds found (active status.json or archive bundles) — M3 skipped]",
|
||||
)
|
||||
} else if (filtersActive && windowRounds.size === 0) {
|
||||
console.log("[NOTE: no data in window for M3 — no reviews match the requested window/slug]")
|
||||
} else {
|
||||
const roundsMap = filtersActive ? windowRounds : mergedRounds
|
||||
const reviews = roundsMap.size
|
||||
const totalRounds = [...roundsMap.values()].reduce((a, b) => a + b, 0)
|
||||
const reworkRounds = [...roundsMap.values()].reduce((a, b) => a + (b - 1), 0)
|
||||
const nonFirstPass = [...roundsMap.values()].filter((r) => r > 1).length
|
||||
const fraction = totalRounds > 0 ? reworkRounds / totalRounds : 0
|
||||
const nonFirstPct = (nonFirstPass / reviews) * 100
|
||||
const m3Light = fraction > 0.3 ? "🔴" : fraction >= 0.15 ? "🟡" : "🟢"
|
||||
if (filtersActive)
|
||||
console.log(`Window filter: ${windowRounds.size}/${mergedRounds.size} reviews match (--since/--slug)`)
|
||||
console.log(`Reviews: ${reviews} | total rounds: ${totalRounds} | rework rounds: ${reworkRounds}`)
|
||||
console.log(
|
||||
`Sources: ${activeRounds.size} active status.json + ${archivedOnly} archive bundles (dedup by slug+stage)`,
|
||||
)
|
||||
console.log(`Non-first-pass reviews: ${nonFirstPass}/${reviews} (${nonFirstPct.toFixed(0)}%)`)
|
||||
console.log()
|
||||
console.log(`Rework fraction: ${(fraction * 100).toFixed(1)}% ${m3Light}`)
|
||||
console.log(
|
||||
` ${
|
||||
fraction > 0.3
|
||||
? "High rework — review findings not actionable or design unclear"
|
||||
: fraction >= 0.15
|
||||
? "Moderate rework — some review churn"
|
||||
: "Low rework — reviews converge efficiently"
|
||||
}`,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
---
|
||||
name: review-artifact
|
||||
description: >-
|
||||
Use ONLY after analyze-dag has produced the DAG artifact — review the task DAG (review-dag single gate) — OR when auditing the <instance-root>/ process infrastructure itself (audit-process). Spawns N parallel Explorer reviewers (1 dimension each) against the matching checklist, synthesizes findings with a Worker Synthesizer, and iterates with a Worker reviser until convergence. The legacy roadmap / design-space / iteration-plan targets were archived 2026-08-21 ([org-internal #3072] phase 3, <instance-root>/archive/). Do NOT use for code review — use the review-code skill for that.
|
||||
triggers:
|
||||
- audit-process
|
||||
- review-dag
|
||||
- dag review
|
||||
- DAG 评审
|
||||
- process audit
|
||||
- parallel review
|
||||
- reviewer
|
||||
- checklist review
|
||||
- mandatory gate
|
||||
# `stage` is set to the primary gate `audit-process`; the second live target
|
||||
# (review-dag) carries its own stage id in the Target Selection table below.
|
||||
stage: audit-process
|
||||
role: Reviewer
|
||||
---
|
||||
|
||||
> Core 中立版(Increment 6a 改写,原 deferHard verbatimDir)。机制、结构与 frontmatter 保持;实例术语(工具名、路径、工单号)按 `core/adapters/TERMINOLOGY.md` 绑定到具体实例。
|
||||
|
||||
# Skill: review-artifact
|
||||
|
||||
## Tool Availability & Path Resolution
|
||||
|
||||
This skill's reviewers (parallel Explorer subagents) read two kinds of files.
|
||||
Some runs lack the `gitea_wiki__*` MCP tools (cross-repo scenario, [org-internal #2115]); the
|
||||
fallbacks below keep reviews working without them.
|
||||
|
||||
The Orchestrator probes availability **before dispatching reviewers** (Phase A
|
||||
step 1, `_shared/review-pipeline-phases.md`) and fails fast if neither path is
|
||||
usable, so every reviewer has a working read path by the time it runs.
|
||||
|
||||
### Wiki artifacts (DAG artifact / review reports)
|
||||
|
||||
- **Primary**: `wiki 读写 API(见 TERMINOLOGY)(owner, repo, page_name)` — use when the
|
||||
tool is available. `page_name` construction is in
|
||||
`_shared/gitea-read-patterns.md` Pattern 1.
|
||||
- **Fallback** (when `gitea_wiki__*` is unavailable): read the **local wiki
|
||||
clone** at `<wiki-clone-root>/{repo}-wiki/` — a git checkout of the wiki repo,
|
||||
kept current. Wiki filenames are URL-encoded (`/` → `%2F`, with a possible
|
||||
`.-` before `.md`), so resolve the file with `glob` rather than constructing
|
||||
one fixed path:
|
||||
|
||||
```
|
||||
glob(pattern="*{slug}*{page-title}*.md", path="<wiki-clone-root>/{repo}-wiki/")
|
||||
# then read the returned path
|
||||
```
|
||||
|
||||
### Checklists (`core/checklists/*.md`)
|
||||
|
||||
`<instance-root>/` **is git-tracked** (skills, checklists, rules — only runtime
|
||||
artifacts under `<instance-root>/locks/`, `entropy-baseline.json`, etc. are
|
||||
gitignored). Therefore the relative paths referenced throughout this skill
|
||||
(`core/checklists/dag-single-gate.md`, etc.) **resolve correctly inside
|
||||
any git worktree** — no absolute path is needed.
|
||||
|
||||
- **Primary**: read `core/checklists/<name>.md` relative to the project /
|
||||
worktree root (the reviewer subagent's CWD).
|
||||
- **Fallback** (non-standard CWD only): read from the main project directory,
|
||||
e.g. `/data/{repo}/core/checklists/<name>.md`.
|
||||
|
||||
## Target Selection
|
||||
|
||||
This skill reviews different artifact types. Identify the target from the pipeline stage that triggered the review:
|
||||
|
||||
| Target | Stage ID | Checklist | Dimensions | Revision Role | Output namespace |
|
||||
|---|---|---|---|---|---|
|
||||
| **process** | `audit-process` | `core/checklists/audit-process.md` | 10 | Remediator | `audit/{date}/` |
|
||||
| **review-dag** | `review-dag` | `core/checklists/dag-single-gate.md` | 3 | Analyst | `{epic-slug}/reviews/review-dag/` |
|
||||
|
||||
> **Retired targets (historical)**: the `roadmap`, `design-space`, and `plan`
|
||||
> targets were archived on 2026-08-21 per [org-internal #3072] phase 3 (ruling:
|
||||
> `docs/workflow-refactor/phase3-retirement-ruling.md`) — the legacy
|
||||
> production skills they gated no longer exist (see `<instance-root>/archive/`).
|
||||
> Their stage ids (`review-roadmap`, `review-design-space`,
|
||||
> `review-iteration-plan`) were retired from the stage registry in the same
|
||||
> ruling. Historical review pages under `{slug}/reviews/roadmap|design-space|plan/`
|
||||
> remain readable; no new reviews run against those targets.
|
||||
|
||||
The stage IDs remain as-is — they are used by the stage registry and auto-approve system. The skill name is `review-artifact`, but the stage IDs do not change.
|
||||
|
||||
> **Progressive disclosure ([org-internal #3734])**: this skill has two disjoint live
|
||||
> targets — dispatch an auditor/reviewer with
|
||||
> `skills: ["review-artifact:target-process"]` or
|
||||
> `["review-artifact:target-review-dag"]` (preamble + that target only)
|
||||
> instead of the full body. Tool availability, target selection, and shared
|
||||
> pipeline mechanics are unmarked preamble and always inject. Full-body
|
||||
> remains the default when no selector is passed.
|
||||
|
||||
## Shared Pipeline Mechanics
|
||||
|
||||
This skill follows the standardized parallel-review pipeline. The full mechanics are in:
|
||||
|
||||
- `<!-- inject: ../_shared/review-pipeline-phases.md -->` — Phase A through Phase E
|
||||
- **Phase A step 0**: move the source issue to the `review` column on the
|
||||
Pipeline Stages board per `_shared/gitea-write-patterns.md` Pattern 7.5
|
||||
(see `_shared/review-pipeline-phases.md` for the canonical orchestrator
|
||||
actions — step 0 is now part of the shared flow).
|
||||
- `<!-- inject: ../_shared/review-orchestrator-rules.md -->` — mandatory orchestrator rules
|
||||
- `<!-- inject: ../_shared/review-final-report-template.md -->` — final report format
|
||||
- `<!-- inject: ../_shared/review-revision-prompt.md -->` — Phase C revision prompt template
|
||||
- `<!-- inject: ../_shared/compaction-round-boundary.md -->` — round-boundary compaction procedure (Phase D)
|
||||
|
||||
**Context compaction**: this skill compacts at every review round ≥ 2. The
|
||||
round-boundary procedure (`compaction-round-boundary.md`) is injected directly
|
||||
above — that is the normative source; do not restate it here. At the stage
|
||||
level, the main session also compacts after this skill converges, per
|
||||
`core/rules/compact.md` §"Stage-boundary compaction".
|
||||
|
||||
Each target binds the canonical phase placeholders and defines target-specific overrides in its section below.
|
||||
|
||||
### JSON Schemas
|
||||
|
||||
| Schema File | Used For |
|
||||
|---|---|
|
||||
| `core/schemas/reviewer-output.schema.json` | Per-dimension reviewer findings |
|
||||
| `core/schemas/synthesis.schema.json` | Orchestrator round synthesis |
|
||||
| `core/schemas/review-status.schema.json` | Review lifecycle tracking |
|
||||
|
||||
### Reviewer Prompt
|
||||
|
||||
The Orchestrator MUST use the standardized reviewer prompt template at `core/skills/_shared/review-reviewer-prompt.md` when dispatching each reviewer. The template is the same for all reviewers; the Orchestrator fills in skill-level constants and per-dimension values. Each target defines its own constants in its section below.
|
||||
|
||||
### Agent Roles
|
||||
|
||||
| Role | Agent Type | Count | Responsibility |
|
||||
|---|---|---|---|
|
||||
| Orchestrator | Worker | 1 | Spawns reviewers, reads synthesis, drives revision loop |
|
||||
| Reviewer | Explorer | N (per target) | Reviews ONE dimension, outputs structured JSON |
|
||||
| Revision Role | Worker | 1 | Processes findings, revises artifact (role varies by target) |
|
||||
|
||||
All reviewers run in parallel. The orchestrator and revision role run sequentially.
|
||||
|
||||
### Preconditions
|
||||
|
||||
Before launching any review, confirm:
|
||||
- [ ] The target artifact exists (source varies by target: wiki pages for
|
||||
review-dag; the local `<instance-root>/` corpus for audit-process — no upstream
|
||||
producer, self-authored).
|
||||
- [ ] The matching checklist is accessible (see target selection table above).
|
||||
- [ ] `core/schemas/` contains the shared review schemas (`reviewer-output`, `synthesis`, `review-status`).
|
||||
- [ ] `slug` is known (for process audits, `slug` is the literal `audit`).
|
||||
|
||||
Each target adds specific preconditions listed in its section below. If any precondition is unmet, abort and inform the user.
|
||||
|
||||
> ## Retired targets (historical, [org-internal #3072] phase 3)
|
||||
|
||||
The three legacy production-review targets — **roadmap** (stage
|
||||
`review-roadmap`, checklist `roadmap.md`, revision role Analyst),
|
||||
**design-space** (stage `review-design-space`, checklists
|
||||
`requirements-analysis.md` + `design.md`, revision role Architect), and
|
||||
**plan** (stage `review-iteration-plan`, checklist `iteration-plan.md`,
|
||||
revision role Planner) — were archived on 2026-08-21 together with the
|
||||
production skills they gated (ruling:
|
||||
`docs/workflow-refactor/phase3-retirement-ruling.md`; skills and exclusive
|
||||
checklists/templates recoverable under `<instance-root>/archive/`). Their stage ids
|
||||
are retired from the stage registry; no new reviews run against them, and
|
||||
historical review pages under `{slug}/reviews/{roadmap|design-space|plan}/`
|
||||
stay readable as produced. Live targets: **process** (below) and
|
||||
**review-dag** (single gate).
|
||||
---
|
||||
|
||||
<!-- section:target-process -->
|
||||
## Target: process (audit)
|
||||
|
||||
The audit target is the **SDLC factory itself** — the set of skills, checklists, templates, and schemas under `<instance-root>/` — judged for internal consistency, cross-reference integrity, and conformance to audit standards (ISO 19011:2018, IEEE 1028-2008) and `AGENTS.md` conventions.
|
||||
|
||||
**Scope boundary**: This skill audits the *definition* of the pipeline. It does NOT audit whether a project *followed* the pipeline — that is the `retrospective` skill's responsibility.
|
||||
|
||||
### Audit Subject
|
||||
|
||||
| Area | Path | What is audited |
|
||||
|---|---|---|
|
||||
| Skills | `core/skills/**/SKILL.md` | Frontmatter, references, pipeline wiring |
|
||||
| Checklists | `core/checklists/*.md` | Coverage, orphans, dimension count match |
|
||||
| Templates | `core/templates/*.md` | Orphans, referenced existence |
|
||||
| Schemas | `core/schemas/*.json` | Validity, field alignment with skill text |
|
||||
| Conventions | `AGENTS.md` | Engineering-convention conformance |
|
||||
|
||||
### Preconditions
|
||||
|
||||
- [ ] `core/skills/` exists and contains at least one `SKILL.md`.
|
||||
- [ ] `core/checklists/audit-process.md` is accessible.
|
||||
- [ ] `core/schemas/` contains the shared review schemas (`reviewer-output`, `synthesis`, `review-status`).
|
||||
|
||||
No `.artifacts/{slug}/` inputs are required — the audit reads `<instance-root>/` itself. Audit outputs are published as Gitea wiki pages under `audit/{date}/`.
|
||||
|
||||
### Audit Dimensions
|
||||
|
||||
| # | Dimension | Label | Checklist Ref | Files to Read |
|
||||
|---|---|---|---|---|
|
||||
| 1 | INV | Inventory Completeness | INV 1.1–1.7 | `core/skills/` tree, `checklists/`, `templates/`, `schemas/` listings |
|
||||
| 2 | XREF | Cross-Reference Integrity | XREF 2.1–2.7 | ALL `SKILL.md`; resolve every referenced path |
|
||||
| 3 | FM | Frontmatter Conformance | FM 3.1–3.7 | Frontmatter block of every `SKILL.md` |
|
||||
| 4 | NAM | Naming Convention | NAM 4.1–4.6 | ALL `SKILL.md`, checklist filenames, schema enums |
|
||||
| 5 | FLOW | Pipeline Cohesion | FLOW 5.1–5.6 | Preconditions + output sections of every `SKILL.md` |
|
||||
| 6 | SCH | Schema Contract | SCH 6.1–6.5 | `core/schemas/*.json` + skills that reference them |
|
||||
| 7 | DUP | Duplication & Drift | DUP 7.1–7.4 | ALL `SKILL.md` + ALL `checklists/*.md` |
|
||||
| 8 | STD | Audit Standards (ISO/IEEE) | STD 8.1–8.7 | `review-*` skills, `review-artifact` (target: `audit-process`) itself |
|
||||
| 9 | AGT | AGENTS.md Conformance | AGT 9.1–9.5 | ALL `SKILL.md` + `AGENTS.md` |
|
||||
| 10 | TRC | Traceability | TRC 10.1–10.5 | `review-*` skills, schemas, status artifacts |
|
||||
|
||||
### Phase A Threshold
|
||||
|
||||
The process target has 10 dimensions, so Phase A is satisfied when **≥8 of 10
|
||||
dimension findings are produced after retries** (≥75% coverage). If fewer
|
||||
than 8 dimension JSONs are available after
|
||||
retries, abort the round with FAIL — an audit round must not proceed with an
|
||||
underspecified fraction of dimensions covered. Crashed/timed-out dimensions are
|
||||
marked `UNRESOLVED` and retried in the next round.
|
||||
|
||||
### Auditor Constants
|
||||
|
||||
In auditor output, the `dimension` field carries the audit dimension code (INV, XREF, FM, ...) and `slug` in the commit status is the literal `audit`.
|
||||
|
||||
The Orchestrator MUST use the standardized auditor prompt template from `core/skills/_shared/review-reviewer-prompt.md` with these constants:
|
||||
|
||||
- Model tier: default session reviewer model (`Reviewer` Explorer role, `_shared/roles/reviewer.yaml`) — the process target sets no `reviewer-tier` override; record any assignment change here so sibling review skills stay comparable (TRC 10.5)
|
||||
- `{REVIEWER_ROLE}`: process auditor
|
||||
- `{BASELINE_SECTION}`:
|
||||
```
|
||||
## Audit Subject
|
||||
You are auditing the <instance-root>/ process infrastructure ITSELF — the SDLC factory of skills, checklists, templates, and schemas. You are NOT auditing any application code, and NOT auditing whether a project followed the process.
|
||||
```
|
||||
- `{READ_FIRST_STEPS}`:
|
||||
1. Read every file listed above. Do NOT skip any file.
|
||||
2. For XREF/SCH dimensions: for every path or field referenced in a skill, actually resolve it (Read/Glob) and confirm it exists and matches. A referenced-but-missing path or a field-name mismatch is a finding.
|
||||
3. For all other dimensions, read every assigned file and flag any cross-file inconsistency (terminology, numbering, drift) you find.
|
||||
- `{ARTIFACT_NOUN}`: process infrastructure
|
||||
- `{FINDING_DETAILS}`: file path and line number, a summary, detailed explanation, and quoted evidence from the file
|
||||
- `{FINDING_ID_EXAMPLE}`: INV-F001
|
||||
- `{CONTRADICTION_RULE}`:
|
||||
6. (not applicable — the audit is a self-contained inspection, not a baseline comparison)
|
||||
- `{SEVERITY_DEFINITIONS}`:
|
||||
- BLOCKER: broken cross-reference (skill references a non-existent checklist/template/schema/path), missing required frontmatter `description` (skill never triggers), a pipeline stage with no producer for its declared precondition, schema field mismatch that would make written artifacts invalid
|
||||
- MAJOR: orphan checklist/template/schema, dimension count mismatch between a review skill and its checklist, naming inconsistency for a dimension code or role, missing convergence/approval gate, AGENTS.md command divergence (e.g. `bun test` instead of `bun run test:parallel`)
|
||||
- MINOR: terminology drift, duplicated rule that should live in one place, inconsistent standard-number formatting, missing cross-reference comment
|
||||
- INFO: observation or improvement suggestion beyond the checklist
|
||||
- `{LOOKS_FINE_PHRASE}`: the pipeline looks fine
|
||||
|
||||
### Phase Bindings
|
||||
|
||||
| Placeholder | Value |
|
||||
|---|---|
|
||||
| `{DIMENSION_COUNT}` | 10 |
|
||||
| `{REVISION_ROLE}` | Remediator |
|
||||
| `{REVIEW_TYPE}` | audit-process |
|
||||
| `{STAGE}` | audit-process |
|
||||
| `{REVIEWER_OR_AUDITOR}` / `{REVIEWER_OR_AUDITOR_NAME}` | auditor / Audit |
|
||||
| `{REVIEWER_OR_AUDIT_LOOP}` | Re-audit |
|
||||
| Output mode | Tier 1 local files under `<runs-root>/audit/reviews/audit-process/round{N}/findings-{DIMENSION_CODE}.json` (validated by `core/schemas/reviewer-output.schema.json`); synthesis wiki page + commit status stay Tier 2 |
|
||||
| Commit status context | `pipeline/audit-process` |
|
||||
| Wiki namespace | `audit/{date}/` |
|
||||
| Status tracking | Commit status `pipeline/audit-process` (via REST, `_shared/gitea-write-patterns.md` Pattern 8) — `slug: "audit"` |
|
||||
| Date resolution | `{date}` resolved as `YYYY-MM-DD` |
|
||||
| Task file names | `task-synthesizer`, `task-remediator` |
|
||||
|
||||
### Phase E — Close-actor (process target)
|
||||
|
||||
After the final report is written and the `## 工件索引` REVIEW row is
|
||||
updated per the shared Phase E step 2, IF this route closes the source issue
|
||||
(audit-only close, the common Kind/Enhancement process-improvement path that
|
||||
does NOT run verify), execute archive-at-close per
|
||||
`core/rules/artifact-index-guards.md` guard #3: edit the index comment
|
||||
in place — add the canonical archive banner (defined in
|
||||
`_shared/gitea-write-patterns.md` Pattern 10) above the table, and set
|
||||
every row's 重读 column to `ARCHIVE`. Mechanism details (single-comment
|
||||
invariant, no URL deletion, reversibility) per Pattern 10.
|
||||
|
||||
### Remediator Constraints
|
||||
|
||||
The Remediator revises `<instance-root>/` files. Constraints:
|
||||
- Do NOT change the meaning of a skill's workflow to satisfy a finding; fix the inconsistency, not the intent.
|
||||
- Preserve frontmatter `name`/`description` semantics; only correct conformance issues.
|
||||
- When fixing a broken cross-reference, fix the reference OR create the missing file — whichever preserves the pipeline's intent; explain the choice.
|
||||
|
||||
### Output Artifacts
|
||||
|
||||
```
|
||||
<runs-root>/audit/reviews/audit-process/
|
||||
├── index.json # Tier 1 manifest (one row per findings/synthesis/task file; validated by runs-index.schema.json)
|
||||
├── status.json # Review lifecycle tracking (shared Phase A step 0.5; validated by review-status.schema.json)
|
||||
└── round{N}/
|
||||
├── task-remediator.md # Remediator input prompt (Tier 1 copy; index row type `remediation-prompt`)
|
||||
├── task-synthesizer.md # Synthesizer input prompt (Tier 1; index row type `synthesis-prompt`)
|
||||
├── findings-{DIMENSION_CODE}.json # Auditor dimension findings (Tier 1 local, validated by reviewer-output.schema.json; index row type `review-findings`)
|
||||
└── synthesis-return.md # Synthesizer return summary (index row type `other`)
|
||||
|
||||
audit/{date}/ # Wiki page hierarchy (Tier 2)
|
||||
├── round{N}/
|
||||
│ ├── task-remediator # Remediator input (wiki page)
|
||||
│ ├── revision-summary # Remediator revision summary
|
||||
│ └── synthesis # Orchestrator synthesis (```json block, wiki page — audits have no source issue comment)
|
||||
├── round{N+1}/...
|
||||
└── final/
|
||||
└── report # Final human-readable report
|
||||
```
|
||||
|
||||
> **Wiki namespace override**: the audit target writes `task-{ROLE}`,
|
||||
> `revision-summary`, `synthesis`, and `final/report` under `audit/{date}/`
|
||||
> (date-slug), NOT the shared `{slug}/reviews/{stage}/` formula. See the Phase
|
||||
> Bindings `Wiki namespace` row above and `_shared/review-pipeline-phases.md`
|
||||
> Phase C step 1 / Phase E step 1 for the audit carve-out.
|
||||
|
||||
### References
|
||||
|
||||
- `core/checklists/audit-process.md` — The authoritative checklist for all 10 audit dimensions
|
||||
- `core/schemas/` — Shared JSON schemas (reviewer-output, synthesis, review-status) reused by this audit
|
||||
- `retrospective` skill — Complementary: audits a project's *adherence* to the process; this skill audits the process *definition* itself
|
||||
- ISO 19011:2018 — Guidelines for auditing management systems (independence, evidence-based, documented)
|
||||
- IEEE 1028-2008 — Software Reviews and Audits (completeness, repeatability, severity classification)
|
||||
|
||||
---
|
||||
|
||||
<!-- section:target-review-dag -->
|
||||
## Target: review-dag
|
||||
|
||||
The DAG review is a **single gate** (spec-02 §1 D-02) that replaces `review-design-space` +
|
||||
`review-iteration-plan` for a DAG artifact. It reviews one artifact — the DAG
|
||||
at wiki page `{epic-slug}/dag` (NOT per-size-tier pages) — against
|
||||
`core/checklists/dag-single-gate.md` with three fixed dimensions. It saves
|
||||
orchestration overhead, not the revision loop — the three dimensions each
|
||||
review an independent risk surface.
|
||||
|
||||
### Preconditions
|
||||
|
||||
- [ ] The DAG artifact exists at wiki page `{epic-slug}/dag` (read via `wiki 读写 API(见 TERMINOLOGY)`).
|
||||
- [ ] The DAG's `dag_metrics.review_depth` is set (D1–D4) — review depth, reviewer count, `dim_split`, and the Phase D round cap are all derived from this field, NOT from `Size/*` labels.
|
||||
- [ ] `core/checklists/dag-single-gate.md` is accessible.
|
||||
- [ ] `{epic-slug}` is known.
|
||||
|
||||
### Review Dimensions
|
||||
|
||||
Fixed dimension ids — do NOT rename:
|
||||
|
||||
| # | Dimension | Label | Checklist Ref | Files to Read |
|
||||
|---|---|---|---|---|
|
||||
| 1 | TOPO | Topology | `core/checklists/dag-single-gate.md` §TOPO | `{epic-slug}/dag` |
|
||||
| 2 | REQMAP | Requirement Mapping | `core/checklists/dag-single-gate.md` §REQMAP | `{epic-slug}/dag` (+ `{epic-slug}/dag-nodes/{node-id}` subpages when AC detail is sunk) |
|
||||
| 3 | RELEASE | Rolling Release | `core/checklists/dag-single-gate.md` §RELEASE | `{epic-slug}/dag` |
|
||||
|
||||
> **REQMAP reviews task nodes only** — milestone nodes carry no `req_refs` /
|
||||
> `acceptance_criteria` (only a DoD) and are not part of the REQMAP surface.
|
||||
|
||||
### Review Depth (from `dag_metrics.review_depth`)
|
||||
|
||||
Review depth is derived from the DAG's `dag_metrics.review_depth` (D1–D4), NOT
|
||||
from `Size/*` labels.
|
||||
|
||||
| Depth | Reviewers | dim_split |
|
||||
|---|---|---|
|
||||
| D1 | 1 | `all` |
|
||||
| D2 | 3 | `TOPO:1 REQMAP:1 RELEASE:1` |
|
||||
| D3 | 5 | `TOPO:2 REQMAP:2 RELEASE:1` |
|
||||
| D4 | 5 ([org-internal #3061] cap) | `TOPO:2 REQMAP:2 RELEASE:1` |
|
||||
|
||||
> **D4 cap ([org-internal #3061])**: D4 keeps its own rounds budget (4) but its fanout is
|
||||
> capped at D3 level (5 reviewers) — under the 3-dimension + checklist-bound
|
||||
> review the marginal coverage of 9 reviewers was redundancy, and round
|
||||
> iteration is the backstop.
|
||||
|
||||
### Reviewer slots & findings files (dim_split > 1)
|
||||
|
||||
`dim_split` may assign MORE THAN ONE reviewer to a dimension (D3/D4
|
||||
`TOPO:2`). Each reviewer within a dimension occupies a 1-based **slot**
|
||||
and writes its OWN findings file — parallel reviewers must never share one
|
||||
file (concurrent write race):
|
||||
|
||||
- 1 reviewer on the dimension → `findings-{DIM}.json` (unchanged shape).
|
||||
- n > 1 reviewers → `findings-{DIM}-r{k}.json`, k = 1..n — e.g. a D3/D4 round
|
||||
(dim_split `TOPO:2 REQMAP:2 RELEASE:1`) produces `findings-TOPO-r1.json` /
|
||||
`-r2.json`, `findings-REQMAP-r1.json` / `-r2.json`, and a single
|
||||
`findings-RELEASE.json` (RELEASE has one reviewer at every depth ≥ D2).
|
||||
- Multi-slot findings JSONs MUST set `reviewer_slot` (their k) and
|
||||
`reviewer_count` (n) — optional fields in
|
||||
`core/schemas/reviewer-output.schema.json`. Finding ids inside slot
|
||||
files MUST be slot-prefixed (`{DIM}R{k}-F{NNN}`, e.g. `TOPOR1-F001`) so ids
|
||||
stay unique across a dimension's slot files. This preserves per-reviewer
|
||||
attribution (retrospective overlap analysis, [org-internal #2996] step ①a) and makes the
|
||||
write race impossible by construction.
|
||||
- Synthesis input for a dimension = ALL `findings-{DIM}*.json` in the round
|
||||
directory — the synthesizer already globs every `findings-*.json`; the slot
|
||||
files of one dimension aggregate as that dimension's findings.
|
||||
- Phase A's "dimension produced" check: a dimension counts as produced only
|
||||
when EVERY assigned slot's file exists.
|
||||
|
||||
### Reviewer Constants
|
||||
|
||||
- Model tier: `reviewer-tier` (per `analyze-dag` §Exec-Resource Configuration — reviewer-tier's only allowed consumer; corresponds to the `Reviewer` Explorer role, `_shared/roles/reviewer.yaml`)
|
||||
- `{REVIEWER_ROLE}`: DAG reviewer
|
||||
- `{BASELINE_SECTION}`:
|
||||
```
|
||||
## Baseline
|
||||
The artifact under review is the DAG at wiki page `{epic-slug}/dag` (read via `gitea_wiki__get_page`). Read it first for orientation. Its `dag_metrics.review_depth` (D1–D4) sets this review's depth and round cap.
|
||||
```
|
||||
- `{READ_FIRST_STEPS}`:
|
||||
1. Read the DAG artifact at `{epic-slug}/dag` first for orientation.
|
||||
2. Read `core/checklists/dag-single-gate.md`.
|
||||
3. For REQMAP when AC detail is sunk: follow the `{node-id} → {epic-slug}/dag-nodes/{node-id}` pointers to read the subpage ACs.
|
||||
- `{ARTIFACT_NOUN}`: DAG artifact
|
||||
- `{FINDING_DETAILS}`: location in the document, a summary, detailed explanation, quoted evidence from the document
|
||||
- `{FINDING_ID_EXAMPLE}`: TOPO-F001
|
||||
- `{CONTRADICTION_RULE}`:
|
||||
6. When a DAG element contradicts its own declared topology, requirement mapping, or rolling-release plan, this is at minimum a MAJOR finding. When a required DAG section is entirely absent, this is a BLOCKER.
|
||||
- `{SEVERITY_DEFINITIONS}`:
|
||||
- BLOCKER: cycle in the DAG edge graph (TOPO 环检测), milestone missing at a `cross_session_in ≥ 2` convergence point (TOPO 里程碑位置), ≥3 task nodes below the granularity floor (`estimated_sessions < 0.25`, TOPO 粒度下限), a requirement with zero node coverage (REQMAP 需求覆盖)
|
||||
- MAJOR: missing or reversed dependency edge (TOPO 依赖正确性), giant node (`estimated_sessions ≥ 2`) needing split (TOPO 大小均匀性), `estimated_hours`/`estimated_sessions` field inconsistency (`|estimated_hours − 8 × estimated_sessions| > 2`), a single task node below the granularity floor, AC missing error/boundary-path coverage (REQMAP AC 路径覆盖), contract↔node AC inconsistency (REQMAP 契约↔节点一致性), task node with empty/invalid `req_refs` (REQMAP 节点溯源)
|
||||
- MINOR: `1 < estimated_sessions < 2` (over-upper-limit, suggest split), formatting issue, missing cross-reference, terminology drift, revision record not initialized
|
||||
- INFO: observation or recommendation beyond the checklist, including the TOPO page-size signal (see below) — does not affect any gate verdict
|
||||
- `{LOOKS_FINE_PHRASE}`: the DAG looks fine
|
||||
|
||||
### Phase Bindings
|
||||
|
||||
| Placeholder | Value |
|
||||
|---|---|
|
||||
| `{DIMENSION_COUNT}` | 3 |
|
||||
| `{REVISION_ROLE}` | Analyst |
|
||||
| `{REVIEW_TYPE}` | review-dag |
|
||||
| `{STAGE}` | review-dag |
|
||||
| `{REVIEWER_OR_AUDITOR}` / `{REVIEWER_OR_AUDITOR_NAME}` | reviewer / Review |
|
||||
| `{REVIEWER_OR_AUDIT_LOOP}` | Re-review |
|
||||
| Output mode | Tier 1 local files under `<runs-root>/{epic-slug}/reviews/review-dag/round{N}/findings-{DIMENSION_CODE}.json` — one file per reviewer slot when dim_split > 1 (`findings-{DIM}-r{k}.json`, see §"Reviewer slots & findings files") — validated by `core/schemas/reviewer-output.schema.json`; synthesis comment + commit status stay Tier 2 |
|
||||
| Commit status context | `pipeline/review-dag` |
|
||||
| Wiki namespace | `{epic-slug}/reviews/review-dag/` |
|
||||
| Task file names | `task-synthesizer`, `task-analyst` |
|
||||
|
||||
### Output (single gate)
|
||||
|
||||
The review produces a **single-gate synthesis**: the synthesis comment (reusing
|
||||
`core/schemas/synthesis.schema.json`) uses `dimensions` key =
|
||||
`TOPO` / `REQMAP` / `RELEASE`, and the commit status context is
|
||||
`pipeline/review-dag`. Revision role = **Analyst** (the DAG author).
|
||||
|
||||
### Phase A Threshold
|
||||
|
||||
Unlike the process target (≥8 of 10 dimensions), review-dag has only 3 dimensions, so
|
||||
Phase A is satisfied when **ALL THREE dimension findings are produced (3/3 = 100%)**.
|
||||
|
||||
- "3/3=100%" applies to a **full round** only (all 3 dimensions assigned and produced).
|
||||
- In a **partial re-review round** (rounds ≥3 re-review only FAIL/WARN/UNRESOLVED dimensions), Phase A is satisfied when the assigned dimensions' findings are all produced.
|
||||
- In a **full round where one dimension is UNRESOLVED** (e.g. a reviewer crashed, only 2/3 produced), that round proceeds with the produced dimensions and the UNRESOLVED dimension defers to the next partial re-review round — do NOT abort and do NOT apply the 8-file threshold.
|
||||
|
||||
### Phase D MAX_ROUNDS (per depth)
|
||||
|
||||
review-dag's Phase D convergence loop round cap is read from
|
||||
`dag_metrics.review_depth`, NOT the shared `review-artifact` skill default of 3:
|
||||
|
||||
| Depth | MAX_ROUNDS |
|
||||
|---|---|
|
||||
| D1 | ≤2 |
|
||||
| D2 | ≤3 |
|
||||
| D3 | ≤3 |
|
||||
| D4 | ≤4 |
|
||||
|
||||
**D4 override**: the shared `review-artifact` skill MAX_ROUNDS is 3 (see
|
||||
`_shared/review-pipeline-phases.md` Phase D "`{MAX_ROUNDS}` binding"), but
|
||||
review-dag overrides it per depth. D4 reaches round 4 (≤4), which exceeds the
|
||||
shared default — the D4 round cap MUST take precedence for review-dag.
|
||||
|
||||
### Round-3 Escalation Guardrail interaction
|
||||
|
||||
The Round-3 Escalation Guardrail (see `_shared/review-pipeline-phases.md`
|
||||
Phase D) applies to review-dag. Its interaction with the per-depth round cap:
|
||||
|
||||
- **D1–D3** (cap ≤3): the guardrail and the per-depth cap coincide — no conflict.
|
||||
- **D4** (cap ≤4): when the Round-3 guardrail triggers, it MUST offer the option "(b) 继续进入第 4 轮" (continue into round 4) rather than a hard stop. The guardrail means "escalate to human / give the user a choice", not "force-stop at round 3" — so D4's round 4 is reachable via that option. Independently of the guardrail, entering round 4 at all is gated by the shared **Round-4 user-choice escalation point** (`_shared/review-pipeline-phases.md` Phase D) — the choice is offered unconditionally at the round-3→4 transition, whether or not the guardrail's >50% condition fired.
|
||||
|
||||
### TOPO Page-Size Signal
|
||||
|
||||
The TOPO reviewer reads the `> 页尺寸自检: 超限` flag at the top of
|
||||
`{epic-slug}/dag`. If present, the TOPO reviewer records it as an **INFO
|
||||
finding** in the synthesis — summary = page-size over-limit, with the
|
||||
acceptance-criteria detail sunk to the `{subpages}` of the DAG. This INFO
|
||||
finding does NOT change any gate verdict and does NOT trigger re-derivation of
|
||||
the DAG.
|
||||
|
||||
### Output Artifacts
|
||||
|
||||
```
|
||||
<runs-root>/{epic-slug}/reviews/review-dag/
|
||||
└── round{N}/
|
||||
├── findings-{DIMENSION_CODE}.json # single-reviewer dimension (Tier 1 local, validated by reviewer-output.schema.json)
|
||||
└── findings-{DIMENSION_CODE}-r{k}.json # one per reviewer slot when dim_split > 1 (D3/D4) — reviewer_slot/reviewer_count set
|
||||
|
||||
Wiki: {epic-slug}/reviews/review-dag/
|
||||
├── round{N}/
|
||||
│ ├── task-analyst # Phase C dispatch prompt (wiki page)
|
||||
│ └── revision-summary # Analyst revision summary (wiki page)
|
||||
└── final/
|
||||
└── report # Final human-readable report (wiki page)
|
||||
|
||||
Ticket: #{issue_number}
|
||||
└── synthesis comment # Orchestrator synthesis (```json block, Tier 2) — dimensions key = TOPO / REQMAP / RELEASE
|
||||
|
||||
Commit status: context "pipeline/review-dag"
|
||||
```
|
||||
|
||||
### References
|
||||
|
||||
- `core/checklists/dag-single-gate.md` — The authoritative checklist for the 3 review dimensions (TOPO / REQMAP / RELEASE)
|
||||
- `core/schemas/` — Shared JSON schemas for reviewer output, synthesis, and status
|
||||
- IEEE 1028-2008 — Software Reviews and Audits
|
||||
- ISO/IEC/IEEE 24765:2017 — Systems and software engineering vocabulary
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- `core/checklists/audit-process.md` — Process audit dimensions
|
||||
- `core/checklists/dag-single-gate.md` — DAG single-gate review dimensions (TOPO / REQMAP / RELEASE)
|
||||
- `core/schemas/reviewer-output.schema.json` — Per-dimension reviewer findings
|
||||
- `core/schemas/synthesis.schema.json` — Orchestrator round synthesis
|
||||
- `core/schemas/review-status.schema.json` — Review lifecycle tracking
|
||||
- `core/templates/dag.md` — DAG artifact structure contract (review-dag target)
|
||||
- IEEE 1028-2008 — Software Reviews and Audits
|
||||
- ISO 19011:2018 — Guidelines for auditing management systems
|
||||
- ISO/IEC/IEEE 24765:2017 — Systems and software engineering vocabulary
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
# Code Review Dimensions
|
||||
|
||||
> Reference data for the 10 review dimensions. Read on-demand by the Orchestrator
|
||||
> when dispatching reviewers and filling per-dimension prompt values.
|
||||
|
||||
Each reviewer is assigned exactly **one** dimension at **L tier** (1 reviewer
|
||||
per dimension). At S/M tiers the risk-tier table groups dimensions into a
|
||||
single-reviewer sweep (S: all 10) or three grouped sweeps (M: COR+TST+DBT /
|
||||
DGN+SEC+PERF / STY+A11Y+DOC+TRC) — a reviewer covering a group inspects each
|
||||
dimension's checklist items listed below and nothing else (see
|
||||
`review-code/SKILL.md` "Risk Tier Assessment").
|
||||
|
||||
| # | Dimension | Label | Checklist Ref | Code Files to Review | Design Reference Files |
|
||||
|---|-----------|-------|---------------|----------------------|------------------------|
|
||||
| 1 | COR | Correctness, Error Handling & Compatibility | COR 1.1-1.17 | All changed code files | `{slug}/03-design-03-component-design-*`, `{slug}/03-design-04-interface-design` (error paths) |
|
||||
| 2 | DGN | Design Compliance & Dependencies | DGN 2.1-2.15 | All changed code files, package manifests | ALL design files |
|
||||
| 3 | SEC | Security | SEC 3.1-3.10 | Auth, input, data-handling | `{slug}/03-design-06-non-functional-design` (security section) |
|
||||
| 4 | PERF | Performance | PERF 4.1-4.10 | Loops, queries, I/O paths | `{slug}/03-design-06-non-functional-design` (performance section) |
|
||||
| 5 | TST | Test Quality | TST 5.1-5.8 | All test files | `{slug}/03-design-03-component-design-*` (expected behavior) |
|
||||
| 6 | STY | Style & Convention | STY 6.1-6.8 | All changed code files | (none — self-contained against project conventions) |
|
||||
| 7 | DBT | Database & Data | DBT 7.1-7.7 | Migrations, query code | `{slug}/03-design-05-data-design` |
|
||||
| 8 | A11Y | Accessibility & Browser Compatibility | A11Y 8.1-8.13 | UI components, markup | `{slug}/03-design-06-non-functional-design` (a11y section) |
|
||||
| 9 | DOC | Documentation | DOC 9.1-9.6 | All changed code files | `{slug}/03-design-03-component-design-*` (for cross-ref accuracy) |
|
||||
| 10 | TRC | Traceability | TRC 10.1-10.7 | All changed code files, tests | `{slug}/03-design-08-traceability`, work items (DAG task mode: the node `req_refs` + requirement registry rows in `{epic-slug}/dag` + node AC `test_id` declarations) |
|
||||
|
||||
> **DAG task mode**: the legacy `{slug}/03-design-*` Design Reference Files in
|
||||
> the rows above (COR/SEC/PERF/TST/DBT/A11Y/DOC) resolve from the frozen DAG
|
||||
> copy per the DAG-route read map — node spec + cross-session edge contracts
|
||||
> in `{epic-slug}/dag` + node ticket body (same remap as the TRC row above;
|
||||
> see `implement/SKILL.md` § Preconditions "DAG-route read map"d map").
|
||||
|
||||
**Rationale**: Single-dimension assignments prevent context explosion.
|
||||
A reviewer inspecting one dimension examines fewer checklist items and can
|
||||
read code files more deeply within the token budget. The 10 dimensions
|
||||
consolidate the previous 13: COR absorbs error handling (ERR) and
|
||||
compatibility (COR 1.15-1.17), DGN absorbs dependencies (DEP).
|
||||
|
||||
## Design Baseline
|
||||
|
||||
The approved design document at wiki page `{slug}/03-design-index` is the
|
||||
**authoritative baseline** for code correctness. Every reviewer receives the
|
||||
design `index.md` as a mandatory input for orientation.
|
||||
|
||||
Reviewers whose dimension involves design compliance (COR, DGN, SEC, PERF,
|
||||
TST, DBT, A11Y, DOC) additionally receive the relevant design sub-files listed
|
||||
above. Reviewers whose dimension is self-contained (STY) use the design baseline
|
||||
index only. (TRC is NOT self-contained — it reads `{slug}/03-design-08-traceability`
|
||||
and the work-item list to verify REQ references, per its row above; on the
|
||||
DAG route that archived page does not exist — TRC's declared input remaps to
|
||||
the node `req_refs` + requirement registry rows in `{epic-slug}/dag` and the
|
||||
node AC `test_id` declarations, mirroring the DGN remap in
|
||||
`review-code/SKILL.md` § DAG Task Mode. Historical `{slug}/03-design-*`
|
||||
pages stay readable via `_shared/gitea-read-patterns.md`.)
|
||||
@@ -0,0 +1,27 @@
|
||||
# Code Review Output Artifacts
|
||||
|
||||
> Output tree for code review artifacts. Read on-demand for reference.
|
||||
> Output model ([org-internal #1988] Tier 1 migration): Tier 1 local files (raw dimension findings) + Tier 2 wiki pages (task prompts, revision summaries, final reports) + Tier 2 synthesis comment. Status tracking via commit status `pipeline/review-code`.
|
||||
|
||||
```
|
||||
<runs-root>/{slug}/reviews/code/
|
||||
├── round{N}/
|
||||
│ ├── findings-{DIMENSION_CODE}.json # Reviewer dimension findings (Tier 1 local, validated by reviewer-output.schema.json)
|
||||
│ └── task-synthesizer.md # Synthesis prompt (Tier 1 local, passed to Synthesizer via prompt_file)
|
||||
|
||||
Ticket: #{issue_number}
|
||||
└── synthesis comment # Orchestrator synthesis (```json block, Tier 2)
|
||||
|
||||
Wiki: {slug}/reviews/code/
|
||||
├── round{N}/
|
||||
│ ├── task-developer # Phase C dispatch prompt (wiki page)
|
||||
│ └── revision-summary # Developer revision summary (wiki page)
|
||||
└── final/
|
||||
└── report # Final human-readable report (wiki page)
|
||||
|
||||
Commit status: context "pipeline/review-code"
|
||||
```
|
||||
|
||||
**Dimensions (10)**: COR, DGN, SEC, PERF, TST, STY, DBT, A11Y, DOC, TRC
|
||||
|
||||
See `code-review-dimensions.md` for mapping. ERR/DEP/CMP were merged into COR/DGN per the 13→10 consolidation.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user