crossexamine
Your coding agent writes its own summary of what it did. Of course it looks fine.
crossexamine puts the agent's raw session transcript in front of models from other vendors —
and lets them veto the commit.
A witness's own account of the testimony is not evidence. The transcript is.
The problem
You let an AI agent work for an hour. At the end it tells you:
✅ Implemented the feature, added tests, all passing. Also fixed the quota gate.
Every word of that is written by the thing you are trying to audit. And the failure modes that actually cost you are invisible in a summary, because a summary is where they go to hide:
- It said "verified" — and never ran a verification command.
- It saw a weird test result, decided it was flaky, and moved on.
- You corrected it. It agreed with you. It did not change what it does.
- It reported a number that no command in the session produced.
- It disabled its own guard to get past it, then reported success.
Reviewing the diff does not catch these. The diff is the one artifact the agent controls completely. What it cannot edit is the record of what it actually did.
What Crossexamine does
your agent works ─▶ transcript (what it said / ran / saw) ─▶ reviewers from other vendors
│
git commit ◀── PASS ────────────────┤
blocked ◀── VETO ────────────────┘
- Slices the transcript — verbatim: its words, its commands, the output it saw. Credentials masked. Mistakes kept, because mistakes are the evidence.
- Two-pass review. A fast, high-temperature pass nominates faults. Then a tool-equipped pass verifies every nomination against the actual repository — keeping what holds up, killing what was invented, and adding what the first pass missed.
- Vetoes. A veto blocks the commit. You can override it; the override goes on the record, written by the tool, not by you.
Where the design comes from
The mechanism is older than software. In Tang-dynasty China the Chancellery (門下省) sat between the body that drafted imperial edicts and the body that carried them out, and its defining power was not to advise but to refuse — it could reject an edict outright and send it back to be rewritten. Not because the emperor was foolish, but because nobody should be the sole reviewer of their own decisions. That is the whole idea here, with an AI agent in the drafting seat.
The name is the courtroom version of the same thing: you do not cross-examine a witness by asking for their summary. You cross-examine them against the record.
Why two passes (this is the whole design)
The two channels you can reach a model through have opposite defects, and neither alone works:
| channel | temperature | tools | what goes wrong alone |
|---|---|---|---|
| direct API call | ✅ | ❌ | invents evidence |
agent CLI (claude -p) |
❌ | ✅ | restates the author's framing at low temperature |
That first row is not hypothetical. On the very first run of this tool, a no-tools reviewer wrote:
"crossexamine_guard.py source grep result: only a print statement, no Popen call"
It had no tools. It had never grepped anything. The Popen call was on line 216.
Two of its six findings were supported by citations it made up.
But here is the part that decided the design: the problem it pointed at was real. The auto-dispatch path existed in the source and had never once been executed. High temperature without tools finds real things and then fabricates the receipts. So: let it nominate freely, then make something with tools go check. In the run right after that fix, the verifying pass found a contradiction between a ledger entry and a design document — and quoted the file and line number to prove it.
Install
git clone https://github.com/yandie-AI/crossexamine && cd crossexamine && pip install -e .
Not on PyPI yet —
pip install crossexaminewill work once the first tag is cut. Zero dependencies, so installing from source is a clone and apip install -e ..
Setup (about a minute)
Seats are configured through the environment. Any endpoint speaking the Anthropic Messages API
works — Anthropic itself, or the /anthropic-compatible endpoints most vendors now ship.
export CX_SEATS=minimax,deepseek,moonshot # three vendors = real independence
export CX_MINIMAX_BASE_URL=https://api.minimaxi.com/anthropic
export CX_MINIMAX_API_KEY=...
export CX_MINIMAX_MODEL=MiniMax-M2.7-highspeed
export CX_MINIMAX_TEMPERATURE=1.0 # high: you want it hunting, not agreeing
export CX_DEEPSEEK_BASE_URL=https://api.deepseek.com/anthropic
export CX_DEEPSEEK_API_KEY=...
export CX_DEEPSEEK_MODEL=deepseek-v4-pro
export CX_MOONSHOT_BASE_URL=https://api.kimi.com/coding
export CX_MOONSHOT_API_KEY=...
export CX_MOONSHOT_MODEL=kimi-k3
export CX_MOONSHOT_TEMPERATURE=off # some reasoning models reject temperature
Then:
crossexamine review # review what is staged
crossexamine install-hook # or wire it into git so it is not optional
crossexamine status # reviews, vetoes, overrides, bypasses
What it looks like
$ crossexamine review
crossexamine: routine change 27fb9cae4b1e -- 1 seat(s)
CONCERN solo/minimax 103s+165s -- 2 finding(s) [verified]
[high] (kept) Ledger says T2a was moved to position 4, but the design doc still says position 1
evidence: docs/.../order0_rules.md:67 "**Next (position 1)**: T2a ..." vs
docs/audit/ledger.md:43 "T2a moved to position 4, paused"
[low] (kept) The comment on crossexamine_guard.py:87 quotes the user, but the wording is the
author's paraphrase, not what was actually said
evidence: transcript [11:16:53] actual wording: "..."
That first finding is the shape of thing this tool is for. The agent had recorded the decision in one place and never propagated it to the authoritative document — so both versions sat in the repo contradicting each other. No diff review finds that. It needs someone who read the record, then went and looked.
Design decisions worth knowing about
There is no exempt tier. An earlier version waved through "small" changes, reasoning that
blocking typo fixes would just push the author into --no-verify. That was wrong. Small changes
are where the expensive mistakes live. Two real ones from the project this came from: a one-line
insertion into a table silently turned a three-reviewer panel into four reviewers with two on the
same vendor (fake independence, invisible); a stray git add -A swept 190 unrelated files into a
commit. Neither would have been reviewed. Routine change → one seat. Major change → three seats.
Nothing is skipped.
The author never picks his own reviewer. Seats rotate by a hash of the change. Otherwise you can shop for the soft one — and you will, without noticing you are doing it.
Reviewers read as widely as the agent, and write nothing. Read, Grep, Glob, git history,
and an allow-list of read-only shell commands. No Write, no Edit. (Honest limit: this is a
narrowed tool surface, not a sandbox. Whether git log; rm -rf / gets split and judged depends on
your agent CLI's permission layer. For real isolation, run it in a read-only worktree.)
A hang is reported as death, not as "still reviewing." The first version launched the verifying agent in the background and never watched it. It hung for 19 minutes — 6 seconds of CPU, zero child processes, doing nothing at all — and the run looked like a review in progress. Anything that can hang gets a watchdog and an honest verdict of dead.
Overrides are recorded by the tool, not by you. You keep the final say — a reviewer that cannot
be overridden gets routed around within a week. But every override lands in the ledger with your
stated reason, and so does every CX_EMERGENCY bypass. Bypassing is allowed. Bypassing
quietly is not.
Stances, not checklists. Each seat is told where to stand, never what to look for. A checklist becomes a mechanical gate, and a mechanical gate can only catch the failure modes you already thought of — which are, by construction, not the ones that will get you.
Cost and latency
Measured, not estimated, on a ~110 KB transcript slice:
| pass | wall clock | notes |
|---|---|---|
| nomination (direct API, high temp) | 55–105 s | |
| verification (tool-equipped agent) | 140–170 s | 17 tool calls in the observed run |
| routine change, one seat | ~3–4 min | |
| major change, three seats | ~4–5 min | seats run in parallel |
Three to four minutes, against what a bad commit costs you. That ratio is the whole argument, and it gets more favourable the more your agent is trusted — which is the opposite of how most AI-tooling economics work.
Who this is for
The value of this tool scales with how much you have let the agent touch, not with how much you spend on tokens.
- One person, one side project, reading every diff by hand: you probably do not need this.
- A team where agents open pull requests faster than humans can read them: the reviewing capacity is already the bottleneck, and the thing filling the gap is the agent's own summary.
- An organisation where an agent has commit access to a codebase that carries real money, real customers, or a regulator: the question is not whether the agent's self-report is reliable, it is what you have that is independent of it. Right now, for most teams, the honest answer is: the diff, and nothing else.
That last case is the one this was built for. Not because large teams have worse agents — because a mistake there costs orders of magnitude more, and the record of what the agent actually did is sitting on disk, unread, in every one of those organisations today.
Requirements
- Python ≥ 3.10, no dependencies
- An agent CLI for the verification pass (
claudeby default;--agentto change it) - API access to at least one model; three different vendors if you want the full panel
FAQ
Does this work with agents other than Claude Code?
The slicer currently understands Claude Code's transcript format
(~/.claude/projects/*/*.jsonl). The rest is format-agnostic. A slicer for another agent is
about 80 lines — PRs very welcome.
Why not just use the same model to review itself? You can, and it is better than nothing. But a model reviewing its own output shares its blind spots exactly. Different vendors, different training, different blind spots. That is the point of insisting three seats land on three vendors.
Isn't the transcript full of secrets?
Credentials are masked before anything leaves your machine (see slicer.REDACTIONS). Everything
else — including your prompts and your agent's mistakes — is sent to whichever endpoints you
configured. If that is not acceptable, point the seats at a local model.
What if the reviewer is just wrong?
It often is, partly — that is why the second pass exists and why killed findings stay visible
in the output. You hold the final say. Override it; the override is on the record.
License
MIT — see LICENSE.
Extracted from the governance layer of an internal AI engineering platform, where it reviews an AI agent that has commit access. Its first real veto found a mistake the agent had made that same hour.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file crossexamine-0.1.0.tar.gz.
File metadata
- Download URL: crossexamine-0.1.0.tar.gz
- Upload date:
- Size: 23.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5db22a79ba57c415d53eb521bfb4777191aee6fa019041c6d6c2b55acfe21f55
|
|
| MD5 |
0bf51c5db0123b83723614d2cd693280
|
|
| BLAKE2b-256 |
e8ee7cd4ba0040cdd6f0ad3f53131f909d9f140db41595ffb0b91413f8f92a7c
|
Provenance
The following attestation bundles were made for crossexamine-0.1.0.tar.gz:
Publisher:
release.yml on yandie-AI/crossexamine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
crossexamine-0.1.0.tar.gz -
Subject digest:
5db22a79ba57c415d53eb521bfb4777191aee6fa019041c6d6c2b55acfe21f55 - Sigstore transparency entry: 2290993296
- Sigstore integration time:
-
Permalink:
yandie-AI/crossexamine@4dca6af5cce32840673cd632a2a6f152506ab3fc -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/yandie-AI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4dca6af5cce32840673cd632a2a6f152506ab3fc -
Trigger Event:
push
-
Statement type:
File details
Details for the file crossexamine-0.1.0-py3-none-any.whl.
File metadata
- Download URL: crossexamine-0.1.0-py3-none-any.whl
- Upload date:
- Size: 24.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
54354d68e0bd65e25c67d5a8ab4b6e3c09c4b2c11fe65e057a1dbb8d0af9955c
|
|
| MD5 |
2cc892e8e1417fd3880eb4436b80c0e3
|
|
| BLAKE2b-256 |
0cd2b14de9da2d6786f80bee614b7a19efa0cf968b0b66c2d05a0eff1f5ce186
|
Provenance
The following attestation bundles were made for crossexamine-0.1.0-py3-none-any.whl:
Publisher:
release.yml on yandie-AI/crossexamine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
crossexamine-0.1.0-py3-none-any.whl -
Subject digest:
54354d68e0bd65e25c67d5a8ab4b6e3c09c4b2c11fe65e057a1dbb8d0af9955c - Sigstore transparency entry: 2290993395
- Sigstore integration time:
-
Permalink:
yandie-AI/crossexamine@4dca6af5cce32840673cd632a2a6f152506ab3fc -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/yandie-AI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4dca6af5cce32840673cd632a2a6f152506ab3fc -
Trigger Event:
push
-
Statement type: