Skip to main content
Glama

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.

protect-context injects a session brief — contract, decisions, corrections, loops, last evidence — before the agent starts working

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+, fastmcp

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.

Architecture: the pi extension, MCP server, and Python CLI share a project-local .agent-guard store for rules, journal entries, and evidence; only pi injects briefs automatically

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, status, guard_journal elsewhere

Orientation

Bounded repo digest: tree, languages, and conventions files (CLAUDE.md, AGENTS.md, CONVENTIONS.md, README.md, README)

Injected once per digest in pi; orientation in CLI/MCP

Evidence ledger

Every verification run recorded as ev-NNNN with exit code, duration, command, and a bounded output digest

guard_verify, protect-context verify, guard_evidence

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

git diff stats over tracked, staged, and bounded untracked work, with ok / warn / hard verdicts

Widget and status in pi; diff, guard_diff_audit

Journal

Durable decision / correction / risk / note entries with tags and trust levels

guard_note, protect-context note, guard_journal

Recall

Keyword, tag, type, evidence-id, and command search over the retained journal and evidence window — no embeddings

guard_recall, protect-context recall; the brief names it when a cap hides entries

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

preflight, guard_preflight

Context budget

Watermarks on context usage and hard caps on every injected block

Widget, budget, guard_budget

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-context

Install 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.ts

One 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-context

Start 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 — 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 mcp

Or 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 stdio

The 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 check

If 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.ts

A 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.4s

And the always-on one-line widget:

PC Δ+88L/6f 0loop ev5 ctx8%

Illustrative pi widget: Δ+123L/4f means 123 changed lines across 4 files, 1loop means one command at the blocking threshold, ev2 counts two verification runs this session, and ctx41% shows context usage

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

guard_orientation

orientation

Verification evidence (ev-NNNN)

guard_verify, /guard-verify

guard_verify

verify

Loop blocking

blocks the command

guard_loop_check reports and advises

verify refuses; loops exits 1

Scope audit

session-start baseline + warnings

guard_diff_audit (explicit baseline)

diff --baseline REF

Journal write/read

guard_note, /guard-note, guard_journal

guard_note, guard_journal

note, journal

Recall across the retained window

guard_recall; brief may surface 1–2 trusted tag matches

guard_recall

recall

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

/guard-task, auto-captured from the first prompt

—

—

Contract management

/guard-contract

guard_contract

contract list|add|remove

Risky-command confirmation

one grouped confirm

host approval policy

—

Status widget

yes

—

status

Reference

pi commands and tools

Slash commands:

Command

Action

/guard-status

Compact session status: diff verdict, loops, evidence, contract, context

/guard-contract

list · add <text> · remove <id> — edit the persistent contract

/guard-verify

Run a command through the evidence ledger

/guard-note

Journal a decision · correction · risk · note

/guard-task

Show · set <text> · --clear the owner task anchor that the brief re-injects

/guard-report

Full session summary for handoff or review

Model-callable tools:

Tool

Arguments

Notes

guard_verify

command, timeout_s (default 180, clamped 1–900)

Records ev-NNNN; refusal on a blocked loop

guard_note

type, text, optional tags

Forced to the agent trust level

guard_evidence

limit (default 5), optional evidence_id

Read-only; the window shows each non-ok row's last output line, and evidence_id returns one run in full

guard_journal

limit (0–20, default 5), optional type

Read-only; recovers decisions, corrections, and the agent's own earlier notes after a compaction

guard_recall

query, type, tags (AND), limit (0–20, default 5), evidence_id, cmd

Read-only scan of the retained window; hits labelled trusted or agent

guard_contract_check

summary

Deterministic keyword coverage checklist, never a proof

guard_status

—

Same data that feeds the widget, including journal totals

MCP tools

Tool

Annotation class

Purpose

guard_status

local, initializing read

Session status digest

guard_verify

shell execution (destructive, open-world)

Run a command, record evidence

guard_evidence

read-only

Recent verification entries

guard_contract

local mutation

list · add · remove

guard_note

local append

Journal an entry (tagged agent)

guard_journal

read-only

Recent journal entries, optionally filtered by type

guard_recall

read-only

Search the retained journal and evidence window by keyword, tag, type, evidence id, or command

guard_loop_check

local, initializing read

Failing-command streaks and advice

guard_diff_audit

local, initializing read

Diff stats and verdict vs thresholds

guard_preflight

local, initializing read

Tool/version and worktree check

guard_budget

read-only

Context-budget watermarks

guard_orientation

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 mcp

Configuration

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

loop_block_after

3

Block the Nth identical failing command attempt

diff_warn_lines

400

Changed-line warning threshold

diff_hard_lines

800

Changed-line hard-level threshold

diff_warn_files

10

Changed-file warning threshold

ctx_warn_pct

70

First context-usage warning watermark

brief_mode

"changed"

Inject only when the brief digest changes; "always" injects every turn

brief_max_precedents

3

lesson precedents re-injected per brief (0–10; 0 disables the section)

risk_confirm

true

Ask once before a matching risky pi bash command when UI is available

preflight_tools

git, python3, pip, node, npm, rg, pytest

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.json

The 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.json with every default value; a project value equal to the built-in default therefore does not pin it, so your personal ctx_warn_pct still 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 --global write 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.json

Runtime 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

PC_SESSION_ID

Names the session used for evidence and loop views, so parallel hosts do not share a view

PC_USER_DIR

Overrides the personal-layer directory (default $XDG_CONFIG_HOME/protect-context or ~/.config/protect-context)

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; guard_journal read views; guard_recall searches the retained window

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

session_start

Seed config if missing, scan orientation, freeze the diff baseline, start the status widget

before_agent_start

Build the brief; inject orientation and/or brief as a hidden message when their digests changed

tool_call (bash)

Match the nine risky patterns → one grouped confirm; check the loop fingerprint → block at the threshold; record the normalized command, with ok=false when the call was refused

tool_execution_start / tool_execution_end

Record bash outcomes and verify results into the shared loop state and evidence ledger

turn_end

Refresh the widget: diff stats, open loops, evidence count, context percentage

session_compact

Re-inject trusted context so compaction cannot silently erase it

session_shutdown

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:

  1. newest owner constraints are selected first,

  2. then the frozen seed constraints,

  3. 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-NNNN also matches that evidence id (ev-7 and ev-0007 are the same id).

  • --type / type — decision, correction, risk, note, or evidence.

  • --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.

Verification workflow: run a command, persist its evidence, and cite the result. With the default threshold, two failures of the same normalized command block its third attempt; only that command succeeding resets its streak

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 lesson needs trigger words you would actually search: guard_note says so when a lesson carries no tag outside the generic set, because a lesson whose only tag is lesson is reachable by nothing but its own wording.

  • Tag an entry lesson and it becomes a precedent: the brief re-injects it under precedents:, ahead of the recent decisions: and agent 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 is brief_max_precedents in config.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 satisfied or dissatisfied to 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 as dissatisfed is 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-dist

npm 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

src/protect_context/

Store, contract, journal, recall, verify, loops, diff scope, orientation, preflight, budget, report, CLI, MCP server

extension/

pi entry point (index.ts) and pi-import-free lib/ modules

tests/

pytest suites, including a fastmcp in-memory client smoke over every MCP tool

extension/test/

node --test suites for fingerprinting, risk patterns, loops, brief digest, widget, git parsing, config

SPEC.md

Frozen v1.0 contract — structure, defaults, and definition of done

Troubleshooting

Symptom

Likely cause

Fix

No brief appears in pi

Digest unchanged (brief_mode: "changed") and nothing new to say

Expected on quiet turns; set brief_mode: "always" to see it every turn

Δ? in the widget

Not a Git worktree, so no diff baseline

Run inside a repository, or use the CLI with an explicit --baseline

Δ≥ in the widget

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 loops / guard_loop_check. Change the command for real — do not mask the failure

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 true

Re-run without the mask. For a pipeline the tool returns the exact rewrite to paste (bash -o pipefail -c '<command>'). Do not cite that evidence id

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 timeout_s

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 guard_journal, or search tags and evidence with guard_recall

No guard block after an automatic compaction

Host emits no context event, so mid-run re-assertion cannot run

It returns at the next user prompt; check the host supports the context hook

task: line is wrong or missing

Auto-capture ignores short prompts and slash commands, and an owner task always wins

Set it explicitly with /guard-task <text> or /guard-task --clear

PC_SESSION_ID changes nothing in evidence

It scopes ledger views, not state.json

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 command uvx and args ["protect-context", "mcp"]. From a checkout, point command at .venv/bin/python and pass -m protect_context.mcp_server

Extension does not load

Node older than 22.19 or pi not restarted

Check pi list, then restart; use pi -ne to start without extensions

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 pi.dev/packages says "No packages match"

The gallery index is built from npm search, and npm drops search documents whose downloads.monthly is 0. A publish rewrites that document with the download figure of that moment, so publishing before npm's first download rollup pins it at 0

Run scripts/check-discoverability.sh; if the document's downloads are 0 while the downloads API reports traffic, publish once the rollup exists (npm re-crawls documents on its own as well, typically within a week)

Limitations and security boundary

Shell execution

  • guard_verify deliberately 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_verify to 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 ok field 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_journal and guard_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 addressed result 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_journal returns the full entry.

  • A mid-run compaction (threshold or overflow recovery) resumes the turn through AgentStart, where before_agent_start does not run. The extension arms a context hook on session_compact and re-asserts the block on every request until a persisted one is back in the conversation. On a host that does not emit context, 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.json is project-global. Atomic replacement prevents torn JSON, but simultaneous sessions can still overwrite notification/session state; use PC_SESSION_ID to 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:

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 tools
guard_budgetB
Read-onlyIdempotent

Session tool-output chars, estimated tokens (chars/4), top-5 tools.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
resultYes

TDQS

B3/5.0
Behavior3/5

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.

Conciseness3/5

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.

Completeness3/5

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.

Parameters4/5

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.

Purpose3/5

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.

Usage Guidelines2/5

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_contractA
Destructive

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).

NameRequiredDescriptionDefault
idNo
textNo
actionYes

Output Schema

NameRequiredDescription
resultYes

TDQS

A3.8/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines4/5

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_auditB
Idempotent

Diff audit vs HEAD (or a baseline ref): numstat stats + verdict vs thresholds.

NameRequiredDescriptionDefault
baselineNo

Output Schema

NameRequiredDescription
resultYes

TDQS

B3.3/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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_evidenceA
Read-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.

NameRequiredDescriptionDefault
limitNo
evidence_idNo

Output Schema

NameRequiredDescription
resultYes

TDQS

A3.8/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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_journalC
Read-onlyIdempotent

Read recent journal entries, optionally filtered by journal type.

limit is clamped to a compact, finite range at the journal boundary.

NameRequiredDescriptionDefault
typeNo
limitNo

Output Schema

NameRequiredDescription
resultYes

TDQS

C2.9/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness2/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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_checkC
Idempotent

Failing-command streaks for this session + change-strategy advice.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
resultYes

TDQS

C2.6/5.0
Behavior2/5

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.

Conciseness3/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose3/5

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.

Usage Guidelines2/5

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.

NameRequiredDescriptionDefault
tagsNo
textYes
typeYes

Output Schema

NameRequiredDescription
resultYes

TDQS

A3.6/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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_orientationB
Read-onlyIdempotent

Bounded repository tree, language counts, and conventions files.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
resultYes

TDQS

B3.4/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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_preflightB
Idempotent

Tool presence/versions for configured tools + git-repo check.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
resultYes

TDQS

B3.3/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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_recallA
Read-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).

NameRequiredDescriptionDefault
cmdNo
tagsNo
typeNo
limitNo
queryNo
evidence_idNo

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.1/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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_statusB
Idempotent

Dashboard: session, diff delta, loop streaks, evidence count, contract size, last verify.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
resultYes

TDQS

B3.1/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters4/5

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.

Purpose3/5

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.

Usage Guidelines2/5

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_verifyA
Destructive

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].

NameRequiredDescriptionDefault
commandYes
timeout_sNo

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

  1. 12 tool updatesv0.1.0
    • First observedguard_budget
    • First observedguard_contract
    • First observedguard_diff_audit
    • First observedguard_evidence
    • First observedguard_journal
    • First observedguard_loop_check
    • First observedguard_note
    • First observedguard_orientation
    • First observedguard_preflight
    • First observedguard_recall
    • First observedguard_status
    • First observedguard_verify

TDQS

A3.5/5.0

Scored across 12 tools

Disambiguation4/5

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.

Naming Consistency5/5

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.

Tool Count5/5

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.

Completeness4/5

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

ActivityMaintained
ResponsivenessNo recent community issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables 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.
    2
    MIT
  • A
    license
    Not graded
    quality
    Not graded
    maintenance
    Enables 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
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables 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