@ssheleg/agent-stack 0.7.1 → 0.8.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,100 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.0] — 2026-08-14
4
+
5
+ ### Added
6
+
7
+ - **`agent-harness`, a fourth skill — the layer between the loop and the model.** The pack
8
+ could wire an agent (`agent-orchestrator`), prove it behaved (`agent-evals`) and connect
9
+ it to other processes (`agent-interop`), and said nothing about **what the agent is
10
+ told**. Five references and a scanner. It runs in both directions: building a harness and
11
+ auditing somebody else's are the same checklist read forwards and backwards, which is why
12
+ the audit lives here rather than in a sixth skill.
13
+
14
+ It is a fourth skill rather than a section because `agent-orchestrator`'s body is
15
+ **489 lines / ~4761 tokens** — already past the 4750 working limit — and could not absorb
16
+ a paragraph, let alone a layer.
17
+
18
+ - **`references/system-prompt.md`** — the right altitude (hardcoded branches on one side,
19
+ vague hope on the other), what actually belongs in a system prompt in order of behaviour
20
+ bought, **enumerating the vocabulary** so an agent stops inventing `pending` and `to-do` in
21
+ the same run, injecting what the model cannot know, and flexible-while-learning versus
22
+ strict-in-production. Plus the three things reasoning models changed: **do not add
23
+ chain-of-thought** (it can degrade instruction-following), give goals rather than
24
+ procedures, and treat reasoning effort as a per-stage dial.
25
+
26
+ - **`references/tools.md`** — the agent–computer interface. Fewer tools than instinct
27
+ suggests, namespacing, and the description as the product: a worked before/after where the
28
+ strong version names *when*, *what it costs*, *how to narrow*, and **the neighbouring tool
29
+ it is confused with** — the highest-value sentence in a tool definition and the one almost
30
+ nobody writes. Then meaning over identifiers, token efficiency as a correctness issue,
31
+ errors that teach, and **poka-yoke** — changing the interface so the wrong call cannot be
32
+ made.
33
+
34
+ - **`references/techniques.md`** — fifteen techniques with a **verdict each for a production
35
+ loop**, not a benchmark score. ReAct is the agent loop and its under-quoted failure is that
36
+ non-informative results derail it; reflection is strong exactly where a cheap objective
37
+ signal exists and is a second opinion from the same source where it does not; Tree of
38
+ Thoughts is almost never worth its combinatorics. Ends with an ordered five-question
39
+ chooser.
40
+
41
+ - **`references/layers.md`** — the question that resolves most framework arguments (*which
42
+ layer am I working at*), what a harness owns, and the design position that **permission
43
+ boundaries usually belong to the environment**: a harness that also claims to be a sandbox
44
+ is claiming a guarantee it cannot keep from inside the same process.
45
+
46
+ - **`references/audit.md`** — seven tracks, three evidence tiers (**measured / documented /
47
+ judgement**, never inflated), computed priority, and a report shape that ends in a plan
48
+ rather than a score. The finding that ends most audits early is stated first: no evals
49
+ makes everything downstream unfalsifiable, including the audit.
50
+
51
+ - **`scripts/audit_agent.py`** — the mechanical half. Five conservative detectors
52
+ (unbounded loop, empty tool description, swallowed error, missing timeout, duplicated
53
+ model literal), each requiring the file to show **two** independent signs of an agent, each
54
+ finding carrying `file:line`. It always prints **what it cannot see** and a **denominator**,
55
+ because `read: 1` alone looks like a broken pass while `1 of 4261` is itself a finding.
56
+ Virtualenvs are skipped by their `pyvenv.cfg` marker rather than by name — a real
57
+ repository met during testing kept 4249 of its 4261 files in `myenv/`, and was excluded
58
+ only because `site-packages` happened to be listed too.
59
+
60
+ ### Changed
61
+
62
+ - **`PROTOCOL_PINNED` now covers `agent-harness`.** Its references document guidance that
63
+ moves, so each carries `**Spec pinned:** … · read <date>` and the build fails without it.
64
+ - **CI runs the scanner's own self-test**, and asserts that a real-tree run discloses both
65
+ its blind-spot list and its denominator — a scanner that could stop disclosing would be a
66
+ scanner nobody could calibrate.
67
+
68
+
69
+ ## v0.7.2 — the plants say whether they landed, and two of them were not
70
+
71
+ Eight negative self-tests asserted inline, in Python, that their edit had happened —
72
+ which works, and is the **fifth** careful copy of an idea this family already scripted.
73
+ `plant_guard.py`'s own docstring was written against exactly that shape: five hand-written
74
+ variants produced five different bugs, one of which reached a pull request.
75
+
76
+ Adopting the shared implementation found two plants that were doing nothing at all.
77
+
78
+ ### Fixed
79
+
80
+ - **`cp -R . /tmp/x` into an existing `/tmp/x` nests the tree instead of replacing it.**
81
+ The plant then edits a file left by the previous run, and `touch` on a file that already
82
+ exists changes neither content nor mode — so *a reference nobody links* and *stray
83
+ SKILL.md* both planted nothing, the validator honestly passed, and the step would have
84
+ reported a healthy guard as broken. CI is always fresh, so this was invisible there and
85
+ only ever bit the machine the plants were written on. Every copy is now `rm -rf`'d first.
86
+
87
+ ### Added
88
+
89
+ - **`test/plant_guard.py` and its nine fixtures**, shared with `make-skill`, the umbrella
90
+ and `seo-aeo-audit`. It compares content **and permission bits**, because the variant
91
+ that shipped compared bytes against a plant whose whole effect was `chmod`.
92
+ - **Every plant wrapped in `snap` / `verify`**, the description being the step's own name,
93
+ so a refusal names which plant died.
94
+ - **A negative for the guard itself**: an unchanged copy must be reported, not passed.
95
+
96
+ All eight plants were then watched landing on the machine they were written on.
97
+
3
98
  All notable changes to this project are documented here.
4
99
  Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
100
  Versioning: [SemVer](https://semver.org/spec/v2.0.0.html).
package/README.md CHANGED
@@ -9,9 +9,10 @@ Part of the [ssheleg skill family](https://github.com/ssheleg/sshlg-skills).
9
9
 
10
10
  ## What is in here
11
11
 
12
- Three skills — `agent-orchestrator` for building one, `agent-evals` for proving it
13
- behaves, `agent-interop` for everything it talks to outside its own process — and
14
- eleven references they load on demand.
12
+ Four skills — `agent-orchestrator` for wiring the loop, `agent-evals` for proving it
13
+ behaves, `agent-interop` for everything it talks to outside its own process,
14
+ `agent-harness` for what it is **told** — and sixteen references they load on demand,
15
+ plus one scanner.
15
16
 
16
17
  **The orchestrator** (`SKILL.md`) — what the agent reads first:
17
18
 
@@ -56,6 +57,23 @@ undoes both), `mcp-ship.md` (mounting, and the 404 that is really a double path)
56
57
  an API gateway does not). Plus a link map, and a verdict on each neighbouring standard —
57
58
  ACP, AGNTCY, AP2, Agent Skills — so an agent stops guessing.
58
59
 
60
+ **The harness skill** (`agent-harness/SKILL.md`) — the layer between the loop and the
61
+ model, and the one where most agent bugs actually live: *the biggest performance
62
+ improvements often come from clearly explaining tool usage in the system prompt*, and *even
63
+ small refinements to tool descriptions can yield dramatic improvements*. Before adding a
64
+ retry or a sub-agent, it asks four questions about the text. Five references —
65
+ `system-prompt.md` (right altitude, enumerated vocabulary, and the three things reasoning
66
+ models changed — starting with **do not add chain-of-thought**), `tools.md` (the
67
+ agent–computer interface, with a worked before/after and poka-yoke), `techniques.md`
68
+ (fifteen techniques, a verdict each **for production** rather than a benchmark),
69
+ `layers.md` (which layer you are building at, and why permission boundaries are usually the
70
+ environment's job), `audit.md` (seven tracks, evidence tiers, a plan instead of a score).
71
+
72
+ It runs in both directions: **building a harness and auditing somebody else's are one
73
+ checklist read forwards and backwards.** `scripts/audit_agent.py` is the mechanical half —
74
+ five conservative detectors, and it always prints what it *cannot* see plus a denominator,
75
+ so its silence is never read as a pass.
76
+
59
77
  **`references/context-engineering.md`** — what the loop gives up when the window
60
78
  runs out: the five-rung compaction ladder and why to re-measure between rungs,
61
79
  the tool-pair boundary invariant, typed carryover blocks copied across the
@@ -139,6 +157,10 @@ audit row has to carry to prove a control was on.
139
157
  trajectory rather than a final answer, turning a production failure into a
140
158
  permanent fixture, calibrating a judge, gating a release on offline evals.
141
159
 
160
+ `agent-harness`: writing or fixing a system prompt, shaping tools so the model picks the
161
+ right one, choosing between ReAct, reflection, planning and voting — or auditing an agent
162
+ system somebody else built. Not the loop's plumbing, its evals, or its protocols.
163
+
142
164
  `agent-interop`: building or consuming an MCP server, exposing or calling another
143
165
  agent over A2A, publishing to the MCP Registry, or putting a gateway in front of
144
166
  agent traffic. Not for designing one server's tool set — that is a design problem,
@@ -147,7 +169,7 @@ construction, which is `make-skill`. That boundary runs both ways: `make-skill`
147
169
  what changes *because you are writing a skill*, and the protocol itself is described
148
170
  here and nowhere else in the family.
149
171
 
150
- None of the three triggers for a single LLM call in a script or for prompt wording —
172
+ None of the four triggers for a single LLM call in a script or for prompt wording —
151
173
  that is not an orchestrator, and pulling this much doctrine for it is how a skill
152
174
  teaches you to route around it.
153
175
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ssheleg/agent-stack",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "Production patterns for AI agent orchestrators \u2014 tool-calling loops, multi-stage pipelines with checkpoints, LLM provider routing with fallback, four-layer memory with confidence decay \u2014 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"
@@ -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.7.1",
5
+ "version": "0.8.0",
6
6
  "author": {
7
7
  "name": "ssheleg",
8
8
  "url": "https://x.com/sshlg93"
@@ -0,0 +1,161 @@
1
+ ---
2
+ name: agent-harness
3
+ description: >-
4
+ Use when the question is what the agent is TOLD rather than how its loop is wired — writing
5
+ or fixing a system prompt, shaping tools so the model actually picks the right one,
6
+ deciding whether a job wants a workflow or an agent, or choosing between ReAct, reflection,
7
+ planning and voting. Also the audit direction: reviewing an agent system somebody else
8
+ built, with tracks, evidence tiers and a prioritized plan instead of a score, plus a
9
+ scanner for the defects that are mechanically visible. Triggers - "system prompt", "tool
10
+ description", "the agent picks the wrong tool", "agent loops forever", "prompt engineering",
11
+ "ReAct", "reflection", "workflow or agent", "audit this agent", "review our agent system",
12
+ "системный промпт", "агент не вызывает тул", "аудит агента", "воркфлоу или агент". Not for
13
+ the loop's plumbing, its evals, or the protocols it speaks — those are the sibling skills.
14
+ ---
15
+
16
+ # Agent harness — what the agent is told, and how to audit what someone else told theirs
17
+
18
+ `agent-orchestrator` wires the loop. `agent-evals` proves it behaves. `agent-interop` gets
19
+ it talking to other processes. **This skill is the layer between them and the model: the
20
+ prompt, the tools, and the shape of the work.**
21
+
22
+ It runs in both directions. Building one and auditing one are the same checklist read
23
+ forwards and backwards, which is why they live together here.
24
+
25
+ ---
26
+
27
+ ## Rule zero — most agent bugs are prompt bugs wearing a stack trace
28
+
29
+ The instinct when an agent misbehaves is to change the code. The measured reality, in every
30
+ source this skill was built from, is that the largest behavioural changes come from the
31
+ text: **"the biggest performance improvements often come from clearly explaining tool usage
32
+ in the system prompt"**, and **"even small refinements to tool descriptions can yield
33
+ dramatic improvements."**
34
+
35
+ Before adding a retry, a router, or a sub-agent, check in this order:
36
+
37
+ 1. **Does the tool description say when to use it, not just what it does?**
38
+ 2. **Does the system prompt name the vocabulary?** An agent told to track status will invent
39
+ `pending` and `to-do` and `done` and `completed` in the same run unless the allowed values
40
+ are enumerated.
41
+ 3. **Does the agent know today's date?** A model with a training cutoff will answer from
42
+ memory rather than search unless the current date is injected.
43
+ 4. **Is the instruction flexible where it should be strict?** *"Use the tools in the order
44
+ that makes most sense to you"* is right while you are learning the task and wrong in
45
+ production, where *"you MUST execute a web search for each task"* is what stops a step
46
+ from being skipped.
47
+
48
+ Only then reach for architecture. Reaching for it first is how a prompt defect becomes a
49
+ permanent structural cost.
50
+
51
+ ---
52
+
53
+ ## Workflow or agent — decide this before anything else
54
+
55
+ An **agent** dynamically directs its own process. A **workflow** follows predefined code
56
+ paths. The choice is not about sophistication; it is about whether the number of steps is
57
+ knowable in advance.
58
+
59
+ | Build a workflow when | Build an agent when |
60
+ |---|---|
61
+ | requirements are clear and stable | the task is open-ended or exploratory |
62
+ | predictability and explicit control matter | flexibility outweighs predictability |
63
+ | debugging and cost control are priorities | adaptive reasoning across variables is needed |
64
+ | you can name every step now | step count is unpredictable and cannot be hardcoded |
65
+
66
+ **Start at the simplest thing that works, and stop there.** An agent adds latency, cost and
67
+ a class of failure a workflow does not have — it needs *trust in its own decisions*. Pay for
68
+ that only where a fixed path genuinely cannot be written.
69
+
70
+ ### The five workflow patterns, before you reach for autonomy
71
+
72
+ | Pattern | Shape | Reach for it when |
73
+ |---|---|---|
74
+ | **Prompt chaining** | sequential calls, each on the last output, with programmatic checks between | the task decomposes into fixed steps — outline then draft, draft then translate |
75
+ | **Routing** | classify the input, send it to a specialist | categories are distinct and each wants its own prompt |
76
+ | **Parallelization** | *sectioning* (independent subtasks at once) or *voting* (same task N times) | subtasks are independent, or confidence needs more than one sample |
77
+ | **Orchestrator–workers** | a central model decomposes and delegates, then synthesizes | the subtasks **cannot be predefined** — this is the honest boundary with routing |
78
+ | **Evaluator–optimizer** | one model produces, another critiques, loop | clear evaluation criteria exist and iteration measurably helps |
79
+
80
+ **Orchestrator–workers versus routing is the distinction people get wrong.** Routing picks
81
+ from a known set. Orchestration invents the set per request. If you can enumerate the
82
+ branches, you wanted routing and it is cheaper.
83
+
84
+ ---
85
+
86
+ ## References
87
+
88
+ Each opens with its own **Load this when** line and a revision stamp — this material moves,
89
+ and `test/validate.py` fails the build on a reference that does not say when it was read.
90
+
91
+ | File | Read it when |
92
+ |---|---|
93
+ | [`references/system-prompt.md`](references/system-prompt.md) | you are **writing or fixing the prompt** — altitude, structure, vocabulary, dynamic context, and what changes for reasoning models |
94
+ | [`references/tools.md`](references/tools.md) | the model **picks the wrong tool, or none** — the agent–computer interface: how many, named how, described how, returning what |
95
+ | [`references/techniques.md`](references/techniques.md) | you are choosing between **ReAct, reflection, voting, planning** and the rest — every entry carries a verdict for production, not a benchmark score |
96
+ | [`references/layers.md`](references/layers.md) | deciding **what your harness owns** — kernel, workbench and product layers, and why permission boundaries are usually somebody else's job |
97
+ | [`references/audit.md`](references/audit.md) | reviewing **an agent system you did not build** — seven tracks, evidence tiers, and a prioritized plan |
98
+
99
+ **`scripts/audit_agent.py`** — the mechanical half of the audit. It finds what is visible
100
+ without understanding intent (an unbounded loop, a tool with no description, a swallowed
101
+ tool error, a hardcoded model, a missing timeout) and **prints the list of things it cannot
102
+ see**, so its silence is never read as a pass.
103
+
104
+ ---
105
+
106
+ ## Auditing an agent system — the short version
107
+
108
+ The long version is `references/audit.md`. The shape:
109
+
110
+ 1. **Run the scanner first.** It is cheap, and its blind-spot list tells you what the rest of
111
+ the audit must cover by hand.
112
+ 2. **Walk the seven tracks** — prompt, tools, control flow, context, failure, permission,
113
+ evidence — and record a finding only with an observation attached.
114
+ 3. **Tier every recommendation** by what backs it: measured here, documented upstream, or
115
+ judgement.
116
+ 4. **Output a prioritized plan, not a score.** A number tells nobody what to change on
117
+ Monday. This is the same rule `agent-evals` applies to eval rubrics and
118
+ `seo-aeo-audit` to sites.
119
+
120
+ **The finding that ends most audits early:** the system has no evals. Everything downstream
121
+ is then unfalsifiable — including this audit. Say so first, and make it the first item.
122
+
123
+ ---
124
+
125
+ ## Boundaries
126
+
127
+ **Against `agent-orchestrator`.** That skill owns the loop's *plumbing*: iteration guards,
128
+ trimming, sub-agent dispatch, provider routing, memory layers, checkpoints. This one owns
129
+ what the model is *told*. They meet in one place:
130
+ `agent-orchestrator/references/context-engineering.md` covers **compaction** — what to drop
131
+ when the window fills — while this skill's `system-prompt.md` covers what to put there in
132
+ the first place. Filling and emptying, two files.
133
+
134
+ **Against `agent-evals`.** That skill measures whether an agent behaves, from execution
135
+ records. This one reviews how it was *built*, from its source and prompts. An audit that
136
+ finds no evals hands over to it; an eval suite that keeps failing on the same axis hands
137
+ back here.
138
+
139
+ **Against `agent-interop`.** MCP, A2A, the registry, gateways — the wire between processes.
140
+ Tool *descriptions* are here; tool *protocol* is there.
141
+
142
+ **Not covered:** model choice and pricing (see the `claude-api` skill for Anthropic's), the
143
+ wallet under resale (`agent-orchestrator/references/llm-proxy-billing.md`), and RAG
144
+ retrieval quality, which is a search problem this skill only touches where it enters the
145
+ prompt.
146
+
147
+ ---
148
+
149
+ ## Checklist — a harness worth shipping
150
+
151
+ - [ ] Workflow-versus-agent decided deliberately, and the simpler option was actually tried
152
+ - [ ] System prompt at the **right altitude** — heuristics, not hardcoded branches, not vague hope
153
+ - [ ] Every status, category and enum the agent must produce is **enumerated in the prompt**
154
+ - [ ] Today's date, and any other volatile context, injected rather than assumed
155
+ - [ ] Tools: a few high-impact ones, namespaced, each described as if to a new colleague
156
+ - [ ] Tool responses carry **meaning, not identifiers**, and are paginated or truncated by default
157
+ - [ ] Tool errors **teach the next attempt** instead of restating a stack trace
158
+ - [ ] One technique chosen per problem, with a reason — not ReAct because it was in a paper
159
+ - [ ] Sub-agents return **distilled summaries**, not transcripts
160
+ - [ ] The agent can be observed: which tool, which arguments, which observation, how many tokens
161
+ - [ ] An eval exists before the prompt is tuned, or the tuning is folklore
@@ -0,0 +1,141 @@
1
+ # Auditing an agent system
2
+
3
+ **Load this when:** reviewing an agent system you did not build — a due-diligence pass, an
4
+ inherited codebase, or a "why is this unreliable" investigation.
5
+
6
+ **Spec pinned:** the tracks below are this pack's synthesis of Anthropic agent guidance, `promptingguide.ai` and the sibling skills · read 2026-08-14
7
+
8
+ ## Contents
9
+
10
+ - What this audit produces
11
+ - Run the scanner first
12
+ - Seven tracks
13
+ - Evidence tiers
14
+ - Priority, computed
15
+ - The report
16
+ - Traps
17
+
18
+ ## What this audit produces
19
+
20
+ **A prioritized change plan, with an observation behind every finding.** Not a score.
21
+
22
+ A number compresses away the only useful information — *what to change on Monday* — and
23
+ invites arguing about the number. This is the same rule `agent-evals` applies to eval
24
+ rubrics and `seo-aeo-audit` applies to sites, and it is the family's position rather than a
25
+ preference: **pass/fail with a named failure condition beats a scalar that names no fix.**
26
+
27
+ ## Run the scanner first
28
+
29
+ ```bash
30
+ python3 scripts/audit_agent.py <path> # human-readable
31
+ python3 scripts/audit_agent.py <path> --json # machine-readable
32
+ ```
33
+
34
+ It finds only what is mechanically visible, and **it prints the list of what it cannot see.**
35
+ Read that list: it is the agenda for the manual half. A scanner that goes quiet is reporting
36
+ its own blindness, and an audit that stops there has audited the scanner.
37
+
38
+ ## Seven tracks
39
+
40
+ Walk them in order. Later tracks assume earlier ones.
41
+
42
+ ### 1 — Prompt
43
+
44
+ - Is there a system prompt under version control, or is it a string literal edited in place?
45
+ - **Altitude**: hardcoded branches (brittle) or vague hope (useless)? See `system-prompt.md`.
46
+ - Are the values the agent must emit **enumerated**, or invented per run?
47
+ - Is volatile context — the date above all — **injected**?
48
+ - Is tool *policy* in the prompt, or only tool *schema*?
49
+ - Has it been pruned, or only appended to since the first incident?
50
+
51
+ ### 2 — Tools
52
+
53
+ - How many? Can the team say, without hesitating, which applies to a borderline case?
54
+ - Are descriptions written **for a model choosing under uncertainty**, or for a human reading docs?
55
+ - Do responses return **meaning or identifiers**?
56
+ - Is there a default limit on response size, or only an optional one?
57
+ - Do errors **name the next action**?
58
+ - Are destructive tools guarded by shape (`confirm: true`, absolute paths, enums) rather than by instruction?
59
+
60
+ ### 3 — Control flow
61
+
62
+ - **Workflow or agent — and was that decided, or defaulted?** An agent where a chain would do is the most expensive finding on this list.
63
+ - Is there a **bounded iteration guard**, and what happens at the bound — a partial answer, or nothing?
64
+ - Are retries and fallbacks **multiplied**? Three providers × three retries is nine calls for one prompt.
65
+ - Is there loop detection, or does a repeated near-identical tool call run until the budget does?
66
+ - Do sub-agents return **distilled summaries** or transcripts?
67
+
68
+ ### 4 — Context
69
+
70
+ - Does anything measure window usage **before** a request fails?
71
+ - Is there a compaction strategy, and does it preserve **decisions and open questions** rather than the discussion?
72
+ - Can a large tool result be **offloaded** and referenced, or does it land in the window whole?
73
+ - Is memory a design, or the conversation history by default?
74
+
75
+ ### 5 — Failure
76
+
77
+ - What happens when a tool errors — is it distinguished from a tool returning nothing?
78
+ - Under MCP, is `isError: true` handled, or does a 200 read as success?
79
+ - Is there a timeout on every external call?
80
+ - Is degradation **honest** — does the user learn the answer is partial?
81
+ - Is there a path where the agent silently does nothing and reports success?
82
+
83
+ ### 6 — Permission
84
+
85
+ - **Which layer owns the boundary?** If the harness delegates it (`layers.md`), audit the surroundings instead of filing a finding.
86
+ - Is tool access differentiated per caller, or is one credential shared by every path?
87
+ - Is tool output treated as **untrusted input**?
88
+ - Can an audit row prove a control was applied — does it carry the **policy version**?
89
+ - Is there a deterministic limit anywhere consequential, or only probabilistic content checks?
90
+
91
+ ### 7 — Evidence
92
+
93
+ - **Are there evals?** If not, this is finding number one and everything else is unfalsifiable.
94
+ - Do they judge the **trajectory**, or only the final answer?
95
+ - Has any production failure become a permanent fixture?
96
+ - Is a judge calibrated against human labels, or trusted because it is a judge?
97
+ - Can a past run be replayed — is the execution record durable?
98
+
99
+ ## Evidence tiers
100
+
101
+ Every finding carries one, and the tier is part of the finding:
102
+
103
+ | Tier | Means | Example |
104
+ |---|---|---|
105
+ | **Measured** | observed here, in this system, with the observation attached | "`agent.py:212` — the `while` has no bound; a repeated call ran 47 times in the log at `logs/2026-08-02`" |
106
+ | **Documented** | the upstream source says so, and this system contradicts it | "descriptions restate the name; Anthropic's tool guidance calls this the highest-leverage fix" |
107
+ | **Judgement** | experience, no measurement available here | "two tools look interchangeable to us" |
108
+
109
+ **Never present judgement as measured.** A finding whose tier is honest survives the meeting
110
+ where it is challenged; one that is inflated loses the whole report.
111
+
112
+ ## Priority, computed
113
+
114
+ `P = blast × confidence / effort`
115
+
116
+ - **blast** 3 = a user of the system is harmed · 2 = the operator · 1 = a future maintainer
117
+ - **confidence** 3 = measured · 2 = documented · 1 = judgement
118
+ - **effort** 1 = under an hour · 2 = a session · 3 = its own project
119
+
120
+ Computed, not felt — so a dramatic finding nobody can act on ranks below a boring one that
121
+ is fixed before lunch, and the ranking can be argued with on its inputs.
122
+
123
+ ## The report
124
+
125
+ 1. **One paragraph** — what the system is, which layer, and the single most important thing.
126
+ 2. **The scanner output**, including its blind-spot list, verbatim.
127
+ 3. **Findings by track**, each with observation, tier and priority.
128
+ 4. **The plan** — ordered by P, with the first three items sized so they can start immediately.
129
+ 5. **What was not looked at**, and why. An audit that does not say where it stopped is read
130
+ as complete.
131
+
132
+ ## Traps
133
+
134
+ - **Auditing the code and not the prompt.** The prompt is the largest behavioural surface and
135
+ is often not in the repository at all — ask where it lives before concluding it is fine.
136
+ - **Filing "no permission model" against a harness that delegates by design.** Check the
137
+ layer first (`layers.md`).
138
+ - **Grading instead of planning.** A score ends the conversation the audit was meant to start.
139
+ - **Confusing "no evals" with "not measured yet."** It is the root finding; put it first,
140
+ because every other conclusion inherits it.
141
+ - **Reading a silent scanner as a clean system.** It is silent about what it can see.
@@ -0,0 +1,100 @@
1
+ # Layers — what your harness owns, and what it must not
2
+
3
+ **Load this when:** deciding what to build versus adopt, or comparing agent frameworks and
4
+ finding the comparison keeps sliding.
5
+
6
+ **Spec pinned:** the three-layer reading of Pi / Goose / OpenCode (gist `AIMOWAY/bd8007c8`); Anthropic agent guidance · read 2026-08-14
7
+
8
+ ## Contents
9
+
10
+ - The question that resolves most framework arguments
11
+ - Three layers
12
+ - What a harness owns
13
+ - What a harness should delegate
14
+ - Choosing your layer
15
+ - Traps
16
+
17
+ ## The question that resolves most framework arguments
18
+
19
+ "Which of these agent frameworks is better" is usually unanswerable because the candidates
20
+ sit at different heights. The question that resolves it:
21
+
22
+ > **Which layer of the agent stack am I trying to work at?**
23
+
24
+ Compare within a layer. Across layers, the comparison is a category error, and the argument
25
+ will not converge no matter how long it runs.
26
+
27
+ ## Three layers
28
+
29
+ | Layer | What it is | Shape | Best when |
30
+ |---|---|---|---|
31
+ | **Kernel / harness** | the agent loop itself — runtime, LLM API abstraction, tool dispatch, a terminal UI | a toolkit you build *on* | you are building an agent product, or studying how agents actually work |
32
+ | **Workbench / orchestration** | a local environment: desktop app, CLI and API, extensions, workflows across many kinds of work | a product you *extend* | you want capability now, across coding, research and automation, without owning the loop |
33
+ | **Product agent** | a domain agent — most visibly coding: explore, plan, edit, test, with built-in modes | a product you *use* | the domain is the one it was built for |
34
+
35
+ The distinction is not quality. A kernel is *supposed* to be smaller than a workbench; that
36
+ is what makes it legible.
37
+
38
+ ## What a harness owns
39
+
40
+ If you are building at the kernel layer, these are yours and nobody else's:
41
+
42
+ 1. **The loop** — iterate, dispatch tools, decide when to stop. With a bounded iteration
43
+ guard, because an unbounded loop is the defect that costs money while looking like work.
44
+ 2. **The model boundary** — one abstraction over providers, so the loop does not know which
45
+ vendor answered. See `agent-orchestrator` §6.
46
+ 3. **Tool dispatch and the ACI** — registration, schemas, parallel versus sequential
47
+ execution, error surfacing. See `tools.md`.
48
+ 4. **Context accounting** — knowing how full the window is *before* the request fails, and
49
+ what to do about it. See `agent-orchestrator/references/context-engineering.md`.
50
+ 5. **Observability** — which tool, which arguments, which observation, how many tokens. If
51
+ this is missing, every other item becomes unfalsifiable.
52
+ 6. **The interrupt/resume contract** — one mechanism, not two. See
53
+ `agent-orchestrator/references/runtime.md`.
54
+
55
+ ## What a harness should delegate
56
+
57
+ The interesting stance, and it is a design position rather than an omission:
58
+
59
+ **Permission boundaries usually belong to the environment, not the harness.** Pi states this
60
+ explicitly — it provides the kernel and delegates sandboxing and permissions to whatever
61
+ surrounds it. Read as a weakness, it looks like a missing feature. Read as architecture, it
62
+ is a clear statement: *a harness that also claims to be a sandbox is claiming a guarantee it
63
+ cannot keep*, because it runs in the same process as the code it would be confining.
64
+
65
+ Two consequences worth stating plainly:
66
+
67
+ - **A harness advertising "safe tool execution" without an OS-level or container boundary is
68
+ advertising a preference, not a control.** MCP says the same about roots: servers *SHOULD*
69
+ respect them, and real enforcement is OS permissions and sandboxing.
70
+ - **Deciding the layer decides the audit.** If the harness delegates permission, the audit
71
+ looks at what surrounds it, and "the harness has no permission model" stops being a
72
+ finding and becomes a question about the deployment.
73
+
74
+ Also usually delegated: identity and secrets (a credential store, never the loop), durable
75
+ state (a database or a file system, not the conversation), and policy (see
76
+ `agent-orchestrator/references/governance.md` — permission, not protocol).
77
+
78
+ ## Choosing your layer
79
+
80
+ - **Building a product with agentic features** → adopt a workbench or embed a kernel; do not
81
+ write a third loop. Loops are commodity; your tools and prompt are not.
82
+ - **Building an agent platform others build on** → the kernel layer is yours, and the ACI is
83
+ your product surface.
84
+ - **Automating your own work** → a product agent, and stop. The most common expensive mistake
85
+ is building a kernel to solve a workbench problem.
86
+
87
+ **The test that catches the mistake early:** if you cannot name a behaviour you need that the
88
+ layer above does not provide, you are building the layer for its own sake.
89
+
90
+ ## Traps
91
+
92
+ - **Comparing across layers**, then choosing on a benchmark that only makes sense within one.
93
+ - **Writing a loop because the loop is the interesting part.** It is a week; the tools and
94
+ the prompt are the year.
95
+ - **Claiming a security boundary the layer cannot enforce.** Say what is enforced and by
96
+ what; a sandbox protects the host, not the sandbox.
97
+ - **Adopting a workbench and then fighting its opinions.** Its opinions are the product; if
98
+ you disagree with enough of them, you wanted the kernel.
99
+ - **Assuming the layer is stable.** These projects move; check what the current version
100
+ actually owns before designing around a division of labour you read about once.
@@ -0,0 +1,127 @@
1
+ # The system prompt — altitude, vocabulary, and what changes for reasoning models
2
+
3
+ **Load this when:** writing or fixing an agent's system prompt, or explaining why the same
4
+ prompt behaves differently across runs.
5
+
6
+ **Spec pinned:** Anthropic context-engineering and agent guidance; `promptingguide.ai` agents/* and guides/reasoning-llms · read 2026-08-14
7
+
8
+ ## Contents
9
+
10
+ - The right altitude
11
+ - What actually belongs in there
12
+ - Enumerate the vocabulary
13
+ - Inject what the model cannot know
14
+ - Flexible while learning, strict in production
15
+ - Structure, examples, and the cost of formatting
16
+ - Reasoning models change three things
17
+ - Traps
18
+
19
+ ## The right altitude
20
+
21
+ A system prompt fails in two directions and the middle is narrower than it looks.
22
+
23
+ **Too low** — hardcoded if-then branches for every case. It works on the cases you wrote and
24
+ is brittle everywhere else, and each new case costs another branch. You are writing a
25
+ program in prose, badly.
26
+
27
+ **Too high** — vague guidance that assumes a shared understanding the model does not have.
28
+ "Be helpful and use good judgement" tells it nothing it did not already believe.
29
+
30
+ The target: **specific enough to guide behaviour, flexible enough to give the model strong
31
+ heuristics.** A useful test — could a competent new colleague follow this without asking a
32
+ clarifying question, and without being insulted? If they would ask, it is too high. If they
33
+ would feel micromanaged into a corner where their judgement cannot help, it is too low.
34
+
35
+ ## What actually belongs in there
36
+
37
+ In rough order of how much behaviour each buys:
38
+
39
+ 1. **Tool usage instructions.** Not the tool schema — the *policy*. When to reach for which,
40
+ what order usually makes sense, what to do when one fails. The largest measured gains come
41
+ from here, and it is the part most teams leave to the schema alone.
42
+ 2. **The role and its boundaries** — what this agent is for, and what it must hand off.
43
+ 3. **The vocabulary** it must produce (below).
44
+ 4. **Volatile context** it cannot know (below).
45
+ 5. **Failure instructions** — what to do when a tool errors, when data is missing, when the
46
+ task is impossible. Absent, the model invents a recovery, and inventions are not uniform.
47
+ 6. **Output contract** — shape, not prose about shape.
48
+
49
+ ## Enumerate the vocabulary
50
+
51
+ **Be explicit about allowed values.** An agent asked to track task status will produce
52
+ `pending` in one turn and `to-do` in the next, `completed` here and `done` there — and any
53
+ code reading those strings now has a bug that appears intermittently and reads as
54
+ flakiness.
55
+
56
+ This generalises past status: every category, label, severity, priority or state the agent
57
+ emits should appear as an enumerated set in the prompt, or in the tool's parameter `enum`,
58
+ or both. It is the cheapest determinism available.
59
+
60
+ ## Inject what the model cannot know
61
+
62
+ **Today's date is the canonical example**, and its absence has a specific failure signature:
63
+ the agent answers from training data instead of searching, confidently and with no error.
64
+ Inject the date and the behaviour changes without another word of instruction.
65
+
66
+ The general rule: anything volatile that the model would otherwise fill from memory —
67
+ current date, environment, tenant, available capabilities, the user's locale — is injected,
68
+ not assumed. A capability-aware prompt that lists only the tools actually connected beats a
69
+ static prompt describing tools that may be absent.
70
+
71
+ ## Flexible while learning, strict in production
72
+
73
+ The same instruction should not survive the whole lifecycle.
74
+
75
+ - **While you are still learning what good looks like:** *"Use the tools in the order that
76
+ makes most sense to you."* This surfaces what the model thinks the task is, which is the
77
+ information you need.
78
+ - **Once the sequence is known and a skipped step is a defect:** *"You MUST execute a web
79
+ search for each task."* Flexibility here buys nothing and costs a silently missing step.
80
+
81
+ Teams get stuck at the first form because it felt elegant, then debug an agent that
82
+ "sometimes forgets" — which is not forgetting, it is permission.
83
+
84
+ ## Structure, examples, and the cost of formatting
85
+
86
+ **Structure inputs and outputs** with delimiters, XML tags or JSON. Clear segmentation
87
+ reduces the class of error where the model treats data as instruction.
88
+
89
+ **Examples are worth more than description**, and the mistake is quantity. Curate a few
90
+ **diverse, canonical** examples rather than an exhaustive list of edge cases — the latter
91
+ reads as a lookup table and the model generalises from it badly.
92
+
93
+ **But formatting has a cost.** Keep formats close to natural internet text where you can;
94
+ elaborate escaping, deeply nested structures and unusual syntaxes spend the model's
95
+ attention on parsing rather than the task. Ask whether a human writing this by hand would
96
+ choose the format. If not, it is overhead.
97
+
98
+ ## Reasoning models change three things
99
+
100
+ Treating a reasoning model like a completion model is now a common and expensive mistake.
101
+
102
+ 1. **Do not add chain-of-thought instructions.** Native reasoning already happens.
103
+ Explicit step-by-step prompting is redundant and **can hurt instruction-following** —
104
+ which is the opposite of what the person adding it intends.
105
+ 2. **Give goals, not procedures.** Be explicit about the high-level outcome and let the
106
+ model plan the route. Procedural micro-steps fight the thing you are paying for.
107
+ 3. **Reasoning effort is a dial that did not exist before** — low/medium/high trades cost
108
+ against accuracy per call, so it is a per-stage decision, not a global setting.
109
+
110
+ Two more, worth knowing before you architect around them: **few-shot is still useful, but
111
+ mainly for output *format***, not for teaching the task; and **tool-calling remains weaker
112
+ in most reasoning models**, which is why the common shape is a reasoning model for planning
113
+ and a different one for execution.
114
+
115
+ ## Traps
116
+
117
+ - **Growing the prompt instead of fixing it.** Every incident adds a sentence; nothing is
118
+ ever removed; a year later nobody can say which line does work. Prune on the same schedule
119
+ you add.
120
+ - **Describing tools twice**, in the schema and in the prompt, with the two drifting. Put
121
+ *policy* in the prompt and *contract* in the schema, and say which is which.
122
+ - **A prompt that assumes a tool exists.** Capability-aware assembly, or an explicit
123
+ fallback; never a promise the runtime may not keep.
124
+ - **Tuning the prompt with no eval.** You are optimising against the last thing you noticed.
125
+ See `agent-evals`.
126
+ - **One prompt for planning and execution.** Separation of concerns measurably improves
127
+ reliability and lets a cheaper model take the mechanical half.
@@ -0,0 +1,115 @@
1
+ # The technique catalogue, with a verdict on each
2
+
3
+ **Load this when:** choosing between ReAct, reflection, voting, planning and the rest — or
4
+ being asked why the system does not use one that appeared in a paper.
5
+
6
+ **Spec pinned:** `promptingguide.ai` techniques/* and guides/*; Anthropic agent guidance · read 2026-08-14
7
+
8
+ **How to read the verdicts.** Every technique here works somewhere; the column says whether
9
+ it earns its cost *in a production agent loop*, which is a narrower question than whether it
10
+ raised a benchmark. Costs are real: each of these multiplies calls, latency or context, and
11
+ several of them were measured on single-turn QA rather than a long-running agent.
12
+
13
+ ## Contents
14
+
15
+ - The catalogue
16
+ - ReAct, in detail
17
+ - Reflection, in detail
18
+ - Voting and self-consistency
19
+ - What reasoning models made redundant
20
+ - Choosing one
21
+
22
+ ## The catalogue
23
+
24
+ | Technique | What it is | Verdict for a production agent loop |
25
+ |---|---|---|
26
+ | **Zero-shot** | instruction alone | **Default.** Start here; everything below is a cost you must justify |
27
+ | **Few-shot** | input→output exemplars | **Yes, for format.** Curate a few diverse canonical examples. It teaches shape far better than it teaches judgement, and an exhaustive list makes the model brittle |
28
+ | **Chain-of-thought** | "think step by step" | **Legacy on reasoning models — actively harmful there** (it can degrade instruction-following). Still useful on non-reasoning models for arithmetic and multi-constraint tasks |
29
+ | **ReAct** | interleaved thought → action → observation | **Yes — this is the agent loop.** Most harnesses implement it without naming it. See below for what it does not fix |
30
+ | **Reflexion / self-critique** | actor, evaluator, self-reflection, with episodic memory | **Selectively.** Real gains where a *cheap objective signal* exists — tests pass, query runs, schema validates. Without one, the model grades its own homework |
31
+ | **Self-consistency** | sample N, take the majority | **Rarely.** N× cost and latency for a single answer; needs a well-defined answer to vote on. Use for a high-stakes classification, not for a whole trajectory |
32
+ | **Tree of Thoughts** | explore and prune a branching search | **Almost never in production.** Combinatorial cost, and the pruning heuristic is usually the hard part you have not solved. A planner plus a bounded retry gets most of the value |
33
+ | **ART** (automatic reasoning + tool use) | select exemplars and tools from a task library automatically | **Watch.** The idea — a library of trajectories rather than a hand-written prompt — is where prompt maintenance is heading; the tooling is not settled |
34
+ | **Prompt chaining** | fixed sequence with checks between | **Yes — and prefer it over an agent** wherever the steps are knowable |
35
+ | **Meta prompting** | prompt about the structure of the task, not its content | **Occasionally.** Useful for generating scaffolds; not a loop technique |
36
+ | **Generate-knowledge** | elicit facts first, then answer | **No.** Retrieval solves the same problem with grounding; this invents plausible knowledge |
37
+ | **RAG** | retrieve, then generate | **Yes, and it is a search problem.** Its quality lives outside this skill; what belongs here is *just-in-time* retrieval — see below |
38
+ | **Just-in-time retrieval** | hold lightweight identifiers (paths, queries, links); load at runtime via tools | **Yes.** The scalable default: it mirrors how people work, and keeps the window for reasoning rather than for data |
39
+ | **Structured note-taking** | write notes to durable memory outside the window, read them back | **Yes for long tasks.** Persistent memory at low overhead, and it survives compaction — which a summary of the discussion does not |
40
+ | **Sub-agents** | specialists returning distilled summaries | **Yes where the sub-task has its own context need.** The value is the *isolated window*; a sub-agent returns a 1–2k-token distilled summary, never a transcript |
41
+
42
+ ## ReAct, in detail
43
+
44
+ **Thought → Action → Observation**, repeated. Reasoning traces and task actions generated in
45
+ an interleaved way, so the plan updates on what the world actually returned.
46
+
47
+ What it fixes: plain chain-of-thought is isolated from external information and therefore
48
+ suffers **fact hallucination and error propagation** — it reasons confidently past a wrong
49
+ premise. ReAct grounds each step in an observation.
50
+
51
+ **What it does not fix, and this is under-quoted:**
52
+
53
+ - It **constrains reasoning flexibility** compared with free-form CoT — the format itself is
54
+ a cost.
55
+ - **Non-informative results derail it.** A search returning nothing useful leaves the model
56
+ struggling to reformulate, and it will loop on near-identical queries. This is the failure
57
+ your iteration guard exists for, and it is why tool errors must teach (`tools.md`).
58
+ - It is strongest **combined** with CoT and self-consistency rather than alone — which is
59
+ the honest reading of the paper and rarely the reading in a blog post.
60
+
61
+ ## Reflection, in detail
62
+
63
+ Three roles, and naming them separates the ones people conflate:
64
+
65
+ | Role | Does | In practice |
66
+ |---|---|---|
67
+ | **Actor** | generates text and actions, produces a trajectory | your existing loop |
68
+ | **Evaluator** | scores the trajectory | **the part that decides whether this works at all** |
69
+ | **Self-reflection** | turns the score into verbal guidance stored for next time | an extra call, plus memory |
70
+
71
+ **The evaluator is the whole question.** Where the signal is objective and cheap — the test
72
+ suite ran, the SQL executed, the JSON validated, the build passed — reflection is one of the
73
+ strongest available techniques. Where the evaluator is the same model judging its own
74
+ output with no ground truth, you have added cost and a confident second opinion.
75
+
76
+ Stated limitations worth carrying: it depends on **accurate self-evaluation**, its memory is
77
+ typically a **sliding window**, and it struggles where correctness is non-deterministic.
78
+
79
+ ## Voting and self-consistency
80
+
81
+ Sample the same task several times and take the majority. It genuinely reduces variance —
82
+ and it multiplies cost and latency by N, needs a discrete answer to vote on, and does
83
+ nothing for a long trajectory where the runs diverge at step three.
84
+
85
+ Reach for it on a **single high-stakes decision** — a routing classification, a safety
86
+ judgement, an extraction that everything downstream depends on. Not on a whole agent run.
87
+
88
+ ## What reasoning models made redundant
89
+
90
+ A live shift, and it invalidates a lot of otherwise-good advice:
91
+
92
+ - **Do not instruct step-by-step thinking.** It is native, and explicit CoT can hurt
93
+ instruction-following.
94
+ - **Give high-level goals rather than procedures**, and let planning happen inside the model.
95
+ - **Reasoning effort** is now a per-call dial, which is a cheaper knob than most of the
96
+ techniques above.
97
+ - But **tool-calling stays weaker** in most reasoning models — so the common production shape
98
+ is a reasoning model that plans and a different model that executes tools. That is
99
+ *separation of concerns*, and it also lets the cheap half be cheap.
100
+
101
+ ## Choosing one
102
+
103
+ Ask in this order, and stop at the first yes:
104
+
105
+ 1. **Are the steps knowable?** → prompt chaining, not an agent.
106
+ 2. **Is there a cheap objective signal?** → evaluator–optimizer or reflection.
107
+ 3. **Does the sub-task need its own window?** → a sub-agent returning a distilled summary.
108
+ 4. **Is one decision disproportionately expensive to get wrong?** → voting, on that decision only.
109
+ 5. **Otherwise** → a plain ReAct loop with a bounded iteration guard, and spend the effort on
110
+ the tools and the prompt instead. That is where the measured gains are.
111
+
112
+ **The anti-pattern this section exists to prevent:** adopting a technique because it appears
113
+ in a paper, without naming the signal it consumes or the cost it adds. If you cannot say
114
+ what the evaluator measures, you are not doing reflection — you are paying for a second
115
+ opinion from the same source.
@@ -0,0 +1,154 @@
1
+ # Tools — the agent–computer interface
2
+
3
+ **Load this when:** the model picks the wrong tool, calls none, calls one with bad arguments,
4
+ or you are deciding what to expose in the first place.
5
+
6
+ **Spec pinned:** Anthropic *Writing tools for agents* and *Building effective agents*; `promptingguide.ai` agents/function-calling · read 2026-08-14
7
+
8
+ Anthropic's framing is worth adopting whole: this is the **agent–computer interface**, and it
9
+ deserves the same craft a human interface gets. Most teams spend their effort on the model
10
+ and none on the ACI, then conclude the model is bad at tool use.
11
+
12
+ ## Contents
13
+
14
+ - Fewer tools than you think
15
+ - Namespacing
16
+ - The description is the product
17
+ - Return meaning, not identifiers
18
+ - Token efficiency is a correctness issue
19
+ - Errors that teach
20
+ - Poka-yoke: make the wrong call impossible
21
+ - Evaluating tools
22
+ - Traps
23
+
24
+ ## Fewer tools than you think
25
+
26
+ **More tools do not lead to better outcomes.** The reflex — wrap every API endpoint, ship
27
+ forty tools, let the model choose — produces an agent that chooses badly, because selection
28
+ degrades with the size of the set and every definition costs context.
29
+
30
+ Build **a few thoughtful tools targeting specific high-impact workflows.** The test:
31
+ consolidate where a human would. `search_and_summarize` beats `search` + `fetch` +
32
+ `summarize` when the three are always used together, because it removes two decisions and
33
+ two round trips.
34
+
35
+ **The honest check, and it is brutal:** *if your engineers cannot definitively say which
36
+ tool applies to a case, the model cannot either.* Ambiguity between two tools is a design
37
+ defect, not a prompting problem.
38
+
39
+ ## Namespacing
40
+
41
+ Group related tools under a common prefix — `asana_search`, `asana_create_task`,
42
+ `jira_search`. Boundaries become visible, and the model stops crossing services by accident.
43
+
44
+ This also matters at the federation layer: behind a gateway, tool names commonly gain a
45
+ prefix from their source server, and a name that changes between sessions invalidates every
46
+ prompt and eval that referenced it. See `agent-interop/references/gateway.md`.
47
+
48
+ ## The description is the product
49
+
50
+ **Even small refinements to tool descriptions yield dramatic improvements.** The rule that
51
+ makes them good: **write as if explaining to a new team member**, and make implicit context
52
+ explicit.
53
+
54
+ A description must answer **when and why**, not only what:
55
+
56
+ ```jsonc
57
+ // weak — restates the name, and says nothing about choosing it
58
+ { "name": "search_users", "description": "Searches for users." }
59
+
60
+ // strong — the model can now decide
61
+ { "name": "search_users",
62
+ "description": "Find users by name, email or team. Use this before any operation \
63
+ that needs a user ID — IDs are never guessable. Returns at most 20 matches; narrow \
64
+ with `team` rather than paging when you can. Do NOT use for the current user: \
65
+ `get_current_user` is cheaper and always correct." }
66
+ ```
67
+
68
+ Note what the strong version carries: **when to reach for it, what it costs, how to narrow,
69
+ and the neighbouring tool it is confused with.** That last clause is the highest-value
70
+ sentence in most tool descriptions and almost nobody writes it.
71
+
72
+ **Parameters carry their own guidance.** Use `enum` to constrain values rather than
73
+ describing the constraint in prose, give examples in parameter descriptions, and mark
74
+ required versus optional honestly — an optional parameter the tool actually needs is a
75
+ silent failure.
76
+
77
+ ## Return meaning, not identifiers
78
+
79
+ Prioritise **contextual relevance over flexibility**. A response of
80
+ `{"id": "u_8f3a", "gid": "1209...", "rid": 44}` gives the model nothing to reason with; it
81
+ will echo identifiers into prose and hallucinate what they mean. Return
82
+ `{"name": "Ada Lovelace", "team": "Platform", "id": "u_8f3a"}` — the id stays for the next
83
+ call, the meaning arrives for the reasoning.
84
+
85
+ ## Token efficiency is a correctness issue
86
+
87
+ Not merely a cost issue: a tool that returns 40,000 tokens of JSON has consumed the window
88
+ the agent needed to finish the task, and no amount of history trimming recovers it (see
89
+ `agent-orchestrator/references/context-engineering.md` → *tool-output offload*).
90
+
91
+ Build in **pagination, range selection, filtering and truncation — with sensible defaults**.
92
+ The default matters more than the capability: an agent will rarely opt into a limit it was
93
+ not given.
94
+
95
+ ## Errors that teach
96
+
97
+ A tool error is a turn in a conversation. Compare:
98
+
99
+ ```
100
+ Error: 422 Unprocessable Entity
101
+ ```
102
+ ```
103
+ Error: `due_date` must be ISO-8601 (e.g. 2026-08-14). You sent "next friday".
104
+ Call `resolve_date` first, or pass an absolute date.
105
+ ```
106
+
107
+ The second costs nothing extra and converts a dead end into a recovery. **Return informative
108
+ messages that help the agent recover or try an alternative** — naming the alternative is the
109
+ part that gets skipped.
110
+
111
+ Two structural notes: under MCP a failed tool arrives as a *successful* response carrying
112
+ `isError: true`, so code that only catches transport exceptions treats every tool failure as
113
+ a success containing an apology (`agent-interop/references/mcp.md`). And in code-mode
114
+ harnesses, generated wrappers should convert that into a thrown exception so model-authored
115
+ code can `try`/`catch`.
116
+
117
+ ## Poka-yoke: make the wrong call impossible
118
+
119
+ Borrowed from manufacturing, and the highest-leverage idea here: **change the interface so
120
+ the mistake cannot be made**, rather than documenting the mistake.
121
+
122
+ - Absolute paths instead of relative ones, when relative paths get resolved against a
123
+ directory the agent guessed.
124
+ - An `enum` instead of a free-text field with a list of valid values in the description.
125
+ - One tool that does the two-step correctly instead of two tools that must be ordered.
126
+ - A required `confirm: true` on a destructive action, so a partially-formed call fails
127
+ closed.
128
+
129
+ ## Evaluating tools
130
+
131
+ Tools deserve **thorough documentation and testing**, and testing means running the agent
132
+ against real tasks and reading which tool it picked, with what arguments, and what came
133
+ back. Enable intermediate-step visibility and look for the three recurring faults:
134
+
135
+ | Symptom | Almost always |
136
+ |---|---|
137
+ | wrong tool chosen | two descriptions do not distinguish themselves; add the "do NOT use for…" clause |
138
+ | bad arguments | the parameter description assumes context the model does not have, or the type is too loose |
139
+ | result misread | the response returned identifiers, or too much, or both |
140
+
141
+ Fix the interface, not the prompt, when the fault is in this table.
142
+
143
+ ## Traps
144
+
145
+ - **Wrapping the API you have** instead of designing the tools the agent needs.
146
+ - **A description written for a human reading docs** rather than a model choosing under
147
+ uncertainty.
148
+ - **Unbounded responses** with an optional `limit` nobody sets.
149
+ - **Errors that are true and useless.** `null is not an object` names the symptom the model
150
+ can do nothing with.
151
+ - **Treating tool output as trusted.** It is attacker-controlled input if the server is; the
152
+ specification says descriptions and annotations are untrusted unless the server is.
153
+ - **Adding a tool to fix a prompt problem.** The set grows, selection degrades, and the
154
+ original defect is still there.
@@ -0,0 +1,299 @@
1
+ #!/usr/bin/env python3
2
+ """Mechanical half of an agent-system audit.
3
+
4
+ Finds only what is visible WITHOUT understanding intent, and prints what it cannot see —
5
+ because a scanner that goes quiet is reporting its own blindness, and an audit that stops
6
+ at a silent scanner has audited the scanner.
7
+
8
+ python3 audit_agent.py <path> human-readable
9
+ python3 audit_agent.py <path> --json machine-readable
10
+ python3 audit_agent.py --self-test plant each defect, require each to be found
11
+
12
+ Zero dependencies. Python 3.9+.
13
+
14
+ Design rule, and the reason this file is short: every detector is CONSERVATIVE. A false
15
+ positive costs more than a miss here, because an audit report that cries wolf is discarded
16
+ whole — and the seven tracks in `references/audit.md` cover by hand everything this cannot
17
+ reach. Detectors therefore require corroboration (the file must look agent-related) and
18
+ each finding carries `file:line` so a human can disagree with it in one click.
19
+ """
20
+
21
+ import argparse
22
+ import json
23
+ import os
24
+ import re
25
+ import sys
26
+
27
+ # A file is "agent-related" only if it shows two independent signs. One is a coincidence:
28
+ # plenty of code says "message" or "prompt" without being an agent loop.
29
+ AGENTISH = [
30
+ re.compile(r"\btool[_ ]?call", re.I),
31
+ re.compile(r"\btools\s*=|\"tools\"\s*:|'tools'\s*:"),
32
+ re.compile(r"\bsystem[_ ]?prompt", re.I),
33
+ re.compile(r"\b(anthropic|openai|litellm|langchain|langgraph|bedrock|mistral)\b", re.I),
34
+ re.compile(r"\bfunction[_ ]?call", re.I),
35
+ re.compile(r"\bmessages\s*=\s*\[|\"messages\"\s*:"),
36
+ ]
37
+ CODE_EXT = {".py", ".js", ".mjs", ".cjs", ".ts", ".tsx", ".jsx"}
38
+ SKIP_DIRS = {".git", "node_modules", "venv", ".venv", "__pycache__", "dist", "build",
39
+ ".next", "target", "vendor", ".tox", "site-packages"}
40
+ MAX_BYTES = 400_000 # a generated bundle is not worth reading, and skews everything
41
+
42
+ # Model ids that are usually hardcoded by accident. Deliberately not exhaustive: this is a
43
+ # smell detector, and the finding says "pin it deliberately", not "this id is wrong".
44
+ MODEL_LITERAL = re.compile(
45
+ r"[\"']((?:claude|gpt|gemini|llama|mistral|deepseek|qwen)[-\w.]*\d[\w.-]*)[\"']", re.I)
46
+
47
+ FINDINGS = []
48
+
49
+
50
+ def add(kind, path, line, detail, fix):
51
+ FINDINGS.append({"check": kind, "file": path, "line": line, "detail": detail, "fix": fix})
52
+
53
+
54
+ def agentish(text):
55
+ return sum(1 for p in AGENTISH if p.search(text)) >= 2
56
+
57
+
58
+ def walk(root):
59
+ for dirpath, dirnames, filenames in os.walk(root):
60
+ # Skip a virtualenv by its MARKER, not by its name. `venv`/`.venv` in SKIP_DIRS
61
+ # only catches the conventional names; a real repository met during testing used
62
+ # `myenv/`, holding 4249 of its 4261 code files, and was excluded only because
63
+ # `site-packages` happened to be listed too. Right by accident is not right.
64
+ dirnames[:] = [d for d in dirnames
65
+ if d not in SKIP_DIRS and not d.startswith(".")
66
+ and not os.path.exists(os.path.join(dirpath, d, "pyvenv.cfg"))]
67
+ for fn in filenames:
68
+ if os.path.splitext(fn)[1] not in CODE_EXT:
69
+ continue
70
+ full = os.path.join(dirpath, fn)
71
+ try:
72
+ if os.path.getsize(full) > MAX_BYTES:
73
+ continue
74
+ with open(full, encoding="utf-8", errors="replace") as fh:
75
+ text = fh.read()
76
+ except OSError:
77
+ continue
78
+ yield os.path.relpath(full, root), text
79
+
80
+
81
+ # ------------------------------------------------------------------ detectors
82
+
83
+ def check_unbounded_loop(rel, text, lines):
84
+ """`while True` in an agent file with no visible iteration bound.
85
+
86
+ Conservative twice over: the file must be agent-related, AND the file must not mention
87
+ any bound at all. A loop with `max_iter` somewhere else in the file is left alone.
88
+ """
89
+ if re.search(r"max[_ ]?iter|max[_ ]?steps|max[_ ]?turns|iteration_limit|for\s+\w+\s+in\s+range\(",
90
+ text, re.I):
91
+ return
92
+ for i, l in enumerate(lines, 1):
93
+ if re.search(r"^\s*while\s+(True|true|1)\s*[:)]|^\s*while\s*\(\s*true\s*\)", l):
94
+ add("unbounded-loop", rel, i,
95
+ "`while True` in an agent file with no iteration bound anywhere in it",
96
+ "Add a max-iteration guard that composes a partial answer at the bound, "
97
+ "rather than returning nothing")
98
+
99
+
100
+ def check_tool_without_description(rel, text, lines):
101
+ """A tool declared with an empty or missing description.
102
+
103
+ Only fires on an explicit empty string — a missing key is too easy to get wrong across
104
+ frameworks, and a wrong finding here is worse than a missed one.
105
+ """
106
+ for i, l in enumerate(lines, 1):
107
+ if re.search(r"[\"']description[\"']\s*:\s*[\"']\s*[\"']", l) or \
108
+ re.search(r"\bdescription\s*=\s*[\"']\s*[\"']", l):
109
+ add("tool-no-description", rel, i,
110
+ "a tool description is the empty string",
111
+ "Describe WHEN and WHY to use it, and name the neighbouring tool it is "
112
+ "confused with — the highest-leverage sentence in a tool definition")
113
+
114
+
115
+ def check_swallowed_error(rel, text, lines):
116
+ """An exception caught and discarded inside an agent file."""
117
+ for i, l in enumerate(lines, 1):
118
+ nxt = lines[i] if i < len(lines) else ""
119
+ if re.search(r"^\s*except[^\n]*:\s*$", l) and re.search(r"^\s*pass\s*$", nxt):
120
+ add("swallowed-error", rel, i,
121
+ "`except: pass` — the failure is invisible to the loop and to the model",
122
+ "Return an error the agent can act on; a tool error is a turn in the "
123
+ "conversation, not a silence")
124
+ if re.search(r"catch\s*\([^)]*\)\s*\{\s*\}", l):
125
+ add("swallowed-error", rel, i,
126
+ "empty `catch` block — the failure is discarded",
127
+ "Surface it to the loop; an error that teaches the next attempt costs "
128
+ "nothing extra")
129
+
130
+
131
+ def check_no_timeout(rel, text, lines):
132
+ """An outbound HTTP call with no timeout, in an agent file."""
133
+ for i, l in enumerate(lines, 1):
134
+ if re.search(r"\brequests\.(get|post|put|patch|delete)\s*\(", l) and "timeout" not in l:
135
+ add("no-timeout", rel, i,
136
+ "`requests` call with no `timeout=` — a hung provider hangs the agent",
137
+ "Set an explicit timeout on every external call, and decide what the loop "
138
+ "does when it fires")
139
+ if re.search(r"\burllib\.request\.urlopen\s*\(", l) and "timeout" not in l:
140
+ add("no-timeout", rel, i, "`urlopen` with no `timeout=`",
141
+ "Set an explicit timeout on every external call")
142
+
143
+
144
+ def check_hardcoded_model(rel, text, lines):
145
+ """A model id as a literal, in more than one place — the smell is duplication."""
146
+ hits = []
147
+ for i, l in enumerate(lines, 1):
148
+ if l.lstrip().startswith(("#", "//", "*")):
149
+ continue
150
+ m = MODEL_LITERAL.search(l)
151
+ if m:
152
+ hits.append((i, m.group(1)))
153
+ if len(hits) >= 2:
154
+ i, name = hits[0]
155
+ add("hardcoded-model", rel, i,
156
+ f"model id {name!r} appears as a literal {len(hits)}× in this file",
157
+ "Resolve the model from configuration at one boundary; three levels — request, "
158
+ "tenant, system default — is the shape that bills correctly")
159
+
160
+
161
+ CHECKS = [check_unbounded_loop, check_tool_without_description, check_swallowed_error,
162
+ check_no_timeout, check_hardcoded_model]
163
+
164
+ # What no static pass can reach. Printed every run, never suppressed.
165
+ BLIND = [
166
+ "whether the SYSTEM PROMPT is at the right altitude — or whether it is in this repo at all",
167
+ "whether two tool descriptions actually distinguish themselves to a model",
168
+ "whether the workflow/agent choice was made deliberately or defaulted to an agent",
169
+ "whether retries and fallbacks MULTIPLY (three providers x three retries is nine calls)",
170
+ "whether compaction preserves decisions and open questions, or keeps the discussion",
171
+ "whether tool output is treated as untrusted input",
172
+ "whether evals exist, judge trajectories, and are calibrated",
173
+ "whether an audit row could prove a control was applied (policy version)",
174
+ "everything in a language this pass does not read, and everything in configuration",
175
+ ]
176
+
177
+
178
+ def scan(root):
179
+ seen_files = considered = 0
180
+ for rel, text in walk(root):
181
+ considered += 1
182
+ if not agentish(text):
183
+ continue
184
+ seen_files += 1
185
+ lines = text.splitlines()
186
+ for c in CHECKS:
187
+ c(rel, text, lines)
188
+ return seen_files, considered
189
+
190
+
191
+ def report_text(root, seen, considered):
192
+ # The denominator is not decoration. "read: 1" alone looks like a broken pass; "1 of
193
+ # 4261" says the repository is mostly not an agent, which is a finding in itself when
194
+ # somebody called it one.
195
+ out = [f"agent-audit: {root}",
196
+ f" code files considered: {considered}",
197
+ f" of those, agent-related: {seen}"]
198
+ if not seen:
199
+ out.append(" NOTHING READ — no file showed two independent signs of an agent loop.")
200
+ out.append(" That is a fact about this pass, not about the system. Check the path,")
201
+ out.append(" and whether the agent lives in a language or a config this cannot read.")
202
+ out.append("")
203
+ if FINDINGS:
204
+ out.append(f"FINDINGS ({len(FINDINGS)}) — each is a smell with a location, not a verdict:")
205
+ for f in FINDINGS:
206
+ out.append(f" {f['file']}:{f['line']} [{f['check']}]")
207
+ out.append(f" {f['detail']}")
208
+ out.append(f" fix: {f['fix']}")
209
+ else:
210
+ out.append("FINDINGS (0) — nothing mechanically visible.")
211
+ out.append("")
212
+ out.append("THIS PASS CANNOT SEE — the manual half of the audit, and it is the larger half:")
213
+ for b in BLIND:
214
+ out.append(f" - {b}")
215
+ out.append("")
216
+ out.append("Walk the seven tracks in references/audit.md. Silence above is not a pass.")
217
+ return "\n".join(out)
218
+
219
+
220
+ def self_test():
221
+ """Plant each defect and require the matching check to fire.
222
+
223
+ A detector nobody has watched fire is not evidence that it works, and every plant
224
+ asserts it changed something so a reworded fixture fails HERE rather than reporting a
225
+ healthy checker as broken.
226
+ """
227
+ import tempfile
228
+ header = ("import requests\n"
229
+ "system_prompt = 'x'\n"
230
+ "tools = [{'name': 't', 'description': 'does a thing'}]\n"
231
+ "messages = []\n")
232
+ cases = {
233
+ "unbounded-loop": header + "while True:\n pass\n",
234
+ "tool-no-description": header + "T = [{'name': 'a', 'description': ''}]\n",
235
+ "swallowed-error": header + "try:\n x = 1\nexcept Exception:\n pass\n",
236
+ "no-timeout": header + "r = requests.get('https://example.com')\n",
237
+ "hardcoded-model": header + "a = 'claude-opus-4'\nb = 'claude-opus-4'\n",
238
+ }
239
+ failures = 0
240
+ for kind, body in cases.items():
241
+ FINDINGS.clear()
242
+ with tempfile.TemporaryDirectory() as d:
243
+ p = os.path.join(d, "agent.py")
244
+ with open(p, "w", encoding="utf-8") as fh:
245
+ fh.write(body)
246
+ assert agentish(body), f"PLANT DID NOT LAND: fixture for {kind} is not agent-related"
247
+ scan(d)
248
+ got = {f["check"] for f in FINDINGS}
249
+ if kind in got:
250
+ print(f" OK {kind}: detected")
251
+ else:
252
+ print(f" FAIL {kind}: NOT detected (found {sorted(got) or 'nothing'})")
253
+ failures += 1
254
+ # and a clean file must produce nothing, or every finding above is noise
255
+ FINDINGS.clear()
256
+ with tempfile.TemporaryDirectory() as d:
257
+ with open(os.path.join(d, "agent.py"), "w", encoding="utf-8") as fh:
258
+ fh.write(header + "for _ in range(10):\n pass\n"
259
+ "r = requests.get('https://example.com', timeout=5)\n")
260
+ scan(d)
261
+ if FINDINGS:
262
+ print(f" FAIL clean file produced {len(FINDINGS)} finding(s): "
263
+ f"{[f['check'] for f in FINDINGS]}")
264
+ failures += 1
265
+ else:
266
+ print(" OK clean file: silent")
267
+ print(f"\nself-test: {len(cases) + 1 - failures}/{len(cases) + 1} passed")
268
+ return 1 if failures else 0
269
+
270
+
271
+ def main(argv):
272
+ ap = argparse.ArgumentParser(description="Mechanical half of an agent-system audit.")
273
+ ap.add_argument("path", nargs="?", default=".")
274
+ ap.add_argument("--json", action="store_true")
275
+ ap.add_argument("--self-test", action="store_true")
276
+ a = ap.parse_args(argv)
277
+
278
+ if a.self_test:
279
+ return self_test()
280
+
281
+ root = os.path.abspath(a.path)
282
+ if not os.path.isdir(root):
283
+ print(f"error: {a.path} is not a directory", file=sys.stderr)
284
+ return 2
285
+ seen, considered = scan(root)
286
+ if a.json:
287
+ print(json.dumps({"root": root, "files_considered": considered,
288
+ "files_agent_related": seen, "findings": FINDINGS,
289
+ "cannot_see": BLIND}, indent=2))
290
+ else:
291
+ print(report_text(root, seen, considered))
292
+ # Findings are smells, not failures: exit 0 so this composes in a pipeline, and let the
293
+ # human decide. A non-zero exit here would turn an audit into a gate it was never
294
+ # calibrated to be.
295
+ return 0
296
+
297
+
298
+ if __name__ == "__main__":
299
+ sys.exit(main(sys.argv[1:]))