keeper: drain the queue — 21 issues, 24 config improvements - #6
keeper: drain the queue — 21 issues, 24 config improvements#6bjones84arrow wants to merge 21 commits into
Conversation
The checkout rule blocked every operand form, including a plain branch change,
because that command cannot distinguish a branch from a pathspec. Its sibling
takes no pathspec and so cannot reach the destructive mode — it is now allowed
outright, and the deny message directs the user there.
The deny message also now states plainly that the block is hard: no phrase said
to Claude lifts it. The previous wording implied an approval path that nothing
actually honored, which sent users looking for a bypass that did not exist.
Tests: switch assertions flipped from blocked to allowed, plus new slashed-branch
and previous-branch ("-") cases. 70/70 pass.
fleet-launch stamps launch_epoch as it dispatches, so a ledger whose last
write lands in that same second belongs to a session that had not started
yet. Both readers admitted it: fleet-status.sh compared -lt, and
fleet-metrics.py had no launch gate at all.
Measured 2026-08-21 on jarvis — a prior session's ledger tied launch_epoch
exactly (1787271573) and rode a whole retro as a fleet member: shipped
5 -> 7, session-hours 7.4 -> 12.0, churn 5 reviews -> 7, severity
0/3/19 -> 1/5/28. /fleet-status also showed it ALIVE, sending the operator
after a phantom idle session for four consecutive checks.
Three limbs in fleet-metrics.py, because gating one changed nothing:
- the state-file filter, truncating st_mtime (a float) to whole seconds,
or `...573.448 <= ...573` passes and the gate is inert;
- the ledgerless pass, which re-adopted the excluded file off disk;
- the verdict scan, which globs files rather than going through the
session set — why churn stayed at 7 after sessions read 2.
Six regression arms, each mutation-verified against its own limb.
The existing check only required a `Collision edges:` line to be present, which an excuse satisfies. JA-148's verdict recorded "none wired — could not be created with available tooling. linear-cli exposes no relation subcommand" and skipped a step /quality-review Step 6 mandates, then recommended a /reflect to fix the toolchain — which would have filed config work against a gap that does not exist. `linear-cli relations add <BLOCKER> <BLOCKED> -r blocks` is in the linear skill's own command map; `relations --help` settles it in one call. Warns rather than blocks: refusing the write would turn a lost audit record into a blocked ship. An evidenced failure still warns by design — the warning asks for a command and its error, and both are then present.
Refuses `finish-detect-mode.sh pr` in an unattended run whose originating invocation never carried a `pr` token, plus its registration and the three skill docs that describe the flow it guards. Three sessions added `pr` on their own judgment — JA-390, JA-367 (shipped PR #239 after being dispatched `merge`), JA-415 — each citing the same circular evidence: "every recent issue shipped via PR", which is a product of the interactive convention rather than evidence about an unattended run. Prose had already lost here: jarvis CLAUDE.md 448ad05 stated the rule and JA-415 drifted hours later. Per fleet-retro doctrine a rule that existed and lost gets a mechanical guard, not more prose. Interactive flows are untouched — the guard fires only with an `auto` token in the dispatch chain, so a hand-typed `/finish pr` and `/loop /auto pr` both pass. Gates: hooks/finish-flow-guard.test.sh 19/0; settings.json parses; markdownlint clean on the three docs.
All five conflicts were additive-vs-additive; nothing was dropped.
settings.json both hooks registered (finish-flow-guard +
auto-deadline-gate); upstream's duplicate
skipAutoPermissionPrompt key dropped, local
model:opus and upstream outputStyle both kept
fleet-metrics.py parse_verdicts takes launch_epoch AND until; both
gates apply (prior-fleet exclusion vs window upper
edge); launch_epoch threaded through upstream's
session-scoped v_cutoff/v_until derivation
fleet-metrics.test.sh upstream's --until/--sessions arms + the
launch_epoch boundary arms
qr-write-verdict.sh the tooling-claim WARN + upstream's parentless and
origin-class WARNs, in that order
qr-write-verdict.test upstream keeps cases 14-15; the tooling-claim case
renumbered 14 -> 16 to stay contiguous
Gates: fleet-metrics 184/0, quality-review-write-verdict 57/0,
finish-flow-guard 19/0, settings.json parses. Re-ran fleet-metrics against
the live jarvis run: still 2 sessions / 7.4h / 5 reviews / 0-3-19.
…rine into standards/testing.md Three keeper issues filed the same subject at once, two of them against the same insertion point (agents/developer.md:67, which already carried five sub-bullets). Landing them separately would have made seven, which JA-353's own comment named as the risk: "three sub-bullets accreting separately under the same line will read as a list nobody finishes." - JA-319 — new standards/testing.md: the falsifiable-regression rule (restore by file copy, never git checkout/restore), the degenerate-shape table, and assert-the-requirement-not-that-the-code-ran. Its comment's JA-320 evidence opens the file: the mutation discipline as practised covers fix deltas, not the implementation under them (an accumulation test named for its requirement was a tautology; breaking the map's seeding left 3998 tests green). - JA-345 — the two read-time shapes that stay green against their own mutation: a subset matcher cannot see a missing key (117/117 green), and a fixture already satisfying the transformation exercises it as a no-op (98/98 green). - JA-353 — different values are not enough when the predicate has a decision boundary; the fixture pins which field only if the candidates straddle it (available: 5 / onHand: 8 both sit above <= 0, so the suite stayed green). agents/developer.md:67 gains one sub-bullet separating the protocol it already carries from fixture construction, pointing here. standards/README.md indexes it.
…ntations agree Reverting the exact behavior an assertion names is the first mutation, not the only one: an arm can redden under that revert and still pass under a different wrong implementation, because the fixture leaves a coincidental route to the expected result. Name the plausible wrong implementations and confirm the arm reddens for each; where one slips through, fix the fixture rather than adding assertions. Measured on JA-368: a severity-ordering arm reddened correctly when the sort was deleted, but mutating the comparator to sort by occurredAt ascending — a plausible wrong key, not a missing sort — left it green, because the fixture's CRITICAL was dated two weeks before the burst and an oldest-first sort reproduced "CRITICAL first" while consulting no severity at all. Folds in the issue comment's JA-374 enrichment, which supplies the independent recurrence its own verification had cut the framing down for: a requirement of the form "driven by X, not by Y" names Y as a mutation to run and a fixture value to supply, which the natural fixture omits precisely because Y is irrelevant. There, adding `if (addr.ShipToOther === false) return buildAddressLines(addr)` left 100/100 passing against three assertion-anchored arms. Also links the new standards/testing.md from the High-severity rule, for when a fix is fixture-side rather than assertion-side.
…wo artifacts and one guard on the wrong thing Three separate issues filed the same fix-delta collision; one more filed the scope guard. All four are silent-wrong-artifact failures, not new obligations. Issue-scope the fix-delta artifacts (JA-366, JA-334, JA-347). Both paths were cycle-scoped only, so tmp/ — never cleaned between sessions, and CLAUDE.md forbids removing it — accumulated every issue this repo had reviewed. Measured at 52 files (46 from two earlier issues), at 61, and at 38 spanning three days and three issues, across three independent sessions. A stale base is worse than a missing one because it disarms the snapshot paragraph's own `git diff -- <file>` fallback, which is keyed on absence: a previous issue's copy is present, readable and diffable, so a strayed file is diffed against another issue's content and diff exits 0 either way. The delta file is sharper still — its STOP condition fires only when the file is missing, empty or unreadable, none of which a prior issue's delta is — and under an imported NO_CLOBBER a plain `>` refuses the overwrite and leaves that delta in place to be passed as this run's, so the path is now written with `>|`. Covers all three cycle tokens; `deferred` and `corrective` are fixed strings that collide on every run. Widen the Step 1 scope-mismatch guard (JA-356). It named explicit positional paths, but auto-detected scope reaches the hazard identically and is the more common route: detect-issue-id.sh falls back to the latest commit subject, so any HEAD whose tip names a merged issue resolves to it whenever the branch name carries no ID. Live-reproduced on branch bjones84arrow/clear-jarvis-alerts, which resolved to JA-317 at exit 0 with a real populated tmp/quality-review-verdict-ja-317.md at VERDICT_STALE=1 — re-persisting would have cleared the stale flag on a verdict that no longer described the code.
… it closed Three issues, one shape: a fix that is locally correct while its siblings, its untested input classes, or its other exit paths stay broken — each costing a re-review cycle per instance. Item 2's completion family gains two bullets and item 4's sweep gains a bound. JA-283 — bound the class-shaped sweep so it terminates. Broadening is for finding the class, not fixing all of it inside this ticket, and the loop's convergence test cannot terminate work discovered mid-review. The delegation must produce a complete audit list with a disposition per site, fix only what the change reaches plus any divergence the sweep itself introduces, and report the rest through item 5's deferred lanes. Measured on JA-281: cycle 4 broadened repo-wide, cycle 5 found the next frontier plus two new Highs the partial sweep had itself introduced by stopping at a file boundary. Findings ran 3H+5M -> 3M -> 2M -> 3M -> 2H+5M, terminated only by the cycle ceiling. JA-347 — enumerate a function's exit paths when a finding names one. Three consecutive cycles each found one more `return` in the same function failing to populate a field the caller depended on. What closed the class was enumerating all eight and accounting for each: the enumeration, not the fix. JA-297 — enumerate an added guard's input classes. In JA-285, 4 of 9 High findings were defects in fixes ordered during the review, and one guard was rewritten three times, each version stating its intent correctly and never its inputs; two of them would have permanently stranded orders on the billing tail. Marked explicitly as a reporting obligation, not licence to widen the fix, since it sits beside three bullets that do mandate extra edits. The scope escape hatch now names the completion bullets instead of counting them — JA-347 asked for "two" to become "three", but JA-297 lands a fourth in the same block, so the count was the thing to remove. Acceptance demands all three reports. Per JA-347's explicit instruction, the complete-the-class family note and item 4's re-review sweep are untouched: its four members are handling paths, not class shapes, and the "all four" cross-references stay true.
Both sit in "Empirical claims: measure before you write", and both are claims the
existing text invites rather than catches.
JA-348 — deciding to leave a detail out is a claim too. The section's escape hatch
("if you can't measure it, write the narrower claim you did confirm") governs what
to write when measurement is out of reach; it never says the decision to narrow is
itself measurable, and read alone it licenses hedging past a one-command check.
Measured: a build-failure note said "a module-load env assertion" rather than naming
the error string, on the unverified premise that an earlier-imported database
client's constructor would throw first. It defers its validation and does not throw,
so the named string is exactly what the operator gets — the hedge traded away their
only search anchor to cover a case that never arises.
JA-356 — a comment justifying why a guard exists names a failure you have never
seen. The guard is present, so the failure never happens, and re-reading the guard
confirms the sentence every time. Check the triggering condition instead: point at
the type that admits null, the caller that passes it, the page that reads the
column. Three such comments in one session each named a plausible-but-wrong failure
mode; two were settled by reading the named precondition, which is why that is the
primary check and remove-and-observe is the fallback for conditions turning on
runtime semantics no line shows.
…what you think
Both are cases where a type-level edit was believed to make a mistake impossible and
did not, each costing a review cycle. rules/typescript.md path-injects on .ts/.tsx
edits, so it is in context exactly when someone is editing a signature.
JA-355 — prefer required over optional-with-a-default. A default answers the
question on behalf of every existing caller and nothing flags the one it answers
wrongly: omitting a defaulted parameter is not a type error under any TypeScript
configuration, and no Biome rule inspects call sites for inherited defaults.
Discriminator: if you would have to open the call sites to know whether the default
is right, make it required and let tsc open them. Measured on JA-317, where
`options?: { provenPerDraft?: boolean }` defaulting to true was right for the new
call site and wrong for a pre-existing sibling that kept compiling and kept telling
an operator to hand-void a draft only collectively implicated.
JA-364 — a structural parameter type does not prevent a wrong value. The
excess-property check fires only on a fresh object literal checked against a target
type, never on a variable, so wrapping `qty: number` in an object relocates the
mistake rather than removing it. Both remedies carry their limits: a brand must
reach the producing type and is defeated at the mint site; passing the whole object
still admits a same-shaped sibling. Measured on JA-359 — after the change, both
`f({ outstanding: line.ordered })` and `f(line)` produced zero errors.
…ination did not cover
JA-323 — parallel delegations falsify each other's comments. This is the uncovered
complement of write-target exclusivity, which resolves parallel hazards by making
file sets disjoint: for comments, disjoint is not sufficient. A delegate writes
comments true of the tree it can see while a sibling changes the behaviour one of
them asserts, and nothing catches it — a comment cannot fail a type check, each
delegate's own check passes, and /quality-review bars a comment-accuracy audit as a
re-review lens. In JA-320 the data half falsified two of the UI half's comments over
provably disjoint files. Sits immediately after write-target exclusivity, since it
only makes sense as that section's next sentence.
JA-342 — scope vs correctness prohibitions in a delegation prompt. Scope
prohibitions are legitimately categorical because they ship with the fix-dispatch
escape hatch; a correctness prohibition ("X won't work here") must name the failing
shape and what a passing version looks like. The hatch cannot rescue it — it fires
when a fix needs MORE than the dispatch allows, and a delegate complying by doing
less never trips it. In JA-337 a brief banned extracting a label-formatting helper;
the delegate complied, review graded a HIGH (a literal wrong id at the send call
left all 4312 tests green), and the fix was an extraction inside the banned
category, forcing the orchestrator to reverse its own instruction mid-fix.
JA-314 — a write-capable delegation that dies mid-task leaves an unknown tree.
Every existing crash branch is scoped to reviewer dispatches or to a delegation that
completed, so a developer fix batch dying mid-edit falls through all of them.
Establish state (git status, the check gate, wt-baseline.sh diff in wt mode), then
route: nothing changed -> resume by agentId; partial and green -> resume or
re-dispatch scoped to the remainder; partial and red -> restore green first. Never
re-dispatch the original prompt verbatim against a partially-edited tree — it was
composed for a clean one, so the delegate double-applies or conflicts silently.
That is the trap in /auto's retry policy, whose idempotence covers the issue and the
worktree but not a half-applied delegation's edits.
…ches the answer
JA-339 — a precedent is not a safety argument. "The sibling does it this way"
establishes that a shape is conventional, never that it is safe here. Before copying
a guard, lock, retry, timeout, cache, or self-healing mechanism, read the new call
site and write one sentence: what happens when this misfires here, and how that
differs from where you copied it. The sentence is the point — you cannot write it
without leaving the function you are editing, and the invariant that forbids the
copy is usually documented at the caller. Measured on JA-332: copying two siblings'
5-minute staleness OR onto claimInvoiceEmail graded Critical, because the
permanently-held claim WAS the anti-double-send guard and said so one file away; a
TTL would have re-emailed customers who already had their invoice.
This is a qualifier on existing text, not a new rule. problem-solving.md's PROCEED
DIRECTLY list said the opposite unqualified ("Fix aligns with existing patterns and
conventions"), and CLAUDE.md restates the same guidance in its Decision-Making
section — which is what most sessions actually have in context, so leaving it
unqualified would have left the licensing text intact at the more-read site. Both
now carry the qualifier. The stakes: a copied TTL is localized, small and
contract-free, so it clears every fix-now gate and auto-applies.
JA-428 — asserting a capability does not exist. Never record that a tool, flag, or
command is unavailable, and never skip a required step on that basis, without having
run the check. This is worse than ignoring a rule: the agent concludes compliance is
impossible and documents it persuasively enough to pass its own review, so nothing
downstream re-examines it. Measured on JA-148, whose verdict recorded that linear-cli
exposes no relation subcommand — it is in the linear skill's own command map, and
`relations --help` settles it in one call — skipped a mandated step, and recommended
a /reflect against a toolchain gap that does not exist.
…sibling issue Only the issue being shipped may sit behind a close keyword; every other issue ID in a commit message, PR title, or PR description is referenced bare. This happened. PR #211 shipped JA-328 and contained "Next in sequence: JA-329 (receipt send for settled invoices, which also closes JA-299's archive gap)" — a forward reference describing what a FUTURE issue would do. On merge JA-299 moved to Done with all three success criteria unchecked and nothing in the repo implementing them. PR #188's "this mirrors the fix JA-269 shipped" — fix as a noun — linked JA-269 to the wrong PR; already Done, so that one only cost a bad link. The same PR #211 is its own negative control: five sibling IDs mentioned bare, zero linked, zero moved. Two corrections to the obvious version of the rule, both from the reviewer. The keyword set is bigger than the familiar three — Linear also closes on complete* and implement*, and "which also completes JA-299's archive gap" reads more naturally than "closes" in that exact sentence. And the mechanism is LINEAR's GitHub integration, not GitHub's own closing keywords, which only close GitHub #N issues and cannot touch Linear state; a rule saying "GitHub" sends the reader to the wrong docs. Structural rather than a one-off: across the 25 most recent merged PRs, 22 mention at least one sibling issue ID (median ~3, max 12), because /quality-review files deferred items as follow-up issues and /finish Step 4 asks for follow-up work. Placed after PR Guidelines rather than the filing's stale anchor. That section already owns the identical rule class in [skip ci] — an external system parsing a magic token out of prose, across the same three surfaces — and the new subsection names that kinship. Three satellite edits put it at the composition sites: /finish Step 9 (which composes PR bodies itself and forbids dispatching pr-update, so a rule living only in pr-update would be missed), pr-update's checklist (which /finish does not run), and bugfix.md's Related Issues line, which was teaching the hazard under a heading whose whole purpose is listing other issues.
…check the landing state Two premises the skill assumed and never verified, both writing edges that strand work. JA-428 — refuse to wire anything while a fleet is live. The skill is documented as the "before" step but nothing detects that a fleet is already running, and Step 3's wiring is destructive when one is. Check tmp/fleet-deadline.json for an unpassed deadline_epoch, or any tmp/auto-state-*.json newer than launch_epoch with status active; report the audit read-only and stop before Step 3's writes. Measured 2026-08-21: JA-303 was demoted specified -> backburner-specified at 01:54:34 and two blocks edges were wired onto it at 02:21:33, stranding JA-313 and JA-290 behind an issue nothing will pick up, on top of five already stranded by the demotion. The lane count went stale twice in forty minutes. Placed at Step 1 rather than Step 3 because the lane count is as invalidated as the edges are. JA-401 — confirm the team has a completed-type landing state before wiring edges. next-candidates.sh clears a blocker only once its state's type is completed or canceled, so an edge whose blocker ships into a started state never releases: the dependent stays hidden until a human transitions it by hand. Wire the edges anyway when no such state exists — they are correct about the collision — but say so and treat every chain as manual. The JA-401 evidence is deliberately stated as history: JA has since added a Ready for Release (completed) state, so the measurement (2026-08-18: three edges, three hand transitions, one session idle ~2.3h with In Review as the landing state) describes a condition that no longer holds on this team. The check is what tells a reader which situation they are in; asserting the premise in the present tense would have been false within days of applying.
… two linear-cli gotchas JA-401 (issue comment) — every statuses-list call in scripts/ read through linear-cli's Statuses cache, so a workflow state added minutes ago is invisible and the error blames the team. Symptom: mark-ready-for-release.sh exits 1 with "no Ready-For-Release state found for team 'JA'. Set it manually." while the state exists and every matcher handles it. Measured against JA with a 24-minute-old cache: the cached path returned nothing and the script exited 1, --no-cache returned "Ready for Release", and `linear-cli cache clear` fixed the cached path. It appears only in the window right after someone changes a workflow state — exactly when these scripts matter most — and self-heals on expiry, which makes it intermittent and easy to dismiss. All five call sites now pass --no-cache: mark-ready-for-release.sh (both, name and UUID), linear-set-state.sh, linear-create-child.sh, linear-file-improvement.sh. Cost is one API round-trip on paths that already make several. Recorded as gotcha #23 so a new script inherits the reason rather than repeating the bug. Sibling suites green: linear-set-state 15/15, linear-create-child 21/21. JA-401 — gotcha #22: filtering `relations list` on `blocks` drops every `blocked by` row, so the result reads as "no blockers" precisely when blockers are what you ran it for. Measured on JA-396, which printed one blocks and one related row under that filter while carrying two blocked-by rows that decided whether it was pickable — one sibling matched only because its TITLE contained "duplicated". Numbered #22, not the filing's #21, which is taken. Explicitly distinguished from CLAUDE.md's line-scoped pipeline and wrapped-phrase traps: both tokens are on the line, and the failure is that the term you grepped does not subsume the sibling term it looks like it should. JA-334 — the residual clause its proposal 2 had that gotcha #12 lacked: do not add a priorityLabel read-back guard to linear-create-child.sh. Unlike --state, -p/--priority takes on both create and update, so the guard would fire every run and retry a write that already succeeded. The rest of that proposal was already covered.
…porting a killed session as clean Two flags that misread this team's conventions, plus a vacuous-pass defect found in the suite that guards them. Merge reconciliation accepted only a `Merge <ID>` subject, which only /finish merge produces — a /finish pr ship lands as `Merge pull request #N from <owner>/<branch>`, carrying the id lowercased inside the branch name. On any team defaulting to the pr flow, every shipped issue tripped the flag, so the "Landed without a Merge <ID> commit" line was pure noise. Validated against real jarvis history: JA-291, JA-321 and JA-367 all flagged before and resolve after, while JA-425 (a genuine `Merge JA-425`) still resolves through the original arm. The reason the flag can see the PR merge at all is that the merge commit's BODY carries the uppercase id, so --grep returns it while --oneline prints a subject where the id is only lowercased. A session killed mid-loop reported as clean. It arms wakeups normally and simply never terminates, so the "never armed a ScheduleWakeup" flag cannot see it — that one requires wakeups == 0, making the two mutually exclusive. Its signature is a still-active ledger with zero stop-wakeups, and the data was already on the aggregate row. Killing a session in `claude agents` is the documented way to abort in-flight work, so the case is routine, and it always strands a Linear claim and a preserved worktree — precisely what the retro exists to surface. Regression fixture covers a PR-flow ship, a merge-flow ship, a genuinely-unmerged ship, a killed session and a drained control. Three mutations run, each reddening its own arm and only that arm: dropping the PR-shape arm, removing the died_active condition, and widening it to `wakeups > 0` alone. Also fixes ck_has/ck_lacks, which ran `grep -qF "$2"` — so a needle starting with `-`, i.e. every markdown bullet in the report, was parsed as a flag. BSD grep exits 2 with a usage error, which ck_lacks reads as "absent" and passes vacuously, on exactly the assertions most likely to be written against report bullets. Both now use `grep -qF -e`.
…ite enumeration The quality bar bans file lists outright and /spec names the reason as stale assumptions, but neither says what to do when the work is site enumeration — a normalization sweep, an encoding pass, "these N call sites are unguarded". That shape is common, and with nowhere sanctioned to put the census the author writes bare line numbers into the criteria. This is a gap, not a compliance failure: the existing rule forbids the list without providing the alternative the work needs. Three forms that degrade honestly instead of lying: describe each site by greppable code shape rather than line number; anchor the count to a commit sha and say line numbers are a convenience, not a specification; state the grep that produces the count so pickup re-derives it. Measured on JA 2026-08-20. JA-292 was certified naming six raw Status sites at specific lines; by grooming, four had been fixed by adjacent work and two of its cited line numbers no longer existed. JA-410, certified hours earlier the same session, went stale when a sibling merged into the file it names. Nothing detects either, and in a fleet it decays fast because parallel sessions land adjacent work continuously. The quality-bar checkbox banning file lists now carries the carve-out inline, so a reader who meets the ban does not have to already know the exception exists — that was the gap, and leaving it only in the prose above would have half-fixed it.
Reviewer side (/keeper adjudication standard): - mode probe requires push permission, not just the reflect.keeper flag - route-to-project lane + mixed-item split rule - evidence self-contained in shared prose; no foreign-team issue IDs (test fixtures exempt) - over-prescription bar: a rule earns its place by contradicting what an unprompted agent actually does - verification-skipped filings forfeit spot-checking: re-verify all Producer side (/reflect): - bar-check is not skippable; a 'verification disabled' note never licenses a filing (#6 carried three such batches) - gate 8: over-prescription (must name the observed wrong behavior) - gate 9: an exception must update every sentence it falsifies - proposal bodies written self-contained, no team-local vocabulary
rosskevin
left a comment
There was a problem hiding this comment.
PR #6 review — verdict
Thanks for this — the volume is real (21 issues, 24 proposals), the strongest items are genuinely good, and several will merge nearly as-is. But the PR is not mergeable in its current shape: the branch carries unadjudicated ride-along commits, the body's headline quality claim does not hold up, and the highest-blast-radius addition has four defects. The path forward is a revised proposal PR (see "Revision path" at the end), not fixes stacked onto this branch.
Every claim below was verified adversarially on a clean worktree of this branch — nothing here is taken from the PR body on trust.
Keeper rulings (not open for relitigation in the revision)
- The "Refuse to wire anything while a fleet is live" auto-prep block is rejected outright.
/auto-prepmust not refuse to operate mid-fleet. The hazard you observed is partly real but misdiagnosed — see item 20 for the refiling shape that would be accepted. - No foreign-team issue IDs in shared-config prose. Evidence is stated self-contained — mechanism, date, magnitude ("Measured 2026-08-21: a certified issue was demoted mid-fleet…"). The IDs' durable home is your commit messages and this PR. Test fixtures replaying real history are exempt. This touches nearly every accepted item; the per-item table marks each file needing the pass.
backburner-specified(and any team-local label vocabulary) does not enter shared config, even as evidence prose. House parking mechanisms aresoloandneeds decision/human.- A config rule earns its place by contradicting what an unprompted agent actually does, not by being true. Well-known doctrine agents already follow is over-prescription.
standards/testing.mdis the poster case — see item 4. - Project-scoped value routes to your project's
.claude/, whatever file the diff touches. One SPLIT below (item 17) and one ride-along (the Prisma guard).
Blocking defects (fix in the revision)
- D1 —
pnpm checkis red as delivered; the body's "green gate" claim is false.scripts/quality-review-write-verdict.test.shresolves its subject via$HOME/.claude, so from any other checkout it tests the installed script. Your own new case 16 fails 3 arms (54/57) in exactly the topology a proposal worktree creates. The guard itself is sound. Fix:SCRIPT="$(cd "$(dirname "$0")" && pwd)/quality-review-write-verdict.sh", matching the 10 sibling suites. - D2 — a test arm that can never fail, in the commit that adds it.
fleet-metrics.test.sh:737asserts ond.get('ledgerless', [])— a JSON keyfleet-metrics.pynever emits — so it is always[]. Mutation-proved: deleting the exact limb the arm's comment names leaves it green. Repoint atledger_missing. (This is the "advertises coverage, pins nothing" shape your own quality-reviewer paragraph grades High.) - D3 —
finish-flow-guard.shfalse-BLOCKs the correct flow.…finish-detect-mode.sh merge > tmp/pr.log 2>&1is hard-blocked because the PR-regex matches the log filename. Match only the first token of the argument tail. - D4 — the same hook hard-blocks an explicit human instruction ("open a PR for JA-9 instead of merging" after
/loop /auto→ BLOCK, verified), and its message tells the operator to discard the run's context. A hard block with no approve path must not override a human's stated intent. - D5 — the hook's most complex limb has zero coverage. Deleting the entire transcript-scan fallback leaves the suite 19/19 green. Add an arm that exercises it.
- D6 —
finish-flow-guard.test.shis not inpnpm test. For a file this PR creates, that is a gap in the PR, not a pre-existing condition. - D8 —
standards/testing.mdcontradictsagents/developer.mdand the claimed consolidation was not performed (developer.md went five sub-bullets → six; the two files now give opposite fixture advice). See item 4 for the required shape. - D9 —
skills/quality-review/SKILL.md: the new item-4 bound contradicts item 2. See item 1 for the required reconciliation. - D11 — the killed-mid-loop flag calls every live session dead. It fires on ledger state alone with no
pid/pidStartliveness check (the ledger carries both). Run mid-fleet, every running session reports as killed. - D12 — the close-keywords mechanism paragraph is under-scoped and the satellites miss the path that matters most. Linear scans PR titles, PR descriptions, and commit messages (verified against Linear's docs); your paragraph omits commits, and no satellite guards
/finishStep 7 or the merge-commit message — the unattended/autodefault. Thetemplates/bugfix.mdnote also sits on a GitHub#Nline the section itself says cannot touch Linear. - D13 — the PR falsifies an existing sentence.
skills/auto-prep/SKILL.mdStep 3 still claims issue-spec "bans implementation-planning file lists altogether"; your JA-422 edit adds an exception to that ban. Update the sentence in the same pass. - D14 — stale operator message, now pinned by your new test.
fleet-metrics.py:1831still saysLanded without a `Merge <ID>` committhough the check accepts two landing shapes;fleet-metrics.test.sh:792greps that exact string. - D15 — the JA-347 "count → name" fix still enumerates. The three-item escape-hatch list goes stale the same way the count did, and silently omits the new guard bullet. State the property ("the completion bullets above").
- D16 —
fleet-status.sh-lt→-leis an untested one-sided boundary flip on the operator's only during-view, justified by a single observed tie. Add coverage or evidence. - D17 —
rules/typescript.mdcites an ambient count ("its 22 parameter-adjacent rules") — banned byrules/comments.md; it rots on the next Biome release. State the property. - D18 — the
--no-cachesweep is complete for scripts (all five call sites verified) butskills/dependency-updater/SKILL.md:99documents the same recipe without it. Add it or scope the gotcha's "keep it that way" sentence.
Verified sound — kept from the body's claims
All three fleet-metrics mutation claims hold exactly as stated (each mutation reddens its own arm and only that arm; 190/190 baseline). The keep-both-sides merge produced no duplicated prose (shingle-scanned against every paragraph in the tree). settings.json registers the hook exactly once. --no-cache is a real flag on statuses list (0.3.27). No PR edit relaxes a ship gate.
Per-item verdicts
Lanes: accept · accept-c (accept with the named condition) · reject · SPLIT · split-out (refile separately). "reword" = ruling 2 (strip foreign-team IDs / restate evidence self-contained) in the named file.
| # | Item | Verdict | Notes |
|---|---|---|---|
| 1 | JA-283 — bound the class-shaped sweep | accept-c | D9. Ruling: item 2's "fix every sibling in a named file" wins inside named files; the new bound governs sites beyond them. Reword the bound to say so. |
| 2 | JA-297 — enumerate an added guard's input classes | accept | Reporting-only, correctly scoped. |
| 3 | JA-314 — dead write-capable delegation recovery | accept | reword standards/agent-coordination.md. |
| 4 | JA-319/345/353 — new standards/testing.md |
accept-c | D8 + ruling 4. Ruling: compress ~75 → ~25-30 lines keeping only the measured tells (tautological fixture, boundary rule, subset-matcher blindness, already-clean no-op, "name the mutation"); actually move the overlapping developer.md bullets in; reconcile the fixture advice as one rule — different records must straddle the predicate's boundary — owned here, with developer.md pointing at it. |
| 5 | JA-323 — parallel delegations falsify comments | accept | reword standards/agent-coordination.md. |
| 6 | JA-334/347/366 — issue-scoped qr-fix paths | accept | reword skills/quality-review/SKILL.md. |
| 7 | JA-334 (2nd) — gotcha #12 priorityLabel clause | accept | |
| 8 | JA-336 — Linear close keywords + satellites | accept-c | D12. Ruling: accept extended — correct the mechanism paragraph (commits are scanned), add the /finish Step 7 / merge-commit guard, move the bugfix-template note to a line that can actually close a Linear issue. reword standards/git.md, skills/finish/SKILL.md. |
| 9 | JA-339 — a precedent is not a safety argument | accept | reword standards/problem-solving.md. |
| 10 | JA-342 — scope vs correctness prohibitions | accept | Best-argued item in the batch. reword standards/agent-coordination.md. |
| 11 | JA-347 (2nd) — exit-path bullet + escape hatch | accept-c | D15: restate the escape hatch as a property, not a list. |
| 12 | JA-348 — omitting a detail is a claim too | accept | |
| 13 | JA-355 — required over optional-with-a-default | accept-c | D17: drop the "22 rules" count. reword rules/typescript.md. |
| 14 | JA-356 — guard-justification + scope-mismatch guard | accept | |
| 15 | JA-364 — structural type ≠ wrong-value prevention | accept | reword rules/typescript.md. |
| 16 | JA-373 — one mutation is not proof | accept | Consider splitting the long paragraph. |
| 17 | JA-401 — completed-type landing-state check | SPLIT | Rule is team-neutral and accepted. The trailing "JA has since added a Ready for Release state" clause is project status → your project CLAUDE.md, not shared config. Keep the dated measurement, reworded team-neutral. |
| 18 | JA-401 — gotcha #23 + --no-cache ×5 |
accept-c | D18: cover skills/dependency-updater/SKILL.md:99 or scope the sentence. reword gotcha #23. |
| 19 | JA-396 — gotcha #22 relations-filter trap | accept | reword skills/linear/SKILL.md. |
| 20 | JA-401/428 — refuse to wire while a fleet is live | reject (ruling 1) | The real hazard is "the blocker is not fleet-eligible," which bites whenever the edge is wired, not only mid-fleet — and your detection recipe would have fail-closed permanently: its liveness signature is byte-identical to what your own fleet-metrics change defines as killed mid-loop, and killed-session ledgers persist until the next launch. Accepted refiling shape: validate the blocker's eligibility at wire time and report ineligible blockers — no refusal, no fleet detection. Drops cleanly; nothing else in the PR references it. |
| 21 | JA-422 — fleet-metrics landing shapes + killed flag | accept-c | Mutation claims fully verified. Ruling: accept conditional on a pid/pidStart liveness gate on the flag (D11), the operator message naming both landing shapes (D14), and the vacuous arm repointed (D2). reword the fleet-metrics.py comment; test fixtures exempt. |
| 22 | JA-422 — issue-spec site-enumeration census | accept-c | D13: correct the auto-prep "bans … altogether" sentence in the same pass. reword standards/issue-spec.md. |
| 23 | JA-428 — asserting a capability does not exist | accept | reword standards/problem-solving.md. |
| 24 | verdict-script unavailability WARN + case 16 | accept-c | Blocked on D1 (the $HOME coupling). reword the script comment; fixtures exempt. |
| 25 | launch_epoch tie fix + coverage | accept-c | Both limbs mutation-verified load-bearing. Fix D2; note D16. |
| 26 | finish-flow-guard hook + suite + settings + prose | split-out | D3, D4, D5, D6 — four defects in a hard-block hook registered on every machine. The prose halves (skills/auto, skills/full, skills/finish) are sound, tighten, and stay in the main revision. Ruling: the hook (+ suite + settings.json registration) is split out of this batch — refile it as its own proposal PR once the first-token fix, an intent path for explicit human instructions (approvable block, not exit 2), fallback coverage, and pnpm test wiring are done. Hard-block hooks get solo review. reword the hook header and its operator-facing BLOCK message; test fixtures exempt. |
Ride-along commits (outside the adjudicated set — dropped from the revision)
These were never adjudicated by anyone and must not appear in the revised PR:
0c085705"local: settings" — rejected."model": "opus"in the sharedsettings.jsonis a personal preference that would change every teammate's default model.b7218d5d"allow git switch" — rejected as written. Its central claim — git fails closed on switch — covers only conflicting changes; non-conflicting uncommitted work rides silently onto the target branch, which is precisely the multi-session hazard the block exists for and that the deleted message named. The checkout-side hardening in the same commit is worth refiling separately.da1f6065— split. The/finishfresh-staging rule and the general "review is read-only — never mutate databases or external services" sentence look acceptable: refile them through the normal flow. The four Prisma specifics and the shadow-DB incident belong in your Prisma project's.claude/rules/(stack-scoped, rides the samepaths:injection as the schema files).- The merge commits and
89fe386bdissolve naturally in the rebuild.
Your machine
reflect.keeper is set to true on your machine but the repo grants you no push access — that mismatch is why your runs adjudicated the whole queue and stranded commits on local main. Unset it (git -C ~/.claude config --unset reflect.keeper), run /update, and /keeper will run in contributor mode from now on. It also currently skews your /next ranking.
Revision path
- Unset the flag and
/update(above). - Run
/keeper— contributor mode will gather your still-open filings and your local main's commits, and this verdict table is the adjudication input: carry the accepts (with their conditions and reword passes), drop the rejects and ride-alongs, and it will open a fresh proposal PR and restore your main to a pure clone behind a rescue branch. - This PR closes when the replacement opens, with a pointer here.
Upstream follow-ups (ours, not yours — do not fix in the revision)
The review also surfaced pre-existing issues on main, recorded here so they aren't lost: the un--e grep -qF helper pair survives in fleet-launch.test.sh, fleet-blockers.test.sh, linear-set-state.test.sh, next-candidates.test.sh (trap armed, no live vacuous pass today); reap-worktrees.test.sh has the same $HOME coupling as D1; fleet-status.sh has no test suite at all.
- grep -qF -- in the four ck_has/ck_lacks helper pairs that could parse a dash-leading needle as a flag and pass vacuously - reap-worktrees.test.sh resolves the script under test and its library from its own checkout, not $HOME — proposal-worktree topology now tests the branch's code - new fleet-status.test.sh (36 assertions, hermetic, wired into pnpm test), pinning deadline states, liveness, scoping (launch-second tie shown), shipped/failed joins, and Linear-down degradation - fleet-status.sh: treat merge-queue's 'no repos registered' as an empty queue — found by the new suite on its first run
Adopted from PR #6 (item 24, accept-c). Adds a content guard on the `Collision edges:` line: a verdict that satisfies the presence check with a tooling-unavailability excuse now warns, naming the command that does exist. Publishes regardless — refusing the write would turn a lost audit record into a blocked ship. Conditions applied: - D1: quality-review-write-verdict.test.sh resolved its subject through $HOME/.claude, so from any other checkout it tested the INSTALLED script (and the new case 16 failed 3 arms in a proposal worktree's topology). Now resolves via $(dirname "$0"), matching the 10 sibling suites. - Reword pass: foreign-team issue IDs stripped from the script comment and the case header; the measurement (2026-08-21, the verdict text, the skipped step) is stated self-contained. Test-fixture IDs are data and stay. Suite: 57 passed / 0 failed.
Adopted from PR #6 (item 25, accept-c). A ledger (or verdict) whose last write lands at or before launch_epoch belongs to a PRIOR fleet: fleet-launch stamps that epoch as it dispatches, so such a session had not started when the write happened. Three independent limbs: - fleet-metrics.py pass 1 now drops same-second-or-older ledgers, comparing an int()-truncated mtime (launch_epoch is whole seconds; the float remainder made a raw `<=` inert). - The ledgerless pass's seen-key set is seeded with those dropped keys, or its adopt-the-real-ledger branch reads the same file straight back off disk. - parse_verdicts takes launch_epoch: verdict files are globbed off disk, not attributed through the session set, so they need their own gate. - fleet-status.sh scoping flips `-lt` to `-le` to match. Conditions applied: - D2: the new coverage's `d.get('ledgerless', [])` arm asserted on a JSON key the script never emits, so it was always `[]` — it could not fail in the commit that added it. Repointed at the emitted `ledger_missing` key and mutation-confirmed: it reddens when the seed and the adopt-the-real-ledger branch are both gone. Every other limb has a reddening arm too (seed removed, `<=`→`<`, int() dropped, verdict gate removed each redden and only their own arms). - D16 (untested one-sided boundary flip): main now carries scripts/fleet-status.test.sh, whose case 6 deliberately pinned the tie-SHOWN behavior. Flipped with the change — tie hidden, count 1→2, and the arm's comment restates the new pinned semantics. Case 7's count is unchanged. - Reword pass: measurement stated self-contained (date + magnitudes); no foreign-team IDs in prose or comments. Test-fixture IDs are data and stay. Suites: fleet-metrics 184/184, fleet-status 36/36.
Adopted from PR #6 (item 21, accept-c). - git_merged accepts both landing shapes: `/finish merge` writes `Merge <ID>`, `/finish pr` lands through GitHub as `Merge pull request #N from <owner>/<branch>` with the id only inside the branch name, lowercased. One shape produced a false flag on every ship of the other. - New flag for the killed-mid-loop shape — a still-`active` ledger with wakeups armed and no stop-wakeup. The existing "never armed a ScheduleWakeup" flag requires wakeups == 0, so the two are mutually exclusive and this shape reported as clean. Conditions applied: - D11: the flag is gated on a pid/pidStart liveness check (new session_alive, mirroring scripts/fleet-status.sh including the ps -o lstart= whitespace normalization). A live session has the same ledger signature as a killed one; ungated, a retro run mid-fleet called every running session dead. The docstring records that in a fleet this pid is the shared fleet root, which is the suppression this gate wants. - D14: the operator message named only one landing shape though the check now accepts two. It names both, and the suite's pinning grep moved with it. Coverage is mutation-proved — each limb reddens its own arm and only that arm: PR-shape alternative removed -> "PR-flow ship resolves"; flag removed -> "mid-loop death flagged"; liveness gate removed -> "live session not flagged" (the live001 control exists for exactly that arm); message reverted -> "message names both shapes". Also adds `--` to this suite's ck_has/ck_lacks: the new `- None. Every session armed its heartbeat` needle starts with a dash, which BSD grep parses as a flag and exits 2 on — which ck_lacks reads as "absent" and passes vacuously. Matches scripts/fleet-status.test.sh. Reword pass: measurement stated self-contained (date + magnitudes), no foreign-team IDs in comments. Test-fixture IDs are data and stay. Suite: 192 passed / 0 failed.
Adopted from PR #6 (item 18, accept-c). `linear-cli`'s Statuses cache made a workflow state added minutes ago invisible to every state-resolution call site, and the resulting error blamed the team ("no Ready-For-Release state found for team '<KEY>'"). All five call sites in scripts/ now pass --no-cache; skills/linear/SKILL.md gains gotcha #23. Conditions applied: - D18: skills/dependency-updater/SKILL.md documented linear-create-child.sh's recipe without the flag, which the gotcha's "keep it that way" sentence would have contradicted. Updated to match the script. - The gotcha states the property and names the grep that produces the current roster instead of enumerating the four scripts (rules/comments.md), which also scopes "keep it that way" to the scripts it actually governs. - Reword pass: no foreign-team label or ID in the prose; the measurement (date, cache age, the three observed outcomes) stated self-contained. Suites: linear-set-state 15/15, linear-create-child 21/21.
Adopted from PR #6 (item 19, accept). Filtering `relations list` on `blocks` drops every `blocked by` row: the two types render as distinct strings, `blocks` is not a substring of `blocked by`, and the pipeline exits 0 — so the result reads as "no blockers" and inverts the answer. Reword pass: the measurement (date, the row counts, the title-match false positive) stated self-contained; no foreign-team issue ID in the prose.
Adopted from PR #6 (item 7, accept). The structural `priorityLabel` null reads as a failed write, which invites a read-back guard on the one script that sets priority. Unlike `--state`, `-p/--priority` takes on both create and update, so such a guard would fire on every run and retry a write that already succeeded. Says so at the gotcha.
Adopted from PR #6 (item 5, accept). Write-target exclusivity resolves parallel hazards by making file sets disjoint. For comments, disjoint is not sufficient: a delegate writes comments true of the tree it can see, and a sibling changing the asserted behaviour lands them false with nothing to catch it — no type check, no re-read prompt, and each delegate's own gate passes. Reword pass: the measurement (date, the two falsified comments, the split shape) stated self-contained; no foreign-team issue ID in the prose.
…ompt Adopted from PR #6 (item 10, accept). A scope prohibition is safe as a blanket ban because it ships with a release valve (the fix-dispatch escape hatch). A correctness prohibition — "X won't work here" — is not: the delegate cannot tell "this kind of X is useless" from "no X", complies with the broader reading, and the hatch never fires because complying means doing LESS. Name the shape that fails and what a passing version looks like, or state the goal and prohibit nothing. Reword pass: the measurement (date, the banned extraction, the HIGH its absence earned, the 4312-test mutation) stated self-contained; no foreign-team issue ID.
… mid-task Adopted from PR #6 (item 3, accept). A dead delegate's partial output is narration, not a filesystem record. Establish the real tree state first (git status --porcelain, the check gate, wt-baseline diff in wt mode), then route on what you find — and never re-dispatch the original prompt verbatim against a partially-edited tree, which double-applies silently because neither outcome errors. Reword pass: no foreign-team issue IDs; the section states mechanism rather than provenance.
Adopted from PR #6 (item 9, accept). "The sibling does it this way" establishes that a shape is conventional, never that it is safe at the new call site. Before transplanting a guard, lock, retry, timeout, cache, or self-healing mechanism, read the new caller and write the sentence that says what a misfire costs HERE. The PROCEED DIRECTLY bullet and CLAUDE.md's autonomy paragraph both now point at it. Reword pass: the measurement (date, the copied 5-minute staleness OR, the Critical it earned, the anti-double-send guard documented at the caller) stated self-contained; no foreign-team issue ID in the prose.
Adopted from PR #6 (item 23, accept). A capability claim is a factual claim about this machine. Never record that a tool, flag, or command is unavailable — and never skip a required step on that basis — without running the check. When a call genuinely fails, record the command and its exact error, not the generalization. Reword pass: the measurement (date, the verdict text, the skipped step, the misdirected /reflect) stated self-contained; no foreign-team issue ID in prose.
Adopted from PR #6 (item 12, accept). Leaving a concrete out of a comment because you believe it varies rests on a belief about behavior, and that belief needs the same measurement an assertion would. The existing "write the narrower claim" escape is for a measurement genuinely out of reach, not a way past a one-command check.
…breadth Adopted from PR #6 (item 14, accept). - rules/comments.md: a comment explaining why a guard exists names a failure the guard's own presence prevents, so re-reading the guard confirms it every time. Check the triggering condition instead; where runtime semantics decide it, remove the guard, run what covers it, and record what actually broke. - skills/quality-review/SKILL.md: the scope-mismatch guard read as covering only explicit positional paths. It fires on auto-detected scope too — the more common route, since detect-issue-id.sh falls back to the latest commit subject. The test is the diff, not the issue's state.
…ault Adopted from PR #6 (item 13, accept-c). When a new parameter selects between behaviors, a default answers the question on behalf of every existing caller and nothing flags the one it answers wrongly: omitting a defaulted parameter is not a type error under any tsconfig, and no Biome rule inspects call sites for inherited defaults. Required makes tsc enumerate the call sites — the audit you cannot otherwise prove complete. Conditions applied: - D17: the ambient "its 22 parameter-adjacent rules" count is gone (banned by rules/comments.md; it rots on the next Biome release). States the property instead — every parameter-adjacent rule constrains the declaration site. - Reword pass: the measurement (date, the shipped signature, the sibling it answered wrongly, two-true/two-false across four call sites) stated self-contained; no foreign-team issue ID in the prose.
… value Adopted from PR #6 (item 15, accept). Wrapping `qty: number` in `{ qty: number }` does not stop a caller passing the wrong field: the excess-property check fires only on a fresh object literal, so both `f({ qty: x.otherField })` and `f(x)` compile clean. Branding at the source or removing the choice from the call site help; a signature-only edit does not. Write the mistake and run `tsc --noEmit` before claiming a type change forecloses it. Reword pass: the measurement (date, the dispatch's claim, the two expressions that produced zero errors) stated self-contained; no foreign-team issue ID.
Adopted from PR #6 (item 16, accept). An arm can redden under the revert of the behavior it names and still pass under a different wrong implementation, because the fixture leaves a coincidental route to the expected result. Name the plausible wrong implementations and redden the arm for each; where one slips through, fix the fixture, not the assertion count. Also: grep the subject file for the mutated token, since a non-matching sed/perl leaves the suite green and reads as "the arm pins nothing". Split into two paragraphs per the verdict's note on the item.
Adopted from PR #6 (item 4, accept-c). New standard owning what neither agents/developer.md nor agents/quality-reviewer.md covers: choosing a fixture that discriminates. Conditions applied (ruling 4 + D8): - Compressed from the proposed 75 lines to 43, keeping only the measured tells — the tautological fixture, the boundary rule, subset-matcher blindness, the already-clean no-op, and "name the mutation". Dropped the protocol restatement (developer.md owns it), the "assert the requirement, not that the code ran" section, and the "one mutation is not proof" section (quality-reviewer.md owns that one). Slightly over the ~25-30 target because two developer.md bullets moved in. - The consolidation is actually performed: agents/developer.md's fixture bullets — the A-over-B distinguishability rule and the UI state-transition rule — MOVED here, replaced by one pointer. Its sub-bullet list goes 5 -> 4, not 5 -> 6. - The two files gave opposite fixture advice ("seed a genuinely different record" vs "different values are not enough"). Reconciled as one rule owned here: different records must straddle the predicate's boundary — and both failure halves (same record; divergent values on the same side) are named. - agents/quality-reviewer.md and standards/README.md point at the new file. - Reword pass: every measurement is dated and self-contained; no foreign-team issue IDs.
Adopted from PR #6 (item 6, accept). `tmp/qr-fix-base-<cycle>/` and `tmp/qr-fix-delta-<cycle>.diff` collide across runs: tmp/ is never cleaned, the cycle integer restarts at 1, and `deferred` / `corrective` are fixed strings. Both paths now carry the issue id (literal `no-issue` when Step 1 resolved none) and the snapshot directory is cleared before use. A stale base is worse than a missing one — it disarms the snapshot-absent fallback, since a previous issue's copy is present, readable, and diffable. The delta file is written with `>|` so an imported NO_CLOBBER cannot leave a previous run's file in place.
Adopted from PR #6 (item 11, accept-c). An exit-path finding — a `return` omitting a field, an early return skipping a stamp or an audit write — is class-shaped inside one function. The dispatch now requires enumerating every `return` with a per-exit verdict, and the enumeration rather than the fix is what shows the class is closed. Stays inside the function named in the Findings, so it never reaches past the file-list bound. Condition applied (D15): the escape hatch's exclusion list is stated as a property — "completing any class they name, within the named files" — instead of enumerating the completion bullets. The proposed three-item list would go stale the way the count it replaced did, and it already omitted the guard bullet.
Adopted from PR #6 (item 2, accept). A guard specified only by its triggering case over-fires on a class nobody enumerated, which is how a fix becomes the next cycle's finding. The fix dispatch now requires the delegate to report the guard's verdict for each input class it will actually receive — triggering case, well-formed, absent/empty, malformed, and input where the deciding data is itself missing. Reporting-only: it is not licence to widen the fix.
Adopted from PR #6 (item 1, accept-c). Broadening a re-review for a class-shaped finding is for FINDING the class; the convergence test cannot terminate work discovered mid-review. The sweep now carries a bound: a complete audit list with a disposition per site, fixes limited to what the change reaches, and everything else reported as deferred items. Condition applied (D9): as proposed, the bound contradicted item 2's fix-dispatch rule, which requires fixing EVERY sibling in a file named in the Findings regardless of reachability. Per the keeper ruling, item 2 wins inside the named files and the new bound governs only the sites the sweep reaches beyond them — stated explicitly in the bound and in the delegation wording it prescribes. Reword pass: the measurement (date, the cycle-4/5 frontier, the two Highs the partial sweep introduced, the findings series) stated self-contained; no foreign-team issue ID.
… them Adopted from PR #6 (item 8, accept-c, extended per the keeper ruling). New standards/git.md section: only the issue being shipped may sit behind a Linear close keyword; every other ID is bare or behind a non-closing keyword. The closing set is larger than the familiar three and the match is positional, not grammatical — it fires on a close verb adjacent to an ID even when the word is a noun. Conditions applied (D12): - Mechanism corrected: Linear scans commit messages as well as PR titles and descriptions. The proposal named only the two PR surfaces. - Satellites now cover the path that matters most for unattended runs: /finish Step 7's commit message and Step 9's merge-commit subject, alongside Step 9.3's PR body and pr-update's pre-publish checklist. - The bugfix template's note moved off the GitHub `#N` line — which the section itself says cannot touch Linear state — onto a Linear line that can. Reword pass: the measurements (date, the forward-reference close with three unchecked criteria, the noun-`fix` mislink, 22 of 25 PRs mentioning siblings, the negative control) stated self-contained; no foreign-team issue IDs or PR numbers.
…ng edges Adopted from PR #6 (item 17, SPLIT — the team-neutral half). next-candidates.sh clears a blocker only once its state's type is `completed` (or `canceled`), so an edge whose blocker ships into a `started` state never releases: the dependent stays hidden until a human transitions it by hand and an unattended fleet idles the lane. Step 3 now reads the team's state types first and, when no completed-type landing state exists, still wires the edges but reports every chain as manual. Split applied: the proposal's trailing "<team> has since added a Ready for Release state" clause is project status, not shared config, and is dropped entirely — it routes to the contributor's project CLAUDE.md. The dated measurement stays, reworded team-neutral (no team key, no foreign issue ID).
Adopted from PR #6 (item 22, accept-c). When the work IS site enumeration, the file-list ban leaves nowhere sanctioned to put the census, so authors write bare line numbers into criteria. The census now has a form that degrades honestly: greppable code shapes, anchored to a commit, with the grep that re-derives the count. Condition applied (D13): skills/auto-prep/SKILL.md Step 3 still asserted that the spec bar "bans implementation-planning file lists altogether" — which this change falsifies. Corrected in the same pass, and its edge-classification reasoning now covers both reasons a path can survive certification. Reword pass: the measurement (date, six sites down to two by grooming, the sibling that went stale within hours) stated self-contained; no foreign-team issue IDs.
… is authoritative Adopted from PR #6 (item 26, split-out — prose halves only). An unattended run's flow is whatever token the invocation carried. Never infer `pr` from repo history, a memory, a CLAUDE.md, or a sibling session: "every recent issue shipped via PR" is a product of the interactive convention, not evidence about this run, and treating it as evidence is circular. /auto's Arguments and Step 3, /full's args composition, and /finish's Step 0 normalization all say so; /finish stops rather than substituting the other flow. Scope: the hook (hooks/finish-flow-guard.sh), its suite, and its settings.json registration are NOT adopted — the keeper split them out for solo review (D3-D6), so every reference to the hook is stripped from this prose. The rules stand on their own without it. Reword pass: the measurement (date, three consecutive drifted issues, the PR shipped against a `merge` dispatch) stated self-contained; no foreign-team issue IDs or PR numbers.
Two items from PR #6's unadjudicated ride-along commit da1f606, accepted on merits and taken through the normal flow. - /finish Step 7: derive the staging list from a fresh `git status --short` at commit time, never from a list captured earlier in the session. Review-fix cycles touch files beyond the original implementation set, and staging from a stale list ships a commit whose halves don't match — local `pnpm check` passes on the working tree while the committed tree fails CI's type check. - agents/quality-reviewer.md: review is read-only verification; never run state-mutating commands against databases or external services. The Prisma-specific details and the shadow-DB incident are deliberately NOT adopted — they are stack-scoped and route to the contributor's project .claude/rules/, where they ride the same paths: injection as the schema files.
|
Adopted. The accepted subset of this PR now lives on Landed (with conditions from the review): all 20 accepted/accept-c items, the item-17 split (team-neutral rule kept, team-status clause dropped), the item-26 prose halves, and two ride-along adoptions judged on merits — the Not landed: the refuse-to-wire-mid-fleet block (rejected — see the review for the eligibility-check shape that would be accepted as a refiling); the Your machine: everything accepted is now upstream, so cleanup is short — Thanks — the strongest items here (the scope-vs-correctness rule, the delegation hazards, the gotchas) made the shared config genuinely better. |
Drains the
keeperqueue: 21 Linear issues, 29 proposals, adjudicated in one pass. 24 applied, 1 rejected as already covered, 1 routed out of scope.Opened as a fork PR rather than pushed to main because this account has
permissions.push: falseonalienfast/claude. That is also why the first 8 commits here are pre-existing — every prior/keeperrun on this machine committed and could not push.reflect.keeperis set totruelocally, which is worth reconciling: either the flag is wrong for this machine or the access grant is missing.Gate:
pnpm checkgreen — markdownlint 0 errors across 79 files, every script/hook suite passing. Plushooks/finish-flow-guard.test.shandscripts/linear-create-child.test.sh, which are not inpackage.json'stestscript (worth adding — flagged, not fixed here).Consolidations
Three sets of filings described one defect each and land as one edit rather than three:
/quality-reviewfix-delta path collision, independently, from three sessions (measured at 52, 61, and 38 stale files).agents/developer.md— which already had five sub-bullets. Newstandards/testing.mdowns it; the agents point to it.skills/auto-prep/SKILL.md, and JA-422, JA-428 both targetscripts/fleet-metrics.py. Applied together, as those issues' comments asked.Per-issue
skills/quality-review/SKILL.mdskills/quality-review/SKILL.mdstandards/agent-coordination.mdstandards/testing.md(new)standards/agent-coordination.mdskills/quality-review/SKILL.md,skills/linear/SKILL.mdstandards/git.md+ 3 satellitesstandards/problem-solving.md,CLAUDE.mdstandards/agent-coordination.mdstandards/testing.mdskills/quality-review/SKILL.mdrules/comments.mdstandards/testing.mdrules/typescript.mdrules/comments.md,skills/quality-review/SKILL.mdrules/typescript.mdskills/quality-review/SKILL.mdagents/quality-reviewer.mdskills/auto-prep/SKILL.md,skills/linear/SKILL.md, 4 scriptsscripts/fleet-metrics.py+ test,standards/issue-spec.mdskills/auto-prep/SKILL.md,standards/problem-solving.mdJudgment calls worth a look
In Review(astartedstate) was JA's landing state; JA has since addedReady for Release(completed). The generic guard still holds for any team, so it is applied with the evidence stated as dated history rather than a live condition.### Safe Alternativesheading that is not instandards/git.md. Placed after### PR Guidelinesinstead, beside### [skip ci]— the same rule class (an external system parsing a magic token out of prose) across the same three surfaces.fleet-metrics.test.sh'sck_has/ck_lacksrangrep -qF "$2", so a needle starting with-— every markdown bullet in the report — was parsed as a flag; BSD grep exits 2, whichck_lacksreads as "absent" and passes. Both helpers now usegrep -qF -e. Found while adding coverage for JA-422./reflect's primary noise guard being inoperative across three runs is unaddressed by this PR and probably wants its own issue.Verification
Both
fleet-metrics.pychanges are mutation-verified: dropping the PR-shape merge arm reddenslanding: PR-flow ship resolves; removing thedied_activecondition reddenskilled: mid-loop death flagged; widening it towakeups > 0alone reddenskilled: drained control clean. Each mutation reddens its own arm and only that arm. Suite 190/190.The merge-shape fix was also validated against real history rather than the fixture alone — JA-291, JA-321 and JA-367 all false-flagged before and resolve after, while JA-425 (a genuine
Merge JA-425) still resolves through the original arm.