baldart 4.21.1 → 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 +25 -0
- package/VERSION +1 -1
- package/framework/.claude/skills/new/references/final-review.md +7 -1
- package/framework/.claude/skills/new/references/merge-cleanup.md +27 -0
- package/framework/.claude/skills/prd/SKILL.md +16 -0
- package/framework/.claude/skills/prd/assets/state-template.md +9 -0
- package/framework/.claude/skills/prd/references/discovery-phase.md +25 -6
- package/framework/.claude/skills/prd/references/prd-writing-phase.md +5 -3
- package/framework/.claude/skills/prd/references/validation-phase.md +74 -0
- package/framework/.claude/workflows/new2.js +3 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@ 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
|
+
|
|
18
|
+
## [4.22.0] - 2026-06-09
|
|
19
|
+
|
|
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).
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **`framework/.claude/skills/prd/references/validation-phase.md`** — new **Step 7.6 — Obsidian back-reference** (runs after the merge, when the canonical paths/IDs/SHA are final). Writes a marker-delimited section (`<!-- BALDART:prd:<slug> START/END -->`) holding a fenced `yaml` payload + human-readable links back into the spec note. **Idempotent** (re-runs / `/prd-add` replace the block in place), **non-blocking** (no note, unresolved vault path, or unwritable file → skip with a log line). The note lives in the user's vault, outside the worktree and outside git — a plain filesystem write, never staged.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- **`framework/.claude/skills/prd/references/discovery-phase.md`** — Step 1 point 2 Obsidian detection broadened beyond `[[wikilink]]` to also accept `obsidian://open?…` URIs and pasted `.md` paths, and now resolves + persists the note's **absolute `path:`** (vault root from `paths.obsidian_vault` / memory / one-time ask) so Step 7.6 can write back without re-deriving.
|
|
29
|
+
- **`framework/.claude/skills/prd/assets/state-template.md`** — new structured `## Obsidian Spec Note` section (`wikilink:` / `path:` / `status:`).
|
|
30
|
+
- **`framework/.claude/skills/prd/SKILL.md`** — new **HARD RULE 19** (Obsidian back-reference) + Step 7.6 row in the flow table.
|
|
31
|
+
- **`framework/.claude/skills/prd/references/prd-writing-phase.md`** — Canonical Sources row now reads the structured `wikilink:` field and cross-links Step 7.6.
|
|
32
|
+
|
|
8
33
|
## [4.21.1] - 2026-06-09
|
|
9
34
|
|
|
10
35
|
**Nightly reconciliation of the canonical API registries (errors.md / schemas.md).** Makes the nightly `doc-review` run explicitly keep `${paths.api_errors}` and `${paths.api_schemas}` in sync with the day's code changes, instead of relying on the generic doc↔code drift step. **PATCH** (strengthens existing routine/agent behaviour; no new agent/skill/command/config key).
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
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.
|
|
@@ -49,6 +49,7 @@ message. You ask questions, wait for answers, and iterate.
|
|
|
49
49
|
| 5 | Backlog Cards | [backlog-phase.md](references/backlog-phase.md) |
|
|
50
50
|
| 6 | Quality Audit | [validation-phase.md](references/validation-phase.md) |
|
|
51
51
|
| 7 | Resolution, Commit & Merge | [validation-phase.md](references/validation-phase.md) |
|
|
52
|
+
| 7.6 | Obsidian back-reference (only if a spec note was given) | [validation-phase.md](references/validation-phase.md) |
|
|
52
53
|
|
|
53
54
|
**Before starting each phase, read the corresponding reference file.**
|
|
54
55
|
|
|
@@ -233,6 +234,21 @@ message. You ask questions, wait for answers, and iterate.
|
|
|
233
234
|
transcripts for REAL per-role token + wall-clock cost. The token itself is NOT part
|
|
234
235
|
of the feature description — strip it before recording the user's verbatim feature
|
|
235
236
|
text in `## Feature Description`.
|
|
237
|
+
19. **Obsidian back-reference (MANDATORY when a spec note was given).** If the user
|
|
238
|
+
kicked off the PRD from an Obsidian note (any of: `[[Note]]` wikilink,
|
|
239
|
+
`obsidian://open?…` URI, or a pasted `.md` path), Step 1 detection persists it in
|
|
240
|
+
the state file `## Obsidian Spec Note` (`wikilink:` + absolute `path:` + `status:`).
|
|
241
|
+
At the END of the run — **after** the merge (validation-phase Step 7, item 9) so the
|
|
242
|
+
paths/IDs/SHA are final — Step 7.6 writes an **idempotent** snippet back into that
|
|
243
|
+
note: a marker-delimited section (`<!-- BALDART:prd:<slug> START/END -->`) holding a
|
|
244
|
+
fenced `yaml` block with the feature slug, PRD path, epic + card IDs, branch, merge
|
|
245
|
+
commit/PR, and the PRD's Canonical Sources as `resources:`. This makes the original
|
|
246
|
+
note the durable index the user can later query ("come è stata sviluppata la feature
|
|
247
|
+
X, dov'è finita"). The note lives in the user's **vault, outside the worktree and
|
|
248
|
+
outside git** — it is a plain filesystem write, never staged/committed. The step is
|
|
249
|
+
**NON-BLOCKING**: no spec note, unresolved vault path, or an unwritable note all skip
|
|
250
|
+
it with a log line, never aborting the PRD. See discovery-phase.md Step 1 point 2 and
|
|
251
|
+
validation-phase.md Step 7.6.
|
|
236
252
|
|
|
237
253
|
---
|
|
238
254
|
|
|
@@ -12,6 +12,15 @@ stats_enabled: {{true if invoked with -stats/--stats, else false}}
|
|
|
12
12
|
- created_at: {{ISO-8601 UTC}}
|
|
13
13
|
- merge_commit: (pending — populated by validation-phase Step 7 after mw-docs)
|
|
14
14
|
|
|
15
|
+
## Obsidian Spec Note
|
|
16
|
+
|
|
17
|
+
<!-- Populated by discovery-phase.md Step 1 point 2 ONLY when the user passed an
|
|
18
|
+
Obsidian note as the documental starting point. When no note was given, leave
|
|
19
|
+
status: none and the Step 7.6 back-reference is skipped. -->
|
|
20
|
+
wikilink: none # [[Note Name]] form — feeds PRD Canonical Sources row
|
|
21
|
+
path: none # absolute fs path of the note — target of the Step 7.6 writeback
|
|
22
|
+
status: none # none | detected | back-reference-written
|
|
23
|
+
|
|
15
24
|
## Feature Description
|
|
16
25
|
{{user's description verbatim}}
|
|
17
26
|
|
|
@@ -59,12 +59,31 @@ this is a fresh session (no existing state file).
|
|
|
59
59
|
|
|
60
60
|
1. Derive `<slug>`: kebab-case, lowercase, max 40 chars, no special chars.
|
|
61
61
|
Example: "Nutritional info on menu" -> `menu-nutritional-info`
|
|
62
|
-
2. **Obsidian spec detection:** scan the user's message
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
-
|
|
67
|
-
|
|
62
|
+
2. **Obsidian spec detection (and back-reference setup):** scan the user's message
|
|
63
|
+
for a reference to an Obsidian note used as the documental starting point. Accept
|
|
64
|
+
any of these forms:
|
|
65
|
+
- Wikilink: `[[Note Name]]` or `[[Note Name|Alias]]`.
|
|
66
|
+
- `obsidian://open?vault=<Vault>&file=<rel/path/Note>` URI (URL-decode `file`).
|
|
67
|
+
- A filesystem path to a `.md` file the user pastes ("la nota è in `…/Vault/…/Note.md`").
|
|
68
|
+
|
|
69
|
+
If found, populate the `## Obsidian Spec Note` section of the state file with:
|
|
70
|
+
- `wikilink:` — the `[[Note Name]]` form (for the PRD Canonical Sources row, see
|
|
71
|
+
prd-writing-phase.md Step 4 point 2).
|
|
72
|
+
- `path:` — the **absolute** filesystem path of the note. Resolve it now so the
|
|
73
|
+
end-of-run back-reference (validation-phase Step 7.6) can write to it without
|
|
74
|
+
re-deriving:
|
|
75
|
+
- URI/full path given → use it directly (URL-decode + join `vault`+`file`).
|
|
76
|
+
- Only a bare wikilink given → you need the vault root to resolve it. Use the
|
|
77
|
+
vault path recorded in memory (`reference_obsidian_vault.md`) if present; else
|
|
78
|
+
**ASK the user once** for the vault root (or the note's full path) and persist
|
|
79
|
+
their answer in this state section. Do NOT assume a default. If the user
|
|
80
|
+
declines, set `path: none` — detection still works for pre-population, but the
|
|
81
|
+
Step 7.6 writeback is skipped (logged, never fatal).
|
|
82
|
+
- `status: detected`.
|
|
83
|
+
|
|
84
|
+
Then read the note content (from `path:`) to pre-populate discovery dimensions
|
|
85
|
+
where possible — treat it as a user-provided spec. This same note becomes the
|
|
86
|
+
target of the back-reference snippet at the end of the run (HARD RULE 19).
|
|
68
87
|
3. **Create the docs worktree (MANDATORY — HARD RULE 17)** — invoke the
|
|
69
88
|
`worktree-manager` skill in programmatic docs mode BEFORE any file write:
|
|
70
89
|
|
|
@@ -7,10 +7,12 @@ Mark task 3 as `in_progress`.
|
|
|
7
7
|
## Write PRD
|
|
8
8
|
|
|
9
9
|
1. Create directory `${paths.prd_dir}/<slug>/` if it doesn't exist.
|
|
10
|
-
2. **Obsidian spec note (if present):** if the state file
|
|
11
|
-
add a row to the Canonical Sources table in the PRD
|
|
10
|
+
2. **Obsidian spec note (if present):** if the state file `## Obsidian Spec Note`
|
|
11
|
+
section has `status: detected`, add a row to the Canonical Sources table in the PRD
|
|
12
|
+
using its `wikilink:` value:
|
|
12
13
|
`| Obsidian spec | [[Note Name]] |`
|
|
13
|
-
This preserves traceability from the user's original spec note to the final PRD
|
|
14
|
+
This preserves traceability from the user's original spec note to the final PRD, and
|
|
15
|
+
the same note receives the end-of-run back-reference snippet (validation-phase Step 7.6).
|
|
14
16
|
3. **Resolve canonical sources** before writing:
|
|
15
17
|
- Read `${paths.references_dir}/ssot-registry.md` to identify the macro feature and any
|
|
16
18
|
existing canonical docs.
|
|
@@ -247,6 +247,79 @@ markers it can emit, then act:
|
|
|
247
247
|
empty, no `[SYNC-NEEDS-DECISION]` marker is left unhandled, and the merged remote
|
|
248
248
|
branch is gone (or its deletion is explicitly user-deferred).
|
|
249
249
|
|
|
250
|
+
### Step 7.6 — Obsidian back-reference (NON-BLOCKING — runs only when a spec note was given)
|
|
251
|
+
|
|
252
|
+
**Why this exists.** When the user kicked off the PRD from an Obsidian note (state
|
|
253
|
+
file `## Obsidian Spec Note / status: detected`, see HARD RULE 19), they want that
|
|
254
|
+
same note to become the durable index of *how the feature was developed and where it
|
|
255
|
+
landed* — so they can later ask "come è stata sviluppata la feature X?" and get the
|
|
256
|
+
PRD, cards, and merge from the note itself. This step writes a single, idempotent,
|
|
257
|
+
machine-and-human readable snippet back into the note.
|
|
258
|
+
|
|
259
|
+
**Runs AFTER the merge (Step 7 item 9) succeeds** — only then are the canonical paths,
|
|
260
|
+
card IDs, branch, PR/SHA final. The note lives in the user's **vault, OUTSIDE the
|
|
261
|
+
worktree and outside git** — so this is a plain filesystem write to the absolute
|
|
262
|
+
`path:`, NOT a worktree-relative path and NOT a git-tracked artefact (do not stage it).
|
|
263
|
+
|
|
264
|
+
**Skip conditions (each logged, never fatal):**
|
|
265
|
+
- `## Obsidian Spec Note / status` is `none` (no note was given) → skip silently.
|
|
266
|
+
- `path:` is `none` or empty (vault root never resolved at Step 1) → skip with a note.
|
|
267
|
+
- The file at `path:` does not exist or is not writable → skip with a one-line warning.
|
|
268
|
+
|
|
269
|
+
**Procedure:**
|
|
270
|
+
|
|
271
|
+
1. Read the current content of the note at `path:`.
|
|
272
|
+
2. Build the snippet, delimited by HTML-comment markers keyed on the slug so the step
|
|
273
|
+
is **idempotent** — a re-run (or a later `/prd-add`) REPLACES the block between the
|
|
274
|
+
markers in place instead of appending a duplicate. If the markers are absent, append
|
|
275
|
+
the block at the end of the note (preceded by one blank line).
|
|
276
|
+
|
|
277
|
+
```markdown
|
|
278
|
+
<!-- BALDART:prd:<slug> START -->
|
|
279
|
+
## 🛠 Sviluppo BALDART — <slug>
|
|
280
|
+
|
|
281
|
+
```yaml
|
|
282
|
+
feature: <slug>
|
|
283
|
+
prd: ${paths.prd_dir}/<slug>/PRD.md
|
|
284
|
+
epic: FEAT-XXXX-00-<slug>-epic
|
|
285
|
+
cards:
|
|
286
|
+
- FEAT-XXXX-01-<sub-slug>
|
|
287
|
+
- FEAT-XXXX-02-<sub-slug>
|
|
288
|
+
resources: # other Canonical Sources from the PRD (mockups, ADRs, ref docs, research)
|
|
289
|
+
- ${paths.prd_dir}/<slug>/design.html # omit if no design
|
|
290
|
+
- <ssot-registry entry / ADR path / research link>
|
|
291
|
+
branch: prd/<slug>
|
|
292
|
+
merge_commit: <SHA from state ## Validation>
|
|
293
|
+
pr: <PR number, or n/a for local-push>
|
|
294
|
+
trunk_branch: <git.trunk_branch>
|
|
295
|
+
date: <ISO-8601 UTC>
|
|
296
|
+
status: planned # planned | in-progress | done
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
- **PRD:** [`${paths.prd_dir}/<slug>/PRD.md`](...)
|
|
300
|
+
- **Cards:** FEAT-XXXX-00 (epic) · FEAT-XXXX-01 · FEAT-XXXX-02 …
|
|
301
|
+
- **Merge:** `<SHA>` su `<git.trunk_branch>`<!-- · PR #N -->
|
|
302
|
+
|
|
303
|
+
_Generato da `/prd` il <date>. Aggiorna `status:` a `done` quando la feature è in produzione._
|
|
304
|
+
<!-- BALDART:prd:<slug> END -->
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Fill the `yaml` block from the final state: `<slug>`, the real epic + child card
|
|
308
|
+
IDs created in Step 5, the merge SHA written under `## Validation` (Step 7 item 8),
|
|
309
|
+
the PR number from the `mw-docs` return (or `n/a`), and the Canonical Sources list
|
|
310
|
+
the PRD already assembled (prd-writing-phase.md Step 4 point 3) for `resources:`.
|
|
311
|
+
The inner ```` ```yaml ```` block is fenced, NOT real top-of-file frontmatter — it
|
|
312
|
+
is the queryable payload Claude reads back when asked about the feature later.
|
|
313
|
+
|
|
314
|
+
3. Write the updated note back to `path:`.
|
|
315
|
+
4. Set `## Obsidian Spec Note / status: back-reference-written` in the state file.
|
|
316
|
+
(The state file is already committed; this is a local marker for a possible resume —
|
|
317
|
+
do NOT re-commit just for this field.)
|
|
318
|
+
|
|
319
|
+
**This step is NON-BLOCKING** — any failure (note moved, vault unmounted, permission
|
|
320
|
+
denied) logs "Obsidian back-reference: SKIPPED (<reason>)" and the run completes. The
|
|
321
|
+
PRD merge has already happened; the back-reference is a convenience index, never a gate.
|
|
322
|
+
|
|
250
323
|
### Final output
|
|
251
324
|
|
|
252
325
|
Print final summary:
|
|
@@ -257,6 +330,7 @@ Print final summary:
|
|
|
257
330
|
- Branch & merge strategy used
|
|
258
331
|
- PR number (if `strategy = pr`) or merge SHA (if `local-push`)
|
|
259
332
|
- Confirmation that the worktree was cleaned up AND the remote branch was deleted
|
|
333
|
+
- Obsidian back-reference: the note path the snippet was written to (only when Step 7.6 ran; omit otherwise)
|
|
260
334
|
|
|
261
335
|
**HARD RULE — no passive deferral.** The final summary MUST NOT contain a
|
|
262
336
|
"your manual actions / non-blocking notes / you can do it yourself" section.
|
|
@@ -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
|
}
|