@ssheleg/agent-stack 0.11.0 → 0.12.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 +63 -0
- package/package.json +5 -2
- package/plugins/agent-stack/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-stack/skills/agent-evals/SKILL.md +50 -12
- package/plugins/agent-stack/skills/agent-harness/SKILL.md +2 -1
- package/plugins/agent-stack/skills/agent-orchestrator/SKILL.md +2 -1
- package/plugins/agent-stack/skills/agent-orchestrator/references/graph-engineering.md +85 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.12.0 — 2026-08-19
|
|
4
|
+
|
|
5
|
+
Three places where this pack's own doctrine disagreed with the Proof of Done manifesto it
|
|
6
|
+
is built on. Each was confirmed from *inside* the pack before anything was changed.
|
|
7
|
+
|
|
8
|
+
### The gate before every convergence asked how sure a branch was, never what it could show
|
|
9
|
+
|
|
10
|
+
The manifesto's checker contract is *arrived · matches its contract · **carries its
|
|
11
|
+
evidence** · does not contradict a sibling*. This pack named five things and substituted
|
|
12
|
+
**under-confident — a confidence signal below the bar** for the evidence item. In a family
|
|
13
|
+
whose first value is Evidence over confidence, and whose own text says "an uncalibrated
|
|
14
|
+
judge is an opinion with a number attached", the gate guarding every convergence never
|
|
15
|
+
asked what a branch could show.
|
|
16
|
+
|
|
17
|
+
Six mandatory items now, in run order, with `unevidenced` as item 3 — an assertion with no
|
|
18
|
+
receipt, explicitly distinguished from *wrong*. **Arrival is a count**, not an accident: a
|
|
19
|
+
never-returning branch is caught because it is missing, not because the host happened to
|
|
20
|
+
null it. The confidence signal is kept and demoted to a hint: **low confidence flags,
|
|
21
|
+
absent evidence blocks.**
|
|
22
|
+
|
|
23
|
+
### A node was defined by three of its five fields, and read as whole
|
|
24
|
+
|
|
25
|
+
`One input, one output, one job` — against the manifesto's five, which add **one owner** and
|
|
26
|
+
**its own completion test**. Both texts then gave the same justification in nearly the same
|
|
27
|
+
words, which is what let the abridged version read as complete.
|
|
28
|
+
|
|
29
|
+
Worktree isolation answers **when** — two writers cannot corrupt one file. Ownership answers
|
|
30
|
+
**whose** — which node's version is authoritative once the branches return. Isolation without
|
|
31
|
+
ownership loses nothing during the run and moves the loss to the merge, where a silently
|
|
32
|
+
one-sided merge is indistinguishable from a convergence that only ever had one answer. The
|
|
33
|
+
`check` field matches `task-pipeline`'s exactly — same name, same one-string rule, same
|
|
34
|
+
`parked` exemption — rather than inventing a second vocabulary for one idea.
|
|
35
|
+
|
|
36
|
+
### The suite that could never be authored up front had to gate the first release
|
|
37
|
+
|
|
38
|
+
`Never author the suite up front` read as an absolute because the pack had no word for the
|
|
39
|
+
other tier: `grep -ci observable` and `grep -ci requirement` both returned **0**. Taken as
|
|
40
|
+
written the rule made a first release ungateable — §3 names the offline suite "this is the
|
|
41
|
+
gate", and a suite that may never be authored up front cannot exist before there is
|
|
42
|
+
production to grow it from.
|
|
43
|
+
|
|
44
|
+
Two clocks now. The **observable** is a criterion, written before the implementation. The
|
|
45
|
+
**corpus** is a sample, grown from production. The original imperative survives word for
|
|
46
|
+
word and gains only its subject: *"Never author the suite up front — the corpus, that is:
|
|
47
|
+
the inputs."* Neither rule softens the other, because they govern different objects.
|
|
48
|
+
|
|
49
|
+
Each contract is declared machine-readably with a floor and named keys, so an item cannot
|
|
50
|
+
silently go missing again. Validator 10 → 13 checks; negative self-tests 15 → 19.
|
|
51
|
+
|
|
52
|
+
## v0.11.1 — 2026-08-16
|
|
53
|
+
|
|
54
|
+
**This pack was the one place nothing was looking.** The family umbrella's shared checker
|
|
55
|
+
had an early exit for a member carrying no routed triggers, and `agent-stack` is that
|
|
56
|
+
member — so when a sibling's front matter turned out to be invalid YAML (B-56: a
|
|
57
|
+
colon-space inside an unquoted scalar, which every regex-based gate in the family reads
|
|
58
|
+
happily and a real parser refuses), nothing here would have caught the same mistake.
|
|
59
|
+
|
|
60
|
+
`test/validate.py` now asks that checker, which no longer exits early: it validates the
|
|
61
|
+
shipped front matter of every skill first, and only then the routed triggers a member may
|
|
62
|
+
or may not have. The table it reads is not copied here, so there is nothing to drift.
|
|
63
|
+
Watched refusing a planted `Broken: now a nested mapping.` in `agent-orchestrator`'s
|
|
64
|
+
description, and green after restore.
|
|
65
|
+
|
|
3
66
|
## [0.11.0] — 2026-08-16
|
|
4
67
|
|
|
5
68
|
### Changed
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ssheleg/agent-stack",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.12.0",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"test": "python3 test/validate.py && python3 test/plant_guard_test.py"
|
|
6
|
+
},
|
|
7
|
+
"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
8
|
"bin": {
|
|
6
9
|
"agent-stack": "bin/agent-stack.js"
|
|
7
10
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "agent-stack",
|
|
3
3
|
"displayName": "Agent Stack",
|
|
4
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
|
+
"version": "0.12.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "ssheleg",
|
|
8
8
|
"url": "https://x.com/sshlg93"
|
|
@@ -20,7 +20,8 @@ license: MIT
|
|
|
20
20
|
|
|
21
21
|
An agent's behaviour does not exist in its source. The code says what it is *allowed* to
|
|
22
22
|
do; only a run says what it *did*. So the artifact under test is the execution record,
|
|
23
|
-
and the suite
|
|
23
|
+
and the suite runs on **two clocks**: the *observable* is authored up front, before the
|
|
24
|
+
implementation exists, and the *corpus* it runs against is grown from production (§6).
|
|
24
25
|
|
|
25
26
|
Three claims follow, and they are what makes this different from testing ordinary code:
|
|
26
27
|
|
|
@@ -28,7 +29,8 @@ Three claims follow, and they are what makes this different from testing ordinar
|
|
|
28
29
|
- **Every natural-language input is unique**, so the edge cases cannot be enumerated
|
|
29
30
|
offline. Production is not only where you catch what you missed — it is where you
|
|
30
31
|
discover what to test for.
|
|
31
|
-
- **Traces become test cases.** The
|
|
32
|
+
- **Traces become test cases.** The corpus grows from what actually happened; the
|
|
33
|
+
criterion it is measured against does not.
|
|
32
34
|
|
|
33
35
|
---
|
|
34
36
|
|
|
@@ -169,17 +171,27 @@ depends on **it** rather than on the branches — otherwise the gate has a bypas
|
|
|
169
171
|
|
|
170
172
|
It matters here because it is the same machinery as §5, positioned differently:
|
|
171
173
|
|
|
174
|
+
<!-- checker-contract: missing, empty, unevidenced, malformed, contradictory, off-topic | optional: under-confident -->
|
|
175
|
+
|
|
172
176
|
| It catches | Decided by |
|
|
173
177
|
|---|---|
|
|
174
|
-
|
|
|
175
|
-
|
|
|
176
|
-
|
|
|
177
|
-
|
|
|
178
|
-
|
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
| a **missing** branch — the arrival count falls short of the fan-out promised | a code check |
|
|
179
|
+
| an **empty** or null result | a code check |
|
|
180
|
+
| an **unevidenced** claim — nothing attached that a reader could re-check | a code check |
|
|
181
|
+
| a **malformed** shape the consuming node cannot parse | a code check |
|
|
182
|
+
| **contradictory** siblings, including two that paraphrased one shared assumption | a judge |
|
|
183
|
+
| an **off-topic** answer to something nobody asked | a judge |
|
|
184
|
+
|
|
185
|
+
Four of six are free. Run them first — §5's *cheap checks first*, applied to a position
|
|
181
186
|
in the graph rather than to a suite.
|
|
182
187
|
|
|
188
|
+
**An under-confident branch is a hint, not a row: its own confidence number is optional
|
|
189
|
+
and deliberately not one of the six.** §5 above is the reason: a score from an
|
|
190
|
+
uncalibrated source is an opinion, so it may order retries and it may not open a gate.
|
|
191
|
+
The gate asks for the third row instead — a receipt — which is the same reason this pack
|
|
192
|
+
ranks evidence over confidence everywhere else. The contract's home, with the argument in
|
|
193
|
+
full, is `agent-orchestrator/references/graph-engineering.md` §6.
|
|
194
|
+
|
|
183
195
|
**A checker is a node, so it can be wrong, and its failure mode is silent approval.** A
|
|
184
196
|
model checker that has never been shown a bad input passes everything, and a graph with a
|
|
185
197
|
checker that always says yes is **worse** than one with none: the missing checkpoint has
|
|
@@ -198,10 +210,34 @@ and they are eval work rather than orchestration work:
|
|
|
198
210
|
Where the checker sits in the shape, and why the convergence needs one at all:
|
|
199
211
|
`agent-orchestrator/references/graph-engineering.md`.
|
|
200
212
|
|
|
201
|
-
## 6.
|
|
213
|
+
## 6. Two clocks — the observable up front, the corpus from production
|
|
202
214
|
|
|
203
|
-
|
|
204
|
-
|
|
215
|
+
Two different objects get called *the eval*, and they are written at opposite ends of the
|
|
216
|
+
work. Naming them apart is what stops either rule from reading as the other's exception.
|
|
217
|
+
|
|
218
|
+
<!-- eval-tiers: observable, corpus -->
|
|
219
|
+
|
|
220
|
+
| Tier | Is | Written | Because |
|
|
221
|
+
|---|---|---|---|
|
|
222
|
+
| **Observable** | the criterion that would show one requirement was met — a pass/fail rubric (§4), a trajectory or state-change assertion (§2) | **before the implementation exists** | a requirement with no observable is unfinished: attach one afterwards and you are inventing the test having already seen the code, so the output has decided what counts as success |
|
|
223
|
+
| **Corpus** | the inputs those criteria run against — fixtures, datasets, minimised production failures | **from production, never up front** | every natural-language input is unique, so the edge cases cannot be enumerated offline; inputs invented in advance test your imagination |
|
|
224
|
+
|
|
225
|
+
**Neither rule softens the other, because they govern different objects.** An observable is
|
|
226
|
+
a *criterion* — what would count as success. A corpus is a *sample* — which inputs you
|
|
227
|
+
happen to have. The criterion costs nothing to write early and can only be written honestly
|
|
228
|
+
early; the sample written early is green on inputs no user sends. Both therefore hold at
|
|
229
|
+
full strength: **a requirement that ships without an observable is unfinished, and a corpus
|
|
230
|
+
with no production in it is imagination.** The requirement itself gets its id and its
|
|
231
|
+
definition of done from `task-pipeline`'s REQ spine — what this pack owns is the
|
|
232
|
+
observable's *form*, not the register it hangs on.
|
|
233
|
+
|
|
234
|
+
**The first release has no production, so its offline gate is observables only** (§3). That
|
|
235
|
+
is not the corpus rule suspended for a special case: the corpus is empty because nothing has
|
|
236
|
+
run yet, and it fills from the first real traces. Inventing *inputs* to fill it sooner would
|
|
237
|
+
still be imagination.
|
|
238
|
+
|
|
239
|
+
**Never author the suite up front** — the *corpus*, that is: the inputs. Every production
|
|
240
|
+
failure and every thumbs-down becomes a fixture:
|
|
205
241
|
|
|
206
242
|
1. Capture the state at the failure point.
|
|
207
243
|
2. Minimise it to the smallest input that still reproduces.
|
|
@@ -257,6 +293,8 @@ None of the above runs without these, and they are the part people skip:
|
|
|
257
293
|
- [ ] Every checker node watched refusing a planted output, its verdicts stored as scores,
|
|
258
294
|
and its rejection rate on the dashboard — a checker at zero rejections is a finding
|
|
259
295
|
- [ ] Domain-expert review for output a general judge cannot grade
|
|
296
|
+
- [ ] Every requirement carries an **observable** written before the implementation — the
|
|
297
|
+
corpus waits for production, the criterion does not
|
|
260
298
|
- [ ] Every production failure minimised into a permanent fixture
|
|
261
299
|
- [ ] Annotation queue with filters, and the two reviewer roles kept separate
|
|
262
300
|
- [ ] Simulated users trained on real transcripts, with adversarial personas
|
|
@@ -179,4 +179,5 @@ prompt.
|
|
|
179
179
|
- [ ] One technique chosen per problem, with a reason — not ReAct because it was in a paper
|
|
180
180
|
- [ ] Sub-agents return **distilled summaries**, not transcripts
|
|
181
181
|
- [ ] The agent can be observed: which tool, which arguments, which observation, how many tokens
|
|
182
|
-
- [ ] An eval
|
|
182
|
+
- [ ] An observable before the implementation, an eval before the prompt is tuned, or the
|
|
183
|
+
tuning is folklore — only the corpus waits for production
|
|
@@ -365,7 +365,8 @@ heading — the ones that were learned by getting them wrong:
|
|
|
365
365
|
to the input is a function call wearing a costume
|
|
366
366
|
- [ ] Model, window and price are resolved at one boundary from configuration or the
|
|
367
367
|
provider — never from a table of vendor ids in source
|
|
368
|
-
- [ ] An eval
|
|
368
|
+
- [ ] An observable before the implementation, an eval before the prompt is tuned, or the
|
|
369
|
+
tuning is folklore — only the corpus waits for production
|
|
369
370
|
|
|
370
371
|
## References
|
|
371
372
|
|
|
@@ -49,10 +49,55 @@ Four things are **this pack's**, not the source's, and each is marked where it a
|
|
|
49
49
|
|
|
50
50
|
## 1. Node and edge
|
|
51
51
|
|
|
52
|
-
**A node is one unit of work.** One input, one
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
**A node is one unit of work, and it has five fields.** One input, one job, one output,
|
|
53
|
+
**one owner**, and **its own completion test**. Not *"research the topic, summarise it,
|
|
54
|
+
and check the sources"* — that is three nodes wearing one name. The smaller and more
|
|
55
|
+
defined the job, the more useful the node, because a node is also the unit you retry,
|
|
56
|
+
cache, review and replace.
|
|
57
|
+
|
|
58
|
+
<!-- node-contract: input, job, output, owner, check -->
|
|
59
|
+
|
|
60
|
+
The first three get drawn every time. The last two are the ones a graph is usually drawn
|
|
61
|
+
without — this file shipped without them until 2026-08-19 — and each has a failure that
|
|
62
|
+
surfaces only at the convergence, where it is cheapest to mistake for success.
|
|
63
|
+
|
|
64
|
+
**One owner — exactly one node is answerable for a given artifact.** Not one *agent*: the
|
|
65
|
+
same subagent may own several nodes, and a node owned by a person is still a node. The
|
|
66
|
+
unit of ownership is the artifact, and the assignment is made when the layer is built, not
|
|
67
|
+
discovered when the writes come back.
|
|
68
|
+
|
|
69
|
+
**Ownership is not what `isolation: "worktree"` gives you, and the two are complementary
|
|
70
|
+
rather than alternatives.** §9's primitive table calls a private checkout the only safe way
|
|
71
|
+
to fan out writers, and against a *race* that is true. Isolation answers **when**: two
|
|
72
|
+
writers cannot corrupt one file, because neither can see the other's copy while it runs.
|
|
73
|
+
Ownership answers **whose**: which node's version is authoritative once the branches
|
|
74
|
+
return. Take isolation without ownership and nothing is lost during the run — the loss
|
|
75
|
+
moves to the merge, which is the quieter place for it, because a convergence that silently
|
|
76
|
+
takes one side is indistinguishable from a convergence that only ever had one answer. §4's
|
|
77
|
+
first rule is the *detection* half of this: two independent workers writing one file are
|
|
78
|
+
one node with a race in it. Ownership is the *assignment* half, and it is what makes
|
|
79
|
+
fanning writers out decidable rather than merely survivable.
|
|
80
|
+
|
|
81
|
+
**Its own completion test — the node names what will close it, before it runs.** One
|
|
82
|
+
string: a command a verifier can run, or a named judgement standing in where no command
|
|
83
|
+
can, with the judge named and the verdict recorded *as* judgement. Whatever it prints is
|
|
84
|
+
that node's evidence row.
|
|
85
|
+
|
|
86
|
+
The field's name across this family is **`check`** — `task-pipeline`'s node schema
|
|
87
|
+
(`graph.schema.json`) carries it as one required string per node and exempts only a
|
|
88
|
+
`parked` one, the single node nobody intends to close. Same name and same meaning here,
|
|
89
|
+
one string and never a list, because a node needing two unrelated checks is a node doing
|
|
90
|
+
two jobs and the answer is to split it. One gate built from two commands is `a && b`,
|
|
91
|
+
which is still one gate.
|
|
92
|
+
|
|
93
|
+
**A completion test is not a checker node, and carrying both is not redundancy.** §6's
|
|
94
|
+
checker sits between a parallel layer and its convergence and judges *arriving siblings*
|
|
95
|
+
from the outside — including the two things no node can establish about itself: whether it
|
|
96
|
+
contradicts a neighbour, and whether it answered the question the layer was actually
|
|
97
|
+
asked. The completion test is the node's own gate on its own output, run by whoever owns
|
|
98
|
+
it. Drop the per-node test and the checker becomes the only gate in the graph, which is how
|
|
99
|
+
a convergence ends up re-deriving what each branch should have proved about itself; drop
|
|
100
|
+
the checker and every branch can pass its own test and still disagree with its neighbour.
|
|
56
101
|
|
|
57
102
|
**An edge is a dependency, and it carries data.** It exists when the second node
|
|
58
103
|
genuinely consumes what the first produced. Not when the second merely *happens after*
|
|
@@ -151,19 +196,44 @@ A node between the parallel layer and the convergence whose only job is to decid
|
|
|
151
196
|
each output may proceed. It synthesises nothing and writes nothing. It answers *is this
|
|
152
197
|
usable* and then passes, flags, retries or drops.
|
|
153
198
|
|
|
154
|
-
|
|
155
|
-
of the
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
199
|
+
Six things it must catch — the list is the contract, and a checker that cannot say which
|
|
200
|
+
of the six it is asserting is not a checker:
|
|
201
|
+
|
|
202
|
+
<!-- checker-contract: missing, empty, unevidenced, malformed, contradictory, off-topic | optional: under-confident -->
|
|
203
|
+
|
|
204
|
+
1. **Missing** — a branch the split promised never came back at all. This item needs an
|
|
205
|
+
**arrival count**: the fan-out is fixed when the layer is built, so the checker is
|
|
206
|
+
handed the number of results to expect and compares it with the number it is holding.
|
|
207
|
+
Without that number a vanished branch is caught only when its slot happens to hold
|
|
208
|
+
nothing — item 2, by luck rather than by design, and only on a host that fills the slot
|
|
209
|
+
at all (§9's primitive table records what this one does with a thrower). A host that
|
|
210
|
+
simply drops it makes the gap invisible, and a short list looks like a complete one.
|
|
211
|
+
2. **Empty or null** — the result arrived and carries nothing usable.
|
|
212
|
+
3. **Unevidenced** — an assertion with no receipt: no citation, no tool result, no file
|
|
213
|
+
and line, nothing a later reader could re-check. Whether the claim is *wrong* is item 5
|
|
214
|
+
or 6's business; this item is about a claim nobody downstream can verify at all, and it
|
|
215
|
+
is the one most checkers are missing.
|
|
216
|
+
4. **Malformed** — a shape that will break the convergence node's parsing.
|
|
217
|
+
5. **Contradictory** — two results that cannot both be true, **including two that used
|
|
218
|
+
different words for one shared assumption**. The disagreement hides inside the
|
|
219
|
+
paraphrase, so comparing strings is not enough to find it.
|
|
220
|
+
6. **Off-topic** — an output that answers a different question than the one asked.
|
|
221
|
+
|
|
222
|
+
Four of the six are code checks (1–4) and cost nothing; only 5 and 6 need a model. Run
|
|
223
|
+
them in that order — this is `agent-evals` §5's *cheap checks first*, applied to a
|
|
165
224
|
position in the graph rather than to a test suite.
|
|
166
225
|
|
|
226
|
+
**A confidence signal is optional, and it is not the evidence item.** *Under-confident*
|
|
227
|
+
was item 4 of this list until 2026-08-19; it is kept, demoted to a hint. A branch's own
|
|
228
|
+
number is worth carrying as exactly that — which result to retry first, which to hand to a
|
|
229
|
+
person — and it is not a gate. It never stands in for item 3, and the argument is this
|
|
230
|
+
pack's own, made twice already: *an uncalibrated judge is an opinion with a number
|
|
231
|
+
attached* (`agent-evals` §5), and for anything consequential the control is a
|
|
232
|
+
deterministic limit or a human, **never a classifier's confidence**
|
|
233
|
+
(`references/governance.md`). A gate that asks a branch how sure it is and never asks
|
|
234
|
+
what it can show has graded the run instead of measuring it. So: low confidence *flags*,
|
|
235
|
+
absent evidence *blocks*.
|
|
236
|
+
|
|
167
237
|
**What a checker costs, and how it turns into a rubber stamp — this pack's addition.** A
|
|
168
238
|
checker is a node, so it has the failure mode of every node: it can be wrong. A model
|
|
169
239
|
checker that has never been shown a bad input will pass everything, and a graph with a
|