bantamkit
This MCP server gives coding agents persistent memory plus validation, auditing, token accounting, and shift-work orchestration tools.
Save, recall, compact, and consolidate durable facts, and manage likely-prompt queries across project/profile memory layers.
Validate model-produced JSON against JSON Schema and audit skill catalogues for cost, collisions, and frontmatter issues.
Draft and run multi-step shift-work jobs: batch dependency graphs, brief/clock units in and out, and inspect checkpoint status.
Read token costs from host transcripts and check server health, build identity, and store/budget status.
Install for Claude Code, Claude Desktop, Cursor, VS Code Copilot, or any stdio MCP client, with an offline-capable install route.
Provides persistent memory tools to GitHub Copilot in VS Code, enabling the agent to store and retrieve project facts and preferences across sessions.
bantamkit โ memory MCP server for Claude Code, Cursor, VS Code Copilot and Claude Desktop
Give your coding agent a memory that outlasts the session. bantamkit is an MCP server: the agent saves what is worth keeping โ a preference you stated, a decision, the fix that worked โ and recalls it next session instead of asking you again. It works with Claude Code, Claude Desktop, Cursor, GitHub Copilot in VS Code and any stdio MCP client. Install it once from npm or PyPI; after that it starts offline.
๐ Quick start
npx -y bantamkit-mcp@latest --install claude # or claude-desktop, copilot, cursorThen restart the host (in Claude Code, open a new session) and ask the agent to call
bantamkit_status. Prefer Python? pip install "bantamkit[mcp]" into a venv you keep, then
bantamkit-mcp --install claude. Every route and host is below.
Related MCP server: mnemo
โจ What you get
Memory that persists. Facts are plain Markdown files under
.bantamkit/memory, deduped and held to a byte budget.memory_saveandmemory_recalldo the work;memory_compactandmemory_dreamkeep the store tidy.Layered by default. Recall reads the project store, any stores you grant in
.bantamkit/config.yaml, and your profile store~/.bantamkit/memory. Saves go to the project store.One command per host.
--installwrites the entry for Claude Code, Claude Desktop, Cursor or VS Code Copilot, backs up any file it edits, and never prompts.Offline after one install. Both install routes record the server by absolute path, so no launch needs the network or your shell's PATH.
Claude Code hooks, if you want them.
--install-hooksputs the memory index in front of each new session, surfaces matching facts on each prompt, and nudges a save at the end of a long session that saved nothing (docs/hooks.md).More than memory. JSON Schema validation, a token ledger read off the host's transcripts, a skill-catalogue audit, and shift-work tools that plan and hand off multi-step jobs โ the fifteen tools.
Two runtimes, one store. Pure Node on npm and Python on PyPI read and write the same files, and every surface is compared between both runtimes by a conformance suite (
node tools/conformance/run.mjs --all).A Python library for small models, too.
pip install bantamkitlifts small-model agents (~1Bโ8B, any OpenAI-compatible endpoint): malformed output, unreviewed answers, forgotten context and runaway loops are absorbed by code, and a bundled eval suite measures the uplift.
๐ Contents
Topic | What you'll find |
Three ways to set it up โ pick one | |
One command; later launches need no network | |
The online form, and why it hangs offline | |
A venv you keep; a wheelhouse for no-network machines | |
The same five steps for every host | |
| |
| |
| |
| |
Codex and any stdio host | |
Every flag and environment variable, with its default | |
| |
Timeouts, Connection closed, refusals, the wrong store | |
15 tools, three prompts, two resource templates | |
| |
Which components to attach, measured on four models | |
Every page under | |
What lives where |
๐ฅ Install the MCP server
Two implementations of one server โ Node on npm, Python on PyPI โ share one memory store on disk. To just try it:
npx -y bantamkit-mcp --assets-root # Node, no Python required
pipx run --spec "bantamkit[mcp]" bantamkit-mcp --assets-root # PythonA host launches the server every session, so what matters is whether each launch needs the network. Pick one of the three ways below.
Install once, run offline (recommended)
npx -y bantamkit-mcp@latest --install claude # or claude-desktop, copilot, cursorThis runs npm install --prefix ~/.bantamkit/mcp once and records
<the node that ran it> ~/.bantamkit/mcp/node_modules/bantamkit-mcp/dist/cli.js, both absolute.
No later launch needs npx, the registry or your PATH, and it never downgrades a newer kept
install.
The recorded node is one version of node. Switch away from it and re-run with
--force.Replacing an old
npxentry needs--force(Claude Code:claude mcp removefirst).Installing into another host while offline: plain
npx -yhangs before bantamkit starts. Usenpx --offline -y bantamkit-mcp@<version> --install <host>, or the kept install's own CLI:<node> ~/.bantamkit/mcp/node_modules/bantamkit-mcp/dist/cli.js --install <host>.
Measurements: docs/install.md โ MCP server: measured install detail.
Run with npx at every launch
{"mcpServers": {"bantamkit": {"command": "npx", "args": ["-y", "bantamkit-mcp"]}}}This asks the registry on every launch. Offline it fails silently: with a warm cache,
npx -y bantamkit-mcp, npx -y bantamkit-mcp@latest and a pinned version each hung to the 45 s
bound with 0 bytes on stdout, and the host reports only its own handshake timeout.
npx also caches what latest resolved to, so add @latest or pin a version
(why).
Install with Python (PyPI)
pip install "bantamkit[mcp]" # into a venv you keep, not a pipx run
bantamkit-mcp --install claude # or claude-desktop, copilot, cursor--install records the venv's console script by absolute path with "args": [], so no launch
needs the network. With no network at all, pip download a wheelhouse on a machine with the
same OS, CPU architecture and Python version, then pip install --no-index --find-links:
steps.
๐ Connect to a host
--install <host> (claude, claude-desktop, copilot, cursor) writes the entry. It backs
the file up first, refuses an entry that differs unless you pass --force, and never prompts โ
so it behaves the same in a terminal, in CI and inside another agent. With Python, run
bantamkit-mcp --install <host> from your venv instead of the npx line.
Each host below has the same steps: 1 command, 2 file, 3 entry, 4 confirm,
5 undo or re-run. The entry's command and args depend on the install route:
Route |
|
|
npm, install once |
|
|
Python venv |
|
|
npx every launch |
|
|
For JSON hosts, a quick check of the recorded command: run it in a terminal โ with nothing on
stdin it prints usage: bantamkit-mcp โฆ and exits 0.
Connect to Claude Code
Command:
npx -y bantamkit-mcp@latest --install claudeFile: none edited directly โ it runs
claude mcp add bantamkit -s user -- <command> <args>(user scope, every project), because~/.claude.jsonalso holds host state that is not MCP config.Entry: printed as
ran : claude mcp add bantamkit -s user -- <node> <cli.js>.Confirm:
claude mcp listprintsbantamkit: <node> <cli.js> - โ Connected(Claude Code 2.1.272; run it outside a project whose.mcp.jsonalso names bantamkit, or it prints[Conflicting scopes]). In a session, ask the agent to callbantamkit_status.Undo / re-run:
--forcedoes not reach Claude Code, and a second add fails withMCP server bantamkit already exists in user config. Remove first:claude mcp remove bantamkit -s user npx -y bantamkit-mcp@latest --install claude
Connect to Claude Desktop
Command:
npx -y bantamkit-mcp@latest --install claude-desktopFile: macOS
~/Library/Application Support/Claude/claude_desktop_config.jsonยท Windows%APPDATA%\Claude\claude_desktop_config.jsonยท Linux~/.config/Claude/claude_desktop_config.jsonEntry (key
mcpServers):{"mcpServers": {"bantamkit": {"command": "/absolute/path/to/node", "args": ["/Users/you/.bantamkit/mcp/node_modules/bantamkit-mcp/dist/cli.js"]}}}Confirm: it prints
installed bantamkit into claude-desktopwith file, key and command. Fully quit and reopen Claude Desktop, then ask it to callbantamkit_status.Undo / re-run: there is no uninstall flag โ delete the
bantamkitentry or restoreclaude_desktop_config.json.backup-<date>. A matching re-run printsbantamkit is already installed in claude-desktop and matches; a differing entry needs--force.
Connect to Cursor
Command:
npx -y bantamkit-mcp@latest --install cursorFile:
~/.cursor/mcp.jsonon every OS (%USERPROFILE%\.cursor\mcp.jsonon Windows);.cursor/mcp.jsonfor one project, by hand.Entry (key
mcpServers):{"mcpServers": {"bantamkit": {"command": "/absolute/path/to/node", "args": ["/Users/you/.bantamkit/mcp/node_modules/bantamkit-mcp/dist/cli.js"]}}}Confirm: it prints
installed bantamkit into cursorandkey : mcpServers. Restart Cursor and ask the agent to callbantamkit_status.Undo / re-run: delete the entry or restore
mcp.json.backup-<date>. To replace a differing entry, such as an oldnpxone:npx -y bantamkit-mcp@latest --install cursor --force
Connect to VS Code (GitHub Copilot)
Command:
npx -y bantamkit-mcp@latest --install copilotFile: macOS
~/Library/Application Support/Code/User/mcp.jsonยท Windows%APPDATA%\Code\User\mcp.jsonยท Linux~/.config/Code/User/mcp.json;.vscode/mcp.jsonfor one workspace, or MCP: Open User Configuration, by hand.Entry โ the key is
servers, notmcpServers, plus"type": "stdio". This is the detail that catches people out:{"servers": {"bantamkit": {"type": "stdio", "command": "/absolute/path/to/node", "args": ["/Users/you/.bantamkit/mcp/node_modules/bantamkit-mcp/dist/cli.js"]}}}Confirm: it prints
installed bantamkit into copilotandkey : servers. Restart VS Code and ask Copilot to callbantamkit_status.Undo / re-run: delete the entry or restore
mcp.json.backup-<date>;--forcereplaces a differing entry.
Connect other MCP clients
Any stdio MCP client runs the same command/args; nothing is host-specific. Codex and a
generic JSON config: docs/mcp.md โ Client setup. Hand-written forms
for every host: npm README ยท
Python README.
๐ง Configuration
Flags go in the entry's args; environment variables in its env block (or
claude mcp add -e NAME=value). Both runtimes print the same --help.
Setting | What it does | Default | Example |
| Pins the store to one absolute path; a missing or relative path refuses at startup | unset: nearest existing |
|
| One store, layering off; outranks | off (layered) |
|
| Where store discovery starts; not with | cwd |
|
Layered memory | Recall reads the project store, stores granted in | on | |
| Default recall budget |
|
|
| Memory index byte budget |
|
|
| Logs tool outcomes as JSONL | off; |
|
| The daily Retention job: deletes session read ledgers older than 7 days, folds log lines older than 30 days, archives closed | on; |
|
| The daily skill audit (#205): the same once-a-day child audits your skills and writes | on; |
|
| Skills from experience (#206): lets the daily run INSTALL a skill it mined (twice the bar, a gated draft, one a day, five live, 30 days of probation) and make the low-risk EDITS to installed skills (keyword widening, a Pitfalls line, a drafted description) and PROMOTE a skill used 3 times in 30 days to a native link, on its own. Off, the same work waits for you as proposals in the | off in 0.37.0; |
|
| Skills from experience (#206): an OpenAI-compatible endpoint ( | unset: no endpoint |
|
| Skills from experience (#206): the catalogue budget in bytes (the summed |
|
|
| Where the derived recall indexes live (hot and cold); deleting them loses nothing |
|
|
| Your own asset pack (skills, rubrics, schemas) | the pack inside the package |
|
| Where | macOS |
|
| Price table for the token ledger |
|
|
| Where a checkout launcher resolved its halves โ only | โ |
|
Don't add --store by reflex: the layering is most of the value. One-shot commands that print
and exit: --install {claude,claude-desktop,copilot,cursor} (with --force),
--install-hooks / --remove-hooks (with --yes), --hook, --update,
--assets-root, --mcp-report, --statusline.
More: store binding ยท
pinning ยท
event log ยท prices.
๐ Update
npx -y bantamkit-mcp@latest --update # patches the kept install at ~/.bantamkit/mcp
npx -y bantamkit-mcp@latest --assets-root # npx CACHES; without @latest you get an old resolve
npm i -g bantamkit-mcp@latest # global npm install
pip install -U "bantamkit[mcp]" # PyPI (pipx upgrade bantamkit ยท uv tool upgrade bantamkit)
git pull && npm ci --prefix runtime-ts && npm run build --prefix runtime-ts # a checkout: dist/ is build output--update (check the package index and update this install if it differs, then exit) patches a
registry install or a kept install to exactly the version it compared, and reports success only
after the install says so; any other shape it refuses with exit 1 and names the manual route.
It is the only network access here, and only when you type it.
Then restart the server in the host (/mcp โ reconnect in Claude Code; a full restart of
Claude Desktop) โ a running server keeps the code it started with. bantamkit_status prints the
version and the build_id of the code answering you; a new version with an old build_id
means an old process. Per-install table: npm README โ Updating.
๐ฉบ Troubleshooting
Symptom | Fix |
The host times out; the server never answers | The entry uses |
| A GUI host does not read your shell rc, so |
Connection closed at startup | Older builds crashed when a GUI host started them in |
| Re-run with |
|
|
The server stopped launching after a node change |
|
Recall finds nothing, or the wrong store | Set |
๐งฐ What it serves
Both servers serve the same 16 tools, three prompts and two resource templates
(bantamkit://skills/{name}, bantamkit://rubrics/{name}). The prompts are what a person invokes from
the host's slash menu: bantamkit_status (the status report), skill_audit (#205: gathers the
skills this host loads, audits them, and inserts the report plus a proposed settings.json change
that is never applied for you; in Claude Code it is /mcp__bantamkit__skill_audit) and
skill_propose (#206: lists the skills bantamkit proposes from your repeated, successful work and
the ones it installed, and approves, rejects, undoes, promotes or demotes one; in Claude Code it is
/mcp__bantamkit__skill_propose, see docs/skills.md):
Tool | What it does |
| store one durable fact, deduped and budgeted |
| retrieve facts matching a query |
| archive the stalest facts to fit the index budget |
| consolidate facts the project and profile layers hold under the same name |
| list the facts that have no likely-prompt queries yet, and store the queries written for them; recall matches a prompt against them |
| validate a document against a JSON Schema |
| audit a skill catalogue for findings |
| open a unit of work and get its brief |
| close a unit with status and accounting |
| read-only progress summary of a checkpoint |
| read-only: which units of a checkpoint its |
| turn any |
| what a session cost, read off the host's transcripts |
| is bantamkit working, and which build: version, |
| report the fingerprint of the source on disk, not the executing code โ useful when a machine carries two installs under one name |
๐ Use it as a Python library
pip install bantamkitEditable install and runtime differences: docs/install.md,
docs/porting.md. Copy-paste start: examples/.
from bantamkit import Agent, CritiqueGate, Memory, OpenAICompatible, Tool, ToolDef
client = OpenAICompatible(base_url="http://localhost:11434/v1", model="qwen2.5:7b-instruct")
price_lookup = ToolDef(
tool=Tool(
name="price_lookup",
description="Get the unit price of an item",
parameters={
"type": "object",
"required": ["item"],
"properties": {"item": {"type": "string"}},
},
),
handler=lambda item: f"{item} price: 25",
)
agent = Agent(client=client, tools=[price_lookup]).use(
Memory(store="./.bantam-memory"),
CritiqueGate("task-completion"),
)
result = agent.run("What does a widget cost? Remember it for next time.")
print(result.output, result.usage.total)๐ Recommended defaults
Measured on the bundled 22-task suite across four models, 528 runs each. Numbers and caveats: docs/usage.md โ Recommended defaults.
Always attach
Memoryโ the biggest single mover on every model measured.Skip the blind
CritiqueGateon small instruct models โ โค6 passes at 2โ3.5ร bare's tokens.Don't credit a rubric edit without a bar โ the non-fragile anchor set is the floor.
Use
structured()for schema'd output โ zero schema retries in 2,112 runs.Attach
FileAccessGraphwhen the agent reads files, on ~4B-class models.full(memory + schema + grounded critique) is a 4b-reference result โ 66/66 there only.Prefer
GroundedCritiqueGateoverCritiqueGatewhen the agent has tools โ same 4b scope.
๐ Documentation
Page | Covers |
Requirements, editable install, endpoints, | |
The 5-layer model and the no-mixing rule | |
Client, agent, tools, components, | |
Store layout, the four ops, dedupe and budget, layers, the operator CLI ( | |
The read ledger and the | |
Running the suite, the config matrix, current results | |
| |
The JSONL outcome log, its vocabulary and the 1 MiB cap | |
| |
| |
| |
| |
| |
| |
| |
Checkpoint contract and clock-in/clock-out driver (experimental) | |
The Node port, | |
The two-runtime diff harness and what a | |
What ships and the pre-publish checklist |
๐ Repo layout
Path | What |
| The Python runtime ( |
| The pure-Node MCP server ( |
| Language-agnostic asset pack: skills, rubrics, tool schemas, eval tasks |
| Runnable starter scripts (quickstart, structured output, layered memory) |
| Repo tools that ship outside the wheel (e.g. the shift-work driver) |
| This runbook |
Design notes live in docs/superpowers/specs/.
Available Tools
16 toolsbantamkit_statusbantamkit statusARead-only
Is bantamkit working, and which bantamkit? Returns a short report a person can read in the transcript: bantamkit Active ๐ข or bantamkit Degraded ๐ on the first line, then the version and build_id of the code answering, the surfaces it serves, the memory store it is bound to with its index size against its budget, whether the event log is on, and โ when degraded โ every condition in full with what to do about it. Call it when the user asks whether the server is alive or working, when a bantamkit tool answered oddly and you need to know whether the server is the reason, or when a reply arrived carrying a bantamkit degraded footer and you want the rest of it. Every fact is observed at call time from the server's own state; nothing is a heartbeat or a cached verdict. Takes no arguments, writes nothing, and never echoes another call's arguments. build_identity answers only the build_id fingerprint, as JSON, when all you need is to compare two installs; shiftwork_status reports a job, not the server.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and openWorldHint=false, but the description adds substantially beyond them: no arguments, writes nothing, never echoes another call's arguments, and every fact is observed at call time with no heartbeat or cached verdict. It also discloses the degraded-path behavior (full conditions plus remediation), which is not derivable from annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the question it answers and the return shape, then triggers, then guarantees, then alternatives โ a sensible order with no filler. The middle sentence is long and clause-stacked, which slightly hurts scanability, but every clause carries real information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers the return format, the degraded case and remediation, purity guarantees, and sibling disambiguation โ everything an agent needs to decide and call correctly, so nothing further is required of it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters, so the baseline is 4 and the schema is trivially complete. The description reinforces this by stating 'takes no arguments,' which is consistent and mildly useful but adds no semantics beyond the empty schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a direct question the tool answers ('Is bantamkit working, and which bantamkit?') and immediately specifies the exact returned form (`bantamkit Active ๐ข` first line, version, build_id, surfaces, memory store, event log). It explicitly contrasts itself with build_identity (build_id only) and shiftwork_status (a job, not the server), so an agent can distinguish it from siblings without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives three concrete trigger conditions: the user asks whether the server is alive, a bantamkit tool answered oddly and the server may be the cause, or a reply carries a `bantamkit degraded` footer. It also names the alternatives and when to prefer them (build_identity for fingerprint comparison, shiftwork_status for jobs), leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
build_identityBuild identityARead-only
Which bantamkit build is answering you, readable mid-call. Returns build_id โ a content fingerprint of the running source and the asset pack it loads โ plus the paths it was computed over. Call it when one MCP server name may resolve to more than one install: two endpoints reporting the same version are the SAME build only if their build_id matches, because a version string moves on release bumps and not on builds. Any fact that cannot be derived comes back as {"unavailable": ""} and is named in the unavailable list โ never omitted, never a placeholder that reads as a value. Takes no arguments and reads no state. Use bantamkit_status when the question is whether the server is healthy or which store it is bound to โ that report includes this fingerprint; call this tool when you need only the fingerprint, as JSON, to compare two endpoints.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With readOnlyHint/openWorldHint already covering safety, the description still adds real context: no arguments, reads no state, and a precise unavailable-value contract (`{"unavailable": "<reason>"}` plus an `unavailable` list) that prevents an agent from mistaking a sentinel for a genuine value. That is disclosure beyond what annotations or schema provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with what it returns before the when-to-use rationale, and every sentence carries information. Slightly dense โ the version-vs-build argument is restated across clauses โ but no filler sentences.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, yet the description still tells the agent the key field name and the unavailable-sentinel convention, plus the comparison workflow. For a no-argument read tool this is complete: an agent knows what it gets, what it means, and what to do with it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Zero-parameter tool, so baseline is 4; the description confirms 'takes no arguments,' which is consistent with the empty schema. There is nothing further to disambiguate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource โ returns `build_id`, a content fingerprint of the running source and asset pack โ with enough detail that its uniqueness versus `bantamkit_status` is immediately legible. The contrast between version strings and build fingerprints clarifies what the tool actually reports.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit trigger ('when one MCP server name may resolve to more than one install'), a decision rule (same version != same build; compare `build_id`), and a named alternative (`bantamkit_status` for health or store binding, which embeds this fingerprint). Both when-to-use and when-not-to-use are covered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_compactCompact the memory storeADestructive
Free room in persistent memory when memory_save was refused for budget. Archives the stalest facts of the writable project store until a fact as large as the biggest one kept will fit, and returns what moved โ every archived name with its type and description. Nothing is deleted: archived facts leave the index but stay on disk under archive/, memory_recall with include_archived still finds them, and bringing one back is an operator action rather than a tool โ bantamkit-memory restore <name> (Node) or python -m bantamkit.memory restore <name> (Python) on the store; no tool deletes a fact. Read-only grants and the profile layer are never touched. Call it after a budget refusal, then retry the save; calling it under budget archives nothing and says so. It is not the pass for two copies of one name across layers โ that is memory_dream.
| Name | Required | Description | Default |
|---|---|---|---|
| reserve | No | Bytes of index headroom to free below the budget: the target is `budget - reserve`, a negative value counts as 0, any value is capped at half the budget, and 0 compacts to the budget itself. Omit for the default: the room the degraded-index warning demands (the budget minus `(90 * budget - 1) // 100`) plus the size of the largest index line the store holds -- at the default 24000-byte budget with a 39-byte largest line that is 2401 + 39 = 2440, a 21560-byte target. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial context beyond the annotations: what actually moves, that nothing is deleted and archived facts remain on disk and are still visible to memory_recall with include_archived, that restoration is an operator CLI action rather than a tool, and that read-only grants and the profile layer are untouched. The 'Nothing is deleted' line could read as tension with destructiveHint=true, but it is a clarification of what 'destructive' means here (index removal, recoverable archive) rather than a contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the trigger and the action, and each subsequent clause adds a distinct, load-bearing fact (returns, recoverability, operator restore, scope exclusions, sibling differentiation). It is dense and parenthetical-heavy, which costs a little readability, but no sentence is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With annotations, a fully documented single parameter, and an output schema, the description covers what an agent still needs: when to call, what happens under budget, what is returned ('every archived name with its type and description'), and that recovery is out-of-band. Nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description never mentions the single parameter, but schema description coverage is 100% and the schema's `reserve` description is unusually thorough, including the default formula and a worked numeric example. Baseline 3 applies since the schema carries the parameter meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (compact/archive) and resource (the writable project memory store), names the exact trigger ('when memory_save was refused for budget'), and describes the selection rule ('stalest facts ... until a fact as large as the biggest one kept will fit'). It also pre-empts the nearest sibling by declaring 'It is not the pass for ... memory_dream.'
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives the precise usage sequence ('Call it after a budget refusal, then retry the save'), the negative case ('calling it under budget archives nothing and says so'), and the explicit alternative for a different problem ('that is memory_dream'). Nothing about when to reach for this tool is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_dreamDream: consolidate the memory storeADestructive
Consolidate the memories the project layer and the machine-wide profile layer hold under the SAME NAME, and say exactly what changed. Byte-identical copies collapse to one; a pair that has diverged is UNIONED โ every claim from both copies survives, never a winner-takes-all โ and a relative date in a body is annotated with the day it resolves to against that fact's own mtime, never against today. The survivor stays in the writable project layer and the profile copy MOVES to that store's archive/: nothing is deleted, and bringing a consumed copy back is an operator action, not a tool โ bantamkit-memory restore <name> or python -m bantamkit.memory restore <name> on the profile store. This is a correctness pass, not a token saving: the profile index is derived at read time and is not loaded from a prompt, so consolidating it frees approximately no bytes โ what it buys is one copy of a ruling instead of two that can disagree. It defaults to a DRY RUN because the profile store is machine-wide: a memory archived out of it stops answering for every other project on this machine that has no store of its own. Read the plan first, then call it again with dry_run false to apply it. For room in the index call memory_compact instead; for the facts themselves, memory_recall.
| Name | Required | Description | Default |
|---|---|---|---|
| dry_run | No | Preview only. True (the default) performs every read, merge and projection and writes nothing. Pass false to apply the plan. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: names the survivorship rule (project layer wins, profile copy moves to archive/), asserts nothing is deleted, states the out-of-band recovery path (bantamkit-memory restore <name>), and flags the blast radius that the archived copy stops answering for every other project on the machine. This is consistent with destructiveHint=true while clarifying that 'destructive' here means archive-and-move, not deletion.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the verb and the core rule, and every clause carries operational content. It is nonetheless dense and long for a one-boolean tool, with some rhetorical framing ('never a winner-takes-all', 'what it buys is one copy of a ruling instead of two') that could be trimmed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need not be explained, and the description covers everything else an agent needs: default behavior, safety impact, recovery route, sibling alternatives, and the reason the merge is done at all. Nothing material is missing for a destructive one-parameter tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single dry_run parameter is fully described there, so the baseline applies. The description still adds workflow meaning โ that the default is a preview, that true performs all reads and merges but writes nothing, and that the agent should call twice (preview then apply).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: consolidating memories held by the project layer and machine-wide profile layer under the same name, then reporting what changed. It spells out the three resolution outcomes (byte-identical collapse, divergent union, relative-date annotation) and names the surviving location, so an agent knows precisely what the operation does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes away from siblings: 'For room in the index call memory_compact instead; for the facts themselves, memory_recall.' It also gives the required call sequence โ read the plan first, then re-call with dry_run false โ and warns that the default dry run exists because the profile store is machine-wide.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_queriesLikely-prompt queries for saved factsA
Write, or list the facts that still lack, the likely-prompt queries recall matches a user's prompt against โ for each fact, the prompts a user would type when that fact is what they need. Call it with {} at the end of a session that saved facts, or when bantamkit_status reports queries: n of m facts with n below m: it answers up to 10 facts with no queries yet (layer, name, description, a body excerpt), the writing instructions, and remaining. Then call it with {"facts": [{name, layer?, queries}]} to store what you wrote. Every item is checked before anything is written and the first bad one refuses the whole call โ an unknown name or layer, more than 8 queries, one over 200 characters; queries: [] removes a fact's entry. No fact file changes: queries live in a per-layer sidecar under ~/.bantamkit/queries/, keyed to the fact's content, so a fact edited later is listed as missing again. memory_save takes queries directly for a fact you are saving now; this tool is for facts already on disk.
| Name | Required | Description | Default |
|---|---|---|---|
| facts | No | Omit to list the facts missing queries |
Output Schema
| Name | Required | Description |
|---|---|---|
| facts | No | With `missing`: up to 10 {layer, name, description, body} still without queries. |
| reason | No | With result `error`: one sentence saying why the call was refused. |
| result | Yes | `missing` for the list call, `written` for the store call, `error` with `reason` when refused. |
| written | No | With `written`: how many items were stored. |
| remaining | No | How many facts across the readable layers still have no queries after this call. |
| instructions | No | With `missing`: how to write good queries. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations by disclosing all-or-nothing validation (the first bad item refuses the whole call), the specific rejection reasons, that no fact files are modified, that queries live in a per-layer sidecar keyed to fact content, and the consequence that an edited fact is re-listed as missing. It also states the batch cap of 10 facts and what the listing returns.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the what and when, then the write mode and constraints; every clause is informative. It is a single long run-on paragraph with an awkward opening clause and some repetition of limits already in the schema, which costs readability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, yet the description still tells the agent what the listing returns (layer, name, description, excerpt, instructions, `remaining`), plus the validation failure modes and the sidecar lifecycle. Nothing needed to call it correctly in either mode is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description still adds meaning the schema does not, such as the 10-fact listing cap, the fact that the listing includes body excerpts and `remaining`, and the semantics of `queries: []` as removal. It restates some limits (8 queries, 200 chars) already in the schema, so it is additive but not fully non-redundant.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb pair (write / list) and a precise resource (likely-prompt queries attached to saved facts), and explains the concept in-line: the prompts a user would type when that fact is what they need. It explicitly contrasts itself with the sibling memory_save, so an agent can route between them without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete trigger conditions: call with {} at the end of a session that saved facts, or when bantamkit_status reports `queries: n of m facts` with n below m; then call with {facts: [...]} to store results. It also names the alternative path (memory_save takes `queries` directly for a fact being saved now) and scopes this tool to facts already on disk.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_recallRecall memoriesA
Search persistent memory for facts a past session saved. Call it BEFORE starting a task that resembles past work, and when the user refers to something decided earlier; use memory_save to add a fact, and memory_compact or memory_dream only to tidy the store โ this tool never changes what a fact says. Reads every layer in order โ the project store, any read-only grants in .bantamkit/config.yaml, then the machine-wide profile โ and returns the top word matches, never fewer than the store default of 3 when that many match; a query that is exactly a fact's name returns that fact alone. Pass file instead of query to see the facts linked to a file. Why it is not read-only: every hit is appended to the usage ledger (~/.bantamkit/usage/.jsonl) as a recall event โ the staleness order memory_compact archives by โ and a project fact file still in the 0.35.x format is rewritten once into the current one; both are best-effort and never fail a recall. A fact file that cannot be read is named in the reply, never silently skipped. Pass outcome: error to see only recorded failures.
| Name | Required | Description | Default |
|---|---|---|---|
| k | No | Facts wanted, clamped to 1..5. Raised to the store default of 3 when smaller; never lowered below it. | |
| file | No | A tracked file, or the shortest unique suffix of one (store.ts, memory/store.ts). Returns the facts linked to it and when it was last touched; query is then ignored. | |
| query | No | Words a past-you would have used to describe the fact | |
| outcome | No | Keep only facts saved with this outcome โ `error` answers "what failed before", `success` "what worked". Facts with no outcome are dropped from the answer; omit it for the normal search. | |
| include_archived | No | Also search facts compaction moved to archive/. Archived matches come after the live ones, tagged [archived]. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations declare readOnlyHint=false, and the description explains why: every hit appends to the usage ledger and old-format project fact files may be rewritten once, both best-effort. It also discloses layer read order, minimum result behavior, exact-name behavior, and that unreadable fact files are named rather than silently skipped.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with purpose and usage, and nearly every sentence carries useful behavioral detail. It is dense and long, with semicolon-heavy clauses, but not padded with empty filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given five optional parameters, an output schema, and annotations, the description covers timing, alternatives, side effects, read order, result rules, and failure handling. Nothing important for correct invocation appears to be missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents all five parameters. The description still adds cross-parameter meaning: `file` supersedes `query`, exact fact-name queries return that fact alone, `outcome: error` filters to recorded failures, and `k` is floored by the store default.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: search persistent memory for facts saved by a past session. It also distinguishes itself from memory_save and memory_compact/memory_dream, and explicitly says it never changes what a fact says.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use guidance: before starting work resembling past work, and when the user refers to something decided earlier. It names alternatives for saving and tidying, and explains using `file` instead of `query`.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
memory_saveSave a memoryADestructive
Save ONE durable fact to persistent memory โ a preference the user stated, a decision, the fix that worked โ so a future session can recall it. Use it only for facts a future session needs; never for anything derivable from the repo, git history or docs, nor for one-off state of this conversation. To find a fact use memory_recall; to change one, save again under its EXACT name, which replaces it in place; to free room call memory_compact. Refused with nothing written, each with its own sentence: a description too close to an existing fact's (the reply names that fact โ save under that SAME name or skip, never rename to force a copy), a spelling that normalises onto an existing name (Deploy Command onto deploy-command), and a save that would push the always-loaded index over its byte budget (the reply names the remedy). Every save lands in the writable project store (.bantamkit/memory), never in the profile or a read-only grant, and is appended to the usage ledger. Optional outcome (error|success) tags a lesson and status: done closes a project fact; nothing here archives โ that is the operator's bantamkit-memory candidates/merge.
| Name | Required | Description | Default |
|---|---|---|---|
| body | Yes | The fact itself, terse | |
| name | Yes | short-kebab-case slug | |
| type | Yes | What kind of fact: `user` (who the user is, their role and preferences), `feedback` (a correction or confirmed way of working, with the why), `project` (ongoing work, a decision, a constraint), `reference` (a URL, ticket or dashboard pointer). | |
| links | No | names of related memories | |
| status | No | Lifecycle of a project fact. Save an existing name with `done` when the work it describes has shipped; `bantamkit-memory candidates` lists done facts for the operator to archive. Absent means active; `active` is never written. | |
| outcome | No | What the lesson records: `error` (a failure to avoid) or `success` (a way that worked). Only on feedback and project facts; refused on user and reference. Read back by `bantamkit-memory list --outcome` and by memory_recall's `outcome` filter. | |
| queries | No | The prompts a user would type when this fact is what they need: three in the user's language and three in English, at most 8, each at most 200 characters | |
| keywords | No | Words the user will type when they need this fact, in the user's own language as well as English (e.g. Thai and English terms) | |
| description | Yes | One line written to match the FUTURE recall query, not to summarize the body |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: it enumerates the three refusal modes each with their remedy (duplicate description, name normalisation collision, index byte budget), states where the write lands (.bantamkit/memory, never profile or read-only grant), and notes the usage-ledger append. It also explains that a re-save replaces in place, which gives meaning to destructiveHint=true rather than contradicting it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core instruction and the exclusion rule are front-loaded in the first two sentences, which is the right order. The remainder is dense and clause-heavy (nested parentheticals around the refusal cases), so it is efficient for its complexity but occasionally hard to parse in one pass.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 9-parameter mutation tool with an output schema, this covers everything the agent needs: scope, exclusions, failure modes with remedies, storage destination, replacement semantics, and lifecycle via status/outcome. Return values are already covered by the output schema, so nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds genuine semantics on top: re-saving under the EXACT name replaces in place, renaming to force a copy is refused, and `status: done` closes a project fact while nothing archives. These are behavioural consequences of specific parameter values that the schema descriptions alone do not fully convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Save ONE durable fact to persistent memory') and immediately bounds the scope with examples of what counts. It also names the complementary siblings (memory_recall for finding, memory_compact for freeing room), so an agent can place it among its neighbours without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when NOT to use it ('never for anything derivable from the repo, git history or docs, nor for one-off state of this conversation') and routes to the right alternatives: memory_recall to find, re-saving under the exact name to change, memory_compact to free space. That is the full when/when-not/alternative triad.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
shiftwork_clock_inShift work: clock inA
Shift-work clock-in: validate the checkpoint against the checkpoint schema and return the brief of ONE unit โ {unit, role, invariants, handoff, do_not, files} โ to hand to the agent you spawn, verbatim. Call it once per unit, right before spawning; use shiftwork_plan first to see the ready batch when several units are to run at once, and shiftwork_clock_out, not this tool, when a unit finishes. The unit briefed is plan.cursor, or unit_id when given; either way it must be in shiftwork_plan's ready โ a done or dropped cursor, a cursor whose dependencies have not run, or a unit_id naming no unit answers {"result": "error", "reason": "unit is not ready; ready is <a, b>"}, and the batch view's own refusals (cycle, unknown dependency, unreadable checkpoint) pass through verbatim. A ready unit whose touches shares a path with a unit briefed and not yet clocked out is refused too, before any brief: unit <id> touches <path>, which <other> holds until its clock-out. A checkpoint whose reviews break review independence is refused with shiftwork_plan's sentence. With job.budget declared, a brief that does not fit is refused: budget <dim> spent: <n> of <limit>; raise job.budget or stop the job. The other outcomes, never exceptions: escalate when handoff.open_questions is non-empty (stop and ask the user) or when the cursor names no unit, success when every unit is done or dropped, error when the checkpoint fails validation. It never moves plan.cursor; clock_out does. What it WRITES: on result=brief, and only then, one line {event: brief, ts, unit, role} appended to .log.jsonl โ best-effort, so a log it cannot write costs the brief nothing. That line is what later lets clock_out accept the unit while the cursor is elsewhere and sets briefed on its accounting line. The checkpoint file itself is never modified. See docs/shiftwork.md.
| Name | Required | Description | Default |
|---|---|---|---|
| unit_id | No | The unit to brief instead of plan.cursor; it must be in shiftwork_plan's `ready` for this checkpoint. Omit it, or pass null, to brief the cursor unit. | |
| checkpoint | Yes | Path to the shift-work checkpoint JSON file (by convention .shiftwork/checkpoint.json in the repo the job works on), absolute or relative to the server's working directory. The same file shiftwork_plan, shiftwork_clock_in and shiftwork_clock_out take; it must validate against assets/schemas/shiftwork-checkpoint.json. |
Output Schema
| Name | Required | Description |
|---|---|---|
| role | No | The unit's role, the key job.roles may bind models to. |
| unit | No | The plan.units entry briefed: id, role, status, depends_on and its own fields. |
| files | No | state.artifacts: the files the job has produced so far. |
| do_not | No | handoff.do_not: near-mistakes past sessions made. |
| reason | No | With result `escalate`, `success` or `error`: one sentence saying why. |
| result | Yes | `brief` (the unit is yours to spawn), `escalate` (stop and ask: see `reason` and `open_questions`), `success` (every unit done or dropped), or `error` with `reason`. |
| handoff | No | The checkpoint's handoff block: next_action, open_questions, do_not, notes. |
| upstream | No | One {unit, title, status, last} per depends_on id, in depends_on order: last is that unit's newest attempt (status, outcome, notes?, produced?) or null. Always present with a brief. |
| agreements | No | job.agreements: the working agreements adopted so far through clock_out, carried on every brief; [] when none. |
| invariants | No | job.constraints: what every unit must keep true. |
| open_questions | No | With result `escalate`: the questions the orchestrator must put to the user before any unit runs. An irreversible unit whose verb job.authorized does not grant also escalates (#225), with no open_questions. |
| prior_attempts | No | This unit's own attempts, oldest first, at most 3; [] when none. A unit clocked out todo and briefed again is a rework, and this is what it found. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false and destructiveHint=false, and the description explains exactly why: 'on result=brief, and only then, one line {event: brief, ts, unit, role} appended to <checkpoint>.log.jsonl โ best-effort' plus 'The checkpoint file itself is never modified' and 'It never moves plan.cursor'. That is genuine context beyond the annotations, including failure tolerance of the log write. It stops short of describing concurrency behavior or ordering guarantees across parallel clock-ins.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded well: purpose and return shape land in the first sentence. But the body collapses into a dense run-on enumerating refusal strings and outcome names ('escalate', 'success', 'error') in a single paragraph, mixing guidance, error catalogs, and write semantics. It is informative rather than wasteful, but a bulleted outcome list would carry the same content with far less parsing cost.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a stateful tool that reads a checkpoint and appends a log, the description covers preconditions, all outcome variants, side effects, and non-effects (cursor untouched, checkpoint unmodified), and it points at docs/shiftwork.md for depth. Even though an output schema exists, the inline brief and refusal shapes make the contract unambiguous.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds resolution semantics the schema only implies: 'The unit briefed is plan.cursor, or `unit_id` when given; either way it must be in shiftwork_plan's `ready`', including what happens for a done/dropped cursor or a dependent cursor. The `checkpoint` parameter adds nothing beyond the schema, but the unit selection logic is meaningfully elaborated.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource plus the exact payload: 'return the brief of ONE unit โ {unit, role, invariants, handoff, do_not, files} โ to hand to the agent you spawn, verbatim.' It then names the two siblings it must not be confused with (shiftwork_plan for the `ready` batch, shiftwork_clock_out for unit completion), so an agent can route correctly without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit timing ('Call it once per unit, right before spawning'), an explicit alternative for the multi-unit case ('use shiftwork_plan first to see the `ready` batch when several units are to run at once'), and an explicit exclusion ('shiftwork_clock_out, not this tool, when a unit finishes'). It also states the precondition that the target unit must appear in shiftwork_plan's `ready`.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
shiftwork_clock_outShift work: clock outADestructive
Shift-work clock-out: record that a unit finished โ set its status, advance plan.cursor, merge handoff_patch into handoff, push history_entry onto the 5-entry history ring, and append one accounting line to .log.jsonl. Call it once per unit after its agent returns: shiftwork_clock_in briefs, this tool closes, and shiftwork_status reads what both wrote. The whole mutated document is validated against the checkpoint schema BEFORE one atomic write, so a failure writes nothing and answers {"result": "error", "reason": ...}; nothing is ever deleted from the checkpoint. unit_id must be plan.cursor โ which needs no brief and clocks out with briefed=false โ or a unit clock_in briefed since its own last clock-out; any other id is refused with unit X is not the cursor unit Y. handoff_patch and history_entry are both required arguments: pass {} to leave handoff unchanged, and give history_entry at least unit and outcome. plan.cursor then moves to the head of the ready batch recomputed on the new document โ plan.units order over the remaining non-terminal units when the graph cannot batch, unit_id when none remain. The accounting line carries unit, role, status, ts and briefed (written by the runtime, never taken from you: true only when a brief was issued since this unit's LAST clock-out) plus your accounting fields when given. follow_ups appends the work it discovered as todo units. agreements adopts or retires a working agreement every later brief carries. Each clock-out also appends {status, outcome, notes?, produced?} to the unit's attempts (newest 3 kept). With job.budget declared, a line reporting 0 tokens is refused unless it carries estimate: true. When job.roles names this unit's role, accounting.model must be on that role's list, spelled exactly, or nothing is written โ see the model property.
| Name | Required | Description | Default |
|---|---|---|---|
| status | Yes | The unit's new status; the same five values the checkpoint schema allows on plan.units[].status. | |
| unit_id | Yes | The unit being clocked out. Must be plan.cursor, or a unit briefed since its own last clock-out: clock_in briefs the cursor unit by default and, when its unit_id names one, any unit shiftwork_plan reports in `ready` โ and that brief is what lets clock_out accept a unit the cursor is not on. | |
| agent_id | No | The `agentId` the Agent tool's result returned for this unit's subagent (#266), copied verbatim: 1 to 64 letters, digits, - or _, or nothing is written. It goes on the accounting line, never into the checkpoint, and is how the skill miner attributes the skill uses the hook logged inside that subagent to this unit. Omit it for a unit run inline. | |
| accounting | No | Per-unit cost line, or null (the default) to log a line with no cost figures at all. A non-null line must carry `tokens` and `duration_ms`; `model` is required when job.roles names this unit's role. The object shape is described below. | |
| agreements | No | A lesson this unit wants every later brief to carry (#261): retire by exact text, then adopt. Refused, nothing written, for text not on the list or a result over 20; each applied change is recorded in retro[]. | |
| checkpoint | Yes | Path to the shift-work checkpoint JSON file (by convention .shiftwork/checkpoint.json in the repo the job works on), absolute or relative to the server's working directory. The same file shiftwork_plan, shiftwork_clock_in and shiftwork_clock_out take; it must validate against assets/schemas/shiftwork-checkpoint.json. | |
| conclusion | No | An investigation's answer (#264), written to job.conclusion. Only a unit with `concludes: true` may send it; `resolved` is refused unless `supported` has a done run, a done replay of that run in another role, and a done review of either. A run unit (`tests` or `replay_of`) clocked out done must report a `run-output` in history_entry.produced. | |
| follow_ups | No | Work this unit discovered (#260): each item becomes a todo unit appended to plan.units in the given order, on the same write. Refused whole, nothing written, on a duplicate id, a brief that is not a file, an unknown dependency or a cycle (the planner's sentence), or a role job.roles does not declare. | |
| handoff_patch | Yes | Shallow-merged into `handoff`: only these four keys exist there, and a key outside them is refused by the schema check. Required; `{}` leaves `handoff` unchanged. | |
| history_entry | Yes | Pushed onto the 5-entry `history` ring; `unit` and `outcome` are required, `notes` optional, other keys pass through. |
Output Schema
| Name | Required | Description |
|---|---|---|
| log | No | Path of the <checkpoint>.log.jsonl the accounting line was appended to. |
| unit | No | The unit clocked out. |
| cursor | No | plan.cursor after the move. |
| reason | No | With result `error`: one sentence saying why the call was refused. |
| result | Yes | `ok` when the checkpoint was rewritten; `error` with `reason` when the call was refused and nothing was written. |
| status | No | The status it now holds. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already mark this as a non-read-only, destructive, non-idempotent operation, and the description adds substantial context: schema validation before a single atomic write, failure semantics with {"result": "error", "reason": ...}, cursor advancement rules, history-ring behavior, attempts truncation, and accounting gates. This gives the agent a clear picture of what is mutated and what happens on failure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense and long, but it is front-loaded with the core action and every sentence carries operational detail for a complex 10-parameter mutation tool. It could be split into clearer sections, but it avoids filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the complexity of the tool, the nested schema, the annotations, and the presence of an output schema, the description is complete enough for correct invocation. It covers the atomic write model, required arguments, cursor rules, accounting constraints, follow-up creation, agreements, and role/model validation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all parameters in detail. The description still adds useful cross-parameter meaning, especially the required handoff_patch/history_entry contract, cursor behavior, accounting-line contents, follow_ups and agreements effects, and the job.roles model gate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence states a specific verb and resource: clock out a shift-work unit and record that it finished, then enumerates the concrete mutation effects. It explicitly distinguishes the sibling tools by saying shiftwork_clock_in briefs, this tool closes, and shiftwork_status reads what both wrote.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description says exactly when to call it: once per unit after its agent returns. It also names the related alternative tools and explains the unit_id prerequisite, including which units are accepted and which are refused.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
shiftwork_draftShift work: draftA
Shift-work draft: turn tickets into a new job โ a checkpoint that validates against the checkpoint schema, plus one brief file per ticket โ so step 1 of the shift-work loop is not typed by hand. Call it once, when a job's tickets are settled and before the first shiftwork_clock_in; it never edits a job in progress (that is the planner's edit, or shiftwork_clock_out's follow_ups), and work_plan is the read-only answer when all you want is the batches. Every ticket needs a non-empty id, title, role, verify, data_shape (the types, fields, files or messages the unit produces or consumes โ a ticket without one is not ready to execute) and brief; the first missing one is refused by name. The graph is work_plan's: its refusals come back verbatim, batches and width are its answer, and cursor is the first ready unit, the one shiftwork_clock_out would pick. With job.roles declared, a ticket role it does not name is refused. Each ticket becomes a todo unit with brief_path <briefs_dir>/.md; any other ticket key (touches, reviews, irreversible, ...) is copied onto the unit, and the composed document must pass the checkpoint schema and shiftwork_plan's graph checks, or the schema's sentence is the refusal. write is false by default: the document and every brief's text come back and nothing is touched. With write: true the briefs and then the checkpoint are written by temp file + rename under .lock; an existing checkpoint, or an existing brief with other bytes, is refused before anything is written, and a failure part-way names what landed in written. It never runs git, never runs verify and never reads the working tree: repo is taken as given. Refusals are {"result": "error", "reason"}, never exceptions.
| Name | Required | Description | Default |
|---|---|---|---|
| job | Yes | The checkpoint's `job` object, copied as given: id, goal, done_definition, constraints, and optional roles, budget or agreements. | |
| repo | Yes | state.repo as given: {branch, head_sha, dirty}. Never read from git. | |
| write | No | true writes the briefs and the checkpoint; false or absent is a dry run that touches nothing. | |
| handoff | No | Optional do_not, open_questions and notes for the checkpoint's handoff; next_action is always written as `Read <cursor brief> and clock in <cursor>.` | |
| tickets | Yes | One per unit, in plan order. Keys other than the six required ones, depends_on and priority are copied onto the unit and judged by the checkpoint schema. | |
| artifacts | No | {path, role} entries (spec, plan) appended to state.artifacts after one brief entry per unit. | |
| briefs_dir | No | Directory of the brief files, also the prefix of every brief_path; default .shiftwork/briefs. | |
| checkpoint | No | Where the checkpoint is written with write: true, absolute or relative to the server's working directory; default .shiftwork/checkpoint.json. |
Output Schema
| Name | Required | Description |
|---|---|---|
| width | No | work_plan's width: the widest batch. |
| briefs | No | One per unit: its id, its brief_path, and `bytes`, the file's full UTF-8 text. |
| cursor | No | plan.cursor: the first unit of the first batch. |
| reason | No | The refusal sentence, when result is `error`. |
| result | Yes | `draft` on success; `error` with `reason` for the first refusal. |
| batches | No | work_plan's batches over the tickets, priorities included. |
| written | No | Paths written by this call, briefs first and the checkpoint last; [] on a dry run, and on a write-side refusal the paths that landed before it. |
| checkpoint | No | The composed checkpoint document, valid against assets/schemas/shiftwork-checkpoint.json. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare this is a non-read-only, non-destructive, non-idempotent, closed-world write, and the description adds substantial detail beyond them. It explains the dry-run default, write:true temp-file-and-rename behavior under a lock, refusal on existing checkpoints or differing briefs, partial-failure reporting in `written`, and that it never runs git, verify, or reads the working tree. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with purpose and usage, and nearly every sentence carries behavioral or validation detail rather than filler. It is dense as one long paragraph; splitting write semantics and validation into short sections would improve scanability without adding length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an eight-parameter tool with nested ticket objects, an output schema, and write behavior, the description covers defaults, refusal shape, write atomicity, schema validation, and what the tool does not touch. An agent has enough information to invoke it correctly without opening the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the description adds validation and relational meaning beyond the schema: every ticket needs non-empty id, title, role, verify, data_shape, and brief, with the first missing one refused by name. It also explains role checks against job.roles, brief_path composition, cursor derivation, and the dry-run default for `write`.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: turn tickets into a new job consisting of a schema-valid checkpoint plus one brief file per ticket. It names the exact stage of the shift-work loop and distinguishes itself from planner edits, clock_out follow_ups, and the read-only work_plan.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says to call it once, when a job's tickets are settled and before the first shiftwork_clock_in. It also states when not to use it (a job in progress) and names the alternatives for those cases: the planner's edit, shiftwork_clock_out's follow_ups, and work_plan for read-only batches.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
shiftwork_planShift work: planARead-only
Shift-work plan: which units of a checkpoint may run NOW, and in what waves โ the batches its depends_on graph permits, computed read-only from the file. Call it before dispatching a wave: ready (the first batch) is what shiftwork_clock_in will brief, and width is how many agents the widest wave needs. A ready member beyond the cursor is clockable only once clock-in has briefed it; without that brief, clock-out refuses it. Use shiftwork_status instead when you only want progress counts, and work_plan when the graph is yours to hand over as nodes rather than a checkpoint on disk โ this tool reads the checkpoint's own plan.units. Units already done or dropped count as satisfied and are left out of every batch; todo, in_progress and blocked stay in. Order inside a batch is plan.units order, because the checkpoint schema has no priority field. It never moves plan.cursor โ the cursor is echoed unchanged so the pointer and the batch view can be read side by side โ and never writes anything. Refusals are structured, never exceptions: work_plan's sentences for a duplicate id, an unknown dependency or a cycle, and shiftwork_status's for a checkpoint it cannot read; a plan whose units are all terminal answers an empty ready, which is an answer, not a refusal. depends_on says nothing about two units writing the same file; a unit's optional touches (repo-relative paths it will WRITE) does: no batch holds two units sharing a path, a unit held out of one is reported in held: [{id, batch, by, path}] (absent when no unit declares touches), and a bad path is refused with work_plan's sentence. A unit whose reviews names a unit that is undeclared, not in its depends_on, or in its own role is refused: unit <id> reviews <r> .... With job.budget declared it adds budget (spent, remaining, per_unit, affordable).
| Name | Required | Description | Default |
|---|---|---|---|
| checkpoint | Yes | Path to the shift-work checkpoint JSON file (by convention .shiftwork/checkpoint.json in the repo the job works on), absolute or relative to the server's working directory. The same file shiftwork_plan, shiftwork_clock_in and shiftwork_clock_out take; it must validate against assets/schemas/shiftwork-checkpoint.json. |
Output Schema
| Name | Required | Description |
|---|---|---|
| held | No | Present only when a unit declares `touches`: one {id, batch, by, path} per unit held out of batch `batch` because `by`, admitted there, already writes `path`. |
| ready | No | batches[0] โ the units clock_in may brief now; [] when every unit is done or dropped. |
| width | No | The largest batch length: the widest fan-out. |
| budget | No | Present only when job.budget is declared: the limit, what the log records as spent, units in flight (each reserving one unit and one per-unit estimate), what remains, the per-unit token estimate (the job's lower-median line, 60000 before any) and how many more units fit. |
| cursor | No | plan.cursor, echoed unchanged. |
| reason | No | With result `error`: one sentence saying why the call was refused. |
| result | Yes | `plan` on success; `error` with `reason` when the checkpoint cannot be read or its graph cannot batch. |
| winner | No | With ranking: the first candidate's id, null when none is validated. |
| batches | No | Unit ids by wave: batch k holds every non-terminal unit whose dependencies all sit in batches below k. |
| ranking | No | Present only for job.kind experimentation (#265): validated candidates as {id, score, sha}, score in experiment.direction then id; score is the reported string. A candidate is validated when a done unit that reviews it reported a validated-sha equal to the commit it submitted. |
| sequence | No | The batches flattened in order. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint and openWorldHint=false; the description adds substantial behavior beyond that: never moves plan.cursor, never writes, refusals are structured rather than exceptions, terminal units are treated as satisfied, and a bad `touches` path or invalid `reviews` reference is refused with a named sentence. Output fields (`ready`, `width`, `held`, `budget`) and their absence conditions are also disclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose and the routing guidance to siblings are front-loaded in the first two sentences, which is exactly right. It is a long, dense block afterward, and a few clauses (repeated refusal-by-reference to work_plan's and shiftwork_status's sentences) could be tightened, but nearly every sentence carries operational information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only planner with one parameter and an output schema, the description covers everything an agent needs: batch composition rules, ordering, cursor behavior, empty-ready semantics, holds from path conflicts, review validation, and budget output. Nothing material is left to inference.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single `checkpoint` parameter is well documented in the schema, so the baseline would be 3. The description adds real meaning by explaining which part of that file is consumed (plan.units, depends_on, touches, reviews, job.budget) and that cursor is echoed unchanged.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb and resource: computes which units of a checkpoint may run now and in what waves, derived read-only from the depends_on graph. It immediately distinguishes itself from shiftwork_status and work_plan, so an agent can route without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit call point ("Call it before dispatching a wave") and names both alternatives with the condition that selects each: shiftwork_status for progress counts only, work_plan when the graph is supplied as `nodes` rather than read from disk. It also explains the downstream dependency on shiftwork_clock_in and clock-out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
shiftwork_statusShift work: statusARead-only
Shift-work status: where ONE checkpoint file stands โ units counted by status, the unit plan.cursor points at, how many handoff.open_questions are waiting, and the last history entry. Call it first when resuming a job or answering 'where is job X'; it is the cheap read before any clock-in. It does not say WHICH units may run now โ that is shiftwork_plan, the dependency batches โ and it says nothing about the server itself, which is bantamkit_status. Every fact is read from the checkpoint on this call, nothing is cached, and a checkpoint that is missing, not valid JSON or failing the checkpoint schema answers {"result": "error", "reason": ""} instead of a partial summary โ the same refusals shiftwork_plan and shiftwork_clock_in give for that file. With search, it also returns the lines of jobs Retention archived under the checkpoint's .shiftwork/archive/ that hold every word of the query. Opens only the checkpoint (and that archive directory when search is given); writes nothing, not even the log shiftwork_clock_in appends to.
| Name | Required | Description | Default |
|---|---|---|---|
| search | No | Also search jobs Retention archived under this checkpoint's .shiftwork/archive/: every line holding all the query's words, as {job, file, line}. | |
| checkpoint | Yes | Path to the shift-work checkpoint JSON file (by convention .shiftwork/checkpoint.json in the repo the job works on), absolute or relative to the server's working directory. The same file shiftwork_plan, shiftwork_clock_in and shiftwork_clock_out take; it must validate against assets/schemas/shiftwork-checkpoint.json. |
Output Schema
| Name | Required | Description |
|---|---|---|
| units | No | Unit count per status, keys among todo, in_progress, done, blocked, dropped; a status no unit holds is absent. |
| cursor | No | The unit id plan.cursor points at. |
| reason | No | With result `error`: one sentence saying why the call was refused. |
| result | Yes | `status` on success; `error` when the checkpoint could not be read or failed the schema, with `reason`. |
| search | No | Only when `search` was given: every archived line holding all its words, as {job, file, line}. |
| last_history | No | The newest entry of the 5-entry history ring ({unit, outcome, notes?, ...}), or null when the ring is empty. |
| open_questions | No | How many handoff.open_questions are waiting; non-zero means the next clock-in escalates. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly/openWorld annotations, it discloses that every fact is read fresh on this call (no caching), that invalid or missing checkpoints return a structured {"result": "error", "reason": ...} instead of a partial summary, and that it opens only the checkpoint (plus the archive dir when search is given) and writes nothing, not even a log line.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Content is front-loaded with purpose, then usage, exclusions, behavior, and side effects, and nearly every clause carries information. It is dense and long-winded in places (the error/refusal sentence and the search aside), slightly hurting scanability, but nothing is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only status tool with an output schema, the description covers everything an agent needs: what is returned, when to call it, which siblings to use instead, failure semantics, and side effects. No material gap remains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so both parameters are already fully documented in the schema. The description's search sentence largely restates the schema text, adding only the note about opening the archive directory; with the schema doing the heavy lifting, baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('where ONE checkpoint file stands') and enumerates the exact facts returned (units by status, plan.cursor unit, handoff.open_questions count, last history entry). It explicitly demarcates itself from siblings: shiftwork_plan for runnable units and bantamkit_status for the server.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit trigger ('Call it first when resuming a job or answering where is job X; it is the cheap read before any clock-in') plus explicit exclusions routed to named alternatives. Nothing about when to pick this versus shiftwork_plan or bantamkit_status is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
skill_auditAudit the skill catalogueARead-only
Price the skill catalogue every session pays for, and name the collisions in it. A skill's description: frontmatter is loaded into the agent's context in EVERY session; its body is read only when the skill is invoked, so the descriptions are the standing bill. Call it before adding or retiring a skill, or when two skills keep firing on the same phrase. Walks root for <marketplace>/<plugin>/<version>/skills/<name>/SKILL.md and answers one JSON document: roots, skills (counted), catalogue_bytes (UTF-8 bytes of their description: values), findings and omissions. A degraded server adds the footer as its last key, bantamkit_degraded. Read findings first โ each is {kind, severity, skills, detail}: shared-trigger-phrase (high; two skills quote the same literal phrase, so which fires is a coin flip), catalogue-over-budget (high; only when budget is given), never-invoked (low; only when usage is given), frontmatter-malformed and name-mismatch (medium). Then omissions: counted skills plus omissions account for every SKILL.md found, each {subject, count, size, what} with subject one of plugin-not-enabled, duplicate-skill, stale-version, unreadable-file, unparsed-frontmatter. The tool reads nothing but root; the three facts a directory cannot answer come from the caller โ enabled (plugin ids the host has switched on, <plugin>@<marketplace>; the cache also holds disabled plugins, so without it the bill is inflated), usage (call counts per skill) and versions (the version directory the host serves per plugin; a plugin absent from it gets the directory name that sorts LAST in byte order, which is wrong for 10.0.0 against 9.0.0). Refused with their own sentence, never as a partial answer: an unknown check, a negative budget, an EMPTY root, a root that is missing or is a file. Full rules in docs/skill-audit.md.
| Name | Required | Description | Default |
|---|---|---|---|
| root | Yes | Directory to scan for SKILL.md files, absolute or relative to the server's working directory. It must name a directory: the empty string is refused rather than resolved, because resolving it would audit whatever directory the server happens to be standing in | |
| check | No | Which family of findings to report; default `all`. `skills`, `catalogue_bytes` and `omissions` are reported whatever this says | |
| usage | No | Skill id (`<plugin>:<name>`) to call count, the caller's own measurement; a counted skill absent from a supplied map is treated as zero | |
| budget | No | Catalogue byte budget; `catalogue-over-budget` is only reported when this is given | |
| enabled | No | Plugin ids that are switched on, `<plugin>@<marketplace>`; when given only skills under those plugins are counted, when omitted every skill under `root` counts | |
| versions | No | Plugin id (`<plugin>@<marketplace>`) to the version directory name the host actually serves; when a plugin is named here that directory is resolved whatever byte order would have said, and a plugin absent from it falls back to byte order |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark it read-only and closed-world, and the description adds substantial context beyond that: the exact walk pattern, the JSON keys returned, the degraded-server footer key, and the four refusal conditions (unknown check, negative budget, empty/missing/file root). It stops short of describing pagination or size limits, but the disclosure is far richer than the annotations alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Long, but front-loaded with purpose and dense with non-redundant detail (findings kinds, omissions accounting, refusal rules), each sentence carrying weight. Some of the output-schema content (`findings`, `omissions` shapes) is repeated, which trims the score from 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-parameter tool with nested objects and an output schema, this covers everything: what is scanned, what each optional input contributes, how degraded servers signal themselves, and that refusals are total rather than partial. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description supplies genuine semantics the schema does not: why omitting `enabled` inflates the bill (the cache holds disabled plugins), and that `versions` exists specifically to fix byte-order misresolution such as `10.0.0` against `9.0.0`. These are caller-facing semantic hints, not restatements.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource โ pricing the skill catalogue and naming collisions โ with a clear mechanism (the `description:` frontmatter being the standing bill). No sibling tool covers anything similar, so differentiation is implicit but unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit trigger conditions: 'Call it before adding or retiring a skill, or when two skills keep firing on the same phrase.' This tells the agent when the tool is worth invoking rather than leaving it to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
token_ledgerToken ledgerARead-only
What a session actually cost, read off the host's own transcripts. Walks root for *.jsonl transcripts โ the host writes <cwd-slug>/<session>.jsonl and <cwd-slug>/<session>/subagents/*.jsonl beneath ~/.claude/projects โ and answers ONE JSON document: root, transcripts, lines, requests (distinct API requests), totals (the four classes: input_tokens, cache_creation_input_tokens, cache_read_input_tokens, output_tokens), sessions (one row each), omissions, and cost only when model is given. A degraded server adds the footer as its last key, bantamkit_degraded. Call it to answer 'what did this work cost' from the API's own usage blocks; nothing is estimated. Point root at ONE project's slug directory: there is no time window or summary mode, so a whole-corpus call answers a row per session ever recorded and blows the host's tool-result cap. The four classes are priced separately: on a real prompt 98.1 % of the tokens were cache_read. Counting is per requestId, never per line: one response is written as several records with the same usage, so the dedupe spans the whole walk and every later copy is a duplicate-request omission. A subagent's transcript carries its PARENT's sessionId, so its cost lands in the parent row as sidechain_requests. lines equals requests plus the sum of every omission's count, always. An omission is {subject, count, what}; what names the first site as <relpath>:<line> plus and N more. cost needs a rate: the shipped price table has none, so {"unavailable": "no rate recorded for model ..."} is the NORMAL answer, not an error; a rate enters through $BANTAMKIT_PRICES or prices. Refused with their own sentence: an EMPTY root, a root that is missing or is a file, an EMPTY model, and a token total above 2**53-1. The record rule and the nine omission subjects: docs/ledger.md.
| Name | Required | Description | Default |
|---|---|---|---|
| root | Yes | Directory of host transcripts to walk, absolute or relative to the server's working directory; `~/.claude/projects` is where the host keeps them on this machine. It must name a directory: the empty string is refused rather than resolved, because resolving it would read whatever directory the server happens to be standing in | |
| model | No | Model to price the totals as, e.g. the id an assistant record's `message.model` carries. Omit it and no `cost` key is reported at all; name one with no recorded rate and `cost` is `{"unavailable": ...}`, which is what the shipped table answers for every model | |
| prices | No | Price table to read instead of `$BANTAMKIT_PRICES` or the shipped one; only consulted when `model` is given. A malformed table is a configuration fault and stops with its own sentence, rather than being reported as an unpriced model |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover readOnly/openWorld; the description adds substantial behavior beyond them: per-requestId dedupe spanning the whole walk, duplicate-request omissions, subagent cost attributed to the parent as sidechain_requests, the invariant lines = requests + omission counts, the bantamkit_degraded footer, and the four explicit refusal cases. This is far more than the annotations convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose, scoping rule, and misuse warning are front-loaded and every sentence carries information. It is dense and jargon-heavy (bantamkit_degraded, relpath:line omission format), which pushes against readability, but there is essentially no filler to cut.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-input aggregation tool with an output schema present, the definition covers invocation constraints, edge cases, invariants, and failure modes; an agent has everything needed to call it correctly and interpret the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so the baseline is 3, but the description adds real meaning beyond the schema โ the one-project-per-call constraint on `root`, that `prices` is only consulted when `model` is set, and that `{"unavailable": ...}` is the normal cost answer rather than an error.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource framing โ reporting what a session cost from the host's own transcripts โ and names the exact artifacts it walks (*.jsonl under ~/.claude/projects). No sibling tool does anything similar, and the agent can tell immediately what this produces.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit trigger ('Call it to answer "what did this work cost"'), an explicit scoping rule ('Point root at ONE project's slug directory'), and an explicit when-not with the consequence ('there is no time window or summary mode, so a whole-corpus call ... blows the host's tool-result cap').
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
validate_jsonValidate JSON against a schemaARead-only
Validate candidate output text against a JSON Schema and say what is wrong. Answers {valid, feedback}: when invalid, hand feedback back to the model and retry โ it says where the document fails and quotes the offending value, clipped to 200 characters; when valid, feedback is null. Use it on any JSON a model produced before acting on it; for a shift-work checkpoint call shiftwork_status or shiftwork_clock_in instead, which validate against the shipped checkpoint schema themselves. output may be a bare JSON value; otherwise the first fenced code block, else the first object or array in the text, is what gets validated. $schema selects draft-04, draft-06, draft-07, 2019-09 or 2020-12 (the default). $ref resolves only inside the one schema given (#, #/..., #name): a remote reference or an embedded $id is refused with its own sentence, never fetched. pattern is Python re syntax without inline flags, look-behind, backreferences, atomic, possessive or conditional groups or \N{...}, and all of a document's patterns share 500 ms: past that the answer is one refusal sentence naming the pattern that ran out, not a partial validation. Reads nothing but its two arguments and writes nothing.
| Name | Required | Description | Default |
|---|---|---|---|
| output | Yes | The text to validate, as the model returned it: a JSON value, or prose holding a fenced code block or a JSON object/array. | |
| schema | Yes | The JSON Schema object (draft-04 through 2020-12) the output must satisfy; `$ref` may only point inside it. |
Output Schema
| Name | Required | Description |
|---|---|---|
| valid | Yes | True when the extracted JSON satisfies the schema. |
| feedback | Yes | null when valid; otherwise where the document fails and the clipped offending value, to hand back to the model. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover read-only and closed-world; the description goes far beyond them with the retry contract (hand `feedback` back, retry), feedback truncation at 200 characters, null-on-success, $ref confinement with remote refs refused and never fetched, and a 500 ms shared regex budget that yields a refusal rather than partial results. The closing "reads nothing but its two arguments and writes nothing" reinforces the annotation rather than contradicting it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Dense single paragraph but front-loaded: purpose and return shape come first, then routing, then argument conventions, then edge cases. Every sentence carries information an agent needs to call or interpret the tool; nothing is decorative.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-arg validator with an output schema and closed-world annotations, the description covers the retry loop, the failure modes (refused remote refs, regex timeout), and the input acceptance rules. No gap remains that would cause a misinvocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Despite 100% schema coverage, the description adds real semantics the schema lacks: the extraction order for `output` (bare JSON value, else first fenced block, else first object/array), which drafts `$schema` accepts and that 2020-12 is the default, and the allowed `$ref` forms. These change how an agent must shape its arguments.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ("Validate candidate output text against a JSON Schema") and immediately states the answer shape {valid, feedback}. It also distinguishes itself from the sibling checkpoint tools, so an agent can route correctly without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit trigger ("Use it on any JSON a model produced before acting on it") and an explicit exclusion with named alternatives ("for a shift-work checkpoint call shiftwork_status or shiftwork_clock_in instead"). Both the when and the when-not are present.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
work_planWork plan: batch a dependency graphARead-only
Work plan: turn a dependency graph you hand it into the batches that may run in parallel โ pure computation over nodes: no file opened, no path taken, nothing mutated. Use it when the graph is in your hands (tasks with depends_on); use shiftwork_plan when the graph is a shift-work checkpoint on disk, which it reads for you under the same batching rules. Batch k holds every node whose dependencies all sit in batches below k; sequence is the batches flattened; width is the largest batch โ the fan-out an orchestrator must afford. Inside a batch: priority descending, then the order the nodes were given; both halves are contract, so two implementations answer byte-identically. Empty nodes is an ANSWER (no batches, width 0), not a refusal. Four refusals, in this order, one sentence each: nodes[<i>].<field> must be ... (an id that is not a string, depends_on not a list of strings, priority not an integer โ named by position, never coerced), duplicate node id <id>, node <id> depends on <dep>, which no node declares, and the graph has a cycle: <a> -> <b> -> <a>. depends_on encodes logical order, not file contention; give a node touches (repo-relative paths it will WRITE) and no batch ever holds two nodes sharing a path: inside a level, in the same order, a node whose path an admitted node already claims is held for the next batch, and the answer then carries held: [{id, batch, by, path}] (absent when no node declares touches). A path that is empty, absolute, holds a backslash or a ./../empty segment is the fifth refusal, checked with the shape: node <id> touches <json>, which is not a plain relative path.
| Name | Required | Description | Default |
|---|---|---|---|
| nodes | Yes | The graph: one object per task, {id, depends_on?, priority?, touches?}. The order given is the tie-break inside a batch after priority. |
Output Schema
| Name | Required | Description |
|---|---|---|
| held | No | Present only when a node declares `touches`: one {id, batch, by, path} per hold, the node held out of batch `batch` because `by`, admitted there, already writes `path`. |
| width | No | The largest batch length: the widest fan-out. |
| reason | No | With result `error`: one sentence saying why the call was refused. |
| result | Yes | `plan` on success; `error` with `reason` for one of the four refusals. |
| batches | No | Node ids by wave: batch k holds every node whose dependencies all sit in batches below k. |
| sequence | No | The batches flattened in order. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already say readOnlyHint/openWorldHint=false, and the description reinforces rather than contradicts them ('no file opened, no path taken, nothing mutated'). Beyond that it discloses the determinism contract (priority then input order, byte-identical across implementations), the `held` output with its exact shape and the condition under which it is absent, and an ordered list of five refusals with their message shapes โ behavior an agent cannot infer from annotations alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and purity are front-loaded, and nearly every clause carries operative information (batching rule, tie-break order, refusal order, held semantics). It is dense to the point of being a wall of text, and it explains `sequence`/`width` return values despite an output schema existing, which is the main redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given a single nested-graph parameter and a complex batching contract, the description covers ordering, fan-out, contention handling, and all failure modes an agent could hit. The output schema carries the return shape, so the extra prose about `sequence`/`width` is surplus rather than a gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the field-level docs are already in the schema; the description still adds real meaning by stating that `depends_on` encodes logical order rather than file contention, and that `touches` lists paths the node will WRITE, which explains why the two fields interact. It also specifies validation consequences (invalid ids/paths named by position, never coerced) that go beyond the schema's types.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource โ turn a dependency graph into parallel batches โ and immediately bounds it as 'pure computation over `nodes`'. It names the sibling it is not (shiftwork_plan) and states the distinguishing condition (graph in hand vs. checkpoint on disk), so an agent can route between them without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use: 'Use it when the graph is in your hands (tasks with depends_on)', paired with an explicit alternative and its trigger ('use shiftwork_plan when the graph is a shift-work checkpoint on disk'). It also preempts a likely misuse by declaring empty `nodes` an answer rather than a refusal.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.38.0- Changed
shiftwork_clock_in6 fields changed- added
Output schema / properties / agreementsAdded value: +{ + "description": "job.agreements: the working agreements adopted so far through clock_out, carried on every brief; [] when none.", + "items": { + "type": "string" + }, + "type": "array" +} - changed
Output schema / properties / open_questions / descriptionPrevious value: -"With result `escalate`: the questions the orchestrator must put to the user before any unit runs."New value: +"With result `escalate`: the questions the orchestrator must put to the user before any unit runs. An irreversible unit whose verb job.authorized does not grant also escalates (#225), with no open_questions." - added
Output schema / properties / open_questions / items / anyOfAdded value: +[ + { + "description": "A decision question.", + "type": "string" + }, + { + "description": "A question with a kind (#225): {q, kind: relay|decision, probe}. Escalation happens only for a decision or a relay whose probe did not exit 0; an answered relay is logged as a `resolved` ledger event.", + "type": "object" + } +] - removed
Output schema / properties / open_questions / items / typeRemoved value: -"string" - added
Output schema / properties / prior_attemptsAdded value: +{ + "description": "This unit's own attempts, oldest first, at most 3; [] when none. A unit clocked out todo and briefed again is a rework, and this is what it found.", + "type": "array" +} - added
Output schema / properties / upstreamAdded value: +{ + "description": "One {unit, title, status, last} per depends_on id, in depends_on order: last is that unit's newest attempt (status, outcome, notes?, produced?) or null. Always present with a brief.", + "type": "array" +}
- Changed
shiftwork_clock_out11 fields changed- changed
Input schema / properties / accounting / anyOfPrevious value: -[ - { - "additionalProperties": true, - "description": "Per-unit cost. Written verbatim, beside ts/unit/role/status, as one line of <checkpoint>.log.jsonl - the ledger the N-sessions experiment reads. The keys named here have fixed meanings so a reader holding only the ledger knows what each number counts, in which unit, and which model produced it; any other key passes through unchanged, except the five the runtime writes itself โ unit, role, status, ts, event โ which are refused (result=error) before anything is written; a self-reported briefed is overwritten, not refused. Lines written before this schema existed may spell these differently; the schema governs new writes only.", - "properties": { - "cache_read_tokens": { - "description": "Tokens the unit's requests served from prompt cache, kept OUT of `tokens` because they dominate a real session's traffic and would swamp the work signal. Optional: omit it when the harness reports no such figure - a written 0 means the unit read nothing from cache.", - "minimum": 0, - "type": "integer" - }, - "duration_ms": { - "description": "Wall-clock from spawning the subagent to its final message, in whole milliseconds. In a non-null line it is required, and it is the only duration key with a defined meaning: older lines carry `duration`, `duration_min` or `duration_s`, which this schema neither reads nor renames.", - "minimum": 0, - "type": "integer" - }, - "model": { - "description": "The exact identifier of the model the subagent actually ran on (e.g. `claude-sonnet-5`) and nothing else - a caveat about how it was chosen belongs in `note`. Not required here: when the checkpoint's job.roles names this unit's role, clock_out already requires it and refuses a value off that role's list, spelled exactly; a checkpoint with no job.roles leaves it optional. This schema does not change that gate.", - "type": "string" - }, - "note": { - "description": "Free text for what the fields above cannot say: that `tokens` is an estimate, that this is a retry or a second scope under the same unit id, what a harness counter excludes. Anything that is not a model identifier goes here, not in `model`.", - "type": "string" - }, - "tokens": { - "description": "Tokens the unit consumed as the harness's subagent counter reports them: every class it reports (input, output, cache creation) summed, EXCLUDING cache reads. In a non-null accounting line it is required โ a line without it is refused before anything is written; only `accounting: null`, the default, is exempt, and that logs no cost at all. A rounded self-estimate is allowed only if `note` says it is one; a unit whose cost is unknown cannot be compared with any other.", - "minimum": 0, - "type": "integer" - }, - "tool_uses": { - "description": "Tool calls the subagent made, as the harness counts them. Optional; it is the denominator that makes `tokens` comparable across units of different size (per-unit cost is linear in tool calls, not in unit length).", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "tokens", - "duration_ms" - ], - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "additionalProperties": true, + "description": "Per-unit cost. Written verbatim, beside ts/unit/role/status, as one line of <checkpoint>.log.jsonl - the ledger the N-sessions experiment reads. The keys named here have fixed meanings so a reader holding only the ledger knows what each number counts, in which unit, and which model produced it; any other key passes through unchanged, except the six the runtime writes itself โ unit, role, status, ts, event, agent_id โ which are refused (result=error) before anything is written; a self-reported briefed is overwritten, not refused. Lines written before this schema existed may spell these differently; the schema governs new writes only.", + "properties": { + "cache_read_tokens": { + "description": "Tokens the unit's requests served from prompt cache, kept OUT of `tokens` because they dominate a real session's traffic and would swamp the work signal. Optional: omit it when the harness reports no such figure - a written 0 means the unit read nothing from cache.", + "minimum": 0, + "type": "integer" + }, + "duration_ms": { + "description": "Wall-clock from spawning the subagent to its final message, in whole milliseconds. In a non-null line it is required, and it is the only duration key with a defined meaning: older lines carry `duration`, `duration_min` or `duration_s`, which this schema neither reads nor renames.", + "minimum": 0, + "type": "integer" + }, + "estimate": { + "description": "true when `tokens` is a guess rather than the host's figure. With job.budget declared, a line reporting 0 tokens is refused unless it carries estimate: true.", + "type": "boolean" + }, + "model": { + "description": "The exact identifier of the model the subagent actually ran on (e.g. `claude-sonnet-5`) and nothing else - a caveat about how it was chosen belongs in `note`. Not required here: when the checkpoint's job.roles names this unit's role, clock_out already requires it and refuses a value off that role's list, spelled exactly; a checkpoint with no job.roles leaves it optional. This schema does not change that gate.", + "type": "string" + }, + "note": { + "description": "Free text for what the fields above cannot say: that `tokens` is an estimate, that this is a retry or a second scope under the same unit id, what a harness counter excludes. Anything that is not a model identifier goes here, not in `model`.", + "type": "string" + }, + "tokens": { + "description": "Tokens the unit consumed as the harness's subagent counter reports them: every class it reports (input, output, cache creation) summed, EXCLUDING cache reads. In a non-null accounting line it is required โ a line without it is refused before anything is written; only `accounting: null`, the default, is exempt, and that logs no cost at all. A rounded self-estimate is allowed only if `note` says it is one; a unit whose cost is unknown cannot be compared with any other.", + "minimum": 0, + "type": "integer" + }, + "tool_uses": { + "description": "Tool calls the subagent made, as the harness counts them. Optional; it is the denominator that makes `tokens` comparable across units of different size (per-unit cost is linear in tool calls, not in unit length).", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "tokens", + "duration_ms" + ], + "type": "object" + }, + { + "type": "null" + } +] - added
Input schema / properties / agent_idAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The `agentId` the Agent tool's result returned for this unit's subagent (#266), copied verbatim: 1 to 64 letters, digits, - or _, or nothing is written. It goes on the accounting line, never into the checkpoint, and is how the skill miner attributes the skill uses the hook logged inside that subagent to this unit. Omit it for a unit run inline.", + "title": "Agent Id" +} - added
Input schema / properties / agreementsAdded value: +{ + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "adopt": { + "description": "Agreements to add to job.agreements; text already listed is left as it is.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "retire": { + "description": "Agreements to remove, by exact text; one not on the list is refused.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "A lesson this unit wants every later brief to carry (#261): retire by exact text, then adopt. Refused, nothing written, for text not on the list or a result over 20; each applied change is recorded in retro[].", + "title": "Agreements" +} - added
Input schema / properties / conclusionAdded value: +{ + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "evidence": { + "description": "Refs to what was observed: run outputs, commits, files.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "outcome": { + "description": "`inconclusive` and `stopped` are never refused; `resolved` needs the evidence below.", + "enum": [ + "resolved", + "inconclusive", + "stopped" + ], + "type": "string" + }, + "supported": { + "description": "The hypothesis id the evidence supports; required for `resolved`.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "outcome", + "evidence" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "An investigation's answer (#264), written to job.conclusion. Only a unit with `concludes: true` may send it; `resolved` is refused unless `supported` has a done run, a done replay of that run in another role, and a done review of either. A run unit (`tests` or `replay_of`) clocked out done must report a `run-output` in history_entry.produced.", + "title": "Conclusion" +} - added
Input schema / properties / follow_upsAdded value: +{ + "anyOf": [ + { + "items": { + "properties": { + "brief_path": { + "description": "Its brief, a file that must already exist, relative to the repo (the nearest ancestor of the checkpoint holding .git).", + "minLength": 1, + "type": "string" + }, + "depends_on": { + "description": "Unit ids it waits for; absent means the unit being clocked out.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "id": { + "description": "The new unit's id; must not be a unit already, nor repeat in this list.", + "minLength": 1, + "type": "string" + }, + "role": { + "description": "Who runs it; when job.roles is declared it must be one of its keys.", + "enum": [ + "planner", + "implementer", + "reviewer" + ], + "type": "string" + }, + "title": { + "description": "One line naming the work.", + "minLength": 1, + "type": "string" + }, + "touches": { + "description": "Repo-relative paths it will write (#257).", + "items": { + "type": "string" + }, + "type": "array" + }, + "verify": { + "description": "Command proving it done; absent means the clocked-out unit's verify.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "id", + "title", + "role", + "brief_path" + ], + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Work this unit discovered (#260): each item becomes a todo unit appended to plan.units in the given order, on the same write. Refused whole, nothing written, on a duplicate id, a brief that is not a file, an unknown dependency or a cycle (the planner's sentence), or a role job.roles does not declare.", + "title": "Follow Ups" +} - changed
Input schema / properties / handoff_patch / properties / open_questions / descriptionPrevious value: -"The autonomy switch: empty = driver keeps cycling, non-empty = STOP and ask."New value: +"The autonomy switch: empty = driver keeps cycling, non-empty = STOP and ask. A bare string is a decision; a `relay` question carries a `probe` that clock_in runs instead of escalating (#225)." - added
Input schema / properties / handoff_patch / properties / open_questions / items / anyOfAdded value: +[ + { + "description": "A decision question: the user's to answer.", + "minLength": 1, + "type": "string" + }, + { + "additionalProperties": false, + "properties": { + "kind": { + "description": "`decision` needs the user; `relay` is answered by its probe.", + "enum": [ + "relay", + "decision" + ], + "type": "string" + }, + "probe": { + "description": "Shell command run from the repo root by clock_in; exit 0 answers the relay. Required on a relay, or nothing is written.", + "minLength": 1, + "type": "string" + }, + "q": { + "description": "The question.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "q", + "kind" + ], + "type": "object" + } +] - removed
Input schema / properties / handoff_patch / properties / open_questions / items / minLengthRemoved value: -1 - removed
Input schema / properties / handoff_patch / properties / open_questions / items / typeRemoved value: -"string" - changed
Input schema / properties / history_entry / properties / notes / descriptionPrevious value: -"Optional free text for the next session reading the ring."New value: +"Optional free text for the next session reading the ring, and recorded in the unit's attempt (#259): at most 2000 code points." - added
Input schema / properties / history_entry / properties / producedAdded value: +{ + "description": "What the unit produced, [{kind, ref}] (a commit, a file, a run output). Recorded with the unit's attempt, and handed to every unit that depends on it as upstream[].last.", + "items": { + "properties": { + "kind": { + "description": "What sort of thing: commit, file, pr, run-output.", + "minLength": 1, + "type": "string" + }, + "ref": { + "description": "Where it is: a SHA, a path, a URL.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "kind", + "ref" + ], + "type": "object" + }, + "type": "array" +}
- Added
shiftwork_draft - Changed
shiftwork_plan4 fields changed- added
Output schema / properties / budgetAdded value: +{ + "description": "Present only when job.budget is declared: the limit, what the log records as spent, units in flight (each reserving one unit and one per-unit estimate), what remains, the per-unit token estimate (the job's lower-median line, 60000 before any) and how many more units fit.", + "type": "object" +} - added
Output schema / properties / heldAdded value: +{ + "description": "Present only when a unit declares `touches`: one {id, batch, by, path} per unit held out of batch `batch` because `by`, admitted there, already writes `path`.", + "items": { + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / rankingAdded value: +{ + "description": "Present only for job.kind experimentation (#265): validated candidates as {id, score, sha}, score in experiment.direction then id; score is the reported string. A candidate is validated when a done unit that reviews it reported a validated-sha equal to the commit it submitted.", + "items": { + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / winnerAdded value: +{ + "description": "With ranking: the first candidate's id, null when none is validated.", + "type": [ + "string", + "null" + ] +}
- Changed
work_plan3 fields changed- changed
Input schema / properties / nodes / descriptionPrevious value: -"The graph: one object per task, {id, depends_on?, priority?}. The order given is the tie-break inside a batch after priority."New value: +"The graph: one object per task, {id, depends_on?, priority?, touches?}. The order given is the tie-break inside a batch after priority." - added
Input schema / properties / nodes / items / properties / touchesAdded value: +{ + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "description": "Repo-relative POSIX paths this node will WRITE, compared as exact strings. Optional: absent or null claims nothing. Two nodes sharing a path never land in one batch" +} - added
Output schema / properties / heldAdded value: +{ + "description": "Present only when a node declares `touches`: one {id, batch, by, path} per hold, the node held out of batch `batch` because `by`, admitted there, already writes `path`.", + "items": { + "type": "object" + }, + "type": "array" +}
4 tool updates
v0.37.1- Added
memory_queries - Changed
memory_recall3 fields changed- added
Input schema / properties / fileAdded value: +{ + "description": "A tracked file, or the shortest unique suffix of one (store.ts, memory/store.ts). Returns the facts linked to it and when it was last touched; query is then ignored.", + "type": "string" +} - added
Input schema / properties / include_archivedAdded value: +{ + "default": false, + "description": "Also search facts compaction moved to archive/. Archived matches come after the live ones, tagged [archived].", + "type": "boolean" +} - changed
Input schema / requiredPrevious value: -[ - "query" -]New value: +[]
- Changed
memory_save2 fields changed- added
Input schema / properties / keywordsAdded value: +{ + "description": "Words the user will type when they need this fact, in the user's own language as well as English (e.g. Thai and English terms)", + "items": { + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / queriesAdded value: +{ + "description": "The prompts a user would type when this fact is what they need: three in the user's language and three in English, at most 8, each at most 200 characters", + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
shiftwork_status2 fields changed- added
Input schema / properties / searchAdded value: +{ + "description": "Also search jobs Retention archived under this checkpoint's .shiftwork/archive/: every line holding all the query's words, as {job, file, line}.", + "type": "string" +} - added
Output schema / properties / searchAdded value: +{ + "description": "Only when `search` was given: every archived line holding all its words, as {job, file, line}.", + "items": { + "properties": { + "file": { + "description": "Path of the archived file, relative to the job.", + "type": "string" + }, + "job": { + "description": "The archived job the line belongs to.", + "type": "string" + }, + "line": { + "description": "1-based line number within that file.", + "type": "integer" + } + }, + "type": "object" + }, + "type": "array" +}
8 tool updates
v0.36.2- Changed
memory_recall1 field changed- added
Input schema / properties / outcomeAdded value: +{ + "description": "Keep only facts saved with this outcome โ `error` answers \"what failed before\", `success` \"what worked\". Facts with no outcome are dropped from the answer; omit it for the normal search.", + "enum": [ + "error", + "success" + ], + "type": "string" +}
- Changed
memory_save3 fields changed- added
Input schema / properties / outcomeAdded value: +{ + "description": "What the lesson records: `error` (a failure to avoid) or `success` (a way that worked). Only on feedback and project facts; refused on user and reference. Read back by `bantamkit-memory list --outcome` and by memory_recall's `outcome` filter.", + "enum": [ + "error", + "success" + ], + "type": "string" +} - added
Input schema / properties / statusAdded value: +{ + "description": "Lifecycle of a project fact. Save an existing name with `done` when the work it describes has shipped; `bantamkit-memory candidates` lists done facts for the operator to archive. Absent means active; `active` is never written.", + "enum": [ + "active", + "done" + ], + "type": "string" +} - added
Input schema / properties / type / descriptionAdded value: +"What kind of fact: `user` (who the user is, their role and preferences), `feedback` (a correction or confirmed way of working, with the why), `project` (ongoing work, a decision, a constraint), `reference` (a URL, ticket or dashboard pointer)."
- Changed
shiftwork_clock_in4 fields changed- added
Input schema / properties / checkpoint / descriptionAdded value: +"Path to the shift-work checkpoint JSON file (by convention .shiftwork/checkpoint.json in the repo the job works on), absolute or relative to the server's working directory. The same file shiftwork_plan, shiftwork_clock_in and shiftwork_clock_out take; it must validate against assets/schemas/shiftwork-checkpoint.json." - added
Input schema / properties / unit_id / descriptionAdded value: +"The unit to brief instead of plan.cursor; it must be in shiftwork_plan's `ready` for this checkpoint. Omit it, or pass null, to brief the cursor unit." - added
Output schema / propertiesAdded value: +{ + "do_not": { + "description": "handoff.do_not: near-mistakes past sessions made.", + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "description": "state.artifacts: the files the job has produced so far.", + "type": "array" + }, + "handoff": { + "description": "The checkpoint's handoff block: next_action, open_questions, do_not, notes.", + "type": "object" + }, + "invariants": { + "description": "job.constraints: what every unit must keep true.", + "items": { + "type": "string" + }, + "type": "array" + }, + "open_questions": { + "description": "With result `escalate`: the questions the orchestrator must put to the user before any unit runs.", + "items": { + "type": "string" + }, + "type": "array" + }, + "reason": { + "description": "With result `escalate`, `success` or `error`: one sentence saying why.", + "type": "string" + }, + "result": { + "description": "`brief` (the unit is yours to spawn), `escalate` (stop and ask: see `reason` and `open_questions`), `success` (every unit done or dropped), or `error` with `reason`.", + "type": "string" + }, + "role": { + "description": "The unit's role, the key job.roles may bind models to.", + "type": "string" + }, + "unit": { + "description": "The plan.units entry briefed: id, role, status, depends_on and its own fields.", + "type": "object" + } +} - added
Output schema / requiredAdded value: +[ + "result" +]
- Changed
shiftwork_clock_out7 fields changed- added
Input schema / properties / accounting / descriptionAdded value: +"Per-unit cost line, or null (the default) to log a line with no cost figures at all. A non-null line must carry `tokens` and `duration_ms`; `model` is required when job.roles names this unit's role. The object shape is described below." - added
Input schema / properties / checkpoint / descriptionAdded value: +"Path to the shift-work checkpoint JSON file (by convention .shiftwork/checkpoint.json in the repo the job works on), absolute or relative to the server's working directory. The same file shiftwork_plan, shiftwork_clock_in and shiftwork_clock_out take; it must validate against assets/schemas/shiftwork-checkpoint.json." - added
Input schema / properties / history_entry / properties / notes / descriptionAdded value: +"Optional free text for the next session reading the ring." - added
Input schema / properties / history_entry / properties / outcome / descriptionAdded value: +"What happened, in a few words: done, blocked on X, dropped because Y." - added
Input schema / properties / history_entry / properties / unit / descriptionAdded value: +"The unit this entry is about โ normally the `unit_id` being clocked out." - added
Output schema / propertiesAdded value: +{ + "cursor": { + "description": "plan.cursor after the move.", + "type": "string" + }, + "log": { + "description": "Path of the <checkpoint>.log.jsonl the accounting line was appended to.", + "type": "string" + }, + "reason": { + "description": "With result `error`: one sentence saying why the call was refused.", + "type": "string" + }, + "result": { + "description": "`ok` when the checkpoint was rewritten; `error` with `reason` when the call was refused and nothing was written.", + "type": "string" + }, + "status": { + "description": "The status it now holds.", + "type": "string" + }, + "unit": { + "description": "The unit clocked out.", + "type": "string" + } +} - added
Output schema / requiredAdded value: +[ + "result" +]
- Changed
shiftwork_plan3 fields changed- added
Input schema / properties / checkpoint / descriptionAdded value: +"Path to the shift-work checkpoint JSON file (by convention .shiftwork/checkpoint.json in the repo the job works on), absolute or relative to the server's working directory. The same file shiftwork_plan, shiftwork_clock_in and shiftwork_clock_out take; it must validate against assets/schemas/shiftwork-checkpoint.json." - added
Output schema / propertiesAdded value: +{ + "batches": { + "description": "Unit ids by wave: batch k holds every non-terminal unit whose dependencies all sit in batches below k.", + "items": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": "array" + }, + "cursor": { + "description": "plan.cursor, echoed unchanged.", + "type": "string" + }, + "ready": { + "description": "batches[0] โ the units clock_in may brief now; [] when every unit is done or dropped.", + "items": { + "type": "string" + }, + "type": "array" + }, + "reason": { + "description": "With result `error`: one sentence saying why the call was refused.", + "type": "string" + }, + "result": { + "description": "`plan` on success; `error` with `reason` when the checkpoint cannot be read or its graph cannot batch.", + "type": "string" + }, + "sequence": { + "description": "The batches flattened in order.", + "items": { + "type": "string" + }, + "type": "array" + }, + "width": { + "description": "The largest batch length: the widest fan-out.", + "type": "integer" + } +} - added
Output schema / requiredAdded value: +[ + "result" +]
- Changed
shiftwork_status3 fields changed- added
Input schema / properties / checkpoint / descriptionAdded value: +"Path to the shift-work checkpoint JSON file (by convention .shiftwork/checkpoint.json in the repo the job works on), absolute or relative to the server's working directory. The same file shiftwork_plan, shiftwork_clock_in and shiftwork_clock_out take; it must validate against assets/schemas/shiftwork-checkpoint.json." - added
Output schema / propertiesAdded value: +{ + "cursor": { + "description": "The unit id plan.cursor points at.", + "type": "string" + }, + "last_history": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ], + "description": "The newest entry of the 5-entry history ring ({unit, outcome, notes?, ...}), or null when the ring is empty." + }, + "open_questions": { + "description": "How many handoff.open_questions are waiting; non-zero means the next clock-in escalates.", + "type": "integer" + }, + "reason": { + "description": "With result `error`: one sentence saying why the call was refused.", + "type": "string" + }, + "result": { + "description": "`status` on success; `error` when the checkpoint could not be read or failed the schema, with `reason`.", + "type": "string" + }, + "units": { + "additionalProperties": { + "type": "integer" + }, + "description": "Unit count per status, keys among todo, in_progress, done, blocked, dropped; a status no unit holds is absent.", + "type": "object" + } +} - added
Output schema / requiredAdded value: +[ + "result" +]
- Changed
validate_json4 fields changed- added
Input schema / properties / output / descriptionAdded value: +"The text to validate, as the model returned it: a JSON value, or prose holding a fenced code block or a JSON object/array." - added
Input schema / properties / schema / descriptionAdded value: +"The JSON Schema object (draft-04 through 2020-12) the output must satisfy; `$ref` may only point inside it." - added
Output schema / propertiesAdded value: +{ + "feedback": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "null when valid; otherwise where the document fails and the clipped offending value, to hand back to the model." + }, + "valid": { + "description": "True when the extracted JSON satisfies the schema.", + "type": "boolean" + } +} - added
Output schema / requiredAdded value: +[ + "valid", + "feedback" +]
- Changed
work_plan3 fields changed- added
Input schema / properties / nodes / descriptionAdded value: +"The graph: one object per task, {id, depends_on?, priority?}. The order given is the tie-break inside a batch after priority." - added
Output schema / propertiesAdded value: +{ + "batches": { + "description": "Node ids by wave: batch k holds every node whose dependencies all sit in batches below k.", + "items": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": "array" + }, + "reason": { + "description": "With result `error`: one sentence saying why the call was refused.", + "type": "string" + }, + "result": { + "description": "`plan` on success; `error` with `reason` for one of the four refusals.", + "type": "string" + }, + "sequence": { + "description": "The batches flattened in order.", + "items": { + "type": "string" + }, + "type": "array" + }, + "width": { + "description": "The largest batch length: the widest fan-out.", + "type": "integer" + } +} - added
Output schema / requiredAdded value: +[ + "result" +]
14 tool updates
v0.36.0- First observed
bantamkit_status - First observed
build_identity - First observed
memory_compact - First observed
memory_dream - First observed
memory_recall - First observed
memory_save - First observed
shiftwork_clock_in - First observed
shiftwork_clock_out - First observed
shiftwork_plan - First observed
shiftwork_status - First observed
skill_audit - First observed
token_ledger - First observed
validate_json - First observed
work_plan
TDQS
Scored across 16 tools
Most tools occupy clearly separate niches, and the descriptions actively police the boundaries (work_plan vs shiftwork_plan, build_identity vs bantamkit_status vs shiftwork_status, memory_compact vs memory_dream each say when to use the other). The remaining ambiguity is real but narrow: the read-only 'plan' pair and the three status/diagnostic tools share vocabulary and could be mispicked without reading the full text. No two tools actually do the same thing.
Everything is snake_case with sensible namespace prefixes (shiftwork_*, memory_*, work_plan), which makes the surface easy to scan. The deviation is word order: memory_save/token_ledger/skill_audit are noun-first while validate_json/build_identity are verb-first, and bantamkit_status is server-prefixed where shiftwork_status is domain-prefixed.
Sixteen tools is on the heavy side, but this is a multi-surface server (shift-work orchestration 6, memory 5, diagnostics 2, plus ledger, audit, validation), and each tool maps to an operation no sibling covers. Nothing looks like filler, though the set is larger than a single-purpose server should ever be.
Shift-work has a full lifecycle (draft, plan, clock-in, clock-out, status) and memory has save/recall/compact/dream/queries, so the main domains are closed loops. Gaps exist but are documented as deliberate (no tool edits a checkpoint in progress, no tool deletes a memory fact, both deferred to operator commands), so an agent rarely hits a dead end it wasn't warned about.
Maintenance
Related MCP Connectors
Persistent engineering memory designed to follow developers across coding agents.
Shared project memory for coding agents: decisions, lessons, guardrails and tasks per repository.
Long-term memory for AI coding agents: durable project facts, recalled by every MCP client.
Shared project memory for AI coding agents: decisions, lessons, risks and tasks in one graph.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenancePersistent memory for AI coding agents1,455 PyPI225Apache 2.0
- AlicenseNot gradedqualityAmaintenanceA persistent, local memory layer for AI coding agents that remembers decisions, bugs, and rules across sessions with three core MCP verbs (recall, remember, search).Apache 2.0
- AlicenseNot gradedqualityDmaintenancePersistent, structured memory for AI coding agents via a git-like filesystem with branch/commit/merge tools, enabling agents to maintain context across sessions.MIT
- AlicenseAqualityBmaintenanceLocal-first memory for AI coding agents using SQLite, enabling persistent recall of decisions, lessons, and project context without cloud or API keys.10136 PyPI288Apache 2.0