Agent guide
For a coding agent driving drover. Flat and imperative, with the exit code against every refusal. A person wants Lifecycle instead, which is the same material with the reasoning behind each rule.
Read the generated instructions before this page. drover agents renders this project's own paths, verify command table, label vocabulary and frontmatter schema from its .drover.toml. This page is what is true of drover everywhere; drover agents is what is true here, and it cannot describe a command surface the installed binary lacks.
drover agents # this project's instructions, from its config
drover --help # every command by kind, the safety classes, the exit codes
drover <cmd> --help # a worked Examples: block and a Safety: line, per command
The root listing is grouped by kind, so read the order out of Lifecycle rather than off the listing.
Is this a drover project
Test for the file, at the repository root:
test -e "$(git rev-parse --show-toplevel)/.drover.toml" && echo "drover project"
Do not probe with drover check. A missing binary exits 127 and a project with findings exits 2, so the exit code answers a different question.
The binary walks up to .drover.toml on its own, so once the file is there you can run drover from anywhere in the tree.
Ids
Bare in every command: 1, 2b, B1. Item ids explains the namespaces. Zero-padded in filenames only (B01-<slug>.md), so reading a filename and typing what you saw is the reliable way to get it wrong. drover status prints the form to type, and a padded id is refused with the bare form spelled out rather than guessed at.
What you may run
| You may | Command |
|---|---|
| Read anything | agents check drives intake --list projects report follow-ups schema show skills status unaudited version, and run --dry-run |
| Write locally | new follow-up open follow-up close report --save agents --write skills --from skills --prune status -o status --render |
| Audit your own work | audit <id> claimed --code-by agent, or audit <id> reviewed --code-by agent |
| Assemble a prompt without launching anything | draft <id> and review <id>, both with no --runner |
| Reach the T3 Code server | t3 login, t3 discover and t3 thread --stop <id> |
What you may not run, and what stops you
drover exports DROVER_ACTOR=agent into every session it launches, so these refuse where you are and exit 3. You will not have to remember the list; the refusal names the command and the reason.
The variable reaches only a session drover started itself. So drover also writes .drover/session into the tree each session works in, a JSON object naming the same actor, session id and project root, and every command reads it, walking up from the working directory, whenever DROVER_ACTOR is unset. A session another program opened in that tree (a sandbox thread, an agent a person started by hand in a drive's worktree) is sealed the same way, and audit --code-by agent reads its session id from the marker. The walk stops at the first .drover/session or .drover.toml, so a marker in a parent repository does not seal a project checked out below it. A refusal the marker caused says so, naming the file, its session and whether the drover that wrote it is still running, with the way out for each. A launch is refused only while that drover runs, and then with the text of the occupied-tree refusal rather than the nested-launch one, because the process reading the marker may be a person; a marker whose drover is gone still seals the operator commands until someone removes it, but the next launch into that tree replaces it.
| Refuses inside a session | Why it is not yours |
|---|---|
accept / unaccept |
A run cannot accept its own requirements. A person reads the drafted list and accepts it, which is the one boundary drover does not automate. |
amend / touch / fold |
Each changes or vouches for an accepted contract, and a session would be moving the target it is measured against. |
live |
It runs a check against real hosts, which no session drover launched may reach. |
finish |
The operator merges, so the operator records the merge. |
mark-merged / unmark-merged |
The ledger half of the same thing: a merge nobody else witnessed, or a withdrawal contradicting a row somebody already read. |
intake <n> |
It relabels a live issue on a tracker other people read. |
open-pr |
An open pull request asserts that a branch may merge, and no session makes that assertion about its own work. |
signoff |
It certifies a requirement the session was told not to attempt. |
drive / supervise |
Each launches sessions unattended on the operator's behalf, and no launched session may start that. |
Separately, a launched session cannot launch another. run, draft --runner, review --runner, t3 run, t3 thread --new and t3 thread --from-event refuse where you are, because nesting spends a session nobody asked for and the operator sees one run while paying for two. Report what you found and let them start the next one. run --dry-run stays open, and is how you see what a run would carry. t3 run is the one runner that is drover itself: drover launches it as the session, and it runs as that session only when its parent process is the drover that wrote the tree's .drover/session. Run from inside any session, it is a nested launch like the others.
finish --force exists and is not an escape hatch. Its refusal on a branch it cannot see merged is the design.
audit is deliberately open, at every level. A review session is independent of the session that implemented the work, drover launched both, and the actor variable cannot tell them apart. --code-by agent can: drover gives every launched session its own id, records it on the stage row, and resolves agent against the item's implement rows, so a review session drover launched counts as independent and the implement session auditing its own code does not. A FIX-IN-REVIEW correction no numbered requirement traces to is not code the level certifies, so it does not make the session an author of it; anything else does. drover refuses verified from a session that matches a --code-by entry and exits 3, naming the entry.
init, init --user, shelve and unshelve are open too. They write, but they certify nothing, and running init --verify "go test ./..." with a verify command the operator supplied is doing what you were told.
What the check is not
A boundary. The seal has two signals, the variable and the marker, and one opt-out: DROVER_ACTOR=operator, which a person exports to steer from inside a sealed tree and which reads as nobody launched, whatever the marker says. env -u DROVER_ACTOR defeats the variable and deleting the marker defeats the file, and drover does not pretend otherwise. It exists to stop a command being run because it appeared in a code block, not to stop one being run on purpose. If you find yourself reaching for the bypass, that is the signal to stop and report instead: the operator is cheaper to ask than a false ledger row is to unwind.
Exit codes
| Code | Means |
|---|---|
0 |
Clean. |
1 |
drover could not answer: unreadable config, no such id, gh unreachable, no project here. |
2 |
Findings against the project. Only check returns it. |
3 |
A rule drover enforces refused the operation, and nothing was written. |
Refusals you will hit
Each of these is deliberate. The message names the next step; do not work around it.
| Attempt | Exit | What to do |
|---|---|---|
run or run --dry-run on a scaffold |
3 | The stop marker is still there. A human writes the requirements and runs accept. |
draft on an item past intake |
3 | Its requirements are somebody's accepted contract. File what you found against the item that owns it. |
audit <id> verified --code-by <who>, where you match an entry |
3 | Record claimed or reviewed. Leave verified to a session or person that did not write the code. |
unaccept on a merged item |
3 | It shipped. The ledger row stands. |
finish on a branch that is not merged |
3 | Report it ready instead. |
finish or open-pr while run, draft or review holds the checkout, or finish while a live session holds the item's worktree |
3 | The refusal names the pid. A drive holding the checkout does not refuse either command, because its items run in worktrees of their own; a second drive still refuses. |
mark-merged, unmark-merged, open-pr or finish for an item a live drive lists as running |
3 | The drive is reading the forge for that item and records what it read afterwards. The refusal names the drive and its pid: wait with drover watch <drive> --until exit, or stop it with drover drive --stop, then run the command again. Every other item passes. |
open-pr |
3 | Also the operator's, and sealed for finish's reason: an open pull request asserts that a branch may merge, and the session that wrote the branch cannot make it. It refuses a merge gate that did not exit 0, a review whose most recent pass was not clean, a review that sent work back or an item that ran again since, and, with or without --force, a request for changes the cap refused that no amend, signoff or passing review has answered since, naming the refused-N.md file that holds its findings. Report the item ready. |
An operator command (accept, unaccept, signoff, intake, finish, mark-merged, unmark-merged, open-pr) inside a session drover launched |
3 | Report the item's state and let the operator run it. |
amend or touch inside a session drover launched |
3 | The contract is the operator's to change. Report the requirement you would amend and why. |
request-changes whose evidence names a requirement the body lacks, or says one was removed |
3 | Correct the evidence to the requirements the body holds. A contract change is the operator's amend. |
request-changes from a review whose findings name neither a numbered requirement nor BRANCH-REGRESSION |
3 | Name the requirement the finding fails, mark a real regression BRANCH-REGRESSION, or put the finding in the audit note. A round is spent only on the contract or the branch's own regressions. |
request-changes sending back a requirement the previous review request also named, with no invariant written since |
3 | Write the property those fixes keep breaking with drover invariant <id> -f <file>, then send the findings again. A finding marked --still-open or --operator is not counted. |
A launch (run, draft --runner, review --runner, t3 run, t3 thread --new, t3 thread --from-event) inside one |
3 | Report what you found. Nothing was written: the check runs before the branch switch and the stage row. |
drive |
3 | You will not run this one: it is the operator's, and it refuses inside a session anyway. It also refuses --for with --until, a project with no [run] review_runner, because a drive with no reviewer merges a night's work nobody read, a project with no [drive] merge_gate unless --no-merge is passed, because drive merges what it drives, a tree with no origin or an unreachable gh, because it pushes what it drives and opens the pull requests, and a runner that says it is not signed in, because every launch after that would park. |
shelve / unshelve |
3 | No shelf is configured, the id is already on it, or another item declares it on deps. Report it; do not configure a shelf to get past this. |
follow-up open / follow-up close |
3 | The [register] backend is not file, so the tracker owns the write: file the issue with the follow-up label and link it to its owner. open --owner also refuses an item not recorded merged, because an unmerged item owns nothing filed: send a defect in its work back with drover request-changes <id> -f {file}. |
link <id> <issue>... |
3 | The project declares no closes key, the tracker could not be read, or the issue is owned by another item. Link it to the item that owns it; do not relink somebody else's report to get past this. |
live <id> |
3 | It runs against real hosts, so it is the operator's and sealed: report the item instead. Outside a session it also refuses an item with no check or no branch, and a lock held past [live] wait. |
split <id> --into <kind> "<title>" -f <file> |
3 | No -f or --into, an empty proposal, a kind no namespace declares, a project with no deps key, or a dependency that would close a cycle. The scaffold is written before the deps line, so a refusal here leaves neither. |
invariant <id> -f <file> |
3 | No -f, empty text, or a write that would change the count of numbered requirements. An invariant is a property the code must keep; the finding that taught it goes back with request-changes. |
close-changes <id> |
3 | The body carries no review block, the latest review sent work back itself, or the write would change the count of numbered requirements. A block nobody wrote has nothing to discharge, and one this pass wrote is current: record the look with audit instead. |
request-changes <id> -f <file> |
3 | No -f, empty evidence, a write that would change the count of numbered requirements, evidence whose every finding traces to an [operator] requirement, or an item already sent back [review] max_review_rounds times. At the cap the findings are kept in <stem>-<slug>.refused-N.md and the item parks needs_decision for the operator; record the look with audit and file nothing. The requirements are the contract you are reviewing against; findings go below the review heading as prose or bullets. An [operator] requirement is the operator's to meet: record the look with audit and let the item reach its pull request. |
signoff <id> <n> <note> |
3 | It is the operator's, and sealed: the session was told not to attempt an [operator] requirement, so it cannot certify one. Say in your report what the operator has to check. |
resolve <id> <issue> <disposition> |
3 | The disposition or --code-by is missing or unknown, the item's closes key does not name that issue, or finish already wrote the disposition to the tracker. drover show <id> names what the item closes; --link widens it when this run fixed the issue. |
new <kind> <title> |
3 | The kind matches no namespace, the title yields no filename, a body already sits at that path, or --found-in names an item not recorded merged. A defect in an unmerged item's work goes back to its branch with drover request-changes <id> -f {file}; one in merged code is filed with new and no --found-in. drover new --help lists the kinds this project takes. |
accept <id> on a body citing a file another unmerged branch changes, with no deps edge between the two |
3 | Order them: the refusal names the deps edit for each order. You cannot accept, so report it to the operator with the edit you recommend. |
accept <id> on a body with more numbered requirements than the backlog's max_requirements (8 unless set) |
3 | Move what is its own change into another item with a deps line, or merge requirements that check one property. --yes accepts it anyway. You cannot accept, so report it to the operator with the split you recommend. |
fold <from> --into <to> |
3 | Either item is a scaffold or merged, <to> depends on <from>, another item depends on <from>, or the combined list passes [backlog] fold_max. It is also the operator's, and sealed: report the pair instead. |
new -f <file> or split -f <file> whose text holds an angle-bracket placeholder or a hold marker |
3 | Nothing was written. Write the variable part in braces, {name}, which the refusal spells out, or finish the line, and run it again. |
accept --dry-run <id> on a body accept would refuse |
3 | Nothing was written: the output is what accept would refuse with. Fix the body and run it again; it is read-only, so you may run it yourself. |
intake --list |
3 | The [register] backend is not github, so there is no tracker to take anything from. |
projects --forget <root> |
3 | The registry never named that root. drover projects --all lists every root it holds. |
skills --from |
3 | The project declares no [paths] skills, so there is nowhere to copy to. Report it rather than picking a directory. |
status --sev |
3 | The severity is outside the vocabulary. Use one the message lists. |
watch --until <event,...> |
3 | The drive exited, or stopped running, without emitting one of those events. Read what it did emit with drover attach; exit 1 instead means there is no such drive. |
status -o <path> |
3 | A file is already at the path. Pass --force only when replacing it is what you were asked to do. |
config --stamp |
3 | The binary carries no release version to write (a plain go build). The stamp is the operator's record of having reviewed the config, so leave it to them either way. |
| every project command | 3 | .drover.toml or a generated instruction block differs from what the binary renders. A session drover launched never meets this; outside one, report it rather than passing -I, since the stamp records a review you did not do. |
init |
3 | A .drover.toml is already there, so you are inside the project. Run drover status. |
agents --write |
3 | The target file carries the end marker above the begin marker. A human fixes them; drover will not guess which half is yours. |
init --user |
3 | A per-user config is already there, or there is no terminal to ask on. Report it. |
init supervise |
3 | No terminal and no --defaults, or DROVER_ACTOR is set. The policy is the operator's to choose; report it. |
supervise |
3 | A session drover launched cannot start a supervisor. |
finish as a supervisor |
3 | [supervise] merge_drive_pr is not supervisor, or the ledger shows no pull request row drive wrote for the branch, or the item's latest review pass is not a passed that drive recorded, or the pull request's head holds commits after the head that review read (named, with drover review <id>, and refused before anything merges); --force refuses at any policy. The refusal names the condition. Report the item and let the operator merge and finish it. |
agents --write that widens a supervise policy |
3 | The block's policy line records less than the config now grants, and nobody passed --yes or confirmed at a terminal. Nothing was written. Report the printed changes; widening is the operator's call. |
drive, close-changes, accept or request-changes --over-limit as a supervisor |
3 | The action is at operator or off in [supervise], so the operator owns it. Report the item, the action and what the operator has to run. |
--runner naming a runner [runners] does not declare, on run / draft / review |
1 | Use one the message lists. On finish and mark-merged the same name warns and records: there it is attribution, not a launch. |
A padded id (B01) |
1 | Retype it bare, as drover status prints it. |
Any follow-up read with gh unauthenticated |
1 | Say the follow-up list was unreadable. Do not report zero open follow-ups. |
Parse --json, never the table
--json is on status, show, unaudited, drive, drives, attach, check, follow-ups, intake --list, projects, skills, t3 discover and report. Each prints a sample object under Examples: in its own --help, and Automation carries every format with the field meanings.
A degraded read says so rather than reporting a zero. drover unaudited with gh down prints follow-ups unavailable, ranked on blast radius alone; treat that as unknown, never as none.
The loop, end to end
Where an agent sits in each step. The operator runs the steps marked operator; everything else is yours.
| Step | Command | Yours |
|---|---|---|
| Take in a tracker issue | drover intake <n> |
operator: it relabels a live issue |
| Draft the requirements | drover draft <id> |
yes |
| Accept the contract | drover accept <id>... |
operator: a run cannot accept its own requirements |
| Implement | drover run <id> --headless |
operator: a session cannot launch another |
| Review | drover review <id> --runner <x> --headless |
operator, same reason. drover review <id> with no runner is yours: it assembles the prompt and launches nothing. |
| Send the review's findings back to the branch | drover request-changes <id> -f <file> |
yes, and nothing else carries a finding into the next run |
| Record a property every later pass must keep | drover invariant <id> -f <file> |
yes, and nothing else survives the next round of request-changes |
| Clear a block whose findings the branch closed | drover close-changes <id> |
yes, and nothing else says so without parking the branch |
| Send work to its own contract | drover split <id> --into <kind> "<title>" -f <file> |
yes, and the operator accepts it before anything runs |
| Link an issue this run fixed | drover link <id> <n> |
yes |
Judge each closes issue |
drover resolve <id> <n> <disposition> --code-by <who> |
yes |
| Record the audit level | drover audit <id> <level> --code-by <who> |
yes, at the level your authorship allows |
| Merge | your forge | operator |
| Record the merge, write the tracker | drover finish <id> |
operator |
| Confirm nothing was left unjudged | drover check |
yes |
You can carry an item from a tracker issue to an audited review without the operator touching anything but intake, accept, the two launches, the merge and finish. Those six are the boundary: each either certifies work somebody else has to trust, or reaches a tracker other people read.
Between review and merge there is a loop, not a straight line. A requirement the review finds unmet goes back to the branch rather than to the tracker; see step 4 below.
A runner out of budget
A session refused for a spent account parks its item runner_fault and ends the drive, unless the runner named the instant it serves again and that instant falls inside the drive's --for or --until, in which case drive sleeps until then and relaunches. Claude Code states its reset twice: as an epoch on the last rate_limit_event before its result, which drive reads first, and as a sentence such as resets Oct 25 at 8pm (America/Los_Angeles), read in the zone it names when the event carries none. A weekly limit resets days out, and --until takes a time of day, so it never reaches past the next 24 hours: only --for (--for 48h) can cover such a reset. The park quotes the instant either way, so the operator can relaunch at it.
Reviewing a finished item
Start with drover review <id>. It assembles six things a reviewer would otherwise gather from as many commands: the numbered contract, each dependency with how well it is audited, the open follow-ups, each dependency's saved report, the run's self-report, and the verify command. The failure mode of gathering them by hand is skipping one silently.
Then do the part no tool can:
-
Check the code, not the summary. Read the run's requirement-to-test map before anything else: one line per numbered requirement naming the test that fails without the change. A requirement with no test is unmet. The self-report is unverified and it lies by omission. A requirement met by something unreachable, unowned or unconsumed is not met.
-
Treat a flagged dependency as unproven.
reviewmarks any dependency nobody independently confirmed. -
Re-run the verify command yourself, unless a merge gate runs it.
reviewprints the command. Under[drive] merge_gatethe merge already waits on a green head, so the brief asks for a build and the touched packages' tests instead. -
Send each finding to its owner. Four questions, in this order: does a numbered requirement reach it, would it break something in use, can you correct it here without the verify command noticing, and does any existing item own it.
git diff <trunk>...HEADnames what this run answers for, which decides who caused a defect rather than whether the pull request should stay open. Read them all before filing one; picking wrong costs in both directions, a backlog of rows nothing closes or a pull request that collects round after round.BRANCH-REQUIREMENT: a numbered requirement of the item you are reviewing, unmet, goes back to the branch. Do not file it. Review runs before the merge, so the branch is still open and no pull request exists yet: report which requirement failed and how you know, and the operator re-runsdrover run <id>, which returns to that branch. Then this review runs again. Severity does not enter here: the numbered list is the contract. An unmet requirement filed as an issue merges the item incomplete and leaves a report whose only owner just shipped.BRANCH-REGRESSION: a defect this branch's diff introduced that would break something in use goes back the same way, its heading markedBRANCH-REGRESSION, whether or not a requirement names it and on any pass. Anything smaller isFIX-IN-REVIEWorREPORT-ONLYand costs no round;request-changesrefuses a finding that names neither a requirement nor this token. An unmerged item has no bugs: a defect in its own work is unfinished implementation, and it is never filed. A typo, a stale comment or a dead link is stillFIX-IN-REVIEWby that row's own definition. Put one in the report and it evaporates; file it against another item and it falls on somebody who did not cause it.SPLIT-OUT: a finding whose fix changes a contract this item's numbered requirements never named goes to its own item, and this one blocks on it. Another subsystem, every caller of an interface, a storage or wire format.drover split <id> --into <kind> "<title>" -f <proposal>writes the scaffold and the dependency; the operator accepts it, because a session cannot accept its own contract. This item stays blocked and unmerged until that one merges. The finding and the blame stay where they were sent and only the implementation moves. Read the bar before reaching for this: a wider diff is not a different contract, a fix touching three files because the requirement reaches three files is still this item, and work you could describe as one more requirement on this list belongs on this list.FIX-IN-REVIEW: a defect no numbered requirement traces to, that no verify command can observe, and that you can correct in the file you found it in, is fixed in this pass. A typo, a stale comment, two sentences of prose that contradict each other, a dead link. Trace every requirement first, so the files a requirement reaches are written down before you edit anything. Confirmgit rev-parse --abbrev-ref HEADis the item's branch, becausereviewnever switches branches and a session standing elsewhere would commit onto the trunk. Re-run the checks that cover the fix after making it, commit it alone with areview:subject, push, and name every one in the audit note. Touch anything a requirement traces to and you wrote the code you are auditing: the honest row names you in--code-by, which cannot recordverified.BRANCH-BLOCKER: a pre-existing defect blocking a numbered requirement is fixed here. It is somebody else's code and this run's problem anyway, because the contract cannot be discharged without touching it. The pull request says why the diff is wider than the requirements name.FOLLOW-UP-TO-OWNER: a pre-existing defect outside the diff that blocks no requirement here, in code a merged item owns, is filed against that item and named in your report. An unmerged item owns nothing filed: its gaps go back to its own branch. Attribute to the item that owns the gap, not the one that noticed it. Ownership travels one hop, so confirm the run you expect to close it lists that owner as a direct dependency:drover run <id> --dry-run | grep -A20 'Open follow-ups'. An unowned row reaches no run.NEW-ITEM: a gap in merged code that no existing item owns and that blocks no requirement here is written as a new item, never a defect this branch's diff introduced.drover new <kind> "<title>" -f <file>scaffolds it, without--found-inbecause the gap predates this branch, and you name the new id in your report and the audit note. The-ffile is the finding, written for whoever writes the requirements: the file and line, the command that reproduces it with its actual output, and what is wrong. The audit note names the new id and does not carry the description. The scaffold is inert until a human writes the requirements and runsdrover accept, andacceptis one of the commands a launched session cannot run, so nothing you file here can schedule itself. A project with no intake stop marker has no inert scaffold,newrefuses, and the finding isREPORT-ONLY.REPORT-ONLY: anything you would not spend a run on, would not file, and cannot correct in this pass goes in the report and nowhere else.
-
Write anything going back to the branch into the item body.
drover request-changes <id> -f findings.mdThat file is the whole prompt the next run reads: your report is not in it, and neither is the audit note. Give it the file and line, the command that reproduces the gap and its actual output, and the outline of the fix. Every finding carries a test that fails on the branch as it stands, as a fenced block or the path and name of a test already on the branch; the next run commits it unchanged and makes it pass.
request-changesreplaces the section rather than appending, so say which findings are now settled; it refuses to change the numbered requirements and demotes a top-levelN.in your evidence to a bullet, because the body reads one as a requirement.A property the code must keep goes in with
drover invariant <id> -f <file>instead. That section appends and is never replaced, so it is where what this round taught you survives the next round's findings:drover invariant <id> -f invariants.mdThe difference decides whether the loop terminates. "The fix dropped the mode to 0644" is a finding and closes when the mode is restored. "Replacing an object preserves whatever mode an operator set" is an invariant, and the next four fixes are all judged against it. A loop that files only findings sends back five corrections that each pass the verify command and each break the property the one before it established.
A block whose findings this branch closed gets closed out, and closing it does not send anything back:
drover close-changes <id>That replaces the section with the statement that it is closed and writes no ledger row. Leave it standing instead and the next run reads settled gaps as current instructions, because the prompt outranks its own reading of the tree. Say "nothing came back" with
request-changesinstead and the row that writes falls inside this review, whereopen-prreads it as work still outstanding and[review] max_review_roundscounts the round: you refuse the branch you just closed out.-f <file>carries what you traced, per finding.One disposition per pass.
close-changesrefuses while this pass's ownrequest-changesrow stands, and refuses a body with no such section at all: a review that found nothing on a first pass records the look withauditand writes no body. -
Judge each issue the item says it resolves. The prompt lists them from the item's
closeskey, read from the tracker as it was assembled. Record one disposition each:drover resolve <id> <number> <fixed|partial|unrelated> --code-by <who> "<what shipped, and what did not>"This writes one ledger row and writes to no tracker;
finishdoes the close or the comment after the operator merges. Do not touch the tracker yourself. An issue you leave unjudged is onefinishnames and skips, anddrover checkreports asNO DISPOSITIONafterwards.An issue this run fixed that the
closeskey never named is not one of these:resolveexits 3 on it, because a number no item declared would otherwise end up as a comment on a stranger's report. Link it first, or in the same step:drover link <id> <number> drover resolve <id> <number> fixed --code-by agent --link "<what shipped>"Both check the number against the tracker and refuse one owned by another item. Reporting it instead is what leaves a fix and the report it answered as strangers:
drover checknames that stateUNLINKEDwhile the branch is open, and after the merge nothing does. -
Record what you checked, at the level your authorship allows. The note is the value: name what you re-checked and what you found, not that you looked. The most recent row wins rather than the strongest, so a level can be corrected in either direction. An over-claimed
verifiedis worse thanUNAUDITED, which is at least honest about what nobody knows. -
Stop at the merge point. A project whose
[supervise] merge_drive_prissupervisorlets a supervising session merge a pull request drive opened once its review passed and its checks are green; the generated instructions spell out the conditions. An agent session never merges.
Writing an item
drover new <kind> "<title>" scaffolds one. The scaffold is inert until a human accepts it, which is the point: requirements lifted mechanically out of a bug report are checkboxes nobody can falsify.
- The numbered list is the contract.
report --saveturns every top-level line opening with a number, a period and a space into an audit checkbox. A requirement written as prose is on no checklist and nobody will ever confirm it.drover checkreportsNO REQUIREMENTSon an accepted item without one, until it merges. - Cite files and lines. An item saying "improve the handler" produces a run that improves something; one citing
path/to/file.go:120-145produces a run that can be checked. - Cite files, not directories.
drive --jobsnever runs two items whose bodies cite an overlapping path side by side, and a directory overlaps every path below it (see Running items side by side). The dispatcher cannot tell a directory named for context from one the item will edit, so a directory in backticks holds the item apart from every item citing any file under it. Name a directory that is context only in plain prose, without backticks and without a file name after it.drover acceptprints a note for each directory a body cites, and the exit stays 0. - Frontmatter is a closed schema. An undeclared key is a parse error, because a typo'd
depswould read as no dependencies and render a blocked item READY.drover schema --frontmatterprints this project's contract.
Where doctrine belongs
Per-item, in [prompts] implement and [prompts] review, which reach the item, its dependencies, the selected verify command and the open follow-ups. A rule that lives only in AGENTS.md applies to whoever remembered to read AGENTS.md. See Prompt templates.