@ssheleg/agent-stack 0.16.0 → 0.17.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
@@ -1,5 +1,83 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.17.0 — the descriptions stop colliding, and the claims get their dates
4
+
5
+ Wave-2 of the 2026-08-29 family audit (rows AST-02, AST-03, AST-04, AST-06, AST-11), plus
6
+ the harness-engineering externals read 2026-08-30.
7
+
8
+ - **AST-02: `agent-orchestrator` gives "checker node" back to `agent-evals`.** The quoted
9
+ trigger appeared verbatim in both descriptions, so the router had two skills advertising
10
+ one phrase. `agent-evals` owns it; the orchestrator's description already carries "a
11
+ checker before convergence" in prose, which is the claim it actually makes.
12
+ - **AST-11: the single-word triggers "agent" / "агент" narrow to "agent system" /
13
+ "агентная система".** A one-word trigger matching every sentence with the word *agent*
14
+ in it is how a skill teaches the router to route around the boundary its own description
15
+ draws ("Not for a single LLM call in a script"). The multiword triggers carry the load.
16
+ - **`agent-harness` advertises "ReAct loop" and "react pattern" instead of bare "ReAct"** —
17
+ a phrase the umbrella's deferred hook trigger can carry without firing on every mention
18
+ of the React framework. The body prose still explains ReAct by its own name.
19
+ - **AST-06: the harness↔orchestrator boundary names its four seams instead of claiming
20
+ one.** `agent-harness` §Boundaries said the two skills "meet in one place" while the
21
+ pack actually crosses at four: orchestrator §3 → harness `tools.md` (describing a tool
22
+ vs assembling the list), orchestrator §10 → harness `system-prompt.md` (what the prompt
23
+ says vs rebuilding it per request), harness static-or-dynamic → orchestrator
24
+ `graph-engineering.md`, and `context-engineering.md` (compaction) vs `system-prompt.md`
25
+ (filling). The rewrite states explicitly that shape-of-the-work doctrine has ONE home —
26
+ `agent-orchestrator/references/graph-engineering.md` — and the harness intro stops
27
+ claiming "the shape of the work" as its own ground, which was the sentence that made
28
+ both skills owners of one table.
29
+ - **AST-03/AST-04: `agent-interop`'s undated external claims get their dates.** "Moved in
30
+ the last twelve months", "SDKs and blog posts still document v0.3" and "its own overview
31
+ page still introduces `binds`" sat undated inside the very section whose rule is *a
32
+ protocol claim without a date is a guess* — all three now anchor to **2026-08-13**, the
33
+ stamp every reference in the skill already carries. The neighbourhood table gains a
34
+ "Verdicts as of 2026-08-13" line, since `test/validate.py` gates stamps only under
35
+ `references/` and the table lives in SKILL.md.
36
+ - **`agent-harness` cites the outside term for its ground: harness engineering.** OpenAI's
37
+ harness-engineering article and Anthropic's harness-design article (both read
38
+ 2026-08-30) name the layer this skill covers, with the measured leverage — ARC-AGI-3
39
+ harness-level changes moved a fixed model 13.3%→38.3% at a sixth of the tokens (as
40
+ reported 2026-08-30). "harness engineering" joins the description triggers: users will
41
+ say it. The deeper work is filed, not done: board rows **AST-A1** (coverage check
42
+ against OpenAI's nine-subsystem taxonomy, identity/approval policy the suspected gap)
43
+ and **AST-B** (the dated generator-evaluator citation in `agent-evals`, deferred to
44
+ evals day).
45
+
46
+ ## v0.16.1 — the installers refuse the shadow, and the pack stops mis-selling itself
47
+
48
+ - **Both installers refuse to write plain copies over an installed plugin.** The family
49
+ audit of 2026-08-29 reproduced the shadow live: a bare `npx @ssheleg/telegram-dev`
50
+ created three plain copies in `~/.claude/skills/` while that plugin was enabled — and
51
+ this member had **no plugin check at all**, in either installer, so a bare
52
+ `npx @ssheleg/agent-stack` on a machine with the plugin would have shipped **four**
53
+ shadows, one per skill, each serving its frozen version forever. `bin/agent-stack.js`
54
+ and `install.sh` now implement make-skill v0.25.0's canon (`distribution.md`, "The
55
+ installer must refuse the shadow it documents"): detect from the TARGET home's
56
+ `installed_plugins.json` (keys are `<name>@<marketplace>`, and the two names differ
57
+ often), keep the `marketplaces/` dir read only as the fallback signal, refuse with
58
+ exit **3** and a remedy that names the spec read from the JSON
59
+ (`claude plugin marketplace update agent-stack` + `claude plugin update
60
+ agent-stack@<marketplace>`, plus the family launcher), offer `--force` as the recorded
61
+ deliberate override, fail open on a missing or corrupt JSON, and gate only the
62
+ `~/.claude` write — no other agent has plugins.
63
+ - **`test/installer_test.js` joins `npm test` and CI** — 11 cases against throwaway
64
+ HOMEs: fresh / rerun-skip / `--force` / unknown-arg, plugin-present refusal (exit
65
+ code, remedy text, nothing written — all three asserted), a differently-named
66
+ marketplace in the remedy spec, corrupt JSON failing open, no false refusal on other
67
+ plugins or an `agent-stack-extra` prefix-collider, the marketplaces-dir fallback, and
68
+ the same matrix for `install.sh`. Watched failing before trusted: **7 of 11 red**
69
+ against the pre-fix installers (`git stash` the two, run, pop). The suite follows the
70
+ house residue rule — a failing case keeps its HOME, and the run ends by saying what it
71
+ left. It replaces the inline fresh-HOME-only step in `validate.yml`, which is the CI
72
+ shape that let the plugin-present case go unrun everywhere.
73
+ - **A successful install now says how the next version arrives** — the last line names
74
+ `npx @ssheleg/agent-stack@latest --force` and the family launcher, in both installers.
75
+ - **AST-01: the plugin stops selling itself as two skills.** `plugin.json` and
76
+ `marketplace.json` both opened with "Two skills: agent-orchestrator … and agent-evals"
77
+ while the pack ships **four** — `agent-interop` and `agent-harness` were invisible in
78
+ `claude plugin details` and on the marketplace. Both descriptions now name all four
79
+ skills with what each covers.
80
+
3
81
  ## v0.16.0 — the whole survey, not just its taxonomy
4
82
 
5
83
  v0.15.0 took the taxonomy and the named failure modes. This takes the rest: the write
@@ -16,6 +16,44 @@ const os = require('os');
16
16
 
17
17
  const ROOT = path.resolve(__dirname, '..');
18
18
  const REPO = 'ssheleg/agent-stack';
19
+ const PLUGIN = 'agent-stack';
20
+
21
+ // Exit codes are the contract: 0 installed or skipped, 1 corrupted package,
22
+ // 2 usage error, 3 refused — the plugin channel owns this agent (--force overrides).
23
+ const EXIT_PLUGIN_PRESENT = 3;
24
+
25
+ /**
26
+ * The plugin spec (`<name>@<marketplace>`) installed for `name` in this home,
27
+ * or null.
28
+ *
29
+ * `installed_plugins.json` is the record of what is actually installed. The
30
+ * `plugins/marketplaces/<name>` directory under-reports: a marketplace added
31
+ * from a local `directory` source has no dir there at all, and plugin names
32
+ * differ from marketplace names, so a check keyed on it stays green while the
33
+ * shadow lands. Absence and corruption both read as "no plugin": the fresh
34
+ * HOME is the common case, and an installer that crashes on a parse error
35
+ * refuses the machines that need it most.
36
+ */
37
+ function installedPluginSpec(home, name) {
38
+ try {
39
+ const raw = fs.readFileSync(
40
+ path.join(home, '.claude', 'plugins', 'installed_plugins.json'), 'utf8');
41
+ const parsed = JSON.parse(raw);
42
+ const plugins =
43
+ parsed && typeof parsed === 'object' &&
44
+ parsed.plugins && typeof parsed.plugins === 'object'
45
+ ? parsed.plugins
46
+ : parsed;
47
+ if (!plugins || typeof plugins !== 'object') return null;
48
+ for (const spec of Object.keys(plugins)) {
49
+ if (spec === name) return `${name}@${name}`;
50
+ if (spec.startsWith(name + '@')) return spec;
51
+ }
52
+ } catch {
53
+ // missing or corrupt = no plugin — fail open on absence, never crash
54
+ }
55
+ return null;
56
+ }
19
57
 
20
58
  function usage() {
21
59
  console.log(`agent-stack installer
@@ -25,6 +63,12 @@ Usage:
25
63
  into ~/.claude (skip existing unless --force)
26
64
  npx @ssheleg/agent-stack --help
27
65
 
66
+ Exit codes:
67
+ 0 installed or skipped 2 usage error
68
+ 1 corrupted package 3 refused: the agent-stack PLUGIN is installed in
69
+ this home — plain copies would shadow it (pass
70
+ --force to write them anyway)
71
+
28
72
  Other install paths:
29
73
  Claude Code plugin: /plugin marketplace add ${REPO}
30
74
  /plugin install agent-stack@agent-stack
@@ -86,6 +130,41 @@ function main(argv) {
86
130
  }
87
131
 
88
132
  const home = os.homedir();
133
+
134
+ // One channel per agent. A plain ~/.claude/skills/<name> beside the installed
135
+ // agent-stack plugin is two listings of the same skill per skill this pack
136
+ // ships, and the stale copy wins — the exact shadow make-skill's distribution
137
+ // canon forbids (§ "The installer must refuse the shadow it documents").
138
+ // Refuse rather than create it, and refuse LOUDLY: a presence check keyed on
139
+ // the marketplaces/ dir alone that exits 0 is the fail-open class — a
140
+ // directory-sourced marketplace has no dir there, plugin names differ from
141
+ // marketplace names, and exit 0 reads as success to every script above it.
142
+ // Reproduced live 2026-08-29: a bare `npx @ssheleg/telegram-dev` shipped
143
+ // three shadows past exactly this hole while the plugin was enabled. Only the
144
+ // ~/.claude write is gated — no other agent has plugins.
145
+ const spec = installedPluginSpec(home, PLUGIN);
146
+ const marketplace = path.join(home, '.claude', 'plugins', 'marketplaces', PLUGIN);
147
+ const viaMarketplaceDir = !spec && fs.existsSync(marketplace);
148
+ if ((spec || viaMarketplaceDir) && !force) {
149
+ const found = spec
150
+ ? `installed as the Claude Code plugin ${spec}\n` +
151
+ ' (declared in ~/.claude/plugins/installed_plugins.json)'
152
+ : `registered as a Claude Code marketplace\n (${marketplace})`;
153
+ console.error(
154
+ `refused: agent-stack is already ${found}.\n` +
155
+ ` Plain copies in ~/.claude/skills/ (${names.join(', ')})\n` +
156
+ ' would shadow the plugin and serve this frozen version forever.\n' +
157
+ ' Update the plugin channel instead:\n' +
158
+ ' claude plugin marketplace update agent-stack\n' +
159
+ ` claude plugin update ${spec || 'agent-stack@agent-stack'}\n` +
160
+ ' Family launcher (updates every member, prunes shadow copies):\n' +
161
+ ' npx --yes sshlg-skills@latest update\n' +
162
+ ' Pass --force to write the plain copies anyway — a deliberate choice\n' +
163
+ ' to run two channels, where the stale one wins.'
164
+ );
165
+ return EXIT_PLUGIN_PRESENT;
166
+ }
167
+
89
168
  for (const name of names) {
90
169
  installOne(
91
170
  `${name} skill`,
@@ -95,6 +174,15 @@ function main(argv) {
95
174
  force
96
175
  );
97
176
  }
177
+ // The last line says how the next version arrives — "Installed" is not a
178
+ // complete sentence. Auto-update is off on purpose: this member composes
179
+ // with its family, and per-marketplace autoUpdate moves each member on its
180
+ // own clock, into combinations nobody tested together.
181
+ console.log(
182
+ '\nUpdates: rerun `npx @ssheleg/agent-stack@latest --force`, or refresh the\n' +
183
+ 'whole family with `npx --yes sshlg-skills@latest update` (every channel,\n' +
184
+ 'and it prunes plain copies that would shadow a plugin).'
185
+ );
98
186
  return 0;
99
187
  }
100
188
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@ssheleg/agent-stack",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "scripts": {
5
- "test": "python3 test/validate.py && python3 test/plant_guard_test.py"
5
+ "test": "python3 test/validate.py && python3 test/plant_guard_test.py && node test/installer_test.js"
6
6
  },
7
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.",
8
8
  "bin": {
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "agent-stack",
3
3
  "displayName": "Agent Stack",
4
- "description": "Two skills: agent-orchestrator — 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 — run/trace/thread evals, judges, and fixtures grown from production.",
5
- "version": "0.16.0",
4
+ "description": "Four skills: agent-orchestrator — tool-calling loops, pipelines with checkpoints, provider routing with fallback, memory architecture, plus the wallet side of reselling LLM access; agent-evals — run/trace/thread evals, LLM judges, and fixtures grown from production; agent-interop — MCP servers and clients, A2A agent cards, the MCP Registry, and gateways; agent-harness — system prompts, tool shaping, workflow-vs-agent, and auditing an agent system.",
5
+ "version": "0.17.0",
6
6
  "author": {
7
7
  "name": "ssheleg",
8
8
  "url": "https://x.com/sshlg93"
@@ -8,7 +8,8 @@ description: >-
8
8
  instead of a score, plus a scanner. Carries Pi as a worked kernel implementation — SDK, RPC
9
9
  and extension seams — for embedding or extending a harness. Triggers - "system prompt",
10
10
  "tool description", "agent picks the wrong tool", "agent loops forever", "prompt
11
- engineering", "ReAct", "workflow or agent", "static or dynamic", "audit this agent",
11
+ engineering", "harness engineering", "ReAct loop", "react pattern", "workflow or agent",
12
+ "static or dynamic", "audit this agent",
12
13
  "embed an agent", "agent SDK", "Pi harness", "системный промпт", "агент не вызывает тул",
13
14
  "аудит агента", "встроить агента". Not for the loop's plumbing, its evals, or its
14
15
  protocols — those are siblings.
@@ -18,7 +19,13 @@ description: >-
18
19
 
19
20
  `agent-orchestrator` wires the loop. `agent-evals` proves it behaves. `agent-interop` gets
20
21
  it talking to other processes. **This skill is the layer between them and the model: the
21
- prompt, the tools, and the shape of the work.**
22
+ prompt, the tools, and the choice of technique.** The outside term for this ground is
23
+ **harness engineering** — OpenAI's article of that name (`openai.com/index/harness-engineering`,
24
+ read 2026-08-30) and Anthropic's harness-design guidance
25
+ (`anthropic.com/engineering/harness-design-long-running-apps`, read 2026-08-30) both name
26
+ this same layer, and its leverage is measured: on ARC-AGI-3, harness-level changes alone
27
+ moved a fixed model from 13.3% to 38.3% while spending a sixth of the tokens (as reported
28
+ 2026-08-30).
22
29
 
23
30
  It runs in both directions. Building one and auditing one are the same checklist read
24
31
  forwards and backwards, which is why they live together here.
@@ -146,10 +153,22 @@ is then unfalsifiable — including this audit. Say so first, and make it the fi
146
153
 
147
154
  **Against `agent-orchestrator`.** That skill owns the loop's *plumbing*: iteration guards,
148
155
  trimming, sub-agent dispatch, provider routing, memory layers, checkpoints. This one owns
149
- what the model is *told*. They meet in one place:
150
- `agent-orchestrator/references/context-engineering.md` covers **compaction** — what to drop
151
- when the window fills while this skill's `system-prompt.md` covers what to put there in
152
- the first place. Filling and emptying, two files.
156
+ what the model is *told*. They meet at four seams, each crossing in exactly one place:
157
+
158
+ 1. **Describing a tool** so the model picks the right one is this skill's
159
+ `references/tools.md`; assembling the tool *list* per request from capability flags is
160
+ the orchestrator's §3, which points here for the wording.
161
+ 2. **What the prompt says** — altitude, vocabulary, enumerated statuses — is this skill's
162
+ `references/system-prompt.md`; *rebuilding* that prompt per request, in the same pass
163
+ as the tools, is the orchestrator's §10.
164
+ 3. **The shape of the work** — the static/dynamic table, the fake-edge test, the checker
165
+ before a convergence — has ONE home, and it is not here:
166
+ `agent-orchestrator/references/graph-engineering.md`. This skill's static-or-dynamic
167
+ section stops at the decision and links there for the model.
168
+ 4. **The context window:** `agent-orchestrator/references/context-engineering.md` covers
169
+ **compaction** — what to drop when the window fills — while this skill's
170
+ `system-prompt.md` covers what to put there in the first place. Filling and emptying,
171
+ two files.
153
172
 
154
173
  **Against `agent-evals`.** That skill measures whether an agent behaves, from execution
155
174
  records. This one reviews how it was *built*, from its source and prompts. An audit that
@@ -24,19 +24,20 @@ registry, and the gateway between them.
24
24
 
25
25
  ## Rule zero — a protocol claim without a date is a guess
26
26
 
27
- Every one of these specifications moved in the last twelve months, and each moved in a way
28
- that silently breaks code written against the previous revision. Concretely, and each one
29
- measured against the live specification rather than recalled:
27
+ Every one of these specifications moved in the twelve months before **2026-08-13** the
28
+ date every reference below was read and each moved in a way that silently breaks code
29
+ written against the previous revision. Concretely, and each one measured on that date
30
+ against the live specification rather than recalled:
30
31
 
31
32
  - MCP replaced the `initialize` handshake with `server/discover` and made the protocol
32
33
  **stateless**; a client that opens with `initialize` is speaking a revision that is on its
33
34
  way out.
34
35
  - MCP **deprecated `sampling`, `roots` and `logging`** — three things an older model will
35
36
  reach for first, because for a year they were the interesting part of the client side.
36
- - A2A renamed its wire surface between v0.x and v1.0, and SDKs and blog posts still document
37
- v0.3.
37
+ - A2A renamed its wire surface between v0.x and v1.0, and SDKs and blog posts still
38
+ documented v0.3 as of 2026-08-13.
38
39
  - agentgateway **deprecated `binds`** in favour of `gateways` — while its own overview page
39
- still introduces `binds` as a core concept.
40
+ still introduced `binds` as a core concept as of 2026-08-13.
40
41
 
41
42
  So: **every reference in this skill opens with a `**Spec pinned:**` line**, and
42
43
  `test/validate.py` fails the build without one. That is a mechanical check, not an
@@ -119,6 +120,8 @@ it); prompt wording; a single LLM call in a script.
119
120
  ## The neighbourhood — one verdict each
120
121
 
121
122
  Named so an agent stops guessing, with the verdict stated rather than implied.
123
+ **Verdicts as of 2026-08-13**, the day this skill's references were pinned — this ground
124
+ moves, so re-verify anything marked *watch* before building on it.
122
125
 
123
126
  | Thing | What it actually is | Verdict |
124
127
  |---|---|---|
@@ -7,9 +7,9 @@ description: >-
7
7
  architecture, retrieval and decay, context budgets, sub-agent coordination, error hierarchies; the
8
8
  work as a graph — parallel layers, fake edges, a checker before convergence; for resale:
9
9
  tiered wallets, one markup boundary, two-phase commit across database and provider API,
10
- spend-delta polling, budget and loop guards, per-tenant keys. Triggers - "agent",
10
+ spend-delta polling, budget and loop guards, per-tenant keys. Triggers - "agent system",
11
11
  "orchestrator", "tool calling", "sub-agent", "LLM router", "fallback chain", "human in the
12
- loop", "memory layer", "LLM billing", "token wallet", "checker node", "агент", "оркестратор",
12
+ loop", "memory layer", "LLM billing", "token wallet", "агентная система", "оркестратор",
13
13
  "суб-агент", "роутер моделей", "человек в цикле", "слой памяти", "биллинг LLM", "граф задач".
14
14
  Not for a single LLM call in a script, or prompt wording.
15
15
  ---