Architecture The core pattern
The cascade
Put each kind of decision in the cheapest tier that can make it correctly. Let each tier pass up what it can't handle. Then, every time an expensive tier resolves something, make a change in a cheaper tier so it never has to again.
What each tier does
| Tier | Owns | Never does |
|---|---|---|
| Guards | Safety reflexes in the fastest loop: pause when a health metric drops below its floor, stop a job when its supply runs out, stop at target counts | Decide anything. They act and log |
| Code | Anything computable: extracting state, listing legal options, planning from data, verifying outcomes, policies with a right answer | Guess at judgment calls |
| Decision model | Priorities among options and worth-it calls, phrased narrowly and scored per option; code combines scores with explicit weights | Plan a chain, do arithmetic, audit its own options |
| LLM | Audits plans as well as doubt; writes milestones and playbook (the context the decision model judges against); turns each escalation into a lower-tier change | Sit in the hot path for routine decisions |
| Human | Preferences, rulings, approvals, environment failures automation can't reach | Get asked what a rule could answer |
Every escalation ends with a lower-tier change
This is what makes a cascade get cheaper over time instead of just routing. When the LLM resolves an escalation, the resolution is a change that stops the same escalation from recurring:
| Escalation | Push-down | Effect |
|---|---|---|
| Selling items made only for practice (doubt 0.74) | Playbook: "items made only for practice are surplus" | Next time 0.35 |
| Retiring an obsolete supply tier (novelty 0.71) | Playbook: "older tiers are surplus once the better one is in use and the standing goal names it" | Retiring older equipment next time: novelty 0.35, no escalation |
| Doubt 0.63 on the same safe pick, three times | Playbook safety condition | 0.51, then 0.46 |
| A plan that relied on a rare outcome (doubt 0.84) | Code: plans never rely on rare outcomes | Next pick 0.41 doubt, no escalation |
Concrete milestones are a strong lever too. Against a paragraph-long goal, the decision model's picks scored 0.3–0.5. Along a named milestone's chain of steps, they scored 0.85–0.96.
Promote questions to code when the answer is computable
- The model rated "install a free upgrade into an empty slot" under 0.7 every time. The answer is never worse than nothing, so it became a code policy.
- It rated spare copies of equipment already in use as "possibly useful", so storage filled up. "Surplus under storage pressure" became a code policy.
- Picking the target for a job is arithmetic once you can compute useful output per unit of supply, with a safety cutoff. It moved to code.
Code policies ended up running roughly a fifth of ticks.
What the decision model can't see: planner gaps
The biggest single lesson from the fourth session: most bad plans reached the LLM with the decision model at low doubt (0.27–0.35). It judges the options it is given. When the planner offers only bad options, it confidently picks the best bad one. Each of these was fixed in code:
- Checked one unit, not the whole batch. Enough material for one unit passed a "can make at least once" test, so the restock rule never ran, and the plan made 30 units instead of 300.
- Off-by-one threshold. Stock of 51 sat one above a restock threshold of 50.
- Fixed buy size. Restocks bought 300 when the run needed 1,000: buy → make → buy cycles.
- Wrong metric. Ranking options by output per unit of material ignored gathering time: about 100 minutes of gathering when stock for an almost-as-good option already sat in storage.
- Wasted capacity. A hardcoded target wasted most of each unit of supply: 41 units of useful work per unit, against 51 for a better-matched target at the same risk.
So the LLM tier has to audit the plans, not only the doubt scores. Each finding goes into the planner as a rule, with a comment naming the case that motivated it.
Rules interact; state invariants per goal
- Independent policies, each correct alone, formed loops: buying one input when output ran low, though the real block was a different input; selling "surplus" of an item while also producing it. Each fix narrowed a precondition.
- A global readiness check written for one goal ("a stored backup tool counts as equipped") later sent a job out with zero of the supply it actually needed. The invariant moved to the goal that needs it.
LLM wakes are the scarce resource
In a long-running loop the expensive thing is the LLM waking up, not the decision model or wall time. Each "do a small thing, then stop" cycle costs a tick and a wake. What reduced wakes:
- Size batches to the remaining need. A fixed batch of 120 meant about 12 rounds to reach one target; sizing the batch to the remaining need (capped at 400) cut it to about 4.
- Size restocks to the whole run. One purchase of 800 instead of three of 300.
- Size standing stock to the workload. Raising standing stock of one supply from 300 to 1,000 cut about 16 cycles to about 5.
- Don't make trips for nothing. Selling a few cents of leftovers after every round cost a full trip. Leftovers now wait until storage is nearly full.
- Wake on events, not timers.
Safety goes in the fastest tier, and it needs its own audit
A 1-second in-page guard tops up reserves when a health metric drops below its floor, stops a job when its supply runs out, and stops production at target counts. Before it existed, the health metric fell from 102 to 6 between two LLM wakes. But the guard drew only from the current reserve slot; with that slot empty it did nothing, and in that environment hitting zero destroyed equipment in use. The fix: switch to the next stocked slot, and stop the job only when every slot is empty. Prevention belongs in the planner: load enough reserves before a long job.
Guards must also fail closed by construction. A guard that crashes, times out or can't reach its judge should stop the action or ask a person, never quietly let it through. Some hook systems skip a failed hook by default, so check what yours does. Claude Code mods are one: the sister site Hook Chain verified on 2.1.296 that a guard hook that throws, or runs past its 10-second budget, is skipped and the tool runs anyway, unless the guard registers a .catch that denies. Calling a decision model from a hook has its own seam: time spent waiting on the HTTP call doesn't count against that budget and the request has no timeout field, so race it against a timer and fail closed. Hook Chain's tested cascade guard does exactly that: a regex fast path, remembered human rulings, a decision model with a separate doubt check and a watch band, then a person.
What a cascade saves
Run the numbers on the cost calculator. With 70% of decisions handled by code, 25% by a decision model alone, 4.5% by an LLM and 0.5% by a person, a cascade costs well under half of sending everything to the LLM, even though both still pay for the same deep reads and the same people. And the push-down loop moves the mix toward code every week.
Next: Novelty and doubt, the two signals that decide what leaves the decision tier.