Capability: tools (full control)
This capability treats the agent's entire tool surface as the optimizable
artifact. It applies when the agent owns its tools — it implements the handlers,
defines the wire schema, and controls every caller — so names, descriptions,
parameter docs, in-description examples, the JSON Schema, and the implementation
code are all fair game. (When the tools come from an external server you can only
re-describe, not re-implement: that is mcp-tool, whose policy is tightened to
documentation-only edits.)
What you can change here
The tool's documentation AND its return value are what the agent SEES — make both
clear and recovery-oriented. The doc surface (description, important-notes,
per-param, error/Raises text, examples) drives which tool the model calls and
how it fills the arguments; the return value (and especially the error text) steers
the next turn. Confirm which parts of a docstring your runtime actually SENDS before
writing into it — some frameworks discard whole sections, and guidance written into
that void does nothing (references/field-notes.md §1).
Ship MULTIPLE fixes per iteration — but every one must be REAL (targets a
currently-failing task), SAFE (cannot change a passing task's behavior), and VERIFIED
(proven to fix its target). Several such fixes beat a long list that includes a
speculative edit: one edit that regresses a passing task sinks the whole candidate at
the val gate. Never add an edit to hit a count, and never re-add a rule or tool the run
already tried and rejected.
Per-change SAFETY (the rule that makes multi-change work). Scope every guard to
fire ONLY on the exact violating condition, and check its blast radius: run it on the
args of 1–2 currently-PASSING tasks that use the same tool and confirm it does NOT
fire. A guard that fires on a passing task is a regression — rescope or drop it.
Pick the lever by failure type
Each item is an edit class. In ONE pass, apply EVERY class the traces call for — a
validation wrapper AND a loop tool AND enriched returns/errors AND doc fixes across all
implicated tools can and should ship in the same candidate. The in-body guard is the
default strong move; reach for a documentation edit only after asking "can this rule be
code in the existing body instead?"
- Edit the CODE of an EXISTING tool (reach for this FIRST for a rule violation).
Most violated textual rules govern a tool that ALREADY exists, and the fix is an
in-body guard there, not a new tool: bake the precondition, normalization, or
actionable refusal into the body so correctness does not depend on the LLM. Ex:
add
if not rec["cancellable"]: raise ValueError("not cancellable; reason=...; do X instead") to the existing cancel_record body. Expect to touch the BODIES of
SEVERAL existing tools per iteration — one per violated rule. A deterministic guard
beats a sentence in a prompt: prose makes the model more likely to comply, code
makes the right behavior the only thing that can happen.
- Add a composite atomic-WRITE tool — for a stalled or abandoned multi-step action,
encapsulate the ENTIRE action in one tool whose body performs all the steps in order
via the existing primitives, then
remove the raw primitives so the action is
un-skippable. Ex: apply_change_plan(record_id, steps) validates → applies each →
returns final state as one call. Reach for this even though a write primitive already
exists: the primitive is exactly what the agent declines to call.
- Add a discriminating-predicate guard for an ACT-vs-REFUSE cluster — an in-body
guard on the tool that owns the action, expressing the EXACT policy predicate, that
refuses only when the qualifying condition is (or is not) met. It is the narrowest
edit available here and the tool-side alternative to changing a global rule.
- Add a real targeted tool for a capability gap — a task that needs a compute /
composite / predicate tool it does not have stays failing after any docstring reword.
Ship a tool the agent will CALL that changes the graded state — Ex: a
find_duplicate_records it has no way to compute today, or a search_logs that
returns the relevant lines instead of a raw dump.
- Add a loop tool — replace N repeated single-item calls with one list call. Ex:
get_records(ids: [...]) replaces N× get_record(id).
- Replace / wrap a tool — superset an existing tool and route the old behavior
through it. Ex: wrap
find_record+charge_payment behind one
charge_record(record_id) that resolves then charges.
- Improve a tool's documentation — sharpen description / important-notes / error
conditions / per-param docs / examples; rename for least surprise. Ex:
lookup(record) → get_record(record_id: str) with "returns an error object if not
found."
- Improve RETURN VALUES for recoverability — high-signal fields, stable
human-readable ids, and actionable error text with a next-step hint and what NOT to
do. Ex: an error returning "payment method not on file; available: ['card_1'] —
pass one of these" instead of a raw traceback. Adding to a return is not free,
though: it is re-read every turn, so treat enrichment as a hypothesis to gate, not a
free win (
references/field-notes.md §2).
- Remove-with-replacement — remove a redundant/overlapping tool only after a
replacement preserving its capability exists. Ex: drop
query once get_record +
search_records cover it.
The two ways to waste an iteration: leaving a rule the agent keeps breaking as loose
prose instead of a guard (or loosening a global permission rule instead of scoping a
guard); and padding the candidate with low-value helper tools or cosmetic rewrites that
move no graded task.
Guardrails
- Encode deterministic logic in code, not prose — a tool body the model cannot skip
beats a sentence it can forget. A tool whose body enforces nothing (a
think() /
check_policy() passthrough with the rule only in its docstring) is prose in a tool's
costume; reach for it only when the behavior genuinely cannot be made deterministic.
- You must write the BODY. A
compose/add/code edit whose body is ..., a bare
pass, or docstring-only is not this edit — it does nothing. Emit the real loop, the
real precondition check, the real calls to existing tools (get_record(i) — or
self.get_record(i) if your adapter binds tools as methods).
- Never remove a tool without a capability-preserving replacement. Add → verify →
swap (
references/concepts.md §8). Bare-removing strands every task that needed it;
adding a wrapper but leaving the primitive exposed lets the model route around the
guard and reproduce the original failure.
- Keep the toolset small and namespaced — aim for < ~20 active tools; selection
degrades sharply past that. Prefer consolidating over piling on: when you add a safer
or looped tool,
remove the now-redundant primitive.
- Ship correct, bug-free code — every code edit needs validation plus a
validate
run, and proof the toolset still registers (an import check is not a registration
check — references/field-notes.md §3).
- Generalize, never hardcode. Every guard must fire on the GENERAL condition that
defines the failure class, never on a literal value from one task. Good:
if payment_id not in user_payment_methods: raise .... Bad: if record_id == "<TASK_SPECIFIC_ID>": raise ... — that overfits, gets rejected by the held-out gate,
and helps nothing else. Use a failing task's specifics only to identify the class,
then write the general check. The test for any edit: would this help on a task the
optimizer has never seen?
How agents fail (and how tools fix it)
Map the trace symptom to the edit. This table is the single canonical statement of what
to ship for what failure. The rows are independent: fix as MANY of them as appear in the
trajectories in one candidate, not just the first — each guarded tool is its own bounded
fix. Verify the fix you ship actually FIRES on the failing trace (run the new body on
the exact arguments from that trajectory; a guard that never triggers on the failing
task is dead code, not a fix).
| Trace symptom |
Fix |
| Wrong ARGUMENT the tool could validate — a write whose id / reference / count / unit is not consistent with the agent-visible state. Right tool, bad argument; partial credit or a corrupted write. |
Normalize-then-call wrapper: wrap the write in a body that RESOLVES / VALIDATES the argument against current state, and on mismatch returns available=[...] or raises an actionable error naming what is wrong and what to pass instead. Never let a write proceed on an unvalidated reference. |
| The action never happens — the agent analyzes, explains, even confirms, then never calls the write tool and stops; or it hands off / gives up on an action it could have completed. Task left half-done. |
Composite WRITE tool (lever 2): one tool whose body performs the whole sequence — or the whole eligible-action batch, skipping any ineligible item with a recorded reason — then remove the raw primitives so completing it is the only path. Not a "be sure to act" prose rule. |
narrated_without_action — the strong form of the row above, and diagnose names it as its own cluster: the final message REPORTS the change as done, with specifics, and the trace holds no mutating call at all. The agent took the user's confirmation as its completion signal. |
Make "confirmed" and "executed" ONE call (lever 2, structurally): a single write whose body performs the approved change, sharing the code path the confirmation handling already uses, then remove the primitives that let the two come apart. A prose reminder to call the tool has been tried on this class and rejected — the agent knows the rule and violates it. |
| Recoverable error that strands the agent — a tool raises an opaque traceback / bare code; the agent retries the same bad call or gives up. |
Enriched RETURN that aids recovery: on a recoverable error return what is wrong + the valid options + the recommended next action ({"error": "id not found", "available": [...], "next": "call search_x to resolve the id"}) so the model self-corrects next turn. |
| The same primitive called N times in a row — looping over a list in the agent's own context, burning turns and dropping or mis-threading results. |
Loop tool (lever 5): one tool that takes the list and loops inside a single call. |
| A rule stated in the prompt but repeatedly violated — a required order ("read before write"), a precondition the API does not enforce, a normalization the model forgets. |
In-body guard / validation wrapper (lever 1): enforce the rule in the body of the tool that owns it; remove the unguarded primitive if the safe path must be the only one. |
| A wrong ACT-vs-REFUSE call — the agent acts where policy says refuse/escalate, or refuses where it should act. |
Discriminating-predicate guard (lever 3) on the tool that owns the action. |
| Mis-selection — the agent calls the wrong tool, calls none when one applied, or invents a tool that does not exist. |
Name + description fix: selection is driven almost entirely by the name and description — sharpen what/when/when-not and the boundary against the nearest sibling (references/doc-contract.md). |
| Bad argument-filling — right tool, wrong arguments: a missing required field, the wrong enum value, free text where a structured object was expected. |
Schema + per-parameter docs: close the value set with an enum, pin units/format/default per parameter, add an in-description example. |
| A bloated or overlapping toolset — too many tools, or several that do nearly the same thing, distracting the agent. |
Consolidate then remove the originals (lever 9). Remove for overlap/confusion, not for low call-count. |
| A bug in a handler — the tool returns the wrong thing. |
code edit: because you own the code, fix it directly. |
The throughline: a failure the agent knows better than but still commits is fixed by
removing the choice — putting the behavior in code and remove-ing the path that let it
go wrong.
If the problem is what the agent is told to do rather than what it can do, it
belongs to whatever capability edits the agent's instructions, not here.
What can be optimized (default policy = all of these)
| Action |
Changes |
Why it moves the metric |
description |
tool-level wording incl. in-desc examples |
the single biggest lever on selection |
params |
per-parameter descriptions / defaults |
drives correct argument-filling |
examples |
example call strings |
shows concrete well-formed calls |
schema |
the full JSON Schema (types, required, enum) |
constrains/guides the model's output |
code |
the handler body of an EXISTING tool |
the default high-leverage edit — convert a violated prose rule into an in-body guard; expect to edit SEVERAL bodies per iteration |
compose |
add a code-bearing tool that calls existing tools |
enforce a rule, collapse a multi-call chain, or perform a whole stalled WRITE action in code |
add / remove |
introduce / delete a tool |
shape and shrink the toolset (replace primitives; keep it lean) |
policy.json in the capability dir is the safety boundary between "reword the docs" and "rewrite the
program" — the same artifact is edited in very different trust settings, so tighten the
allowed set to match your deployment's blast radius (a frozen-API deployment might allow
only ["description", "params", "examples"]). apply() refuses anything outside the
allowed set and reports the refusal, so an over-tight policy surfaces as visible
refusals rather than silent no-ops.
Adapting to the runtime reader's capability tier
Scale the edit to WHO calls these tools at runtime (if your instructions state the
runtime reader's capability tier, use it). For a mid/weak reader, push harder on
this skill's already-preferred code enforcement (in-body guards, composite
atomic-write tools) — a weak reader skips a prose rule but a guard fires regardless —
and write literal, example-bearing per-parameter slot-filling docs on every tool
(exact format, units, and one concrete valid value, e.g. date: ISO-8601 "2026-07-20").
A weak reader mis-fills under-documented arguments far more often, so explicit parameter
docs and a smaller, less-confusable toolset are worth most there. For a frontier
reader, terser parameter docs and fewer worked examples suffice; spend the budget on
removing redundant tools instead.
Artifact + handlers
tools.json — a list of {name, description, parameters, examples, code?}.
scripts/abstract.py provides:
materialize(dir) — flatten the surface into named text components
(tool.<name>.description, .parameters, .examples) for a text optimizer.
apply(dir, edits) — policy-enforced edits incl. schema/code/compose;
returns {changed, refused}.
validate(dir) — schema well-formedness, empty-description and duplicate-name checks.
is_empty(dir) — whether the artifact is an empty seed (no tools yet).
How to run
python scripts/check.py
python scripts/run.py --path <capability_dir> # candidate + policy + validity
References
Each is standalone — read the one that matches what you are about to do.
references/examples.md — worked before/after edits with
full bodies, ordered by leverage: in-body guards (§0), description/schema fixes
(§1–§2), loop and composite/write tools (§3b–§3e), result shaping (§3f), the doc
contract in practice (§3g), removal and refusals (§4–§7). Load when you are about to
write an edit and want the exact JSON and a real body to model.
references/concepts.md — how an LLM turns tool definitions
into a call (select from name+description, fill from schema+examples), toolset-size
limits, response shaping, the safe replacement protocol, and the action-policy model,
with cited sources. Read once per project, before your first candidate.
references/doc-contract.md — the full documentation
contract for one tool: what/when/when-not, important-points, error conditions,
per-parameter units and formats, one always-valid example. Load when the fix is
documentation rather than code.
references/pitfalls.md — edits that look like improvements
and regress (stripped error conditions, cosmetic rewording, task-overfitted
descriptions, composite sprawl, example dumps, an exposed primitive behind a wrapper),
each with how to detect it. Read before shipping a docs-only candidate, and when
an accepted candidate barely moved the metric.
references/field-notes.md — observations from real runs:
how much of a docstring the runtime actually delivers, why enriching a return is not
free, a docstring header that broke tool registration, and a return shape that
corrupted the feedback signal. Read before your first candidate on a new harness,
and whenever an edit "verified" green without an explanation you can point at.
references/optimizer-playbook.md — what the
authored optimizer INSTRUCTIONS must demand when tools is selected: the
existing-tool-code mandate, the depth mandate's tools wording, and the two-phase
(diagnose fan-out → implement fan-out → merge) subagent pattern. Read when authoring
or reviewing those instructions (intake points here rather than inlining it).
1---2name: tools3description: Optimize an agent's OWN tool surface (tools it implements, not an external MCP server). Use when the agent mis-selects tools, fills arguments wrong, calls the same tool N times in a row, or has a confusing, redundant, or oversized toolset. Covers tool names and descriptions, parameter docs, tool schemas, handler code, function-calling accuracy, and adding or removing tools.4---56# Capability: tools (full control)78This capability treats the agent's **entire tool surface as the optimizable9artifact**. It applies when the agent *owns* its tools — it implements the handlers,10defines the wire schema, and controls every caller — so names, descriptions,11parameter docs, in-description examples, the JSON Schema, *and the implementation12code* are all fair game. (When the tools come from an external server you can only13re-describe, not re-implement: that is `mcp-tool`, whose policy is tightened to14documentation-only edits.)1516## What you can change here1718**The tool's documentation AND its return value are what the agent SEES — make both19clear and recovery-oriented.** The doc surface (description, important-notes,20per-param, error/`Raises` text, examples) drives *which* tool the model calls and21*how* it fills the arguments; the return value (and especially the error text) steers22the *next* turn. Confirm which parts of a docstring your runtime actually SENDS before23writing into it — some frameworks discard whole sections, and guidance written into24that void does nothing (`references/field-notes.md` §1).2526**Ship MULTIPLE fixes per iteration — but every one must be REAL (targets a27currently-failing task), SAFE (cannot change a passing task's behavior), and VERIFIED28(proven to fix its target).** Several such fixes beat a long list that includes a29speculative edit: one edit that regresses a passing task sinks the whole candidate at30the val gate. Never add an edit to hit a count, and never re-add a rule or tool the run31already tried and rejected.3233**Per-change SAFETY (the rule that makes multi-change work).** Scope every guard to34fire ONLY on the exact violating condition, and check its blast radius: run it on the35args of 1–2 currently-PASSING tasks that use the same tool and confirm it does NOT36fire. A guard that fires on a passing task is a regression — rescope or drop it.3738## Pick the lever by failure type3940Each item is an edit class. In ONE pass, apply EVERY class the traces call for — a41validation wrapper AND a loop tool AND enriched returns/errors AND doc fixes across all42implicated tools can and should ship in the same candidate. The in-body guard is the43default strong move; reach for a documentation edit only after asking "can this rule be44code in the existing body instead?"45461. **Edit the CODE of an EXISTING tool (reach for this FIRST for a rule violation).**47 Most violated textual rules govern a tool that ALREADY exists, and the fix is an48 in-body guard *there*, not a new tool: bake the precondition, normalization, or49 actionable refusal into the body so correctness does not depend on the LLM. *Ex:*50 add `if not rec["cancellable"]: raise ValueError("not cancellable; reason=...; do X51 instead")` to the existing `cancel_record` body. Expect to touch the BODIES of52 SEVERAL existing tools per iteration — one per violated rule. A deterministic guard53 beats a sentence in a prompt: prose makes the model *more likely* to comply, code54 makes the right behavior the only thing that can happen.552. **Add a composite atomic-WRITE tool** — for a stalled or abandoned multi-step action,56 encapsulate the ENTIRE action in one tool whose body performs all the steps in order57 via the existing primitives, then **`remove` the raw primitives** so the action is58 un-skippable. *Ex:* `apply_change_plan(record_id, steps)` validates → applies each →59 returns final state as one call. Reach for this even though a write primitive already60 exists: the primitive is exactly what the agent declines to call.613. **Add a discriminating-predicate guard** for an ACT-vs-REFUSE cluster — an in-body62 guard on the tool that owns the action, expressing the EXACT policy predicate, that63 refuses only when the qualifying condition is (or is not) met. It is the narrowest64 edit available here and the tool-side alternative to changing a global rule.654. **Add a real targeted tool for a capability gap** — a task that needs a compute /66 composite / predicate tool it does not have stays failing after any docstring reword.67 Ship a tool the agent will CALL that changes the graded state — *Ex:* a68 `find_duplicate_records` it has no way to compute today, or a `search_logs` that69 returns the relevant lines instead of a raw dump.705. **Add a loop tool** — replace N repeated single-item calls with one list call. *Ex:*71 `get_records(ids: [...])` replaces N× `get_record(id)`.726. **Replace / wrap a tool** — superset an existing tool and route the old behavior73 through it. *Ex:* wrap `find_record`+`charge_payment` behind one74 `charge_record(record_id)` that resolves then charges.757. **Improve a tool's documentation** — sharpen description / important-notes / error76 conditions / per-param docs / examples; rename for least surprise. *Ex:*77 `lookup(record)` → `get_record(record_id: str)` with "returns an error object if not78 found."798. **Improve RETURN VALUES for recoverability** — high-signal fields, stable80 human-readable ids, and **actionable error text with a next-step hint and what NOT to81 do**. *Ex:* an error returning "payment method not on file; available: ['card_1'] —82 pass one of these" instead of a raw traceback. Adding to a return is not free,83 though: it is re-read every turn, so treat enrichment as a hypothesis to gate, not a84 free win (`references/field-notes.md` §2).859. **Remove-with-replacement** — remove a redundant/overlapping tool *only* after a86 replacement preserving its capability exists. *Ex:* drop `query` once `get_record` +87 `search_records` cover it.8889The two ways to waste an iteration: leaving a rule the agent keeps breaking as loose90prose instead of a guard (or loosening a global permission rule instead of scoping a91guard); and padding the candidate with low-value helper tools or cosmetic rewrites that92move no graded task.9394## Guardrails9596- **Encode deterministic logic in code, not prose** — a tool body the model cannot skip97 beats a sentence it can forget. A tool whose body enforces nothing (a `think()` /98 `check_policy()` passthrough with the rule only in its docstring) is prose in a tool's99 costume; reach for it only when the behavior genuinely cannot be made deterministic.100- **You must write the BODY.** A `compose`/`add`/`code` edit whose body is `...`, a bare101 `pass`, or docstring-only is not this edit — it does nothing. Emit the real loop, the102 real precondition check, the real calls to existing tools (`get_record(i)` — or103 `self.get_record(i)` if your adapter binds tools as methods).104- **Never remove a tool without a capability-preserving replacement.** Add → verify →105 swap (`references/concepts.md` §8). Bare-removing strands every task that needed it;106 adding a wrapper but leaving the primitive exposed lets the model route around the107 guard and reproduce the original failure.108- **Keep the toolset small and namespaced** — aim for **< ~20** active tools; selection109 degrades sharply past that. Prefer consolidating over piling on: when you add a safer110 or looped tool, `remove` the now-redundant primitive.111- **Ship correct, bug-free code** — every code edit needs validation plus a `validate`112 run, and proof the toolset still *registers* (an import check is not a registration113 check — `references/field-notes.md` §3).114- **Generalize, never hardcode.** Every guard must fire on the GENERAL condition that115 defines the failure class, never on a literal value from one task. *Good:* `if116 payment_id not in user_payment_methods: raise ...`. *Bad:* `if record_id ==117 "<TASK_SPECIFIC_ID>": raise ...` — that overfits, gets rejected by the held-out gate,118 and helps nothing else. Use a failing task's specifics only to identify the class,119 then write the general check. The test for any edit: *would this help on a task the120 optimizer has never seen?*121122## How agents fail (and how tools fix it)123124Map the trace symptom to the edit. This table is the single canonical statement of what125to ship for what failure. The rows are independent: fix as MANY of them as appear in the126trajectories in one candidate, not just the first — each guarded tool is its own bounded127fix. Verify the fix you ship actually FIRES on the failing trace (run the new body on128the exact arguments from that trajectory; a guard that never triggers on the failing129task is dead code, not a fix).130131| Trace symptom | Fix |132|---------------|-----|133| **Wrong ARGUMENT the tool could validate** — a write whose id / reference / count / unit is not consistent with the agent-visible state. Right tool, bad argument; partial credit or a corrupted write. | **Normalize-then-call wrapper**: wrap the write in a body that RESOLVES / VALIDATES the argument against current state, and on mismatch returns `available=[...]` or raises an actionable error naming what is wrong and what to pass instead. Never let a write proceed on an unvalidated reference. |134| **The action never happens** — the agent analyzes, explains, even confirms, then never calls the write tool and stops; or it hands off / gives up on an action it could have completed. Task left half-done. | **Composite WRITE tool** (lever 2): one tool whose body performs the whole sequence — or the whole eligible-action batch, skipping any ineligible item with a recorded reason — then `remove` the raw primitives so completing it is the only path. Not a "be sure to act" prose rule. |135| **`narrated_without_action`** — the strong form of the row above, and `diagnose` names it as its own cluster: the final message REPORTS the change as done, with specifics, and the trace holds no mutating call at all. The agent took the user's confirmation as its completion signal. | **Make "confirmed" and "executed" ONE call** (lever 2, structurally): a single write whose body performs the approved change, sharing the code path the confirmation handling already uses, then `remove` the primitives that let the two come apart. A prose reminder to call the tool has been tried on this class and rejected — the agent knows the rule and violates it. |136| **Recoverable error that strands the agent** — a tool raises an opaque traceback / bare code; the agent retries the same bad call or gives up. | **Enriched RETURN that aids recovery**: on a recoverable error return what is wrong + the valid options + the recommended next action (`{"error": "id not found", "available": [...], "next": "call search_x to resolve the id"}`) so the model self-corrects next turn. |137| **The same primitive called N times in a row** — looping over a list in the agent's own context, burning turns and dropping or mis-threading results. | **Loop tool** (lever 5): one tool that takes the list and loops inside a single call. |138| **A rule stated in the prompt but repeatedly violated** — a required order ("read before write"), a precondition the API does not enforce, a normalization the model forgets. | **In-body guard / validation wrapper** (lever 1): enforce the rule in the body of the tool that owns it; `remove` the unguarded primitive if the safe path must be the only one. |139| **A wrong ACT-vs-REFUSE call** — the agent acts where policy says refuse/escalate, or refuses where it should act. | **Discriminating-predicate guard** (lever 3) on the tool that owns the action. |140| **Mis-selection** — the agent calls the wrong tool, calls none when one applied, or invents a tool that does not exist. | **Name + description fix**: selection is driven almost entirely by the name and description — sharpen what/when/when-not and the boundary against the nearest sibling (`references/doc-contract.md`). |141| **Bad argument-filling** — right tool, wrong arguments: a missing required field, the wrong enum value, free text where a structured object was expected. | **Schema + per-parameter docs**: close the value set with an `enum`, pin units/format/default per parameter, add an in-description example. |142| **A bloated or overlapping toolset** — too many tools, or several that do nearly the same thing, distracting the agent. | **Consolidate then `remove`** the originals (lever 9). Remove for *overlap/confusion*, not for low call-count. |143| **A bug in a handler** — the tool returns the wrong thing. | **`code` edit**: because you own the code, fix it directly. |144145The throughline: a failure the agent *knows better than* but still commits is fixed by146removing the choice — putting the behavior in code and `remove`-ing the path that let it147go wrong.148149If the problem is *what the agent is told to do* rather than *what it can do*, it150belongs to whatever capability edits the agent's instructions, not here.151152## What can be optimized (default policy = all of these)153154| Action | Changes | Why it moves the metric |155|--------|---------|-------------------------|156| `description` | tool-level wording incl. in-desc examples | the single biggest lever on *selection* |157| `params` | per-parameter descriptions / defaults | drives correct *argument-filling* |158| `examples` | example call strings | shows concrete well-formed calls |159| `schema` | the full JSON Schema (types, `required`, `enum`) | constrains/guides the model's output |160| `code` | **the handler body of an EXISTING tool** | **the default high-leverage edit** — convert a violated prose rule into an in-body guard; expect to edit SEVERAL bodies per iteration |161| `compose` | add a code-bearing tool that calls existing tools | enforce a rule, collapse a multi-call chain, or perform a whole stalled WRITE action in code |162| `add` / `remove` | introduce / delete a tool | shape and shrink the toolset (replace primitives; keep it lean) |163164`policy.json` in the capability dir is the safety boundary between "reword the docs" and "rewrite the165program" — the same artifact is edited in very different trust settings, so tighten the166allowed set to match your deployment's blast radius (a frozen-API deployment might allow167only `["description", "params", "examples"]`). `apply()` refuses anything outside the168allowed set and *reports* the refusal, so an over-tight policy surfaces as visible169refusals rather than silent no-ops.170171## Adapting to the runtime reader's capability tier172173Scale the edit to WHO calls these tools at runtime (if your instructions state the174runtime reader's capability tier, use it). For a **mid/weak** reader, push harder on175this skill's already-preferred **code enforcement** (in-body guards, composite176atomic-write tools) — a weak reader skips a prose rule but a guard fires regardless —177and write **literal, example-bearing per-parameter slot-filling docs** on every tool178(exact format, units, and one concrete valid value, e.g. `date: ISO-8601 "2026-07-20"`).179A weak reader mis-fills under-documented arguments far more often, so explicit parameter180docs and a smaller, less-confusable toolset are worth most there. For a **frontier**181reader, terser parameter docs and fewer worked examples suffice; spend the budget on182removing redundant tools instead.183184## Artifact + handlers185186`tools.json` — a list of `{name, description, parameters, examples, code?}`.187`scripts/abstract.py` provides:188- `materialize(dir)` — flatten the surface into named text components189 (`tool.<name>.description`, `.parameters`, `.examples`) for a text optimizer.190- `apply(dir, edits)` — policy-enforced edits incl. `schema`/`code`/`compose`;191 returns `{changed, refused}`.192- `validate(dir)` — schema well-formedness, empty-description and duplicate-name checks.193- `is_empty(dir)` — whether the artifact is an empty seed (no tools yet).194195## How to run196197```198python scripts/check.py199python scripts/run.py --path <capability_dir> # candidate + policy + validity200```201202## References203204Each is standalone — read the one that matches what you are about to do.205206- [`references/examples.md`](references/examples.md) — worked before/after edits with207 full bodies, ordered by leverage: in-body guards (§0), description/schema fixes208 (§1–§2), loop and composite/write tools (§3b–§3e), result shaping (§3f), the doc209 contract in practice (§3g), removal and refusals (§4–§7). **Load when you are about to210 write an edit** and want the exact JSON and a real body to model.211- [`references/concepts.md`](references/concepts.md) — how an LLM turns tool definitions212 into a call (select from name+description, fill from schema+examples), toolset-size213 limits, response shaping, the safe replacement protocol, and the action-policy model,214 with cited sources. **Read once per project**, before your first candidate.215- [`references/doc-contract.md`](references/doc-contract.md) — the full documentation216 contract for one tool: what/when/when-not, important-points, error conditions,217 per-parameter units and formats, one always-valid example. **Load when the fix is218 documentation rather than code.**219- [`references/pitfalls.md`](references/pitfalls.md) — edits that look like improvements220 and regress (stripped error conditions, cosmetic rewording, task-overfitted221 descriptions, composite sprawl, example dumps, an exposed primitive behind a wrapper),222 each with how to detect it. **Read before shipping a docs-only candidate**, and when223 an accepted candidate barely moved the metric.224- [`references/field-notes.md`](references/field-notes.md) — observations from real runs:225 how much of a docstring the runtime actually delivers, why enriching a return is not226 free, a docstring header that broke tool registration, and a return shape that227 corrupted the feedback signal. **Read before your first candidate on a new harness**,228 and whenever an edit "verified" green without an explanation you can point at.229- [`references/optimizer-playbook.md`](references/optimizer-playbook.md) — what the230 authored optimizer INSTRUCTIONS must demand when `tools` is selected: the231 existing-tool-code mandate, the depth mandate's tools wording, and the two-phase232 (diagnose fan-out → implement fan-out → merge) subagent pattern. **Read when authoring233 or reviewing those instructions** (`intake` points here rather than inlining it).