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 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)) 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