@ssheleg/agent-stack 0.4.0 → 0.5.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,46 @@ 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.5.0] — 2026-08-12
8
+
9
+ ### Added
10
+
11
+ - **A second skill: `agent-evals`** — how you know the thing built by
12
+ `agent-orchestrator` actually behaves. There was no evaluation doctrine anywhere in
13
+ this family; `grep -ril "llm-as-judge\|eval dataset\|regression fixture\|trace id"`
14
+ across four plugins returned nothing.
15
+
16
+ An agent's behaviour is not in its source — the code says what it is allowed to do,
17
+ only a run says what it did — so the artifact under test is the execution record.
18
+ Three primitives (run, trace, thread) crossed with three granularities (single-step,
19
+ full-turn, multi-turn), each with its own fixture shape and its own precondition:
20
+ step assertions need a stable architecture or they die at the next refactor; turn
21
+ assertions cover trajectory **and** response **and** state change, because an agent
22
+ that says it saved the preference and did not passes two axes out of three; thread
23
+ scripts checkpoint after every turn and fail fast, or turn 3 derails and turns 4–10
24
+ assert nothing while still reporting a result.
25
+
26
+ Then the parts that decide whether any of it is trustworthy: the offline/online/ad-hoc
27
+ timing axis and why offline is necessary and not sufficient; pass-fail rubrics with
28
+ enumerated failure conditions instead of scalar scores that name no fix; code checks
29
+ before model judges; judging the trajectory, not just the answer; **calibrating a judge
30
+ against human labels before trusting it**, because an uncalibrated judge is an opinion
31
+ with a number attached; the classes of output no general judge can grade; a corpus
32
+ grown from production failures where every fixed failure stays a fixture permanently;
33
+ annotation queues with the two reviewer roles kept apart; and simulated users made
34
+ deliberately worse so offline results predict production.
35
+
36
+ Body 214 lines / ~2500 tokens; description 911/1024 with paired RU triggers.
37
+
38
+ ### Changed
39
+
40
+ - **Both installers iterate over `skills/` instead of naming one skill.** `install.sh`
41
+ and `bin/agent-stack.js` each hardcoded `agent-orchestrator`, so a second skill would
42
+ have shipped in the package and reached nobody. Verified by running both against a
43
+ clean `HOME` and listing what arrived. The CI smoke test now asserts both skills.
44
+ - README and both manifests describe two skills; the marketplace entry's description is
45
+ the manifest's, rather than a second one drifting beside it.
46
+
7
47
  ## [0.4.0] — 2026-08-12
8
48
 
9
49
  ### 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 three references the first loads on demand.
13
14
 
14
15
  **The orchestrator** (`SKILL.md`) — what the agent reads first:
15
16
 
@@ -29,6 +30,22 @@ 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
+
32
49
  **`references/patterns.md`** — the data models and algorithms underneath:
33
50
  message and result protocols, pipeline models, the SQL validation loop,
34
51
  context-window sizes and token estimation, learning-extraction heuristics,
@@ -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.5.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.5.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.