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

This commit is contained in:
octopus
2026-09-15 08:41:51 +08:00
commit bb35e661b2
114 changed files with 20240 additions and 0 deletions
View File
+136
View File
@@ -0,0 +1,136 @@
# 任务 DAG 模板
> 任务 DAG 是「需求(节点验收标准)+ 设计(边契约)+ 计划(拓扑排序)」三视图的
> **单一工件**,由 `analyze-dag` skill 产出,落地为 wiki 页 `{epic-slug}/dag`。
> 结构契约以 `.octopus/templates/dag.md` 为准;字段语义权威源:
> `dag-pipeline/spec-03`、冻结契约 `dag-pipeline/spec-02` §2。
> DAG 工件状态: draft
> 版本戳: v1
> 页尺寸自检: 未超限
---
## 1. 头部卡
| 项 | 内容 |
|---|---|
| slug / 源 DAG 父级 | `{epic-slug}` / #N`## 父级 / Parent`DAG 父级 = `Kind/Epic``Kind/Feature`[org-internal #3061] 阶段 2——文中「Epic scope」泛指源工单 scope |
| 单门 | `review-dag`TOPO / REQMAP / RELEASE 三维,深度由 `dag_metrics` 导出) |
| 评审深度 | `D1 | D2 | D3 | D4``dag_metrics.review_depth` |
## 2. 需求登记表
> 仅登记功能性需求(Epic scope 项中的功能性条目)。产品 NFR 不占 `REQ-F` 编号、
> 不登记入表、以 `NFR:` 前缀条目落节点 AC(见 §3 `acceptance_criteria`)。
> 登记表登记的是「提出者写了什么」;§2.1 类目覆盖矩阵补上「还有什么该问的」
> [org-internal #2905]legacy elicitation 广度扫描被折叠后的补偿层——`表 ↔ 现实` 完备性)。
| id | title | source | refs_by |
|---|---|---|---|
| REQ-F-001 | {需求标题} | {Epic scope 项} | {引用节点 id 集} |
### 2.1 类目覆盖矩阵([org-internal #2905] 方案 1
> 固定类目集(≤10 类,自 legacy `.octopus/archive/checklists/requirements-analysis.md` CMP/SAF 维抽取)。
> 每类目状态 ∈ {已覆盖, 明确排除, 待确认}:
> - **已覆盖** — 类目下存在登记需求(§2 登记表 `REQ-F-{NNN}` 行或节点 `NFR:` 条目);
> - **明确排除** — 类目整体或部分条目 out of scope,须在 §2.2 排除账本登记 `E-n` 行;
> - **待确认** — 无法判定的中间态,须写明「向{确认人}确认{什么}」。
> 「明确排除」与「漏了」在工件上可区分:漏了 = 状态空白、或写「N/A」而无账本行。
> 矩阵是 analyze-dag 生产侧义务,**不新增单门判据**(REQMAP 三张表不变,
> spec-04 §1 冻结文本不动)。矩阵 + 账本计入页尺寸预算(spec-02 §2.6),
> 超限时类目细目下沉子页 `{epic-slug}/dag-coverage`(同 AC 下沉机制)。
| 类目 | 状态 | 证据 / 指针 |
|---|---|---|
| 用户角色(CMP 1.7:直接/间接/自动化) | {已覆盖/明确排除/待确认} | {REQ-F-{NNN}… / E-{n} / 向{确认人}确认{问题}} |
| 外部系统集成(CMP 1.5;产品 Epic 强制——External-System Rule 的 N 类目推广) | {已覆盖/明确排除/待确认} | {证据/指针} |
| 接口需求(CMP 1.13:用户/硬件/软件/通信) | {已覆盖/明确排除/待确认} | {证据/指针} |
| 数据需求(CMP 1.14:数据量/保留/质量/敏感性) | {已覆盖/明确排除/待确认} | {证据/指针} |
| 业务规则(CMP 1.15,含来源) | {已覆盖/明确排除/待确认} | {证据/指针} |
| 约束(CMP 1.8:法规/技术/组织/进度/预算) | {已覆盖/明确排除/待确认} | {证据/指针} |
| 安全(SAF 5.1–5.10:敏感数据/认证/授权/审计/加密/威胁) | {已覆盖/明确排除/待确认} | {证据/指针} |
| 非功能需求 NFRCMP 1.12ISO/IEC 25010:2011;条目落节点 `NFR:` AC——矩阵仅确认已扫描) | {已覆盖/明确排除/待确认} | {证据/指针} |
| 错误与边界场景(CMP 1.11:空输入/边界值/异常状态,Epic 级;节点级路径覆盖由 REQMAP「AC 路径覆盖」承担) | {已覆盖/明确排除/待确认} | {证据/指针} |
| 假设与依赖(CMP 1.9:列出并评估影响) | {已覆盖/明确排除/待确认} | {证据/指针} |
### 2.2 排除账本([org-internal #2905] 方案 1
> 「明确排除」的唯一凭证登记处。每行一条 `E-n`:条目 — out of scope、理由、确认人。
> 排除可逆:重开时删行并把 §2.1 矩阵状态改为「已覆盖/待确认」,同一次修订内完成。
> 无账本行的「N/A」视为未扫描(等同「漏了」)。
| id | 类目 | 条目 | 理由(为何 out of scope | 确认人 |
|---|---|---|---|---|
| E-1 | {类目名} | {具体条目} | {一句话理由} | {提出者/干系人} |
## 3. 节点表
> 任务节点 `N-{nn}`N-01…);里程碑节点 `M-{nn}`M-01…)。
> `status ∈ {pending, ready, in_progress, done, blocked, green}`——`green` 仅里程碑节点可用。
```yaml
nodes:
- id: "N-01"
title: "{一行}"
type: task # task | milestone
acceptance_criteria: # 每条可证伪 + 映射 test_id;可含 NFR: 前缀条目
- "AC-1: <可证伪的验收条件>"
- "NFR: <产品 NFR 条目>" # 可选
req_refs: ["REQ-F-001"] # 由 §2 需求登记表派生;milestone 节点无此字段
status: pending
owner_session: null
size_attrs: # 仅 type=task 节点必填;milestone 节点不填
cross_session_in: 0 # 入边跨 session 边数
cross_session_out: 0 # 出边跨 session 边数
contract_change: none # none | additive | breaking(仅聚合承载契约的跨 session task 出边)
estimated_hours: 8 # 预估实现时长(小时)
estimated_sessions: 1 # 1 session ≈ 8h;须满足 |estimated_hours 8×estimated_sessions| ≤ 2
```
## 4. 边表
> 与里程碑相连的边(入边 + 出边)`cross_session: true` 但无 `contract_ref`、无
> `change_type`、`contract_state` 不适用(里程碑 = 无 session 的集成点)。
```yaml
edges:
- from: "N-01"
to: "N-02"
contract_ref: "shared/{contract-name}" # 跨 session task 边必填;里程碑边省略
cross_session: true # from/to 是否由不同 session 拥有
contract_state: draft # draft | frozen(仅 task 间跨 session 边有冻结语义)
change_type: additive # none | additive | breaking(仅跨 session task 边)
```
## 5. 里程碑节点
> 每个 `cross_session_in ≥ 2` 汇聚点焊入里程碑(无实现工作,只有 DoD)。
> 里程碑 DoD = 汇聚范围集成验证(集成测试 / 契约符合性 / 回归 / NFR 覆盖核对),
> 由 `verify` skill 按 DAG 切片生成——见 `dag-pipeline/spec-05`。
```yaml
milestones:
- id: "M-01"
fan_in: ["N-02", "N-03"] # 全部跨 session 入边源节点
downstream: ["N-05"] # 下游消费者;无下游则为 sink 里程碑(无出边)
```
## 6. 拓扑约束(必须满足,否则单门 TOPO 维不通过)
1. **无环**:边方向定义的图无环。环 → BLOCKER。
2. **里程碑位置**:任意节点 `cross_session_in ≥ 2` → 必须焊入里程碑(见 §5)。
3. **粒度下限**:以 `estimated_sessions` 为准(完整分档见 spec-03 §3 规则 3)。
## 7. dag_metrics
> 键名冻结于 spec-02 §2.6。`review_depth` 由 spec-06 §1 派生公式取三输入 MAX。
```yaml
dag_metrics:
node_count: 6 # 任务 + 里程碑节点总数
cross_session_edge_count: 7 # 全图 cross_session: true 边总数
contract_change_surface: additive # none | additive | breaking(最坏值聚合)
review_depth: D4 # max(depth_by(node_count), depth_by(cross_session_edge_count), depth_by(contract_change_surface))
```
+287
View File
@@ -0,0 +1,287 @@
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
# Tier 1 Local Run Layout — `<runs-root>/{slug}/`
Normative layout for **Tier 1 local structured artifacts**. Authoritative
boundary rule: `core/rules/two-tier-artifacts.md`. Schemas that validate
the structured files below live in `core/schemas/runs-*.schema.json`.
This document is the **foundation** (MVP slice, [org-internal #1968]). It defines the
container, the manifest, and the metadata. Stage-specific artifact schemas
(design detail dumps, plan items, review dimension findings, synthesis
prompts, draft contracts) are added incrementally as each skill migrates its
transient output here — the follow-up iteration ([org-internal #1988]) has landed: `review-artifact` /
`audit-process` raw findings and producer working drafts are now Tier 1.
([org-internal #3072] phase 3, 2026-08-21: the legacy producer stages whose drafts are listed
below were archived — their rows and artifact dirs are kept for reading
historical run bundles.) See the migrated stages table below.
## Kind/MVP mode ([org-internal #3061])
`Kind/MVP` tickets write **nothing** to `<runs-root>/` by design — no stage
dirs, no `index.json`, no manifest. The ticket body is the doc (`## 决策日志`
+ `## Debt Register`). Optionally a single free-form
`<runs-root>/{slug}/working-notes.md` may capture scratch that does not
belong in the body; it has no schema and is not indexed. On graduation
(relabel `Kind/Feature`) the DAG route initializes the standard layout from
scratch; the MVP body remains the REQMAP baseline (analyze-dag backfill).
## Why a local tier
Agent-internal handoff (subagents sharing one worktree) currently pays the
Gitea tax (HTTP latency, wiki 409 conflicts, auth, Gitea-health dependency)
even though the consumer is a sibling subagent in the same process tree. The
embryo already proved local files viable (`/tmp/octopus/synthesis_task_*.md`
for `prompt_file`, see `core/skills/_shared/review-pipeline-phases.md` §Phase B step 1 — synthesis prompt).
This layout formalizes that embryo into a discoverable, schema-validated,
archive-at-close tier — without touching Gitea's role as the system of record
for human-visible decisions.
## Directory tree
```
<runs-root>/
├── .gitignore # ignores active-run workspaces; tracks archive/ + self
├── archive/ # TRACKED — closed-run bundles land here
│ ├── .gitkeep
│ └── {slug}.json # one bundle per closed run (meta + index digest)
└── {active-slug}/ # GITIGNORED — one subdir per active run
├── meta.json # run metadata (runs-meta.schema.json)
├── index.json # artifact manifest (runs-index.schema.json)
├── working-notes.md # cross-stage narrative memory ([org-internal #2600], schema-less md)
├── workers/ # worker final-return reports ([org-internal #2847], schema-less md —
│ └── ... # see _shared/worker-report-persistence.md)
└── {stage}/ # per-stage transient artifacts (added by skills)
└── ... # json (structured) / md (prompt files)
```
### `{active-slug}`
- One subdir per active pipeline run, named by the run slug (the same `{slug}`
used by the Gitea wiki namespace and the workflow branch tail).
- **Gitignored** while active — subagents share the worktree, not the git
history, so active-run content stays local (no repo bloat, no merge noise).
- **Initialized at run start** by `bun <harness-package>/script/runs-init.ts
--slug {slug} --ticket N [--branch …] [--worktree …]` ([org-internal #3642]) — the
producing role / Orchestrator runs the script (hand-writing the files is the
legacy path): it creates the `{active-slug}/` workspace, writes `meta.json`
with `state: "active"` and the run's slug/branch/worktree/ticket (branch
omitted / worktree null for ad-hoc runs without a worktree), and an empty
`index.json` (`artifacts: []`), schema-validated before write and idempotent
on re-run. Kind/MVP and Kind/Documentation tickets are skipped automatically
(label check, § Kind/MVP mode below). `script/claim-provision.sh` chains this
after claim + worktree in one command. Subsequent producing roles append to
`index.json` as they emit artifacts.
- Removed by the Verifier's archive step at close (its content is bundled into
`archive/{slug}.json` first).
### `meta.json` — run metadata
Schema: `core/schemas/runs-meta.schema.json`. Fields:
| field | type | notes |
|---|---|---|
| `schema_version` | integer | const `1` |
| `slug` | string | run slug; matches branch tail + wiki namespace |
| `ticket` | object\|null | `{ owner, repo, number }` of the source issue, or null for ad-hoc runs |
| `branch` | string | workflow branch, e.g. `workflow/enhancement/1968-two-tier-artifacts`; omitted for ad-hoc runs without a branch |
| `worktree` | string\|null | absolute path of the worktree, or null for ad-hoc runs without a worktree |
| `parent_epic` | object\|null | `{ number }` of the parent Epic, if any |
| `state` | enum | `active` → `archived` (set at close) |
| `created_at` | string | RFC 3339 timestamp |
| `updated_at` | string | RFC 3339 timestamp |
| `closed_at` | string\|null | RFC 3339; null while active |
| `close_commit_sha` | string\|null | git SHA of the archive commit; null while active |
### `index.json` — artifact manifest
Schema: `core/schemas/runs-index.schema.json`. This is the **compact
recovery entry point**: after `compact` (which never deletes local files), an
agent re-reads `index.json` to recover the run's artifact set without scanning
the tree. Each entry:
| field | type | notes |
|---|---|---|
| `schema_version` | integer | const `1` |
| `slug` | string | run slug |
| `artifacts` | array | one entry per Tier 1 artifact in this run |
| `artifacts[].path` | string | path relative to `{active-slug}/` |
| `artifacts[].type` | enum | `meta`, `index`, `design`, `plan`, `review-findings`, `review-status`, `review-synthesis`, `synthesis-prompt`, `precondition-gate`, `working-notes`, `analysis`, `browser-evidence`, `other` (authoritative enum: `core/schemas/runs-index.schema.json`) |
| `artifacts[].schema` | string\|null | `$id` of the validating schema, or null for schema-less (e.g. prompt `.md`) |
| `artifacts[].tier` | const `1` | local tier marker (Tier 2 is never listed here) |
| `artifacts[].stage` | enum\|null | producing pipeline stage (`requirements`/`design`/`plan-iterations`/`implement`/`review-code`/`verify`/…), or null for meta/index |
| `artifacts[].produced_by` | enum\|null | `Producer`/`Reviewer`/`Verifier`/`Tool`/`Coordinator` (canonical role names — the `name` field of `core/skills/_shared/roles/*.yaml`), plus fine-grained sub-roles mapped via `ROLE_ALIASES` in `<harness-package>/src/config/role.ts`: Producer sub-roles `Analyst`/`Architect`/`Planner`/`Developer`/`Synthesizer`/`Orchestrator`/`Remediator`, and the Reviewer sub-role `Auditor` |
| `artifacts[].sha` | string\|null | content digest (sha256), or null if not yet computed |
| `artifacts[].tier2_ref` | string\|null | cross-link to the Tier 2 mirror (e.g. issue comment URL / wiki page) when one exists |
`index.json` is **parallel, not replacement**, for the Tier 2 `## 工件索引`
issue comment: the former is the agent's compact-recovery entry; the latter is
the human-visible traceability hub. They are kept in sync by the producing
role but serve different consumers.
### `working-notes.md` — cross-stage narrative memory ([org-internal #2600])
`index.json` is a MANIFEST: it records WHAT artifacts exist. It does not
carry WHY code was written that way, which paths were tried and abandoned, or
what the reviewer's feedback actually changed — the tacit context every next
stage currently re-derives by re-reading formal artifacts. `working-notes.md`
is that narrative memory, in-file, append-only:
- **Format** (schema-less markdown): each entry is
`## [{stage}] {role} @ {RFC 3339 ts}` followed by **≤30 lines** covering:
decisions made (and why), dead ends (do NOT retry these), reviewer feedback
that changed the work, and one-line hints for the next stage.
- **Append cadence** — one entry per unit of ownership, never per subagent
dispatch: Producer sub-roles append at **stage exit**; the Reviewer side
appends **via the Synthesizer at review convergence** (one entry per
review, not per dimension — 9 dimension dispatches = 1 distilled entry);
the Verifier appends at **verify exit**; Tool agents append only when the
task produced a load-bearing finding (e.g. image evidence that changed a
decision).
- **Re-read rule**: the next stage reads `working-notes.md` FIRST — before
the formal artifacts — and may then skip full re-reads that the notes
already cover. Role yamls carry this line in `compact.preserve`
(`core/skills/_shared/roles/*.yaml`).
- **index.json row** (registered once by the first appending role):
`type: working-notes`, `schema: null` (markdown), `stage: null`
(cross-stage), `produced_by: null` (multi-role), `tier2_ref: null`.
- **Tier judgment**: consumed within one worktree/run ✓, never a human gate
decision ✓, never referenced cross-worktree ✓, archivable at close ✓ — all
four Tier 1 conditions hold (`core/rules/two-tier-artifacts.md` 判据).
### `workers/` — worker final-return reports ([org-internal #2847])
Every worker sub-agent dispatch (`agent: worker` roles — Producer
sub-roles, Verifier, Synthesizer) persists its **final return report**
verbatim to `workers/{chunk-id}-worker-{seq}.md` as its LAST action before
returning (lightweight fallback when no run workspace exists:
`/tmp/octopus/{chunk-id}-worker-{seq}.md`). The persisted copy is the report
of record — when the task completion notification is lost, the orchestrating
session recovers the worker's conclusion by reading the newest
`workers/{chunk-id}-worker-*.md` match. Skills that own a canonical stage
path for the dispatch (e.g. the review-code Synthesizer's
`reviews/{stage}/round{N}/synthesis-return.md`) use it instead of the
generic name. `index.json` row: `type: "other"`, `schema: null`, `stage` =
producing stage, `produced_by` = role. Full convention:
`core/skills/_shared/worker-report-persistence.md`.
### `browser/` — browser evidence packs ([org-internal #4497] N-03)
A browser-debug session writes one evidence pack per session under
`browser/{session-id}/` (contract `browser-evidence-4486/shared/pack-manifest-v1`,
frozen 2026-09-10 — `<harness-package>/src/browser/evidence-pack.ts` is the writer):
```
browser/{session-id}/
├── manifest.json # PackManifest: env probe (shared/env-probe-v1),
│ # replay metrics {attempted, succeeded, failures[]},
│ # artifact rows {kind, path, sha256_16}
├── trace/ # Playwright trace zips (recorded sessions)
├── screenshots/*.png # copies — the .playwright-mcp/ layer is additive-not-replaced
├── console-errors.jsonl # one sanitized entry per line
└── network-failures.json # sanitized failure summary
```
Write boundary invariants (the graph's single mandatory sanitize point,
`shared/sanitize-api-v1`): every textual capture passes `sanitizeForEvidence`
before hitting the disk; entries whose URL fails the navigation allowlist are
excluded from the pack; the write is non-blocking (the writer returns a handle
before the first byte lands). `index.json` row: `type: "browser-evidence"`,
`path: "browser/{session-id}/manifest.json"`, `stage` = producing session's
stage (`implement`/`verify`), `produced_by: "Tool"`.
### `{stage}/` — per-stage transient artifacts
Added incrementally by each skill as it migrates its transient output to Tier 1.
Conventions established by this foundation:
- Structured data → `.json` (schema-validated).
- Prompt files passed to subagents via `prompt_file` → `.md`.
- File naming mirrors the Gitea wiki namespace so a Tier 1 path and its (former
/ future) Tier 2 mirror are visually paired, e.g. on-disk
`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-COR.json`. The
corresponding `index.json` `artifacts[].path` value strips the `{slug}/`
prefix (it is relative to `{active-slug}/`): `reviews/{stage}/round{N}/findings-COR.json`.
- A stage that has not yet migrated continues to write to Gitea (Tier 2) per its
existing skill body — the two tiers coexist during the migration.
**Migrated stages ([org-internal #1988]):**
| Stage | Skill | Tier 1 artifacts | Schema | Notes |
|---|---|---|---|---|
| `review-code` | review-code | `reviews/code/round{N}/findings-{DIM}.json` (raw dimension findings), `reviews/code/round{N}/task-synthesizer.md` (synthesis prompt) | `reviewer-output.schema.json` (findings) | synthesis comment + commit status stay Tier 2 |
| `review-roadmap` | review | `reviews/roadmap/round{N}/findings-{DIM}.json` | `reviewer-output.schema.json` | legacy target archived ([org-internal #3072] phase 3) — row kept for historical bundles |
| `review-design-space` | review | `reviews/design-space/round{N}/findings-{DIM}.json` | `reviewer-output.schema.json` | legacy target archived ([org-internal #3072] phase 3) — row kept for historical bundles |
| `review-iteration-plan` | review | `reviews/plan/round{N}/findings-{DIM}.json` | `reviewer-output.schema.json` | legacy target archived ([org-internal #3072] phase 3) — row kept for historical bundles |
| `review-dag` | review | `reviews/review-dag/round{N}/findings-{DIM}.json` | `reviewer-output.schema.json` | synthesis comment + commit status stay Tier 2 |
| `audit-process` | review | `reviews/audit-process/round{N}/findings-{DIM}.json` | `reviewer-output.schema.json` | synthesis wiki page + commit status stay Tier 2 |
| `requirements` (drafts) | requirements-elicitation | `requirements/` (schema-less `.md`/`.json`) | — | legacy skill archived ([org-internal #3072] phase 3) — historical bundles only |
| `design` (drafts) | design | `design/` (schema-less or `design-detail.schema.json`) | `design-detail.schema.json` (moved to `<instance-root>/archive/schemas/` [org-internal #3072] phase 3) | legacy skill archived ([org-internal #3072] phase 3) — historical bundles only |
| `plan-iterations` (drafts) | plan-iterations | `plan-iterations/` (schema-less or `plan-items.schema.json`) | `plan-items.schema.json` (moved to `<instance-root>/archive/schemas/` [org-internal #3072] phase 3) | legacy skill archived ([org-internal #3072] phase 3) — historical bundles only |
| `roadmap` (draft contracts) | roadmap | `roadmap/contracts/{name}.json` | `draft-contract.schema.json` (moved to `<instance-root>/archive/schemas/` [org-internal #3072] phase 3) | legacy skill archived ([org-internal #3072] phase 3) — historical bundles only |
Every review/audit stage additionally maintains a per-stage lifecycle file
`reviews/{stage}/status.json` (schema `review-status.schema.json`) — live
stages: `review-code`, `review-dag`, `audit-process`; legacy bundle stages
(`review-roadmap`, `review-design-space`, `review-iteration-plan`) maintain
the same file in historical bundles only. It is initialized by the
Orchestrator before round 1, appended by the Synthesizer each round, and
finalized with `converged` at Phase E. The Orchestrator appends an
`index.json` row for it (`type` = `review-status`, `stage` = `{REVIEW_TYPE}`,
`produced_by` = `Orchestrator`).
Stages NOT yet migrated (none — all target stages have been migrated or have opt-in Tier 1 affordances): reviser task prompts (wiki pages) remain Tier 2 in this slice; synthesis prompts for `review-artifact` / `audit-process` targets follow the shared Phase B mechanics (Tier 1 for all `tier1-local` skills).
## Archive-at-close
Closed by the **Verifier** at verify PASS (or by the Orchestrator for flows
without verify). The archive step is the ONLY point at which active-run content
enters git history:
1. Set `meta.json.state = "archived"`, record `closed_at` and `close_commit_sha`.
2. Write `archive/{slug}.json` — a compact bundle carrying `meta` + the
`index.json` manifest (the durable trace; bulk artifacts are summarized by
digest, not copied wholesale, to keep the archive small). One exception
([org-internal #2600]): when `working-notes.md` exists, its final content is inlined as
the bundle's top-level `working_notes` field — the narrative memory is the
single artifact whose content (not just digest) rides in the bundle,
feeding later retro probes and the notes-injection degradation path
([org-internal #2601]). The bundle is validated by `runs-bundle.schema.json`.
Offline review verifiability (TD/TRC-F006, [org-internal #2688]): for each review stage
the run executed, also embed a top-level `review_history` entry in the
bundle — `{stage, rounds, final_verdict, per_round[]}` (per-round:
round/overall_verdict/blockers/majors/minors) — derived from that stage's
live `reviews/{stage}/status.json` `history[]`. Closed-run probes can then
reconstruct round counts and verdicts from the bundle alone, without the
removed workspace or artifact-path reconstruction ([org-internal #2591]).
3. `git add archive/{slug}.json` and commit on the workflow branch (rides into
main via the `--no-ff` merge).
4. Remove the `{active-slug}/` workspace from the worktree (its durable record
now lives in the archive bundle).
Steps 14 collapse into ONE command ([org-internal #3642]): `bash script/archive-run.sh
{slug} [--pr N]` — run from the session worktree, it resolves the PR by branch
head, generates + validates the bundle via `gen-run-bundle.ts` (recording
`closed_at` / `close_commit_sha` and the `--branch` workflow branch), commits
it with a hook-conforming `[{chunk}][iter-N] chore(runs): …` message, removes
the workspace, and tears the worktree down unless invoked from inside it. The
`## 工件索引` archived remark (issue-checklist-sync.md § 归档动作) remains an
agent action — content judgment, not scripted.
Tier 2 (Gitea) remains the **system of record** for decisions (verdicts, issue
status, PRs, frozen contracts). The Tier 1 git archive is the **reproducibility
trace** — enough to reconstruct what a run produced, without re-paying the
Gitea tax.
## Relationship to existing mechanisms
- **`/tmp/octopus/` embryo** — `review-code` ([org-internal #1988] MVP) now writes its
synthesis prompt under `<runs-root>/{slug}/reviews/code/` instead of
`/tmp/octopus/synthesis_task_*`. Other skills still using the `/tmp/octopus/`
embryo coexist until their own migration iteration.
- **`## 工件索引` issue comment (Tier 2)** — unchanged; `index.json` (Tier 1) is
its agent-side parallel, not a replacement.
- **compact** — `compact` never deletes local files, so `index.json` survives
compaction and is the designated re-read entry for a compacted run.
- **worktree discipline** — all subagents of one workflow share its worktree, so
they share `<runs-root>/{slug}/` directly (no cross-worktree coordination
needed for Tier 1; that coordination lives in Tier 2).