baldart 4.22.0 → 4.22.1

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.22.1] - 2026-06-09
9
+
10
+ **Epic-parent closure at end of `/new` + `/new2`.** When the last child of an epic is implemented, the epic/parent card now gets marked `DONE` automatically instead of being left stuck at `TODO`. The Phase 6b post-merge reconciliation gate only ever touched the **batch cards** (the children); the epic/parent card (`group.is_epic: true`) is excluded from the batch by the epic guard, so it never got reconciled — final-review only *reported* "all children done" without writing the epic's own status. **PATCH** (strengthens the existing Phase 6b reconciliation gate; no new agent/skill/command/config key — `/new2` inherits it because its merge agent follows `merge-cleanup.md` as SSOT).
11
+
12
+ ### Changed
13
+
14
+ - **`framework/.claude/skills/new/references/merge-cleanup.md`** — Phase 6b gains **step 5e (Epic-parent closure)**: for each distinct `group.parent` of the batch (plus any epic card in the batch itself), if **every** child is `DONE` (checked across the whole backlog, not just the batch subset), force-update the epic card to `status: DONE` + `completed_date` + note, folded into the same reconciliation commit. Leaves the epic untouched if any child is still open. The step-6 tracker block now records `Epics closed: …`.
15
+ - **`framework/.claude/skills/new/references/final-review.md`** — the "all cards in the epic are DONE" Next-Steps message now states the epic card itself is marked `DONE` (by Phase 6b step 5e), with a note that a still-`TODO` epic under all-DONE children is a Phase 6b failure to flag.
16
+ - **`framework/.claude/workflows/new2.js`** — the merge agent's deterministic policies gain an explicit **EPIC CLOSURE** rule (all-children-DONE gate, distinct from the F-029 forced-DONE prohibition since the epic is a tracker), `MERGE_SCHEMA` gains `epicsClosed[]`, and a new `epic-closure` ledger entry records which epics were closed.
17
+
8
18
  ## [4.22.0] - 2026-06-09
9
19
 
10
20
  **`/prd` Obsidian back-reference.** When a PRD is kicked off from an Obsidian note (the documental starting point), the same note now receives, at the end of the run, an idempotent snippet indexing how the feature was developed and where it landed — PRD path, epic + card IDs, branch, merge commit/PR, and the PRD's Canonical Sources — so the user can later query the note ("come è stata sviluppata la feature X, dov'è finita"). **MINOR** (additive capability on the `prd` skill; no new `baldart.config.yml` key — the note path is runtime-detected and state-persisted, so the schema-change propagation rule does not apply).
package/VERSION CHANGED
@@ -1 +1 @@
1
- 4.22.0
1
+ 4.22.1
@@ -303,10 +303,16 @@ deterministic script outside this orchestrator's context window.
303
303
  ```
304
304
  ## Prossimi Passi
305
305
 
306
- Tutte le card dell'epic **FEAT-XXXX** sono state completate.
306
+ Tutte le card dell'epic **FEAT-XXXX** sono state completate, e la card epic stessa è
307
+ stata marcata `DONE` (chiusura automatica in Phase 6b, step 5e).
307
308
  Pronto per il deploy: `/deploy` o `git push origin <trunk branch>` (il `git.trunk_branch` configurato)
308
309
  ```
309
310
 
311
+ > Nota: la chiusura della card epic è eseguita dal gate Phase 6b (epic-parent closure),
312
+ > non da questo step di reporting — qui la si riporta soltanto. Se l'epic risultasse
313
+ > ancora `TODO`/`IN_PROGRESS` mentre tutti i figli sono `DONE`, è un fallimento di
314
+ > Phase 6b step 5e da segnalare in `## Issues & Flags`.
315
+
310
316
  5. **Proceed to Phase 7** (production readiness checklist).
311
317
 
312
318
  ---
@@ -74,12 +74,39 @@ The most common failure mode is leaving cards IN_PROGRESS after merge. This crea
74
74
  - If doc-freshness requires ssot-registry.md → update its `last_verified_from_code` date, stage it, and retry.
75
75
  - If COMMIT_LOCK → re-run step 5a and retry.
76
76
  - Do NOT retry more than 2 times. Log the failure and move on.
77
+ 5e. **Epic-parent closure (MANDATORY — the epic card is NOT in the batch)**: the batch
78
+ implements children; the epic/parent card (`group.is_epic: true`, id usually ending
79
+ `-00` / filename `*-epic.yml` / `review_profile: skip`) is never implemented and is
80
+ excluded from the per-card loop in step 2 — so it stays at its old status (almost
81
+ always `TODO`) even when its **last** child just went DONE. Close it here, deterministically:
82
+
83
+ For each **distinct parent epic** referenced by the batch — the set of `group.parent`
84
+ values across the batch cards, PLUS any card in the batch that is itself an epic
85
+ (`group.is_epic: true`) — do:
86
+ a. Read the epic parent card `${paths.backlog_dir}/<EPIC-ID>.yml`. If `status: DONE`
87
+ already → skip (idempotent).
88
+ b. Check that **every** child is DONE (not just the batch's subset — children
89
+ implemented in earlier sessions count too):
90
+ ```bash
91
+ # Lists children of <EPIC-ID> that are NOT yet DONE. Empty output ⇒ all DONE.
92
+ grep -l "parent: <EPIC-ID>" ${paths.backlog_dir}/*.yml | xargs grep -L "status: DONE"
93
+ ```
94
+ c. **Only if** that command prints nothing (all children DONE) → force-update the epic
95
+ parent: set `status: DONE`, add `completed_date: <today>`, add note
96
+ `"Marked DONE by epic-closure gate (Phase 6b) — all children DONE"`. Stage it and
97
+ **fold into the SAME reconciliation commit** as step 5 (or, if step 5 didn't run
98
+ because all children were already DONE, make a dedicated
99
+ `[BATCH] Close epic <EPIC-ID> — all children DONE` commit, honoring the same
100
+ COMMIT_LOCK / doc-freshness precautions as step 5a–5d).
101
+ d. **If any child is still open** → leave the epic untouched: it is genuinely still in
102
+ progress, and step-4b's "Card rimanenti nell'epic" table already reports the gap.
77
103
  6. **Update tracker** with reconciliation results:
78
104
  ```
79
105
  ## Phase 6b — Status Reconciliation
80
106
  Cards checked: N
81
107
  Already DONE: M
82
108
  Force-updated to DONE: K [list card IDs]
109
+ Epics closed: J [list epic IDs] (or "none — children still open" / "n/a — no epic in batch")
83
110
  Reconciliation commit: <hash> (or "not needed")
84
111
  ```
85
112
  7. **HALT condition**: if a card cannot be updated (e.g., file read error, YAML parse error), log it in `## Issues & Flags` and continue with the remaining cards. Report the failure in the final summary.
@@ -163,6 +163,7 @@ const MERGE_SCHEMA = {
163
163
  mergeTs: { type: 'string' },
164
164
  reconciliation: { type: 'string' },
165
165
  forcedDone: { type: 'array', items: { type: 'string' }, description: 'MUST be empty — false-DONE is forbidden (F-029)' },
166
+ epicsClosed: { type: 'array', items: { type: 'string' }, description: 'Epic/parent cards marked DONE by Phase 6b step 5e (all children DONE) — NOT a forcedDone violation' },
166
167
  uncommittedLeft: { type: 'boolean', description: 'true if dirty code was left (NOT committed) + reported (F-030)' },
167
168
  note: { type: 'string' },
168
169
  },
@@ -634,6 +635,7 @@ if (!committed.length) {
634
635
  `• G24 → auto-merge via merge_strategy.\n` +
635
636
  `• F-030 HARD RULE: NEVER \`git add\`/commit code that did not pass the per-card gates. If the worktree is dirty with uncommitted code → DO NOT commit it; leave it, set uncommittedLeft:true, and report. NO "safety commit". Security/migration code is NEVER swept in.\n` +
636
637
  `• F-029 HARD RULE: Phase 6b reconciliation marks a card DONE ONLY if it has a real commit in ${TRUNK}..HEAD AND its gates are green. NEVER force a non-implemented card to DONE. Return forcedDone:[] (must be empty).\n` +
638
+ `• EPIC CLOSURE (Phase 6b step 5e): the epic/parent card (group.is_epic:true) is NOT in the batch and stays TODO unless closed here. For each distinct group.parent of the batch cards (and any epic card in the batch itself): if EVERY child of that epic — \`grep -l "parent: <EPIC-ID>" backlog/*.yml | xargs grep -L "status: DONE"\` prints nothing — set the epic card status:DONE + completed_date + note "epic-closure gate — all children DONE" and fold into the reconciliation commit. If any child is still open → leave the epic untouched. This is NOT a forcedDone violation (the epic is a tracker, gated on all-children-DONE, not on its own commit). Return epicsClosed:[<EPIC-IDs marked DONE>].\n` +
637
639
  `• G19 sync-deferred → HEAD==${TRUNK} ff-pull, else leave+report. G20 → leave+report. G21 post-batch dirty → partition-ignore framework artifacts; leave the rest + report (do NOT commit). G22 divergence → behind: ff-pull; ahead/both: leave+report; NEVER reset --hard/force-push. G23 stash restore conflict → leave intact + report.\n\n` +
638
640
  `Return: { merged, mergeCommit, mergeTs, reconciliation, forcedDone:[], uncommittedLeft, note }`,
639
641
  { label: 'merge', phase: 'Merge', agentType: 'general-purpose', schema: MERGE_SCHEMA }
@@ -641,6 +643,7 @@ if (!committed.length) {
641
643
  } catch (e) { if (e && e.transientExhausted) noteDegraded('outage'); mergeResult = null }
642
644
  if (mergeResult && (mergeResult.forcedDone || []).length) { noteDegraded('false_done'); ledger(firstCard, 'F029-guard', 'VIOLATION', `forcedDone: ${mergeResult.forcedDone.join(' ')}`) }
643
645
  if (mergeResult && mergeResult.uncommittedLeft) ledger(firstCard, 'F030-guard', 'LEFT-UNCOMMITTED', 'dirty code left (not swept) + reported')
646
+ if (mergeResult && (mergeResult.epicsClosed || []).length) ledger(firstCard, 'epic-closure', 'CLOSED', `epics marked DONE (all children DONE): ${mergeResult.epicsClosed.join(' ')}`)
644
647
  ledger(firstCard, 'G24-merge', (mergeResult && mergeResult.merged) ? 'MERGED' : 'INCOMPLETE', (mergeResult && (mergeResult.mergeCommit || mergeResult.note)) || '')
645
648
  if (mergeResult && mergeResult.reconciliation) ledger(firstCard, 'G19-23-reconcile', 'AUTO', mergeResult.reconciliation)
646
649
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "4.22.0",
3
+ "version": "4.22.1",
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"