From 732d43e2981a69bafe70c2e1a969cdcad23fcaf6 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 20 May 2026 19:01:01 -0600 Subject: [PATCH] docs: updated docs with a cross-branch read gotcha --- Graph-Agents.md | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/Graph-Agents.md b/Graph-Agents.md index c4f5d4e..0e8e189 100644 --- a/Graph-Agents.md +++ b/Graph-Agents.md @@ -902,6 +902,47 @@ This means: need that pattern, sequentialize the work or write it as a single multi-step subgraph +### Cross-branch reads are not visible (gotcha) + +A consequence of independent state forks: a branch **cannot read a key +that a sibling branch writes in the same super-step**. Each branch +sees the state snapshot taken BEFORE the super-step started; sibling +writes only become visible at the next super-step (after the join). + +**Validator catches this at load time.** If any branch's templated +field references a state key that another sibling branch writes in +the same super-step, you get a load-time error like: + +``` +node 'rag_lookup' reads state key(s) `db_result` which sibling parallel +branch 'query_local_db' writes in the same super-step; parallel +branches see a state snapshot taken BEFORE the super-step and cannot +observe each other's writes. Move the dependent read to a later +super-step (or remove the cross-branch reference). +``` + +**Fix options:** + +- **Remove the dependency.** Drop the cross-branch reference from the + prompt/query/etc. so the branch operates on what was available _before_ + the super-step. +- **Sequentialize.** Put the dependent node in a later super-step: + `producer → reader_that_needed_producer_output` instead of + `[producer, reader_that_needed_producer_output]`. +- **Use the join.** Restructure so any node that depends on a sibling's + writes runs _after_ the join. The join sees the merged state. + +**Caveat (scripts).** The validator scans templated fields of typed +nodes (`prompt`, `query`, `instructions`, `question`, `default`, +`output`, `over`, and `state_updates` values). It does not scan +script bodies, since scripts read state opaquely via `GRAPH_STATE` / +`GRAPH_STATE_FILE` env vars. A Python script in a parallel branch +that does `state.get("sibling_written_key")` will silently see the +**pre**-super-step snapshot. The script's required `state_updates` +declaration (the validator's separate "parallel scripts must declare +writes" check) covers writes only, not reads. Be aware that script +branches need to be designed so they don't depend on sibling writes. + ## Multi-branch UX In a TTY: @@ -1182,6 +1223,15 @@ startup. add explicit declarations - **`max_concurrency` set to 0**: either `settings.max_concurrency: 0` or `MapNode.max_concurrency: 0` is rejected (would deadlock the semaphore) +- **Cross-branch read in a parallel super-step**: one branch's templated + field (`prompt`, `query`, `instructions`, `question`, `default`, + `output`, `over`, or `state_updates` value) reads a state key that + another sibling branch writes in the same super-step. Branches see a + snapshot taken _before_ the super-step started, so a sibling's writes + are not yet visible. The error names the reader, the reads-key, and + the sibling writer. Scripts are exempt because their reads are opaque + to static analysis. See [Cross-branch reads are not visible](#cross-branch-reads-are-not-visible-gotcha) + for the script caveat **Warnings (printed, execution continues)**: