Turn a sentence into a diagram, edit the diagram, then let Codex implement it one node at a time with tests that have to go green before the next node starts.
Not a single-shot code generator. A visible, editable, per-node iterative build loop you drive from a React Flow canvas.
Brief (a sentence) → Codex drafts a diagram
──────────────────
Diagram (edit on canvas) → nodes, edges, per-node intent/behavior/tests
──────────────────
Build Loop (Start) → topological order, note/group skipped
──────────────────
for each node:
codex writes src + tests ─────┐
↓ │
run vitest │ fail → feed failure into next attempt
↓ │ (up to 3 retries)
done or failed ←─────────┘
You own the diagram. The loop turns each node into code + tests, fails visibly, retries with the test failure in-prompt, and persists progress to .graphcoding/build-state.json so you can stop and resume.
- Brief → Diagram. One sentence, one button. Codex returns a structured diagram with nodes and edges; you edit it on a React Flow canvas with an Inspector panel.
- Per-node build loop. Topological order (ties broken by x-position),
note/groupshapes skipped. Each node goes implementing → done | failed. On failure, the test output is fed into the retry prompt — up to 3 retries. - Real tests without quadratic reruns. Every buildable node writes tests under
tests/<node-slug>/. Vitest/Jest workspaces run that node's focused test directory plus type checking; the terminalstartEndnode and final runtime verification run the complete suite. Other runners use the workspace's declared package-manager test script. Local binaries are resolved from the target workspace, never a sibling project. - Workspace isolation gate. After every Codex attempt, the server rejects external symlinks, host absolute path references, and invalid pnpm
node_modulespackage entries before treating the node as done. - Correct cross-node context. Each node's prompt includes the files produced by every prior completed node, so codex imports from them instead of re-implementing.
- Persistence. Diagram and build state both live on disk under
.graphcoding/(debounced), and auto-restore on workspace open. You can close the tab mid-build and come back. - Stop that actually stops. Clicking Stop aborts the in-flight fetch via
AbortControllerand invalidates a generation counter, so a late response can't overwrite the paused state.
Welcome screen is intentionally two interactive things: a path input with Open and a native-dialog link. Everything else (aux panel, bottom panel, editor tabs) stays hidden until a folder is loaded.
After opening a folder:
- Top: one workflow rail: Target → Generate → Edit → Build.
- Left: Project target, architecture blocks, active docs, and files.
- Center: React Flow canvas + a small bottom panel that starts with just
GRAPH JSON(SPEC / BUILD PROMPT / ITERATION tabs appear only once you've generated a spec). - Right: one detail panel for the currently selected workflow step. The top rail is the only place that switches Generate / Edit / Build.
Spec generation is deliberately collapsed into an optional <details> because the Build Loop does not read the spec — spec is for human review and export.
Requirements:
- Node.js 18+ and npm
codexCLI logged in (codex login statusshould return OK)- A workspace folder you're okay with codex writing into (the sandbox is
workspace-write)
Install and run:
npm install
npm run dev # concurrent server (8791) + vite client (5173)
# or separately:
npm run dev:server
npm run dev:client
# Need a different port? (e.g. 8791 is taken on your machine)
GRAPHCODING_PORT=9100 npm run devOpen http://localhost:5173, type a project folder path, click Open, run the Target wizard that auto-opens, then use Generate to draft the diagram. When the diagram is drafted and edited, switch to Build → Start Build Loop.
Folder /Users/you/Documents/my-messaging-app (empty is fine)
Harness preset SaaS Web App (or whichever fits)
Brief Local-first messaging app with thread list,
chat view, and a calculator popup that can
insert results into the chat.
Expected result ~8–10 nodes; loop builds each with tests.
First node takes the longest (scaffolding).
The first node bootstraps package.json, tsconfig.json, vitest.config.ts, and installs deps. Subsequent nodes only add their own files and tests.
src/
App.tsx main shell; build-loop driver; persistence gates;
React Flow canvas + bottom panel (GRAPH JSON / SPEC tabs)
components/ ExplorerPanel, RunPanel, InspectorPanel, BuildLoopPanel,
WorkspaceSetupModal, DiagramNode, DiagramEmptyState,
LiquidGlassControls
lib/ diagram, harness, workspace, types
styles/app.css VS Code-styled layout, 3-column workbench
server/
index.mjs express API on :8791 (override with GRAPHCODING_PORT)
/api/health liveness probe ({ ok: true })
/api/auth/status fresh codex login check (bypasses 30s cache)
/api/ai/diagram brief → structured diagram (gpt-5.6-sol, high)
/api/ai/spec diagram → structured spec (gpt-5.6-sol, high, optional)
/api/ai/build-order topological sort (note/group skipped)
/api/ai/build-node codex workspace-write + vitest + retry
/api/build-state/save persist .graphcoding/build-state.json
/api/build-state/load auto-restore on workspace open
/api/workspace/validate-isolation external-link/path guard
/api/workspace/runtime-verify assembled-app runtime check (see below)
/api/workspace/* open-folder, read-file, write-artifacts
State lives in three layers:
| piece | react state | localStorage | disk (.graphcoding/) |
|---|---|---|---|
| diagram (nodes + edges) | ✓ | ✓ | diagram.graph.json (debounced 400ms) |
| build-state (per-node status) | ✓ | — | build-state.json (serialized, atomic writes on every transition) |
| brief text | ✓ | ✓ | — |
| harness config | ✓ | — | harness.json (via write-artifacts) |
On workspace open, disk wins for diagram (falls back to localStorage on read failure), and build-state is restored with running:false, paused:true so nothing resumes without an explicit user click.
Per-node vitest proves each module in isolation, but it does not prove the assembled app actually boots. POST /api/workspace/runtime-verify (runWorkspaceRuntimeVerification in server/index.mjs) runs a separate end-to-end pass over the whole workspace and returns { ok, result } where result.status is passed | failed plus the collected checks, failures, and captured stdout/stderr.
The phase runs, in order:
- Isolation gate (before). Same external-symlink / host-path / pnpm-entry guard as the per-node loop, re-run against the assembled workspace.
- Dependency sync. Installs the workspace's own dependencies (skipped with a reason when already satisfied).
- Quality scripts. Requires
test,build, and every quality script enabled by the harness (lint,typecheck,e2e). A missing required script fails closed; optional missing scripts are explicitly recorded as skipped. - Dev-server smoke.
runDevServerSmokeboots the workspace'sdevscript on an allocated loopback port and requires a mountable HTML readiness response, not merely a non-empty HTTP 200. The temporary server is always stopped after evidence is collected. - Isolation gate (after). Re-checks isolation once the app has been installed and booted.
Only fully wired Node targets are selectable: SaaS Web App and Agent Tooling. Python/FastAPI, Tauri/Rust, and Flutter/Dart remain visible but disabled until complete runtime adapters exist.
Things that broke during real E2E testing and got fixed:
- Vitest resolution leaking to sibling projects.
npx vitestvia pnpm's shared store was picking up a neighbor repo's vitest.detectTestRunnernow invokes<cwd>/node_modules/.bin/vitestdirectly when present. - Stop not stopping. In-flight fetch kept going; late response overwrote paused state. Fixed with
AbortController+ abuildGenRefgeneration counter; every await re-checksisCurrent(). - Stale per-node context. The driver kept a local snapshot of
recordsthat never updated; node N always sawpreviouslyBuilt = []. Fixed by mirroring everyupdateNodeRecordinto the local snapshot atomically. - File diff missing modifications. The
fileslist only captured new files. Replaced with content-hash snapshots before/after so modified files are reported even when mtimes collide. - Path-escape inconsistency.
/api/build-state/save|loadand/api/ai/build-nodebypassedensureWithinRoot; file read/write APIs also needed realpath checks so existing symlinks cannot escape the workspace. These now route through root + realpath guards. - External dependency leakage. Codex once linked
node_modulesto a sibling project. Build-node now runs a workspace isolation gate after every attempt and feeds violations back into the retry loop instead of silently passing. - Reload hang.
reloadNativeWorkspaceerrors inside the loop used to leaverunning:trueforever. Now wrapped in asafeReloadNativeWorkspacethat surfaces a notice and lets the driver continue. - Cross-workspace diagram overwrite. Opening workspace B right after A could write A's nodes into B's disk file. A
hydratedKeyRefnow gates the save effect: only writes after the load effect has confirmed the current key. - Event-loop stalls from synchronous subprocesses. Codex authentication and native folder selection now use asynchronous child processes. Concurrent authentication requests coalesce into one check; ordinary internal checks retain a 30-second cache.
- Repeated full-workspace work. Isolation now combines symlink and external-path checks in one bounded-concurrency tree walk. Content snapshots stream hashes and reuse unchanged-file hashes instead of loading and hashing every file on every node.
- Quadratic test cost. A node runs its focused Vitest/Jest directory and typecheck; the terminal graph node and final runtime verifier still run the full suite, preserving the assembled-app gate.
- Persistence races. Build-state writes are queued in the client and atomically renamed on the server. Diagram saves are debounced, and artifact/install state writes use the same atomic replacement primitive.
- Unbounded memory and disk growth. File preview is capped at 4 MiB, text isolation at 2 MiB per file, workspace listing and snapshots at 50,000 files, captured process output at 2 MiB, hash caches at eight workspaces, and hourly artifact cleanup retains at most 500 files no older than seven days per runtime directory.
- Diagram generation and each node build take time —
codexrunsgpt-5.6-solwith reasoning efforthighby default. Override withGRAPHCODING_CODEX_MODELorGRAPHCODING_CODEX_REASONING_EFFORT. "testing"and"fixing"statuses are defined but the client doesn't currently stream intermediate server events, so the UI jumpsimplementing → done | failed. Server-sent events for in-progress retries is the next UX upgrade.- Fallback diagrams/specs are returned as explicit degraded failures (
ok:false, HTTP 502/503) and never unlock Build. - The declared support matrix is macOS plus Node-based SaaS Web App and Agent Tooling targets. Windows/Linux native folder dialogs and the disabled non-Node presets are not claimed as verified.
- Codex node mutations remain sequential by design because attempts share one writable workspace. Parallelizing those writes would trade speed for nondeterministic corruption; scanning, hashing, authentication checks, and persistence are parallelized or coalesced where their contracts permit it.
node_modules/ dist/ .tmp/ generated/ .graphcoding/ .env*
Before publishing, re-scan for secrets:
rg -n --hidden -S "(api[_-]?key|token|secret|password|sk-|ghp_|github_pat_)" . \
--glob '!node_modules' --glob '!.git' --glob '!dist' --glob '!.tmp' --glob '!generated'Unlicensed. Treat the repository as private until a LICENSE is added.