@ssheleg/agent-stack 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,90 @@ All notable changes to this project are documented here.
4
4
  Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
  Versioning: [SemVer](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.6.0] — 2026-08-12
8
+
9
+ ### Added
10
+
11
+ - **`references/runtime.md`** — the layer most orchestrators assume rather than
12
+ specify, and the one this skill was quietly missing. **Checkpoint every iteration,
13
+ not just the stages a human reviews**: the multi-stage path persisted and could
14
+ resume, the simple tool-calling path persisted nothing, so a crash lost the run that
15
+ executes most often — an asymmetry, not a design. Then one interrupt/resume contract
16
+ instead of the two mechanisms the body had for one idea (`ask_user` and a stage
17
+ checkpoint); the four double-texting policies and why interrupt and rollback differ
18
+ in what the transcript looks like afterwards; streaming with event ids so a dropped
19
+ connection rejoins instead of watching nothing for ninety seconds; forking a past
20
+ checkpoint, which debugs through the real loop rather than a reconstruction that may
21
+ not share the bug; stateful versus stateless schedules; and the seven cross-cutting
22
+ concerns welded into the loop pulled out as ordered interceptors — where **order is
23
+ semantics**, because redaction after summarisation redacts a summary that already
24
+ leaked.
25
+
26
+ - **`references/governance.md`** — permission, where `llm-proxy-billing.md` is cost.
27
+ **The greatest risk is usually not what the model says but what the agent can do**,
28
+ so the four boundaries get four control sets: model call, tool call, external server,
29
+ and agent-to-agent — the last being the one designs miss, since a sub-agent
30
+ inheriting its caller's authority silently widens every permission. The guardrail
31
+ taxonomy in order of reliability, and its honest limit: every content check is
32
+ probabilistic, so anything consequential takes a deterministic limit or a human, never
33
+ a classifier's confidence. Why an audit row without a **policy version** cannot prove
34
+ a control was applied. Cost attribution as a hierarchy, because "which team's agent
35
+ did this" is unanswerable from a flat tenant id. Failover that must be
36
+ policy-equivalent rather than merely available — a chain that silently fails into
37
+ another jurisdiction does it precisely when nobody is reading logs. Fail-open versus
38
+ fail-closed per workload. And blast radius: a sandbox protects the host, not the
39
+ sandbox, and credentials never enter it.
40
+
41
+ ### Changed
42
+
43
+ - **The References table is an index again.** Each reference now opens with its own
44
+ `Load this when` line, so the trigger has exactly one home and the table cannot drift
45
+ from the files it points at. Compressing it returned ~100 tokens of body budget, which
46
+ is what paid for two new rows: the body sits at 489 lines / ~4883 tokens against
47
+ 500 / 5000.
48
+ - README describes two skills and five references, and its trigger section covers the
49
+ evals skill and the permission surface, not only the orchestrator and the wallet.
50
+
51
+ ## [0.5.0] — 2026-08-12
52
+
53
+ ### Added
54
+
55
+ - **A second skill: `agent-evals`** — how you know the thing built by
56
+ `agent-orchestrator` actually behaves. There was no evaluation doctrine anywhere in
57
+ this family; `grep -ril "llm-as-judge\|eval dataset\|regression fixture\|trace id"`
58
+ across four plugins returned nothing.
59
+
60
+ An agent's behaviour is not in its source — the code says what it is allowed to do,
61
+ only a run says what it did — so the artifact under test is the execution record.
62
+ Three primitives (run, trace, thread) crossed with three granularities (single-step,
63
+ full-turn, multi-turn), each with its own fixture shape and its own precondition:
64
+ step assertions need a stable architecture or they die at the next refactor; turn
65
+ assertions cover trajectory **and** response **and** state change, because an agent
66
+ that says it saved the preference and did not passes two axes out of three; thread
67
+ scripts checkpoint after every turn and fail fast, or turn 3 derails and turns 4–10
68
+ assert nothing while still reporting a result.
69
+
70
+ Then the parts that decide whether any of it is trustworthy: the offline/online/ad-hoc
71
+ timing axis and why offline is necessary and not sufficient; pass-fail rubrics with
72
+ enumerated failure conditions instead of scalar scores that name no fix; code checks
73
+ before model judges; judging the trajectory, not just the answer; **calibrating a judge
74
+ against human labels before trusting it**, because an uncalibrated judge is an opinion
75
+ with a number attached; the classes of output no general judge can grade; a corpus
76
+ grown from production failures where every fixed failure stays a fixture permanently;
77
+ annotation queues with the two reviewer roles kept apart; and simulated users made
78
+ deliberately worse so offline results predict production.
79
+
80
+ Body 214 lines / ~2500 tokens; description 911/1024 with paired RU triggers.
81
+
82
+ ### Changed
83
+
84
+ - **Both installers iterate over `skills/` instead of naming one skill.** `install.sh`
85
+ and `bin/agent-stack.js` each hardcoded `agent-orchestrator`, so a second skill would
86
+ have shipped in the package and reached nobody. Verified by running both against a
87
+ clean `HOME` and listing what arrived. The CI smoke test now asserts both skills.
88
+ - README and both manifests describe two skills; the marketplace entry's description is
89
+ the manifest's, rather than a second one drifting beside it.
90
+
7
91
  ## [0.4.0] — 2026-08-12
8
92
 
9
93
  ### Added
package/README.md CHANGED
@@ -9,7 +9,8 @@ Part of the [ssheleg skill family](https://github.com/ssheleg/sshlg-skills).
9
9
 
10
10
  ## What is in here
11
11
 
12
- One skill, `agent-orchestrator`, and two references it loads on demand.
12
+ Two skills `agent-orchestrator` for building one, `agent-evals` for proving it
13
+ behaves — and five references the first loads on demand.
13
14
 
14
15
  **The orchestrator** (`SKILL.md`) — what the agent reads first:
15
16
 
@@ -29,6 +30,39 @@ One skill, `agent-orchestrator`, and two references it loads on demand.
29
30
  - context budget allocation by priority
30
31
  - self-learning feedback loops
31
32
 
33
+ **The evals skill** (`agent-evals/SKILL.md`) — how you know any of it works. An
34
+ agent's behaviour is not in its source, so the artifact under test is the
35
+ execution record: three primitives (run, trace, thread) crossed with three
36
+ granularities (single-step, full-turn, multi-turn), the offline/online/ad-hoc
37
+ timing axis, pass-fail rubrics instead of scalar scores that name no fix, cheap
38
+ code checks before model judges, judges calibrated against human labels before
39
+ they are trusted, and a corpus grown from production failures rather than
40
+ authored up front — where every fixed failure stays a fixture permanently.
41
+
42
+ **`references/context-engineering.md`** — what the loop gives up when the window
43
+ runs out: the five-rung compaction ladder and why to re-measure between rungs,
44
+ the tool-pair boundary invariant, typed carryover blocks copied across the
45
+ boundary rather than summarized, tool-output offload to a file, token estimation
46
+ and the direction it errs, the compaction circuit breaker, sub-agent context
47
+ isolation, and how to choose constants for your own window.
48
+
49
+ **`references/runtime.md`** — what keeps an agent alive between requests, which
50
+ most orchestrators assume rather than specify: checkpointing every iteration and
51
+ not just the stages a human reviews, one interrupt/resume contract instead of two
52
+ mechanisms for one idea, the four double-texting policies, streaming a dropped
53
+ connection can rejoin, forking a past checkpoint to debug through the real loop,
54
+ stateful versus stateless schedules, and the seven cross-cutting concerns pulled
55
+ out of the loop into ordered interceptors — where order is semantics.
56
+
57
+ **`references/governance.md`** — permission rather than cost. The four boundaries
58
+ an agent crosses (model, tool, external server, agent-to-agent), each with its own
59
+ control set; the guardrail taxonomy and its honest limit — every content check is
60
+ probabilistic, so anything consequential gets a deterministic limit or a human;
61
+ why an audit row without a policy version cannot prove a control was applied;
62
+ cost attribution as a hierarchy; failover that must land somewhere approved rather
63
+ than merely available; fail-open versus fail-closed as a per-workload decision;
64
+ and blast radius — a sandbox protects the host, not the sandbox.
65
+
32
66
  **`references/patterns.md`** — the data models and algorithms underneath:
33
67
  message and result protocols, pipeline models, the SQL validation loop,
34
68
  context-window sizes and token estimation, learning-extraction heuristics,
@@ -54,7 +88,7 @@ waterfall, and model-routing precedence.
54
88
  /plugin install agent-stack@agent-stack
55
89
  ```
56
90
 
57
- **npm installer** — copies the skill into `~/.claude/skills/`:
91
+ **npm installer** — copies both skills into `~/.claude/skills/`:
58
92
 
59
93
  ```bash
60
94
  npx @ssheleg/agent-stack
@@ -78,13 +112,19 @@ Restart your agent afterwards — skills load at session start.
78
112
 
79
113
  ## When it triggers
80
114
 
81
- Building an agent system, an orchestrator, an LLM-powered tool, a chatbot with
82
- tool use, or an AI pipeline. Also when the work is the money side: metering
83
- usage, per-tenant keys, spend tracking, budget limits, loop detection.
115
+ `agent-orchestrator`: building an agent system, an orchestrator, an LLM-powered
116
+ tool, a chatbot with tool use, or an AI pipeline. Also the money side metering
117
+ usage, per-tenant keys, spend tracking, budget limits, loop detection — and the
118
+ permission side: what a tool may reach, what leaves the boundary, and what an
119
+ audit row has to carry to prove a control was on.
120
+
121
+ `agent-evals`: measuring whether the result behaves. Building a suite, judging a
122
+ trajectory rather than a final answer, turning a production failure into a
123
+ permanent fixture, calibrating a judge, gating a release on offline evals.
84
124
 
85
- It does **not** trigger for a single LLM call in a script, or for prompt
86
- wording — that is not an orchestrator, and pulling 1200 lines of doctrine for it
87
- is how a skill teaches you to route around it.
125
+ Neither triggers for a single LLM call in a script or for prompt wording — that
126
+ is not an orchestrator, and pulling this much doctrine for it is how a skill
127
+ teaches you to route around it.
88
128
 
89
129
  ---
90
130
 
@@ -2,7 +2,7 @@
2
2
  /*
3
3
  * agent-stack installer CLI.
4
4
  *
5
- * Installs the agent-orchestrator skill into ~/.claude/skills/agent-orchestrator
5
+ * Installs every skill this plugin ships into ~/.claude/skills/<name>
6
6
  * (same layout as install.sh). Idempotent: an existing install is skipped unless
7
7
  * --force. Zero dependencies.
8
8
  *
@@ -21,7 +21,7 @@ function usage() {
21
21
  console.log(`agent-stack installer
22
22
 
23
23
  Usage:
24
- npx @ssheleg/agent-stack [--force] install the agent-orchestrator skill
24
+ npx @ssheleg/agent-stack [--force] install every skill this plugin ships
25
25
  into ~/.claude (skip existing unless --force)
26
26
  npx @ssheleg/agent-stack --help
27
27
 
@@ -67,20 +67,34 @@ function main(argv) {
67
67
  return 2;
68
68
  }
69
69
 
70
- const skillSrc = path.join(ROOT, 'plugins/agent-stack/skills/agent-orchestrator');
71
- if (!fs.existsSync(skillSrc)) {
72
- console.error(`error: skill sources missing at ${skillSrc} — corrupted package?`);
70
+ const skillRoot = path.join(ROOT, 'plugins/agent-stack/skills');
71
+ if (!fs.existsSync(skillRoot)) {
72
+ console.error(`error: skill sources missing at ${skillRoot} — corrupted package?`);
73
+ return 1;
74
+ }
75
+
76
+ // Iterate rather than name one skill: a skill added to the plugin must not
77
+ // require an installer change to reach anybody.
78
+ const names = fs
79
+ .readdirSync(skillRoot, { withFileTypes: true })
80
+ .filter((e) => e.isDirectory())
81
+ .map((e) => e.name)
82
+ .sort();
83
+ if (!names.length) {
84
+ console.error(`error: no skills found under ${skillRoot} — corrupted package?`);
73
85
  return 1;
74
86
  }
75
87
 
76
88
  const home = os.homedir();
77
- installOne(
78
- 'agent-orchestrator skill',
79
- skillSrc,
80
- path.join(home, '.claude', 'skills', 'agent-orchestrator'),
81
- true,
82
- force
83
- );
89
+ for (const name of names) {
90
+ installOne(
91
+ `${name} skill`,
92
+ path.join(skillRoot, name),
93
+ path.join(home, '.claude', 'skills', name),
94
+ true,
95
+ force
96
+ );
97
+ }
84
98
  return 0;
85
99
  }
86
100
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ssheleg/agent-stack",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Production patterns for AI agent orchestrators — tool-calling loops, multi-stage pipelines with checkpoints, LLM provider routing with fallback, four-layer memory with confidence decay — plus the wallet side of reselling LLM access. This package is the installer CLI.",
5
5
  "bin": {
6
6
  "agent-stack": "bin/agent-stack.js"
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "agent-stack",
3
3
  "displayName": "Agent Stack",
4
- "description": "Production patterns for AI agent orchestrators: tool-calling loops, multi-stage pipelines with checkpoints, LLM provider routing with fallback, four-layer memory with confidence decay — plus the wallet side of reselling LLM access.",
5
- "version": "0.4.0",
4
+ "description": "Two skills: agent-orchestrator \u2014 tool-calling loops, multi-stage pipelines with checkpoints, provider routing with fallback, four-layer memory, context engineering, plus the wallet side of reselling LLM access; and agent-evals \u2014 run/trace/thread evals, judges, and fixtures grown from production.",
5
+ "version": "0.6.0",
6
6
  "author": {
7
7
  "name": "ssheleg",
8
8
  "url": "https://x.com/sshlg93"
@@ -19,6 +19,9 @@
19
19
  "openrouter",
20
20
  "llm-billing",
21
21
  "claude-code",
22
- "cursor"
22
+ "cursor",
23
+ "evals",
24
+ "llm-judge",
25
+ "observability"
23
26
  ]
24
27
  }
@@ -0,0 +1,230 @@
1
+ ---
2
+ name: agent-evals
3
+ description: >-
4
+ Use when measuring whether an agent actually works — building an eval suite, judging a
5
+ trajectory rather than a final answer, turning production traces into regression
6
+ fixtures, calibrating an LLM judge against human labels, or gating a release on offline
7
+ evals. Covers the three observability primitives (run, trace, thread) crossed with three
8
+ eval granularities (single-step, full-turn, multi-turn), the offline/online/ad-hoc
9
+ timing axis, pass-fail rubrics over scalar scores, cheap code checks before model
10
+ judges, simulated users with adversarial personas, annotation queues, and what to
11
+ instrument so any of it is possible. Triggers - "agent eval", "eval suite", "LLM judge",
12
+ "regression fixture", "trajectory eval", "is the agent getting better", "эвалы агента",
13
+ "оценка агента", "LLM-судья", "регрессионный набор", "как проверить агента". Not for
14
+ unit tests of ordinary code, or for benchmarking a model.
15
+ license: MIT
16
+ ---
17
+
18
+ # Agent evals — proving the thing behaves
19
+
20
+ An agent's behaviour does not exist in its source. The code says what it is *allowed* to
21
+ do; only a run says what it *did*. So the artifact under test is the execution record,
22
+ and the suite is grown from production rather than authored up front.
23
+
24
+ Three claims follow, and they are what makes this different from testing ordinary code:
25
+
26
+ - **You are testing reasoning, not code paths**, so one granularity is never enough.
27
+ - **Every natural-language input is unique**, so the edge cases cannot be enumerated
28
+ offline. Production is not only where you catch what you missed — it is where you
29
+ discover what to test for.
30
+ - **Traces become test cases.** The suite grows from what actually happened.
31
+
32
+ ---
33
+
34
+ ## 1. Three primitives
35
+
36
+ | Primitive | Is | Carries |
37
+ |---|---|---|
38
+ | **Run** | one model call | the complete prompt — instructions, tools, context — and what came back |
39
+ | **Trace** | one full execution | every run, every tool call with arguments and results, nested to show how steps relate |
40
+ | **Thread** | many traces in one session | multi-turn context in order, **state evolution** (memory, files, artifacts), and elapsed time |
41
+
42
+ The thread level is the one most systems lack, and it is where a whole class of defect
43
+ lives: a bad memory write at turn 6 surfaces as a wrong answer at turn 11, and neither
44
+ the run nor the trace view can localise it.
45
+
46
+ **Precondition for all of this: traces are durable and queryable.** A live event stream
47
+ renders a progress bar and evaporates. If you cannot fetch last week's low-scoring runs
48
+ by id, nothing below is available to you — see §7.
49
+
50
+ ---
51
+
52
+ ## 2. Three granularities
53
+
54
+ Each primitive gets its own kind of assertion.
55
+
56
+ ### Single-step — validates a run
57
+
58
+ Fixture is a serialized run: prompt, tool schemas, context. Assert the decision at that
59
+ point — tool name, argument shape.
60
+
61
+ > "Schedule a meeting with Harrison tomorrow morning", with `find_meeting_times`,
62
+ > `schedule_meeting` and `send_email` available, must call `find_meeting_times` first.
63
+
64
+ Cheap, deterministic, CI-blocking. **Precondition: a stable agent architecture.** These
65
+ break on a graph refactor, and a suite that fails on every refactor gets deleted.
66
+
67
+ ### Full-turn — validates a trace
68
+
69
+ Assert on three axes at once, with three different mechanisms:
70
+
71
+ | Axis | Assert | With |
72
+ |---|---|---|
73
+ | Trajectory | tool-call sequence — `read_file` → `edit_file` → `run_tests` | set/subset/order matchers |
74
+ | Final response | quality, tone, policy compliance | rubric or judge |
75
+ | **State change** | the memory row exists, the file was written, the artifact is there | direct inspection of the side effect |
76
+
77
+ The third axis is the one people forget. **Assert on side effects, not only on prose** —
78
+ an agent that says it saved the preference and did not is a pass on two axes out of three.
79
+
80
+ Easiest inputs to generate, hardest outputs to validate automatically.
81
+
82
+ ### Multi-turn — validates a thread
83
+
84
+ A scripted turn sequence with a **checkpoint after every turn and fail-fast on
85
+ deviation**. Without that, turn 3 goes off the rails and turns 4–10 assert nothing while
86
+ still reporting a result.
87
+
88
+ > Turn 1: "I prefer Python over JavaScript." Turn 3's output must still be Python.
89
+
90
+ Hardest to implement well. Start here only for behaviour that is genuinely about memory
91
+ across turns.
92
+
93
+ **Production suites combine all three.** One vendor reports about half of theirs sitting
94
+ at single-step — recorded as their observation, not as a target to hit.
95
+
96
+ ---
97
+
98
+ ## 3. Offline, online, ad-hoc
99
+
100
+ | When | Reference | Blocks a release? | Answers |
101
+ |---|---|---|---|
102
+ | **Offline** | a dataset, ground truth optional | **yes** — this is the gate | did my change break what used to work |
103
+ | **Online** | none — **definitionally reference-free** | no | is production drifting |
104
+ | **Ad-hoc** | none | no | what is actually happening out there |
105
+
106
+ **Offline is necessary and not sufficient.** A green suite proves you did not regress the
107
+ cases you already know about. It cannot prove the agent handles what nobody thought of,
108
+ because that input is not in the dataset — which is what the online tier is for.
109
+
110
+ Online evaluators fire on trace ingestion and check what needs no expected answer:
111
+ trajectory anomalies, step-count and latency trends, judge scores, error rates. Route
112
+ them over all traces, a sample, or a filtered subset; the sampling rate is a cost
113
+ decision, not a correctness one.
114
+
115
+ Ad-hoc is exploratory analysis over stored traces — clustering to surface failure modes
116
+ nobody predefined. A dashboard tracks metrics you chose in advance; this finds the ones
117
+ you did not.
118
+
119
+ ---
120
+
121
+ ## 4. Rubrics beat scores
122
+
123
+ Generic metrics — helpfulness, naturalness, completeness — produce numbers and no
124
+ decision. A 3.4 out of 5 on "helpfulness" tells you nothing about what to change.
125
+
126
+ **Write narrow, behaviour-specific pass/fail rubrics, and write them with the people who
127
+ own the behaviour.** Each failure must point at one thing: a prompt, a tool description,
128
+ a workflow step, a missing capability.
129
+
130
+ A rubric that works, in full:
131
+
132
+ > **Escalation.** On a request for a human: push back once, escalate on the repeat.
133
+ > **Fails if** it escalates immediately · refuses after the second request · escalates
134
+ > before providing information it already had · continues several turns past the point it
135
+ > is clearly not helping.
136
+
137
+ Note the shape: one behaviour, an explicit pass condition, and an enumerated failure list.
138
+ That is what makes a judge reproducible and a disagreement resolvable.
139
+
140
+ ---
141
+
142
+ ## 5. Judges
143
+
144
+ **Cheap checks first.** Schema validation, exact match, format conformity, business-rule
145
+ assertions, tool-call correctness — all deterministic, all faster and cheaper than a model
146
+ call. Send to a judge only what cannot be decided by code.
147
+
148
+ **Judge the trajectory, not just the answer.** Right tools, right order, right arguments.
149
+ An agent that reaches a correct answer through three wrong tool calls is a latent outage.
150
+
151
+ **Calibrate the judge before trusting it.** Collect human labels on the same traces,
152
+ measure agreement, iterate the judge prompt until agreement is high — *then* let it score
153
+ unattended. An uncalibrated judge is an opinion with a number attached, and shipping on it
154
+ is exactly the failure of grading instead of measuring.
155
+
156
+ **Some things a judge cannot do.** Plausible-but-wrong domain output — an invented legal
157
+ citation, a subtly wrong SQL join — reads as correct to a general judge. Route those to a
158
+ domain expert and accept that this tier stays human.
159
+
160
+ ---
161
+
162
+ ## 6. The corpus grows from production
163
+
164
+ Never author the suite up front. Every production failure and every thumbs-down becomes a
165
+ fixture:
166
+
167
+ 1. Capture the state at the failure point.
168
+ 2. Minimise it to the smallest input that still reproduces.
169
+ 3. Add it to the tier that isolates it — step, turn or thread.
170
+ 4. **It stays in the suite permanently.** A fixed bug that silently returns is the defect
171
+ this rule exists to prevent.
172
+
173
+ Two dataset shapes come out of review:
174
+
175
+ - **Ground truth** — the reviewer writes the correct output; the suite asserts equality.
176
+ - **Criteria-based** — for open-ended work, the reviewer labels dimensions (relevance,
177
+ completeness, tone) instead of an exact answer.
178
+
179
+ **The annotation queue** is what feeds both: filters route a subset of traces to humans —
180
+ low automated score, thumbs-down, a feature area, a cluster. Two reviewer roles, and
181
+ mixing them wastes both: generalists judge surface quality, domain experts judge
182
+ correctness only they can see.
183
+
184
+ **Simulated users, if you generate inputs.** A model playing a customer is articulate,
185
+ patient and cooperative, and inflates every pass rate. Fine-tune it on real user
186
+ transcripts and add adversarial personas — the refund-seeker, the AI-sceptic, the one who
187
+ wants a human immediately. Making the simulated user worse makes the offline result
188
+ predictive.
189
+
190
+ ---
191
+
192
+ ## 7. What to instrument first
193
+
194
+ None of the above runs without these, and they are the part people skip:
195
+
196
+ - **A durable trace store, queryable by id, filterable by score and time.** The live
197
+ stream is a view over it, never the record itself.
198
+ - **Scores as first-class records bound to a run**: `run_id`, key, value, and a `source`
199
+ of `human` | `llm_judge` | `code_check`. A score with no source cannot be calibrated,
200
+ audited, or trusted differently from its neighbours.
201
+ - **Whole prompts, not just messages** — instructions, tool schemas and context as they
202
+ were sent. A fixture cannot be replayed from a summary.
203
+ - **State snapshots at turn boundaries**, so a thread test can assert what carried.
204
+
205
+ ---
206
+
207
+ ## Checklist
208
+
209
+ - [ ] Traces durable and queryable by id, not only streamed
210
+ - [ ] Scores bound to runs, each with a source
211
+ - [ ] Single-step fixtures for the decisions that must not drift
212
+ - [ ] Full-turn assertions on trajectory **and** final response **and** state change
213
+ - [ ] Multi-turn scripts with a checkpoint after every turn, failing fast
214
+ - [ ] Offline suite as the release gate; online evaluators reference-free and non-blocking
215
+ - [ ] Pass/fail rubrics with enumerated failure conditions, written with behaviour owners
216
+ - [ ] Code checks before model judges
217
+ - [ ] Judge calibrated against human labels before it is trusted
218
+ - [ ] Domain-expert review for output a general judge cannot grade
219
+ - [ ] Every production failure minimised into a permanent fixture
220
+ - [ ] Annotation queue with filters, and the two reviewer roles kept separate
221
+ - [ ] Simulated users trained on real transcripts, with adversarial personas
222
+
223
+ ---
224
+
225
+ ## Related
226
+
227
+ Building the agent this measures is the **`agent-orchestrator`** skill, shipped in the
228
+ same plugin — named rather than linked, because a packager may ship either directory
229
+ alone. Its context-engineering reference covers the context-pressure behaviour that
230
+ trajectory assertions most often catch drifting.
@@ -490,11 +490,14 @@ transcript.
490
490
 
491
491
  ## References
492
492
 
493
- Load these when the task reaches them the checklist above is the map, these
494
- are the territory.
493
+ The checklist above is the map, these are the territory. Each file opens with its
494
+ own **Load this when** line — the authoritative trigger lives there, so this table
495
+ stays an index and the two cannot drift apart.
495
496
 
496
497
  | File | Read it when |
497
498
  |---|---|
498
- | [`references/patterns.md`](references/patterns.md) | you need the **data models and algorithms**: message and result protocols, pipeline models, the SQL validation loop, context-window sizes and token estimation, learning-extraction heuristics, confidence lifecycle, fuzzy dedup, conflict resolution, cross-resource transfer, the no-LLM suggestion engine |
499
- | [`references/context-engineering.md`](references/context-engineering.md) | the loop is **running out of window**: the five-rung compaction ladder, the tool-pair boundary invariant, typed carryover attachments, tool-output offload, token estimation, the compaction circuit breaker, sub-agent isolation, and how to pick your own constants |
500
- | [`references/llm-proxy-billing.md`](references/llm-proxy-billing.md) | the product **resells LLM access**: tiered wallets and where markup applies, two-phase commit against a provider API with compensating transactions, advisory locking, optimistic concurrency for reclaims, spend-delta polling and its three cases, budget/loop/auto-pause guardrails, per-tenant key lifecycle and healing, the refund waterfall, model routing |
499
+ | [`references/patterns.md`](references/patterns.md) | you need the **data models and algorithms** under the body |
500
+ | [`references/context-engineering.md`](references/context-engineering.md) | the loop is **running out of window** |
501
+ | [`references/runtime.md`](references/runtime.md) | the agent must **survive a crash, a pause, a second message or a schedule** |
502
+ | [`references/governance.md`](references/governance.md) | the question is **permission, not cost** — what it may do, and how you prove it |
503
+ | [`references/llm-proxy-billing.md`](references/llm-proxy-billing.md) | the product **resells LLM access** |
@@ -0,0 +1,138 @@
1
+ # Governance — what the agent is allowed to do
2
+
3
+ **Load this when** the question is permission rather than money: which model may see
4
+ which data, which tool may run against production, what leaves the infrastructure
5
+ boundary, and how you prove afterwards that the control was on. `llm-proxy-billing.md`
6
+ answers *what did this cost and who pays* — this answers *should it have happened at
7
+ all*, and the two are separate systems that share an audit row.
8
+
9
+ The claim worth keeping: **for an agent, the greatest risk is usually not what the model
10
+ says, but what the agent can do.** Content filters are aimed at the first. Most real
11
+ damage comes through the second.
12
+
13
+ ## Contents
14
+
15
+ - Four boundaries, four control sets
16
+ - Guardrails, and their honest limit
17
+ - Where a control runs: before or after
18
+ - The audit row
19
+ - Cost attribution as a hierarchy
20
+ - Failover must land somewhere approved
21
+ - Fail-open or fail-closed, decided by risk
22
+ - Blast radius: sandboxes and credentials
23
+
24
+ ## Four boundaries, four control sets
25
+
26
+ An agent crosses four kinds of boundary, and treating them as one is why "we have
27
+ guardrails" so often means only the first.
28
+
29
+ | Boundary | The risk | Controls that fit |
30
+ |---|---|---|
31
+ | **Model call** | cost; prompt content reaching a provider's logs | spend limits, redaction, provider routing, data-residency choice |
32
+ | **Tool call** | an unintended action on a real system | per-tool authorisation, argument validation, an audit row per invocation |
33
+ | **External server call** (MCP and similar) | data leaving your infrastructure boundary | allowlist of servers, logging, explicit scope per server |
34
+ | **Agent-to-agent** | errors compounding down a chain; context passed on without authority | tracing across hops, and **policy enforced at each hop**, not only at the entrance |
35
+
36
+ The last one is the one most designs miss: a sub-agent that inherits its caller's
37
+ authority silently widens every permission the caller had.
38
+
39
+ ## Guardrails, and their honest limit
40
+
41
+ The content-layer checks worth having, roughly in order of reliability:
42
+
43
+ - **Structured secrets and identifiers** — API keys, tokens, card numbers, national ids.
44
+ Pattern-matched, high precision, cheap. Run these first.
45
+ - **Unstructured personal data** — names, locations, affiliations. Needs entity
46
+ recognition; precision drops.
47
+ - **Injection and jailbreak attempts** — classifier-based, adversarial by nature, and
48
+ the arms race is not winnable by pattern alone.
49
+ - **Groundedness** — does the answer follow from the retrieved material. Runs on output,
50
+ costs a model call, and is the least reliable of the four.
51
+
52
+ **Then the rule that makes the list honest: guardrails reduce risk, they do not
53
+ eliminate it.** Every item above is probabilistic. So for anything consequential —
54
+ money moving, data deleted, a message sent to a customer, a deploy — the control is a
55
+ **deterministic limit or a human**, never a classifier's confidence. A guardrail is a
56
+ filter on the way to a decision, not the decision.
57
+
58
+ A useful signal on top: **a sudden spike in guardrail violations is usually the first
59
+ sign that something upstream is wrong** — a prompt change, a new data source, an agent
60
+ in a loop. Alert on the rate, not just on the individual hit.
61
+
62
+ ## Where a control runs: before or after
63
+
64
+ Almost everything belongs **before** the call: redaction, secret detection, provider
65
+ routing, rate and spend limits, tool authorisation. A control that runs after the
66
+ request has left has already failed at the thing it was for.
67
+
68
+ **After** the call, only what needs the output: groundedness, moderation of generated
69
+ text, structured-output validation.
70
+
71
+ Two consequences: pre-call controls sit on the latency path, so they must be cheap
72
+ enough to run every time; and a control that can only run post-call must be paired with
73
+ something that can undo or withhold the result.
74
+
75
+ ## The audit row
76
+
77
+ An audit row exists to answer a question months later, when the person who ran the agent
78
+ is unavailable. It needs:
79
+
80
+ - **who** — the identity that ran the workload, and separately the identity that last
81
+ changed the policy
82
+ - **what** — the action, its arguments in redacted form, and the outcome
83
+ - **which policy version applied** — this is the field everyone omits and the one that
84
+ makes the record evidence. "The control was on" is unprovable without it; a policy
85
+ that changed twice since is unfalsifiable without it.
86
+ - **which model and which tools were reached**, including through sub-agents
87
+ - **when**, at a precision that survives clock skew between services
88
+
89
+ The rule to hold: **an audit trail written for reconciliation answers "where did the
90
+ money go"; an audit trail written for governance answers "prove the control was
91
+ applied".** They are different queries and the second needs the policy version.
92
+
93
+ ## Cost attribution as a hierarchy
94
+
95
+ The billing reference tracks spend per tenant. Governance needs it resolvable up a
96
+ chain: **organisation → business unit → team → credential → individual**. Not because
97
+ finance asks, but because the question that actually gets asked in an incident is "which
98
+ team's agent did this", and a flat tenant id cannot answer it.
99
+
100
+ Limits belong at more than one level too — a per-credential cap does not stop twenty
101
+ credentials in one team from draining a budget together.
102
+
103
+ ## Failover must land somewhere approved
104
+
105
+ The router in the body falls back to the next healthy provider. Governance adds one
106
+ constraint: **the fallback must be policy-equivalent, not merely available.**
107
+
108
+ A chain that silently fails over to a provider with different data handling, a different
109
+ jurisdiction, or a different retention policy has moved the data somewhere nobody
110
+ approved — and it does it precisely during an incident, when nobody is reading logs. Tag
111
+ each provider with the policy it satisfies, and let the fallback chain filter on the tag
112
+ before it filters on health.
113
+
114
+ ## Fail-open or fail-closed, decided by risk
115
+
116
+ When the control plane itself is unavailable — the guardrail service times out, the
117
+ policy store is unreachable — the system either proceeds without the check or refuses.
118
+ **Both answers are correct for different workloads, and neither is a default.**
119
+
120
+ - Fail-**open** for a low-risk, high-volume path where refusing is the bigger harm.
121
+ - Fail-**closed** for anything consequential.
122
+
123
+ Write the choice down per workload, and make the control plane itself redundant enough
124
+ that the choice is rarely exercised: timeouts, load balancing, and a health check that
125
+ distinguishes "slow" from "gone".
126
+
127
+ ## Blast radius: sandboxes and credentials
128
+
129
+ When an agent runs code, two rules carry most of the weight:
130
+
131
+ - **A sandbox protects the host, not the sandbox.** Anything the agent can reach *from
132
+ inside* is still reachable — network egress, mounted paths, environment. Restrict
133
+ egress explicitly and allowlist commands rather than denylisting.
134
+ - **Credentials never enter the sandbox.** Put a proxy in front that injects them per
135
+ request, so a prompt injection that dumps the environment gets nothing worth having.
136
+
137
+ Ephemeral is the default: create on demand, tear down after, never reuse across tenants.
138
+ A long-lived sandbox accumulates state that nobody audits.
@@ -1,5 +1,12 @@
1
1
  # Reselling LLM access — metering, wallets and guardrails
2
2
 
3
+ **Load this when** the product resells LLM access: tiered wallets and the single
4
+ boundary where markup applies, two-phase commit across a database and a provider API
5
+ with compensating transactions, advisory locking, optimistic concurrency for reclaims,
6
+ spend-delta polling and its three cases, budget / loop / auto-pause guardrails,
7
+ per-tenant key lifecycle and healing, the refund waterfall, and model-routing
8
+ precedence.
9
+
3
10
  When your product gives users LLM access and bills for it, you are running a
4
11
  proxy with a wallet behind it. The failure modes are not model failures: they
5
12
  are **double-credited transfers**, **spend you discovered after it happened**,
@@ -1,6 +1,10 @@
1
1
  # Agent Orchestrator — Reference Guide
2
2
 
3
- Extended patterns, data models, and implementation details.
3
+ **Load this when** you need the data models and algorithms under the body: message
4
+ and result protocols, pipeline models, the SQL validation loop, context-window sizes
5
+ and token estimation, learning-extraction heuristics, the confidence lifecycle, fuzzy
6
+ deduplication, conflict resolution, cross-resource transfer, and the suggestion engine
7
+ that costs no LLM call.
4
8
 
5
9
  ## Contents
6
10
 
@@ -0,0 +1,151 @@
1
+ # Runtime — what keeps an agent alive between requests
2
+
3
+ **Load this when** the agent must survive things a single request does not: a crash
4
+ mid-run, a human who has to approve before it continues, a user who sends a second
5
+ message while the first is still working, a dropped connection, a schedule. The body's
6
+ loop is the *harness* — what the model is given to work with. This is the layer beneath
7
+ it, and most orchestrators assume it exists rather than specify it.
8
+
9
+ The split is worth keeping in mind while reading: **a good harness makes an agent
10
+ capable, a good runtime makes it deployable.** They fail differently, and a team that
11
+ has only built the first one discovers the second in production.
12
+
13
+ ## Contents
14
+
15
+ - Durability, and where our own asymmetry was
16
+ - The interrupt/resume contract
17
+ - Double-texting: four policies
18
+ - Streaming that survives a dropped connection
19
+ - Time travel and forking
20
+ - Scheduled and sleep-time work
21
+ - Middleware: the seven concerns, unwelded
22
+
23
+ ## Durability, and where our own asymmetry was
24
+
25
+ **Checkpoint every iteration of the loop, not just the stages a human reviews.**
26
+
27
+ The body's multi-stage pipeline persists at each `stage.checkpoint` and can resume from
28
+ a `pipeline_run_id`. The simple tool-calling path persists nothing: a crash, a deploy or
29
+ a killed worker loses the entire run, including the tool calls that already cost money
30
+ and time. That asymmetry is a defect, not a design — the simple path is the one that
31
+ runs most often.
32
+
33
+ What a checkpoint holds: the message array, the iteration counter, accumulated token
34
+ usage, the carryover state (see `context-engineering.md`), and whatever the sub-agents
35
+ have returned so far. Keyed by a thread id that acts as a cursor into the run.
36
+
37
+ Two properties earn their cost:
38
+
39
+ - **Resume at the point of failure**, not at the last human review. The difference is
40
+ whole minutes of re-executed tool calls.
41
+ - **A pause frees the worker.** An agent waiting for a human should hold no process. If
42
+ waiting costs a worker, long approvals are quietly expensive and teams stop using them.
43
+
44
+ ## The interrupt/resume contract
45
+
46
+ The body has two mechanisms for one idea: `ask_user` raises a clarification error, and a
47
+ pipeline checkpoint returns a paused result. **They should be one contract.**
48
+
49
+ - **Interrupt** — the run stops at a named point, persists its state, and surfaces a
50
+ payload describing what it needs: a question, a plan to approve, a destructive action
51
+ to confirm.
52
+ - **Resume** — the caller returns a decision, and execution continues *from that point*
53
+ with the decision in scope. Not a fresh run that re-derives its way back.
54
+
55
+ One contract means one persistence format, one place a UI has to understand, and one
56
+ answer to "what happens if nobody replies for a day".
57
+
58
+ **When an interrupt is mandatory** rather than optional: any action that is
59
+ hard to reverse or outward-facing. Content-level guardrails are probabilistic — see
60
+ `governance.md` — so consequential actions need a deterministic limit or a human, not a
61
+ classifier's opinion.
62
+
63
+ ## Double-texting: four policies
64
+
65
+ A user sends a second message while the first is still running. This has four possible
66
+ answers, and a system that never chose one has chosen the worst by accident:
67
+
68
+ | Policy | Behaviour | Fits |
69
+ |---|---|---|
70
+ | **Enqueue** | finish the current run, then start the new one | a task where the first answer is still wanted |
71
+ | **Reject** | refuse the second message while busy | expensive or transactional runs |
72
+ | **Interrupt** | stop the current run, start the new one, keep what was produced | conversational agents — the usual default |
73
+ | **Rollback** | discard the current run *including its input*, start clean | the user is correcting themselves |
74
+
75
+ The difference between interrupt and rollback is what the transcript looks like
76
+ afterwards, and it is worth deciding deliberately: interrupt leaves a half-finished turn
77
+ in history that the next prompt will see.
78
+
79
+ ## Streaming that survives a dropped connection
80
+
81
+ Four things are worth streaming, and they are not the same thing:
82
+
83
+ 1. **State snapshots** after each step — for a UI that renders the whole picture.
84
+ 2. **State deltas** — the same, cheaper.
85
+ 3. **Tokens** — the typing effect.
86
+ 4. **Custom events** — domain progress: "queried 3 of 7 sources".
87
+
88
+ The body's tracker emits an in-memory feed. Two properties turn it into something a
89
+ client can rely on:
90
+
91
+ - **Every event carries a monotonic id**, and a client reconnecting sends the last id it
92
+ saw. The server replays from there. Without this, a dropped connection during a
93
+ ninety-second run means the user watches nothing and then gets an answer from nowhere.
94
+ - **The feed is a view over the durable trace, not the record itself.** If the only copy
95
+ of what happened is a stream nobody stored, evaluation is impossible — see the
96
+ `agent-evals` skill, which cannot function without it.
97
+
98
+ ## Time travel and forking
99
+
100
+ Once every iteration is checkpointed, one capability follows nearly free: **pick a past
101
+ checkpoint, modify the state, and resume from it.** The original history stays; the
102
+ modified run forks.
103
+
104
+ This is the debugging tool the loop otherwise lacks. "Why did it call that tool?" is
105
+ answerable by rewinding to the step before, changing one thing, and running forward
106
+ again — through the real loop, with real model calls and real tools, rather than a
107
+ reconstruction that may not share the bug.
108
+
109
+ It is also how a failed production run becomes a regression fixture: fork at the failure
110
+ point, minimise, save the state as the fixture's input.
111
+
112
+ ## Scheduled and sleep-time work
113
+
114
+ Not all agent work starts with a user. Two shapes, and the distinction matters:
115
+
116
+ - **Stateful schedule** — each run appends to an existing thread, so the agent remembers
117
+ the previous ones. A daily briefing that should not repeat itself.
118
+ - **Stateless schedule** — each run starts a fresh thread. A monitor that must not drift
119
+ on yesterday's context.
120
+
121
+ Scheduled runs need the same retry and tracing as interactive ones, and one extra rule:
122
+ **a schedule that fails silently is worse than no schedule.** Failures must reach a human
123
+ through something other than the absence of a result.
124
+
125
+ **Sleep-time compute** is the useful pattern on top: work done between conversations —
126
+ consolidating memory, refreshing an index, pre-computing what tomorrow's first question
127
+ will need. It is also where memory consolidation belongs when the hot path is too busy
128
+ for it.
129
+
130
+ ## Middleware: the seven concerns, unwelded
131
+
132
+ The body's loop hand-codes seven cross-cutting concerns inside itself: retry, provider
133
+ fallback, summarisation, human-in-the-loop, tool-call limits, redaction, and moderation.
134
+ Each is correct and none is separable — changing the retry policy means editing the loop.
135
+
136
+ The alternative is ordered interceptors at four points:
137
+
138
+ | Hook | Runs | Typical use |
139
+ |---|---|---|
140
+ | `before_model` | before the request is built | inject context, redact, enforce a budget |
141
+ | `wrap_model_call` | around the call | retry, fallback, timing, cost accounting |
142
+ | `wrap_tool_call` | around each tool | authorisation, rate limits, argument validation |
143
+ | `after_model` | on the response | moderation, structured-output repair, guardrails |
144
+
145
+ The hook names are borrowed vocabulary; the shape is generic. What it buys is
146
+ composition — a tool-call limit is one interceptor, not a counter threaded through three
147
+ functions — and testability: an interceptor is a unit, the loop is not.
148
+
149
+ The trap: **order is semantics.** Redaction after summarisation redacts a summary that
150
+ already leaked. Write the order down where the list is defined, not in the head of
151
+ whoever wrote it.