Graft v3.7: Runtime Hardening
Graft is a graph-native language for AI agent harness engineering — you write .gft files that declare contexts, nodes, edges, and graphs, and the compiler generates a Claude Code harness structure with orchestration files. Development uses an adversarial debate harness where multiple AI agents independently analyze, critique, and converge on each implementation.
What v3.7 Adds
After five consecutive LSP-focused versions (v3.2-v3.6), v3.7 pivots back to the runtime. Two correctness gaps that had been deferred since v3.2-v3.3 are now closed: foreach source failure handling and multi-hop conditional edge routing.
Foreach Source Failure
When a foreach's source node fails and a fallback fires, the output gets stored under the fallback node's name. But foreach looks for the original name:
node Planner(model: sonnet, max_tokens: 2k) {
on_failure: fallback(BackupPlanner)
produces { steps: list }
}
graph Pipeline {
Planner
foreach(Planner.output.steps as step) {
Executor // looks for "Planner" output, finds nothing
}
}
v3.7 adds a fallback output alias: when a fallback fires, the output is stored under both the fallback name and the original name. The foreach also gains a skip guard — if the source produced no output at all, the body is skipped rather than iterating over undefined data.
Multi-Hop Conditional Routing
Conditional edges can now chain through up to 10 hops:
edge Router -> Analyzer {
when(Router.output.type == "code"): CodeReview
when(Router.output.type == "text"): TextReview
else: GeneralReview
}
edge CodeReview -> Output {
when(CodeReview.output.severity == "high"): DeepAnalysis
else: done
}
The runtime follows the chain with cycle detection via a visited set (not a depth counter — visited sets catch cycles on first revisit). done terminates the chain. The fallback alias pattern propagates through the chain, ensuring downstream nodes find outputs under expected names regardless of fallback invocations.
The Debate Outcomes
R2: Two Agents, Two Bugs, Zero Overlap
v3.7-R2 is the first MEDIUM round where both agents contributed independent findings that were both essential:
- A3-Skeptic found the fallback alias bug. A2 did not analyze the fallback+foreach interaction.
- A2-Pragmatist found the dangerous
?? ctx.inputfallback — foreach was silently iterating over program input when the source had no output. A3 focused on alias behavior, not input fallback.
Neither agent alone found both issues. This validates the dual-agent analysis model: it is not just "A3 finds bugs, A2 is overhead."
R3: Visited Set Over Depth Counter
A3-Skeptic advocated for a visited set over a depth counter for cycle detection in conditional chains. The argument: a depth counter of 10 allows a cycle of length 5 to execute twice before triggering. A visited set catches it on the first revisit. The convergence adopted A3's approach.
The reviewer then caught 3 items missing from the implementation: a depth-limit error (the chain was silently truncated at 10 hops), a depth-limit test, and a scope validation test for done as a target. Fixed in 1 debug cycle.
The Fallback Alias Pattern
The alias pattern (if (result.node !== requestedName) ctx.outputs.set(requestedName, result.output)) was designed in R2 for foreach and reused in R3 for conditional chains without modification. This organic cross-feature reuse — same pattern, different execution contexts — is a sign that the abstraction is at the right level.
Server.ts Extraction
R1 extracted ensureWorkspaceScan() and collectWorkspaceFileTexts() from 3 duplicated server.ts handlers and fixed findDeclNamePosition to use loc.length instead of a hardcoded keyword-length map. Result: server.ts dropped from 394 to 383 lines — the first time the LSP line count has decreased since its introduction in v2.2.
Process
Two new process improvements proposed:
- R-PROC-17: Treat the convergence spec as an implementation checklist. Verify each requirement before submitting for review.
- R-PROC-18: MEDIUM rounds introducing new error conditions must explicitly list error path tests. Implementers focus on proving features work; this forces them to also prove features fail correctly.
Budget accuracy was exact: ~12 calls budgeted, ~12 actual — the first exact hit since v3.5. The +1 NEEDS_CHANGES fix was absorbed by efficient DIRECT and TEST-ONLY rounds.
Stats
| Metric | Value | |--------|-------| | Tests | 832 (42 new) | | Ratchet decisions | ~225 | | Rounds | 4 (1 DIRECT, 2 MEDIUM, 1 TEST-ONLY) | | Agent calls | ~12 | | Bugs caught by debate | 3 | | Bugs caught by review | 3 | | NEEDS_CHANGES | 1 (R3, fixed in 1 cycle) | | First-try pass rate | 75% (3/4) | | New process rules | 2 (R-PROC-17, R-PROC-18) |
Try It
npm install -g @graft-lang/graft
graft compile myfile.gft
graft check myfile.gft
graft run myfile.gft --input '{"query": "hello"}'
Source: github.com/JSLEEKR/graft
Built with Claude Opus 4.6 via Claude Code's adversarial debate harness. ~12 agent calls across 4 rounds.