Skip to content

Commit 3ba0bc3

Browse files
committed
feat(swarm): add bd-cluster advisory preflight
1 parent b6df903 commit 3ba0bc3

File tree

7 files changed

+83
-7
lines changed

7 files changed

+83
-7
lines changed

.agents/rpi/next-work.jsonl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
{"source_epic": "ag-470", "timestamp": "2026-04-02T10:21:43-04:00", "items": [{"title": "Add real-world corpus from .agents/learnings/ to retrieval bench", "type": "improvement", "severity": "moderate", "source": "post-mortem", "description": "Current corpus is synthetic (16 curated files). Add a mode that benchmarks against the actual .agents/learnings/ directory to measure real retrieval quality, not just synthetic correctness.", "evidence": "ag-470 retrieval-bench scores 1.0/1.0 on synthetic corpus \u2014 too perfect to detect real-world ranking issues", "target_repo": "nami", "claim_status": "consumed", "failed_at": "2026-04-04T13:09:29Z", "consumed": true, "consumed_by": "codex:na-vsu", "consumed_at": "2026-04-12T14:11:10Z"}, {"title": "Add query-term overlap scoring to collectLearnings", "type": "improvement", "severity": "low", "source": "post-mortem", "description": "Text matching is binary substring filter. Adding weighted token overlap (like Layer 2 lexicalSignalWeight) to Layer 1 would improve ranking for partial-match queries.", "evidence": "Research finding: binary filter means close-miss queries return 0 results", "target_repo": "nami", "claim_status": "consumed", "failed_at": "2026-04-04T13:08:59Z", "consumed": true, "consumed_by": "codex:na-vsu", "consumed_at": "2026-04-12T14:11:10Z"}], "consumed": true, "claim_status": "consumed", "consumed_by": "codex:na-vsu", "consumed_at": "2026-04-12T14:11:10Z", "failed_at": "2026-04-04T13:08:59Z", "claimed_by": null, "claimed_at": null}
3939
{"source_epic":"ag-kna","timestamp":"2026-04-02T15:47:22-04:00","items":[{"title":"Content-hash dedup for inject (replace title-based approximation)","type":"tech-debt","severity":"low","source":"council-finding","description":"Title-based dedup in collectLearnings misses learnings with different titles but identical content. Content-hash dedup (like harvest uses) would be more precise.","evidence":"Tech-debt judge finding: two learnings with different titles but same body will both be injected","target_repo":"agentops","consumed":true,"claim_status":"consumed","consumed_by":"na-we7","consumed_at":"2026-04-13T01:45:52Z"},{"title":"Add retrieval-bench --live to CI nightly workflow","type":"improvement","severity":"medium","source":"retro-learning","description":"Retrieval bench exists and regression tests exist in Go, but no CI job runs live benchmarks against the actual corpus. Add to nightly workflow with P@3 >= 0.3 floor as WARN.","evidence":"Retrieval quality regressed silently for 5 months before manual e2e validation caught it","target_repo":"agentops","consumed":true,"claim_status":"consumed","consumed_by":"codex:na-841","consumed_at":"2026-04-12T14:11:10Z"},{"title":"Add source_bead to /retro quick-capture template","type":"improvement","severity":"medium","source":"retro-learning","description":"Retro quick capture already resolves active bead with bd current and writes source_bead when available in skills/retro/SKILL.md and skills-codex/retro/SKILL.md.","evidence":"Verified source and Codex retro skills include bd current plus source_bead frontmatter examples.","target_repo":"agentops","consumed":true,"claim_status":"consumed","consumed_by":"na-nex","consumed_at":"2026-04-13T02:15:30Z"}],"consumed":true,"claim_status":"consumed","consumed_by":"na-nex","consumed_at":"2026-04-13T02:15:30Z","claimed_by":null,"claimed_at":null}
4040
{"source_epic": "bead-audit-session", "timestamp": "2026-04-02T16:35:42-04:00", "items": [{"title": "Close ag-yeg: advance remaining directive gaps (runtime tests, citation gating)", "type": "tech-debt", "severity": "medium", "source": "", "description": "Directives #1,#2,#4 have progress notes but remaining gaps: 2+ runtime smoke tests, runtime install execution tests, citation stage gating."}, {"title": "Add mkdir -p guard before tag-index.txt append in ci-local-release.sh", "type": "tech-debt", "severity": "low", "source": "", "description": "write_tag_index() uses >> without explicit directory creation. Add mkdir -p for robustness. Consumed by na-lff after verifying scripts/ci-local-release.sh already creates the tag index parent directory before append.", "consumed": true, "claim_status": "consumed", "consumed_by": "codex:na-lff", "consumed_at": "2026-04-12T14:06:00Z"}], "consumed": false, "claim_status": "available", "consumed_by": null, "consumed_at": null, "claimed_by": null, "claimed_at": null}
41-
{"source_epic": "full-session-retro", "timestamp": "2026-04-02T17:39:26-04:00", "items": [{"title": "Wire bd-audit.sh into crank pre-flight as a blocking gate", "type": "feature", "severity": "medium", "source": "", "description": "bd-audit.sh exists but crank SKILL.md integration is advisory. Make it a WARNING gate that surfaces audit results before wave execution."}, {"title": "Wire bd-cluster.sh into swarm Step 1.5 for automatic consolidation suggestions", "type": "feature", "severity": "medium", "source": "", "description": "bd-cluster.sh exists but swarm does not call it yet. Add to Step 1.5 before file-ownership map computation."}, {"title": "Add bd audit and bd cluster as native Go commands in gastown", "type": "feature", "severity": "low", "source": "", "description": "Shell scripts work but native Go commands would be faster and support richer output. Port bd-audit.sh and bd-cluster.sh logic to gastown/internal/cmd/."}], "consumed": false, "claim_status": "available", "consumed_by": null, "consumed_at": null, "claimed_by": null, "claimed_at": null}
41+
{"source_epic": "full-session-retro", "timestamp": "2026-04-02T17:39:26-04:00", "items": [{"title": "Wire bd-audit.sh into crank pre-flight as a blocking gate", "type": "feature", "severity": "medium", "source": "", "description": "bd-audit.sh exists but crank SKILL.md integration is advisory. Make it a WARNING gate that surfaces audit results before wave execution."}, {"title": "Wire bd-cluster.sh into swarm Step 1.5 for automatic consolidation suggestions", "type": "feature", "severity": "medium", "source": "", "description": "bd-cluster.sh exists but swarm now runs scripts/bd-cluster.sh --json as a non-blocking advisory Step 1.6 before file-ownership map computation.", "consumed": true, "claim_status": "consumed", "consumed_by": "na-gdl", "consumed_at": "2026-04-13T02:23:30Z", "evidence": "skills/swarm/SKILL.md and skills-codex/swarm/SKILL.md document advisory bd clustering before Step 2 while preserving file-manifest/dependency gates."}, {"title": "Add bd audit and bd cluster as native Go commands in gastown", "type": "feature", "severity": "low", "source": "", "description": "Shell scripts work but native Go commands would be faster and support richer output. Port bd-audit.sh and bd-cluster.sh logic to gastown/internal/cmd/."}], "consumed": false, "claim_status": "available", "consumed_by": null, "consumed_at": null, "claimed_by": null, "claimed_at": null}
4242
{"source_epic": "ag-lzn", "timestamp": "2026-04-03T16:35:11-04:00", "items": [{"title": "Teach closure-integrity audit to consume evidence-only closure packets", "type": "tech-debt", "severity": "high", "source": "council-finding", "description": "Update post-mortem closure-integrity tooling so maintenance or verification-only epics resolve on proof packets under .agents/releases/evidence-only-closures/ instead of failing with parser_miss when no owned files exist.", "evidence": "`bash skills/post-mortem/scripts/closure-integrity-audit.sh --scope auto ag-lzn` returned 4/4 parser_miss failures even though evidence-only closure packets were generated for every child and the epic.", "target_repo": "agentops"}, {"title": "Backfill next-work queue rows to schema v1.3 and add drift validation", "type": "pattern-fix", "severity": "medium", "source": "council-finding", "description": "Normalize legacy .agents/rpi/next-work.jsonl rows to the batch entry and enum contract in docs/contracts/next-work.schema.md, then add a validation or repair path so producers and consumers stop relying on backward-compatible drift.", "evidence": "The live queue still contains legacy rows with stale values like severity=moderate, source=post-mortem, type=docs, and ad-hoc flat item shapes that no longer match schema 1.3.", "target_repo": "agentops"}], "consumed": true, "claim_status": "available", "consumed_by": "swarm-wave-1-2", "consumed_at": "2026-04-12T21:00:00-04:00", "claimed_by": null, "claimed_at": null}
4343
{"source_epic": "rpi-runtime-fixes-2026-04-03", "timestamp": "2026-04-03T17:33:55-04:00", "items": [{"title": "Replace stale next-work heuristic suppression with proof-backed completion evidence", "type": "tech-debt", "severity": "high", "source": "council-finding", "description": "The landed ag-8ai path auto-consumes queue items when consolidation-style goals reference files that are absent in the repo but present in git history. That improves the audited repro, but it is not the explicit completion-proof model described in the bead closeout and can suppress unresolved work without a run-registry or evidence-packet proof.", "evidence": "cli/cmd/ao/rpi_loop.go:426-525; ag-bn9 tracks the stronger proof path.", "target_repo": "nami", "claim_status": "in_progress", "claimed_by": "ao-rpi-loop:cycle-28", "claimed_at": "2026-04-04T18:52:43Z", "failed_at": "2026-04-04T18:52:43Z"}], "consumed": true, "claim_status": "in_progress", "claimed_by": "ao-rpi-loop:cycle-28", "claimed_at": "2026-04-04T18:52:43Z", "consumed_by": "swarm-wave-1-2", "consumed_at": "2026-04-12T21:00:00-04:00", "failed_at": "2026-04-04T18:52:43Z"}
4444
{"source_epic": "ag-9qm", "timestamp": "2026-04-04T15:56:16-04:00", "items": [{"title": "EV-3.2: Weighted matching (adds to substring)", "type": "feature", "severity": "medium", "description": "Weighted section scoring already shipped in ad029207 and is still covered by weightedSectionScore tests plus section-rollup-weighted provenance.", "source": "post-mortem ag-9qm", "consumed": true, "claim_status": "consumed", "consumed_by": "na-nex", "consumed_at": "2026-04-13T02:15:30Z"}, {"title": "Refactor collectLearnings to reduce cyclomatic complexity", "type": "tech-debt", "severity": "low", "description": "collectLearnings flow was split in 7e4b6481; current gocyclo reports collectLearnings complexity 4.", "source": "vibe complexity analysis", "consumed": true, "claim_status": "consumed", "consumed_by": "na-nex", "consumed_at": "2026-04-13T02:15:30Z"}], "consumed": true, "claim_status": "consumed", "claimed_by": null, "claimed_at": null, "consumed_by": "na-nex", "consumed_at": "2026-04-13T02:15:30Z"}

scripts/bd-cluster.sh

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ declare -A BEAD_BODY
9191
declare -A BEAD_LABELS
9292
declare -A BEAD_IS_EPIC
9393

94+
show_jq() {
95+
local filter="$1"
96+
local file="$2"
97+
jq -r "(if type == \"array\" then .[0] // {} else . end) | ${filter}" "$file" 2>/dev/null || true
98+
}
99+
94100
for id in "${BEAD_IDS[@]}"; do
95101
SHOW_FILE="$TMPDIR_CACHE/show_${id}.json"
96102
if ! bd show "$id" --json >"$SHOW_FILE" 2>/dev/null; then
@@ -104,11 +110,11 @@ for id in "${BEAD_IDS[@]}"; do
104110
continue
105111
fi
106112

107-
BEAD_TITLE["$id"]="$(jq -r '.title // .subject // ""' "$SHOW_FILE")"
108-
BEAD_BODY["$id"]="$(jq -r '.body // .description // ""' "$SHOW_FILE")"
109-
BEAD_LABELS["$id"]="$(jq -r '(.labels // []) | join(" ")' "$SHOW_FILE")"
113+
BEAD_TITLE["$id"]="$(show_jq '.title // .subject // ""' "$SHOW_FILE")"
114+
BEAD_BODY["$id"]="$(show_jq '.body // .description // ""' "$SHOW_FILE")"
115+
BEAD_LABELS["$id"]="$(show_jq '(.labels // []) | join(" ")' "$SHOW_FILE")"
110116
# Treat as epic if type/kind field says so, or if it has children
111-
IS_EPIC="$(jq -r 'if (.type=="epic" or .kind=="epic" or ((.children // []) | length > 0)) then "1" else "0" end' "$SHOW_FILE")"
117+
IS_EPIC="$(show_jq 'if (.type=="epic" or .kind=="epic" or .issue_type=="epic" or ((.children // []) | length > 0)) then "1" else "0" end' "$SHOW_FILE")"
112118
BEAD_IS_EPIC["$id"]="$IS_EPIC"
113119
done
114120

skills-codex/.agentops-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,7 @@
998998
"name": "swarm",
999999
"source_skill": "skills/swarm",
10001000
"source_hash": "fe5abbc8873c805485273cc85d2d480c615f8f016fa14445f8c170bcfabd0fdf",
1001-
"generated_hash": "ff431d1ed777720a6bbbfe64b2bbcaae921b4aefe1ed74a0b4221e303eaf9871"
1001+
"generated_hash": "8fb45ad3964ffd5b70355bfdb9ad455247ba6f0a64e0ac9d44b1a6ceadbca6f6"
10021002
},
10031003
{
10041004
"name": "test",

skills-codex/swarm/.agentops-generated.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
"source_skill": "skills/swarm",
44
"layout": "modular",
55
"source_hash": "fe5abbc8873c805485273cc85d2d480c615f8f016fa14445f8c170bcfabd0fdf",
6-
"generated_hash": "ff431d1ed777720a6bbbfe64b2bbcaae921b4aefe1ed74a0b4221e303eaf9871"
6+
"generated_hash": "8fb45ad3964ffd5b70355bfdb9ad455247ba6f0a64e0ac9d44b1a6ceadbca6f6"
77
}

skills-codex/swarm/SKILL.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ Task description: <description>")
8080

8181
Inject the discovered file list back into the task manifest before spawning workers.
8282

83+
### Step 1.6: Advisory Bead Clustering
84+
85+
When tasks come from bd and `scripts/bd-cluster.sh` exists, run `scripts/bd-cluster.sh --json 2>/dev/null || true` before Step 2. Summarize any clusters as consolidation hints only; never run `--apply` here, and keep Step 2's file-manifest and dependency gates authoritative.
86+
8387
### Step 2: Pre-Spawn Conflict Check
8488

8589
**Pre-Spawn Friction Gates:** Before spawning workers, execute all 5 friction gates (base sync, file manifest, dependency graph, misalignment breaker, wave cap). See `references/pre-spawn-friction-gates.md`.

skills/swarm/SKILL.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ If any task is missing its file manifest, auto-generate it before Step 2:
162162

163163
Once all tasks have manifests, proceed to Step 2 where the Pre-Spawn Conflict Check enforces file ownership.
164164

165+
### Step 1.6: Advisory Bead Clustering
166+
167+
When tasks come from bd and `scripts/bd-cluster.sh` exists, run `scripts/bd-cluster.sh --json 2>/dev/null || true` before Step 2. Summarize any clusters as consolidation hints only; never run `--apply` here, and keep Step 2's file-manifest and dependency gates authoritative.
168+
165169
### Step 2: Identify Wave
166170

167171
**Pre-Spawn Friction Gates:** Before spawning workers, execute all 6 friction gates (base sync, file manifest, dependency graph, misalignment breaker, wave cap, base-SHA ancestry). See `references/pre-spawn-friction-gates.md`.

tests/scripts/bd-cluster.bats

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env bats
2+
3+
setup() {
4+
REPO_ROOT="$(cd "$BATS_TEST_DIRNAME/../.." && pwd)"
5+
SCRIPT="$REPO_ROOT/scripts/bd-cluster.sh"
6+
7+
TMP_DIR="$(mktemp -d)"
8+
MOCK_BIN="$TMP_DIR/bin"
9+
mkdir -p "$MOCK_BIN"
10+
11+
cat >"$MOCK_BIN/bd" <<'BD'
12+
#!/usr/bin/env bash
13+
set -euo pipefail
14+
15+
if [[ "$1" == "list" ]]; then
16+
cat <<'JSON'
17+
[
18+
{"id":"na-aaa","title":"Swarm cluster docs","labels":["skill:swarm"]},
19+
{"id":"na-bbb","title":"Swarm cluster runtime","labels":["skill:swarm"]}
20+
]
21+
JSON
22+
exit 0
23+
fi
24+
25+
if [[ "$1" == "show" ]]; then
26+
case "$2" in
27+
na-aaa)
28+
cat <<'JSON'
29+
[
30+
{"id":"na-aaa","title":"Swarm cluster docs","description":"Update skills/swarm/SKILL.md","issue_type":"chore","labels":["skill:swarm"]}
31+
]
32+
JSON
33+
;;
34+
na-bbb)
35+
cat <<'JSON'
36+
[
37+
{"id":"na-bbb","title":"Swarm cluster runtime","description":"Update skills/swarm/SKILL.md","issue_type":"chore","labels":["skill:swarm"]}
38+
]
39+
JSON
40+
;;
41+
*)
42+
exit 1
43+
;;
44+
esac
45+
exit 0
46+
fi
47+
48+
exit 1
49+
BD
50+
chmod +x "$MOCK_BIN/bd"
51+
}
52+
53+
teardown() {
54+
rm -rf "$TMP_DIR"
55+
}
56+
57+
@test "bd-cluster --json accepts bd show array output" {
58+
run env PATH="$MOCK_BIN:$PATH" bash "$SCRIPT" --json
59+
60+
[ "$status" -eq 0 ]
61+
echo "$output" | jq -e '.clusters | type == "array"'
62+
}

0 commit comments

Comments
 (0)