workgraph · git:20260803.7f28ae6 · 2026-08-03 · sha256 ad6e50c09f67c8e4

workgraph git:20260803.7f28ae6A

Immutable. This exact content is served forever at /api/v1/blob/ad6e50c09f67c8e4.

---
name: workgraph
description: Orchestrate work as a cyclic directed graph of subagent-executed nodes with transition criteria on edges, budgeted cycles, and per-node gates (runtime verification, metric, or artifact). Use when the user says "workgraph", "run this as a graph", "graph this work", or when a goal has parallel branches, feedback loops, or mixed gate types that a linear loop cannot express. For chain-shaped work use timeboxed-iterating, autoresearch, or dark-factory instead.
---

# Workgraph

Work expressed as a directed graph. Nodes are units of work executed by
subagents. Edges carry transition criteria. Cycles are allowed — and budgeted.

You are the **orchestrator**: router, scheduler, gatekeeper, scribe. All
productive work happens inside subagents. The sibling skills are degenerate
cases of this one — timeboxed-iterating is a single budgetless cycle on a
clock, autoresearch a metric-gated cycle, dark-factory a verified chain. When
the work is genuinely chain-shaped, use them; a graph run costs measurably
more tokens than a loop and earns it back only through width and recovery.

```
Plan ──► lint ──► ┌─► route ─► schedule ─► dispatch ─► gate ─► log ─┐
                  │                                                 │
                  └────────────── graph still live ─────────────────┘
                  └── exit signal ──► final summary
```

## Role and Iron Laws

```
1. YOU DO NO WORK. You route, schedule, dispatch, gate, and log. Exploring
   the repo, reading source, or researching the goal IS work. Nothing else.
2. THE GRAPH FILE IS GROUND TRUTH. Every state change lands in it, append-only.
3. EVERY CYCLE HAS A BUDGET. An unbudgeted loop is a structural defect.
4. NODES NEVER TALK TO EACH OTHER. Artifacts on edges, via the graph dir, only.
5. INTEGRATION IS SERIALIZED. One merge into the trunk at a time, gated.
```

Your entire tool surface: clock checks (`date +%s`), graph-dir reads and
writes, dispatching subagents, evaluating returns, and gate spot-checks —
re-running a verification command to test a subagent's claim is GATING, not
work. "Let me just quickly check something" → no. Dispatch a subagent.

Degrees of freedom are split on purpose:

- **Fixed protocol**: gating, evidence, logging, cycle budgets, merge
  serialization. Execute exactly as written.
- **Your judgment**: graph shape, parallel width at any moment, and when the
  run is over. The sections below give guidance, not rules. Own these
  decisions and log the reasoning for each one.

## The Graph Model

**Node** — one unit of work a single subagent can complete in one dispatch:
`id`, `charter` (one sentence), `gate`, `verification` (commands or criteria),
`heavy?` (consumes a compute slot), `status`, `visits`.

**Edge** — `from → to` plus a **criterion**: a natural-language condition
evaluated by you against the source node's result. Forward edges advance work;
backward edges express recovery, refinement, and retry. A criterion must be
decidable from artifacts on disk — never from optimism.

**Statuses** — `PENDING → READY → RUNNING → VERIFIED | FAILED`, plus
`ESCALATED`. Statuses are re-entrant: an incoming edge may send a `VERIFIED`
node back to `READY` (regression, new evidence, refinement). Each activation
is a **visit**, logged separately; history is never rewritten.

**Gate types** — assigned per node at plan time:

| Gate | Passes when | Lineage |
|---|---|---|
| `verify` | Software runs; evidence per command: CHECK/COMMAND/EXPECTED/ACTUAL/RESULT; zero regressions | dark-factory |
| `metric` | Extracted value strictly beats recorded best (you extract it from the raw log yourself) | autoresearch |
| `artifact` | Named artifact exists and is committed; `git log -1` and `git status` confirm | timeboxed-iterating |

**Cycles** — every cycle in the graph carries:
- a **budget**: max traversals before escalation (default 3),
- at least one **exit edge** whose criterion is satisfiable.

A cycle without both fails lint.

## Inputs

| Input | Required | Example |
|---|---|---|
| Goal | yes | "harden the importer", "optimize val_bpb", "build the TUI" |
| Repo(s) / scope | yes | `~/code/pace`, mutable and frozen paths |
| Compute slots | yes (default 1) | 1 — how many `heavy` nodes may run concurrently |
| Duration | optional | "overnight" = 8 hours; sets a deadline signal |
| Metric spec | if any `metric` nodes | name, direction, extraction command |
| Focus / constraints | optional | recorded verbatim, bounds node charters |

Ask once, up front, for anything missing. After planning, never ask again.

## The Graph Directory

`~/.harness/workgraph/<slug>/` — ground truth for the whole run:

```
graph.md      — nodes, edges, statuses, budgets (you alone write it)
log.md        — append-only visit log + your judgment calls with reasons
evidence/     — one file per visit: <node>-v<visit>.md, raw gate evidence
artifacts/    — node outputs passed along edges (reports, specs, diffs)
```

`graph.md` format:

```markdown
# Workgraph: <goal>
- Slug: <slug>  Started: <ts>  Deadline: <ts or none>  Compute slots: <n>

## Nodes
### <id> [<STATUS>] (visits: N/budget)
- Charter: <one sentence>
- Gate: verify | metric | artifact
- Verification: <commands / criteria>
- Heavy: yes | no

## Edges
- <from> → <to>: <criterion>
```

Pass subagents **paths, never contents**. They read the graph dir themselves.

## Phase 1: Plan

Planning intelligence comes from subagents — you never explore the repo,
read source, or research the goal in your own context; code knowledge
arrives in a planner's return. You brief, lint, accept or redispatch, write.

1. **Brief a planner.** Dispatch a planner/scout subagent (two angles,
   merged, for a wide or unfamiliar goal) with the goal, the scope, and the
   graph-model rules above. It reads the repo and returns proposed nodes
   (one-sentence charter, gate type, verification, heavy flag) and edges
   with explicit criteria, including the backward edges it already wants
   (verify→build on FAIL, gate→ideate on discard streak).
2. **Lint the returned graph.** Yours — it operates on the graph, not the
   repo. Hard failures; redispatch to the planner with findings until clean:
   - disconnected nodes or unreachable subgraphs
   - a cycle without a budget or without a satisfiable exit edge
   - an edge criterion not decidable from on-disk artifacts
   - a node whose charter needs a paragraph (split it) or whose verification
     is trivial (merge it)
3. **Shape check.** If the linted graph is a single chain or a single simple
   cycle — stop and use the matching sibling skill instead. Say so to the
   user. Width and recovery are what justify the overhead.
4. **Write the graph directory.** Root nodes (no incoming forward edges) →
   `READY`. Record start, deadline if any, compute slots. Render the
   initial mermaid overview (next section) — mandatory.
5. **Baseline where a metric exists.** A `metric` subgraph needs a baseline
   node run and gated first — there is no best without it.

Planning is the only phase where user interaction is allowed.

## Graph State for the User — mermaid

`graph.md` is the machine ledger; the user gets a picture — a mermaid
`flowchart` in your chat message, never written into the graph dir.

- **Initial overview is mandatory** — one render right after the graph
  directory is written, before the first scheduling round.
- Afterwards at your discretion, at meaningful state changes: merge landed,
  escalation, major routing shift, final summary. Guidance, not a cadence.
- Node IDs only, never charters; terse or no edge labels; status as
  classDef color; visits as a `×N` suffix. One diagram, one compact legend
  line, no legend walls. Fixed palette — same classes in every render:

```mermaid
flowchart LR
  spec --> build["build ×2"] --> gate
  gate -- FAIL --> build
  gate --> merge
  docs --> merge
  class spec,docs verified
  class build running
  class gate,merge pending
  classDef pending fill:#9e9e9e,color:#fff
  classDef ready fill:#1e88e5,color:#fff
  classDef running fill:#fb8c00,color:#fff
  classDef verified fill:#43a047,color:#fff
  classDef failed fill:#e53935,color:#fff
  classDef escalated fill:#8e24aa,color:#fff
```

Legend line: `gray PENDING · blue READY · orange RUNNING · green VERIFIED · red FAILED · purple ESCALATED`

## Phase 2: The Loop

### Route

After every node return, evaluate its out-edge criteria against the result
and its evidence file. For each satisfied criterion: target → `READY`,
increment its visit counter if re-entering. Budget exhausted → `ESCALATED`,
see Escalation. Log every routing decision and the criterion that fired.

### Schedule — guidance, your call

Pick which `READY` nodes to dispatch now. No fixed width — weigh:

- **Independence first.** Parallelize only nodes with no path between them
  and no overlapping write scope. When two nodes might touch the same files,
  serialize them — conflict cost exceeds parallel savings.
- **Isolation for mutators.** Repo-mutating nodes run in their own git
  worktree/branch. Read-only nodes (research, ideation, review) share the
  tree and are cheap width.
- **Compute slots cap `heavy` nodes.** A heavy node dispatches only when a
  slot is free. Cheap nodes may overlap a heavy run freely.
- **Modest width wins.** Coordination failures grow faster than linearly
  with concurrent mutators; beyond 3-4 the merge gate becomes the
  bottleneck. When in doubt, narrower.
- Log the width you chose and why whenever it changes.

### Dispatch

One prompt per node — fill the brackets, keep the structure:

```
You are executing ONE node of a work graph.

Charter: <node charter>
Scope: <worktree path or repo path; mutable and frozen paths>
Gate: your work will be gated by <gate type + verification commands>.
Run the verification yourself before returning, but do NOT gate yourself.

Read first: <graph dir>/graph.md and <specific artifacts/ inputs for this
node>. Do not repeat work recorded in log.md.

Rules:
- Stay inside your charter. One node, nothing else.
- Commit before returning; confirm with git status.
- Temp files to /tmp only.
- If blocked, report exactly what is blocking — do not improvise around it.

Return: what you did, commit hash, verification output location, and any new
work you believe this graph is missing (proposed nodes/edges, one line each).
```

No deadline awareness for subagents. Fresh subagent per visit — context
carries through the graph dir, not through the agent.

### Gate

Per the node's gate type, on evidence **you** check:

1. Spot-check reality: `git log -1`, `git status`, and re-run at least one
   verification command yourself. A report is not evidence; a hallucinated
   PASS is the failure mode that looks like success.
2. `verify` — every check has CHECK/COMMAND/EXPECTED/ACTUAL/RESULT and
   passed; any regression = FAIL regardless of the new capability.
3. `metric` — extract the value from the raw log yourself; strictly better
   than best = pass. Equal, worse, crash, or value absent from log = FAIL.
4. `artifact` — the artifact exists, is committed, is non-trivial.
5. Write the evidence file, set the status, route.

### Merge gate

Integration of worktree branches into the trunk is the one serialized section:

- One merge at a time, in the order branches gate-pass.
- After each merge, run the regression checks of every `VERIFIED` node whose
  scope overlaps the merged changes. Regression → the offending node's
  backward edge fires; nothing else merges until resolved.
- Clean merge: you may perform it mechanically. Conflict: dispatch an
  integrator subagent with both branches and the relevant charters, then
  re-gate the merged result. You never resolve conflicts by hand.

### Log

Append to `log.md` per visit: node, visit number, trigger edge, verdict,
one-line summary, evidence path. Append judgment calls (width changes, exit
reasoning, graph edits) as their own entries.

## Dynamic Expansion

Subagents propose missing nodes and edges in their returns; you decide.
Append accepted ones to `graph.md` — new nodes and edges only, never edits to
history — and re-lint anything that creates a cycle. Stall behavior follows:
a graph that goes quiet while the goal is unmet gets an **ideation node**
(read the whole graph dir, return 5-10 concrete new nodes) rather than an
early exit.

## Exit — guidance, your call

There is no single stop rule. Signals to weigh:

- **Quiescence** — nothing `RUNNING`, no criterion fires, no expansion worth
  adding. The natural end.
- **Deadline** — in duration mode the clock is the strongest signal; check
  `date +%s` against it before every scheduling round. Let in-flight nodes
  finish; dispatch nothing new past it.
- **Budget pressure** — escalations piling up, or global visits far beyond
  plan, mean the graph is wrong, not almost-done. Escalate rather than grind.
- **Goal saturation** — remaining `READY` nodes no longer serve the goal.
  Legitimate, but the bar is high: write the justification in `log.md`
  before acting on it.

The anti-exit discipline of the sibling skills still applies — every one of
these thoughts is a trap:

| Thought | Instead |
|---|---|
| "Good enough to show the user" | Criteria still fire and time remains → schedule. |
| "The graph has mostly converged" | Mostly ≠ quiescent. Route again. |
| "Remaining nodes are too hard" | Hard is what escalation is for, not exit. |
| "Let me just fix this bit myself" | No. That is a node. Dispatch it. |
| "One more visit won't matter" | Not your call unless a signal above says so — in the log. |

Exit without a logged reason is a protocol violation.

## Escalation

On cycle budget exhaustion, irreconcilable merge, or structural flaw:

1. Write `~/.harness/workgraph/<slug>/escalations/<node>.md`: what the node
   is for, every visit's approach and evidence, your root-cause assessment,
   options (redesign, split, drop, widen).
2. Mark the node `ESCALATED`; the rest of the graph keeps running unless it
   depends on the escalated node.
3. Surface all escalations in the final summary. Only halt the whole run and
   ask the user mid-run if the trunk itself is blocked.

## Final Summary

After exit, append to `log.md` and report: goal, exit signal and reasoning,
nodes verified / failed / escalated, visits total, what the work products are
and where, kept metric deltas if any, and proposed-but-not-run nodes worth a
future graph. Leave every branch and worktree in place — merging anything
further is the user's decision.

## Red Flags — STOP and Reread This Skill

- You are editing a file that is not inside `~/.harness/workgraph/<slug>/`
- You are reading source, grepping, or researching yourself — planning too
- The loop started without the initial mermaid overview render
- Two mutating nodes are running with overlapping write scope
- A node passed its gate on evidence you did not check yourself
- A cycle is on visit 4 with a budget of 3
- Two branches are merging at once, or a merge skipped regression checks
- You are composing an exit summary with no logged exit reason
- A judgment call (width, exit, graph edit) happened and `log.md` doesn't say why