{"slug": "jev-at-the-branches-the-state-machine-is-the-agent", "title": "Jev at the Branches: The State Machine Is the Agent", "summary": "A state-machine architecture in which deterministic code owns the plan and facts while Jev supplies bounded judgment only at decision branches is detailed in a blog post, with a simulated canary deployment used as the test case. The design assigns state tracking, legal transitions, and waiting to the state machine, and gives Jev the single job of selecting among enabled agent-controlled transitions when the current state has several legitimate next decisions. The author states no production deployment was controlled and that the canary example is a test case for the more general architecture.", "body_md": "# Jev at the Branches: The State Machine Is the Agent\n\nA state-machine architecture in which deterministic code owns the plan and the facts, while Jev supplies bounded judgment only at decision branches.\n\n1.   1.\n  [Jev: AI Decisions as a Typed Function Call](https://stacktoheap.com/blog/2026/09/18/jev-doesnt-write-review-comments) \n2. 2. Jev at the Branches: The State Machine Is the Agent\n\nIn my [first Jev article](https://stacktoheap.com/blog/2026/09/18/jev-doesnt-write-review-comments), I reached a simple conclusion: Jev works best when the application—not the model—defines what can happen next.\n\n**State construction and action-space design are part of model correctness.**\n\nThat is straightforward when the decision is the final output. But what happens when it is only one step in a process that continues over time?\n\nMy answer is to put Jev inside a state machine.\n\nThe machine represents the plan, remembers where execution is, exposes only the transitions currently allowed, and waits for real outcomes before continuing. Jev appears only when the machine reaches a state with several legitimate next decisions. It weighs the current evidence and chooses among those enabled branches.\n\nThis makes the composed system agentic without making the model sovereign. Deterministic guards decide which transitions are legal. Code decides whether confidence is sufficient. Injected handlers perform effects. Tools, users, timers, and the external environment report what actually happened.\n\nTo make the idea executable, I needed a workflow with ambiguous decisions, consequential actions, delayed outcomes, and hard safety constraints. I used a **simulated canary deployment** as that example. It is not the premise of the design, and no production deployment was controlled. It is a test case for the more general architecture.\n\n# The boundary in one picture\n\nHere is the general architecture. The pink path is judgment. The blue path is fact.\n\n``` php\nflowchart TB\n  World[external world] -->|observed system event| Machine[deterministic state machine]\n  Machine -->|enabled decision transitions only| Jev[Jev]\n  Jev -->|choice + probabilities| Policy[confidence and risk policy]\n  Policy -->|approved transition| Action[injected action handler]\n  Action -->|attempt effect| World\n  World -->|observed outcome event| Machine\n  Policy -->|insufficient confidence| Pause[pause before effects]\n\n  classDef judgment fill:#fdf0fa,stroke:#e551ba,color:#3d1433,stroke-width:2px;\n  classDef fact fill:#eef7fe,stroke:#2389da,color:#0c3556,stroke-width:2px;\n  classDef neutral fill:#f7f7f8,stroke:#71717a,color:#18181b;\n  class Jev,Policy judgment;\n  class World,Action fact;\n  class Machine,Pause neutral;\n```\n\nThis is deliberately not “put an LLM in a loop and give it tools.” Jev has one job: when the current state has several legal, agent-controlled transitions, select one of them. The state machine—not Jev—is the durable agent loop.\n\nEverything else has a different owner.\n\n| Question | Owner | \n|---|---|\n| What state are we in? | State-machine snapshot | \n| Which transitions are legal? | Definition + deterministic guards | \n| Which legal response best fits the evidence? | Jev | \n| Is the result safe enough to execute? | Runtime policy | \n| How is the action performed? | Injected handler | \n| Did the action actually succeed? | Tool, user, timer, or environment | \n\n# The machine is the agent\n\nAn agent needs continuity: a representation of where it is, what may happen next, what has already happened, and when it must wait for the world. In this design, those responsibilities belong to the state machine.\n\nJev does not generate a plan or invent the next tool call. It supplies bounded judgment at authored branch points. That division produces a useful composition:\n\n```\nstate machine = plan + memory + legal transitions + waiting\nJev           = contextual judgment among enabled decisions\nruntime       = policy + effects + verified external outcomes\n```\n\n## A concrete example: deciding a canary rollout\n\nTo test that composition, I needed more than a toy choice. The example had to require repeated judgment, perform consequential actions, wait for outcomes, and preserve rules the model could not override. A canary rollout has exactly that shape.\n\nThe simulation starts a release at 5% of traffic. Each telemetry window reports facts such as baseline and canary error rates, p95 latency, request volume, and recent trend. The machine records that observation and enters an `assessing` state. Only there does it ask Jev to judge what should happen next.\n\nAt that branch, the controller may expose up to four decisions:\n\n- `PROMOTE` increases canary traffic;\n- `HOLD` keeps traffic where it is and requests another observation window;\n- `ROLLBACK` returns traffic to the stable version;\n- `ESCALATE` pauses automation for human review.\n\nThe list changes with the state. During a change freeze, for example, `PROMOTE` is absent. After the observation budget is exhausted, V3 leaves only `ESCALATE`, so the one-choice rule bypasses Jev. These are authored transitions in a serializable machine definition rather than free-form commands emitted by the model.\n\nCanary deployment is only the worked example. The same split applies anywhere a process has known states and bounded decisions: incident response, approvals, support routing, browser interaction, or recovery workflows.\n\n``` php\nstateDiagram-v2\n  [*] --> Observing\n  Observing --> Assessing: OBSERVATION_READY · system\n  Assessing --> ShiftingTraffic: PROMOTE · decision\n  Assessing --> Holding: HOLD · decision\n  Assessing --> RollingBack: ROLLBACK · decision\n  Assessing --> HumanReview: ESCALATE · decision\n  Holding --> Observing: WINDOW_ELAPSED · system\n  ShiftingTraffic --> Observing: SHIFT_COMPLETED · system\n  ShiftingTraffic --> HumanReview: SHIFT_FAILED · system\n  RollingBack --> RolledBack: ROLLBACK_COMPLETED · system\n  RollingBack --> HumanReview: ROLLBACK_FAILED · system\n  HumanReview --> Observing: HUMAN_RESUMED · system\n  HumanReview --> RolledBack: HUMAN_ABORTED · system\n  Observing --> FullyDeployed: TARGET_REACHED · system\n  FullyDeployed --> [*]\n  RolledBack --> [*]\n```\n\nThe graph does more than document the workflow. It is the authority boundary.\n\nFor example, promotion has a guard requiring enough canary requests, adequate data quality, no change freeze, and traffic below 100%. A high-risk rollback is also removed, forcing the machine toward human review instead. Guards run **before** the list of choices is constructed. Jev cannot select a transition it never receives.\n\nThe runtime then enforces a second boundary:\n\n``` js\nconst choices = decisionChoices(machine, snapshot, goal, guards);\n\nif (choices.length === 1) {\n  selection = deterministicSelection(choices[0]); // no Jev call\n} else {\n  selection = await evaluator.choose({ goal, snapshot, choices });\n}\n\nif (!choices.some(({ event }) => event === selection.event)) {\n  throw new Error(`evaluator chose disabled event: ${selection.event}`);\n}\n\nif (!policy.accepts(selection)) {\n  return { status: 'low-confidence', pendingDecision: selection };\n}\n\nreturn executeTransition(selection.event);\n```\n\nA one-choice branch bypasses the model. A disabled answer is rejected. A low-confidence answer pauses before its action. These are runtime invariants, not instructions hidden in a prompt.\n\n# Decisions and facts must be different types\n\nThe easiest way for an agent loop to lie is to blur an intended action with its outcome.\n\nSuppose Jev chooses `PROMOTE`. That means “attempt to increase traffic,” not “traffic increased.” The traffic-shift handler may fail. Even if it succeeds, the state machine should move only after the environment reports `TRAFFIC_SHIFT_COMPLETED`.\n\nThe trace therefore keeps the two moments separate:\n\n```\nsequenceDiagram\n  participant M as State machine\n  participant J as Jev\n  participant P as Policy\n  participant A as Action handler\n  participant E as Environment\n\n  E->>M: OBSERVATION_READY (fact)\n  M->>J: PROMOTE / HOLD / ROLLBACK / ESCALATE\n  J-->>P: PROMOTE + probabilities\n  P-->>M: accepted\n  M->>A: deployment.shiftTraffic\n  A->>E: request traffic change\n  E-->>M: TRAFFIC_SHIFT_COMPLETED (fact)\n  Note over M,E: A later frame applies the observed outcome\n```\n\n`TRAFFIC_SHIFT_COMPLETED`, `ROLLBACK_COMPLETED`, and `TARGET_REACHED` never appear in Jev’s choice list. They are `system` events accepted only from the host. The same distinction applies outside deployments: “send email” is a decision; “email delivered” is an observed outcome.\n\n# Replay the captured traces\n\nThe visualization below contains five reviewed traces from the original live `jev-1.13.0` matrix over synthetic telemetry. It does not call a model or require credentials. Select a scenario, step through its timeline, and watch the graph, evidence, probabilities, and policy result change together.\n\nStart with **Clear regression**. Then compare **Transient noise** and **Change freeze**: those are the two cases where the surrounding machine matters most.\n\nLive Jev trace · synthetic telemetry\n\n## Who controls the next transition?\n\nReplay one frame at a time. Pink marks a model-eligible branch; the execution banner shows whether Jev actually ran.\n\n**Environment reported OBSERVATION_READY** This factual system event came from the simulation, not from the model.\n\nDeterministic control flow\n\n### Deployment machine\n\n**OBSERVATION_READY** Observing → Assessing\n\nObserved state\n\n### Canary evidence\n\n**5%** traffic\n\n**0.98%**\n\n**16.12%**\n\n**178.5 ms**\n\n**697.4 ms**\n\n**2,492**\n\n**high**\n\n- Trend\n- worsening\n- Rollback risk\n- low\n- Change freeze\n- off\n\nNo model call\n\n### Enabled choices\n\nThis frame contains an observed system event, not a Jev choice.\n\n**Not evaluated on this frame**\n\nAudit trail\n\n### Transition timeline\n\nThere is also a [full-width standalone version](https://stacktoheap.com/demos/jev-deployment-state-machine) for smaller screens or side-by-side inspection.\n\n# What the simulation showed\n\nThe experiment used eight synthetic scenarios, three seeds, and three repetitions per seed: 72 runs in each matrix. It is not a deployment benchmark.\n\n| Measurement | V1 | V2 | V3 | \n|---|---|---|---|\n| Jev decisions | 106 | 113 | 104 | \n| Runs reaching an authored terminal outcome | 20 | 40 | 36 | \n| Low-confidence runs | 52 | 32 | 22 | \n| Input tokens | 88,920 | 213,039 | 174,578 | \n| Mean decision latency | 285.3 ms | 274.1 ms | 263.2 ms | \n\n“Authored terminal outcome” means the final status matched one small canonical fixture path. It is not an accuracy measure. V3 made that limitation especially visible: safe escalations sometimes disagreed with fixtures that preferred continued automation.\n\nThe useful findings fit into four cases:\n\n1. **Clear evidence produced a clear judgment.** Clear regression chose`ROLLBACK` and completed in all nine runs in every design.\n2. **Uncertainty stopped consequential actions.** In V1, all nine transient-noise runs chose`ROLLBACK` at only`0.26–0.44` confidence, so none executed it.\n3. **The machine enforced authority independently of Jev.** During a change freeze, a guard removed`PROMOTE` . Jev selected it zero times because it was not an available answer.\n4. **State representation changed the judgment.** When V3 replaced`trend: improving` with the numeric recovery sequence, all nine transient runs selected`HOLD` first at`0.80–0.86` confidence.\n\n## Confidence policy must reflect the transition\n\nThe first policy used one confidence floor (`0.50`) and one top-two margin (` 0.05`) for every decision. It paused 52 of 72 runs, including harmless holds and requests for human review. That exposed the key design mistake: [confidence describes how concentrated a Choice distribution is](https://docs.typesafe.ai/confidence); it does not say whether an action is safe or authorized.\n\nThe transition-aware policy combines the selected action with uncertainty:\n\n| Transition | Minimum confidence | Minimum margin | \n|---|---|---|\n| `PROMOTE` | 0.60 | 0.10 | \n| `ROLLBACK` | 0.60 | 0.10 | \n| `HOLD` | 0 | 0 | \n| `ESCALATE` | 0 | 0 | \n\nUnder V2, every low-confidence stop involved `PROMOTE` or `ROLLBACK`; no `HOLD` or `ESCALATE` stopped for low confidence. Healthy canaries completed in six of nine runs instead of two, while clear regressions still rolled back in all nine.\n\nThis was not a clean win. V2 still misread eight of nine transient cases as rollback, and all nine sparse-evidence runs did the same. Input usage also more than doubled because structured criteria were repeated on every call.\n\n## V3: give Jev the trajectory, not a label\n\nThe V2 transient state contained one currently bad window plus the summary `trend: improving`. More instructions repeated that claim, but they did not provide the evidence behind it.\n\nV3 carries at most four numeric telemetry samples ordered oldest to newest. The transient fixture starts with this sequence:\n\n| Sample | Canary errors | Canary p95 | \n|---|---|---|\n| 1 | 12.110% | 599.3 ms | \n| 2 | 7.618% | 431.0 ms | \n| 3 | 5.259% | 309.9 ms | \n\nThe canary is still worse than baseline, but the recovery is now part of the state rather than an adjective.\n\nThat changed the targeted decision: **all nine V3 transient runs chose `HOLD` first**, with confidence from `0.80` to `0.86`. V2 produced eight uncertain rollbacks and one hold.\n\nIt did not make the whole rollout autonomous. After the next healthy window, all nine selected `PROMOTE` at only `0.42–0.57` confidence. The `0.60` gate stopped every promotion. V3 fixed temporal interpretation at the branch it targeted; it did not establish end-to-end deployment success.\n\nShorter criteria also reduced input tokens per decision from 1,885.3 in V2 to 1,678.6 in V3, an 11.0% reduction. Overall input fell 18.1%, partly because the trajectories made fewer Jev calls.\n\nOther V3 changes strengthened the machine rather than the prompt:\n\n- observation-budget exhaustion leaves only `ESCALATE` , so Jev is bypassed;\n- the hard step limit applies before both decision and system transitions;\n- context delivered with a system event is committed only after the event is validated;\n- the transition-aware confidence policy remains unchanged.\n\n# The useful unit is the composed system\n\nJev is valuable after code has enforced the hard rules, where evidence remains contextual: errors are elevated but improving, latency and errors disagree, or baseline and canary fail together. Those cases can become an increasingly brittle threshold tree. A bounded Choice lets the model weigh them without taking over control flow.\n\nThis pattern fits workflows with known states, a finite set of meaningful next actions, and observable outcomes: incident response, approvals, support routing, browser interaction, and recovery workflows. It does not fit when the action itself must be invented or success cannot be observed.\n\nThe V3 result is encouraging but narrow. Numeric history fixed the transient branch, while healthy promotion remained uncertain. Lowering the threshold after seeing these captures would be tuning against the test set. The next evaluation should freeze safe, unsafe, and preferred action sets and test policy changes on new cases.\n\nThe clearest result is still architectural: a freeze made promotion impossible, uncertain traffic-changing actions stopped before effects, and factual outcomes remained outside the model’s vocabulary.\n\nThat is the design: **the state machine supplies continuity and authority; Jev supplies judgment at the branches.**", "url": "https://wpnews.pro/news/jev-at-the-branches-the-state-machine-is-the-agent", "canonical_source": "https://stacktoheap.com/blog/2026/09/21/the-state-machine-is-the-agent/", "published_at": "2026-09-21 08:30:55+00:00", "updated_at": "2026-09-21 08:53:50.499086+00:00", "lang": "en", "topics": ["ai-agents", "artificial-intelligence", "ai-tools"], "entities": ["Jev"], "alternates": {"html": "https://wpnews.pro/news/jev-at-the-branches-the-state-machine-is-the-agent", "markdown": "https://wpnews.pro/news/jev-at-the-branches-the-state-machine-is-the-agent.md", "text": "https://wpnews.pro/news/jev-at-the-branches-the-state-machine-is-the-agent.txt", "jsonld": "https://wpnews.pro/news/jev-at-the-branches-the-state-machine-is-the-agent.jsonld"}}