Skip to content

keeper: drain the queue — 21 issues, 24 config improvements - #6

Closed
bjones84arrow wants to merge 21 commits into
alienfast:mainfrom
bjones84arrow:keeper/2026-08-21-queue-drain
Closed

keeper: drain the queue — 21 issues, 24 config improvements#6
bjones84arrow wants to merge 21 commits into
alienfast:mainfrom
bjones84arrow:keeper/2026-08-21-queue-drain

Conversation

@bjones84arrow

Copy link
Copy Markdown

Drains the keeper queue: 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: false on alienfast/claude. That is also why the first 8 commits here are pre-existing — every prior /keeper run on this machine committed and could not push. reflect.keeper is set to true locally, which is worth reconciling: either the flag is wrong for this machine or the access grant is missing.

Gate: pnpm check green — markdownlint 0 errors across 79 files, every script/hook suite passing. Plus hooks/finish-flow-guard.test.sh and scripts/linear-create-child.test.sh, which are not in package.json's test script (worth adding — flagged, not fixed here).

Consolidations

Three sets of filings described one defect each and land as one edit rather than three:

  • JA-334, JA-347, JA-366 all filed the same /quality-review fix-delta path collision, independently, from three sessions (measured at 52, 61, and 38 stale files).
  • JA-319, JA-345, JA-353 all filed test-falsifiability doctrine, two of them against the same insertion point in agents/developer.md — which already had five sub-bullets. New standards/testing.md owns it; the agents point to it.
  • JA-401, JA-428 both target skills/auto-prep/SKILL.md, and JA-422, JA-428 both target scripts/fleet-metrics.py. Applied together, as those issues' comments asked.

Per-issue

Issue Target Outcome
JA-283 skills/quality-review/SKILL.md applied — bound the class-shaped sweep so it terminates
JA-297 skills/quality-review/SKILL.md applied — enumerate an added guard's input classes
JA-314 standards/agent-coordination.md applied — recovery when a write-capable delegation dies mid-task
JA-319 standards/testing.md (new) applied — consolidated
JA-323 standards/agent-coordination.md 1 applied (parallel delegations falsify comments); 1 routed out — targets a project repo and is self-declared unverified
JA-334 skills/quality-review/SKILL.md, skills/linear/SKILL.md 1 applied (consolidated); 1 rejected — gotcha #12 already covers it, one missing clause folded in
JA-336 standards/git.md + 3 satellites applied — Linear close keywords in PR text
JA-339 standards/problem-solving.md, CLAUDE.md applied — a precedent is not a safety argument
JA-342 standards/agent-coordination.md applied — scope vs correctness prohibitions
JA-345 standards/testing.md applied — consolidated
JA-347 skills/quality-review/SKILL.md both applied — snapshot scoping, exit-path enumeration
JA-348 rules/comments.md applied — omitting a detail is a claim too
JA-353 standards/testing.md applied — consolidated
JA-355 rules/typescript.md applied — required over optional-with-a-default
JA-356 rules/comments.md, skills/quality-review/SKILL.md both applied — guard-justification comments, scope-mismatch guard
JA-364 rules/typescript.md applied — structural types do not prevent a wrong value
JA-366 skills/quality-review/SKILL.md applied — consolidated
JA-373 agents/quality-reviewer.md applied — one mutation is not proof
JA-401 skills/auto-prep/SKILL.md, skills/linear/SKILL.md, 4 scripts all three applied
JA-422 scripts/fleet-metrics.py + test, standards/issue-spec.md all three applied, with mutation-verified regression coverage
JA-428 skills/auto-prep/SKILL.md, standards/problem-solving.md both applied

Judgment calls worth a look

  • JA-347 asked for a count, got a name. Its proposal (2) asked the scope escape hatch's "two bullets above" to become "three". JA-297 landed a fourth bullet in the same block this same pass, so the count was the thing to remove — it now names the bullets. That is the staleness JA-347 was guarding against, fixed once.
  • JA-401's premise had healed. It was filed when In Review (a started state) was JA's landing state; JA has since added Ready 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.
  • JA-336's anchor did not exist. Its diff targeted a ### Safe Alternatives heading that is not in standards/git.md. Placed after ### PR Guidelines instead, beside ### [skip ci] — the same rule class (an external system parsing a magic token out of prose) across the same three surfaces.
  • A vacuous-pass defect in the suite that guards this work. fleet-metrics.test.sh's ck_has/ck_lacks ran grep -qF "$2", so a needle starting with - — every markdown bullet in the report — was parsed as a flag; BSD grep exits 2, which ck_lacks reads as "absent" and passes. Both helpers now use grep -qF -e. Found while adding coverage for JA-422.
  • Three consecutive fleet batches were filed with adversarial verification disabled (JA-401, JA-422, JA-428 all say so). Every claim was re-checked on disk here and all three held up, but /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.py changes are mutation-verified: dropping the PR-shape merge arm reddens landing: PR-flow ship resolves; removing the died_active condition reddens killed: mid-loop death flagged; widening it to wakeups > 0 alone reddens killed: 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.

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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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 rosskevin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

  1. The "Refuse to wire anything while a fleet is live" auto-prep block is rejected outright. /auto-prep must 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.
  2. 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.
  3. backburner-specified (and any team-local label vocabulary) does not enter shared config, even as evidence prose. House parking mechanisms are solo and needs decision/human.
  4. 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.md is the poster case — see item 4.
  5. 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 check is red as delivered; the body's "green gate" claim is false. scripts/quality-review-write-verdict.test.sh resolves 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:737 asserts on d.get('ledgerless', []) — a JSON key fleet-metrics.py never emits — so it is always []. Mutation-proved: deleting the exact limb the arm's comment names leaves it green. Repoint at ledger_missing. (This is the "advertises coverage, pins nothing" shape your own quality-reviewer paragraph grades High.)
  • D3 — finish-flow-guard.sh false-BLOCKs the correct flow. …finish-detect-mode.sh merge > tmp/pr.log 2>&1 is 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.sh is not in pnpm test. For a file this PR creates, that is a gap in the PR, not a pre-existing condition.
  • D8 — standards/testing.md contradicts agents/developer.md and 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/pidStart liveness 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 /finish Step 7 or the merge-commit message — the unattended /auto default. The templates/bugfix.md note also sits on a GitHub #N line the section itself says cannot touch Linear.
  • D13 — the PR falsifies an existing sentence. skills/auto-prep/SKILL.md Step 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:1831 still says Landed without a `Merge <ID>` commit though the check accepts two landing shapes; fleet-metrics.test.sh:792 greps 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-le is 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.md cites an ambient count ("its 22 parameter-adjacent rules") — banned by rules/comments.md; it rots on the next Biome release. State the property.
  • D18 — the --no-cache sweep is complete for scripts (all five call sites verified) but skills/dependency-updater/SKILL.md:99 documents 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 shared settings.json is 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 /finish fresh-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 same paths: injection as the schema files).
  • The merge commits and 89fe386b dissolve 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

  1. Unset the flag and /update (above).
  2. 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.
  3. 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.

rosskevin added a commit that referenced this pull request Aug 21, 2026
- 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
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
…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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
… 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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
…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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
…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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
… 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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
… 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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
…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).
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
… 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.
rosskevin added a commit that referenced this pull request Aug 21, 2026
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.
@rosskevin

Copy link
Copy Markdown
Member

Adopted. The accepted subset of this PR now lives on main (175035d..028ab3a, 26 commits, one per item, each carrying its issue ID) — implemented keeper-side with the review's conditions applied, since a revision round wasn't going to happen. Closing unmerged: the branch itself carried unadjudicated ride-along commits and merge history that could not land as-is.

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 /finish fresh-staging rule and the read-only-review principle (one sentence; the stack-specific detail belongs in your project's .claude/rules/). Every defect condition was fixed in its item's commit, mutation-verified where the review demanded it — including a new dedicated arm proving the killed-flag's liveness gate.

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 finish-flow-guard hook, its suite, and its settings.json registration (split out — welcome as its own PR once the review's four fixes are in); the git switch unblock and local: settings (rejected in the review's ride-along section).

Your machine: everything accepted is now upstream, so cleanup is short — git -C ~/.claude config --unset reflect.keeper, run /update, then /keeper: it will confirm your local commits are all superseded and restore your clone to a clean copy of main (rescue branch left behind).

Thanks — the strongest items here (the scope-vs-correctness rule, the delegation hazards, the gotchas) made the shared config genuinely better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants