baldart 5.7.0 → 5.8.0

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,49 @@ 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
+ ## [5.8.0] - 2026-07-06
9
+
10
+ **Suggested launch plan at the end of `/prd` — waves with a NAME, persisted on the epic card.**
11
+ `/prd` used to close with a progress bar and a bare summary (PRD path, card IDs, branch, PR/SHA)
12
+ but never told you **how to launch `/new`** — one run or several waves, where the boundaries are,
13
+ which engine. You had to reason it out by hand. Now every `/prd` run ends with a mandatory
14
+ **"🚀 Piano di implementazione suggerito"** section, and the plan is **persisted on the epic
15
+ card** so `/new`/`/new2` can, at the end of a subset run, remind you which waves remain to close
16
+ the epic — each wave carries a short, memorable **name** ("Wave 1 · Fondamenta server").
17
+
18
+ The design was hardened by a two-reviewer adversarial pass that refuted the obvious v1
19
+ (a separate `implementation_plan` block, an `engine` field on the card, a new reference module,
20
+ the mechanical card-writer computing the heuristic). The shipped shape:
21
+
22
+ - **Drift-free persistence — no new block, no new module.** The wave partition is three OPTIONAL
23
+ sub-keys (`wave` / `wave_name` / `wave_rationale`) on the EXISTING `execution_strategy.groups[]`
24
+ entries — the single card-ID partition already consumed by `/new` for parallelism. No parallel
25
+ `waves[]` array re-listing card IDs (that would drift). Because they are sub-keys of an
26
+ already-`R` field, the baseline validator never sees them: **no new field-state-matrix row, no
27
+ migration of legacy epics**. Schema SSOT: `framework/agents/card-schema.md`.
28
+ - **Compute-by-orchestrator / persist-by-writer** (the `provenance.planning_session` split):
29
+ the `/prd` orchestrator (opus/high) computes the single-run-vs-waves judgment + names + rationale
30
+ — it needs the discovery context (migration-early, `review_profile: deep` pressure, server↔UI
31
+ cut) the mechanical `prd-card-writer` never holds — and persists it with a targeted Edit on the
32
+ epic card. The card-writer emits the keys ONLY when handed them; else it omits them (explicit
33
+ else branch → no fabrication). Single-run is the default (no wave tags).
34
+ - **Engine + hard rules are render-time, never persisted.** The `/new` vs `/new2` recommendation
35
+ is computed at render and is **runtime-aware** — `/new2` needs the Workflow tool and is
36
+ Claude-only, so on Codex the plan recommends `/new` only. A durable, cross-tool artifact must
37
+ not carry a runtime-capability recommendation a Codex consumer cannot execute.
38
+ - **`/new` + `/new2` remaining-waves.** `/new`'s Step F.6 "Prossimi Passi" and `/new2`'s
39
+ `buildReport()` "Epic goal" render the remaining waves BY NAME (fallback to the flat card list
40
+ when the epic has no wave tags — no regression). `/new2` runs fs-less, so the `wave_name` is
41
+ threaded through `cardGraph` (new `waveName` node field derived by the preflight agent).
42
+ - Project-specific between-wave cautions (migration/deploy gate, compile check, stash) stay in
43
+ `.baldart/overlays/prd.md` and are surfaced at render-time — no stack names in the base.
44
+
45
+ No new `baldart.config.yml` key (rides on the existing `execution_strategy` + `features.*`) → the
46
+ schema-change propagation rule does NOT apply. **Codex parity: portable** (runtime-neutral field;
47
+ engine computed at render-time; `/new2` remaining-waves via `cardGraph`).
48
+
49
+ Skills bumped: `prd` 1.6.1→1.7.0, `new` 2.3.1→2.4.0, `new2` 1.8.1→1.9.0.
50
+
8
51
  ## [5.7.0] - 2026-07-06
9
52
 
10
53
  **Feature-gated skill linking — stop wasting Codex's 2% skill-description budget.** Codex
package/VERSION CHANGED
@@ -1 +1 @@
1
- 5.7.0
1
+ 5.8.0
@@ -754,6 +754,29 @@ The consumer (`new/SKILL.md`) reads `execution_strategy.groups[].level` and
754
754
  `.cards`. Use these exact keys — `groups`/`level`, not the legacy
755
755
  `parallel_groups`/`group`.
756
756
 
757
+ **Launch-wave sub-fields (`wave` / `wave_name` / `wave_rationale`) — write ONLY if handed to you.**
758
+ The `/prd` orchestrator may pass a launch-wave partition (a run-sequencing judgment it computes
759
+ AFTER you return the Parallel Execution Map — it needs discovery context you do not hold). When
760
+ and only when the orchestrator hands you these values, add them as optional sub-keys on the
761
+ matching `groups[]` entries:
762
+
763
+ ```yaml
764
+ groups:
765
+ - level: 0
766
+ cards: [FEAT-XXXX-01]
767
+ description: "Foundation/scaffold"
768
+ wave: 1 # only if passed
769
+ wave_name: "Fondamenta server" # only if passed
770
+ wave_rationale: "…" # only if passed
771
+ ```
772
+
773
+ If the orchestrator does NOT hand you a wave partition, **OMIT all three keys** — do NOT invent a
774
+ wave grouping, a name, or a rationale (a launch judgment fabricated from the card graph alone is
775
+ worse than absent; single-run epics legitimately carry no wave tags). This is the same
776
+ compute-by-orchestrator / persist-by-writer split as `provenance.planning_session`. Schema + the
777
+ render-time derivation live in [`framework/agents/card-schema.md`](../../agents/card-schema.md)
778
+ § "`execution_strategy.groups[].wave*`".
779
+
757
780
  ## Output Format
758
781
 
759
782
  **Self-validation BEFORE returning (v5.6.0 — you own your domain's deterministic hygiene).**
@@ -2,6 +2,21 @@
2
2
 
3
3
  Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
4
4
 
5
+ ## 2.4.0 — 2026-07-06
6
+
7
+ - **Prossime wave per NOME nel messaggio finale.** Il Step F.6 "Prossimi Passi"
8
+ (`references/final-review.md`) ora è **wave-aware**: legge la partizione di lancio persistita
9
+ da `/prd` sull'epic card (sotto-campi opzionali `wave` / `wave_name` su
10
+ `execution_strategy.groups[]`) e, quando presente, mostra le **wave rimanenti per nome**
11
+ ("Prossime wave: **Wave 2 · Superfici UI**") invece della sola lista card piatta — così, dopo
12
+ aver implementato un subset (es. `/new FEAT-0069-01..06`), il riepilogo dice cosa resta per
13
+ chiudere l'epic senza doverlo ricordare. Engine della prossima wave suggerito runtime-aware
14
+ (Codex → solo `/new`). Richiede un Read esplicito dell'epic card a F.6 (non è in contesto:
15
+ letto solo transitoriamente in Phase 0). **Fallback grazioso** alla lista card piatta quando
16
+ l'epic non ha tag wave (single-run / legacy) → nessuna regressione. Schema SSOT:
17
+ `framework/agents/card-schema.md` § `execution_strategy.groups[].wave*`.
18
+ - Codex parity: portable (il campo è runtime-neutral; l'engine è calcolato a render-time).
19
+
5
20
  ## 2.3.1 — 2026-07-06
6
21
 
7
22
  - **Feature-gate**: added `requires_feature: has_backlog` frontmatter — this skill is now linked into a consumer only when `features.has_backlog` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: new
3
3
  effort: medium
4
- version: 2.3.1
4
+ version: 2.4.0
5
5
  requires_feature: has_backlog
6
6
  description: >
7
7
  Orchestrate a team of specialized agents to implement one or more backlog cards
@@ -439,7 +439,42 @@ that is a **gate violation**: log it as
439
439
  | xargs grep -L "status: DONE"
440
440
  ```
441
441
 
442
- Present the section:
442
+ **Wave-aware next steps (v5.8.0).** `/prd` may have persisted a **launch-wave partition** on the
443
+ epic card — optional `wave` / `wave_name` sub-keys on `execution_strategy.groups[]` (schema:
444
+ `framework/agents/card-schema.md` § "`execution_strategy.groups[].wave*`"). The epic card is NOT
445
+ in context here (it was read only transiently in Phase 0) — so **Read the epic card now**
446
+ (`${paths.backlog_dir}/<EPIC-ID>.yml`) to fetch the wave tags. Then:
447
+ - **If wave tags exist:** map each remaining not-DONE card to its `wave_name` (via the group whose
448
+ `.cards` contains it), and render the **remaining WAVES by name** instead of a flat card list — a
449
+ wave is "rimanente" when ≥1 of its cards is not-DONE. This keeps the thread across runs: the user
450
+ recognises "Wave 2 · Superfici UI" from the `/prd` plan. Suggest the engine for the NEXT wave
451
+ runtime-aware (Claude → `/new2` for a high-volume wave, else `/new`; Codex → `/new` only —
452
+ `/new2` is Claude-only).
453
+ - **If NO wave tags** (legacy epic / single-run): render the flat "Card rimanenti nell'epic" table
454
+ exactly as before (backwards-compat, no regression).
455
+
456
+ Present the section — WAVE-AWARE form (when wave tags exist):
457
+
458
+ ```
459
+ ## Prossimi Passi
460
+
461
+ ### Prossime wave da sviluppare
462
+ | Wave | Nome | Card rimanenti | Engine suggerito |
463
+ |------|------|----------------|------------------|
464
+ | 2 | Superfici UI & closer | FEAT-XXXX-07…13 | /new2 |
465
+
466
+ ### Lancio prossima wave (copia e incolla)
467
+ ```
468
+ /new2 FEAT-XXXX-07-FEAT-XXXX-13
469
+ ```
470
+
471
+ ### Card completate in questa sessione
472
+ | Card | Titolo | Commit |
473
+ |------|--------|--------|
474
+ | FEAT-XXXX-01 | [title] | abc1234 |
475
+ ```
476
+
477
+ FLAT form (no wave tags — unchanged):
443
478
 
444
479
  ```
445
480
  ## Prossimi Passi
@@ -2,6 +2,17 @@
2
2
 
3
3
  Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
4
4
 
5
+ ## 1.9.0 — 2026-07-06
6
+
7
+ - **Prossime wave per NOME nel report finale (parità con `/new`).** `buildReport()` (`new2.js`)
8
+ ora, per ogni epic APERTA nella sezione "Epic goal", elenca le **wave rimanenti per nome**
9
+ dalla partizione di lancio persistita da `/prd` sull'epic. Poiché il workflow gira senza
10
+ accesso al filesystem, il `wave_name` è **portato via `cardGraph`** (nuovo campo `waveName`
11
+ sul nodo — l'agente preflight lo deriva dai `execution_strategy.groups[]` dell'epic). Scope
12
+ onesto: visibili solo le wave le cui card sono NEL BATCH (le wave interamente fuori dal batch
13
+ non sono caricate). `''` quando l'epic non ha tag wave (single-run / legacy) → nessuna riga.
14
+ - Codex parity: portable (il campo è runtime-neutral).
15
+
5
16
  ## 1.8.1 — 2026-07-06
6
17
 
7
18
  - **Feature-gate**: added `requires_feature: has_backlog` frontmatter — this skill is now linked into a consumer only when `features.has_backlog` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: new2
3
3
  effort: high
4
- version: 1.8.1
4
+ version: 1.9.0
5
5
  requires_feature: has_backlog
6
6
  description: >
7
7
  Workflow-hosted batch engine for /new. Implements one or
@@ -2,6 +2,34 @@
2
2
 
3
3
  Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
4
4
 
5
+ ## 1.7.0 — 2026-07-06
6
+
7
+ - **Piano di implementazione suggerito nel messaggio finale (sempre).** Il run di `/prd` ora
8
+ chiude con una sezione **"🚀 Piano di implementazione suggerito"** che dice come lanciare
9
+ `/new` — unica run vs **wave con NOME**, confini, engine (`/new` vs `/new2`) — invece di
10
+ lasciare all'utente il compito di ricavarla a mano. La logica del piano è **persistita
11
+ sull'epic card** così `/new`/`/new2` a fine run possono ricordare quali wave restano.
12
+ - **Persistenza drift-free:** nessun blocco nuovo — sotto-campi OPZIONALI `wave` / `wave_name`
13
+ / `wave_rationale` sui `groups[]` esistenti di `execution_strategy` (unica partizione di
14
+ card-ID; niente `waves[]` parallelo che drifta). Sono sotto-chiavi di un campo già `R` →
15
+ il baseline validator non le vede, **nessuna riga nuova nella field-state matrix**, epic
16
+ legacy invariate. Schema SSOT: `framework/agents/card-schema.md`.
17
+ - **Compute-by-orchestrator / persist-by-writer** (stesso split di `provenance.planning_session`):
18
+ l'**orchestratore `/prd`** calcola la partizione wave + nomi + rationale (giudizio di lancio
19
+ che serve il contesto di discovery — migration-early, pressione review `deep`, taglio
20
+ server/UI) e la persiste con un Edit mirato sull'epic card; il `prd-card-writer` scrive i
21
+ sotto-campi **solo se gli vengono passati**, altrimenti li omette (ramo else esplicito →
22
+ niente fabbricazione). Single-run resta il default: nessun tag wave.
23
+ - **Engine + hard-rule = render-time, non persistiti.** La scelta `/new` vs `/new2` è calcolata
24
+ a render ed è runtime-aware (Codex → solo `/new`, `/new2` è Claude-only) — un artefatto
25
+ cross-tool durabile non porta una raccomandazione che un consumer Codex non può eseguire.
26
+ - **Base generico + overlay:** cautele project-specific tra-wave (migration/deploy gate, compile
27
+ check, stash) restano in `.baldart/overlays/prd.md`, innestate a render-time.
28
+ - File: `references/backlog-phase.md` (compute+persist), `references/validation-phase.md`
29
+ (render), `assets/epic-template.yml` (chiavi commentate), `framework/agents/card-schema.md`
30
+ (schema), `framework/.claude/agents/prd-card-writer.md` (omit-if-not-passed).
31
+ - Codex parity: portable (campo runtime-neutral; engine calcolato a render-time).
32
+
5
33
  ## 1.6.1 — 2026-07-06
6
34
 
7
35
  - **Feature-gate**: added `requires_feature: has_prd_workflow` frontmatter — this skill is now linked into a consumer only when `features.has_prd_workflow` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: prd
3
3
  effort: high
4
- version: 1.6.1
4
+ version: 1.7.0
5
5
  requires_feature: has_prd_workflow
6
6
  description: "Structured PRD creation skill. Use when the user says /prd, 'crea un prd', 'nuova funzionalita', 'pianifica feature', or wants to plan a new feature end-to-end. Also handles /prd-add, 'aggiungi requisito', 'serve anche', 'manca un endpoint' for change requests on active PRD sessions — runs ICIAS impact analysis to determine which phases need SKIP/PATCH/REDO. Produces PRD + UI design + backlog cards, all validated and committed. Multi-turn conversation: asks questions, waits for answers, iterates through discovery, design, spec writing, card creation, and validation phases."
7
7
  ---
@@ -135,6 +135,12 @@ execution_strategy:
135
135
  - level: 0
136
136
  cards: [{{FEAT-XXXX-01}}]
137
137
  rationale: "{{foundation / first dependency layer}}"
138
+ # Optional launch-wave tags — written by the /prd ORCHESTRATOR only (not the card-writer),
139
+ # and only when the epic is launched across multiple /new runs. Omit entirely for
140
+ # single-run epics. Schema: agents/card-schema.md § execution_strategy.groups[].wave*
141
+ # wave: 1
142
+ # wave_name: "{{Fondamenta server}}"
143
+ # wave_rationale: "{{migration + config + resolver + enforcement server}}"
138
144
  - level: 1
139
145
  cards: [{{FEAT-XXXX-02}}, {{FEAT-XXXX-03}}]
140
146
  rationale: "{{parallel after L0}}"
@@ -290,6 +290,42 @@ The `prd-card-writer` agent owns the entire card writing pipeline:
290
290
  - Presenting results to the user (the agent returns summary, main context displays it)
291
291
  - Proceeding to Step 6 (validation phase)
292
292
 
293
+ ## Launch-wave partition (orchestrator judgment — YOU compute it, not the card-writer)
294
+
295
+ After the card-writer returns the Parallel Execution Map, **you** (the `/prd` orchestrator)
296
+ decide how the epic should be **launched** across `/new` runs — a judgment the mechanical
297
+ card-writer cannot make because it needs the discovery context you hold (Tier, review-profile
298
+ mix, migration, UI/server split). This is the durable half of the "Piano di implementazione
299
+ suggerito" the final message renders (validation-phase.md § Final output).
300
+
301
+ **Decide single-run vs waves (scoped — default single-run).** Keep the epic as ONE run unless
302
+ at least one split trigger fires; if none fires, do NOT tag any wave (single-run is the explicit
303
+ else branch — never fabricate a wave grouping just to have one):
304
+ - **Large batch** — many children over several dependency levels (a long single run drifts the
305
+ trunk mid-batch → merge conflicts instead of fast-forward; keep this rationale generic, do not
306
+ name a stack).
307
+ - **Migration / schema card present** — land it in an EARLY wave so its migration/deploy gate runs
308
+ once and later waves build on the real schema.
309
+ - **Review pressure** — many `review_profile: deep` children; splitting keeps the orchestrator
310
+ context lucid on exactly the cards that need lucid review.
311
+ - **Clear server↔UI cut** — server/data foundation vs UI surfaces is a natural wave boundary
312
+ (UI/E2E cards exercise the server, so they belong in the later wave).
313
+ - **Cross-domain file conflicts** — `execution_strategy.file_conflicts` that straddle a domain
314
+ boundary force sequencing.
315
+
316
+ **Assign waves over the EXISTING groups (one partition, no re-listing of card IDs).** Waves are a
317
+ contiguous grouping of `execution_strategy.groups[]` in `level` order. For each group in a wave,
318
+ set `wave` (1-based, ascending), `wave_name` (a short, unique, human-memorable name derived from
319
+ the wave's dominant domain/role — e.g. "Fondamenta server", "Superfici UI & closer"), and
320
+ `wave_rationale` (one line). Persist them by **editing the epic card** (`backlog/<slug>-00.yml`)
321
+ in the worktree — a targeted Edit adding the three sub-keys to the matching `groups[]` entries.
322
+ Do NOT re-spawn the card-writer; do NOT create a parallel `waves[]` block. Schema:
323
+ `framework/agents/card-schema.md` § "`execution_strategy.groups[].wave*`".
324
+
325
+ **Do NOT persist the engine (`/new` vs `/new2`) or the hard rules** — those are render-time,
326
+ runtime-aware (validation-phase.md § Final output). On a single-run epic, skip this step entirely
327
+ (no wave keys written).
328
+
293
329
  ## Present and Proceed
294
330
 
295
331
  Present the plan to the user (traceability matrix + parallel execution map) for
@@ -410,4 +410,34 @@ A run that prints "local `git.trunk_branch` did not fast-forward, handle the fil
410
410
  yourself and then pull" has FAILED Step 7.5 — go back and resolve it before
411
411
  printing the summary.
412
412
 
413
+ Then, ALWAYS, append the launch plan:
414
+
415
+ ### 🚀 Piano di implementazione suggerito (MANDATORY — always render)
416
+
417
+ Derive it from the epic card's `execution_strategy` (the wave partition you persisted in
418
+ backlog-phase.md § "Launch-wave partition"); nothing here is recomputed from scratch. Rules +
419
+ schema: `framework/agents/card-schema.md` § "`execution_strategy.groups[].wave*`" (Launch waves).
420
+
421
+ - **Single-run** (≤1 distinct `wave_name`, e.g. a light-lane 1-child epic): render ONE line —
422
+ `**Lancia in un'unica run:** \`/new <all card IDs>\`` — no table. Stop here.
423
+ - **Waves** (≥2 distinct `wave_name`): render a table, one row per wave in `wave` order:
424
+
425
+ | Wave | Nome | Card | Contenuto |
426
+ |------|------|------|-----------|
427
+ | 1 | Fondamenta server | FEAT-XXXX-01…06 | <wave_rationale> |
428
+ | 2 | Superfici UI & closer | FEAT-XXXX-07…13 | <wave_rationale> |
429
+
430
+ Then, below the table:
431
+ - **Engine (runtime-aware — computed here, never read from the card).** On **Claude** (the
432
+ `Workflow` tool is available) recommend `/new2` for a high-volume wave (off-context batch, no
433
+ interactive gates) and `/new` (classic) for a wave that wants human gates (migration / risky
434
+ review) — a hybrid (`/new` on the risky early wave, `/new2` on the rest) is a valid call. On
435
+ **Codex** recommend `/new` only — `/new2` needs the `Workflow` tool and does not exist there
436
+ (`framework/agents/runtime-portability-protocol.md` § "Workflow acceleration"). Give the exact
437
+ command per wave, e.g. `/new2 FEAT-XXXX-01-FEAT-XXXX-06`.
438
+ - **Hard rule:** niente altro lavoro in parallelo sugli stessi file tra una wave e l'altra.
439
+ - **Post-wave gates:** if `.baldart/overlays/prd.md` declares project-specific between-wave
440
+ cautions (e.g. a migration/deploy gate, a compile check, a stash check), surface them after the
441
+ relevant wave. Omit this line when the overlay declares none — never invent stack-specific steps.
442
+
413
443
  Display completed Progress Bar.
@@ -219,6 +219,11 @@ const PREFLIGHT_SCHEMA = {
219
219
  // Drives the epic-closure blocklist (an epic with open scope residuals/follow-ups must
220
220
  // not close) and the Epic goal section of the final report.
221
221
  parentEpic: { type: 'string', description: "parent epic id from group.parent or parent: ('' when none)" },
222
+ // v5.8.0 — the launch-wave this card belongs to, IFF /prd persisted a wave partition on
223
+ // the epic card (optional wave_name on execution_strategy.groups[] whose .cards includes
224
+ // this id — schema: agents/card-schema.md § execution_strategy.groups[].wave*). '' when the
225
+ // epic has no wave tags (single-run / legacy). Drives the "Prossime wave" report line.
226
+ waveName: { type: 'string', description: "launch wave_name from the epic's execution_strategy.groups[] ('' when none)" },
222
227
  },
223
228
  },
224
229
  },
@@ -305,7 +310,7 @@ try {
305
310
  g3Bullet +
306
311
  `• G4 card-field validation (setup.md 1b/1c, card-schema.md consumer contract): card missing acceptance_criteria/files_likely_touched/scope → EXCLUDE (excluded[] + reason). \`requirements\` is CONDITIONAL, not an auto-exclude: if it is missing BUT acceptance_criteria + scope are both present and non-empty, FAITHFULLY DERIVE it (a concrete restatement/decomposition of the existing AC + scope — faithful, never inventing new scope) and WRITE it back to the card YAML on disk in ${MAIN}/${paths.backlog_dir || 'backlog'} (F-040: main repo, not the worktree copy; the card artifact is NOT a source/doc file, so this is allowed despite the role boundary), logging \`[BACKFILL] <id>: requirements=<N items, AC+scope-derived>\`. Only EXCLUDE for missing requirements when acceptance_criteria OR scope is ALSO empty (a genuinely thin card). Never HALT for one bad card.\n` +
307
312
  `• G5 depends-on: a card whose depends_on names a non-DONE card NOT in this batch → EXCLUDE it AND every in-batch card that transitively depends on it.\n` +
308
- `• cardGraph (REQUIRED, F-021): for every runnable card return { id, dependsOn:[IN-BATCH deps only], ownerAgent (the card's owner_agent; G25 unknown→'coder'), hasMockup (BOOLEAN — true IFF the card BUILDS the mocked UI: links.design_src non-empty, OR links.design non-empty AND (component_bindings non-empty OR files_likely_touched intersects component/style/UI source paths). A bare links.design on a docs/test/meta-only card is a POINTER to the epic's explainer, not a build target — v5.6.0, FEAT-0068: the bare test clamped a docs+ADR card to ui-expert; drives the mockup-first build override — implement.md §6a), parentEpic (the card's parent epic id from group.parent or a top-level parent: field; '' when none — drives the epic-closure blocklist + the Epic goal report section), designSrcDir (ABS path of links.design_src / the PRD mockups/_src dir; '' when absent), designHtml (ABS path when links.design is an .html file; '' otherwise), hasBindings (BOOLEAN — non-empty component_bindings), reviewProfile (the card's review_profile; default 'balanced'), policyDeferredACs, alreadyCommitted, alreadyCommittedSha, isEpic (implement.md §6b epic guard: id ends '-00' OR filename ends '-epic.yml' OR group.is_epic:true OR review_profile 'skip' with no requirements), filesLikelyTouched (verbatim from the YAML) }.\n` +
313
+ `• cardGraph (REQUIRED, F-021): for every runnable card return { id, dependsOn:[IN-BATCH deps only], ownerAgent (the card's owner_agent; G25 unknown→'coder'), hasMockup (BOOLEAN — true IFF the card BUILDS the mocked UI: links.design_src non-empty, OR links.design non-empty AND (component_bindings non-empty OR files_likely_touched intersects component/style/UI source paths). A bare links.design on a docs/test/meta-only card is a POINTER to the epic's explainer, not a build target — v5.6.0, FEAT-0068: the bare test clamped a docs+ADR card to ui-expert; drives the mockup-first build override — implement.md §6a), parentEpic (the card's parent epic id from group.parent or a top-level parent: field; '' when none — drives the epic-closure blocklist + the Epic goal report section), waveName (v5.8.0 — when the parent epic card carries a launch-wave partition, the wave_name of the execution_strategy.groups[] entry whose .cards includes this id; '' when the epic has no wave tags — single-run/legacy; drives the "Prossime wave" report line), designSrcDir (ABS path of links.design_src / the PRD mockups/_src dir; '' when absent), designHtml (ABS path when links.design is an .html file; '' otherwise), hasBindings (BOOLEAN — non-empty component_bindings), reviewProfile (the card's review_profile; default 'balanced'), policyDeferredACs, alreadyCommitted, alreadyCommittedSha, isEpic (implement.md §6b epic guard: id ends '-00' OR filename ends '-epic.yml' OR group.is_epic:true OR review_profile 'skip' with no requirements), filesLikelyTouched (verbatim from the YAML) }.\n` +
309
314
  `• B1/F-026 idempotency (per card, AFTER the worktree exists): set alreadyCommitted:true (+ alreadyCommittedSha) IFF ALL hold: (a) a commit referencing the card id exists in ${TRUNK}..HEAD of the worktree; (b) the card's validation_commands re-run GREEN right now; (c) NO open follow-up card for it exists in ${paths.backlog_dir || 'backlog'}. On a FRESH worktree ${TRUNK}..HEAD is empty → all false, zero extra work.\n` +
310
315
  `• F-016 AC↔ownership consistency: for each acceptance_criterion, derive the file(s) it requires editing. If those files are NOT a subset of the card's MAY-EDIT/files_likely_touched → add the AC to policyDeferredACs:[{n,text,owningCard|owningFile,reason}] (it will become ONE follow-up, never a resolve). Do the same for any AC whose remedy is an owner-gated infra action (remote db push / deploy / secret / DNS).\n` +
311
316
  (migrationApplied
@@ -1687,7 +1692,19 @@ function buildReport(o) {
1687
1692
  const epicHigh = residuals.filter((r) => ((graphById[r.card] && graphById[r.card].parentEpic) === e || r.card === e) && (sevRank[residualSeverity(r)] || 0) >= 2)
1688
1693
  if (closedEpics.includes(e) && !closureBlockedEpics.has(e)) L.push(`- **${e}: CHIUSA** (children DONE + AC epic verificati dal merge agent)`)
1689
1694
  else if (closedEpics.includes(e)) L.push(`- **${e}: ⚠️ CHIUSA CONTRO BLOCKLIST (false DONE)** — riaprire: ${epicHigh.length} residuo/i HIGH aperti`)
1690
- else L.push(`- **${e}: APERTA** — ${reason || (closureBlockedEpics.has(e) ? 'blocklist: residui/follow-up aperti' : 'merge non eseguito o children aperti')}${epicHigh.length ? ` · ${epicHigh.length} residuo/i HIGH` : ''}`)
1695
+ else {
1696
+ L.push(`- **${e}: APERTA** — ${reason || (closureBlockedEpics.has(e) ? 'blocklist: residui/follow-up aperti' : 'merge non eseguito o children aperti')}${epicHigh.length ? ` · ${epicHigh.length} residuo/i HIGH` : ''}`)
1697
+ // v5.8.0 — remaining launch waves by NAME (parity with /new final-review F.6). Uses the
1698
+ // waveName threaded through cardGraph (buildReport has no fs access to re-read the epic).
1699
+ // Honest scope: only waves whose cards are IN THIS BATCH are visible — waves entirely
1700
+ // outside the batch aren't loaded, so this reflects the batch, not the whole epic.
1701
+ const openWaves = dedupe(
1702
+ cardGraph
1703
+ .filter((n) => (n.parentEpic || '') === e && n.waveName && (perCardResults.find((r) => r.card === n.id) || {}).status !== 'DONE')
1704
+ .map((n) => n.waveName)
1705
+ )
1706
+ if (openWaves.length) L.push(` - Prossime wave (batch): ${openWaves.map((w) => `**${w}**`).join(' · ')}`)
1707
+ }
1691
1708
  }
1692
1709
  }
1693
1710
  if (finalSummary) {
@@ -230,6 +230,56 @@ Rules:
230
230
  - Omit any sub-field whose session id is unavailable (env var unset) rather than writing a
231
231
  placeholder.
232
232
 
233
+ ### `execution_strategy.groups[].wave*` — launch-wave annotation (epic-only, optional, orchestrator-written)
234
+
235
+ The epic's `execution_strategy.groups[]` is the **single card-ID partition** consumed by `/new`
236
+ for parallelism (`groups[].level` + `.cards` — the machine schedule). A **launch wave** is a
237
+ coarser, human-facing partition layered ON TOP of those same groups so the user can sequence the
238
+ epic across multiple `/new` runs and refer to each run by a stable NAME. It is expressed as three
239
+ **optional sub-fields on the existing `groups[]` entries** — never a parallel block that re-lists
240
+ card IDs (that would be a second SSOT that drifts from `groups[].cards`):
241
+
242
+ ```yaml
243
+ execution_strategy:
244
+ recommended_mode: team | sequential # existing — scheduling shape, runtime-neutral
245
+ groups:
246
+ - level: 0
247
+ cards: [FEAT-0069-01, FEAT-0069-02] # existing — the ONLY card-ID partition
248
+ description: "Foundation/scaffold" # existing
249
+ wave: 1 # opt — 1-based wave index (contiguous over groups)
250
+ wave_name: "Fondamenta server" # opt — short, unique human NAME for the wave
251
+ wave_rationale: "migration + config + resolver + enforcement server" # opt — why this wave
252
+ ```
253
+
254
+ Rules:
255
+ - **Optional (`C`)**, epic-only. They are **sub-keys of `execution_strategy`** (already `R`), NOT
256
+ top-level fields — the field-state matrix carries **no new row** and the baseline validator (which
257
+ checks only top-level presence/non-emptiness) never sees them. Legacy epics lack them → no HALT,
258
+ no migration.
259
+ - **Ownership split (same as [`provenance`](#provenance-session-id-traceability--optional-skill-written)):**
260
+ the `/prd` **orchestrator** COMPUTES the wave partition + names + rationale (a launch judgment that
261
+ needs discovery context — migration-early, review-`deep` pressure, server↔UI cut — which the
262
+ mechanical `prd-card-writer` does not hold), and it is PERSISTED onto the epic card. The card-writer
263
+ emits these keys ONLY when handed them; otherwise it **omits** them.
264
+ - **Contiguity invariant:** waves partition the groups in `level` order — a wave is a contiguous run
265
+ of groups. `wave` numbers ascend 1,2,3…; every group in the same wave shares one `wave_name`.
266
+ - **Single-run:** ≤1 distinct `wave_name` across all groups (e.g. a light-lane 1-child epic) means
267
+ "launch in one run" — the orchestrator leaves the wave keys ABSENT rather than tagging everything
268
+ `wave: 1`.
269
+
270
+ **Launch waves (render-time — NOT persisted):** the human-facing launch plan is derived, never
271
+ stored beyond the three keys above:
272
+ - The **wave view** = group the epic's `groups[]` by `wave_name`, ordered by `wave`; each wave's
273
+ cards = the union of its groups' `.cards`. Consumers: `/prd` final message ("Piano di
274
+ implementazione suggerito") and `/new`/`/new2` final message ("prossime wave rimanenti", joining
275
+ wave membership with each card's on-disk `status`).
276
+ - The **engine recommendation** (`/new` vs `/new2`) is computed at render time and is **runtime-aware**
277
+ — `/new2` needs the Workflow tool and is **Claude-only** (see `framework/agents/runtime-portability-protocol.md`
278
+ § "Workflow acceleration" — *no Codex equivalent*). It is **never persisted** on the card: a durable,
279
+ cross-tool artifact must not carry a runtime-capability recommendation a Codex consumer cannot execute.
280
+ - The anti-parallel hard rule ("no other work on the same files between waves") and any project-specific
281
+ post-wave gates (surfaced from `.baldart/overlays/prd.md`) are render-time constants, likewise not persisted.
282
+
233
283
  ## Acceptance-criteria grammar (EARS) + per-AC oracles (since the EARS+verify wave)
234
284
 
235
285
  **Why**: a prose AC passes gates without being verifiable ("handle it correctly"), and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "5.7.0",
3
+ "version": "5.8.0",
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"