504 lines
28 KiB
Markdown
504 lines
28 KiB
Markdown
---
|
||||
|
|
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)
|