SuperAPI AI Directory

orca-graph

General & other

View repository

Updated 10-16 01:11

About this skill

Skill: orca-graph

Branch of orca-workflow: same propose → gate → plan → dispatch, but deps is DATA (graph.json), not guesswork in your head. Runtime: harness/scripts/orca-graph.py. Draw: fdk/tools/graph-viz.py (1 graph) + fdk/tools/graph-atlas.py (2D atlas). Default store llmwiki/graph/.

The engine lives in a separate repo https://github.com/Rheinmir/orca-graph (tests + VT eval suite + separate history), installed into ~/.orca-graph/repo/. The three paths above in overstack are shims pointing to that engine — commands typed the same as before. The shim reports "engine not installed" (rc 3) → run exactly the command it prints: curl -fsSL https://raw.githubusercontent.com/Rheinmir/orca-graph/main/install.sh | bash. Without overstack: call directly ~/.orca-graph/bin/orca-graph.

WHAT

Purpose and context

  • Purpose: break down a PLAN.md into a DEPENDENCY GRAPH (deps is DATA in graph.json), answer 5 questions with deterministic tools, dispatch by topo layer with locks + leases + generation, durable append-only state, and score every model answer against a rubric with sources.
  • Trigger (when to use):
    • There is a PLAN.md with ≥ 3 tasks and you need to know: what runs in parallel, what waits for what, how far along it is.
    • Dispatch multiple agents and need locks so 2 agents don't grab the same task / write the same file.
    • Work spans multiple sessions: state must survive crashes, no "remembering in your head".
  • Non-goals: NOT for incidents (→ orca-issue), not when there's no PLAN yet (→ /propose then /plan). Outside PRD v1.1 scope (stated plainly): durable pipeline item with outbox/backpressure, token-based layered fan-in, anchor registry, model routing by eval floor, scale proposal, seen ledger for discovery — see evals/vt-matrix.json of the engine repo: 13/28 VT scenarios record out_of_scope with reasons. Outside PRD scope (stated plainly, no pretending it exists): PostgreSQL ledger, secret gateway, money budget, LangGraph, integration queue/candidate hash, compensation for external effects, process-level sandbox (run agent commands in a separate container/VM). This tool is file-based for one machine; if you need the above, that's a different engine, not an orca-graph upgrade. (WRITE file allow-list — different from process sandbox — already exists, see run --strict in the Reference section — Daemon & control-room below and GH#162.)

Mental model

PLAN.md → build → graph.json (cache) + events.jsonl (nguồn chân lý, append-only) → ask (5 câu hỏi) → next/lock/set/run theo lớp topo → verify (+ qc) → audit → graph-viz / graph-atlas / control-room.

State machine per node: proposed → ready (mọi deps xong) → locked → dispatched → done | done_unverified | failed | unknown; blocked = HITL waiting on a human. Three dimensions kept separate: state (lifecycle) · verified (verify rc 0?) · fresh (upstream redone after you finish → stale, warning only). Rules borrowed from the Reprise PRD: idempotent op_key · CAS --if-rev · generation blocks stale results · lease expires → unknown not failed · no verify means no done.

  • qc (GH#163, optional — field **QC:** in PLAN.md, inside spec_hash as verify): the INDEPENDENT review command runs AFTER verify rc=0, NOT run by the same call that self-verified (by != "reconcile" at emit()) — separating the reviewer role from the role of the person/agent that just self-verified. If it fails then: via run/set done directly → emit() demote done→done_unverified (same as verify fail); via reconcile → conclusion ready (reconcile decides definitively on its own, does not leave it hanging at done_unverified). If **QC:** is not declared → behavior is exactly as before (backward compatible).

Input and output contract

Field Required? Meaning
In PLAN.md yes ≥ 3 tasks; line **Depends:**, **Verify:**, optional **Kind:**, **QC:**, **Resources:**; not yet present → call skill plan
In reason alongside should have **Depends:** Task 1 (data), Task 2 (preference) — reason ∈ data · contract · acceptance · effect_order · control (HARD edge) · preference (only by writing order)
In resource claim when shared **Resources:** db-migration(exclusive), api-x(shared), llm(capacity:2) — missing mode = exclusive
In store no default llmwiki/graph/
In --parent <gid>/<node> no attach subgraph to parent graph node
In agent + agent command when dispatching agent name for lock --by; command for run … --
Out <id>.graph.json + <id>.events.jsonl + .locks/ yes graph + persistent state
Out output build yes cycle, inferred deps (gợi-ý), write conflict on the same file between 2 parallel nodes
Out answer with label + source when the model answers score by rubric 1 / 0 / 0.3 / 0.5 / fabricated 0
Out HTML graph + 2D atlas + control-room.html yes 2 python drawing files + control-room generator
Out audit-log.jsonl when concluding the result reopens each source, fabrication = 0

Rules and capabilities

  • RULE-01 (MUST): Lock controls DISPATCH; file-writing side effects have a SOFT allow-list (GH#162) — see run --strict above. The allow-list does NOT replace real process isolation → a separate worktree per task remains an option when full isolation is needed (running commands, environment variables, side effects outside the filesystem).
  • RULE-02 (MUST): Do not dispatch node blocked (HITL) to a headless CLI — it will guess on the person's behalf and then go silent (lesson 250626, delivered ~1/5).
  • RULE-03 (MUST): Every answer from the model about the graph must go through answer with an openable label + source. No source → choose không-biết (0.3) instead of fabricating (0).
  • RULE-04 (MUST): Deps gợi-ý (inferred) must be confirmed by the user or declared **Depends:** before dispatching that layer.
  • RULE-05 (MUST): Do not delete/edit by hand events.jsonl; if wrong, append a correction event. graph.json is only a cache — if corrupted, build again, state is folded from events.
  • RULE-06 (MUST): Generated HTML: light/dark toggle + full path + terms with explanations (fdk rule) — the 2 drawing files already handle it, do not write HTML by hand.
  • RULE-08 (MUST): Resource contention is NOT a DAG edge (PRD v1.1 §23.2). Two independent tasks sharing an integration branch / DB schema / test port / API quota → declare **Resources:**, so that lock serializes at runtime; DO NOT add fake **Depends:** . A separate worktree does not exempt a claim.
  • RULE-09 (MUST): audit-edges only READS and only SUGGESTS. An edge lacking a reason (EDGE_UNJUSTIFIED) must be KEPT and the user asked; "no dependency seen" does not equal "proven independent". Removing an edge = user agreement → fix PLAN → rebuild.
  • RULE-10 (MUST): User requests ADDING work to a running graph → add-node (it modifies the original PLAN then rebuilds, plan_version + 1, history preserved). Forbidden to patch by hand graph.json; forbidden to create a new graph for the same goal.
  • RULE-07 (MUST): What it cannot do (say it plainly): hidden coupling not revealed in files; automatic rollback when an agent dies midway; two-way sync with the Orca ledger; atlas > ~500 nodes requires graphviz; allow-list only checks the filesystem, not network/process/secret access.
  • Capabilities: read PLAN and project files; write graph store append-only + file lock; spawn and monitor agent processes (heartbeat by pid); run verify/qc commands; read git diff to check allow-list; generate static HTML. One-way mirror to an external coordination ledger is optional.

Failure boundaries

  • No PLAN yet → blocked, call plan (or /propose then /plan); if it is an incident → switch orca-issue.
  • Cycle (check-cycles rc 2) or exceeding depth ≤ 6 / ≤ 20 nodes → tool refuses, blocked until scope is reduced.
  • Deps gợi-ý not yet confirmed → clarify with the user before dispatching that layer.
  • Node blocked (HITL) → wait for a human, do NOT dispatch headless.
  • Agent silent beyond lease → unknown (not failed) → reconcile; over 3 attempts → failed.
  • Result of an old gen or a different plan_version → STALE, do not publish.
  • No source for the answer → label không-biết, do not fabricate.

HOW

Main workflow

Step Type Inputs Action Outputs/exit Failure/next
W01 deterministic repo has harness Step 0: pre-work sweep pull-gate-sweep.sh sweep done no harness → skip
W02 judgment PLAN Step 1: PLAN exists yet; not yet → skill plan; fill in real Depends + Verify PLAN.md missing Depends → tool infers, suggested label
W03 deterministic PLAN.md Step 2: python3 harness/scripts/orca-graph.py build <PLAN.md> graph.json + cycle warning, file conflict conflict → add Depends or accept with reason
W03b deterministic graph.json Step 2b: audit-edges <id> — edges missing reason, preference edges can be dropped, critical path before/after finding table (read-only) EDGE_UNJUSTIFIED → ask user for reason, fill into Depends
W04 judgment graph Step 3: 5 questions using ask first, model later via answer with label + source answer has label no source → unknown
W05 effect graph.json Step 4: draw graph-viz.py → send HTML to user for approval (gate-create) HTML + gate unknown kind → B06
W06 effect graph approved Step 5: loop next → lock → set dispatched → dispatch → set done until next reports complete node done lease expired → B05
W07 deterministic graph done Step 6: audit <id> → graph-atlas.py llmwiki/graph/ → problem-tree if issues surface audit-log + atlas fabricated > 0 → report

Details of each step (source of truth for W01–W07; steps 0–6 correspond to W01–W07):

  1. Pre-work sweep as orca-workflow (pull-gate-sweep.sh) if harness exists.
  2. PLAN exists yet? Not yet → call skill plan. Template /plan has lines **Depends:** Task 1, Task 3 and **Verify:** <lệnh rc 0> — fill in real; if Depends is missing then tool INFERS from Consumes/Produces and attaches label gợi-ý.
  3. Build graph: python3 harness/scripts/orca-graph.py build <PLAN.md> → print cycle, inferred deps, and conflict writing the same file between 2 parallel nodes (⚠ → add Depends to force sequential, or accept with reason).
  4. Answer 5 questions — using tool first, model later:
    • ask <id> needs · ask <id> parallel · ask <id> deps <n> · ask <id> why <n> · ask <id> related.
    • Model-added part (meaning, semantic link to old graph, risks) → write using answer with label + source: answer <id> <n> --q why --label chắc|gợi-ý|không-biết --score S --evidence file:path:line edge:a->b event:<op_key> absence:<lệnh> --text "...".
    • Rubric (user finalized 2026-09-12): correct 1 · wrong 0 · don't-know 0.3 · suggestion with real source 0.5 · fabricated source 0. Not sure → say gợi-ý/không-biết, don't attach chắc.
  5. Draw + gate: python3 fdk/tools/graph-viz.py llmwiki/graph/<id>.graph.json → send HTML to user for approval (gate-create like orca-workflow).
    • Nodes on the graph are drawn by the task's kind (**Kind:** build|test|fix|research|docs|design|review|security|infra|data|migrate|deploy|release|cleanup|integration|wiki in PLAN.md, default build) — icon + shape + background color are looked up in the engine/kind-glyphs.json of the engine repo (~/.orca-graph/repo/engine/; the default ledger is SHARED across all graphs — edit/add via PR to that repo). Node border is still state (untouched).
    • kind not yet in the ledger → the tool auto-generates a temporary one (monogram + color/shape by hash, written to ~/.orca-graph/kind-glyphs.local.json — machine-local, doesn't pollute the engine's git clone) and prints a warning to stderr. Seeing this warning → ask the user whether they want /raise-issue to submit a new glyph into kind-glyphs.json ; don't add it to the default ledger on your own without asking.
  6. Run loop (repeat until next reports complete):
    next <id>                                  # node ready = chạy song song NGAY; blocked = HITL, KHÔNG dispatch headless
    lock <id> <n> --by <agent>                 # O_EXCL + lease (mặc định 90 phút); heartbeat <id> <n> khi chờ lâu
    set <id> <n> dispatched --op-key <uuid>    # rồi orca orchestration dispatch --task … --inject (brief NGUYÊN VĂN từ PLAN)
    set <id> <n> done --gen <gen> --op-key <uuid>   # gen lấy từ output lệnh dispatched; gen cũ → STALE, không publish
    
    Agent silent past lease → next auto-pushes the node back to unknown → reconcile <id> <n> (runs verify) before locking again. Max 3 attempts. Want to mirror to the Orca ledger: sync-orca <id> --run (one-way; the Orca ledger is runtime-global, stamps the project).
  7. Result: audit <id> (reopen each source; fabricating = 0; record audit-log.jsonl) → graph-atlas.py llmwiki/graph/ regen atlas → update problem-tree if a process problem is exposed.

Branches

ID Kind Guard Behavior Skip / failure Rejoin
B01 user_optional work needs to split a subgraph under a node build <PLAN> --parent <gid>/<node>; the parent node joins when EVERY child node is done exceeds depth 6 or 20 nodes → tool refuses W03
B02 conditional_required build again on a graph that already has plan_version + 1, node put into superseded[], node changes spec_hash already done → fresh=stale old plan_version result → STALE W06
B03 user_optional user wants to stop / continue / cancel control <id> pause, resume, cancel, status cancelled no resume → build new plan W06
B04 capability_optional want one command instead of 4 hand-typed commands run <id> <node> -- <lệnh agent> + daemon watch + control-room node missing verify → refuse headless W06
B05 recovery agent silent too long lease next push node back to unknown → reconcile <id> <n> run verify then lock again max 3 attempts then failed W06
B06 conditional_required kind strange not yet in kind-glyphs.json tool generates temporary glyph into kind-glyphs.local.json + stderr warning → ask user whether /raise-issue new glyph or not do not automatically add to the default registry W05
B07 user_optional want to mirror to Orca registry sync-orca <id> --run (one-way) — W06
B09 user_optional user requests adding work / "draw more nodes" when the graph already exists add-node <id> --title … [--depends t1:data] [--blocks t5] [--files …] [--verify …] [--kind …] [--resources …] → print + tN, rebuild, redraw HTML, give link to user generate cycle / > 20 nodes / strange dep → tool refuses, PLAN intact W05
B10 recovery lock report chờ RESOURCE another node currently holds a conflicting claim → do another ready node first, ask <id> waiting see who holds it; lock again when that node is done do not automatically unlock another agent's node W06
B08 conditional_required node declares **QC:** run independent QC after verify rc 0; fail → done_unverified (run/set) or ready (reconcile) not declared → old behavior W06

Validation and stopping

Deterministic: done only when verify rc 0 (and QC if declared); lease/generation/op_key/CAS decided by tool; check-cycles and lint/build --strict rc 2 on failure; audit reopen each source, fabricate = 0. Human needed: approve the HTML graph at the gate, confirm deps gợi-ý, handle node blocked. The run loop stops when next reports done; each node max 3 attempts.

Examples

  • Positive: PLAN 5 tasks, t2 and t3 both Depends t1 → build reports no cycle, no file conflict → ask t2 parallel returns t3 → next grants t1, when done grants t2 + t3 in parallel each with one lock --by, set done --gen → audit fabricate = 0 → atlas regen.
  • Boundary/failure: t2 and t3 in parallel both write src/api.ts → build prints ⚠ write conflict on the same file → add **Depends:** Task 2 to t3 to force sequential then rebuild; agent t3 silent too long lease → unknown → reconcile run verify before locking again.
  • Boundary: model wants to say "t4 exists to open the release lock" but there is no openable source → answer … --label không-biết (0.3), do not attach chắc.

Reference — Parent → child graph hierarchy (PRD Reprise Graph Engine §4)

  • build <PLAN> --parent <gid>/<node> → a graph attached to a node of the parent graph (containment tree, DIFFERENT from a dependency DAG). The parent node has child_graph, automatic join: only done_unverified when EVERY child node is done (invariant 4); a child with node blocked → parent blocked.
  • Hard limits: depth ≤ 6, ≤ 20 nodes/graph. Exceeding → the tool refuses, reduce scope instead of digging deeper.
  • Cross-graph deps: **Depends:** other-graph/t3 — satisfied when that node is done in the other graph. check-cycles [dir] merges every graph, prints the SPECIFIC cycle PATH (a/t2 → b/t3 → a/t2), rc 2 (PRD §5.3).
  • lint <id> — whether the leaf fully meets the contract (PRD §4.4): title · files · produces · verify · deps clear. build --strict rc 2 if verify/files are missing.

Reference — Justified topology & owned resources (PRD v1.1 §23)

  • Depends parses STRICTLY: a token that cannot resolve (a note inserted after the —, Task 1 và Task 2, a task that does not exist, a cross-graph dep in the wrong form <gid>/<tid>) is an ERROR build, not swallowed — an edge that silently disappears means a node is ready early with nobody noticing. Prevent it with , or ;; put notes on another line.
  • Edge reason: **Depends:** Task 1 (data), other-graph/t3 (control). The five hard classes (data · contract · acceptance · effect_order · control) cannot be dropped even if no artifact is passed — an approval gate carrying no payload is still a real edge. preference = only due to write order. Reason is NOT in spec_hash: attaching a reason to the running graph does not make an already-done node stale.
  • audit-edges <id> [--json] [--strict] — dry-run. Finding codes: EDGE_UNJUSTIFIED (Depends declared without a reason → keep) · EDGE_INFERRED (inferred deps) · PREFERENCE_REMOVABLE (proposed removal) · PREFERENCE_HAS_HIDDEN_CONSTRAINT (preference recorded but the downstream Consumes/Verify/QC mentions the upstream Task, file or output, or two nodes write the same file → keep; it compares STRINGS so coupling not exposed in words cannot be caught). Print the critical path counted by node (unit-weight, NOT duration), the number of layers and the ready-now sets before/after. --strict rc 2 while EDGE_UNJUSTIFIED.
  • Resource claims: keys are normalized by the tool (lowercase + normpath) so aliases with different spellings are still one resource. lock takes ALL claims or nothing (no holding A while waiting for B), scans every graph in the same store, runs under a single admission mutex. exclusive blocks every claim with the same key; capacity:N = the POOL SIZE of the key — each node currently holding the key (including shared) occupies 1 slot, nodes declaring different N take the smallest N (independent of lock order, shared cannot bypass the quota); shared pure is only blocked by exclusive or a full pool. A lease ending on a DIFFERENT graph is also swept by the reaper when lock/next (a claim does not get stuck across graphs); unlock by hand on a running node returns it to unknown — reconcile before locking again. build prints ℹ … cùng claim for a parallel pair — information, not an error.
  • ask <id> waiting — each node that has not run comes with ONE reason: data (waiting on upstream) · gate (HITL, control/acceptance gate, verify not yet green, unknown awaiting reconcile) · resource (claim is held, state who holds it) · queue (max_parallel full) · control (pause/cancel) · retry (out of 3 attempts) · none. next also print a line for node ready but not yet able to lock — ready ≠ admitted.
  • Topo layer (ask parallel) is a projection for explanation, not a barrier: a node with enough deps is ready immediately even if another node in the same layer as its upstream has not finished.

Reference — Identity, dry run, call budget (PRD v1.1 §24–27)

  • reconcile-items --expected A,B,C --verdicts rows.jsonl [--universe N] — join verdict (supported|refuted|inconclusive) by item_id, not by position. {A,A,B} with all 3 lines still MISSING C → rc 2. Line verdict: null or status ≠ ok = outcome missing C WITH ID (errored_ids), not filtered out, not counted as refuted; the same item having both a verdict and an error line → CONFLICTING_OUTCOME; an empty manifest requires --allow-empty; a missing verdict file is an error, not "0 lines". coverage splits two denominators: done/chosen and chosen/full set (unknown when unknown — do not report 100%). Use when fanning out a batch of items (e.g. /tc-run) then collecting results.
  • dry-streak --prev N --complete 0|1 --new N — a dry discovery round counts only when the round COMPLETES and there are no new candidates; an errored round resets to 0. Dry = stopping heuristic, not proof that everything has been scanned.
  • cost-envelope <id> [--reviewers N] [--max-attempts N] — count model CALLS before dispatch: planner 1 + worker/node afk + reviewer × nodes with QC + synthesis 1; max_calls multiply by the retry ceiling; expected record unknown when there is no measurement yet. Verify shell = 0 tokens but still costs CPU, report separately. Do not say "N agents" instead of the total calls.

Reference — Replan does not break history (PRD §10)

  • build again on an existing graph = plan_version + 1; a dropped node goes into superseded[] (keeping its last state), a node whose contract changes (spec_hash = title/files/deps/verify/produces) but has already finished → fresh=stale (warning only, do not roll back state).
  • Old result: set done --plan-version N with N ≠ current → STALE, do not publish (invariant 2).
  • run remember plan_version at dispatch: a replan happens WHILE the agent is running → the result is STALE, the node returns to ready to redo according to the new spec (rc ≠ 0).
  • add-node is a single-node replan: runs under the admission mutex (two parallel commands do not overwrite each other), sees PLAN exactly as the parser does (ignores code fences), every field must be ONE line, --blocks rejects a node that is locked|dispatched, PLAN must match the graph (for manual edits, build first); a new id does not reuse the id of a dropped node; --blocks t5 inserts a new node BEFORE t5 (wired into t5's Depends line, or add it if absent). Rebuilding a SUB graph but forgetting --parent still keeps the old parent link.

Reference — Control (PRD §8.3, invariant 11)

  • control <id> pause|resume|cancel|status. "Request" ≠ "stopped": pause_requested only becomes paused when there are no nodes left locked|dispatched; next/lock does not grant new nodes when there is no active. cancelled does not resume — build plan version new.
  • build --max-parallel N (default 4): lock rejects when the number of running nodes is already enough (PRD §12.1).

Reference — Daemon & control-room (PRD §8.4: lease 60 s · heartbeat 15 s · reaper 15 s)

  • run <id> <node> [--hb 15] [--lease-sec 60] [--strict] -- <lệnh agent> — replaces 4 hand-typed commands: short lock → dispatched → spawn command → heartbeat by pid → exit 0 becomes done (verify runs automatically), non-zero becomes failed. A node without verify is rejected headless (--allow-unverified to force). Automatically writes the directory into registry ~/.orca-graph/registry.json and automatically spawns the daemon if not present (ORCA_GRAPH_NO_DAEMON=1 to turn off, ORCA_GRAPH_HOME change home directory).
  • Write allow-list (GH#162): when in a git repo, run diff REAL files changed (tracked modified + new untracked, compared to RIGHT BEFORE spawn) against files declared by the node. Out of scope → by default only WARN (written into note of event done); --strict only then hard recovery (new untracked are deleted, tracked modified are git checkout back to HEAD). Not a git repo (e.g. store lies outside the repo) → automatically ignore, no error. Bookkeeping of orca-graph ITSELF (.graph.json/.events.jsonl/.locks/) is always excluded from the diff, not counted as "agent writes".
  • watch [--once] [--interval 5] [--idle-sec 600] — one daemon for the whole machine, lock daemon.lock by pid. Each pass only reads the dirs in the registry that still have running nodes (cost by number of running nodes, not by number of open projects; measured 84 ms/pass even including Python startup): reaper lease expires → unknown → if there is verify then reconcile immediately; print a live/dead table; if the registry is empty longer than idle-sec then exit automatically; if the graph changes then regen control-room.
  • fdk/tools/build-control-room.py → llmwiki/html/control-room.html — the first page that opens, data-first (only reads registry, graph.json, problem-tree, tokens.jsonl, audit-log): what is running on the whole machine + ceiling, progress of each graph + stuck nodes, open debt, today's cost + distance between self-scoring/audit. Auto refresh 5 s; daemon regen each pass and each time run starts/ends so the LIVE page needs no server. Tier 2 (server + click pause/cancel) and tier 3 (embed Orca) not done yet, only do when tier 1 used daily feels lacking.
  • Alive/dead ≠ right/wrong: the daemon only knows whether the process is still there or gone; done or redo is still decided by verify . A process alive but hung still has a green heartbeat — set **Verify:** idempotent and time ceiling per node.

Reference — Recap

/orca-graph = PLAN → graph.json has deps → ask answers 5 deterministic questions → dispatch by layer with lock/lease/gen → durable append-only state → answer/audit scores the model by rubric 1/0/0.3/0.5/fabricated=0 → HTML 1 graph + 2D atlas. Unexpected use case: run build on an old PLAN already finished to recheck whether back then there were 2 tasks writing the same file but running in parallel; or related to see which file of the old PLAN the new PLAN touches first before touching.