Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions formulas/formulas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 10 additions & 8 deletions formulas/mol-cairn-librarian-rig.formula.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<BODY
--labels=dim:review-branch,source:cairn-librarian,needs-pm,needs-investigation --silent <<BODY
Review branch "$BRANCH" (entry $ENTRY_ID, tier $TIER) has been unmerged for
${AGE_SECONDS}s. A reminder mail was already sent to $REVIEWER on an earlier
cairn stale-branches pass at this same commit, and it is now past the
Expand All @@ -118,13 +118,15 @@ action removes the branch from future "cairn stale-branches" output.
BODY
)

# Guard (crn-wqgm): assert the filed bead matches the deacon patrol's own
# selection query (routable label present, assignee empty, gc.routed_to
# empty) -- 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.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 (needs-pm label / empty assignee / empty gc.routed_to) -- it will not be auto-routed" >&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))
Expand Down
18 changes: 10 additions & 8 deletions formulas/mol-cairn-librarian.formula.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<BODY
--labels=dim:review-branch,source:cairn-librarian,needs-pm,needs-investigation --silent <<BODY
Review branch "$BRANCH" (entry $ENTRY_ID, tier $TIER) has been unmerged for
${AGE_SECONDS}s. A reminder mail was already sent to $REVIEWER on an earlier
cairn stale-branches pass at this same commit, and it is now past the
Expand All @@ -127,13 +127,15 @@ action removes the branch from future "cairn stale-branches" output.
BODY
)

# Guard (crn-wqgm): assert the filed bead matches the deacon patrol's own
# selection query (routable label present, assignee empty, gc.routed_to
# empty) -- 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.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 (needs-pm label / empty assignee / empty gc.routed_to) -- it will not be auto-routed" >&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))
Expand Down
27 changes: 27 additions & 0 deletions release-gates/crn-nc9te-gate.md
Original file line number Diff line number Diff line change
@@ -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 <sha> 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.
Loading