The gates
Everything this tool refuses, and why it refuses it rather than warning. A warning that fires on ordinary work gets muted within a week; a refusal that fires on ordinary work gets fixed.
The five verdict values
Every gate records one of five words, and three of them are passes. The distinction between the two failing words is the important one.
| Value | Meaning | Grade |
|---|---|---|
| yes | Measured, and it passed. | pass |
| n/a | Structurally nothing to measure: no branch, no deployed surface, no declared scope. | pass |
| exempt | Measured, and a refusal was the correct answer. A gated product refusing an unauthenticated probe is working. | pass |
| no | Measured, and it failed. A real red. | fail |
| skip | Nothing was measured, for a named reason. | fail |
A skip is not a pass. Neither
n/anorexemptclaims the build was verified, and every close that records one says so in words.
The seven gates a close measures
DONE requires all seven. Anything else is PARTIAL, with the failing gates named in the reason line, so a lane can't report DONE and then list a barred step underneath it.
merged
Refuses a branch whose content isn't on main. It compares the blob of every path the branch
touched in three trees: the merge base, the branch, and origin/main. Not git
ancestry, because a squash merge copies a branch's changes onto main and throws the commit
fingerprints away, so git branch --merged says "not merged" about work that's fully
shipped. A lane with no branch, running in place on a target that isn't a git repository, records
n/a.
green
Refuses a build that wasn't observed green: npm run build in the lane's own
checkout, exit 0. Running the close with --no-build records skip, not a
pass, so choosing not to build can't produce a DONE.
live
Refuses a deployment that wasn't proved to be serving this build. The close issues a GET at the URL the repo's policy row names and reads the body. Two proof forms:
- A commit echo: the deployment reports its own release, and the gate requires it to equal or contain the branch head. This is the only form that can't pass on stale bytes.
- A proof string that must appear in the response. The gate refuses a proof string that isn't new in this branch's own diff. A string already on main proves nothing about whether this deploy landed, and an assertion every build passes is not an assertion.
Credentials are named, never stored. A policy row carries the name of an environment variable;
the value is read at probe time and never printed, not in a verdict and not in an error. If the
variable is unset the probe isn't sent bare, because grading the resulting 401 would measure the
credential rather than the deployment. That case is a skip naming the variable.
renamed
Refuses a brief that still carries no closed prefix, because the next dispatch will read it as
live and fire it a second time. The lane id has to appear in the brief's filename as a whole
hyphen-delimited token, never as a substring, so lane ops-l1 doesn't match a file
belonging to ops-l14.
in-scope
Refuses a branch that touched files outside the scope its lane declared at open. This is the
other end of the allocator's promise: proving two scopes disjoint before dispatch is worth
nothing if a lane can then edit outside its declaration. Every path from the merge gate's own
diff is tested for containment in the declared scope, with a small allowlist for files a required
procedure regenerates. A lane that declared nothing records n/a, having already paid
for it by serializing against the whole repo.
findings
Refuses a problem raised with no solution attached. A report line in this shape:
FINDING: <what> | fix: <the fix> | size: small|medium|large | owner: <seat or owner>
A line missing the fix, a valid size, or an owner fails the gate and is quoted back verbatim.
A report with zero findings passes: the gate polices completeness, never quantity. A
small finding assigned to the human owner is refused outright, because a small thing
the lane could have done itself isn't a decision to hand upward.
no-side-files
Refuses a new tracker-shaped file on the bridge: REFERENCE-*.md,
SWEEP-*.md, TRIAGE-*.md. The project already has a tracker, and a second
static surface beside it is a place for facts to go stale.
A window is not an attribution: many lanes run at once, so a matching file counts against a
lane only if its body names the lane or the brief's title, or it sits in the lane's own branch
diff, or the lane's own report names it. The opt-out is printed, never inferred: a
Side-file: allowed line in the brief's header makes the gate n/a.
Three gates the grader supports and this driver does not measure
Named here so their absence is visible rather than mistaken for a pass. The grader accepts a verdict for each; the shipped close driver records them as not measured, and they don't affect the grade.
| Gate | What it would refuse |
|---|---|
| owner-way-in | A live deployment the owner's stored credential no longer opens. When it fails it leads the reason line, ahead of every other failure: a product the owner can't open is the first sentence, not a line item. |
| report-free | A report filename already taken, where writing would land on top of another lane's report. The derived filename carries the lane, so two sessions of one brief produce two different names, and a taken name gets a numbered variant. |
| roadmap | A brief that cites no board row, so finishing it moves nothing anybody can see. |
Three refusals that stop a close entirely
These aren't gates. A failing gate makes the close PARTIAL and the close still happens. These three abort it: nothing is recorded, nothing is renamed, no worktree is removed, no claim is released, and the same is true on a dry run. The disagreement is between the report and itself, and no grade can be recorded honestly until a person resolves it.
| Condition | What triggers it |
|---|---|
| no-status-word | The report carries no DONE, PARTIAL or BLOCKED. A report with no status word is unreadable as a scope claim, whatever the gates measured. |
| done-without-evidence | STATUS DONE with no Evidence: line naming the verification run, its
result and the date. Without one, the done- prefix means consumed and
nothing more. |
| done-honest-unaddressed | STATUS DONE beside a body that names a skipped, barred or deferred step. A reviewer's
objection has two valid answers, fix it or overrule it in writing, and silence is not a
third. An overruled: line naming the check lets the close through; nothing
else does. |
A report that says PARTIAL or BLOCKED in its own words is a separate rule and a softer one: it lowers a grade of DONE to match, and it never raises one. The status word inside the report is the only scope claim the lane gets to make, and it outranks a passing gate grade.
What the allocator refuses before a lane opens
Half the value is here, before any agent runs. None of these produce a warning; each one keeps the lane out of the FIRE NOW list and prints the reason.
| Refusal | Why |
|---|---|
| Scope overlap | The lane is queued behind the one it collides with, and the card names the paths where the two scopes intersect. Opening it anyway needs an explicit override, which is written into the ledger, because deciding to run it regardless is a decision somebody should be able to find later. |
| Repo at its writer cap | A repo declares how many sessions may hold it at once. A cap above 1 is only safe because every concurrent pair was proved disjoint by path comparison. |
| No policy row | A repo that isn't in the policy table routes nothing. Guessing its deploy style or verification method is how a change ships that nobody proved. |
| No readable target | A brief whose routing block names no repo isn't carded, and the router won't infer the target from the prose. |
| Unrecognized filename word | An unknown word routes nothing and is printed with the word it should have been, so a near-miss is caught rather than resurrecting a closed brief. |
| Already closed | A brief whose lane already filed a CLOSE record is finished work wearing a live filename. It's refused unless its header declares that it's meant to refire, and the card says so in both directions. |
| Held elsewhere | A repo belonging to another dispatcher is skipped by name rather than silently omitted. |
| Held until a date | A brief with a not-before date stays visible and carded the whole time it's held, rather than disappearing from the board on the day somebody needs to see it. |
| Existing branch or checkout | Opening a lane refuses to reuse a branch name or a checkout directory that already exists. Reusing a merged branch name is the consumed-name problem in git form. |
Two quieter refusals matter as much. A card whose scope is truncated by the display limit is counted and named, because a work queue that truncates without saying so reads as "there is nothing for you to do". And a lane left open with no close is flagged as orphaned and stops holding its slot, but is never auto-closed: writing a close record here would manufacture measurements nobody took.