plan-dag

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

plan-dag

plan-dag

Render the current plan as a dependency DAG so sequencing, the critical path, and parallelizable work are visible at a glance. Output is a high-DPI PNG with status fills, an "available next" highlight, and a double-bordered close sentinel — rasterised from a styled graphviz layout through headless Chromium, then sent to the user via
SendUserFile
.
This skill is repo-agnostic. It assumes a GitHub-backed issue tracker with sub-issue links and dependency-language prose; it makes no assumptions about specific labels, area taxonomies, or repo-specific dev-process skills.
Why PNG and not HTML / ASCII? PNG is the only output that survives every chat surface the skill targets. ASCII / Unicode box-drawing loses column alignment when surfaces reflow whitespace or apply syntax highlighting, and it can't carry the color/status fills that make state legible at a glance. Self-contained HTML pages don't render inline in chat — they download as attachments, which defeats the point of an at-a-glance diagram. PNG renders inline, keeps color, and doesn't depend on the host's font or rendering quirks.
将当前计划渲染为依赖DAG,让任务顺序、关键路径和可并行工作一目了然。输出为带有状态填充、“可启动”高亮和双边框完成标记的高DPI PNG——通过无头Chromium将经过样式化的graphviz布局栅格化后,再通过
SendUserFile
发送给用户。
此技能与代码库无关。它假设使用基于GitHub的议题跟踪系统,带有子议题链接和依赖描述文本;不依赖特定标签、领域分类或代码库专属的开发流程技能。
为什么选择PNG而非HTML/ASCII? PNG是唯一能在该技能面向的所有聊天场景中正常显示的输出格式。ASCII/Unicode框线绘图在场景重新排版空白或应用语法高亮时会丢失列对齐,且无法承载让状态一目了然的颜色/状态填充。独立HTML页面无法在聊天中内联渲染——它们会作为附件下载,这违背了“一目了然”的初衷。PNG可内联渲染、保留颜色,且不依赖宿主的字体或渲染特性。

When to use

使用场景

  • After a
    what's next
    survey, to commit to a sequence.
  • When asked "what's left for #N" on an umbrella spec with sub-issues.
  • Before picking the next branch — to see what unblocks the most downstream work.
  • When a spec fans out into N sub-issues and the dependency edges aren't all linear.
Skip when:
  • A single-PR spec — the plan is the Plan section, not a DAG.
  • An unanswered design question — drawing a DAG before alignment is theater.
  • The "graph" is one linear chain of three or fewer nodes — a sentence is shorter than a diagram.
  • 在“what's next”调查后,用于确定任务顺序。
  • 当被问及某个包含子议题的总览规范“#N还剩下什么”时。
  • 在选择下一个分支之前——查看什么任务能解锁最多下游工作。
  • 当一个规范扩展为N个子议题且依赖关系并非全线性时。
无需使用的场景:
  • 单一PR的规范——计划就是Plan部分,无需DAG。
  • 未解决的设计问题——在达成共识前绘制DAG只是形式主义。
  • “图”是包含三个或更少节点的单一线性链——一句话比图表更简洁。

Inputs

输入参数

ParameterDefault
ScopeInferred — current branch's spec, the umbrella the user named, or the open issues just surveyed.
GranularitySpec-level; drop to sub-issue / PR level for an umbrella that has fanned out.
OutputHigh-DPI PNG (only target).
--out <path>
is required; emoji status indicators are on by default and can be turned off with
--emoji=off
if the rendering system lacks a color emoji font.
参数默认值
Scope(范围)自动推断——当前分支的规范、用户指定的总览议题,或刚调查过的开放议题。
Granularity(粒度)规范级别;对于已扩展的总览议题,可降至子议题/PR级别。
Output(输出)高DPI PNG(唯一目标格式)。
--out <path>
为必填项;表情状态指示器默认开启,若渲染系统缺少彩色表情字体,可通过
--emoji=off
关闭。

Workflow

工作流程

1. Discover    Pull issues / sub-issues / PRs in scope
2. Classify    Mark each node done / in-progress / open
3. Edge-build  Read dependencies from sub-issue links + body prose
4. Render      Emit JSON IR → PNG → SendUserFile + prose commentary
1. 发现    获取范围内的议题/子议题/PR
2. 分类    标记每个节点为已完成/进行中/开放
3. 构建边  从子议题链接和正文文本中读取依赖关系
4. 渲染    生成JSON中间表示(IR)→ 转为PNG → 通过SendUserFile发送并附带文本说明

1. Discover

1. 发现

Resolve the scope from the conversation, not from a generic crawl. Typical triggers:
  • An umbrella spec → walk its sub-issue list (
    mcp__github__issue_read
    with
    method: get_sub_issues
    ).
  • A track of follow-ups from a recent merge → use the priority + area filter the user implied.
  • The set of issues just surveyed → reuse that list verbatim, don't refetch.
For each node, capture:
  • Issue state (
    open
    /
    closed
    ) and
    state_reason
    (
    completed
    vs other).
  • Status labels (
    in-progress
    ,
    planned
    ,
    draft
    ) and
    priority:*
    .
  • Linked PRs — look for "merged in PR #N" in the body or comments.
  • Sub-issue list (only for umbrella nodes).
从对话中确定范围,而非通用爬取。典型触发场景:
  • 总览规范→遍历其子议题列表(调用
    mcp__github__issue_read
    并使用
    method: get_sub_issues
    )。
  • 最近合并后的后续任务跟踪→使用用户隐含的优先级和领域筛选条件。
  • 刚调查过的议题集合→直接复用该列表,无需重新获取。
为每个节点捕获以下信息:
  • 议题状态(
    open
    /
    closed
    )和
    state_reason
    completed
    或其他)。
  • 状态标签(
    in-progress
    planned
    draft
    )和
    priority:*
  • 关联的PR——在正文或评论中查找“merged in PR #N”。
  • 子议题列表(仅针对总览节点)。

2. Classify

2. 分类

Each node gets exactly one status:
StateDefinition
done
Closed +
state_reason: completed
, or merged PR.
in_progress
Open +
in-progress
label, or an open PR exists for it.
open
Open +
planned
/
draft
, no PR.
Don't render "blocked" as a separate status — the renderer derives it from the graph (any
open
node with a non-done predecessor) and styles it with a dashed muted fill. Conversely, an
open
node whose predecessors are all
done
is dual-encoded as the "available next" highlight.
每个节点仅对应一种状态:
状态定义
done
已关闭且
state_reason: completed
,或已合并的PR。
in_progress
开放且带有
in-progress
标签,或存在对应的开放PR。
open
开放且带有
planned
/
draft
标签,无对应PR。
不要将“blocked”作为单独状态渲染——渲染器会从图中推导该状态(任何带有未完成前置节点的
open
节点),并使用虚线浅填充样式。反之,所有前置节点均为
done
open
节点会被双重编码为“可启动”高亮。

3. Edge-build

3. 构建边

Edges come from three sources, in this order of trust. Don't draw an edge you can't cite from one of these — speculation pollutes the DAG.
  1. Sub-issue links (
    mcp__github__issue_read
    +
    method: get_sub_issues
    ). Edge direction is child → parent (prerequisite → dependent) — the parent closes when its children close, so the DAG flows toward closure.
  2. Explicit prose in issue body:
    Depends on #N
    ,
    Hard depends on #N
    ,
    Blocks #N
    ,
    Part of #N
    ,
    Closes #N
    .
  3. PR/commit references:
    merged in PR #N
    ,
    closed by #N
    ,
    PR-A → PR-B
    ordering inside an umbrella spec's Plan.
If a dependency is "obvious to me but uncited", the node label can hint at it; the edge stays out.
边来自三个来源,按信任优先级排序。不要绘制无法从以下来源引用的边——推测会污染DAG。
  1. 子议题链接
    mcp__github__issue_read
    +
    method: get_sub_issues
    )。边的方向为子节点→父节点(前置任务→依赖任务)——父议题在子议题完成后关闭,因此DAG流向完成状态。
  2. 议题正文中的明确文本
    Depends on #N
    Hard depends on #N
    Blocks #N
    Part of #N
    Closes #N
  3. PR/提交引用
    merged in PR #N
    closed by #N
    、总览规范Plan部分中的
    PR-A → PR-B
    顺序。
如果某个依赖“对我来说很明显但未被引用”,可以在节点标签中暗示,但不要添加边。

4. Render

4. 渲染

Emit a JSON IR matching the schema below, then invoke the renderer. Do not hand-draw boxes — the renderer produces deterministically correct layout that the AI's spatial reasoning will not match, especially with cross-edges and fan-out.
Schema:
json
{
  "nodes": [
    {"id": "288", "label": "MCP",    "status": "done"},
    {"id": "305", "label": "router", "status": "in_progress"},
    {"id": "306", "label": "cleanup","status": "open"}
  ],
  "edges": [
    {"from": "288", "to": "304", "source": "sub-issue"},
    {"from": "305", "to": "306", "source": "depends-on"}
  ],
  "close": "300",
  "critical_path": ["301", "305", "306", "307", "close"]
}
  • status
    {done, in_progress, open}
    ; defaults to
    open
    . Status is rendered as a fill color plus a leading emoji — do not embed markers in
    label
    .
  • edges[].source
    {sub-issue, depends-on, pr-link, closes, part-of}
    , required. This is the citation rule from Conventions made enforceable: no edge without a documented source on GitHub.
  • Every
    from
    /
    to
    resolves to a declared node id, or the literal
    "close"
    .
  • critical_path
    is optional; communicate it in prose alongside the rendered PNG, not inside the image.
Visual encoding. Status is dual-encoded by fill and a leading emoji so the eye picks up state before reading the label:
StateFillBorderEmoji
Donemuted greengreen
In-progressamberthicker amber🟡
Open + all preds done ("available next")cool bluethick blue🎯
Open + blockednear-whitegrey, dashed
Close sentinelwhitedouble🏁
The "available next" highlight is computed from the graph (open + every predecessor is
done
) — no IR field for it. Critical-path edges are not bolded: which path is "the" critical path is a caller judgement, and elevating it visually would conflate the recommendation with the graph's topology. Keep the critical path in
ir.critical_path
and let prose carry the next-pick recommendation under the rendered PNG.
The
--emoji
flag controls whether status emoji are emitted:
on
(default) shows ✅ / 🟡 / 🎯 / ⬜ / 🏁 emoji;
off
falls back to trailing ✓ / … text markers. Turn
off
if the target system lacks a color emoji font and the PNG shows tofu boxes.
Invocation. The renderer ships inside the skill. Use the path that matches how the skill was installed:
  • Project-scope install (default for
    npx skills add onsager-ai/dev-skills
    from a repo root):
    .claude/skills/plan-dag/scripts/plan-dag-render.py
  • User-global install (
    npx skills add -g …
    ):
    ~/.claude/skills/plan-dag/scripts/plan-dag-render.py
Pick whichever exists. If unsure,
test -x .claude/skills/plan-dag/scripts/plan-dag-render.py && echo project || echo global
.
bash
SCRIPT=.claude/skills/plan-dag/scripts/plan-dag-render.py   # project install
生成符合以下 schema 的JSON中间表示(IR),然后调用渲染器。不要手动绘制方框——渲染器会生成确定正确的布局,这是AI空间推理无法匹配的,尤其是在处理交叉边和分支时。
Schema:
json
{
  "nodes": [
    {"id": "288", "label": "MCP",    "status": "done"},
    {"id": "305", "label": "router", "status": "in_progress"},
    {"id": "306", "label": "cleanup","status": "open"}
  ],
  "edges": [
    {"from": "288", "to": "304", "source": "sub-issue"},
    {"from": "305", "to": "306", "source": "depends-on"}
  ],
  "close": "300",
  "critical_path": ["301", "305", "306", "307", "close"]
}
  • status
    {done, in_progress, open}
    ;默认值为
    open
    。状态通过填充色和前置表情渲染——不要在
    label
    中嵌入标记。
  • edges[].source
    {sub-issue, depends-on, pr-link, closes, part-of}
    ,为必填项。这是《约定》中引用规则的可执行形式:没有GitHub上的文档来源,就不能添加边。
  • 每个
    from
    /
    to
    必须解析为已声明的节点ID,或字面量
    "close"
  • critical_path
    为可选;在渲染后的PNG旁的文本说明中提及,不要放在图像内。
视觉编码:状态通过填充色和前置表情双重编码,让用户在阅读标签前就能识别状态:
状态填充色边框表情
已完成暗绿色绿色
进行中琥珀色较粗琥珀色🟡
开放且所有前置任务已完成(“可启动”)冷蓝色粗蓝色🎯
开放且被阻塞近白色灰色虚线
完成标记白色双边框🏁
“可启动”高亮由图计算得出(开放且所有前置节点为
done
)——无需在IR中设置字段。关键路径的边加粗:哪条路径是“关键路径”由调用者判断,视觉上突出它会将建议与图的拓扑结构混淆。将关键路径保留在
ir.critical_path
中,在渲染后的PNG下方通过文本说明给出下一个任务的建议。
--emoji
标志控制是否显示状态表情:
on
(默认)显示✅/🟡/🎯/⬜/🏁表情;
off
则回退到标签末尾的✓/…文本标记。如果目标系统缺少彩色表情字体且PNG显示豆腐块,可设置为
off
调用方式:渲染器随技能一起发布。使用与技能安装方式匹配的路径:
  • 项目范围安装(从代码库根目录执行
    npx skills add onsager-ai/dev-skills
    的默认方式):
    .claude/skills/plan-dag/scripts/plan-dag-render.py
  • 用户全局安装
    npx skills add -g …
    ):
    ~/.claude/skills/plan-dag/scripts/plan-dag-render.py
选择存在的路径。若不确定,可执行
test -x .claude/skills/plan-dag/scripts/plan-dag-render.py && echo project || echo global
bash
SCRIPT=.claude/skills/plan-dag/scripts/plan-dag-render.py   # 项目安装

SCRIPT=~/.claude/skills/plan-dag/scripts/plan-dag-render.py # global install

SCRIPT=~/.claude/skills/plan-dag/scripts/plan-dag-render.py # 全局安装

default: high-DPI PNG with emoji status indicators

默认:带表情状态指示器的高DPI PNG

"$SCRIPT" /tmp/plan.json --out /tmp/plan-dag.png
"$SCRIPT" /tmp/plan.json --out /tmp/plan-dag.png

emoji off — falls back to ✓ / … text markers in node labels

关闭表情——回退到节点标签中的✓/…文本标记

"$SCRIPT" /tmp/plan.json --out /tmp/plan-dag.png --emoji=off

The renderer needs `dot` (graphviz; `apt install graphviz` / `brew install graphviz`) on PATH for the SVG layout step, and `node` (≥18) + Playwright Chromium (`npm i -g playwright && npx playwright install chromium`) for the rasterisation step. Both checks run upfront and fail loudly with install guidance — there is no silent fallback to text or ASCII, by design (the formats removed had limitations the PNG output exists to avoid).

If the renderer aborts with `IR validation failed`, fix the IR — do not work around it by hand-drawing. The validation surface is the citation rule (`Conventions › No invented edges`) made executable.

**Response handling after running the renderer:**

- Send the PNG via `SendUserFile` so it renders inline as part of the assistant message.
- Add prose commentary below the file — critical path, next pickable node, sequencing rationale. The PNG carries the topology; the prose carries the recommendation.
- Do **not** re-render the same plan in another format and attach both — one DAG per response.
- For very wide graphs (>10 nodes with cross-edges) where the single PNG becomes unwieldy, split the plan into per-track DAGs (one renderer call per track) and render the cross-edges as a final short prose list, per the existing "Cross-edges" convention in §3.
"$SCRIPT" /tmp/plan.json --out /tmp/plan-dag.png --emoji=off

渲染器需要PATH中存在`dot`(graphviz;可通过`apt install graphviz`/`brew install graphviz`安装)用于SVG布局步骤,以及`node`(≥18)+ Playwright Chromium(`npm i -g playwright && npx playwright install chromium`)用于栅格化步骤。这两个检查会在启动时执行,若缺失会明确提示安装指南——设计上没有静默回退到文本或ASCII格式(这些格式存在PNG输出要解决的局限性)。

如果渲染器因`IR validation failed`中止,请修复IR——不要手动绘制来规避。验证环节是将《约定》中的引用规则(`Conventions › No invented edges`)转化为可执行的检查。

**运行渲染器后的响应处理:**

- 通过`SendUserFile`发送PNG,使其作为助手消息的一部分内联渲染。
- 在文件下方添加文本说明——关键路径、可选择的下一个任务、顺序理由。PNG承载拓扑结构,文本承载建议。
- 请勿以其他格式重新渲染同一计划并同时附加两种格式——每个响应只包含一个DAG。
- 对于非常宽的图(超过10个节点且带有交叉边),单个PNG会难以处理,可将计划拆分为按跟踪项划分的DAG(每个跟踪项调用一次渲染器),并根据§3中现有的“Cross-edges”约定,将交叉边作为最终的简短文本列表呈现。

Conventions

约定

  • No invented edges. If you can't cite the source (sub-issue link, body prose, PR header), don't draw it.
  • Summarize done nodes when dense. More than ~3 done nodes in a track? Collapse to
    Landed: #A #B #C ✓
    in prose rather than enumerating them in the graph.
  • One DAG per response. Don't render the same plan twice under different framings — pick the framing that answers the question that was actually asked.
  • End with the picked path. A DAG without a recommended sequence is a wall of boxes. Close with the critical path and the next pickable node in prose, framed so the user can redirect.
  • Don't editorialize inside the diagram. Commentary ("this looks risky", "we should reorder") goes in prose above or below, never inside a node label.
  • 禁止虚构边:如果无法引用来源(子议题链接、正文文本、PR标题),请勿绘制边。
  • 密集时汇总已完成节点:某个跟踪项中有超过约3个已完成节点?在文本中折叠为
    已完成:#A #B #C ✓
    ,而非在图中逐一列出。
  • 每个响应一个DAG:不要以不同框架重复渲染同一计划——选择能实际回答问题的框架。
  • 以选定路径结尾:没有推荐顺序的DAG只是一堆方框。在文本中以关键路径和可选择的下一个任务结尾,方便用户调整。
  • 不要在图中加入评论:评论(如“这看起来有风险”“我们应该重新排序”)放在图上方或下方的文本中,永远不要放在节点标签内。

Tests

测试

Tests live next to the renderer at
scripts/plan-dag-render.test.sh
and exercise validation (bad IR, cycle detection, own-id-prefix boundary cases), the styled DOT structural checks (status fills, available-next, blocked-dashed, close double border), and the end-to-end PNG smoke. The PNG smoke is auto-skipped when Playwright Chromium is unavailable so the validator coverage still runs in restricted CI. Run with the same install-aware path the renderer uses:
bash
undefined
测试用例与渲染器一同存放在
scripts/plan-dag-render.test.sh
,涵盖验证(错误IR、循环检测、自身ID前缀边界情况)、样式化DOT结构检查(状态填充、可启动高亮、阻塞虚线、完成双边框)和端到端PNG冒烟测试。当Playwright Chromium不可用时,PNG冒烟测试会自动跳过,以便在受限CI环境中仍能运行验证覆盖。使用与渲染器相同的安装感知路径运行:
bash
undefined

project-scope install

项目范围安装

.claude/skills/plan-dag/scripts/plan-dag-render.test.sh
.claude/skills/plan-dag/scripts/plan-dag-render.test.sh

user-global install

用户全局安装

~/.claude/skills/plan-dag/scripts/plan-dag-render.test.sh

Both forms are in `allowed-tools` so Claude Code doesn't re-prompt for permission. The test script internally `cd`s into the skill root and invokes `scripts/plan-dag-render.py` as a child process — that child invocation runs inside the script's own shell, not through Claude Code's permission engine, so it doesn't need a separate allowlist entry.

Requires `dot` (graphviz) on PATH; the PNG smoke additionally requires `node` + Playwright Chromium.
~/.claude/skills/plan-dag/scripts/plan-dag-render.test.sh

两种形式都在`allowed-tools`中,因此Claude Code不会重新请求权限。测试脚本会内部`cd`到技能根目录,并将`scripts/plan-dag-render.py`作为子进程调用——该子进程调用在脚本自身的shell中运行,不通过Claude Code的权限引擎,因此无需单独的允许列表条目。

需要PATH中存在`dot`(graphviz);PNG冒烟测试还需要`node`+Playwright Chromium。

Related skills

相关技能

  • The repo's spec-driven-development loop skill (e.g.
    onsager-dev-process
    ,
    duhem-dev-process
    ) — the parent / child / depends-on semantics the DAG visualizes.
  • The repo's
    issue-spec
    skill — how parent / child / depends-on edges are persisted on GitHub.
  • The repo's PR-lifecycle skill — how "in-progress" status flips on PR open / merge, which drives the amber fill on the rendered PNG.
  • 代码库的规范驱动开发循环技能(如
    onsager-dev-process
    duhem-dev-process
    )——DAG可视化所基于的父/子/依赖语义。
  • 代码库的
    issue-spec
    技能——父/子/依赖边在GitHub上的持久化方式。
  • 代码库的PR生命周期技能——PR打开/合并时“in-progress”状态如何切换,这决定了渲染PNG中的琥珀色填充。