CRUMB a card from devarno-cloud

Graph Topology Snapshot

kahn beginner 4 min read

ELI5

A floor plan that travels with the work order — the shape of the assembly line on the day the order ran, photographed and filed alongside the logbook so any later reviewer sees what was actually built against, not what’s been redrawn since.

Technical Deep Dive

contracts/schemas/graph.schema.json describes the static DAG that KAHN Scope renders alongside the event log. The orchestrator snapshots it into .kahn/archive/runs/<run_id>/graph.json once at run_start, closing open question O-3 (replay renderability).

Top-Level

FieldTypeConstraint
versionconstexactly 1
nodesarrayminItems: 1

Node Shape

classDiagram
class GraphDoc {
+int version
+Node[] nodes
}
class Node {
+string id
+string deliverable
+string[] depends_on
+string[] touches
+bool parallel_safe
+string[] done_when
+int max_ralph_iters
+int estimated_loc
}
GraphDoc --> Node : 1..*

Field Constraints

FieldConstraintNote
id^[a-z0-9][a-z0-9-]{0,63}$kebab-case, stable across runs (I-10)
deliverableminLength: 1Human-readable goal sentence
depends_onunique items, same id pattern[] = root
touchesunique stringsFile globs/paths driving conflict detection
done_whenminItems: 1Each item is a non-empty shell command
max_ralph_itersint ≥ 1, default 6
estimated_locint ≥ 0Optional planning hint

Snapshot Discipline

  • Read at run_start from <graph_path>.json; written verbatim to <archive>/runs/<run_id>/graph.json.
  • Live graph.json may change after the run; the snapshot is immutable for the run’s lifetime.
  • A version bump (anything other than 1) is a breaking change — KAHN bumps the wire path prefix when this happens.

Key Terms

  • deliverable → The “what should be true when this node converges” sentence rendered in the node drawer.
  • touches → Conflict-detection input for the scheduler; not the same as a real filesystem write list — the node may touch fewer files in practice, but never more.
  • estimated_loc → Optional planning hint, never enforced.

Q&A

Q: Can a graph have a single node? A: Yes. nodes: minItems: 1 permits it; the cycle guard only requires at least one root, which the lone node trivially is.

Q: What if the live graph.json drifts between run_start and a later replay? A: The replay reads the archived snapshot, not the live file. Replays remain renderable.

Q: Does the schema enforce that depends_on IDs exist in nodes? A: No — only the regex pattern. The orchestrator’s cycle guard and ready_nodes() rely on the IDs lining up; an unknown dep silently keeps a node pending forever.

Examples

A two-node DAG that pairs an isolated migration with a dependent service:

{"version":1,"nodes":[
{"id":"auth-table","deliverable":"v2 schema applied",
"depends_on":[],"touches":["migrations/0012_auth.sql"],
"parallel_safe":true,"done_when":["psql -c 'select 1 from auth_v2'"]},
{"id":"auth-service","deliverable":"service reads v2",
"depends_on":["auth-table"],"touches":["src/auth.ts"],
"parallel_safe":true,"done_when":["pnpm test src/auth.test.ts"]}
]}

neighbors on the map