protect-context
protect-context · Agent Guardrail Suite
Durable context, real evidence, and a brake pedal for AI coding agents.
protect-context keeps the rules, decisions, and proof-of-work an agent needs in
front of it — then makes scope creep, unverified claims, and repeated failures
visible before they cost you a session.

One project-local .agent-guard/ store · three interfaces backed by the same
logic:
Interface | Use it for | Runtime |
pi extension | Automatic brief injection, status widget, loop blocking, diff warnings, risky-command confirmation | TypeScript, loaded by pi via jiti |
MCP server | Portable model/host access to the same tools | Python 3.10+, |
Python CLI | People, scripts, CI, and hosts without MCP | Python 3.10+ |
The suite is advisory-first. It records evidence and makes unsafe or
repetitive behavior visible; it is not a sandbox and does not prove that a
change is correct. SPEC.md is the frozen v1.0 contract.
Contents
Related MCP server: ContextPact
Why this exists
Long agent sessions fail in boring, expensive ways:
the constraints you stated at minute two are gone by minute forty;
"done" is claimed with no command output behind it;
a "quick fix" quietly rewrites six unrelated files;
decisions get re-litigated because nothing recorded the reasoning;
the same failing command is retried until the context window dies.
protect-context treats those as state problems, not intelligence problems.
A durable store plus three small habits — brief before work, evidence after
work, journal when something is decided — removes most of the failure surface
without turning the agent into a different agent.
What it does
Primitive | What it gives you | Where you meet it |
Brief | Owner task, contract, recent trusted decisions and corrections, a short unverified section for the agent's own notes, open loops, and the last evidence ID — injected before a turn and re-asserted mid-run after a compaction | Automatic in pi; |
Orientation | Bounded repo digest: tree, languages, and conventions files ( | Injected once per digest in pi; |
Evidence ledger | Every verification run recorded as |
|
Loop brake | Identical failing commands are fingerprinted; the Nth attempt is blocked instead of re-run | Hard block in pi and the CLI; reported through MCP |
Scope audit |
| Widget and |
Journal | Durable |
|
Recall | Keyword, tag, type, evidence-id, and command search over the retained journal and evidence window — no embeddings |
|
Risky-command confirm | Nine catastrophic shell patterns get one grouped confirmation instead of none | pi, when a UI is available |
Preflight | Tool presence and versions plus Git worktree detection |
|
Context budget | Watermarks on context usage and hard caps on every injected block | Widget, |
Quick start
Python 3.10+ is required for the CLI and MCP server. Node 22.19+ is required only to validate the TypeScript extension from a checkout — pi supplies the extension's runtime modules, and the extension has zero npm runtime dependencies.
Install the pi extension
Installed from npm. The package declares "pi": { "extensions": ["./extension/index.ts"], "image": "./preview.png" } and the pi-package
keyword required by the Pi package gallery:
pi install npm:protect-context
# Try it for one run without adding it to settings
pi -e npm:protect-contextInstall it through pi, not through npm install. The extension declares pi's
host modules (@earendil-works/pi-ai, @earendil-works/pi-coding-agent,
typebox) as "*" peer dependencies, as Pi requires; Pi suppresses peer
installation for managed npm packages, while a plain npm install protect-context does not. The second path downloads the whole pi runtime and
surfaces advisories from that tree, which belong to the host and not to this
zero-runtime-dependency extension.
Remove any hand-installed copy first — a directory such as
~/.pi/agent/extensions/protect-context or .pi/extensions/protect-context,
or pi remove npm:protect-context if that copy came from npm. Two copies
register the same tool names, and pi drops the second one:
Error: Failed to load extension "…/.pi/npm/node_modules/protect-context/extension/index.ts":
Tool "guard_verify" conflicts with /home/you/.pi/agent/extensions/protect-context/index.tsOne error is reported per tool (guard_verify, guard_note,
guard_contract_check, guard_status, guard_journal, guard_recall), so
nothing from the npm copy loads until the other copy is gone.
Or copy/symlink the checkout into a trusted extension directory:
# Personal
mkdir -p ~/.pi/agent/extensions
cp -r extension ~/.pi/agent/extensions/protect-context
# Project-local (read after the project is trusted)
mkdir -p .pi/extensions
cp -r extension .pi/extensions/protect-contextStart a session and you get a single-line status widget, a brief before the
first turn, and five slash commands. No configuration file is needed: the
extension seeds .agent-guard/config.json with the frozen defaults on first
session start.
Install the MCP server
The MCP server is the PyPI package. With uv installed, uvx protect-context mcp
serves it on stdio. After pip install protect-context, the same entry point
is protect-context mcp. Both run the protect-context console script
(protect_context.cli:main); the mcp verb starts protect_context.mcp_server.
Cursor / Claude Code / any stdio host (recommended)
Cursor — project .cursor/mcp.json or global MCP settings:
{
"mcpServers": {
"protect-context": {
"command": "uvx",
"args": ["protect-context", "mcp"]
}
}
}Claude Code — CLI:
claude mcp add protect-context -- uvx protect-context mcpOr hand-edit Claude Desktop / Claude Code MCP config with the same command /
args shape.
Generic .mcp.json (pi-mcp-adapter and other hosts). This repository
checks in the same file at the root:
{
"mcpServers": {
"protect-context": {
"command": "uvx",
"args": ["protect-context", "mcp"]
}
}
}Set "env": { "PC_SESSION_ID": "my-session" } when several host sessions share
one repo and need separate evidence and loop views.
From a checkout
An editable install uses that environment's interpreter:
{
"mcpServers": {
"protect-context": {
"command": "/absolute/path/to/protect-context/.venv/bin/python",
"args": ["-m", "protect_context.mcp_server"]
}
}
}PC_SESSION_ID uses the same env object as the stdio examples above.
The MCP surface exposes the nine frozen v1.0 tools plus later read surfaces
(guard_journal, guard_orientation, and guard_recall). Tools carry
conservative MCP annotations; in particular
guard_verify is marked destructive and open-world because it can run any
shell command. Annotations are host hints, not enforcement — configure the host
to require human approval for that tool.
Install the Python CLI
pip install protect-context # or: pipx install protect-context
protect-context init # seed .agent-guard/ in the current project
protect-context status
protect-context verify "pytest -q"
protect-context mcp # serve the MCP tools on stdioThe CLI and MCP server are distributed through PyPI; the npm package ships only
the pi extension. Both read the same .agent-guard/ directory, so a project can
use the extension in pi and scripted verification in CI at the same time.
From source
# Python package, CLI, MCP server, and Python tests
python3 -m venv .venv
.venv/bin/python -m pip install -e ".[dev]"
# Reproducible extension development checks (dev dependencies only)
npm ci --ignore-scripts
npm run checkIf the machine's default Node is older than 22.19, run the same checks with an
ephemeral Node 22 binary after npm ci:
npx -y node@22 node_modules/typescript/bin/tsc --noEmit -p extension/tsconfig.json
npx -y node@22 --experimental-strip-types --test extension/test/*.test.tsA 30-second tour
Excerpts from a real session in this repository.
Ask for the current state:
$ protect-context status
PC status · session default · git 91fad4d
Δdiff +88/-8 · 6 files · verdict ok
loops: none
contract 12 constraints
conventions: README.md
Nudge: verify changes with guard_verify and cite the evidence id before claiming success.Record proof instead of asserting it:
$ protect-context verify --timeout 180 npm run check
PC verify ev-0005 · exit 0 · ok · 1440ms
cwd: /path/to/project
cmd: npm run check
digest: …
Nudge: cite this evidence id when claiming success.A pipeline or || true is still recorded with the shell's real exit code, but
the report says UNTRUSTED and the CLI exits 1. That exit 0 is not proof.
Re-run the same failing command a third time and the brake engages:
$ protect-context verify "npm test"
PC verify blocked · npm test · 3 prior failures
Nudge: change approach or gather new information — do not re-run unchanged.What pi injects before the agent starts working (sections elided):
contract:
01-obey-instructions: Obey the owner's explicit instructions and stated constraints exactly; when instructions conflict, ask instead of guessing.
02-evidence-before-success: Never claim success without citing a fresh verification run (evidence id or pasted output) for the exact claim.
…
decisions:
- Use the atomic-mkdir lease, not fcntl alone; Python must interoperate with the extension. [locking,spec]
last verify: ev-0005 exit=0 ok 'npm run check' 1.4sAnd the always-on one-line widget:
PC Δ+88L/6f 0loop ev5 ctx8%Interface comparison
Capability | pi extension | MCP server | CLI |
Brief injected automatically before a turn | yes (change-triggered) | — | — |
Orientation digest (tree, languages, conventions) | injected once per digest |
|
|
Verification evidence ( |
|
|
|
Loop blocking | blocks the command |
|
|
Scope audit | session-start baseline + warnings |
|
|
Journal write/read |
|
|
|
Recall across the retained window |
|
|
|
Trusted re-injection of decisions | yes, after change and after every compaction (incl. mid-run) | read views only | read views only |
Re-injection of the agent's own notes | yes, in an explicitly unverified section | read views only | read views only |
Owner task anchor |
| — | — |
Contract management |
|
|
|
Risky-command confirmation | one grouped confirm | host approval policy | — |
Status widget | yes | — |
|
Reference
pi commands and tools
Slash commands:
Command | Action |
| Compact session status: diff verdict, loops, evidence, contract, context |
|
|
| Run a command through the evidence ledger |
| Journal a |
| Show · set |
| Full session summary for handoff or review |
Model-callable tools:
Tool | Arguments | Notes |
|
| Records |
|
| Forced to the |
|
| Read-only; the window shows each non-ok row's last output line, and |
|
| Read-only; recovers decisions, corrections, and the agent's own earlier notes after a compaction |
|
| Read-only scan of the retained window; hits labelled |
|
| Deterministic keyword coverage checklist, never a proof |
| — | Same data that feeds the widget, including journal totals |
MCP tools
Tool | Annotation class | Purpose |
| local, initializing read | Session status digest |
| shell execution (destructive, open-world) | Run a command, record evidence |
| read-only | Recent verification entries |
| local mutation |
|
| local append | Journal an entry (tagged |
| read-only | Recent journal entries, optionally filtered by type |
| read-only | Search the retained journal and evidence window by keyword, tag, type, evidence id, or command |
| local, initializing read | Failing-command streaks and advice |
| local, initializing read | Diff stats and verdict vs thresholds |
| local, initializing read | Tool/version and worktree check |
| read-only | Context-budget watermarks |
| read-only | Repo digest and conventions files |
CLI
Every command returns 0 for OK, 1 for a triggered warning or failed
verification, and 2 for an input/environment error.
protect-context init
protect-context status
protect-context verify [--timeout 180] pytest -q
protect-context evidence [--limit 5]
protect-context contract list
protect-context contract add "Keep the public API stable" [--global]
protect-context contract remove ID [--global]
protect-context note correction "use rg, not grep" --tags search
protect-context journal [--limit 5] [--type decision]
protect-context recall [QUERY] [--type decision|correction|risk|note|evidence] [--tag TAG] [--limit 5] [--evidence-id ev-NNNN] [--cmd FINGERPRINT]
protect-context orientation
protect-context loops
protect-context diff [--baseline REF]
protect-context preflight
protect-context budget
protect-context mcpConfiguration
The extension seeds .agent-guard/config.json on first session start; init
does the same for CLI/MCP-first use. Unknown, wrongly typed, fractional, or
out-of-range values fall back to the frozen default.
Key | Default | Meaning |
|
| Block the Nth identical failing command attempt |
|
| Changed-line warning threshold |
|
| Changed-line hard-level threshold |
|
| Changed-file warning threshold |
|
| First context-usage warning watermark |
|
| Inject only when the brief digest changes; |
|
|
|
|
| Ask once before a matching risky pi bash command when UI is available |
|
| Executables checked by preflight |
Numeric thresholds may be set to zero; the extension records an explicit first notification so a zero watermark does not silently disable alerts. Preflight checks at most 20 configured tool entries. If more are configured, it lists the omitted count and returns a warning instead of claiming the environment is fully checked. Version probes have a two-second per-tool timeout, share a 15-second overall deadline, and decode at most 4 KiB of output; a missing version is reported as an incomplete preflight.
Making it yours: the personal layer
The twelve seeds and the config defaults are the same for everyone, on purpose: they are the floor that makes the guardrail comparable across machines. What is yours goes in a personal layer that every project inherits:
built-in defaults < ~/.config/protect-context/ < .agent-guard/
contract.json contract.json
config.json config.jsonThe directory is $PC_USER_DIR, else $XDG_CONFIG_HOME/protect-context, else
~/.config/protect-context. Write it from the owner surfaces:
$ protect-context contract add "Prefer stdlib over new dependencies" --global
added 13-prefer-stdlib-over-new-dependencies to personal layer
…
$ protect-context contract list # every project sees it
personal layer: 1 from /home/you/.config/protect-context/contract.json (applies to every project; a project row with the same id or text wins)The same flag works in pi: /guard-contract add <text> --global and
/guard-contract remove <id> --global. Without it, writes go to the project
store as before, and guard_contract (the agent-callable tool) never touches
the personal layer.
Rules that make the layering predictable:
The project wins. A project row with the same id overrides the personal one, and a personal constraint identical in text to a project row is not listed twice. Give a personal override the same id (e.g.
03-stay-in-scope) to reword a seed for every repo.Defaults mean "no opinion". The extension seeds
config.jsonwith every default value; a project value equal to the built-in default therefore does not pin it, so your personalctx_warn_pctstill applies. Any different project value is an explicit choice and wins.Nothing is created behind your back. The layer is never seeded or repaired: a missing file means "no personal constraints", and an unreadable one is ignored in memory and left untouched. Deleting a personal constraint requires
--global; a project-scope remove says so instead of silently doing nothing.Each layer has its own lease. A
--globalwrite locks the personal directory, not the project store, so two projects writing personal state cannot race each other.
Sharing a contract with a team
.agent-guard/ is runtime state and normally gitignored. If a team wants one
shared owner contract, track only that file:
.agent-guard/*
!.agent-guard/contract.jsonRuntime files (events, evidence, journal, session state) stay local, while
contract.json becomes reviewable in pull requests. Personal constraints still
apply on top — each teammate's personal layer is weaker than the shared project
row.
The nine catastrophic patterns that trigger the pi confirmation are frozen and deliberately narrow, so benign lookalikes stay silent:
rm -rf / · rm -rf ~ · git push --force (not --force-with-lease) ·
git reset --hard · mkfs … /dev/… · dd … of=/dev/… ·
chmod -R 777 / · curl … | sh · sudo rm
Environment variables
Variable | Effect |
| Names the session used for evidence and loop views, so parallel hosts do not share a view |
| Overrides the personal-layer directory (default |
Runtime store and concurrency
Runtime files stay in the frozen .agent-guard/ layout and are gitignored:
config.json · contract.json · journal.jsonl · events.jsonl ·
verify_runs.jsonl · state.json · verify_count.json (a stamp-verified
evidence-count sidecar, discarded rather than trusted when the ledger moves)
The optional personal layer lives outside the repository at
~/.config/protect-context/ (contract.json, config.json) and is never
written implicitly; see Making it yours above.
Ledgers bound themselves instead of growing forever: once events.jsonl,
verify_runs.jsonl or journal.jsonl passes 1 MiB it rotates to
<name>.1.jsonl, keeping at most three rotated segments and dropping the oldest.
Reads concatenate the segments oldest-first, so recent rows and the highest
evidence id always survive; budget and loop streaks describe the retained
window. Rotation is lesson-aware: journal rows tagged lesson are carried
into the fresh live file when their segment is dropped, so a durable precedent
cannot be deleted by size-based rotation (each lesson is copied once, and a
copy that somehow fails never breaks the append that triggered the rotation). Session baselines are keyed per session inside state.json, so parallel
pi sessions in one repo no longer overwrite each other's watermarks.
Python and TypeScript use the same atomic-directory lease at
.agent-guard/.lock.d, with an owner token, PID/hostname checks, bounded
waiting, and conservative stale recovery. Python also takes its legacy local
fcntl.flock inside that lease. Evidence-ID allocation, JSONL appends, and
contract read-modify-write operations hold the shared lease, preventing
duplicate IDs and lost contract updates across runtimes. JSON file replacement
uses unique temporary files.
The lease follows the atomic mkdir design used by mature lock libraries while
remaining dependency-free and interoperable with Python. A crashed same-host
owner is reclaimable after the stale interval; a stale foreign-host owner fails
closed because process liveness cannot be proved safely without an external
coordinator. Recovery retains a small identity-bound .lock.d.stale.*
directory as a durable race guard; it does not block new writers and lives
inside the already-ignored runtime store. Contract mutations also fail closed
when owner rows are malformed, preserving the original file for repair.
The 12 failure modes and what ships
# | Failure mode | Shipped mitigation | Enforcement |
1 | Violating instructions | Persistent contract; provenance-aware, change-triggered brief injection; seed-removal refusal at the MCP boundary | Context/advisory |
2 | Claiming success without verification | Verification ledger, evidence IDs, status/evidence views, failure digests | Recorded evidence; not semantic proof |
3 | Editing too much / scope creep | Git diff audit over tracked, staged, and bounded non-ignored untracked work; safe explicit baselines | Warn/advisory |
4 | Misunderstanding the repo | Worktree-root-aware bounded orientation digest in pi, CLI, and MCP; conventions-file discovery | Automatic context + explicit read |
5 | Plausible but subtly wrong code | Deterministic contract checklist, verification gate, test-required seed | Heuristic/advisory |
6 | Losing earlier context | Persistent journal; owner task anchor; recent trusted decisions re-injected after change and after every compaction (including mid-run threshold/overflow); agent notes re-injected as unverified; | Automatic context + explicit read |
7 | Repeating corrected behavior | Bounded tagged corrections; trusted corrections re-injected; model-authored entries retained for owner review | Automatic context + trust boundary |
8 | Large diffs | Configurable line/file warning and hard-level notifications, status verdict, widget | Warn/advisory |
9 | Repetitive debugging loops | Shared bash/evidence streak reconstruction; third identical failing command blocked by default in pi, CLI, and MCP | Hard block at configured threshold |
10 | Environment/tool failures | Tool/version and Git-worktree preflight with warning exit outside a repository | Diagnostic |
11 | Permission interruptions | One grouped pi confirmation for the frozen risky-command patterns; environment preflight | Interactive confirmation where UI exists |
12 | Token/API cost and latency | Bounded brief sections, journal/contract text, evidence digests, untracked reads, output previews, and context watermarks | Automatic budgeting/advisory |
"Shipped" means the mitigation is reachable and tested. It does not turn an advisory signal into a security or correctness guarantee.
How it works
Session lifecycle
The extension is a thin event layer over the shared store. Nothing leaves your machine: there are no network calls and no LLM calls in the guardrail logic.
Event | What happens |
| Seed config if missing, scan orientation, freeze the diff baseline, start the status widget |
| Build the brief; inject orientation and/or brief as a hidden message when their digests changed |
| Match the nine risky patterns → one grouped confirm; check the loop fingerprint → block at the threshold; record the normalized command, with |
| Record bash outcomes and verify results into the shared loop state and evidence ledger |
| Refresh the widget: diff stats, open loops, evidence count, context percentage |
| Re-inject trusted context so compaction cannot silently erase it |
| Flush state and clear the widget |
The brief
The brief is deliberately tiny — a persistent prompt that grows without bound is its own failure mode. Within the frozen 12-line constraint cap:
newest owner constraints are selected first,
then the frozen seed constraints,
then other entries.
An agent-authored constraint therefore cannot crowd out a seed. Below the
contract it carries up to 3 recent trusted decisions, up to 3 trusted
corrections, up to 2 unverified corrections, up to 2 unverified agent notes,
up to 2 open loops, and the last evidence ID with its trust label (ok,
FAIL, UNTRUSTED), exit code, duration, and a truncated command. Corrections
the model wrote get their own unverified slot so a newer ordinary note cannot
starve the record of what the owner corrected. When a cap
hides journal entries, the brief points at guard_recall. If the owner task's
words match a tag on a trusted entry that did not fit, it also injects at most
two of those matches. brief_mode: "changed" (default) injects only when the
rendered digest actually changed, so a quiet turn costs nothing.
Local recall
guard_recall (and protect-context recall) scans the retained window: the
live journal.jsonl and verify_runs.jsonl files plus the rotated segments
the other readers already concatenate. It does not embed text and it does not
add a vector index.
Filters combine with AND:
keywords — every word must occur as a case-insensitive substring of journal text, a verify command, or a verify digest. A word that is itself
ev-NNNNalso matches that evidence id (ev-7andev-0007are the same id).--type/type—decision,correction,risk,note, orevidence.--tag/tags— every tag must be present. Matching is exact and case-insensitive. Evidence rows have no tags, so a tag filter excludes them.--evidence-id/evidence_id— one exact evidence id.--cmd/cmd— the frozen command fingerprint, or a substring of it, against verify runs only. Fingerprint matching stays case-sensitive.
Hits are ordered by match strength (text outweighs a command, which outweighs
a digest; an exact fingerprint outweighs a substring), then by recency. With
no keywords and no filters, that is simply the newest retained rows. Each
line is labelled trusted or agent. Agent hits stay unverified. limit
clamps to 0–20 and defaults to 5. The command is read-only and does not create
.agent-guard/ when the store is absent. Rows rotated past the third segment
are already gone and are not searched.
Evidence and loop detection
guard_verify runs your command with the caller's privileges, spools output to
temporary files to bound RAM, and records one append-only entry: ID, exit code,
duration, command, and a digest of the output. Stdin is closed, so a command
cannot read the MCP protocol stream or hang waiting for input. IDs are
allocated inside the shared lease, so parallel pi and CLI processes never
collide. If the ledger's last line was torn by a crash, the next append starts
on its own line instead of gluing two JSON values together.
The report is the part an agent should read. A failure includes the tail of
the output, why that exit happened, and a concrete fix. Exit 0 is recorded
honestly, but a pipeline (no pipefail) or a failure mask (|| true, ; :)
is labelled UNTRUSTED and must not be cited as proof. The CLI exits 1 for
that case. ANSI color is stripped before the 2000-character digest cap so the
tail stays readable.
Loop detection still stores the frozen fingerprint (whitespace collapsed,
nothing else). The streak key also ignores a trailing ; outside quotes and a
trailing failure mask, so pytest -q; and npm test || true count as the
same command as pytest -q and npm test. An unproven exit 0 — a failure
mask, or a pipeline that never checked its earlier stages — does not reset the
streak; it counts as one more attempt, so masking repeatedly cannot walk a
command back under the threshold. At loop_block_after the attempt is
refused, with the prior failure count and a change-strategy nudge.
Scope audit
Diffs are computed with git porcelain/numstat over tracked, staged, and
bounded non-ignored untracked files, then compared against the configured line
and file thresholds to produce ok, warn, or hard. The pi extension freezes
its baseline at session start, so committing mid-session does not reset the
scope; CLI and MCP default to HEAD and accept an explicit baseline.
Untracked content reads are capped at 1,000 files, 1 MB per file, and 8 MB
total, and stop once the hard line threshold is known. A truncated scan is
reported as a bounded scan, promoted to at least a warning (CLI exit 1), and
its count treated as a lower bound — the widget renders it as Δ≥.
Precedents and satisfaction
Recording is not the same as remembering. Two conventions make a journal entry reusable instead of merely stored:
A
lessonneeds trigger words you would actually search:guard_notesays so when alessoncarries no tag outside the generic set, because a lesson whose only tag islessonis reachable by nothing but its own wording.Tag an entry
lessonand it becomes a precedent: the brief re-injects it underprecedents:, ahead of the recentdecisions:andagent notes (unverified):caps, so a lesson the owner already paid for cannot be pushed out by newer session chatter. Precedents are ranked by how much of the current task they answer, then by recency, and the cap isbrief_max_precedentsinconfig.json(default 3, 0 disables the section). Keep each to one actionable sentence — "when a review lists N follow-ups, take all N" — because it is re-sent every turn.Tag a correction
satisfiedordissatisfiedto record how the owner reacted to a decision, plus the action that produced that reaction. The next similar situation then starts from the outcome instead of re-litigating it. These two words are a fixed vocabulary: a near-miss such asdissatisfedis normalized on write and reported in the acknowledgement, so owner feedback cannot silently decay into an ordinary tag.
Recall is paraphrase-tolerant rather than exact-string: common stopwords are
dropped, tags are searched as synonyms, and hits are weighted by rarity, so
a word that appears in every entry cannot outvote the one word that answers the
question. "why did the owner get annoyed?" therefore reaches a lesson tagged
annoyed even though the text says "prefers the broad fix" — the tag is the
owner's own word, written once and reused forever. One tag match also satisfies
the multi-word floor on its own; otherwise a three-or-more-word query needs two
real matches so a single shared word cannot answer a long question.
Matching is word-boundary aware: old no longer hits threshold or search,
while a prefix still finds its own inflections (patch → patches).
When a query is given, past successful bash commands from events.jsonl join
the hits as command · re-run to verify · ran N× · last ok exit 0 rows at half
weight — a repeated chore ("copy these to that folder") starts from the command
that already worked instead of being re-derived, and the row carries the
outcome the command last had, because a command that ran is not a command that
still works. A family whose last observed run failed is demoted and labelled
last FAILED — change it before reuse; refused or declined calls are never
reused. Non-shell tools leave their own trail: a read/edit/search call
records the path or pattern it touched, so the same query also returns
read · reuse · used 2× · <path> hints — "you were here before" instead of
re-deriving where the code lives. Those keys are paths, never commands, and only
bash and guard_verify rows may form a loop streak, so a hint can never be
blocked as a repeated failure.
Trust and provenance
Direct CLI and pi slash-command contract writes are treated as owner input.
MCP/model journal writes are forcibly tagged agent: they never enter the
trusted decisions:/corrections: sections. They are not discarded either —
the brief shows the newest two under corrections (unverified): when they
are corrections, else agent notes (unverified):, clearly labelled and capped
shorter than trusted text, and guard_journal returns the
full stored entry. That keeps the model's own reasoning recoverable after a
compaction without granting it authority. CLI and pi slash-command journal
entries are trusted. Untagged
legacy entries are treated as trusted for backward compatibility, because the
frozen journal schema has no dedicated author field.
This is a provenance convention, not authentication. A model that already has shell access can invoke the CLI and appear on that trusted path; use host-level tool permissions when the distinction must be a security boundary.
All newly stored contract, journal, and tag text is normalized to one bounded line, and legacy values are normalized again when rendered. This prevents terminal-line forging and unbounded persistent prompt growth; it does not make untrusted prose safe to follow.
Context budget
Every injected and printed block has a cap: brief sections, journal and contract
text, evidence digests, orientation output, and previews. Context-watermark
notifications fire at ctx_warn_pct and above, and the widget always shows the
current percentage. The result is a guardrail layer whose own cost stays
constant while a session grows.
Development and validation
.venv/bin/pytest -q # Python suites
npm run typecheck # extension types (tsc --noEmit)
npm test # extension unit tests (node --test)
.venv/bin/python -m compileall -q src # byte-compile check
.venv/bin/python -m build --outdir /tmp/protect-context-distnpm run check runs the typecheck and the extension tests together. The
definition-of-done checks are run twice consecutively before release. The dated
audit records the observed counts, integration probes, and package smoke test
instead of leaving stale numbers in this README.
Path | Contents |
| Store, contract, journal, recall, verify, loops, diff scope, orientation, preflight, budget, report, CLI, MCP server |
| pi entry point ( |
| pytest suites, including a fastmcp in-memory client smoke over every MCP tool |
|
|
| Frozen v1.0 contract — structure, defaults, and definition of done |
Troubleshooting
Symptom | Likely cause | Fix |
No brief appears in pi | Digest unchanged ( | Expected on quiet turns; set |
| Not a Git worktree, so no diff baseline | Run inside a repository, or use the CLI with an explicit |
| Bounded untracked scan hit a cap | Treat the count as a lower bound; narrow the scan or review scope manually |
Loop never blocks | Commands differ by flags or paths. A trailing semicolon or an or-true failure mask shares a streak; other edits do not | Compare with |
Verify says UNTRUSTED, or CLI verify exits 1 after exit 0 | The shell status is only the last stage of a pipeline, or the command forces success with | Re-run without the mask. For a pipeline the tool returns the exact rewrite to paste ( |
Verify timed out, or an MCP session stalled on an old build | The command waited on stdin, or it exceeded timeout_s (clamped to 1–900, recorded as exit 124) | Current builds close stdin. Fix the hang or pass a larger |
Agent forgot its own earlier decision after a compaction | The brief shows only the newest 2 agent notes (plus 2 unverified corrections), 120 chars each | Read the full entry with |
No guard block after an automatic compaction | Host emits no | It returns at the next user prompt; check the host supports the |
| Auto-capture ignores short prompts and slash commands, and an owner task always wins | Set it explicitly with |
| It scopes ledger views, not | Expected: state is project-global by design |
MCP tool missing in host | Host is not launching the PyPI server, or a checkout server is not using its venv interpreter | Use |
Extension does not load | Node older than 22.19 or pi not restarted | Check |
Contract edit rejected over MCP | Removing a frozen seed constraint | Seeds are protected; use the CLI or a pi slash command as the owner |
Published, but | The gallery index is built from npm search, and npm drops search documents whose | Run |
Limitations and security boundary
Shell execution
guard_verifydeliberately invokes a shell with the caller's privileges. It can modify files, access the network, or disclose data. The pi layer asks for confirmation only when one of the frozen regex patterns matches; the MCP layer relies on host approval. Neither is a shell parser or a sandbox.MCP annotations are untrusted hints. A client may ignore them. Do not expose
guard_verifyto an untrusted model without a real host sandbox/approval policy.
Evidence
An evidence record proves only that one command returned one exit code. It is not cryptographically bound to a commit/worktree, has no freshness policy, and does not prove the claimed behavior. Exit 0 from a pipeline or a failure mask is labelled UNTRUSTED in the report for that reason. The stored
okfield stays the shell's real status.
Scope
The pi extension freezes its diff baseline at session start, so committing mid-session does not reset its scope. CLI/MCP audits default to current
HEAD; pass them an explicit baseline for cross-commit scope. Binary files count as files but zero lines.Untracked-file content reads are capped at 1,000 files, 1 MB per file, and 8 MB total, and stop once the hard line threshold is known. Output explicitly reports a bounded scan, promotes it to at least a warning/CLI exit 1, and treats the truncated count as a lower bound.
Determinism
The frozen brief carries at most 12 constraints, 3 trusted decisions, 3 trusted corrections, 2 unverified corrections, and 2 agent notes, plus at most 2 trusted tag matches when the owner task names a journal tag the caps hid. Overflow is visible (each section announces what it hid) and points at
guard_journalandguard_recall, but hidden entries are not enforced by context injection.Frozen fingerprint normalization collapses whitespace everywhere, including inside quoted shell strings. Semantically different commands can therefore share a loop fingerprint.
The deterministic contract checker is keyword coverage, not program analysis. Treat every
addressedresult as a review prompt, not proof.
Compaction
Re-injection is only as good as the store. A constraint, decision, or task that was never written down cannot be restored, and the brief is a bounded pointer rather than a transcript;
guard_journalreturns the full entry.A mid-run compaction (threshold or overflow recovery) resumes the turn through
AgentStart, wherebefore_agent_startdoes not run. The extension arms acontexthook onsession_compactand re-asserts the block on every request until a persisted one is back in the conversation. On a host that does not emitcontext, the block returns at the next user prompt instead.Task capture stores the first substantive prompt (≤400 chars) in
.agent-guard/state.json. That is conversation content: it stays local and gitignored, but review it before sharing a store, and clear it with/guard-task --clear.Agent notes are unverified context, never authority. A model that journals "ignore the contract" is shown that sentence back under an explicitly unverified heading; treat it as data, and keep owner constraints in the contract where they are trusted.
Store
state.jsonis project-global. Atomic replacement prevents torn JSON, but simultaneous sessions can still overwrite notification/session state; usePC_SESSION_IDto separate ledger views, not state files.Commands and the last 2,000 output characters are stored verbatim. There is no automatic secret detector or redactor; never put credentials in commands or verification output.
JSONL ledgers rotate once the live file passes 1 MiB, keeping three older segments. Recall and the other readers see that retained window only; rows rotated past the third segment are gone. Output views stay bounded.
Python verification bounds RAM by spooling output to temporary files, but a very noisy command can consume temporary disk until it exits. On Windows, killing the direct process cannot guarantee that every descendant exits.
Design sources
The implementation was checked against primary or professional sources:
Git's stable porcelain/other-file interfaces for non-ignored untracked enumeration: git-status and git-ls-files.
Pi's documented lifecycle, tool, context, and UI APIs: Pi extension documentation.
The mature
proper-lockfiledesign, which uses atomicmkdirrather thanO_EXCL: moxystudio/node-proper-lockfile. The package itself was not added because the frozen extension requirement is zero runtime dependencies and the lock must interoperate with Python.Operational-safety evidence motivating explicit constraints, failure transparency, and safe-halt behavior: What Breaks When LLMs Code? (ASE 2026), plus evidence-conditioned execution results in Preventing Premature Commitment in Coding Agents.
MCP's own warning that annotations are risk vocabulary rather than enforcement: Tool Annotations as Risk Vocabulary.
Non-goals
No network or LLM calls are made by the guardrail logic itself. It is not a sandbox, policy engine, auto-fixer, secret manager, or replacement for pi/MCP host permissions. Recall is a local scan of the retained JSONL window: the suite does not embed journal or evidence text and does not add a vector database. The suite complements host permissions with durable context, evidence, telemetry, and narrowly scoped blocking.
License
MIT — see LICENSE. Copyright (c) 2026 XJTLUmedia.
Available Tools
12 toolsguard_budgetBRead-onlyIdempotent
Session tool-output chars, estimated tokens (chars/4), top-5 tools.
| 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, idempotentHint, openWorldHint=false and destructiveHint=false, so the safety profile is covered. The description adds two genuine behavioral details beyond that: the tokens are an estimate computed as chars/4, and only the top-5 tools are reported (an explicit truncation rule). It does not say whether counts are cumulative for the session or since the last call, which is the main remaining gap.
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?
It is short and front-loaded with no filler, but it is written as a compressed noun phrase rather than a sentence, with no verb and no leading statement of what the tool returns. Brevity here shades into under-specification rather than efficiency.
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, so return values need not be explained, and with no parameters the surface area is small. Still, for a session-scoped metrics tool the description omits whether measurements are cumulative, per-session or reset, and how it relates to the sibling status tools, so it is only minimally complete.
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 tool takes zero parameters, so there is nothing for the description to disambiguate and the baseline of 4 applies. The description correctly introduces no argument concepts.
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 description names the resource loosely ('Session tool-output') and lists three reported metrics (chars, estimated tokens at chars/4, top-5 tools), so an agent can infer this is a stats/introspection tool. However, there is no verb and no differentiation from siblings such as guard_status, which likely also reports session state, leaving the exact purpose ambiguous.
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?
There is no guidance on when to call this versus guard_status or the other guard_* introspection tools, and no stated trigger such as 'call when you need to check context budget usage'. The agent must infer usage entirely from the fragmentary noun phrase.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
guard_contractADestructive
Contract store: action="list" | "add" (with text) | "remove" (with id). Seed constraints (source="seed") cannot be removed by the model — the owner removes them via the CLI or /guard-contract, so a model cannot delete its own injected guardrails (e.g. 02-evidence-before-success).
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | ||
| text | No | ||
| action | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and readOnlyHint=false, so the mutation profile is known. The description adds genuinely non-obvious behavior: the model cannot delete its own seed-injected guardrails, which explains why a remove call may be rejected. It does not say whether adds persist across sessions, whether non-seed removals are irreversible, or how failures surface.
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 action enumeration is front-loaded in the first sentence, and the seed-constraint restriction follows as the one behavioral caveat that matters. The parenthetical example (02-evidence-before-success) is illustrative rather than filler, though the sentence is slightly dense.
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 three-parameter store tool with an output schema, the description covers the action vocabulary, per-action parameter needs, and the important ownership restriction on seed constraints. It leaves minor holes around persistence and error behavior, but nothing that would cause an agent to call it incorrectly.
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 0%, so the description carries the full burden and mostly succeeds: it enumerates the legal action values and binds text to add and id to remove. Remaining gaps are nullability (text and id default to null but the action-specific requirement is only implied) and the id format or source of valid ids.
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 description names a concrete resource (a contract store) and enumerates its three operations (list, add with text, remove with id), so an agent can tell what the tool does at a glance. It is distinguishable from siblings like guard_evidence or guard_note by the word "contract," though it does not explicitly contrast itself with them.
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?
Each action carries an inline precondition — add needs text, remove needs id — which is actionable routing guidance. It also states an exclusion: seed constraints cannot be removed by the model and must be handled by the owner via CLI or /guard-contract. It stops short of saying when to prefer this store over sibling tools such as guard_note or guard_journal.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
guard_diff_auditBIdempotent
Diff audit vs HEAD (or a baseline ref): numstat stats + verdict vs thresholds.
| Name | Required | Description | Default |
|---|---|---|---|
| baseline | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare idempotent, non-destructive, closed-world behavior, so the bar is lower. The description adds that a verdict is computed against thresholds, but it never says where thresholds come from, whether the call can fail or block, or why readOnlyHint is false for what reads like a pure analysis operation.
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?
One short sentence with no filler, and the diff target is front-loaded before the outputs. It is dense to the point of being cryptic ('numstat stats + verdict vs thresholds'), but nothing is wasted.
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, so return values need not be described, and the single optional parameter is covered. Still missing is how thresholds are configured or overridden and what the verdict values mean, which matters for an auditing tool whose whole purpose is producing a pass/fail judgment.
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 0% for the single `baseline` parameter, so the description carries the burden. It does so usefully by explaining that baseline is a git ref and that HEAD is the implicit default, which is meaning the bare nullable string schema does not 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?
The description names a specific action and output: it diffs against HEAD or a baseline ref, produces numstat statistics, and yields a verdict compared against thresholds. That distinguishes it reasonably from siblings like guard_status or guard_verify, though 'audit' and 'thresholds' remain jargon without a stated meaning.
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?
There is no guidance on when to call this versus guard_verify, guard_status, or guard_preflight, nor any stated prerequisite. The only implied usage is 'when you want a diff-based verdict', which is thin for a family of twelve guard_* tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
guard_evidenceARead-onlyIdempotent
Recent verification runs, or one run in full by evidence id.
Passing an id returns the complete record (digest tail, why, fix) so a past failure can be read back instead of only counted.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| evidence_id | No |
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, idempotentHint, destructiveHint=false and openWorldHint=false, so safety is covered. The description adds genuinely new behavioral information: the two-mode behavior (list vs. full record) and what a full record contains ('digest tail, why, fix'), which tells an agent it can retrieve remediation detail rather than just a count.
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?
Two short sentences, zero filler, and the primary behavior (recent runs) is front-loaded before the id-mode caveat. The parenthetical 'digest tail, why, fix' is terse but each item conveys a distinct payload field.
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?
The tool is simple (2 optional params, output schema present), so return-value detail need not be spelled out. The one real gap is the undocumented limit parameter; otherwise an agent has enough to call both modes correctly.
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 0%, so the description carries the burden. It explains evidence_id well ('one run in full by evidence id'), but says nothing about 'limit' — neither its default of 5 nor what it bounds (run count vs. record size). Half the parameters are therefore undocumented anywhere.
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 description names the resource precisely — 'recent verification runs' for the list mode and 'one run in full by evidence id' for the detail mode — so an agent knows exactly what comes back. It does not name any sibling (guard_journal, guard_status, guard_recall are adjacent in scope) to differentiate itself, keeping it short of a 5.
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 second sentence implies the selection rule: pass an id when you need to 'read back' a past failure rather than just count it. That is real but implicit guidance, and there is no explicit statement of when to use guard_journal or guard_status instead, nor any note that both modes are optional (0 required params).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
guard_journalCRead-onlyIdempotent
Read recent journal entries, optionally filtered by journal type.
limit is clamped to a compact, finite range at the journal boundary.
| Name | Required | Description | Default |
|---|---|---|---|
| type | No | ||
| limit | No |
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, idempotentHint, destructiveHint=false, and openWorldHint=false, so the safety profile is covered. The description adds one genuinely new behavioral fact — that limit is silently clamped — but does not state the range or whether a warning is returned, so the disclosure is vague rather than actionable.
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?
Two short sentences with the main action front-loaded and no filler. The phrase "at the journal boundary" is slightly opaque and could be tightened, but the structure is efficient overall.
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, so return values need no explanation, but with 0% parameter coverage the description should have supplied allowed 'type' values and concrete limit bounds; it does neither. Given eleven sibling tools with adjacent semantics, the lack of any disambiguation leaves the definition incomplete for correct tool selection.
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 0%, so both parameters fall entirely on the description. It only restates that 'type' filters by journal type without listing valid values (the field is a bare string with no enum) and says 'limit' is clamped without giving the bounds or the default of 5. Neither parameter gains usable meaning beyond the 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?
The description uses a specific verb and resource ("Read recent journal entries") and adds a scope detail (optional filtering by journal type). It does not distinguish this tool from plausibly overlapping siblings such as guard_recall or guard_evidence, so it falls short of a 5.
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?
There is no when-to-use or when-not-to-use guidance, and none of the eleven siblings are named as alternatives. The phrase "optionally filtered by journal type" hints at a use case but leaves the choice between guard_journal, guard_recall, and guard_note entirely to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
guard_loop_checkCIdempotent
Failing-command streaks for this session + change-strategy advice.
| 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=false, idempotentHint=true, destructiveHint=false and openWorldHint=false, so the safety profile is partly covered. However, the description reads like a passive analysis call while annotations say it is not read-only; it never discloses what state it touches, per-session scope of that state, or rate/cost limits. That is the exact gap the description should close, and it does not.
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?
It is a single short fragment with zero filler, which is efficient. But the terseness is achieved by dropping the verb and the trigger condition, so the brevity costs clarity rather than earning its place.
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 no parameters and an existing output schema, the description does not need to explain return values, which lowers the burden. Still, for a non-read-only tool sitting among eleven similarly named guard_* siblings, the description leaves the core question — when to call it instead of the others and what it mutates — unanswered.
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 tool takes zero parameters, so there is nothing for the description to disambiguate beyond the implicit session scoping. Baseline for a 0-parameter tool applies, with a slight mark down because 'this session' is the only semantic hint about what the empty argument list means.
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 description names a specific resource ('failing-command streaks for this session') plus a secondary output ('change-strategy advice'), so the agent can infer it reports on repeated failures. But it is a noun phrase without a verb, and it never distinguishes itself from the eleven sibling guard_* tools (guard_status, guard_verify, guard_loop_check being easily confused).
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?
There is no when-to-use guidance at all: no trigger condition (e.g. 'call after two consecutive failures'), no exclusion, and no pointer to an alternative such as guard_status or guard_verify. The agent must guess when this tool is appropriate versus its very similar siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
guard_noteA
Journal an agent-authored entry with a durable agent provenance tag.
Tag it lesson to make it a precedent: lessons are re-injected into the
session brief every turn instead of rotating out with the recent caps. Tag a
correction satisfied or dissatisfied to record how the owner reacted,
so the next similar situation starts from that outcome.
| Name | Required | Description | Default |
|---|---|---|---|
| tags | No | ||
| text | Yes | ||
| type | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark this as a non-read-only, non-destructive, non-idempotent write, and the description adds behavior the annotations cannot convey: an 'agent' provenance tag is attached automatically, entries are durable, and 'lesson'-tagged entries survive the recent-entry cap by being re-injected into the session brief every turn. That persistence/rotation distinction is exactly the kind of effect an agent needs to know before writing. It stops short of saying whether repeated identical calls duplicate entries, which matters given idempotentHint=false.
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 is front-loaded in the first clause, and the two tag-behavior sentences each add distinct information. Minor waste from reStructuredText double backticks and the trailing rationale 'so the next similar situation starts from that outcome,' which restates the value of recording an outcome.
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, so return values need no explanation, and the tag semantics are handled well. Still missing are the semantics of the required 'type' field and any statement separating this tool from guard_journal, which leaves real ambiguity for a three-parameter write 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 description coverage is 0%, so the description must carry the parameter load. It explains the 'tags' parameter well by naming the values 'lesson', 'satisfied', and 'dissatisfied' and their effects. But the required 'type' parameter is never mentioned at all, and since no parameter has an enum, an agent must guess its valid vocabulary — a significant residual gap.
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 description opens with a specific verb and resource: journaling an agent-authored entry with a durable 'agent' provenance tag. That is far more concrete than the tool name alone. However, it never distinguishes this from the sibling guard_journal, which reads like it could do the same thing, so an agent cannot fully disambiguate on the description.
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?
There is real guidance on how to use tags: 'lesson' for precedents that get re-injected each turn, 'satisfied'/'dissatisfied' for recording owner reactions. But that is guidance on tag choice, not on when to call guard_note rather than guard_journal or guard_recall, so the when-to-use signal is only implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
guard_orientationBRead-onlyIdempotent
Bounded repository tree, language counts, and conventions files.
| 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 establish that this is a read-only, idempotent, non-destructive, closed-world operation. The description adds useful content scope by saying the tree is 'bounded' and that language counts and conventions files are included, but it does not explain the bounds, size limits, or whether conventions files are optional. This is helpful but not rich behavioral disclosure beyond the 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 extremely concise and contains no wasted clauses. However, it is a sentence fragment rather than a structured statement, and the single phrase does not front-load an action or usage cue.
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 no parameters, read-only annotations, and an output schema present, the description does not need to explain return structure in detail. It sufficiently names the main outputs, though it could better explain what 'bounded' means and when an agent should call this rather than another guard 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?
The tool has zero parameters, so there are no parameter meanings for the description to clarify. The baseline for no parameters is 4, and nothing in the description creates confusion about inputs.
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 description specifies the resource content clearly: a bounded repository tree, language counts, and conventions files. It does not use a verb such as 'returns' and does not differentiate this orientation step from siblings like guard_status or guard_preflight. Still, an agent can infer the tool's output scope more specifically than the name alone.
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?
There is no guidance on when to use this tool, when not to use it, or which sibling tool to choose instead. The description only lists returned artifacts, leaving the agent to infer whether this is an initial orientation call or a prerequisite for other guard tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
guard_preflightBIdempotent
Tool presence/versions for configured tools + git-repo check.
| 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?
The annotations already declare idempotent, non-destructive, closed-world, and not read-only behavior, so the description only needs to add domain context. It adds the scope of what is checked, but does not explain side effects, failure behavior, or what 'configured tools' means; the apparent inspection framing sits somewhat awkwardly with readOnlyHint=false without explicitly 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 description is a single compact fragment with no wasted sentences and the scope is front-loaded. However, the telegraphic phrasing ('Tool presence/versions... + git-repo check') is terse enough to reduce immediate clarity.
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?
Because there are no parameters, an output schema exists, and annotations cover safety traits, the description does not need to explain return values. Still, with many sibling guard tools and no guidance on when preflight should be invoked or what it gates, the definition is only minimally complete.
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 tool takes zero parameters, so parameter semantics are largely moot and the baseline is 4. The description appropriately does not waste space documenting nonexistent inputs.
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 description names the resources being checked: configured tools, their presence/versions, and a git-repo check. This is clear enough to understand the tool's purpose, but it does not explicitly distinguish it from siblings such as guard_status or guard_verify.
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?
There is no explicit guidance on when to use this tool versus alternatives, nor any stated prerequisites or exclusions. The word 'preflight' implies an early check, but the description does not operationalize that for an agent choosing among many guard_* siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
guard_recallARead-onlyIdempotent
Search the retained journal and evidence window. No embeddings.
Keywords are case-insensitive substrings of text, cmd, and digest; every keyword must match (a keyword that is itself ev-NNNN also matches that evidence id). tags are AND and case-insensitive; evidence rows have no tags, so a tag filter excludes them. type is decision|correction|risk|note |evidence. evidence_id is an exact ev-NNNN id (ev-7 and ev-0007 match). cmd is a fingerprint or substring of a verify run. Hits are ranked by match strength, then recency, and labelled trusted or agent. Read-only: does not create the store. limit is clamped to 0–20 (default 5).
| Name | Required | Description | Default |
|---|---|---|---|
| cmd | No | ||
| tags | No | ||
| type | No | ||
| limit | No | ||
| query | No | ||
| evidence_id | No |
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, idempotentHint and destructiveHint=false, but the description still adds real value beyond them: match ranking ('match strength, then recency'), the trusted/agent provenance labels on hits, the 0–20 clamp with default 5, and the side effect that a tag filter silently excludes evidence rows. 'Does not create the store' partially restates the read-only hint but clarifies behavior on a missing store.
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 is front-loaded and the remaining sentences are dense with non-redundant facts. The phrasing is telegraphic and the keyword sentence is a run-on that takes a second read, but almost nothing is wasted.
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 six-parameter, zero-schema-coverage query tool, the description covers every parameter, the filtering interaction between tags and evidence rows, ranking and provenance of results, and the limit clamp. With an output schema present, return-shape documentation is correctly omitted.
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 0%, so the description carries the full burden and does so: it defines query keyword matching as case-insensitive substrings over text/cmd/digest with AND semantics, states that ev-NNNN keywords also match evidence ids, explains tags are AND and exclude tagless evidence rows, enumerates the type values, and notes evidence_id tolerates zero-padding (ev-7 == ev-0007). Every one of the six parameters gains meaning not present in the 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?
The opening line gives a concrete verb+resource ('Search the retained journal and evidence window') plus a distinguishing constraint ('No embeddings'), so an agent knows exactly what class of retrieval this is. It does not explicitly contrast itself with the closely related siblings guard_journal and guard_evidence, which would have made the differentiation airtight.
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?
Usage is only implied through the parameter semantics — e.g. directing exact-id lookups to evidence_id, verify-run lookups to cmd, and category filtering to type. There is no explicit statement of when to pick this over guard_journal or guard_evidence, and no stated exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
guard_statusBIdempotent
Dashboard: session, diff delta, loop streaks, evidence count, contract size, last verify.
| 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 idempotentHint=true, destructiveHint=false, openWorldHint=false, so the safety profile is partly covered. The description adds only the content inventory; it does not explain the notable readOnlyHint=false (what state does a "dashboard" mutate?) or whether calling it resets counters such as loop streaks. 'Dashboard' leans toward a passive read but does not explicitly contradict the annotation, so no contradiction is flagged.
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?
Nine words, front-loaded with the category label, no filler or repetition. The telegraphic field list is efficient, though the fragment style ('diff delta', 'loop streaks') costs a little 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 and there are no parameters, so the bar is low and the description is close to sufficient. The gap is that several listed fields are project-specific jargon an agent cannot interpret, leaving it unsure what the dashboard actually tells 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?
The tool takes zero parameters, so the baseline is 4 and the description has no parameter semantics to carry. Nothing is misleading about 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?
The description identifies the tool as a "Dashboard" and enumerates the telemetry it surfaces (session, diff delta, loop streaks, evidence count, contract size, last verify), so an agent can guess it aggregates run state. However, there is no verb and no statement of what the tool actually does or returns, and nothing distinguishes it from siblings like guard_orientation or guard_recall, which likely also summarize state.
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?
There is no guidance on when to call this versus the eleven sibling guard_* tools. An agent facing guard_orientation, guard_verify and guard_recall simultaneously gets no signal about which is the orientation entry point and which is the verification step.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
guard_verifyADestructive
Run a command and RECORD the outcome as evidence (id, exit, digest) in
verify_runs.jsonl. shell=True is by design: the command runs verbatim with
the caller's own privileges — this is an evidence ledger, not a sandbox.
Never raises on nonzero exit; failures are recorded and the text says why
and how to re-run. Exit 0 from a pipeline or a failure mask (|| true) is
labelled UNTRUSTED and is not proof. stdin is closed so a command cannot
read the MCP protocol. timeout_s is clamped to [1, 900].
| Name | Required | Description | Default |
|---|---|---|---|
| command | Yes | ||
| timeout_s | No |
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: discloses shell=True verbatim execution with the caller's privileges, that nonzero exits never raise, that masked success (`|| true`) is labelled UNTRUSTED, and that stdin is closed to protect the MCP protocol. The destructive/openWorld hints are reinforced rather than contradicted; the only gap is what gets recorded when the timeout fires.
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?
Five dense sentences, front-loaded with the primary action and artifact, then safety caveats. No filler and no restatement of the tool name.
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 shape need not be spelled out, and the description still previews the recorded tuple and the failure text. Near-complete for a destructive, open-world execution tool; the timeout path is the one behavior left undescribed.
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 0%, so the description carries the burden and largely does: `command` is explained as running verbatim under a shell with the caller's privileges and closed stdin, and `timeout_s` is bounded to [1, 900] with a stated default of 180 in the schema. Missing is the consequence of hitting the timeout (kill semantics, recorded exit value).
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 (run a command) plus the side effect and artifact (records id, exit, digest into verify_runs.jsonl), which is a distinct role from the read-side siblings like guard_evidence and guard_status. An agent can tell this is the execute-and-attest tool without opening the 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?
Usage is implied rather than stated: the 'evidence ledger, not a sandbox' framing tells the agent this is for commands whose outcome must be attested, but no sibling is named and there is no explicit when-not-to-use (e.g. use guard_preflight before, guard_evidence to read back).
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.
12 tool updates
v0.1.0- First observed
guard_budget - First observed
guard_contract - First observed
guard_diff_audit - First observed
guard_evidence - First observed
guard_journal - First observed
guard_loop_check - First observed
guard_note - First observed
guard_orientation - First observed
guard_preflight - First observed
guard_recall - First observed
guard_status - First observed
guard_verify
TDQS
Scored across 12 tools
Most tools have clear distinct roles (verify vs. note vs. contract mutate different stores), and descriptions carefully delineate read/write boundaries such as guard_evidence vs. guard_recall vs. guard_journal. The main soft overlap is the aggregate view tools — guard_status, guard_budget, guard_loop_check, and guard_diff_audit all surface session/diff telemetry, so an agent may hesitate over which one answers a given question.
Every tool uses the same guard_ prefix with lowercase snake_case and a compact noun/noun-phrase form (guard_diff_audit, guard_loop_check, guard_orientation). No camelCase, no stray prefixes, and the pattern is entirely predictable.
Twelve tools sits comfortably in the well-scoped range for a session-guardian server. Each tool maps to a distinct store or check (contract, journal, evidence, budget, diff, preflight), so none feel padded.
Lifecycle coverage is strong: contract is list/add/remove, journal is write/read/search, evidence is record/list/inspect, plus status, budget, diff, loop, and orientation checks. Minor gaps remain — no way to remove or prune recorded evidence, and no explicit session reset/clear operation — but agents can work around these.
Maintenance
Related MCP Connectors
Your agent carries its own resume — outcome receipts minted at settlement, held by the agent.
Rules-based context reduction, planning checks, research prompts and fixed experiences for agents.
Product memory your agents read before they work: briefs, plan checks against decisions, debriefs.
Evidence-graded agent-work lanes, bid advice, live agent jobs and a hash-chained evidence ledger.
Related MCP Servers
- AlicenseAqualityBmaintenanceLocal memory for coding agents that can refuse, not only recall. A rule carrying a machine-checkable proof blocks the matching command before it runs, while prose-only rules can warn but never block.165GPL 3.0
- AlicenseNot gradedqualityBmaintenanceEnables AI agents and tools to share durable, approved knowledge, current decisions, task ownership, and handoffs from a local-first workspace kept outside any single AI product. Markdown holds human-readable rules, goals, and narratives while SQLite tracks agents, sessions, tasks, leases, and audit events behind one transport-independent core.2MIT
- AlicenseNot gradedqualityNot gradedmaintenanceEnables agents to resolve routine typed routing, loop-stall arbitration, and tool safety checks locally instead of invoking a frontier LLM, returning schema-enforced decision outputs and telemetry at sub-millisecond latency. Also provides reversible execution checkpoints, token budgets, and decay-based episodic memory for safe, privacy-preserving autonomous operation.MIT
- AlicenseNot gradedqualityCmaintenanceEnables coding agents to run disciplined hunts over public data, enforcing evidence ledgers, calibration controls, novelty checks against catalogues and literature, and fresh-eyes review before any claim is trusted. Human-only decisions such as waivers and expert attestations stay out of reach of the agent.MIT