baldart 4.53.5 → 4.53.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ All notable changes to BALDART will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [4.53.6] - 2026-06-18
9
+
10
+ **`/new` now caps concurrency at 3 agents per wave — the API rate-limit guardrail, end to end.** Follow-up to the v4.53.5 root-cause finding (parallel coders killed by org-level rate-limiting): the org rate limit is SHARED across every terminal/session on the account, so a wide fan-out saturates the shared pool and the server throttles, killing agents mid-work and forcing full re-spawns (paying twice). Fix puts a hard ceiling of **3 concurrent agents** at every fan-out point in a `/new` epic: (1) `prd-card-writer` caps each `execution_strategy.groups[]` at ≤3 cards — a wider topological layer is split into sequential sub-groups of ≤3 (always safe: same-layer cards are independent by construction, so serializing them is correctness-neutral; the per-card `parallel_group` keeps its true logical layer, only the execution schedule is capped), so the **plan/cards reflect the rule**; (2) team-mode runtime never spawns more than 3 coders per wave and sub-batches a wider group sequentially (protects legacy epics + manual runs — the **guarantee**); (3) the per-wave and final review workflows cap their finder/verify fan-out at 3 via a rolling worker pool (not chunked barriers — those would block fast finders behind the slow background Codex poll). The cap is **3** (the user's choice — prefer never clogging over re-paying), a fixed operational guardrail like the existing retry caps (NOT a `baldart.config.yml` key). Honest limits: BALDART cannot see agents in OTHER terminals — the cap bounds one run; total cross-terminal parallelism is still the user's to manage. `new2` inherits the prd-card-writer group cap (same cards) and the final-review cap (shared workflow); its own per-card scheduling is not separately capped here (follow-up if it sees use). **PATCH** (operational guardrail on existing orchestration; no new agent/skill/command/config key, no install change).
11
+
12
+ ### Fixed
13
+
14
+ - **`framework/.claude/agents/prd-card-writer.md`** — Parallel Group Computation step 7: cap each execution group at ≤3 cards (split wider independent layers into sequential sub-groups); example + `max_concurrent_agents: 3` added; per-card `parallel_group` unchanged (logical layer).
15
+ - **`framework/.claude/skills/new/references/team-mode.md`** — Step B gains a MANDATORY "Concurrency cap — MAX 3 coders concurrently" (sub-batch a wider group sequentially; cross-terminal caveat); Per-Group Execution header notes the cap.
16
+ - **`framework/.claude/workflows/new-card-review.js`** + **`new-final-review.js`** — new `MAX_PARALLEL=3` + `parallelCapped()` rolling worker pool; the Discovery/Review finder fan-out and the Verify fan-out run through it (≤3 concurrent agents, order-preserving, null-on-throw, no head-of-line blocking behind the Codex poll).
17
+
8
18
  ## [4.53.5] - 2026-06-18
9
19
 
10
20
  **The team-mode empty-result gate now distinguishes a rate-limit death from a genuine fabrication — and re-spawns transient failures STAGGERED, not in the same parallel burst.** Root-cause finding (from reading the actual subagent transcripts of the v4.53.4 incident — `~/.claude/projects/<mayo>/<session>/subagents/agent-*.jsonl`): the two `/new FEAT-0035` L2 coders that "came to rest" with no work were **killed mid-flight by API rate-limiting** — both transcripts end on the identical `API Error: Server is temporarily limiting requests (not your usage limit) · Rate limited`, after the agent had done real work (file reads, a written plan) but before any edit. It was NOT model fabrication or a "decided nothing to do" stall (the v4.53.4 framing): a wide parallel wave (3 coders + orchestrator + other agents) saturated the API, the background teammate runtime does not auto-resume a rate-limited teammate, so it rested with no report + no diff, and the full re-spawn re-did the lost reads (the "paid twice" cost). Fix: the gate now reads the rested agent's LAST event — a transient API/rate-limit/overload error ⇒ a **transient infra failure** (re-spawn STAGGERED with backoff; never re-fire several transient-failed agents in the same parallel burst — it re-saturates the API; narrow the wave's fan-out width if rate limits recur; does not consume the genuine-failure Step-B budget), vs a CLEAN rest with no error ⇒ the genuine empty-result/fabrication path (one Step-B re-spawn → `AskUserQuestion`). Honest boundary: the cheaper real fix — *resuming* a rate-limited teammate instead of killing+re-spawning it — is Claude Code **runtime** behaviour, not BALDART's to fix in prose; BALDART mitigates (correct classification + staggered backoff + fan-out narrowing). **PATCH** (refines the v4.53.4 team-mode gate; no code/CLI/workflow change, no `baldart.config.yml` key).
package/VERSION CHANGED
@@ -1 +1 @@
1
- 4.53.5
1
+ 4.53.6
@@ -595,6 +595,7 @@ After all cards and matrices, compute parallelization metadata:
595
595
  - Total cards <= 3: `sequential`
596
596
  - Max cards in any single layer <= 1: `sequential`
597
597
  - Otherwise: `team`
598
+ 7. **Concurrency cap — MAX 3 cards per execution group (API rate-limit guardrail, since v4.53.6).** A parallel wave wider than 3 risks API throttling: the rate limit is enforced at the **organization level** and is SHARED across every Claude Code session/terminal on the account, so a wide coder fan-out saturates the shared pool (observed in a real run — `API Error: ... Rate limited (not your usage limit)` killed coders mid-work, forcing a full re-spawn = paying twice). When a topological layer has **more than 3 cards**, split it into multiple `execution_strategy.groups[]` entries of **≤3 cards each**. This is always safe: cards in the same layer are mutually independent by construction (no `depends_on` among them — step 4 — and no `(MODIFY)` file conflict — step 3), so the sub-groups run sequentially with **zero correctness impact**, only less parallelism. Keep each card's per-card `parallel_group` at its TRUE logical layer (unchanged); only the execution `groups[]` are capped — they are an execution schedule, not the logical DAG. (The `/new` team-mode runtime independently enforces the same ≤3 cap by sub-batching, so this is the plan-side reflection of a guarantee enforced at execution.)
598
599
 
599
600
  **Add to epic parent card** an `execution_strategy` block:
600
601
 
@@ -610,8 +611,17 @@ execution_strategy:
610
611
  - level: 1
611
612
  cards: [FEAT-XXXX-02, FEAT-XXXX-03]
612
613
  description: "Independent modules (no file overlap)"
614
+ # Concurrency cap (≤3/group): a logical layer of 5 independent cards is emitted as TWO
615
+ # sequential groups of ≤3 — same logical layer, split only for the execution schedule.
616
+ - level: 2
617
+ cards: [FEAT-XXXX-04, FEAT-XXXX-05, FEAT-XXXX-06]
618
+ description: "Layer 2 (independent) — batch 1 of 2 (concurrency cap ≤3)"
619
+ - level: 3
620
+ cards: [FEAT-XXXX-07, FEAT-XXXX-08]
621
+ description: "Layer 2 (independent) — batch 2 of 2 (concurrency cap ≤3)"
622
+ max_concurrent_agents: 3 # API rate-limit guardrail — no group exceeds this; runtime sub-batches too
613
623
  file_conflicts:
614
- - "FEAT-XXXX-04 and FEAT-XXXX-05 both modify <shared-types-file> — forced sequential"
624
+ - "FEAT-XXXX-09 and FEAT-XXXX-10 both modify <shared-types-file> — forced sequential"
615
625
  ```
616
626
 
617
627
  The consumer (`new/SKILL.md`) reads `execution_strategy.groups[].level` and
@@ -33,7 +33,7 @@ The standard pre-flight (setup.md Phase 0 + steps 1-6) runs unchanged; add:
33
33
 
34
34
  ### Per-Group Execution
35
35
 
36
- Process groups in order (0, 1, 2, ...). Within each group, spawn coder agents IN PARALLEL — one per card.
36
+ Process groups in order (0, 1, 2, ...). Within each group, spawn coder agents IN PARALLEL — one per card, **capped at 3 concurrent** (sub-batch a wider group — see Step B § "Concurrency cap").
37
37
 
38
38
  #### Step A: Pre-compute shared context (ONCE per group)
39
39
 
@@ -59,7 +59,9 @@ its `arch_baseline_path` at this file, so the review reuses the group baseline i
59
59
 
60
60
  (No visibility emission on wave change — the internal tracker is the only state surface; see SKILL.md § "State surface — the tracker only". Record the wave start in the tracker only.)
61
61
 
62
- For each card in the current group, spawn a coder agent using the Agent tool. ALL agents for the group MUST be spawned in a **SINGLE message** (multiple Agent tool calls) to run truly in parallel.
62
+ For each card in the current group, spawn a coder agent using the Agent tool. The group's cards MUST be spawned in a **SINGLE message** (multiple Agent tool calls) to run truly in parallel.
63
+
64
+ > **Concurrency cap — MAX 3 coders concurrently (MANDATORY — API rate-limit guardrail, since v4.53.6).** Never fire more than **3** coder agents in one parallel burst. The API rate limit is **organization-level and SHARED across every terminal/session on the account** — a wide coder fan-out saturates the shared pool and the server starts throttling (`API Error: ... Rate limited (not your usage limit)`), which **kills coders mid-work** and forces a full re-spawn (paying for the same work twice). `prd-card-writer` already caps `execution_strategy.groups[]` at ≤3, but if a group's `.cards` exceeds 3 (a legacy epic authored before this rule, or a manual run), **sub-batch it into chunks of ≤3 run SEQUENTIALLY** — spawn the first ≤3, wait for the wave to complete its Step-D pipeline (or at least the coders to finish), then the next ≤3. Do NOT fire them all at once. ⚠️ **BALDART cannot see agents spawned in OTHER terminals** — this cap bounds THIS run only; if you routinely run multiple `/new` epics across terminals, keep the total modest, because they all draw from the same shared pool. (Same guardrail the v4.53.5 transient-failure handling references when it says "narrow the fan-out".)
63
65
 
64
66
  Each coder agent receives a **SELF-CONTAINED** mission briefing that includes EVERYTHING it needs — it will NOT call codebase-architect or plan-auditor itself:
65
67
 
@@ -39,6 +39,26 @@ const cfg = a.config || {}
39
39
  const highRisk = (cfg.paths && cfg.paths.high_risk_modules) || [] // security-domain hint
40
40
  const protocolRef = '.claude/skills/new/references/review-cycle.md'
41
41
 
42
+ // Concurrency cap (v4.53.6 — API rate-limit guardrail). The org-level rate limit is SHARED across
43
+ // every terminal/session on the account; a wide agent fan-out saturates it and the server throttles
44
+ // (kills agents mid-work). Cap concurrent agents at 3 — a rolling worker pool (NOT chunked barriers,
45
+ // which would block fast finders behind the slow background Codex poll). Mirrors the team-mode coder
46
+ // cap so review + implementation obey the same ceiling.
47
+ const MAX_PARALLEL = 3
48
+ async function parallelCapped(thunks, cap) {
49
+ if (!Array.isArray(thunks) || !thunks.length) return []
50
+ const results = new Array(thunks.length)
51
+ let next = 0
52
+ const worker = async () => {
53
+ while (next < thunks.length) {
54
+ const i = next++
55
+ try { results[i] = await thunks[i]() } catch (_) { results[i] = null }
56
+ }
57
+ }
58
+ await Promise.all(Array.from({ length: Math.min(cap, thunks.length) }, worker))
59
+ return results
60
+ }
61
+
42
62
  // Curated toolchain (since v4.41.0): when features.has_toolchain is on, the
43
63
  // consumer records LITERAL gate commands in toolchain.commands.* — agents run
44
64
  // THOSE instead of guessing (e.g. `npx biome check .` not `npm run lint`). Empty
@@ -270,7 +290,7 @@ if (maxQaTier === 'full') {
270
290
  log('Discovery: qa-sentinel SKIPPED (wave max tier ≤ light — full suite deferred to the Final FULL gate).')
271
291
  }
272
292
 
273
- const findResults = (await parallel(findThunks)).filter(Boolean)
293
+ const findResults = (await parallelCapped(findThunks, MAX_PARALLEL)).filter(Boolean)
274
294
 
275
295
  // ---- Fan-in: collect findings + Codex fallback branch -----------------------
276
296
  let raw = []
@@ -314,7 +334,7 @@ raw.forEach((f, i) => { f.finding_id = `${f.source || 'src'}#${i}:${f.finding_id
314
334
  // Phase Verify — specialist-owned validation (parity with new-final-review.js F.4)
315
335
  // ───────────────────────────────────────────────────────────────────────────
316
336
  phase('Verify')
317
- const classified = (await parallel(raw.map((f) => () => verifyFinding(f)))).filter(Boolean)
337
+ const classified = (await parallelCapped(raw.map((f) => () => verifyFinding(f)), MAX_PARALLEL)).filter(Boolean)
318
338
 
319
339
  function domainVerifier(domain) {
320
340
  const d = String(domain || 'code').toLowerCase()
@@ -36,6 +36,25 @@ const cfg = a.config || {}
36
36
  const highRisk = (cfg.paths && cfg.paths.high_risk_modules) || [] // security-domain hint
37
37
  const protocolRef = '.claude/skills/new/references/final-review.md'
38
38
 
39
+ // Concurrency cap (v4.53.6 — API rate-limit guardrail; mirrors new-card-review.js + the team-mode
40
+ // coder cap). The org-level rate limit is SHARED across every terminal/session; a wide agent fan-out
41
+ // saturates it and the server throttles. Cap concurrent agents at 3 via a rolling worker pool (NOT
42
+ // chunked barriers — those would block fast finders behind the slow background Codex poll).
43
+ const MAX_PARALLEL = 3
44
+ async function parallelCapped(thunks, cap) {
45
+ if (!Array.isArray(thunks) || !thunks.length) return []
46
+ const results = new Array(thunks.length)
47
+ let next = 0
48
+ const worker = async () => {
49
+ while (next < thunks.length) {
50
+ const i = next++
51
+ try { results[i] = await thunks[i]() } catch (_) { results[i] = null }
52
+ }
53
+ }
54
+ await Promise.all(Array.from({ length: Math.min(cap, thunks.length) }, worker))
55
+ return results
56
+ }
57
+
39
58
  if (!scope.length) {
40
59
  log('new-final-review: empty review scope — nothing to review.')
41
60
  return { codexEngine: 'none', findings: [], gateTable: [], summary: emptySummary() }
@@ -232,7 +251,7 @@ if (!slimApi && a.hasApiDataFiles !== false) {
232
251
  : 'Review: api-perf-cost-auditor skipped (no API/data files in scope).')
233
252
  }
234
253
 
235
- const reviewResults = (await parallel(reviewThunks)).filter(Boolean)
254
+ const reviewResults = (await parallelCapped(reviewThunks, MAX_PARALLEL)).filter(Boolean)
236
255
 
237
256
  // ---- Fan-in (F.4 step 9): collect findings + Codex fallback branch ----------
238
257
  let raw = []
@@ -291,7 +310,7 @@ raw.forEach((f, i) => { f.finding_id = `${f.source || 'src'}#${i}:${f.finding_id
291
310
  // self-judge, never a silent drop).
292
311
  // ───────────────────────────────────────────────────────────────────────────
293
312
  phase('Verify')
294
- const classified = (await parallel(raw.map((f) => () => verifyFinding(f)))).filter(Boolean)
313
+ const classified = (await parallelCapped(raw.map((f) => () => verifyFinding(f)), MAX_PARALLEL)).filter(Boolean)
295
314
 
296
315
  // Route a finding to the specialist that OWNS its domain (never a generic code-reviewer for
297
316
  // doc/api/security). Kept in sync with new2-resolve.js normDomain() routing buckets.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "4.53.5",
3
+ "version": "4.53.6",
4
4
  "description": "Claude Agent Framework - Reusable framework for coordinating AI agents and humans in software projects",
5
5
  "bin": {
6
6
  "baldart": "./bin/baldart.js"