28 KiB
name, description, triggers, role
| name | description | triggers | role | |||||||
|---|---|---|---|---|---|---|---|---|---|---|
| analyze-dag | 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). |
|
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)
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 atest_id∈{ file-path::test-name | MANUAL | BENCH:<script> }(the plan-iterations §6 SDD→TDD handshake; no other values). Entries MAY beNFR:-prefixed (product NFRs — written into node AC as enumerated storage; they carry noREQ-Fnumber 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 (taskdone, milestonegreen; same-session edges do not block).greenis milestone only (a task node's terminal state isdone).size_attrs— decomposition-time measured inputs; the DAG-leveldag_metricsaggregates them.estimated_hours/estimated_sessionsare required only fortype: tasknodes (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 byestimated_sessions(1 session ≈ 8h).
Edge Schema (spec-03 §2)
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 totrueedges (spec-02 §1 D-05). Same-session edges arecross_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, feedingcontract_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)
- Acyclicity — the directed graph must have no cycle. A cycle = BLOCKER.
- Milestone weld rule — any node with
cross_session_in ≥ 2(≥2 cross-session in-edges converging on it) MUST have a milestoneMwelded 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, noacceptance_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
readybefore M isgreen. A convergence point with no downstream consumer is a sink milestone (in-edges only).
- (a) all cross-session in-edges terminate at
- Granularity floor — every
type: tasknode's implementation estimate is carried bysize_attrs.estimated_hours/estimated_sessions(milestones omit both). Judged byestimated_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 — iscore/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:
- 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-Fnumbers and are NOT registered as rows — they are written into nodeacceptance_criteriaasNFR:-prefixed entries (enumerated storage), so "every NFR ≥1 node" is mechanically checkable via node-AC enumeration. refs_byis 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):
- 类目覆盖矩阵 (coverage matrix) — the fixed category set (≤10
categories, extracted from legacy
<instance-root>/archive/checklists/requirements-analysis.mdCMP/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 nodeNFR: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.
- 已覆盖 — the category has registered demand (registry
- 排除账本 (exclusion ledger) — one
E-nrow 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:
- 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)——合法,不得更低
MANUALis a legaltest_idvalue — 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_refsnon-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_splitoversize 信号处理——触发重派生 + 无条件重跑 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):
- Read the
dag:block'sroute.preflightarray (live yaml, repo root). - For each item: run its
itemself-check against the Epic's scope / breadth matrix before writing the DAG (items are typically recurring late-discovery categories, e.g. the retro-derivedREQ × late-discoveryclass — requirements that surfaced only after previous DAGs froze). - An item that fires becomes a 待确认 matrix row (or a probe node, above) — never silently dropped.
- 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):
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 ofchange_typeacross cross-session task edges only (breaking > additive > none). Milestone edges are excluded (treated asnone— no contract, nochange_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_depthis the review-dag review-resource parameter (reviewers / round cap, spec-06 §3) — analyze-dag computes it; it does not assignSize/*labels (DAG-routed tickets ignoreSize/*).
Page-Size Budget (spec-02 §2.6)
Threshold single source: the page-size budget and the
> 页尺寸自检: 超限signal are judged bycore/checklists/dag-single-gate.md(TOPO 页尺寸自检信号 row) — recalibrate there, not here. (One-line summary: the{epic-slug}/dagbody 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:
- Sink node AC detail into sub-pages
{epic-slug}/dag-nodes/{node-id}(sub-page naming{epic-slug}/dag-nodes/{node-id}is frozen); - Keep an in-page pointer
{node-id} → {epic-slug}/dag-nodes/{node-id}for each sunk node; - Put a return-link line on each sub-page, above its status line:
> 返回 [{epic-slug}/dag](…); - 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
- analyze-dag writes wiki page
{epic-slug}/dagwith first line:> DAG 工件状态: draft. - 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 工件状态: frozenand stamps versionv1. The confirmation is an OPTIONIZEDquestioncall (stagereview-dag, neverdestructive— freeze is a process gate, not a business decision): optionsFreeze 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 pagedraft, record the hold and the reason as a comment on the source Epic, and route back toreview-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. - 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 Registersection + 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/BugrelabeledKind/Featureper 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.
- root-cause notes (wiki page
- 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}/dagartifact.
References
core/templates/dag.md— the DAG artifact structure contract (this skill writes{epic-slug}/dagagainst 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 constraintsdag-pipeline/spec-04— single-gate TOPO/REQMAP/RELEASE dimensions (folded-row destinations)dag-pipeline/spec-06— dag_metrics derivation + thresholdsdag-pipeline/spec-07— dag.route landing row (entry skill, skip set, single-gate) + §4 landing listdag-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)