How an ongoing systems debate, one token-economics guide, and a few unusually sharp comment threads turned into 1,031 lines of standard-library Python across a CLI and its test harness, a significantly leaner prefix, and an agent that can no longer quietly edit its own exam.
The Confession & The Stack
Before diving in, here is the setup: I work in Antigravity (Google's agentic CLI/IDE platform), where an orchestrating agent runs tools locally against the workspace. Like many agent environments, it injects root configuration files on every single turn—specifically a global GEMINI.md system prompt and an orchestration protocol (orchestration.md).
For months, my setup had been getting more "secure" the way a junk drawer gets more "organized": by adding more stuff.
Every edge case left behind a new paragraph in Markdown: Don't store secrets in plain text. Use integer font sizes in Tkinter. Never force-push. Always delegate. My global instructions file had ballooned to 20,110 bytes. When I finally audited what was actually getting injected into context on every single turn, I found something embarrassing: several policies were being loaded in duplicate or triplicate. The orchestration protocol was declared in two files. The secrets policy was declared in three. The model was re-reading the exact same sermon three times before writing its first line of code.
Then I followed Randal L. Schwartz's ongoing series, Synthetic Scars (an evolving series spanning parts like Part 3.6, 3.7, and 3.12). One title landed like a direct diagnosis: The Best Prompt Rule Is the One You Delete.
Meanwhile, Paul Irolla's breakdown on Claude Code Hooks and Token Economics supplied the financial grounding: anything that enters conversation history gets re-sent on every subsequent turn, and whatever a script can compute locally is a token you never send to the model.
This post is what happened when I stopped treating Markdown as a runtime firewall and pushed enforcement down into the filesystem, compiler, and Git.
1. The Audit: Finding the Triplicate Sermon
Before writing code, I measured the baseline. Here is the exact footprint of what my agent had to load on every single turn:
| File / Always-On Rule | Before | After | Change |
|---|---|---|---|
Global instructions (GEMINI.md) |
20,110 B | 10,506 B | −47.8% |
Subagent directives (AGENTS.md) |
2,022 B | 2,468 B | +22.1% (+446 B) |
Orchestration protocol (orchestration.md) |
5,922 B | 5,279 B | −10.9% |
Secrets reference (keyring_secrets_management.md) |
3,657 B | Loaded on demand | Removed from prefix |
| Total Fixed Prefix per Turn | 31,711 B | 18,253 B | −42.4% |
Zero safety rules were deleted. All 17 clauses of the security protocol (including Rule Zero requiring explicit human confirmation before any medium or high-risk command) remain intact.
How do conditional modules actually load?
A reasonable question is: if you moved rules out of the permanent prefix, who decides when to load them?
In Antigravity, rules can use an explicit frontmatter trigger: trigger: always_on vs trigger: model_decision. When set to model_decision, the environment only presents a 1-line description in its rule index. The full text mounts only when the task context matches that domain.
The honest risk of this −42.4% cut: if the routing description fails to trigger, the agent operates without those domain instructions. We mitigated that risk for core rules by keeping a 1-line hard constraint in GEMINI.md that explicitly points to the rule file. The rule stays out of the permanent token bill, but the model has an unambiguous signpost when touching that subsystem.
Why did AGENTS.md grow by 446 bytes? Because that small addition tells every spawned subagent where the physical CLI guardrails live. That is context budget well spent.
2. The Engine: ag_ops.py (1,031 Lines Across CLI & Test Suite)
Instead of writing more Markdown pleading with the model not to hallucinate or cheat, I implemented a single standard-library-only Python CLI (available here on GitHub Gist under the MIT License) backed by an automated verification suite—totaling 1,031 lines of pure standard-library Python in the core tool—with deterministic --json output across four subcommands:
# 1. Seal tests & demonstrate the bug fails on untouched code:
python ag_ops.py guard witness-red --test-dir tests/ --cmd "pytest tests/test_calc.py" --json
# ...agent applies code patch in production files...
# 2. Verify green AND guarantee the test tree was not modified:
python ag_ops.py guard verify-green --test-dir tests/ --json
Here is what each component does:
A. guard: Merkle Locks & Command Binding
-
witness-red: Traverses--test-dir, computes a deterministic SHA-256 Merkle manifest of every test file (with automatic CRLF normalization to ensure Windows/POSIX parity, ignoring build caches and receipt files), verifies that running the test suite does not self-mutate--test-dir, binds the exact command string to the receipt, and actively rejects cooperative tests:
{"ok": false, "error": "COOPERATIVE_TEST: Test passed before fix (exit 0). Red state was not witnessed."}
It also rejects non-assertion runner failures (such as pytest exit codes 2, 3, 4, 5, or shell 127/9009) unless explicitly overridden via --expected-exit. Once witnessed, it records the cryptographic receipt atomically via _atomic_write_json (tempfile.mkstemp + os.replace) in <state-dir>/merkle_guard.json.
-
verify-green: Re-hashes the test directory before and after running the test command. It verifies that the target path and the command being executed match the exact witnessed receipt (preventing an agent from runningverify-green --cmd true). Furthermore, the diff engine checks for any changes across three distinct states:-
ADDED:<file>(the agent added new tests to dilute coverage) -
DELETED:<file>(the agent erased a failing test entirely) -
MODIFIED:<file>(the agent tweaked an assertion)
-
If any of these occur without permission, it aborts immediately with exit code 2:
{
"ok": false,
"error": "EACCES_TEST_TAMPERING: Test files modified during fix without renegotiation",
"changes": ["MODIFIED:test_calc.py"]
}
-
renegotiate: The answer to a question raised by Paul Lovelace: What happens when a test legitimately needs to evolve mid-fix? Rather than letting the agent slip edits into the test suite, it must run a formal renegotiation step with a mandatory--reason. This archives the entire chronological history of previous hashes, commands, and rationales inside the receipt, re-computes the Merkle seal, and demands that the new expectation fails on the current code before any fix is attempted.
It is bureaucratic on purpose. A few seconds of ceremony is cheaper than discovering at 2:00 AM that an agent "solved" a failing database constraint by quietly commenting out the foreign key check and leaving a cheerful # TODO: revisit later.
B. distill: Deterministic Output Compression & Process Tree Isolation
Instead of letting noisy test suites dump thousands of lines of terminal output into the context window, distill executes commands in isolated process groups (CREATE_NEW_PROCESS_GROUP + taskkill /F /T on Windows, start_new_session=True + os.killpg on POSIX) with a hard timeout. If a test suite hangs, the entire child process tree is forcefully killed—preventing orphaned processes from holding open stdout/stderr pipes indefinitely.
Execution output is piped through disk (logs/raw_<utc_timestamp>.log) while preserving the real subprocess exit code:
- On success (exit 0), it returns a 5-line tail and notes the suppressed lines.
- On timeout (exit 124), it kills the process tree and returns the last 15 lines before termination.
- On failure, it extracts solely the critical delta: proportionally budgeting
--max-linesbetween the first error (the root cause) and the final traceback frames while pruning intermediate log spam. In our automated test suite, 2,504 lines of log spam were compressed into 5 lines of actionable traceback.
C. state: Runlevels (/etc/init.d/ on Disk) with Drift Detection
Inspired by Part 3.7 of Synthetic Scars, long-running tasks write discrete, ordered state files (00_plan.json, 10_build.json, 20_verify.json) atomically, replacing stale slugs on the same step number and recording SHA-256 hashes of generated artifacts. When an agent hits context compaction in long sessions, running python ag_ops.py state status reconstructs the ground truth in under 5 ms. Even better: state status actively inspects every referenced artifact on disk and flags DRIFT_MODIFIED, FILE_DELETED, or CORRUPTED_STATE_FILE warnings if any produced artifact or state record was mutated behind its back.
D. ast-lint: Millisecond Static Verification
Checks workspace Python code using the native ast parser:
-
TK001: Rejects positive and negative float font sizes in Tkinter/ttk (strictly scoped tofont=arguments, avoiding false positives on canvasscrollregion). -
TK002: Tracks ASTImportandImportFromnodes to flag forbiddenbackground=/foreground=(bg/fg) arguments strictly on themedttkwidgets—supportingimport tkinter.ttk as ttk,from tkinter import ttk, and custom aliases without triggering false positives on standardtk.Frameortk.Labelwidgets. -
AUTH001: Verifies required authorship headers within the first 25 lines (configurable via--author).
Running ast-lint against a violation fixture took 1.07 ms. Scanning the entire ag_ops.py engine and its test harness took 18.6 ms.
3. What Went Wrong (And The Live Demonstration)
1. The agents died mid-flight
I initially delegated this refactor to two parallel subagents (one writing ag_ops.py, the other refactoring the rules). While both were writing files, they hit an API rate limit (429 RESOURCE_EXHAUSTED) and terminated without sending their completion handshakes.
Because the guardrails hadn't been fully verified yet, I couldn't trust the files on disk. I had to manually compare each modified file against its pre-upgrade backup hash, inspect diffs, and run the test suite by hand. Getting a live demonstration of why you need verifiable state files while building a verifiable state machine was humbling.
2. The Windows CP1252 trap
During the first automated test run of ag_ops.py, the suite failed abruptly on guard renegotiate. The logic was sound, but on Windows, child processes piped through Python defaulted to cp1252. When the renegotiation test passed an explanation containing the Spanish word "Ampliación", the accented ó broke the JSON decode stream.
The fix required explicit UTF-8 reconfiguration at both the CLI entrypoint and across every isolated subprocess pipe:
def main(argv: Optional[List[str]] = None) -> int:
for stream in (sys.stdout, sys.stderr):
if hasattr(stream, "reconfigure"):
try:
stream.reconfigure(encoding="utf-8", errors="replace")
except Exception:
pass
and inside _run_command_isolated:
kwargs: Dict[str, Any] = {
"shell": True,
"stdout": subprocess.PIPE,
"stderr": subprocess.PIPE,
"text": True,
"encoding": "utf-8",
"errors": "replace",
}
3. The backdoor in my own guard
During a strict code review of the initial Gist, I caught a glaring logic flaw in verify-green: it accepted any --cmd flag and didn't compare it against witness-red.
An agent could witness failure with pytest tests/test_calc.py, and then run verify-green --cmd "true". Because the test files were untouched and true exited with code 0, the guard happily granted VERIFIED_GREEN_WITH_MERKLE_LOCK without running a single test!
I patched it immediately: verify-green now locks onto both the witnessed test_target and the exact cmd string, rejecting any substituted command with exit code 2 (COMMAND_MISMATCH).
4. Hard Limits & The Obvious Hole in the Merkle Lock
The Circular Authority Hole
If an autonomous agent has unrestricted write access to the filesystem, it can theoretically edit ag_ops.py or overwrite merkle_guard.json to forge its own pass.
Right now, this Merkle lock is stricter by construction, not a cryptographic kernel boundary. It prevents the standard behavioral failure mode (an LLM taking the path of least resistance by editing assertions in tests/test_x.py to match its broken code), but it does not stop an adversarial agent with arbitrary shell execution. True isolation requires OS-level permission boundaries (mounting test harnesses as read-only or using dedicated sandbox containers).
The "Uninformative Red" Trap
As howcani pointed out in Randal's comment section: witnessed ≠ informative. While witness-red blocks collection, syntax, and CLI invocation errors (pytest exit codes 2, 3, 4, 5, and shell 126/127/9009), an unhandled runtime exception inside the test body (TypeError, AttributeError, or a broken fixture) still produces exit code 1—the exact same exit code as an AssertionError. A test failing for the wrong runtime reason can still fool the red check into issuing a receipt. A tighter version needs to inspect the assertion delta to ensure the test fails specifically on the expected invariant.
Scope Creep Outside --test-dir
ag_ops.py guard hashes --test-dir. If your repo relies on external fixtures, root-level conftest.py, or environment variables defined outside that path, an agent could mutate those files without tripping the Merkle lock. The tree hash must eventually cover the broader test harness manifest, not just a single directory.
The Scope of ast-lint
Our three rules run in ~1 ms, but let's be realistic: this is a lightweight style and framework compatibility check. It does not attempt to evaluate hundreds of complex rules across massive codebases, nor does it replace dynamic type checkers or memory safety tools. It simply moves recurring framework-specific quirks out of the prompt.
The Benchmark Reality
This is one developer's setup, not an industry benchmark. The 42.4% reduction reflects raw bytes of fixed prefix in my specific environment; bytes do not map 1:1 to token counts across different tokenizers. Furthermore, I have not benchmarked whether the agent solves tickets faster or with higher pass rates on smaller prompts—only that the physical guardrails now deterministically catch test tampering that Markdown prompts could only politely discourage.
The Takeaway
Markdown is where rules go to blur.
A natural-language prompt is probabilistic; a Merkle tree comparison aborts with exit code 2. A prose reminder costs tokens on every single turn; an ast-lint check runs in milliseconds.
My agent now reads 42% less boilerplate before it starts working, and the test guardrails are rooted in hashes rather than good intentions.
Acknowledgments & Credits
- Randal L. Schwartz: For authoring the Synthetic Scars series, and demonstrating how Unix principles and compiler architecture beat prompt lawyering.
- Paul Irolla: For the token-economics framework in his Claude Code Hooks analysis and his work on Tokenade.
-
Paul Lovelace: For asking the operational question on Merkle lock friction that led to the
renegotiateworkflow. - howcani, Sam LABBE, Vinnie Falco, and Ctbhatia: Whose commentary insights inside Randal's articles helped frame the distinction between sensors and steps, the trap of uninformative red tests, and the speed requirements of AST linters.
The full ag_ops.py CLI script, along with its MIT License and documentation, is available as a standalone GitHub Gist.
Top comments (0)