CommandGraph is a DSL and execution engine for declaring CLI command dependencies as a DAG. Two formats (.cg brace-delimited, .cgr indentation-based) produce identical ASTs. Development source lives in cgr_src/; cgr.py is the generated single-file artifact shipped to users. CLI is cgr.
Current notable features beyond the original core:
- Sub-graph inclusion as a step unit:
[deploy app] from ./deploy_app.cgr: - Wait gates:
wait for webhook "..."andwait for file "..." - Machine-readable apply output:
cgr apply FILE --output json - Execution timing persisted in state: per-step, per-wave, total run, latest bottleneck
cgr.py # Built single-file artifact, still shipped and tracked
cgr_dev.py # Dev entrypoint that imports cgr_src.cli:main()
cgr_src/ # Source modules used during development
MODULE_MAP.md # Quick lookup for where to make specific changes
ide.html # Web IDE frontend, served by `cgr serve`
MANUAL.md # Authoritative syntax reference
COMMANDGRAPH_SPEC.md # Formal language spec (PEG grammar, for code generators)
repo/ # Template repository (44 .cgr stdlib templates)
testing/ # Container demos (7 local demos)
testing-ssh/ # Container SSH demos (5 two-node demos)
benchmarks/ # Parallelism benchmarks
.claude/docs/ # Design docs (historical)
.cg → §1 Lexer → §3 Parser ─┐
├→ §2 AST → §4 Repo → §5 Resolver → waves
.cgr → §3b CGR Parser ────────┘ │
§6 Plan/Apply + §6b State
§7 DOT │ §8 HTML Viz │ §9 IDE+CLI
- Crash safety: A resource is written to
.stateonly AFTER execution completes. Missing from state = re-runs on resume. - Resume matrix:
success/skip_check/skip_when→ skip.warned/failed/timeout→ re-run. Missing → run. - Both formats in sync: The resolver doesn't know which parser produced the AST. Changes to one parser must be mirrored.
- Parallel desugaring: All constructs (
parallel,race,each,stage) desugar into gate→fork→join DAG patterns. No special executor concepts. - Cross-node dedup disabled: Identity hash includes node_name — same command on different hosts is never deduplicated.
- HTTP steps: HTTP verbs (
get/post/put/patch/delete) are first-class. They desugar into the same(rc, stdout, stderr)execution model. Local targets useurllib, SSH targets constructcurlcommands. Auth tokens are auto-redacted. - Sub-graph steps are outer-step state only: parent state tracks the outer step; the included graph keeps its own state journal. Do not assume child resources are flattened into the parent DAG.
- Wait gates are first-class execution types: webhook and file waits are not shell polling hacks. Preserve timeout, cancellation, and state semantics when changing them.
- Metrics live in the state journal:
_waveand_runrecords now coexist with per-resource entries. Tooling that compacts or reads state must preserve them.
- Heredocs break the
.cgrparser (useprintfor.cg) - No rollback (forward-only by design)
- Race cancellation doesn't kill already-started subprocesses
state testonly works for resources withcheckclauses- HTTP syntax only supported in
.cgrparser (not.cgyet) - Sub-graph inclusion is currently
.cgrstep syntax, not.cgsyntax wait for webhookstarts a lightweight local listener duringapply; it is intentionally local-only and not a distributed signal bus- State file isolation requires explicit flags: by default the state file is
FILE.cgr.state, keyed only to the graph file path. Use--run-id IDto salt the default state path or--state FILEto pin an explicit state file for concurrent parameterized runs.
# Compile check
python3 -c "import py_compile; py_compile.compile('cgr.py', doraise=True)"
python3 -c "import py_compile; py_compile.compile('cgr_dev.py', doraise=True)"
# Build graph
python3 cgr.py validate build.cgr
python3 cgr.py apply build.cgr
# Pytest suite
python3 -m pytest test_commandgraph.py -x -q
python3 -m pytest test_modularization.py -x -q
# Validate root feature-exercise example files
cgr validate nginx_setup.cg && cgr validate nginx_setup.cgr
cgr validate webserver.cg --repo ./repo && cgr validate webserver.cgr --repo ./repo
cgr validate parallel_test.cgr && cgr validate multinode_test.cgr && cgr validate multinode_test.cg
cgr validate system_audit.cgr --repo ./repo
cgr validate api_integration.cgr
# Container demos (if available)
testing/run-demos.sh # 7 local demos
testing-ssh/run-ssh-demos.sh # 5 SSH demos- Syntax should read like English.
.cgrusesfirst(notafter/before/needs). - Edit the development modules under
cgr_src/, then rebuildcgr.pyvia the self-hosted build graph withpython3 cgr_dev.py apply build.cgrorpython3 cgr.py apply build.cgr. - Use
MODULE_MAP.mdfirst when choosing which module to inspect for a change. - For composition, prefer sub-graph steps when the user wants another graph executed as one unit rather than flattened child resources.
- When touching state code, account for both resource entries and
_wave/_runmetric records. - When touching apply output, keep
--report FILE.jsonand--output jsonconsistent by deriving both from the same result data when possible. - Test with the root feature-exercise graphs after any change.
- Templates can be
.cgror.cg— all 44 stdlib are.cgr. Repo loader prefers.cgr. - Reference
MANUAL.mdfor syntax details,.claude/docs/design_doc_*.mdfor design rationale.