From f06536ceba9505dad2ce84036640a24887825e13 Mon Sep 17 00:00:00 2001 From: cairn-reviewer-scratch Date: Wed, 19 Aug 2026 18:19:30 -0700 Subject: [PATCH 1/3] RED: stale-review-branch-recovery must carry needs-investigation (crn-27con.4) TestLibrarianStaleReviewBranchRecoveryStepHasNeedsInvestigationLabelAndGuard fails against the current mol-cairn-librarian.formula.toml: the escalation step's bd create --labels= has dim:review-branch,source:cairn-librarian, needs-pm but no needs-investigation, and the crn-wqgm Guard block only checks for needs-pm. needs-pm (crn-wqgm) already makes the filed bead deacon-patrol-routable, but neither dim:review-branch nor source:cairn-librarian is used as a routing inclusion filter anywhere in packs/ (the only reference is an exclusion, in labeller's own health check). needs-investigation additionally puts the filed bead directly in investigator's own pull-based ready query (packs/actual/investigator/pack.toml), giving it a second independent path into a real work queue. --- formulas/formulas_test.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/formulas/formulas_test.go b/formulas/formulas_test.go index e60580b6..c2779845 100644 --- a/formulas/formulas_test.go +++ b/formulas/formulas_test.go @@ -197,6 +197,37 @@ func TestLibrarianStepsHaveNeedsPmLabelAndGuard(t *testing.T) { } } +// crn-27con.4: dim:review-branch,source:cairn-librarian are provenance-only +// labels -- nothing in packs/ uses either as a routing inclusion filter (the +// only reference is an exclusion, in the labeller's own "unlabeled ready +// work" health check, which deliberately skips beads carrying +// source:cairn-librarian). needs-pm alone (crn-wqgm) already makes the filed +// bead deacon-patrol-routable, but needs-investigation additionally puts it +// directly in investigator's own pull-based ready query +// (packs/actual/investigator/pack.toml: --label-any +// needs-pr-response,needs-investigation), giving the filed bead two +// independent paths into a real work queue instead of relying on deacon +// dispatch alone. Scoped to stale-review-branch-recovery only, unlike +// TestLibrarianStepsHaveNeedsPmLabelAndGuard's five steps -- the other four +// are explicitly out of scope for this bead. +func TestLibrarianStaleReviewBranchRecoveryStepHasNeedsInvestigationLabelAndGuard(t *testing.T) { + f := decodeFormula(t, "mol-cairn-librarian.formula.toml") + s, ok := stepByID(f, "stale-review-branch-recovery") + if !ok { + t.Fatal(`mol-cairn-librarian.formula.toml: no "stale-review-branch-recovery" step found`) + } + + if !strings.Contains(s.Description, ",needs-investigation") { + t.Error(`stale-review-branch-recovery: --labels must include needs-investigation, or the filed bead never enters investigator's own "--label-any needs-pr-response,needs-investigation" ready query (crn-27con.4)`) + } + if !strings.Contains(s.Description, `index("needs-investigation")`) { + t.Error(`stale-review-branch-recovery: missing a Guard assertion that needs-investigation is present, matching the label it now requires (crn-27con.4)`) + } + if !strings.Contains(s.Description, ",needs-pm") { + t.Error(`stale-review-branch-recovery: must still include needs-pm -- crn-27con.4 adds needs-investigation alongside it, not in place of it`) + } +} + // cairn cull-evict itself is not safely repeatable for the same entry -- // EvictToReviewBranch hard-errors once a proposal is already pending, since // its review branch name is deterministic ("cull/" + entry ID; see From 1d215d78ed8745d4a874a30e4da9ccd95068c56c Mon Sep 17 00:00:00 2001 From: cairn-reviewer-scratch Date: Wed, 19 Aug 2026 18:22:26 -0700 Subject: [PATCH 2/3] GREEN: stale-review-branch-recovery attaches needs-investigation (crn-27con.4) Both mol-cairn-librarian.formula.toml and mol-cairn-librarian-rig.formula.toml's stale-review-branch-recovery step now file the escalation bead with --labels=dim:review-branch,source:cairn-librarian,needs-pm,needs-investigation (needs-pm and the provenance pair kept, not replaced) and the crn-wqgm Guard block now also asserts needs-investigation is present. Root cause: dim:review-branch/source:cairn-librarian have zero routing inclusion-filter consumers anywhere in packs/ -- the only reference is an exclusion in labeller's own health check. needs-pm (added earlier by #64/ crn-wqgm, predating this bug) already makes the bead deacon-patrol-routable; needs-investigation additionally puts it directly in investigator's own pull-based ready query (packs/actual/investigator/pack.toml: --label-any needs-pr-response,needs-investigation), so the bead now has two independent paths into a real work queue instead of depending solely on deacon dispatch. docs/plans/cairn-librarian-stale-branch-recovery.md's "ready-to-paste" step template still shows the pre-needs-pm label set; that drift predates this bug (introduced when crn-wqgm/crn-go8o added needs-pm) and is left as-is -- updating design-doc history is out of scope here. Second ask investigated and NOT implemented here: the bug describes "the sweep's earlier notify bucket" (cmd/branches.go's sendStaleBranchReminder, which mails a reviewer directly with no queue/SLA semantics) as the source of ~24-25 unread mail in cairn/architect's mailbox. Checked `gc mail inbox cairn/architect --json` directly: 23 unread, all either "cairn cull review"/"cairn remember review" (cmd/reviewer.go's initial review-request mail, sent on every shared-tier write/cull, not just stale ones) or this session's own benign "context cycle" self-mail. Zero "cairn review branch stale" mail (sendStaleBranchReminder's actual subject) is present -- the named mechanism isn't the one generating the noise. Neither of the bug's proposed fixes (drop the reminder, or re-point it at investigator) fits the mechanism actually responsible: cmd/reviewer.go's initial notification is load-bearing (dropping it removes the only mail that a new shared-tier write ever happened), and its reviewer resolution was the subject of a recent, deliberate fix (rott9.1) that re-pointing would partially undo. Filing a separate, correctly-scoped follow-up bead for the real mechanism instead of guessing a change into the wrong file. --- formulas/mol-cairn-librarian-rig.formula.toml | 18 ++++++++++-------- formulas/mol-cairn-librarian.formula.toml | 18 ++++++++++-------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/formulas/mol-cairn-librarian-rig.formula.toml b/formulas/mol-cairn-librarian-rig.formula.toml index 85d6d1e3..2c1f3032 100644 --- a/formulas/mol-cairn-librarian-rig.formula.toml +++ b/formulas/mol-cairn-librarian-rig.formula.toml @@ -104,7 +104,7 @@ pass to pass; if it's instead re-cloned fresh every sweep tick, add NEW_ID=$(bd create \ --title="cairn librarian: stale review branch on $BRANCH ($TIER)" \ --stdin --type=task --priority=3 \ - --labels=dim:review-branch,source:cairn-librarian,needs-pm --silent <&2 + # Guard (crn-wqgm, extended crn-27con.4): assert the filed bead matches + # the deacon patrol's own selection query (routable label present, + # assignee empty, gc.routed_to empty) AND carries needs-investigation, so + # investigator's own "--label-any needs-pr-response,needs-investigation" + # ready query also picks it up -- this prose and the actual --labels + # argument drifted apart once already; this catches a repeat instead of + # filing another bead nothing will ever pick up. + GUARD_OK=$(bd show "$NEW_ID" --json | jq -r '.[0] as $b | ((($b.labels // []) | index("needs-pm")) != null) and ((($b.labels // []) | index("needs-investigation")) != null) and (($b.assignee // "") == "") and (($b.metadata["gc.routed_to"] // "") == "")') + [ "$GUARD_OK" = "true" ] || echo "librarian: GUARD FAILED -- $NEW_ID does not match the deacon patrol's selection query or investigator's ready query (needs-pm+needs-investigation labels / empty assignee / empty gc.routed_to) -- it will not be auto-routed" >&2 echo "filed $NEW_ID for $BRANCH" FILED=$((FILED + 1)) diff --git a/formulas/mol-cairn-librarian.formula.toml b/formulas/mol-cairn-librarian.formula.toml index 87d0b395..63481944 100644 --- a/formulas/mol-cairn-librarian.formula.toml +++ b/formulas/mol-cairn-librarian.formula.toml @@ -113,7 +113,7 @@ pass to pass; if it's instead re-cloned fresh every sweep tick, add NEW_ID=$(bd create \ --title="cairn librarian: stale review branch on $BRANCH ($TIER)" \ --stdin --type=task --priority=3 \ - --labels=dim:review-branch,source:cairn-librarian,needs-pm --silent <&2 + # Guard (crn-wqgm, extended crn-27con.4): assert the filed bead matches + # the deacon patrol's own selection query (routable label present, + # assignee empty, gc.routed_to empty) AND carries needs-investigation, so + # investigator's own "--label-any needs-pr-response,needs-investigation" + # ready query also picks it up -- this prose and the actual --labels + # argument drifted apart once already; this catches a repeat instead of + # filing another bead nothing will ever pick up. + GUARD_OK=$(bd show "$NEW_ID" --json | jq -r '.[0] as $b | ((($b.labels // []) | index("needs-pm")) != null) and ((($b.labels // []) | index("needs-investigation")) != null) and (($b.assignee // "") == "") and (($b.metadata["gc.routed_to"] // "") == "")') + [ "$GUARD_OK" = "true" ] || echo "librarian: GUARD FAILED -- $NEW_ID does not match the deacon patrol's selection query or investigator's ready query (needs-pm+needs-investigation labels / empty assignee / empty gc.routed_to) -- it will not be auto-routed" >&2 echo "filed $NEW_ID for $BRANCH" FILED=$((FILED + 1)) From 4971c81bc73e5f57f7b8119df75cfa2f9e35d81d Mon Sep 17 00:00:00 2001 From: cairn-reviewer-scratch Date: Thu, 20 Aug 2026 00:57:48 -0700 Subject: [PATCH 3/3] Release gate: crn-nc9te (7/7 PASS) Deploy crn-27con.4: attach needs-investigation label on librarian stale-review-branch escalation. See release-gates/crn-nc9te-gate.md for full per-criterion evidence. --- release-gates/crn-nc9te-gate.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 release-gates/crn-nc9te-gate.md diff --git a/release-gates/crn-nc9te-gate.md b/release-gates/crn-nc9te-gate.md new file mode 100644 index 00000000..62ade13a --- /dev/null +++ b/release-gates/crn-nc9te-gate.md @@ -0,0 +1,27 @@ +# Release Gate: crn-nc9te + +**Bead:** crn-nc9te (deploy) — source review + implementation crn-27con.4 (epic crn-27con) +**Commit:** `1d215d78ed8745d4a874a30e4da9ccd95068c56c`, cut onto `deploy/crn-nc9te-gate` off `origin/main` +**Date:** 2026-08-20 + +## Criteria + +| # | Criterion | Result | Evidence | +|---|-----------|--------|----------| +| 6 | Branch diverges cleanly from main | PASS | Merge-base with `origin/main` (tip `817e9b528f4bab9dbf7d0122f7b28f1f0fd6dd1e`) is `aa484f2bf631907b0112fa886c38ae6927526b9b`. Main gained 2 commits since (#123, #125 — `cmd/`, `internal/{cairn,critic}`, `release-gates/`); this branch touches only `formulas/`. `comm -12` on the two changed-file sets: zero overlap. `assert_deploy_ancestry_scope origin/main crn-27con.4`: rc=0. | +| 1 | Review PASS present | PASS | crn-27con.4 closed, REVIEW VERDICT: PASS from cairn/reviewer, independently verified by reviewer in an isolated scratch worktree at this exact commit (build/vet/gofmt clean, full suite + race clean, lint 0 issues on isolated cache). | +| 2 | Acceptance criteria met | PASS | Bug required `needs-investigation` added at the escalation point in both `mol-cairn-librarian*.formula.toml` files, without removing existing labels. Diff confirms: `needs-pm` preserved, `needs-investigation` added to both `--labels` args, guard block in both files extended to assert its presence. Secondary discretionary "second ask" (notify-bucket mail) investigated, found premised on stale evidence, correctly not implemented — follow-up crn-27con.7 filed instead of guessing; bug text itself left this ask optional ("your call"), so it does not block. | +| 3 | Tests pass | PASS | Independently re-verified in a fresh isolated scratch worktree at `1d215d7` (not just trusting the reviewer's claim): `go build ./...`, `go vet ./...`, `gofmt -l .` all clean; `golangci-lint run ./...` (isolated `GOLANGCI_LINT_CACHE`) 0 issues; `go test ./... -race -count=1` — all 7 packages `ok`, 0 FAIL. New regression test `TestLibrarianStaleReviewBranchRecoveryStepHasNeedsInvestigationLabelAndGuard` re-run directly with `-v`: PASS. | +| 4 | No high-severity review findings open | PASS | No `finding`-labeled beads reference crn-27con.4 or crn-nc9te. Related beads: crn-27con.7 (documented non-blocking follow-up), crn-27con/crn-27con.3 (separate parent-epic backlog concern, unrelated to this fix), crn-4j0pa (separate PM task), crn-9r7kn (empty fleet-routing "convoy" artifact, not a code finding). | +| 5 | Final branch clean | PASS | `git status --porcelain` empty on `deploy/crn-nc9te-gate` immediately after cut. | +| 7 | Single feature theme | PASS | Diff vs. merge-base touches exactly 3 files: `formulas/mol-cairn-librarian.formula.toml`, `formulas/mol-cairn-librarian-rig.formula.toml`, `formulas/formulas_test.go` (51 insertions, 16 deletions) — one coherent theme (add `needs-investigation` label + guard + regression test, symmetric across both formula files). | + +## Verdict: PASS (7/7) — proceeding to PR. + +## Merge authority + +quad341/cairn — standing self-merge authorization applies (mayor ruling `gm-wisp-2yhv7u`, 2026-08-19), governed by memory `cairn-auto-merge-requires-explicit-strategy`. This bead's description does not carry the older "route to mayor/mpr" boilerplate at all (unlike some sibling deploy beads this cycle), so there is no stale-copy language to address here either way. Deployer will merge directly (`gh pr merge --squash`, plain, once CI is green) under the ruling's 4 standing conditions. + +## Note + +Source branch `builder/crn-27con.4` is provenance-only per the bead's own instructions — deploy branch cut directly from commit `1d215d7`, not pushed onto or opened from the builder branch.