From 82e60427dacac9a4a848938716fa4d854e7700d0 Mon Sep 17 00:00:00 2001 From: Jim Wordelman Date: Fri, 24 Jul 2026 07:19:38 -0700 Subject: [PATCH 1/2] =?UTF-8?q?feat(cairn):=20green=20=E2=80=94=20libraria?= =?UTF-8?q?n=20PROMOTE=20+=20CULL=20candidate=20beads=20(refs=20crn-28ge.1?= =?UTF-8?q?.8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add promote-candidate-beads and cull-candidate-beads steps to both mol-cairn-librarian.formula.toml and mol-cairn-librarian-rig.formula.toml, following the existing three steps' idempotent-bead-filing shape. PROMOTE consumes `cairn promote-candidates` (crn-28ge.1.6) and files a local fix-proposal bead per finding, with anchor_repo surfaced via title and a target-repo label rather than attempting an unsupported cross-repo bd write (no mechanism exists for that; mirrors cmd/critic_filebead.go's precedent). CULL consumes `cairn cull-candidates` (crn-28ge.1.7), filters to rig/global tier by replicating ResolvedTier's precedence in shell, and triggers `cairn cull-evict`'s review-branch proposal path -- deduplicated before the call, since EvictToReviewBranch hard-errors on a repeat call for the same entry rather than no-op'ing. loop step's needs chain and summary prose updated for five steps. --- formulas/mol-cairn-librarian-rig.formula.toml | 184 +++++++++++++++++- formulas/mol-cairn-librarian.formula.toml | 184 +++++++++++++++++- 2 files changed, 362 insertions(+), 6 deletions(-) diff --git a/formulas/mol-cairn-librarian-rig.formula.toml b/formulas/mol-cairn-librarian-rig.formula.toml index 015a997..9d933ab 100644 --- a/formulas/mol-cairn-librarian-rig.formula.toml +++ b/formulas/mol-cairn-librarian-rig.formula.toml @@ -250,14 +250,192 @@ BODY echo "librarian dedup sweep: $FILED bead(s) filed, $SKIPPED already tracked" ''' +[[steps]] +id = "promote-candidate-beads" +title = "File a bd bead for every cairn recurrence-promotion candidate, deduplicated across cycles" +needs = ["dedup-candidate-beads"] +description = ''' +Run the read-only recurrence-promotion detector (crn-28ge.1.6) and turn every +finding into exactly one open bd bead proposing a durable fix (FR-07). +"Not a duplicate per cycle" comes from checking bd for an already-open bead +whose title contains this finding's bracket-delimited entry anchor before +creating a new one -- the same idiom the other steps above already use. This +step only ever calls "cairn promote-candidates" (read-only) and "bd +list"/"bd create" (bd's own store) -- it never merges, deletes, or rewrites a +cairn entry, per crn-xw3 guardrail 8. + +Each finding's anchor_repo names the repo/tracker the recurring topic +actually belongs to, which is very often NOT the repo this sweep happens to +be running in (crn-78d precedent: get the tracker right, don't default to +wherever the librarian runs). Cairn/bd have no general mechanism to file a +bead directly into an arbitrary named repo's own tracker ("bd create --repo" +only overrides auto-routing among repos already registered in *this* store's +own .beads/config.yaml via "bd repo add", which anchor_repo is not expected +to be) -- so this step files the proposal locally, with anchor_repo made +unmissable in the title and a machine-readable target-repo label, and +explicit body text that it still needs routing to that repo's own tracker. +This mirrors both cmd/critic_filebead.go's FileCriticBead (files locally, +leaves routing beyond that to the fleet's normal ready-to-build/needs-pm +pipeline) and this fleet's own cairn-bead-fix-lives-in-different-rig +convention: label clearly, let a human/PM route cross-repo, never guess an +unsupported cross-store write. + + STORE="${CAIRN_STORE:?CAIRN_STORE must be set}" + FILED=0 + SKIPPED=0 + + while IFS= read -r finding; do + ENTRY_ID=$(printf '%s' "$finding" | jq -r '.entry_id') + TOPIC_KEY=$(printf '%s' "$finding" | jq -r '.topic_key') + COUNT=$(printf '%s' "$finding" | jq -r '.recurrence_count') + ANCHOR_REPO=$(printf '%s' "$finding" | jq -r '.anchor_repo') + ANCHOR="[entry:${ENTRY_ID}]" + + EXISTING=$(bd list --label=dim:promote,source:cairn-librarian \ + --title-contains="$ANCHOR" --json --no-pager 2>/dev/null | jq -r '.[0].id // empty') + + if [ -n "$EXISTING" ]; then + echo "skip $ENTRY_ID: already tracked as $EXISTING" + SKIPPED=$((SKIPPED + 1)) + continue + fi + + NEW_ID=$(bd create \ + --title="cairn librarian: recurring topic \"$TOPIC_KEY\" needs a fix in $ANCHOR_REPO $ANCHOR" \ + --stdin --type=task --priority=3 \ + --labels="dim:promote,source:cairn-librarian,target-repo:$ANCHOR_REPO" --silent < $ANCHOR_REPO)" + FILED=$((FILED + 1)) + done < <(cairn promote-candidates --store "$STORE" | jq -c '.[]') + + echo "librarian promote sweep: $FILED bead(s) filed, $SKIPPED already tracked" +''' + +[[steps]] +id = "cull-candidate-beads" +title = "Propose review-branch eviction for every rig/global-tier cairn cull-candidate, deduplicated across cycles" +needs = ["promote-candidate-beads"] +description = ''' +Run the read-only disuse detector (crn-28ge.1.7) and, for every rig- or +global-tier finding, trigger a review-branch eviction proposal via "cairn +cull-evict" -- never a direct delete. Role-tier is out of scope for the same +reason it's out of scope for the rest of this formula (see the top-level +description); agent-tier is private and handled by each agent's own +opportunistic self-sweep, not this shared-tier formula. Tier is resolved +from each finding's scope array with the same rig > role > agent precedence +(global as the fallback) as internal/cairn/remember.go's ResolvedTier, +replicated here in shell since this step has no access to the Go binary's +internals. + +"cairn cull-evict" is where the never-a-direct-delete guarantee actually +lives (cmd/cull.go: any non-private scope always takes the review-branch +path, via EvictToReviewBranch plus a mail to the tier's reviewer) -- this +step's own job is only to decide which findings are in scope (rig/global +tier) and avoid re-triggering an eviction proposal already made. That +dedup check must run BEFORE calling "cairn cull-evict", not after: +EvictToReviewBranch's review branch is named deterministically ("cull/" + +entry ID) and a second call for the same entry is a hard error, not a +no-op, so re-invoking it once a proposal already exists would fail loudly +every cycle instead of skipping quietly. This step only ever calls "cairn +cull-candidates" (read-only), "cairn cull-evict" (opens a review branch and +mails a reviewer, never a direct delete for rig/global scope), and "bd +list"/"bd create" (bd's own store) -- it never merges, deletes, or rewrites +a cairn entry directly, per crn-xw3 guardrail 8: nothing is evicted until a +human reviewer merges the proposal branch. + + STORE="${CAIRN_STORE:?CAIRN_STORE must be set}" + FILED=0 + SKIPPED=0 + + # Mirrors internal/cairn/remember.go's ResolvedTier: rig beats role beats + # agent; no scope tag at all resolves to "global". + resolve_tier() { + scope_json="$1" + for t in rig role agent; do + match=$(printf '%s' "$scope_json" | jq -r --arg t "$t" \ + 'map(select(startswith($t + ":"))) | .[0] // empty') + if [ -n "$match" ]; then + echo "$t" + return + fi + done + echo "global" + } + + while IFS= read -r finding; do + ENTRY_ID=$(printf '%s' "$finding" | jq -r '.entry_id') + TOPIC_KEY=$(printf '%s' "$finding" | jq -r '.topic_key') + SCOPE_JSON=$(printf '%s' "$finding" | jq -c '.scope // []') + DISUSED_SINCE=$(printf '%s' "$finding" | jq -r '.disused_since') + ANCHOR="[entry:${ENTRY_ID}]" + + TIER=$(resolve_tier "$SCOPE_JSON") + if [ "$TIER" != "rig" ] && [ "$TIER" != "global" ]; then + echo "skip $ENTRY_ID: tier=$TIER out of scope for this sweep (rig/global only)" + continue + fi + + EXISTING=$(bd list --label=dim:cull,source:cairn-librarian \ + --title-contains="$ANCHOR" --json --no-pager 2>/dev/null | jq -r '.[0].id // empty') + + if [ -n "$EXISTING" ]; then + echo "skip $ENTRY_ID: already tracked as $EXISTING" + SKIPPED=$((SKIPPED + 1)) + continue + fi + + CULL_OUTPUT=$(cairn cull-evict "$ENTRY_ID" --store "$STORE" 2>&1) || { + echo "cairn cull-evict $ENTRY_ID failed: $CULL_OUTPUT" >&2 + continue + } + BRANCH=$(printf '%s' "$CULL_OUTPUT" | sed -n 's/^cull review branch: //p') + + NEW_ID=$(bd create \ + --title="cairn librarian: disuse cull proposed for \"$TOPIC_KEY\" ($TIER) $ANCHOR" \ + --stdin --type=task --priority=3 \ + --labels=dim:cull,source:cairn-librarian --silent </dev/null | jq -r '.[0].id // empty') + + if [ -n "$EXISTING" ]; then + echo "skip $ENTRY_ID: already tracked as $EXISTING" + SKIPPED=$((SKIPPED + 1)) + continue + fi + + NEW_ID=$(bd create \ + --title="cairn librarian: recurring topic \"$TOPIC_KEY\" needs a fix in $ANCHOR_REPO $ANCHOR" \ + --stdin --type=task --priority=3 \ + --labels="dim:promote,source:cairn-librarian,target-repo:$ANCHOR_REPO" --silent < $ANCHOR_REPO)" + FILED=$((FILED + 1)) + done < <(cairn promote-candidates --store "$STORE" | jq -c '.[]') + + echo "librarian promote sweep: $FILED bead(s) filed, $SKIPPED already tracked" +''' + +[[steps]] +id = "cull-candidate-beads" +title = "Propose review-branch eviction for every rig/global-tier cairn cull-candidate, deduplicated across cycles" +needs = ["promote-candidate-beads"] +description = ''' +Run the read-only disuse detector (crn-28ge.1.7) and, for every rig- or +global-tier finding, trigger a review-branch eviction proposal via "cairn +cull-evict" -- never a direct delete. Role-tier is out of scope for the same +reason it's out of scope for the rest of this formula (see the top-level +description); agent-tier is private and handled by each agent's own +opportunistic self-sweep, not this shared-tier formula. Tier is resolved +from each finding's scope array with the same rig > role > agent precedence +(global as the fallback) as internal/cairn/remember.go's ResolvedTier, +replicated here in shell since this step has no access to the Go binary's +internals. + +"cairn cull-evict" is where the never-a-direct-delete guarantee actually +lives (cmd/cull.go: any non-private scope always takes the review-branch +path, via EvictToReviewBranch plus a mail to the tier's reviewer) -- this +step's own job is only to decide which findings are in scope (rig/global +tier) and avoid re-triggering an eviction proposal already made. That +dedup check must run BEFORE calling "cairn cull-evict", not after: +EvictToReviewBranch's review branch is named deterministically ("cull/" + +entry ID) and a second call for the same entry is a hard error, not a +no-op, so re-invoking it once a proposal already exists would fail loudly +every cycle instead of skipping quietly. This step only ever calls "cairn +cull-candidates" (read-only), "cairn cull-evict" (opens a review branch and +mails a reviewer, never a direct delete for rig/global scope), and "bd +list"/"bd create" (bd's own store) -- it never merges, deletes, or rewrites +a cairn entry directly, per crn-xw3 guardrail 8: nothing is evicted until a +human reviewer merges the proposal branch. + + STORE="${CAIRN_STORE:?CAIRN_STORE must be set}" + FILED=0 + SKIPPED=0 + + # Mirrors internal/cairn/remember.go's ResolvedTier: rig beats role beats + # agent; no scope tag at all resolves to "global". + resolve_tier() { + scope_json="$1" + for t in rig role agent; do + match=$(printf '%s' "$scope_json" | jq -r --arg t "$t" \ + 'map(select(startswith($t + ":"))) | .[0] // empty') + if [ -n "$match" ]; then + echo "$t" + return + fi + done + echo "global" + } + + while IFS= read -r finding; do + ENTRY_ID=$(printf '%s' "$finding" | jq -r '.entry_id') + TOPIC_KEY=$(printf '%s' "$finding" | jq -r '.topic_key') + SCOPE_JSON=$(printf '%s' "$finding" | jq -c '.scope // []') + DISUSED_SINCE=$(printf '%s' "$finding" | jq -r '.disused_since') + ANCHOR="[entry:${ENTRY_ID}]" + + TIER=$(resolve_tier "$SCOPE_JSON") + if [ "$TIER" != "rig" ] && [ "$TIER" != "global" ]; then + echo "skip $ENTRY_ID: tier=$TIER out of scope for this sweep (rig/global only)" + continue + fi + + EXISTING=$(bd list --label=dim:cull,source:cairn-librarian \ + --title-contains="$ANCHOR" --json --no-pager 2>/dev/null | jq -r '.[0].id // empty') + + if [ -n "$EXISTING" ]; then + echo "skip $ENTRY_ID: already tracked as $EXISTING" + SKIPPED=$((SKIPPED + 1)) + continue + fi + + CULL_OUTPUT=$(cairn cull-evict "$ENTRY_ID" --store "$STORE" 2>&1) || { + echo "cairn cull-evict $ENTRY_ID failed: $CULL_OUTPUT" >&2 + continue + } + BRANCH=$(printf '%s' "$CULL_OUTPUT" | sed -n 's/^cull review branch: //p') + + NEW_ID=$(bd create \ + --title="cairn librarian: disuse cull proposed for \"$TOPIC_KEY\" ($TIER) $ANCHOR" \ + --stdin --type=task --priority=3 \ + --labels=dim:cull,source:cairn-librarian --silent < Date: Fri, 24 Jul 2026 08:00:16 -0700 Subject: [PATCH 2/2] chore: release gate PASS for librarian-promote-cull-steps --- .../librarian-promote-cull-steps-gate.md | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 release-gates/librarian-promote-cull-steps-gate.md diff --git a/release-gates/librarian-promote-cull-steps-gate.md b/release-gates/librarian-promote-cull-steps-gate.md new file mode 100644 index 0000000..73c01f4 --- /dev/null +++ b/release-gates/librarian-promote-cull-steps-gate.md @@ -0,0 +1,31 @@ +# Release Gate: mol-cairn-librarian PROMOTE + CULL candidate-bead steps + +Isolated single-commit deploy for crn-28ge.1.8 (crn-gfd9). Source branch +`gc-builder-769138d1bf3c` is the persistent bare-commit-fallback builder +branch and carries stale duplicate history relative to `origin/main` (most +of its stack is already squash-merged, e.g. PR #48 -> `99d1f96`). Per the +reviewer's explicit merge policy, only the one new commit was cherry-picked +onto a fresh branch off `origin/main` rather than merging the branch +directly. + +Deploy source: `58ffab62b82ff870e745c3a1bcd04bf77b5d28dc` (feat(cairn): +green — librarian PROMOTE + CULL candidate beads, refs crn-28ge.1.8), +cherry-picked onto `deploy/crn-28ge.1.8` = `origin/main` @ +`99d1f960266573c4557d83a22a1ae8cdc9ec9943` + 1 commit. + +| # | Criterion | Result | Evidence | +|---|-----------|--------|----------| +| 1 | Review PASS present for the deployed commit | PASS | crn-ynmf: cairn/reviewer PASS, all 9 checklist items independently re-verified. Reviewer's PASS cites `58ffab6` explicitly; deploy SHA `D` == review SHA `R`. | +| 2 | Acceptance criteria met | PASS | crn-28ge.1.8 AC: two new `[[steps]]` following the existing idempotent-bead-filing shape; PROMOTE files a proposal bead naming each finding's `anchor_repo`; CULL filters to rig/global tier and routes through the review-branch eviction path (never a direct delete); loop step's `needs`/prose updated for five steps. Independently confirmed by reading the full diff — additive only, matches shape of the three existing steps, byte-identical between `mol-cairn-librarian.formula.toml` and `mol-cairn-librarian-rig.formula.toml`. | +| 3 | Tests pass | PASS | `go build ./...` clean. `go vet ./...` clean. `golangci-lint run ./...` — 0 issues (cache cleaned first per known shared-cache staleness gotcha). `go test ./... -race` — all packages green (`cmd`, `formulas`, `internal/cairn`, `internal/critic`, `scripts`), including `internal/critic` where the reviewer previously saw a known-flaky, pre-existing, unrelated failure (`TestRunPerfScenarioCleansUpAfterItself`) — passed clean on this run. | +| 4 | No high-severity findings open | PASS | Reviewer recorded 0 open HIGH findings. Item 9 of the review (`Entry.PromotedBeadID` has no writer) is informational only, explicitly out of scope for this bead, tracked separately as crn-ghn8 — not blocking. | +| 5 | Final branch is clean | PASS | `git status` clean on `deploy/crn-28ge.1.8`, ahead of `origin/main` by exactly 1 commit. | +| 6 | Branch diverges cleanly from main | PASS | `deploy/crn-28ge.1.8` = `origin/main` (`99d1f96`, unchanged since the reviewer's own verification) + 1 cherry-picked commit; applied with zero conflicts. | +| 7 | Single feature theme | PASS | TOML-only diff, 2 files (`formulas/mol-cairn-librarian.formula.toml`, `formulas/mol-cairn-librarian-rig.formula.toml`), +362/-6. Both new steps belong to the same mol-cairn-librarian sweep (PROMOTE and CULL candidate-bead filing) — not independent features; removing either step would leave the formula's other steps working. | + +## Disposition + +PR to be opened from `deploy/crn-28ge.1.8` onto `main`, GitHub-native +auto-merge armed per cairn's scoped deployer merge authority (squash, per +fleet memory `cairn-auto-merge-requires-explicit-strategy` — no merge-queue +ruleset configured on `quad341/cairn`).