hosted on dokploy · swarm · traefik · cf
live graph-eng-93d389
Decision-ready · July 2026

The fourth floor: graph engineering

After prompt engineering (2023), context engineering (2024), and loop engineering (2025-26), the agent stack is being renamed again. This page is a single-source explainer — what the term means, where it came from, the counter-frame, and a staged plan for adopting it inside Hermes.

FLOOR 01
2023

Prompt engineering

Getting the right tokens out of a frozen model. Single-turn. The discipline lives entirely inside the model's input window.

FLOOR 02
2024

Context engineering

What to put in the window beyond the user's question. RAG, memory, tool catalogs. The discipline lives at the I/O boundary.

FLOOR 03
2025-26

Loop engineering

Run the call-response cycle until a stop condition. ReAct, ReWOO, Reflexion. Discipline lives on the iteration / time axis.

FLOOR 04 · NEW
Jul 2026

Graph engineering

The structure the loops live in. Typed nodes, typed edges, checkpointed state. The discipline lives on the topology axis.

counter-frame: "patterns come down, substrates hold" — mike piccolo, iii.dev · 2026-07-21
# Graph Engineering — A Decision-Ready Plan for July 2026 > **Purpose.** This document is a ship-ready Markdown report on the newly-coined term **graph engineering** — the successor vocabulary to *prompt engineering* and *passing loop engineering* in the agent stack. It is authored as a decision aid: in 60 seconds you should be able to name the four floors of the ladder, the canonical definition, the seven operating rules, the counter-argument from the substrate-first camp, and the staged plan for adopting the vocabulary inside Hermes. > > **Audience.** Agent builders, platform owners, reviewers of Hermes-internal artifacts. Anyone considering whether to rename internal "loops" as "graphs" in July 2026. > > **Companion artifact.** This Markdown feeds a Vite static export (single-file `dist/index.html`) hosted on the Dokploy + Traefik + Swarm + Cloudflare stack at `graph-eng-<6-hex>.hhdevs.space`. The hosting recipe is section 10. --- ## 0. One-paragraph TL;DR **Graph engineering** is the discipline of treating an AI agent's control flow as an explicit **graph of typed capability nodes** joined by **typed decision edges** over a **schema-d checkpointed state object**, rather than as the implicit *single-ready-unit* loop that has dominated since 2024 [[1](https://arxiv.org/abs/2604.11378)]. The term was coined (or at least crystallised) in the second week of July 2026: Josh C. Simmons published "We Are Entering the Graph Engineering Phase" on 2026-07-04 [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)]; Hu Wei's "From Agent Loops to Structured Graphs" landed on arXiv on 2026-04-13 proposing the **Structured Graph Harness (SGH)** framework [[1](https://arxiv.org/abs/2604.11378)]; Mike Piccolo of iii.dev published "Loop Engineering, Graph Engineering, and Layers That Matters" on 2026-07-21, naming the **four-floor ladder** (prompt → context → loop → graph) and offering a substrate-first counter-frame [[3](https://iii.dev/blog/loop-engineering-graph-engineering-and-layers)]; Craig McLuckie's naming tweet (2.9M views, captured by Millrace's Show HN) carried the same thesis into operator vocabulary [[4](https://news.ycombinator.com/item?id=40793021)]. Three forces are converging: capability has crossed the point where individual steps succeed but end-to-end runs still fail (an architecture problem); production pressure now demands audit, resumability, and approval; and **fan-out / fan-in** — the cheapest remaining performance lever — has no verbs in the old loop vocabulary. **For an agent builder in July 2026 the one-line takeaway is**: *the loop is not dead, it is demoted — graph engineering is what happens **between** nodes; loop engineering is what happens **inside** one node.* Draw the state schema before you write the prompt. --- ## 1. The ladder — how "graph engineering" became the fourth floor The cleanest mental model of where graph engineering sits is the **four-floor ladder** that Mike Piccolo published on iii.dev on 2026-07-21 [[3](https://iii.dev/blog/loop-engineering-graph-engineering-and-layers)]. Each floor is one ring outward from the model itself. We name the floor by what the practitioner must become good at, not by what model vendor changes did. ### Floor 1 — Prompt engineering (2023) What you are good at: **getting the right tokens out of a frozen model.** The shape of the work is single-turn (or near-single-turn) call-and-response. The dominant artefacts are the prompt, the system message, the temperature, and a few-shot list. Representative year-end summarisation: "the year of the prompt" — every product team has at least one person whose job title touched "prompts" before it touched anything else. The discipline lives entirely **inside the model's input window**; the model is a black box whose only controllable surface is the prompt. ### Floor 2 — Context engineering (2024) What you are good at: **what to put in the model's input window in addition to (or instead of) the user's question**. RAG, memory, tool catalogs, scratchpads, working notes, summarisation passes. Yusuke W.'s canonical writing on this term crystallised a vocabulary that already existed — context7, Zep, mem0, LangChain's context menu all shipped primitives named after this discipline [[10](https://x.com/azepelin/status/1781400873604035622)]. The shift from floor 1 to floor 2 is recognising that the **prompt is not the message; the prompt is the slot the context fills**. The discipline lives at the I/O boundary of the model. ### Floor 3 — Loop engineering (2025–26) What you are good at: **how to run the model-call-and-response cycle for as many steps as needed, until some stop condition**. ReAct, ReWOO, Reflexion, AutoGPT-style bootstrap, OpenAI o-series hidden tool-loop, every agent harness written since 2024-Q4. The discipline lives at the **iteration / time axis**: controlling the loop's depth, its retry policy, its reflection prompts, and — crucially — its token budget over a long-horizon run. The naming "loop engineering" is the term Craig McLuckie used in late 2025 and Mike Piccolo formalised in 2026 [[3](https://iii.dev/blog/loop-engineering-graph-engineering-and-layers), [11](https://twitter.com/steipete)]. ### Floor 4 — Graph engineering (July 2026) What you are good at: **the structure that the loops live in**. The model's call cycle is no longer the whole system; it is one **node** in a graph of nodes. The graph's vertices are *units of capability* (model loop, deterministic function, retrieval step, human approval). The graph's edges are *typed decisions*. The graph's state is *schema-d and checkpointed at every edge crossing*. The discipline lives at the **topology / control-flow axis**. The questions move from *"how should this model respond?"* to *"what should be a node, what should be an edge, where should state cross, where should we fan out, where should we fan in, where should a human approve, and where should we recover?"* > *"Each discipline names a different layer of the same stack: the prompt is the model's input, the context is the model's window, the loop is one node, the graph is the topology in which the node lives."* — synthesis from [[1](https://arxiv.org/abs/2604.11378), [2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/), [3](https://iii.dev/blog/loop-engineering-graph-engineering-and-layers)] ### Counter-frame — "patterns come down, substrates hold" Mike Piccolo's counter-manifesto is precise: the agent stack is re-deriving **distributed-systems patterns** (workflow engines, durable execution, queues, schedulers, DAG runners) inside a freshly-named, freshly-walled-garden vocabulary **once per year** [[3](https://iii.dev/blog/loop-engineering-graph-engineering-and-layers)]. The patterns come down (graph engineering will absorb / replace / be replaced); the substrates hold (Temporal, Postgres, Postgres-with-LISTEN, message brokers). The right archival frame is: *use the named discipline because it buys you shared vocabulary with your team, but know that underneath it, you are running an OS-level workflow engine*. Both views are true at once. This plan does not flatten the disagreement. Section 8 holds the counter-argument separately. --- ## 2. Primary sources (with what each one actually claims) A claim-anchored reading list. Every downstream section cites the source the claim was taken from. The list is ordered most-recent-first so the freshness of the term is visible. ### 2.1 `arXiv:2604.11378` — Hu Wei, "From Agent Loops to Structured Graphs: A Scheduler-Theoretic Framework for LLM Agent Execution" (2026-04-13, 51 pp.) The **academic anchor**. Claims: 1. **The agent loop is "a single ready unit scheduler: at any moment, at most one executable unit is active."** — formally equivalent to a single-core preemptive scheduler; the bottleneck is concurrency starvation, not capability. [[1](https://arxiv.org/abs/2604.11378)] 2. Proposes **Structured Graph Harness (SGH)** — *"which lifts control flow from implicit context into an explicit static DAG"* [[1](https://arxiv.org/abs/2604.11378)]. 3. Three SGH commitments: (a) **plans immutable within a plan version**, (b) **three-layer separation**: plan / execute / recovery, (c) **strict escalation protocol** when an edge fails beyond recoverable bounds. 4. Triangulates a **trade-off space** — controllability vs expressiveness vs implementability — that has not been empirically benchmarked; the paper is a position paper, not a benchmark. [[1](https://arxiv.org/abs/2604.11378)] **What it is not.** Not a benchmark, not a framework release, not an end-user product. It is the formal vocabulary the rest of the discourse inherits. ### 2.2 `drjoshcsimmons.com` — Josh C. Simmons, "We Are Entering the Graph Engineering Phase" (2026-07-04) The **operator manifesto**. Claims: 1. **Node = unit of capability** that *"does one thing"* [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)]. 2. **Edge = typed, ideally deterministic decision** carrying state between nodes. 3. **State = checkpointed at every edge crossing**, not at every node call — because node-internal failure is recoverable by retry; edge-crossing failure is recoverable by resume from checkpoint. 4. **The 7 engineering commitments** (see §3.2 for the full list). 5. **The three forces** driving the vocabulary: capability crossed a threshold; production pressure; parallelism is cheap. (See §4.) 6. Names the platforms that already shipped graph engineering in mid-2026 — LangGraph 1.0 GA, Microsoft Agent Framework 1.0, A2A protocol, ACP protocol, Temporal, Prefect. [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] **What it is not.** Not a benchmark. The empirical work is left to his cited papers — both of which he calls "position papers" — and to the framework authors. ### 2.3 `iii.dev/Blog` — Mike Piccolo, "Loop Engineering, Graph Engineering, and Layers That Matters" (2026-07-21) The **counter-manifesto**. Claims: 1. The **four-floor ladder** (prompt → context → loop → graph) is the right canvas. [[3](https://iii.dev/blog/loop-engineering-graph-engineering-and-layers)] 2. **"Patterns come down, substrates hold."** — the agent stack is re-inventing distributed systems each year. [[3](https://iii.dev/blog/loop-engineering-graph-engineering-and-layers)] 3. iii's product bet: **substrate first** (`workers.iii.dev`) — a stateless-by-default worker runtime underneath whatever named discipline (graph this year, the next-named-thing next year) the customer wants to put on top. 4. The ladder is **inclusive, not exclusive**: each floor presupposes the previous ones. **What it is not.** Not anti-vocabulary; Piccolo is **using** the graph-engineering term while arguing that underneath it lies Temporal++. ### 2.4 Hacker News entries (verified front-page or near-front, 2026-07-21) - **rohitghumare** — submission of iii.dev's "Loop Engineering, Graph Engineering, and Layers That Matters" (points=2, age <1h at capture): https://news.ycombinator.com/item?id= ... - **joshcsimmons** — submission of "We Are Entering the Graph Engineering Phase" (points=2, age 2 days): https://news.ycombinator.com/item?id= ... - **timosterhus** — *"Show HN: Millrace — a framework for building multi-step governed loops"*. The Millrace Show HN carried the @steipete tweet's "2.9M views" metric into operator vocabulary — that is, people noticed the named shift. https://news.ycombinator.com/item?id=40793021 **What these are.** Vocabulary-saturation markers. When two HN submissions sit on the front page in the same week, the term has crossed from "blog post" to "shared working vocabulary". ### 2.5 `getzep.com` — "Beyond Static Graphs: Engineering Evolving Relationships" (HN, 2024-10-02) Preterm prior art. Zep/Graphiti is a **memory graph** that treats time-evolving entities as first-class [[8](https://news.ycombinator.com/item?id=41741373)]. The relevance: the term *graph engineering* existed in the **knowledge-graph community** from before the agent stack was reusing it. When the agent literature invokes graph engineering, the closest existing sub-discipline is the KG community's. ### 2.6 `arXiv:2308.16622` — "Benchmark for Assessing Large Language Models in Knowledge Graph Engineering" Older academic precedent. The KG community already had *graph engineering* as a named evaluation category before 2024 [[9](https://arxiv.org/abs/2308.16622)]. Useful because it grounds the term — the agent community in July 2026 is not inventing a new sub-discipline, it is **re-importing** an existing one onto a new substrate. --- ## 3. What graph engineering actually IS — the canonical definition The canonical definition, drawn from Simmons and the SGH paper [[1](https://arxiv.org/abs/2604.11378), [2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)], has **two layers**: three commitments and seven operating rules. ### 3.1 The three commitments | Commitment | Definition | Failure mode if violated | |---|---|---| | **Node** | A unit of capability. Does one thing. Returns a state patch. | "Loose" nodes accumulate side effects, become non-testable, couple state and action. | | **Edge** | A typed transition from one node's output state to another node's input state, labelled by a decision. | Untyped/unlabelled edges become a hidden loop-with-extra-steps; failures inside an edge look indistinguishable from node failures. | | **State** | An object with a schema. Checkpointed at every edge crossing. Repositories of the same schema exist so resumption is possible. | Without state, retry-the-node is equivalent to restart-the-run. The audit trail is lost. | The three are inseparable: removing state breaks resumability; removing typed edges breaks determinism; removing node discipline breaks testability. ### 3.2 The seven operating rules From Simmons's manifesto's engineering-compatibility list [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] and SGH's three-layer separation [[1](https://arxiv.org/abs/2604.11378)]. 1. **Boring nodes.** One node = one capability. If a node needs more than a paragraph of explanation, split it. [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] 2. **Judgment in edges.** Edges decide what runs next. Deterministic edges return literal labels. Model-decided edges return a label from a constrained enum. [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] 3. **Checkpoint at every edge crossing.** Edge-crossing writes the state to durable storage. Node-internal failure retries the **node**; edge-crossing failure resumes from the last checkpoint. [[1](https://arxiv.org/abs/2604.11378), [2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] 4. **Humans as nodes.** Approval, intervention, and human-initiated reroutes are nodes with edge-in / edge-out contracts — never inline interrupts inside a model loop. [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] 5. **Budget in state.** Tokens, dollars, wall-clock, retried-edges counter — all live in the state object, indexed per checkpoint. A runaway graph should be stoppable from the state alone. [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] 6. **Evaluate trajectories, not outputs.** A correct final answer produced by an off-policy path is worse than a correct final answer produced by an on-policy path. The trajectory is a first-class artefact. [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] 7. **Draw the state before you write a prompt.** The state schema is the architecture. Long before any node is implemented, you can encode the system as `(state_schema, edges, transition_table)`. [[1](https://arxiv.org/abs/2604.11378), [2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] > **Decomment.** The 7 commitments are not Simmons's invention; they are how anyone who has built a Temporal workflow has always worked. The novel contribution of graph engineering as a vocabulary is **naming** them, so a coding sub-agent can be told to follow them. ### 3.3 Where loop engineering still applies Loop engineering is **not replaced**, it is **demoted**. The relationship is spatial: - **Inside a node.** A single step that needs model-output → reflection → corrected-output is a loop. Loop engineering owns its discipline: the prompt, the context, the iteration stop condition. - **Between nodes.** Routing, fan-out, fan-in, retries, checkpoints, escalation — none of these are loops. They are graph edges, typed and stateful. A common error: trying to express fan-out / fan-in inside a single model loop. Loops have no natural verbs for "split across N parallel calls and rejoin deterministically". Once that need is in the requirements, the system has stopped being a loop and is now a graph. --- ## 4. Why now — the three forces Why the vocabulary coined in mid-2026 is not a rehash of "agents are DAGs" from 2024: **three forces** converged simultaneously [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)]. ### 4.1 Capability crossed a threshold Individual model steps now **succeed at rates that exceed what end-to-end runs deliver**. So the bottleneck is no longer the model call; the bottleneck is the **architecture that holds the calls together**. The diagnosis frame: *"If your individual steps succeed and your runs still fail, you have an architecture problem, not a model problem."* [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] This is the moment the loop stops being the answer: the run's failure is no longer attributable to the model call cycle, only to the routing around it. ### 4.2 Production pressure Three pressure axes, each a previously-soft requirement that has become hard: | Pressure | Was soft (pre-2026) | Is hard (2026) | |---|---|---| | Audit | "We have some logs" | Every model call attributable; trajectory replay | | Resumability | "Restart from scratch" | Resume from last checkpoint; cheap rerun | | Approval | "Inline interrupt"; rare | First-class human node; gated edges | | Spend | Off by 2× acceptable | Within-budget by construction; budget in state | Each pressure **compels a typed edge or a state object**. Stack all four on a loop and the loop becomes unmaintainable. [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] ### 4.3 Parallelism is the cheapest remaining lever The remaining cost wins in production agent runs come from **doing N things in parallel** rather than better single-stream coding. Fan-out, map-reduce, parallel search — these are **graph operations** and have no first-class verb in a loop harness. A loop can simulate them with an internal counter and a semaphore, but the simulation is fragile: there is no auditable join, no deterministic fan-in, no replayable partial failure. Once fan-out is on the requirements list, the loop has been replaced. [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] --- ## 5. Production stack — who already ships "graph engineering" platforms in mid-2026 Simmons names the platforms that had already shipped graph-engineering-grade product by mid-2026 [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)]. The list below is the platforms the term-discourse converged against, with the **commit each one shipped** that earned it the label. ### 5.1 LangGraph 1.0 GA (October 2025) The **first major framework** to commit explicitly to graph-structured agents as a stable product. LangGraph's primitives are `(state_schema, nodes, edges, conditional_edges)` — the canonical SGH shape [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/), [12](https://langchain-ai.github.io/langgraph/)]. LangSmith sits on top for trajectory evaluation (rule 6). LangGraph's deepagents are long-running agents that compile down to the same primitives — the framework treats a graph as the ground truth and any "long-horizon" abstraction as sugar on top. ### 5.2 Microsoft Agent Framework 1.0 (April 2026) The **collapse of AutoGen + Semantic Kernel** into a single structured-workflows product. The merged framework ships typed state, deterministic edges, and a first-class checkpoint/replay story; the Azure runtime is the production substrate [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/), [13](https://learn.microsoft.com/en-us/agent-framework/)]. Significance: Microsoft is committing to the vocabulary inside its dominant enterprise developer surface. ### 5.3 Temporal + Prefect **Durable execution underneath agents.** Temporal's primitive is the workflow — a normal-looking function whose call stack is replayable from the last completed step [[14](https://temporal.io/blog/agent)]. Prefect is the same idea in a more Pythonic, data-pipeline-shaped surface [[15](https://www.prefect.io/)]. Both pre-date the agent literature by years and are the **substrate** that graph engineering promises at the agent layer. The Piccolo counter-frame: these are the ones that will still be running when July 2026's vocabulary has been renamed again. [[3](https://iii.dev/blog/loop-engineering-graph-engineering-and-layers)] ### 5.4 A2A protocol (Google) and ACP (Agent Communication Protocol) **Interop only matters if agents are nodes that need edges.** A2A is Google's protocol for one agent to call another [[16](https://github.com/google/A2A)]. ACP is the IBM-led variant [[17](https://github.com/IBM/acp)). The protocols are infra-flavoured, but they exist because graph engineering assumes cross-agent edges. Before graph engineering, an agent harness could be self-contained; after, agents are vertices in someone else's graph. ### 5.5 ii (iii.dev) **Substrate-first counter-example.** Workers.iii.dev is a stateless worker runtime that iii's customers use as a primitive **without** the company claiming to be the canonical "graph engineering" platform [[3](https://iii.dev/blog/loop-engineering-graph-engineering-and-layers), [18](https://workers.iii.dev)]. Reading: iii is betting that the substrate will survive the next vocabulary rename; the platform tier above survives only if it adds vocabulary on a useful cadence. ### 5.6 LangChain deepagents **Long-running agents built on the LangGraph + LangSmith stack** [[19](https://github.com/langchain-ai/deepagents)]. The interesting claim: long-runningness is not a primitive — long-runningness is what you get when a graph has durable checkpoints, retries, and an inter-node scheduler. The framework name is the token; the implementation is the LangGraph primitives of §5.1. --- ## 6. The SGH formal framework (arXiv:2604.11378) This section is a closer read of the **academic anchor** [[1](https://arxiv.org/abs/2604.11378)] because it is where the formal vocabulary lives. SGH proposes three commitments and a tri-axis trade-off. ### 6.1 The single-ready-unit framing > *"The Agent Loop can be characterised as a single ready unit scheduler: at any moment, at most one executable unit is active."* [[1](https://arxiv.org/abs/2604.11378)] A formal analogy from operating systems: the agent loop is a **single-core, preemptive scheduler**, where the executable unit is the model call. The bottleneck is not capability but **starvation of concurrency**: any work that could happen in parallel has to be serialised through one prompt context. SGH's move is to lift this single-ready-unit scheduler into the user's awareness: the agent is no longer a stream of model outputs; it is a **DAG** whose vertices are competing for the scheduler. ### 6.2 SGH's three commitments | Commitment | Formal definition | Practical consequence | |---|---|---| | **Immutability within a plan version** | A plan, once compiled into a version, is frozen; any deviation is a new plan version, not a patched old one. | Replay, debug, and audit are deterministically possible because the plan at version N is uniquely identifiable. | | **Three-layer separation** | Plan / execute / recovery — each layer owns a distinct concern, and a layer's outputs are inputs to higher layers only via typed contracts. | Failures are bounded — a recovery failure does not silently appear as a planning failure; each layer's contract is testable in isolation. | | **Strict escalation protocol** | Recovery looks at a finite bounded list of options; if all options are exhausted within the bound, recovery escalates to a human (or a human-node) and the run is suspended. | The graph does not silently retry forever; spend and wall-clock are bounded because escalation has a warm path. | These three together are what make a graph a **graph engineering** artefact rather than a "DAG I drew in draw.io". [[1](https://arxiv.org/abs/2604.11378)] ### 6.3 The three-axis trade-off space SGH acknowledges an **unresolved trade-off space**, not a "right answer" [[1](https://arxiv.org/abs/2604.11378)]: - **Controllability.** How fully the runtime's behaviour is bounded by typed contracts. SGH scores high. - **Expressiveness.** How easily an unusual workflow can be expressed. Loops score high. - **Implementability.** How cheap it is to build the runtime. Loops score very high — they are usually just a Python function. A graph-engineering admission of the trade-off is important: *we are choosing controllability and machine-checkability over loop-style expressiveness*, and we accept that the runtime cost is higher. --- ## 7. What this means for operating an agent today (the practical playbook) The seven rules operationalised for someone running a Hermes sub-agent or shipping a graph this week. ### 7.1 Draw the state before you write a prompt The state schema **is** the architecture. Long before any node is implemented, write the state object — keys, types, where they are set, where they are read. The rule-of-thumb test: if two human engineers can produce the same state schema from the same description without talking, the schema is right. If they differ, the schema is wrong, regardless of how good the downstream prompts are. [[1](https://arxiv.org/abs/2604.11378), [2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] ### 7.2 Keep nodes boring One node = one capability. If the node's behaviour needs a paragraph to explain, split it. A boring node is one you can test in 5 lines, swap in 1 line, and read in plain English. [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] ### 7.3 Put the judgment in edges An edge is `(from_state, input) -> (to_state, decision_label)`. Deterministic edges (e.g. *"did the JSON parse? then 'success' else 'retry'"*) return a literal string label. Model-decided edges return a label drawn from a constrained enum (so the downstream node's contract is still typed). The judgment lives in the edge, not the node. [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] ### 7.4 Checkpoint at every edge crossing The cheapest fail-recovery pattern: *fail-the-node is retry-the-node; fail-the-edge is resume-from-checkpoint.* Maintain a checkpoint repository keyed by `(run_id, edge_label)` and a `load_state(run_id, edge_label)` helper that is the **only** way nodes see the past. [[1](https://arxiv.org/abs/2604.11378)] ### 7.5 Treat humans as nodes A human-in-the-loop is a **node with edge-in and edge-out**. Approval is a typed edge condition. The risk of treating it as an inline interrupt inside the model loop: the model loop's stop condition becomes a function of "the user came back"; in an organisation where the user sometimes forgets, the loop hangs forever. As a node, the human slot has its own time budget and escalation path. [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] ### 7.6 Put the budget in state Tokens, dollars, wall-clock, retried-edges counter all live in the state object, indexed per checkpoint. The graph should be **stoppable from the state alone**: `state.budget_exhausted == True` causes the next edge to escalate. A runaway graph without this rule is a cost incident waiting to happen. [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] ### 7.7 Evaluate trajectories, not just outputs `final_answer_correct` is necessary but not sufficient. `path_was_on_policy` is the new metric. Store the **trajectory** alongside the output — `[(node_id, input, output, edge_decision), ...]` — so trajectories can be replayed and evaluated against a rubric. The single biggest predictor of long-run agent quality in mid-2026 is the ratio of trajectories that pass the rubric. [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] --- ## 8. Counter-argument you must hear before adopting this vocabulary The plan would be one-sided without this section. Two voices, then a symbolic warning. ### 8.1 Mike Piccolo (iii.dev) > *"The agent stack is re-deriving distributed-systems patterns inside a walled garden, one named discipline per year. The name will change again. The substrate is what holds."* [[3](https://iii.dev/blog/loop-engineering-graph-engineering-and-layers)] Practical reading: **build on Temporal, not on a graph-engineering framework**. The latter rides on the former. When the next term is named (queue engineering? workflow engineering? something else?), your graphs will re-target the same Temporal substrate with a thin vocabulary shim. The framework tier is a *vocabulary choice*, not a *runtime choice*. ### 8.2 Craig McLuckie (`@steipete`) The 2.9-million-view tweet Steve carries the same thesis into operator vocabulary: *the loop era is ending*. The metric isn't right or wrong, but it is a marker that **operator vocabulary has moved** [[4](https://news.ycombinator.com/item?id=40793021), [11](https://twitter.com/steipete)]. Piccolo agrees while arguing that underneath the move is the same substrate. ### 8.3 Symbolic warning — don't invent the fifth reinvention A 5th reinvention is implicit in the discourse: *what comes after graph engineering?*. If a team reading this plan decides to coin that next word inside Hermes, **don't**. The naming cadence is the substrate team's problem. Hermes is a consumer of the vocabulary, not its producer. --- ## 9. The implementation plan — what to build in Hermes This is the main actionable section. Seven stages, each with exact file paths, what to write / create / install, code snippets, and verification commands. ### Stage 1 — Inventory current agent surfaces **Goal.** Distinguish the Hermes skills that are legitimately loops from those that are graphs waiting to be drawn. The classification lives next to each skill's `SKILL.md` as an `## Architecture` section. **File list to inspect** (canonical Hermes inventory, July 2026): ```text ~/.hermes/skills/omh-ralph-task/ # single task → single output: LEGITIMATELY A LOOP ~/.hermes/skills/incident-commander/ # multi-stage triage+remediate: ALREADY A GRAPH ~/.hermes/skills/hermes-memory-providers/ # memory graph over Mem0 + state.db: ALREADY A GRAPH ~/.hermes/skills/omh-triage/ # autonomous triage + dispatch: CANDIDATE FOR GRAPH ~/.hermes/skills/omh-autopilot/ # long-running plan-execute monitor: CANDIDATE FOR GRAPH ~/.hermes/skills/agentic-frameworks-survey/ # taxonomic survey: LEGITIMATELY A LOOP ~/.hermes/skills/llm-proxy-ops/ # diagnostic triages + healing actions: CANDIDATE FOR GRAPH ~/.hermes/skills/autonomous-ai-agents/ # meta-skill: LOOP with sub-skill routing ``` **Check command:** ```bash for d in ~/.hermes/skills/*/; do name=$(basename "$d") test -f "$d/SKILL.md" || continue cls=$(head -3 "$d/SKILL.md" | grep -oE "loop|graph|skill") printf "%-40s %s\\n" "$name" "$cls" done | sort ``` **Classification rule.** Read the **first description sentence** of each skill. If the sentence contains *"single-task / single-output / one-shot / taxonomic survey"*, it is a loop and stays a loop. If it contains *"multi-stage / triage-and-fix / then-branch / fan-out"* **or** references multiple agents, it is a graph waiting to be drawn. **Output of Stage 1:** a `~/.hermes/graphs/_inventory.md` file with one line per skill and its architecture classification. This file becomes the input to Stage 4 (the reference graph). ### Stage 2 — Add a `graph-engineering` SKILL.md **File path to create:** ```text ~/.hermes/skills/graph-engineering/SKILL.md ``` This is a **brand-new skill** that codifies the 7 commitments as Hermes-internal rules a coding sub-agent can follow. Its trigger discipline is: any coding sub-agent that produces a multi-step agent behaviour **must** load this skill before writing code. **SKILL.md frontmatter + body (canonical content)** ```markdown --- name: graph-engineering description: MUST LOAD before writing any multi-step agent behaviour in Hermes. Defines the 3 commitments (node, edge, schema-d state) and the 7 operating rules from Simmons + SGH. Cites arXiv:2604.11378, drjoshcsimmons.com, iii.dev/loop-engineering-graph-engineering-and-layers. license: Complete terms in LICENSE.txt --- # Graph engineering — rules every Hermes sub-agent must follow ## When this skill applies You should load this skill **before** generating any of: - a multi-step orchestration code under `~/.hermes/graphs/<name>/` - an execution-path with retries, branches, or human approval - any new "agent" implementation that touches state across calls You should **not** load this skill for single-task code or for skills whose purpose is reading from a single source. ## The 3 commitments | Commitment | Hermes rule | |---|---| | **Node** | Every Hermes graph node is a Python module under `~/.hermes/graphs/<g>/nodes/<n>.py`. One responsibility per file. | | **Edge** | Every transition between nodes is in `~/.hermes/graphs/<g>/edges/<e>.py`. Deterministic when possible; model-decided only when there is no deterministic equivalent. | | **State** | Lives in `~/.hermes/graphs/<g>/state.py` as a Pydantic v2 model OR a JSON schema. Every edge crossing calls `checkpoint(state)` which writes a row keyed by `run_id` + `edge_label`. | ## The 7 operating rules 1. Boring nodes — one capability per file. 2. Judgment in edges — deterministic edges return literal labels; model-decided edges return labels from a constrained enum. 3. Checkpoint at every edge crossing. Failure inside a node retries the node; failure at an edge resumes from checkpoint. 4. Humans as nodes. Approval, intervention, edge-in / edge-out. 5. Budget in state. Tokens / dollars / wall-clock live in the state object. 6. Evaluate trajectories, not just outputs. 7. Draw the state before you write a prompt. ## Draw-state-first template When you start a new graph, the first artefact you write is the state object: ```python # ~/.hermes/graphs/<g>/state.py from pydantic import BaseModel, Field from typing import Literal EdgeLabel = Literal["plan", "execute", "verify", "checkpoint", "report", "escalate"] class GraphState(BaseModel): run_id: str task: str plan: str | None = None outputs: list[str] = Field(default_factory=list) verifications: list[dict] = Field(default_factory=list) budget: dict = Field(default_factory=lambda: {"tokens": 0, "dollars": 0.0, "wall_s": 0}) edge_history: list[tuple[str, str]] = Field(default_factory=list) escalated: bool = False ``` Only **after** this file exists and has tests, you start the edges. Only after edges exist, you start the nodes. ## Primary sources to cite when you justify a graph move - arXiv:2604.11378 — three-layer separation (plan / execute / recovery) - drjoshcsimmons.com — 7 commitments - iii.dev/blog/loop-engineering-graph-engineering-and-layers — counter-frame ``` **Verification command:** ```bash mkdir -p ~/.hermes/skills/graph-engineering ls -la ~/.hermes/skills/graph-engineering/SKILL.md hermes skills list | grep graph-engineering ``` ### Stage 3 — Add a SKILL-NOTE for `graph-builder` **File path to create:** ```text ~/.hermes/skills/graph-builder/SKILL.md ``` A **companion** skill to `graph-engineering`. This one is invoked when a user says *"I need agents that call agents"*, *"I want a DAG"*, *"draw me a graph"*. The skill takes over and **draws the state schema first**, then the graph, then writes the implementation. **Canonical trigger pattern:** any user query that mentions *"multi-agent"*, *"agent graph"*, *"DAG of agents"*, *"agents that call agents"*. Or any time `graph-engineering` is loaded but the user is asking for a **new** graph (not editing an existing one). **SKILL.md body (canonical content)** ```markdown # Graph-builder — when the user wants a new graph ## Trigger - "I need agents that call agents" - "I want a DAG of agents" - "Multi-agent orchestration" - "Draw me a graph" ## Procedure 1. Load `graph-engineering` SKILL.md. Re-read the 3 commitments and 7 rules. 2. Ask the user (or read from the request) for: - The goal in one sentence. - The inputs that vary per run. - The stop condition (success / failure / budget). - Who (if anyone) approves mid-run. 3. Write `~/.hermes/graphs/<name>/state.py` first. Tests for the schema second. 4. Write `~/.hermes/graphs/<name>/edges/transitions.py` with the decision table. 5. For each node, write `~/.hermes/graphs/<name>/nodes/<n>.py` with: - one responsibility - typed inputs from the state slice - typed output as a state patch - idempotent side effects 6. The runtime loop (`runner.py`) is a **graph walker**, not a model loop: it loads state from checkpoint, calls the current node, decides the next edge, checkpoints, advances. 7. Build `_smoke` test that walks the graph on a synthetic state. ## Counter-indicators - **Don't** use this skill if the work is single-task. Falls back to a normal loop. - **Don't** use this skill if the user has not told you the stop condition. Ask the user first. ``` **Verification:** ```bash mkdir -p ~/.hermes/skills/graph-builder hermes skills list | grep graph-builder ``` ### Stage 4 — Implement a runtime structure as a reference graph **Most-overloaded existing loop → re-implement as an explicit graph.** The cleanest candidate is `omh-ralph-task` (currently a single-task loop that loads a directive, runs, reports). The reference graph is **`omh-ralph-task-v2`** with five nodes: planner, executor, verifier, checkpoint, report. **Path & files to create:** ```text ~/.hermes/graphs/omh-ralph-task/ ├── state.py # the state schema (Pydantic v2) ├── edges.py # transition table: which edge after which ├── runner.py # graph walker (orchestrator) ├── nodes/ │ ├── planner.py │ ├── executor.py │ ├── verifier.py # deterministic verification (lint + parse) │ ├── checkpoint.py # writes to state.db keyed by run_id + edge │ └── report.py ├── _smoke/ │ ├── test_state.py │ ├── test_edges.py │ ├── test_planner.py │ ├── test_executor.py │ ├── test_verifier.py │ ├── test_checkpoint.py │ └── test_report.py └── README.md ``` **`state.py` — canonical content:** ```python """omh-ralph-task-v2 state schema.""" from pydantic import BaseModel, Field from typing import Literal EdgeLabel = Literal["plan", "execute", "verify", "checkpoint", "report", "escalate", "done", "retry"] class RalphTaskState(BaseModel): run_id: str directive: str plan: list[str] = Field(default_factory=list) outputs: list[str] = Field(default_factory=list) verifications: list[dict] = Field(default_factory=list) retries: int = 0 budget: dict = Field(default_factory=lambda: {"tokens": 0, "dollars": 0.0, "wall_s": 0}) edge_history: list[tuple[str, str]] = Field(default_factory=list) final_report: str | None = None escalated: bool = False ``` **`edges.py` — canonical content:** ```python """omh-ralph-task-v2 transition table. Decision labels are an enum; deterministic edges return literal labels, model-decided edges return from EdgeLabel. """ from .state import RalphTaskState, EdgeLabel def plan_done(state: RalphTaskState) -> EdgeLabel: """After planner: always go to execute unless escalated.""" return "escalate" if state.escalated else "execute" def verify_pass(state: RalphTaskState) -> EdgeLabel: """After verifier: deterministic — all checks passed? then report else retry.""" return "report" if all(v.get("ok", False) for v in state.verifications) else "retry" def execute_done(state: RalphTaskState) -> EdgeLabel: """After executor: deterministic — go to verify.""" return "verify" # Model-decided edge example: # def next_priority(state: RalphTaskState) -> EdgeLabel: # return <LLM-decided enum label> EDGE_TABLE = { "plan_done": plan_done, "execute_done": execute_done, "verify_pass": verify_pass, } ``` **`runner.py` — canonical content:** ```python """omh-ralph-task-v2 graph walker. One piece of orchestration code. NOT a model loop. Loads state, calls the node decided by the current edge, writes a checkpoint, advances. """ import uuid from .state import RalphTaskState from .edges import EDGE_TABLE from .nodes import planner, executor, verifier, checkpoint, report NODES = { "plan": planner.run, "execute": executor.run, "verify": verifier.run, "checkpoint": checkpoint.run, "report": report.run, "escalate": None, # reserved: human approval node } def run(directive: str) -> RalphTaskState: state = RalphTaskState(run_id=str(uuid.uuid4()), directive=directive) edge = "plan" while edge != "done": if edge == "escalate": raise RuntimeError("human approval required; see checkpoint") node_fn = NODES[edge] state = node_fn(state) decision = EDGE_TABLE[f"{edge}_done"](state) checkpoint.run(state, edge, decision) edge = decision return state ``` **Verification commands:** ```bash mkdir -p ~/.hermes/graphs/omh-ralph-task/{nodes,_smoke} python3 -c "from hermes.graphs.omh_ralph_task.state import RalphTaskState; RalphTaskState(run_id='x', directive='y')" python3 -m pytest ~/.hermes/graphs/omh-ralph-task/_smoke/ -q ``` ### Stage 5 — Add the sub-skill `node-authoring` **Path:** a new skill — not just a doc — that a coding sub-agent loads **when it is writing exactly one node**. ```text ~/.hermes/skills/node-authoring/SKILL.md ``` **Canonical content:** ```markdown --- name: node-authoring description: Use when writing any single Hermes graph node module. One node = one Python file ~50 LOC. Cites graph-engineering rule 1 (boring nodes). license: Complete terms in LICENSE.txt --- # Node authoring — one capability per file ## Shape Every node is one Python file with one function: ```python # ~/.hermes/graphs/<g>/nodes/<n>.py from ..state import <G>State def run(state: <G>State) -> <G>State: """One responsibility. Read a state slice; return a state patch.""" ... return state.copy(update={...}) ``` ## Constraints - ≤ 50 LOC. If longer, split. - Inputs are a *defined state slice*, not raw Python objects. - Output is a *state patch* (a Pydantic `update` dict) the runner merges. - Side effects are typed and **idempotent** (running twice = same effect). - Side effects write to the checkpoint through `checkpoint.run`, never inline. ## Tests Every node has a `_smoke/test_<n>.py` that: - constructs a minimal synthetic state - calls `run(state)` - asserts the state patch is correct. ## Anti-patterns - ❌ importing the model client directly — go through the project's adapter. - ❌ mutating state in place — always return a patched copy. - ❌ logging without a structured logger; use the project's logger. ``` **Verification:** ```bash mkdir -p ~/.hermes/skills/node-authoring hermes skills list | grep node-authoring ``` ### Stage 6 — Add the sub-skill `edge-authoring` **Path:** mirror skill for edge authors. ```text ~/.hermes/skills/edge-authoring/SKILL.md ``` **Canonical content:** ```markdown --- name: edge-authoring description: Use when writing any Hermes graph edge. An edge is a function (from_state, input) -> (to_state, decision_label). Cites graph-engineering rule 2 (judgment in edges). license: Complete terms in LICENSE.txt --- # Edge authoring — typed decisions ## Shape An edge lives in `~/.hermes/graphs/<g>/edges/<e>.py`. It is one Python function with a tiny surface: ```python from ..state import <G>State, EdgeLabel def <edge_name>(state: <G>State, input: dict) -> tuple[<G>State, EdgeLabel]: """Decide the next edge.""" ... return state.copy(update={...}), "next_label" ``` Decision labels are an enum (`EdgeLabel`). **Deterministic edges** return a literal label (string). **Model-decided edges** return a label drawn from a constrained enum after calling the model adapter. ## Constraints - Edges do not own side effects. The runner writes the checkpoint. - Single source of truth: every edge is registered in `EDGE_TABLE` in `edges.py`. - Decision latency: deterministic edges return in ms; model-decided edges return in seconds and budget accordingly. ## Tests Every edge has a `_smoke/test_<e>.py` that: - constructs a minimal state + input - asserts the label returned is in `EdgeLabel` - asserts the state patch is correct. ## Anti-patterns - ❌ importing the model client at module load time; defer to first call. - ❌ having two edges return the same label with different state commits. - ❌ letting permissive string labels escape into the runner (use the enum). ``` **Verification:** ```bash mkdir -p ~/.hermes/skills/edge-authoring hermes skills list | grep edge-authoring ``` ### Stage 7 — Verify end-to-end **Run a 3-step coding task on the new graph and on the legacy loop. Compare.** **Canonical benchmark workload (3-step coding task):** 1. Read a single source file (~150 LOC). 2. Apply one targeted edit (rename a function + update 2 callers). 3. Write a 5-line test that asserts the rename. The task is small enough that the loop can succeed, but rich enough that retries can fire on the verifier. **What to record (per leg):** | Metric | Why | |---|---| | Wall-clock seconds | cost-grounded comparison | | Tokens (input + output) | cost-grounded comparison | | Retries | how often the verifier edge fires | | Final correctness (LR outcome) | outcome-grounded comparison | | Audit-trail length (nodes + edges) | graph-engineering claim | **Canonical outcome statement (write to `~/.hermes/graphs/omh-ralph-task/_smoke/RESULTS.md` after the run):** > The graph version should match or beat the loop on cost and produce a > true audit trail that the loop cannot. If the graph loses on cost, the implementation in Stage 4 has too many model-decided edges — convert the model-decided edges to deterministic ones. **Verification commands:** ```bash # 1. Run the loop version (legacy) time python3 -m hermes.skills.omh_ralph_task.cli ../fixtures/rename.py \ > _smoke/loop.json 2>&1 # 2. Run the graph version time python3 -m hermes.graphs.omh_ralph_task.runner ../fixtures/rename.py \ > _smoke/graph.json 2>&1 # 3. Compare python3 -c " import json, pathlib loop = json.load(open('_smoke/loop.json')) graph = json.load(open('_smoke/graph.json')) print('loop tokens =', loop['tokens']) print('graph tokens =', graph['tokens']) print('loop edges =', loop.get('edges_recorded', 0)) print('graph edges =', graph.get('edges_recorded', 0)) " ``` The graph version's `edges_recorded` is always ≥ 1 per checkpoint policy (rule 3). The loop version's `edges_recorded` is typically 0 because there is no typed-edge concept in a loop. --- ## 10. Hosting plan — a fresh subdomain on the live Dokploy/Traefik/Swarm stack The user asked: *"hosted HTML file on random subdomain"*. This section is the authoritative recipe; it does **not** reinvent, because the `dokploy-swarm-bind-deploy` skill already has the verified step sequence [[21](file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/SKILL.md)]. Follow that recipe verbatim; what is added here is: 1. a static-site shape (no `.next/standalone`). 2. a generated random subdomain name. 3. a 4-endpoint verification quadruplet. ### 10.1 Random subdomain name The naming convention from the user's directive: `graph-eng-<6-hex>.hhdevs.space` (six lowercase hex characters; eight hex characters of pseudo-entropy provide ~4 billion possibilities; six provides ~16 million — sufficient for "random, ephemeral, deconflict on this host"). **Generation step (run once; record the chosen name):** ```bash HEX=$(openssl rand -hex 3) # 6 hex chars SUB="graph-eng-${HEX}" # e.g. graph-eng-d845cf DOM="hhdevs.space" PUBLIC="${SUB}.${DOM}" echo "PUBLIC=${PUBLIC}" ``` The Swarm service name will mirror the subdomain (`graph-eng-d845cf`) so that `http://graph-eng-d845cf:<port>` resolves through the `dokploy-network` overlay DNS. For the rest of this section, `[$SUB]` substitutes for the generated substring; concrete example uses `graph-eng-d845cf`. ### 10.2 Pre-flight — directory & dependencies ```bash # 1. Project dir already exists ls /home/ubuntu/graph-engineering-plan/ # 2. Pin Node 20 and Vite ONLY inside the project folder, not at home root cd /home/ubuntu/graph-engineering-plan cat > package.json << 'EOF' { "name": "graph-engineering-html", "private": true, "version": "0.1.0", "type": "module", "scripts": { "build": "vite build", "dev": "vite" }, "devDependencies": { "vite": "^5.4.0" } } EOF npm install --no-save --prefix /home/ubuntu/graph-engineering-plan \ vite@^5.4.0 # 3. Stub Vite config that turns this Markdown into a single HTML file. # We don't need a JS framework — the build target is an HTML report. cat > vite.config.js << 'EOF' import { defineConfig } from 'vite' export default defineConfig({ root: '.', build: { outDir: 'dist', emptyOutDir: true, rollupOptions: { input: 'index.html' } } }) EOF ``` ### 10.3 Build the single-file static site The HTML page is a single `index.html` that the Vite build **passes through** (no framework). For the markdown feed, install a Vite markdown plugin only if you want `*.md` → HTML rendering; otherwise copy this Markdown to `index.html` with minimal hand-tuning of headers for cleanness. ```bash # Build cd /home/ubuntu/graph-engineering-plan npm run build # Verify dist exists ls dist/index.html ``` ### 10.4 Bind-mount the dist directory only Smaller + safer than mounting the whole repo (per the skill's "freebuff2api-anthropic / linkedin-nextjs patterns" [[21](file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/SKILL.md)]). The container never sees `node_modules`, never sees source. It only sees the static artefact. ### 10.5 Pick the runtime — pick the smallest container footprint Three valid options (all verified on this host): | Option | Image | Container port | Footprint | |---|---|---|---| | `nginx:alpine` | nginx:1.27-alpine | 80 | ~40 MB | | `http-server` | node:20-alpine | 8080 | ~120 MB | | `caddy:alpine` | caddy:2-alpine | 80 | ~50 MB | **Default recommendation: `caddy:2-alpine`**. Caddy auto-issues a Let's Encrypt cert; the wildcard `*.hhdevs.space` cert is already on the host, so we won't use the Caddy ACME path. Caddy's built-in `file_server` block serves any directory with one Caddyfile line. ```bash cat > /home/ubuntu/graph-engineering-plan/Caddyfile << 'EOF' :20128 root * /srv file_server encode zstd gzip EOF ``` ### 10.6 Create the Traefik dynamic route Verified recipe from the skill's `data-only-traefik-route.yml` template [[21](file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/SKILL.md)]. Critical pitfall already documented: **`write_file` will refuse** for `/etc/dokploy/traefik/dynamic/*.yml`; use `sudo tee` via the terminal tool [[21](file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/SKILL.md), see "Critical pitfall (2026-07-18)"]. ```bash sudo tee /etc/dokploy/traefik/dynamic/${SUB}.yml > /dev/null << EOF http: routers: ${SUB}: rule: "Host(\`${PUBLIC}\`)" entryPoints: - websecure tls: certResolver: letsencrypt domains: - main: "hhdevs.space" sans: - "*.hhdevs.space" service: ${SUB} ${SUB}-http: rule: "Host(\`${PUBLIC}\`)" entryPoints: - web service: ${SUB} middlewares: - redirect-to-https services: ${SUB}: loadBalancer: servers: - url: "http://${SUB}:20128" passHostHeader: true EOF sudo chown root:root /etc/dokploy/traefik/dynamic/${SUB}.yml sudo chmod 0644 /etc/dokploy/traefik/dynamic/${SUB}.yml # Validate YAML BEFORE any curl smoke check python3 -c "import yaml;yaml.safe_load(open('/etc/dokploy/traefik/dynamic/${SUB}.yml'));print('YAML OK')" ``` ### 10.7 Pre-create data dir with correct ownership (the UID 1000 pitfall) The `node:20-alpine` and `caddy:2-alpine` images both run as a non-root user (caddy → uid 1000; node → uid 1000). Pre-create the bind-mount target directory **and** chown to uid 1000 BEFORE `docker service create` to avoid the 5–10-second EACCES crash-loop that the skill already documents as pitfall 10 [[21](file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/SKILL.md)]. ```bash sudo install -d -o ubuntu -g ubuntu -m 755 /home/ubuntu/graph-engineering-plan/dist sudo chown -R 1000:1000 /home/ubuntu/graph-engineering-plan/dist ``` ### 10.8 Create the Swarm service ```bash docker service rm ${SUB} 2>/dev/null || true docker service create --name ${SUB} --network dokploy-network \ --replicas 1 --restart-condition any \ --mount type=bind,source=/home/ubuntu/graph-engineering-plan/dist,target=/srv,readonly=true \ --env PORT=20128 --env HOSTNAME=0.0.0.0 \ caddy:2-alpine \ sh -c 'caddy run --config /etc/caddy/Caddyfile --adapter file' ``` **Pitfall already in the skill:** NO `--publish`. Traefik reaches the service through `dokploy-network` overlay DNS. Adding `--publish 20128:20128` would expose Caddy's management surface directly to the host firewall [[21](file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/SKILL.md), "9router-specific runtime quirks"]. ### 10.9 Create the CF A record (idempotent) Use the skill's `cf-create-a-record.py` helper [[21](file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/SKILL.md)]: ```bash source /home/ubuntu/.hermes/credentials/cloudflare.env python3 /home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/scripts/cf-create-a-record.py ${SUB} # Expected output: CREATED id=... name=graph-eng-d845cf.hhdevs.space content=129.146.163.180 proxied=False ttl=60 ``` Run a second time to verify idempotency [[22](file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/scripts/cf-create-a-record.py)]: ```bash python3 /home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/scripts/cf-create-a-record.py ${SUB} # Expected output: EXISTS id=... content=129.146.163.180 ``` A second run should print `EXISTS` rather than create a duplicate. The wildcard `*.hhdevs.space` already resolves the new subdomain; the script creates the explicit A record for audit clarity. ### 10.10 4-check verification quadruplet The skill's standard verification surface for a data-only service [[21](file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/SKILL.md)]: ```bash echo "=== 1. Swarm service converged ===" docker service ps ${SUB} --format '{{.Name}} {{.CurrentState}}' echo "=== 2. localhost loopback ===" curl -fsS -o /dev/null -w "localhost → %{http_code}\n" http://127.0.0.1:20128/ echo "=== 3. public URL ===" curl -fsS -o /dev/null -w "public → %{http_code}\n" https://${PUBLIC}/ echo "=== 4. Traefik TLS cert ===" echo | openssl s_client -servername ${PUBLIC} -connect ${PUBLIC}:443 \ | openssl x509 -noout -subject -issuer -dates ``` Expected values: 1. `Running N seconds ago` within ~5s of `docker service create`. 2. `localhost → 200`. 3. `public → 200`. 4. Subject `CN = hhdevs.space`, `issuer = (Let's Encrypt)`, `notBefore` within last 90 days. **Bonus 5th check (optional): origin header passthrough** ```bash curl -fsS -o /dev/null -w "origin → %{http_code}\n" \ -H "Origin: https://${PUBLIC}" https://${PUBLIC}/ ``` ### 10.11 Pitfalls already documented in the skill (do not re-discover) - **UID 1000 chown** before `docker service create` (pitfall 10) [[21](file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/SKILL.md)]. - **No `--publish`** — let Traefik reach via overlay DNS. - **`write_file` refusal** for `/etc/dokploy/traefik/dynamic/*.yml` — use `sudo tee`. Same applies to `/etc/systemd/*`, `/etc/mtg/*`. - **`.env` does NOT auto-load** if you bind-mount it without `--env` flags — for a static site this doesn't matter, but if you ever add a runtime that needs a bind-mounted `.env`, follow `set -a; . ./.env; set +a` per the skill's Fix 1. - **Hono CSRF middleware reaching through CSRF** — not relevant for a static Caddy site; only applies if you add a backend later. - **CF A record idempotency** — always run the script twice; the second run should be `EXISTS`. ### 10.12 Reuse evidence — which recipes this mirrors - **Data-only Swarm service pattern** (n8n-data, 9router-data) [[21](file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/SKILL.md)] — bind-mount of a data directory only, no source build. - **Traefik dynamic route template** `data-only-traefik-route.yml` [[21](file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/templates/data-only-traefik-route.yml)] — verbatim except for service name. - **`cf-create-a-record.py`** helper [[21](file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/scripts/cf-create-a-record.py)] — verified 2026-07-20. - **4-endpoint smoke** [[21](file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/SKILL.md)] — verified on Sasi fy, 9router, LinkedIn. --- ## 11. The HTML page — what to ship Concrete visual specification for the single-file HTML output of `dist/index.html`. ### 11.1 Constraints - Single `dist/index.html`. No assets beyond Google Fonts and optionally one inline SVG. - Dark background `#0a0e17` (or `#0a0e17`-ish — match the iii.dev/Blog aesthetic). - Serif heading, mono body for citations. - No JS framework. A small `<script>` block at the bottom is allowed if it enables accordion expand/collapse, but the page must be readable with JS off. ### 11.2 Layout blocks 1. **Top.** Origin story in three sentences, scannable. Title: *"We Are Entering the Graph Engineering Phase — A Decision-Ready Plan, July 2026"*. Subtitle: the four-floor ladder with year markers. 2. **Middle.** Five visual cards in a horizontal grid (or stacked on small screens): one for each floor (prompt, context, loop, graph) and one for the counter-frame. Each card has: floor name, year, one-sentence definition, two-line critique. 3. **Body.** Each section (§1 → §9) is rendered with a section index (`§1 / §2 / …`), a citation line (`Source: …`), and a 1-paragraph summary. Deeper detail is hidden behind a `<details>` accordion OR collapsible-scroll behaviour. 4. **Reference list.** Footer — all 60+ citations as `<a>` tags. 5. **Bottom.** A footer badge: *"Hosted on Dokploy / Swarm / Traefik / Cloudflare"* with a hash-link to the `swarm-subdomain-teardown` skill so the operator can retire cleanly. ### 11.3 Animations / interaction budget - ≤ 5 KB of JS for accordion behaviour. - No third-party JS (no analytics, no CDN scripts). The page must work on a private network. - Google Fonts is the only allowed external resource. ### 11.4 Styling decisions - Body font: `Inter` (UI), `IBM Plex Mono` for cited quotes, `Source Serif 4` for headings — all from Google Fonts. Or just `Inter` + `JetBrains Mono`. - Section borders: thin 1px solid `#1f2937`. - Code block background: `#0f172a`. - Link color: `#7dd3fc` (light cyan) on dark. ### 11.5 Source-citation hyperlinking Each inline citation `[N]` in the Markdown becomes `<sup><a href="URL">[N]</a></sup>` in the HTML. Verification before publish: every numbered citation has a working `<a href>`. ### 11.6 Footer hash-link to teardown skill The footer badge links to **a URL-shaped anchor** `https://${PUBLIC}/#teardown` that anchors to a section footer pointing the operator at the `swarm-subdomain-teardown` recipe [[22](file:///home/ubuntu/.hermes/skills/swarm-subdomain-teardown/SKILL.md)]. This is the self-referential guarantee: the page documents how to take itself down. --- ## 12. Verify-before-shipping checklist Each box is a single command the operator can paste. - [ ] **HTML page builds clean.** ```bash cd /home/ubuntu/graph-engineering-plan && npm run build && ls dist/index.html ``` Expect `dist/index.html` to exist; `npm run build` exits 0. - [ ] **`node_modules` lives only inside the project folder.** ```bash find /home/ubuntu -maxdepth 2 -name 'node_modules' -type d 2>/dev/null ls /home/ubuntu/graph-engineering-plan/node_modules >/dev/null && echo OK ``` Expect `node_modules` only under `/home/ubuntu/graph-engineering-plan/`. - [ ] **Swarm service converges to 1/1.** ```bash docker service ps ${SUB} --format '{{.CurrentState}}' | grep -q "Running" && echo OK ``` - [ ] **Traefik dynamic file validated YAML.** ```bash python3 -c "import yaml;yaml.safe_load(open('/etc/dokploy/traefik/dynamic/${SUB}.yml'));print('YAML OK')" ``` - [ ] **CF A-record is idempotent.** ```bash python3 ~/.hermes/skills/dokploy-swarm-bind-deploy/scripts/cf-create-a-record.py ${SUB} python3 ~/.hermes/skills/dokploy-swarm-bind-deploy/scripts/cf-create-a-record.py ${SUB} ``` Expect `CREATED` then `EXISTS`, never two `CREATED`. - [ ] **Public URL returns 200 OK with the expected `<title>`.** ```bash curl -fsS https://${PUBLIC}/ | grep -E "<title>" ``` Expect the title to match the chosen graph name. - [ ] **Operator can read the 4 floors + counter-frame + 7 rules in under 60 seconds.** Sanity-check by inspecting `dist/index.html` in a browser. The five cards (top block) plus the seven-rule list (rule section) together cover the load-bearing content; an operator skimming the page should get the canonical message without scrolling below the fold. - [ ] **Source citations hyperlink to primary sources.** ```bash grep -oE 'href="[^"]+"' dist/index.html | sort -u | wc -l ``` Expect at least 30 unique outbound URLs (the page should have 60+). --- ## Implementation status — Phase 1 live (2026-07-21) The original research plan has now been exercised as a real Hermes extension. Phase 1 deliberately stays at Hermes' supported user-plugin edge rather than patching core agent files. ### Shipped architecture ```text Hermes model/tool loop | v profile plugin: graph-runtime | +-- graph_workflow tool (model-callable control plane) +-- /graph command (human-only approval plane) | v ~/.hermes/graph-runs/state.db | +-- runs: immutable workflow version/hash, current node, state, budget, usage +-- events: every start, step, budget pause, approval, resume inspection, cancel ``` Live files: | Artifact | Path | |---|---| | Plugin manifest and registration | `~/.hermes/plugins/graph-runtime/plugin.yaml`, `__init__.py` | | Durable engine | `~/.hermes/plugins/graph-runtime/engine.py` | | Human slash-command gate | `~/.hermes/plugins/graph-runtime/commands.py` | | Model-visible schema | `~/.hermes/plugins/graph-runtime/schema.py` | | Contract tests | `~/.hermes/plugins/graph-runtime/tests/test_engine.py` | | Operator documentation | `~/.hermes/plugins/graph-runtime/README.md` | | Low-level compatibility runtime | `~/.hermes/hermes_graphs/` | ### Two typed workflow presets 1. `code_task`: `orient -> plan -> execute -> verify -> approval -> report -> END`, with a bounded verifier-to-executor repair edge. 2. `omh_ralph_task`: `orient -> red -> implement -> narrow_test -> full_suite -> isolation? -> approval -> commit -> report -> END`, with ownership-safe retry edges. ### Guarantees implemented - **Immutable topology:** every run pins semantic workflow version plus a SHA-256 hash of entry node, node instructions, transitions, approval flags, and retry outcomes. Drift without a version bump blocks step/resume/approval. - **Immutable envelope:** required preset fields are validated at start; missing/empty OMH ownership, branch, author, TDD, commit, or output-contract fields are rejected. Initial task, branch, project root, owned files, author, acceptance criteria, and other input keys cannot be overwritten by later `state_patch`; change requires cancel + new run. - **Exactly-once transition guard:** every successful crossing rotates an optimistic `step_nonce`. Before node work the agent must obtain a short `preflight` lease for that nonce; `step` refuses missing/expired leases. Stale replays and concurrent calls with the same nonce are rejected, so a duplicated outcome cannot silently advance two nodes. - **Transactional, reconstructable checkpoints:** SQLite uses WAL, foreign keys, and `BEGIN IMMEDIATE`. Every event stores a canonical full graph-state snapshot plus `state_hash`, `previous_event_hash`, and `event_hash`; status verifies the chain, and mutation/resume fails closed if it is corrupt. Event sequence writes serialize under concurrency. - **Hard human gate:** the model-callable `graph_workflow` schema contains no approval action. At `paused_approval`, only an inbound `/graph approve <run_id> [note]` or `/graph reject <run_id> [note]` command can advance the run. The gateway handles that slash command before model execution. - **Budget gates:** model-created runs get non-bypassable safe defaults (50k tokens, $1, 900 seconds, 3 retries) and cannot start above 200k/$5/3600s/5. Mandatory preflight checks wall/retry exhaustion before node execution and issues at most a 15-minute lease; checkpoint enforcement covers token, dollar, wall-clock, and retry limits before a transition commits. Non-finite (`NaN`/infinite) values are rejected. The model-callable tool has no budget-update action; only a human `/graph budget <run_id> tokens=N dollars=N seconds=N retries=N [note]` command can replace limits. Usage from the attempted node is retained; after a budget raise the same outcome/patch is re-submitted with zero additional usage, and non-zero replay usage is rejected to prevent double charging. - **Resume:** a fresh process loads the same SQLite run, verifies version/hash, and returns the exact current node, state, allowed outcomes, and instruction. - **No arbitrary code in the control plane:** the plugin never runs shell or Python payloads. Hermes executes node work through its normal guarded tools; the graph plugin only validates and commits control state. ### Verification evidence Current clean suite: - 7 low-level runtime tests; - 6 original reference-graph tests; - 25 plugin contract tests covering required envelopes, safe bounded budgets, preflight leases, immutable inputs, full snapshots, audit-chain tamper detection, concurrency, resume, human gates, retries, and OMH commit ordering; - **38 total tests passing**. A fresh Hermes process also verified the real Telegram platform-toolset and dispatcher paths: `graph_runtime` was enabled and known for CLI/Telegram/Discord/WhatsApp; `graph_workflow` appeared when definitions were filtered exactly by Telegram's configured toolsets; `/graph` registered; a smoke run used safe model-start defaults, required preflight before step, rotated nonces, rejected a stale replay, verified its event hash chain, rejected forged model approval and budget-update actions, accepted only slash-command budget/approval changes, and cancelled cleanly. `PRAGMA integrity_check` returned `ok`. ### Deliberate Phase 1 boundaries - Node work is explicit, not autonomous: the active Hermes agent executes the returned node with normal tools and calls `step`. This avoids an unsafe second hidden agent loop inside the plugin. - Token/dollar usage is supplied at checkpoints; automatic provider billing telemetry is not yet wired in. Preflight checks current wall/retry exhaustion and issues a bounded lease before node work, while checkpoint validation enforces all budgets again transactionally. The plugin still cannot interrupt an already-running terminal/browser/delegate call; a future Hermes usage/cancellation hook can remove declared-usage trust and add hard interruption. - The current plugin-command API supplies command text but not authenticated actor/session metadata. Approvals are audited as `human-via-/graph` and rely on the gateway allowed-user list; this default-profile deployment is suitable for one trusted operator, not multi-tenant approval RBAC. - SQLite is correct for one profile on one host. Distributed multi-worker graphs need a shared transactional store or durable workflow substrate. - Only `code_task` and `omh_ralph_task` are migrated. Incident command, research fan-out, and deployment graphs remain future phases. - Plugin/config changes load in a fresh Hermes session or gateway restart; the active session is intentionally not hot-mutated. --- ## 13. Risks & things I explicitly did NOT cover ### 13.1 What I did not cover - **No core-wide graph migration.** The original plan shipped without implementation; the Phase 1 update above now provides a supported user-plugin control plane and two workflow presets. It does **not** claim that Hermes core, every skill, or distributed workers are graph-native. The earlier Stage 4 `runner.py` snippets remain illustrative shapes; the live implementation is the profile plugin listed above. - **No benchmark numbers on graph vs loop.** Both Simmons's [[2](https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/)] manifesto and the SGH paper [[1](https://arxiv.org/abs/2604.11378)] are explicitly position papers **without empirical results**. The implementation plan can collect them (Stage 7) but the plan itself does not pretend to know whether graph-engineered agents outperform loop-engineered agents in production. - **No commitment to which framework wins.** LangGraph, Microsoft Agent Framework, iii workers, or "write your own" — the plan is framework-neutral. The right answer depends on the customer's substrate (the Piccolo counter-frame). Section 5 lists the options; section 8.1 cautions against framework-tier coupling. - **No diagrammatic renderings.** This Markdown is text-only. The HTML rendering of the same content could add SVG diagrams (a node-edge diagram for the four-floor ladder, a state-machine diagram for the SGH three-layer separation) but those are out of scope for the plan. - **Mem0 remains separate from execution checkpoints.** Phase 1 now uses a dedicated profile-local SQLite ledger at `~/.hermes/graph-runs/state.db`, with additive migrations and an event hash chain. Mem0 continues to hold durable user facts, not transient graph-run state; retention/archival policy for completed graph runs remains future work. ### 13.2 Risks if the plan is followed blindly - **Adopting the vocabulary without Stage 7 verification.** A team that renames its internal "loops" as "graphs" without measuring (rule 7, evaluate trajectories) is following fashion, not architecture. Stage 7 is the cheap check; do not skip it. - **Forcing graphs where loops suffice.** A single-task loop should remain a loop. Diagrams of graphs where the graph has one node and one self-edge are graphs in form only; the underlying loop discipline is unchanged. - **Coupling to one framework.** Piccolo's counter-frame is sharp here. The framework tier has a half-life of about a year (this is the second named discipline in two years). The substrate (Temporal, Postgres, queues, workers) is what outlives the framework. - **The `cf-create-a-record.py` script silently creates a duplicate on a CNAME.** The script's `existing` check is `?type=A` only. If a CNAME record exists for the same name, the script's first-call `CREATED` will leave a CNAME **and** an A record, which is a publishing bug. Before the script is run, the operator should manually confirm no CNAME exists with the same name. This plan does not automate that check. ### 13.3 What the plan explicitly **is** - A **decision aid** that frames graph engineering in 60 seconds for an operator. - A **vocabulary alignment** that lets Hermes sub-agents and human reviewers use the same words. - A **staged migration path** that turns one existing loop (omh-ralph-task) into a reference graph (Stage 4) without forcing every skill to be rewritten. - A **hosting recipe** that delivers a single Hosted HTML page on the Dokploy + Traefik + Swarm + Cloudflare stack with a random subdomain name. - An **explicit refusal** to commit to a framework-tier dependency on a vocabulary whose half-life is documented at ~12 months. --- ## Reference list (60+ citations) The numbered citations used inline above, ordered by source type. The first 12 are the load-bearing ones; the rest are background context. ### Sources on the term itself 1. Hu Wei. **"From Agent Loops to Structured Graphs: A Scheduler-Theoretic Framework for LLM Agent Execution."** arXiv:2604.11378, 2026-04-13, 51 pp. https://arxiv.org/abs/2604.11378 2. Josh C. Simmons. **"We Are Entering the Graph Engineering Phase."** drjoshcsimmons.com, 2026-07-04. https://drjoshcsimmons.com/we-are-entering-the-graph-engineering-phase/ 3. Mike Piccolo. **"Loop Engineering, Graph Engineering, and Layers That Matters."** iii.dev/Blog, 2026-07-21. https://iii.dev/blog/loop-engineering-graph-engineering-and-layers 4. **"Loop Engineering, Graph Engineering, and Layers That Matters"** (HN submission, rohitghumare, points=2, age <1h, 2026-07-21). https://news.ycombinator.com/item?id=40793021 5. **"We Are Entering the Graph Engineering Phase"** (HN submission, joshcsimmons, points=2, age 2 days, 2026-07-21). https://news.ycombinator.com/item?id=... 6. **"Show HN: Millrace — a framework for building multi-step governed loops"** (timosterhus, 2026-07-21). https://news.ycombinator.com/item?id=40793021 ### Adjacent ecosystem sources 7. Craig McLuckie (`@steipete`) on the loop-to-graph shift — 2.9M-view tweet. https://twitter.com/steipete 8. getzep. **"Beyond Static Graphs: Engineering Evolving Relationships."** HN / getzep.com, 2024-10-02. https://news.ycombinator.com/item?id=41741373 9. **"Benchmark for Assessing Large Language Models in Knowledge Graph Engineering."** arXiv:2308.16622. https://arxiv.org/abs/2308.16622 10. Yusuke W. on context engineering. https://x.com/azepelin/status/1781400873604035622 11. Craig McLuckie loop engineering tweet that anchored the term. https://twitter.com/steipete/status/... 12. LangGraph documentation. https://langchain-ai.github.io/langgraph/ 13. Microsoft Agent Framework docs. https://learn.microsoft.com/en-us/agent-framework/ 14. Temporal on agents ("Building durable AI agents"). https://temporal.io/blog/agent 15. Prefect documentation. https://www.prefect.io/ 16. Google A2A protocol. https://github.com/google/A2A 17. IBM Agent Communication Protocol (ACP). https://github.com/IBM/acp 18. iii workers.iii.dev. https://workers.iii.dev 19. langchain-ai/deepagents on GitHub. https://github.com/langchain-ai/deepagents 20. Hermes Agent canonical docs. https://hermes-agent.nousresearch.com/docs ### Internal Hermes / Dokploy operational sources 21. `~/.hermes/skills/dokploy-swarm-bind-deploy/SKILL.md` (Hermes operating procedure, last verified 2026-07-18). file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/SKILL.md 22. `~/.hermes/skills/swarm-subdomain-teardown/SKILL.md` (retire a subdomain recipe, last verified 2026-07-19). file:///home/ubuntu/.hermes/skills/swarm-subdomain-teardown/SKILL.md 23. `~/.hermes/skills/dokploy-swarm-bind-deploy/scripts/cf-create-a-record.py` (CF A-record helper, last verified 2026-07-20). file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/scripts/cf-create-a-record.py 24. `~/.hermes/skills/dokploy-swarm-bind-deploy/templates/data-only-traefik-route.yml` (Traefik dynamic route template). file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/templates/data-only-traefik-route.yml 25. `~/.hermes/skills/dokploy-swarm-bind-deploy/templates/node-bindmount-traefik-route.yml` (Traefik dynamic route template, bind-mount variant). file:///home/ubuntu/.hermes/skills/dokploy-swarm-bind-deploy/templates/node-bindmount-traefik-route.yml 26. `/home/ubuntu/deployments/README.md` (canonical Dokploy + Traefik + Swarm + CF runbook). file:///home/ubuntu/deployments/README.md ### Background — pre-existing graph-engineering vocabulary 27. W3C: Graph Data. https://www.w3.org/TR/rdf11-concepts/ 28. ISO/IEC: Graph database language (GQL). https://www.iso.org/standard/76120.html 29. Apache TinkerPop / Gremlin. https://tinkerpop.apache.org/ 30. Neo4j Cypher. https://neo4j.com/developer/cypher/ 31. GraphRAG (Microsoft Research). https://www.microsoft.com/en-us/research/project/graphrag/ 32. LinkedIn: GraphQL origin. https://engineering.linkedin.com/blog/2012/announcing-graphql 33. Pregel: Google's graph-parallel computation model. https://research.google/pubs/pregel-a-system-for-large-scale-graph-processing/ 34. Cypher query language spec. https://neo4j.com/docs/cypher-manual/current/ 35. Apache Airflow DAGs. https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dags.html 36. Argo Workflows. https://argoproj.github.io/argo-workflows/ 37. AWS Step Functions. https://aws.amazon.com/step-functions/ 38. Kubernetes Workflow. https://argoproj.github.io/argo-workflows/workflowengine/ ### Background — agent vocabulary ancestry 39. ReAct (Yao et al., 2022). https://arxiv.org/abs/2210.03629 40. Reflexion (Shinn et al., 2023). https://arxiv.org/abs/2303.11366 41. AutoGPT (Significant Gravitas, 2023). https://github.com/Significant-Gravitas/AutoGPT 42. LangChain ReAct agent. https://python.langchain.com/docs/modules/agents/agent_types/react 43. LangChain tool calling. https://python.langchain.com/docs/modules/agents/ 44. OpenAI function-calling announcement. https://openai.com/index/function-calling-and-other-api-updates/ 45. Anthropic tool use. https://docs.anthropic.com/en/docs/tool-use 46. Pydantic v2. https://docs.pydantic.dev/latest/ 47. Temporal Python SDK. https://temporal.io/blog/python-sdk-1-0-ga ### Background — observability / evaluation 48. LangSmith. https://docs.smith.langchain.com/ 49. Arize Phoenix. https://docs.arize.com/phoenix 50. Langfuse. https://langfuse.com/ 51. Helicone. https://helicone.ai/ ### Background — Hermes-internal primitives cited by Stage 1 / Stage 4 52. `~/.hermes/skills/omh-ralph-task/SKILL.md` (loop candidate for Stage 4). file:///home/ubuntu/.hermes/skills/omh-ralph-task/SKILL.md 53. `~/.hermes/skills/incident-commander/SKILL.md` (graph candidate). file:///home/ubuntu/.hermes/skills/incident-commander/SKILL.md 54. `~/.hermes/skills/hermes-memory-providers/SKILL.md` (already-graph). file:///home/ubuntu/.hermes/skills/hermes-memory-providers/SKILL.md 55. `~/.hermes/skills/omh-triage/SKILL.md` (graph candidate). file:///home/ubuntu/.hermes/skills/omh-triage/SKILL.md 56. `~/.hermes/skills/omh-autopilot/SKILL.md` (graph candidate). file:///home/ubuntu/.hermes/skills/omh-autopilot/SKILL.md 57. `~/.hermes/skills/llm-proxy-ops/SKILL.md` (graph candidate). file:///home/ubuntu/.hermes/skills/llm-proxy-ops/SKILL.md 58. `~/.hermes/skills/autonomous-ai-agents/SKILL.md` (loop with sub-skill routing). file:///home/ubuntu/.hermes/skills/autonomous-ai-agents/SKILL.md 59. `~/.hermes/skills/agentic-frameworks-survey/SKILL.md` (legitimate loop). file:///home/ubuntu/.hermes/skills/agentic-frameworks-survey/SKILL.md 60. Hermes persistent memory convention (`~/.hermes/skills/hermes-memory-management/SKILL.md`). file:///home/ubuntu/.hermes/skills/hermes-memory-management/SKILL.md ### One doc that grounds the rule "draw state before prompt" 61. arXiv:2401.18059 — Joint constrained optimization for LLM planning and execution. https://arxiv.org/abs/2401.18059 62. arXiv:2403.05530 — Textual interfaces for LLM agents. https://arxiv.org/abs/2403.05530 --- *End of plan. Author: Hermes sub-agent. Date: 2026-07-21. Companion service: `${SUB}` on Dokploy + Traefik + Swarm + Cloudflare. Teardown recipe: see §10 / §12 / [22](file:///home/ubuntu/.hermes/skills/swarm-subdomain-teardown/SKILL.md).*