Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 84 additions & 54 deletions .claude/commands/adversarial.md
Original file line number Diff line number Diff line change
@@ -1,75 +1,105 @@
---
description: Hand a pushed branch to a fresh subagent and ask it to break the change
description: Give a pushed branch to a fresh subagent and ask it to break the change
argument-hint: "[branch] (defaults to the current one)"
---

# Attack the branch before the pull request

AGENTS.md asks for this after the push and before the PR is opened, and for a revision of
an open PR, after its push and before review is re-requested. This is the prompt it tells you to
write.
AGENTS.md asks for this pass after the push and before you open the PR. It also asks for
the pass on a revision of an open PR, after its push and before you request review again.
This file gives the prompt that AGENTS.md tells you to write. Do these steps:

Work out the branch (`$ARGUMENTS`, or `git rev-parse --abbrev-ref HEAD`), confirm it is
pushed and that `origin/<branch>` is what you mean to have reviewed, then spawn **one fresh
general-purpose subagent**, never a fork, which would inherit the reasoning that wrote the
branch and agree with it. Give it the prompt below with the branch filled in. Do not edit
the worktree until the pass reports.
1. Find the branch: `$ARGUMENTS`, or `git rev-parse --abbrev-ref HEAD`.
2. Make sure that the branch is pushed, and that `origin/<branch>` is the code that you
want the pass to review.
3. Start **one fresh general-purpose subagent**. Never use a fork. A fork inherits the
reasoning that wrote the branch, and it agrees with that reasoning.
4. Give the subagent the prompt below, with the branch filled in.

Do not edit the worktree until the pass reports.

## The prompt

> You are doing an adversarial pre-PR review in this repository.
> Read AGENTS.md and PLAN.md first. **Change nothing, commit nothing, post nothing to
> GitHub.** Report findings.
> You do an adversarial pre-PR review in this repository.
> Read AGENTS.md and PLAN.md first. **Do not change anything. Do not commit anything. Do
> not post anything to GitHub.** Report your findings.
>
> The change is `git diff origin/main...origin/<branch>`. This is the pushed code, not
> `HEAD`.
>
> Attack these classes, in this order. They are the minimum, not the full list. Each class
> is a guarantee that the design needs. docs/durable-memory-for-disposable-agents.md
> states each guarantee. A branch that breaks one guarantee breaks the record for everyone
> who reads the record later.
>
> 1. **The record's guarantees.** Every write is a new version, and no code edits anything
> in place. Demotion leaves a tombstone. If you delete the index and run reindex,
> reindex builds an identical index from the record alone. Find a path through the new
> code that does one of these:
>
> - It edits a file in place.
> - It loses a version.
> - It leaves data in the index that the record does not have.
>
> 2. **The filter and the labels.** Nothing above the handling of a session appears in
> these:
>
> - a search result;
> - a hit count;
> - a summary;
> - T0.
>
> Nothing leaves an export below its label. The `highest_label` of a bundle is the
> highest label inside the bundle. Find the read path that does not apply the filter,
> or the field that leaks through a summary.
> 3. **Leases and claims.** A task never has two holders. When a lease expires, the task
> goes back to the pool with its handoff. While a lease holds, a second `claim` gets a
> refusal. Try two claimants at the same time, a heartbeat after expiry, and a crash
> between claim and complete.
> 4. **The tests themselves.** Find each new assertion that still passes when the
> behaviour that it names is wrong. To check an assertion, make the behaviour wrong. Do
> not check it only by reading it.
> 5. **Untrusted input.** The code reads frontmatter from disk and from bundles that other
> people wrote. A manifest names paths. Try these:
>
> - a path that escapes the bundle directory;
> - an id that is not an id;
> - a digest that does not match;
> - a label that is not one of the three;
> - a `created` in the future;
> - a body of a hundred megabytes.
>
> The change is `git diff origin/main...origin/<branch>`: what was pushed, not `HEAD`.
> 6. **A guard, a script, or a check: what gets past it, and what does it stop in
> error?** Examine both halves. Find a spelling of a refused thing that it lets
> through. Then find something that the repository's own instructions ask for, and
> that it now refuses. Such a refusal makes people work around it and turn it off. Also
> ask what it does when it cannot answer. A check that says "nothing found" when it
> failed to look is the worst of the three.
>
> Attack these classes, in this order. They are a floor, not the list. Each is a guarantee
> the design depends on, written in docs/durable-memory-for-disposable-agents.md, and a
> branch that breaks one breaks the record for everyone who reads it later.
> Verify a suspicion before you report it. You may write temporary tests in the worktree,
> and then delete them. When you finish, `git status` must be clean. If it was not clean
> when you started, say so. Never pipe `cargo` or `make` into `head` or `tail`. Redirect
> the output to a file, and read the file.
>
> 1. **The record's guarantees.** Every write is a new version and nothing is edited in
> place. Demotion leaves a tombstone. Deleting the index and running reindex rebuilds an
> identical index from the record alone. Find a path through the new code that edits a
> file in place, loses a version, or leaves the index knowing something the record does
> not.
> 2. **The filter and the labels.** Nothing above a session's handling appears in a search
> result, a hit count, a summary, or T0. Nothing leaves an export below its label. A
> bundle's `highest_label` is what is inside it. Find the read path that forgets the
> filter, or the field that leaks through a summary.
> 3. **Leases and claims.** No task ever has two holders. An expired lease returns the task
> to the pool with its handoff. A second `claim` while a lease holds is refused. Try two
> claimants at once, a heartbeat after expiry, a crash between claim and complete.
> 4. **The tests themselves.** Which new assertion would still pass if the behaviour it
> names were wrong? Check each one by making the behaviour wrong, not by reading it.
> 5. **Untrusted input.** Frontmatter is read from disk and from bundles other people wrote.
> A manifest names paths. Try a path that escapes the bundle directory, an id that is
> not an id, a digest that does not match, a label that is not one of the three, a
> `created` in the future, a body of a hundred megabytes.
> 6. **A guard, a script, or a check: what walks past it, and what does it wrongly stop?**
> Both halves. Find a spelling of the thing it refuses that it lets through. Then find
> something the repository's own instructions ask for that it now refuses, since that is
> what gets it worked around and switched off. And ask what it does when it cannot
> answer: a check that says "nothing found" when it failed to look is the worst of the
> three.
> Report only findings, the most serious first. For each finding, give:
>
> Verify a suspicion before reporting it. You may write throwaway tests inside the worktree
> and delete them; `git status` must be clean when you finish, and say so if it was not
> clean when you started. Never pipe `cargo` or `make` into `head` or `tail`; redirect to a
> file and read the file.
> - the `file:line`;
> - what is wrong;
> - a concrete scenario (inputs to wrong behaviour);
> - the smallest change that fixes it.
>
> Report findings only, most serious first. For each: the `file:line`, what is wrong, a
> concrete scenario (inputs to wrong behaviour), and the smallest change that fixes it.
> Separate **CONFIRMED** (you ran something that demonstrates it) from **PLAUSIBLE**
> (reasoning only). Say explicitly if a class has nothing in it. Do not pad the list, and
> do not state a claim you have not checked.
> Keep **CONFIRMED** findings (you ran something that shows the finding) apart from
> **PLAUSIBLE** findings (reasoning only). If a class has nothing in it, say so
> explicitly. Do not add items only to make the list longer. Do not state a claim that
> you did not check.

## When it reports

Verify each finding yourself before acting. Fix the real ones with the failing assertion
shown first. A finding you decline is declined in the commit that declines it. Then say in
the PR description that the pass ran and what came of it.
Verify each finding yourself before you act on it. Fix the real findings, and first show
the failing assertion for each fix. When you decline a finding, say so in the commit that
declines it. Then, in the PR description, say that the pass ran and what it found.

## Growing the list

The classes above are what the design says must hold. When a pass finds something that
fits none of them, add the class here rather than remembering it.
The classes above are what the design says must hold. A pass can find something that fits
none of them. Then add its class here. Do not only remember it.
Loading
Loading