Summary
What is it:
Imagine you have an agent that has a lot of different jobs to do (primarily in code) and the jobs are usually sequential and dependent on each other; it potentially has loads of subagents that it spawns itself, and it has to do an end-to-end job (like market research - > coverage -> distribution -> reporting, etc.). Instead of this agent usually just relying on semantic memory, outputs, and code to pass the job from one phase to the next, it uses a code execution graph to see where in the job it actually has an issue, and repair from there/resume from a trusted boundary of the graph.
Has it been done:
Not really, some substitutes for the graph creation were attempted. Or maybe it's been done, just not published!
Why not:
Because it’s hard to make this graph, then you are still left with questions around how does your agent operate on the graph and how does it define/assess a trusted boundary.
Why does this matter:
Because these agents go wrong a lot and you can’t expect them to cope with all the context they produce. But if you do get a long horizon agent right - autonomy is right around the corner, plus this gives you governance as well.
Long Version
If you’ve read any of my blog posts before then you’ll know this series is solely focused on code execution graphs (Etiq’s in particular) and what they are good for. In this part, I’ll handle a very interesting use case: long horizon agents, or agents that have to do more and as a result struggle more - with context, with keeping track of memory, shared tasks, etc. As a disclaimer: for the experiment side, I haven’t gotten as far as a full long horizon agent partly because on less complex agents the dynamic between the different issues at play is easier to see, but initial findings on a simpler agent are definitely pointing in the right direction (also full disclaimer: this blogpost is not attempting to pretend that it comes from a long horizon expert, my interest is from the point of view of impact of observability on these and other complex agents).
The main idea is that using an execution graph that builds across all the code/pipelines coded and run by a ‘long horizon’ (or similarly complex) agent will help the agent keep track of its steps better and orchestrate handoff points and collaboration tasks more easily.
These agents often suffer from errors that compound - they might make an error early on in a step and then not realize till they’re close to the end of the run by which point it might be too late and a lot of work would be wasted. Moreover, the agent might not then even learn from what happened either, as the context has ballooned too much for it to tell the root cause of the issue, and thus truly waste a run. With the execution graph, as the agents execute their code via the runtime capture, the graphs of different pipelines run by different spawns can get collated together and build a large map of what happened, where some regions/boundaries can be labelled as trusted or not trusted. Then the agent can use this map to orient itself during the many steps it needs to undergo - with some more specific operations underneath. If some boundaries can be defined as trusted that makes it easier for the agent to retrace its steps, or resume from a known trusted boundary.
The other key element this graph could potentially tackle is the context issues these agents end up having (and I’m still exploring how exactly that would happen) - here the assumption is that if only the LLM were given an optimal amount of context it would answer perfectly every time, thus the key to a great long horizon or any kind of complex agent is parcelling out the tasks in a way which encourages this optimal attribution. There are elements of this approach in this initial experiment, namely: splitting the execution graph into smaller regions and having the agent review or repair each of the regions in an independent session with in-depth context only about the relevant region and high level knowledge about the rest of the task - so the agent is given the optimal/relevant context only to help it achieve a good outcome. From a human perspective, it is helpful to have a trace of what the agent has done in code (especially if the trace is deterministic), - and go beyond the usual OpenTelemetry observability and semantic memory, as it can provide a layer of governance.
I have yet to master the art of writing captivating lit reviews (and chatgpt hasn’t mastered it yet either), but really there are some indications that structured, execution-aware state for long-horizon agents is not the craziest idea.
The MAGE paper is probably the most known paper in this general area, and here’s a short AI generated description: MAGE treats memory as an evolving execution state rather than a collection of semantically similar fragments. It organises raw action- observation traces and compressed subgoal summaries into a hierarchical tree, allowing the agent to validate progress, return to an earlier boundary after an error and continue along a repaired branch. On MemoryArena, it improved success rates by 7.8–20.4 percentage points while using 55.1% fewer tokens than retaining the full history. [1]
The main difference with an execution graph is that MAGE is based on textual traces and LLM-generated judgements rather than an execution graph, but the operations on the graph can be very similar [1]. As we’ll see during the experiment, choosing too strictly the area on which the LLM applies the judgement is a double-edged sword… on the one hand it might be a good idea to limit it based on a real execution graph, on the other hand LLMs are just very good on their own… but this is part of the crux of this problem.
SWE-Search is also a good model for using trajectories and different agent paths and good inspiration for the kind of different operations that can potentially be applied on a graph for a coding agent… but of course, it is very different from an execution graph [2].
Zhaoling Chen et al. - LocAgent: Graph-Guided LLM Agents for Code Localization shows that graph structure can improve software agents’ navigation. Its graph is predominantly derived through static analysis, not dynamic [3]. A survey paper Yiqi Wang et al.: From Agent Traces to Trust: A Survey of Evidence Tracing and Execution Provenance in LLM Agents describes the landscape a bit, pretty aligned I would say with the notion that the tooling for observability are a bit fragmented and not quite fit for purpose at the moment and that a new generation of this tooling would be welcome [4].
Some newer, less cited papers are close to the topic - let’s call it emerging literature. Yohei Nakajima, The Log Is the Agent: Event-Sourced Reactive Graphs for Auditable, Forkable Agentic Systems has a different type of graph: agent-level events vs. the execution graph [5]. Josh Rosen and Seth Rosen, From Agent Loops to Deterministic Graphs: Execution Lineage for Reproducible AI-Native Work models AI-assisted work as a directed acyclic graph of artifact-producing computations with explicit dependencies, stable intermediate boundaries and identity-based replay. This is very closely tied to the idea of using graphs/dags for these architectures, but the paper assumes that the computation graph is specified correctly, rather than showcase an actual retrieval mechanic at play; the level of the graph is also probably slightly different [6].
A somewhat more distanced paper: Lakshya Agrawal et al. - GEPA: Reflective Prompt Evolution Can Outperform Reinforcement Learning is a kind of agent prompt optimizer based on recorded trajectories - including reasoning, tool calls, outputs and evaluator feedback. In theory could be combined with an execution graph [7].
Agent architecture
The initial agent architecture involves a series of codex jobs and it is NOT yet a long horizon agent. This is a marketing agent and it includes jobs like
1) ‘segment’ - which takes some initial instruction on product and audience and returns some segments
2) ‘market_demand’ - the actual Python pipeline that will collect and synthesise market-demand evidence for one segment
3) ‘coverage’ - the Python pipeline that maps trusted demand against product capabilities and determines coverage gaps,
4) ‘synthesizer’ - gathering the final result if enough coverage for the segment exists.
Each job has a fresh session and there’s a few sub-jobs that are execution/graph dependent such as review, repair, etc. (for more details see the appendix).
The ideal version is a fuller sequence, building up to an ‘end-to-end marketing agent’ to include distribution (actual campaigns) and measurement steps, with codex as the orchestrator and worker agents that are OSS agents with pydantic and aider harnesses. And the end-to-end process should be fully autonomous including when it comes to distribution. But this experiment is starting light.
The execution graph
As a quick reminder: the Etiq scanner provides runtime observability for agent-, copilot- and human-created code, supporting Python, Spark and SQL across data pipelines, ML/AI workflows, agent builds and larger codebases. Starting from a Python entry file, it scans the code as it runs and builds a deterministic causal-lineage graph from actual execution artifacts rather than LLM-generated claims. The scanner captures datasets, dataframes, models, agent states and other intermediate outputs, tracing each object back to the functions that produced or transformed it. This reveals what the executed code actually did, which intermediate step contributed to a result and where an error may have entered the workflow. The graph is generated without manual or agent instrumentation.
The initial job just gets passed the high level prompt - as it is a planning job it does not run through the scanner. Once codex codes up the plan, every code that is run is run through the scanner, which captures the nodes - the interim and input/output data states, model and llm calls, and the relationships between them. In essence the scanner pretty much provides the execution graph out of the box and all that is left to do is link the various graphs of the various executed pipelines together which is not very hard to do as they share input/output nodes.
Now comes arguably the most complicated part of this fairly simple architecture and the one that still requires experimentation.
The idea here is that the high level graph gets divided into sections based on the nodes captured (a node would include both an interim data state - structured or unstructured and the producer function), and as the reviewer codex goes through the nodes, it notes some as trusted, some as suspect, some as wrong. When the repair codex starts its journey, it then focuses on the issue node and any root or neighbouring context to fix the focused issue. The idea further down the line with a true long horizon job is that this will prevent the agent from making an early issue and then running a long expensive process based on a wrong output it got early in the process. Conversely the details of production of output that has been declared trusted can be cleaned out of the context.
While nice in theory, the implementation of this is not very straightforward yet (to me). There are three key considerations:
1) The selection itself: How is the boundary between different parts of the graph defined?
2) What gets passed on to the agent for its next job? (or in our ideal version to the various agents collaborating on the overall shared tasks and in the truly ideal version what the agent passes itself for its next task)
3) What happens next - what is the operation codex is asked to do? And the follow-up key consideration: in this set-up codex is meant to review a previous job (that another version of codex coded) to define a section of the pipeline and its output, as a ‘trusted boundary’.
1) The selection itself:
At the moment the set-up is a rather arbitrary approach asking codex itself when it codes the pipeline to assign some high level sections; using a simple search a simple wrapper matches the sections back to the corresponding nodes, which is what is used moving forwards. There is a limit on a number of nodes and relationships per section. However there are many other ways to do this, and it requires further experimentation, e.g. selecting them based on the context they would likely represent (which at an optimal context window would then probably optimize performance?).
The scanner can distinguish between nested functions and helper functions at different levels of depth and render different levels of granularity. This gives a bit more flexibility to the selection - if an issue is spotted in a high level region, or repairer agent can dig a bit deeper into the nodes in the region, without having been flooded with that information initially. (while this experiment uses one approach to the detailed investigation, we need to tweak and test different approaches).
2) The package codex is passed on:
Below is a version included in this experiment, but there’s still a few different combinations to try.
| Package field | Exact content |
|---|---|
run_id |
Current pipeline execution ID |
section |
Assigned units, context units, bounds and excluded overlap |
assigned_units |
Unit ID, function name, stack prefix and boundary health |
visible_evidence_by_unit |
Visible nodes, relationships, expanded helpers and still-collapsed helpers |
job_request |
Product, audience and original job configuration |
segments |
Generated segment definitions |
pipeline_input |
Exact runtime input given to the pipeline |
semantic_result |
Complete parsed output produced by the pipeline |
pipeline_stdout |
Complete pipeline stdout |
pipeline_stderr |
Complete pipeline stderr |
captured_nodes |
Selected node records |
captured_relationships |
Selected edge records |
artifact_inspections_by_unit |
Results of any requested artifact inspection |
allowed_evidence_refs |
Exact references the review is permitted to cite |
Codex definitely does not prefer to look at the graph vs. words and code, that much is clear. However, having trusted boundaries has to help it logically as the details of its job starts muddying its context, and second, actually looking at the data or output, not just at the code, has to improve codex’s repair/review capabilities, and the nodes in the graph focus it on it. While the code itself might not overflood codex’s context, depending on the task and general theme of the agent, the data and output definitely can overflood it… but it is also what matters for a well performing, accurate review. Furthermore, as the agent continues its path and begins to chain together more and more outputs, the context will for sure start overflowing.
3) The exact operations codex makes:
| Operation | What Codex is asked to return | What the controller sends next |
|---|---|---|
| Review graph evidence | Decision, reasoning, criteria and cited references for every assigned unit | Nothing further if the review is valid and complete |
| Expand a helper | Exact helper stack in expand_helper_prefixes
|
Rebuilt package containing that helper’s nodes and relationships |
| Inspect an artifact | Node, range, columns and optional search query in inspect_artifacts
|
Same package plus the requested artifact slice |
| Cite graph evidence | Exact node and relationship references in evidence_refs
|
Citations are validated against the supplied package |
| Identify suspect nodes | Exact node references in suspect_node_refs
|
Controller uses them when selecting and explaining a repair target |
| Judge a review unit |
trusted, failed, suspect, superseded, or not_pipeline_step
|
Controller either accepts the result, requests a corrected receipt, or starts repair |
| Repair selected source | Replacement source and change summary | Controller inserts it, reruns the pipeline and captures a new graph |
The key of course is that codex itself at the moment defines whether a boundary is trusted or not - given that each operation is a fresh invocation, this might be a reasonable approach, but there are potential issues with the accuracy of labeling the trusted boundary and it’s unclear the pressures this might put later on in the agent’s task path.
In this modest implementation, codex currently cannot do them, but there are many operations that can be added to the current architecture, e.g. run arbitrary graph queries, ask for an arbitrary node by ID if it is hidden, traverse an arbitrary number of edges interactively, etc.
Just to summarize the architecture: codex codes away, as the pipeline executes it executes via the scanner, and the graph starts building up, a (for now deterministic) wrapper selects the regions of the graph to look at on an individual basis, in its next job - i.e. the review job, codex looks at a region (in depth if needed), assigns it as trusted or not, and then passes on its findings to a repair codex if needed, if not passes on the output as trusted to the next codex job.
Experiment setup/implementation
We’ve tried to make the experiment we ran as fair as possible, and the experiment itself gave us a lot of ideas of how to change the graph selection for codex. But at the moment the experiment is measuring whether the detailed graph information is actually useful for the agent, rather than measuring the impact of correct selection and splitting the space into regions to be reviewed independently. (we will change this in further experiments).
At the moment we have 4 arms:
1) Semantic_only - doesn’t receive nodes inside a selection, only receives the given selection
2) History_full - doesn’t receive nodes inside a selection, but receives the entire history
3) Graph_full - receives the full graph/nodes
4) Graph_selected - receives the relevant nodes inside the graph selection but can also dig deeper if it chooses
For every pipeline execution, the controller:
- Captures an execution graph.
- Matches declared review-boundary function names against observed function stacks.
- Creates review units.
- Orders those units using captured dataflow relationships.
- Packs the ordered units into bounded review sections.
- Sends each section to a fresh review agent.
Even semantic_only uses the execution graph internally to determine which functions are assigned for review. The difference is that nodes and relationships are not shown to that arm’s Codex reviewer.
The same is the case for the full history which is asked to give reviews for each of the region as well - regions as defined by the graph rather than just the initial pipeline declaration which also corrupts the results by leaking the graph into this agent. So I know these are improvements that need to be made to make it a more fair comparison, but I’d expect the impact to favour the execution graph arm.
semantic_only |
history_full |
graph_full |
graph_selected |
|---|---|---|---|
| Full pipeline source. Full pipeline input. Full semantic output. Full stdout and stderr. Assigned unit names and function-stack prefixes. Section metadata. No nodes. No relationships. No artifact inspection. No accumulated history. Its assigned units were still created using the graph, but their boundary_health is deliberately omitted. | The review agent receives everything in semantic_only, plus accumulated non-scanner artifacts such as: Previous pipeline source versions. Previous semantic results. Previous inputs and logs. Applied-repair records. Repair targets and diffs where allowed. Parent-run relationships. Authoring retry records. | The review agent receives: Full pipeline source. Full input, output and logs. Every node in the current graph snapshot. Every captured relationship. Artifact previews and small artifacts. Permission to inspect any visible captured artifact. No accumulated history. | The review agent receives: No full pipeline source during review. Full input, output and logs. Assigned review-unit descriptions. Only selected nodes and relationships. Collapsed-helper descriptions. Bounded artifact previews or small complete artifacts. No accumulated history.* |
Regarding the nested functions, below an example (for the graph-selected arm):
extract_evidence_records included
get_context_value collapsed
source-processing loop collapsed
source_is_secondary... collapsed
Codex can request one direct collapsed child at a time. It cannot request an arbitrary unrelated prefix.
The experiment is supposed to measure how far the job advances correctly (according to a blind codex judge - given the same criteria to follow for each arm), and how much context is consumed.
Detailed findings
| Arm | Total input tokens | Relative to semantic | Final # of boundaries flagged as not trusted* | Effective repairs |
|---|---|---|---|---|
| Semantic only | 568,715 | 1.00× | 4 | 0/3 |
| History full | 1,824,533 | 3.21× | 4 | 1/3 |
| Graph full | 1,331,686 | 2.34× | 3 | 0/3 |
| Graph selected | 1,000,797 | 1.76× | 2 | 1/3 |
* a blind judge labels trusted vs. not trusted in the end for each arm based on the same criteria, but this does include some potential issues for error in measurement
From the point of view of graph performance, it is overall not bad - graph-selected is still a bit of a context guzzler but it delivers on the actual improvements. I feel however the real improvement will come later in the journey of a full blown long horizon agent once history has really accumulated. We also have used very straightforward set-ups and limited codex’s operations on the graph quite heavily.
Various disclaimers: certainly this initial experiment is just a start and it is far from proving that graph selection caused the better result.
Reasons include:
- This is one controlled pipeline run, not a repeated sample.
- Codex decisions are nondeterministic.
- Different arms produced different repaired pipelines, etc.
What we can say is that in this one paltry run, the graph-selected branch ended with the narrowest final issue set and used less context than full graph or accumulated history. This is promising evidence for graph-selected context, but as we stabilize and improve both the graph layer and the experiment set-up, we will starting running multiple experiments.
Vizualizer & observability
This execution graph is born from (and will probably die with) observability. The hope is that giving the agent itself the observability of its own past code as it goes further and further away into the future of the task will help improve it. While that’s work in progress :), we still have observability benefitting us humans. It’s always good to know what your agent is up to.
So this is an initial attempt of vizualizing this agent as it moves back and forth through resolving the nodes… it can be vastly improved but hopefully it paints a picture:
You can tell that the job started with issues throughout every section:
Then as codex goes through the graph, nested function by nested function, it starts solving some of those issues:
This blogpost has references 😳
[1] Chen, Y., Lai, H., Feng, Y., Han, C., Zhang, Q., Lu, B., Li, M., Wang, X., Wang, Z., Xu, S., Li, Z., Jin, Z., Wu, H., Li, C., and Chen, Q. (2026). Beyond Semantic Organization: Memory as Execution State Management for Long-Horizon Agents. arXiv:2606.06090.
[2] Antoniades, A., Örwall, A., Zhang, K., Xie, Y., Goyal, A., and Wang, W. (2025). SWE-Search: Enhancing Software Agents with Monte Carlo Tree Search and Iterative Refinement. ICLR 2025; arXiv:2410.20285.
[3] Chen, Z., Tang, R., Deng, G., Wu, F., Wu, J., Jiang, Z., Prasanna, V., Cohan, A., and Wang, X. (2025). LocAgent: Graph-Guided LLM Agents for Code Localization. In Proceedings of the 63rd Annual Meeting of the Association for Computational Linguistics, pp. 8697–8727.
[4] Wang, Y., Zhang, J., Wu, Z., Cai, T., Liu, Z., Sun, Q., Sun, Z., Dong, M., Zheng, M., Yin, X., and Zhu, Y. (2026). From Agent Traces to Trust: A Survey of Evidence Tracing and Execution Provenance in LLM Agents. arXiv:2606.04990.
[5] Nakajima, Y. (2026). The Log Is the Agent: Event-Sourced Reactive Graphs for Auditable, Forkable Agentic Systems. arXiv:2605.21997.
[6] Rosen, J., and Rosen, S. (2026). From Agent Loops to Deterministic Graphs: Execution Lineage for Reproducible AI-Native Work. arXiv:2605.06365.
[7] Agrawal, L. A., Tan, S., Soylu, D., Ziems, N., Khare, R., Opsahl-Ong, K., Singhvi, A., Shandilya, H., Ryan, M. J., Jiang, M., Potts, C., Sen, K., Dimakis, A. G., Stoica, I., Klein, D., Zaharia, M., and Khattab, O. (2026). GEPA: Reflective Prompt Evolution Can Outperform Reinforcement Learning. ICLR 2026 (Oral); arXiv:2507.19457.


Top comments (0)