baldart 5.5.0 → 5.6.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.
Files changed (31) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/VERSION +1 -1
  3. package/framework/.claude/agents/CHANGELOG.md +16 -0
  4. package/framework/.claude/agents/prd-card-writer.md +37 -3
  5. package/framework/.claude/skills/new/CHANGELOG.md +23 -0
  6. package/framework/.claude/skills/new/SKILL.md +1 -1
  7. package/framework/.claude/skills/new/references/commit.md +12 -8
  8. package/framework/.claude/skills/new/references/completeness.md +10 -0
  9. package/framework/.claude/skills/new/references/final-review.md +23 -0
  10. package/framework/.claude/skills/new/references/implement.md +14 -4
  11. package/framework/.claude/skills/new/references/merge-cleanup.md +35 -14
  12. package/framework/.claude/skills/new2/CHANGELOG.md +36 -0
  13. package/framework/.claude/skills/new2/SKILL.md +52 -12
  14. package/framework/.claude/skills/prd/CHANGELOG.md +36 -0
  15. package/framework/.claude/skills/prd/SKILL.md +17 -5
  16. package/framework/.claude/skills/prd/assets/state-template.md +8 -1
  17. package/framework/.claude/skills/prd/references/audit-phase.md +13 -1
  18. package/framework/.claude/skills/prd/references/backlog-phase.md +20 -0
  19. package/framework/.claude/skills/prd/references/discovery-phase.md +35 -0
  20. package/framework/.claude/skills/prd/references/ui-design-phase.md +28 -0
  21. package/framework/.claude/skills/prd/references/validation-phase.md +45 -1
  22. package/framework/.claude/skills/worktree-manager/CHANGELOG.md +13 -0
  23. package/framework/.claude/skills/worktree-manager/SKILL.md +11 -1
  24. package/framework/.claude/skills/worktree-manager/scripts/merge-worktree.sh +82 -6
  25. package/framework/.claude/workflows/new-final-review.js +9 -7
  26. package/framework/.claude/workflows/new2-resolve.js +29 -1
  27. package/framework/.claude/workflows/new2.js +153 -17
  28. package/framework/agents/card-schema.md +18 -1
  29. package/framework/agents/research-protocol.md +6 -1
  30. package/framework/scripts/stamp-holistic-audit.js +53 -10
  31. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,70 @@ 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.6.0] - 2026-07-04
9
+
10
+ **Outcome-integrity wave (FEAT-0068 post-mortem — mayo, 72.78M token, DONE falso).** Il
11
+ batch chiuse 6/6 card + epic come "✅ DONE, merged" mentre il goal dell'epic era invisibile
12
+ sul ~90% delle schermate: il root cause era in `/prd` (inventario ISA sui consumer del
13
+ meccanismo invece che sui proprietari della superficie; recipe ancorata su un componente
14
+ `@deprecated`; due gate phantom-stampati), attraversava card che nessun gate poteva salvare
15
+ (coverage passata vacuamente su un inventario sbagliato), e veniva sigillato da `/new`/`new2`
16
+ (chiusura epic al commit dell'ultima card — PRIMA della final review — con predicato
17
+ conta-figli, nessun verdetto PARTIAL, safety-commit che spazzava file non gated su develop,
18
+ commit agent in detour sul main checkout). 28 finding crit/high confermati adversarialmente;
19
+ ogni fix è ancorato a un'evidenza del run.
20
+
21
+ - **Chiusura epic v2 (`/new` 2.3.0 + `new2` 1.8.0)**: SOLO in merge phase (il gate
22
+ commit-time v4.99.0 è ritirato — aveva anche un anchor bug `^parent:` che lo rendeva
23
+ no-op); predicato esteso (figli **e follow-up** DONE + nessun residual bloccante +
24
+ **AC epic verificati con oracoli**); blocklist JS + guardia post-merge (F-029 twin) +
25
+ ramo **REOPEN** (il ratchet è bidirezionale); classe finding **closure-invalidating**
26
+ (un "goal not achieved" è un difetto di STATO — il fix di prosa non chiude).
27
+ - **Verdetto PARTIAL calcolato** (`final-review.md` + `buildReport`): residual HIGH+ /
28
+ epic aperta / AC epic `[ ]` ⇒ headline `⚠️ PARTIAL` col gap nominato; sezione
29
+ `## Epic goal`; severity sui residui; "nulla perso" mai come surrogato di "non finito".
30
+ - **Igiene git orchestrata**: `merge-worktree.sh --safety-sweep allowlist:` (worktree-manager
31
+ 1.2.0 — il codice non gated va in quarantena etichettata, mai nel trunk sotto un
32
+ "[safety]" opaco); isolamento worktree del commit agent esteso ai TOOL con evidenza
33
+ porcelain + guardia JS (era 7.1M di detour + stash dangling); divieto stash + audit
34
+ post-run dei commit non-card e degli stash del batch (mai "non miei da droppare").
35
+ - **Review**: relay Codex della final review in foreground `--wait` (il polling veniva
36
+ ucciso dall'enforcement structured-output a 83s/600s → fallback freddo da 3.68M);
37
+ esito **refuted** ratificato in `new2-resolve` (premessa falsa = zero edit) + divieto
38
+ di fix che disattivano verifiche mandate da AC (`verification-posture-change`);
39
+ suite aggiunte dalla card ESEGUITE targeted pre-commit (`completeness.md` 1b).
40
+ - **Routing**: `hasMockup` semantico (la card COSTRUISCE la UI mockata — un bare
41
+ `links.design` su card docs va all'owner router; era 6.55M di ui-expert su una card
42
+ docs+ADR).
43
+ - **`/prd` 1.6.0 (il root cause)**: categoria ISA **UI ROLE FAMILY (registry-first)**
44
+ (owner della superficie dal registry + flag BLOCK su `@deprecated`); §0 LOAD CONTRACT
45
+ su `discovery-phase.md` (vista PARTIAL ⇒ offset-Read fino in fondo — la coda persa
46
+ conteneva il Codex completeness cross-check mai eseguito); `module_read` stamps
47
+ ESEGUITI + item 0-ii Phase-evidence audit (2 gate phantom-stampati sul run reale);
48
+ `step_3_mode` enum chiuso + contratto `skipped` (mai design hand-authored fuori
49
+ `/ui-design`) + Reconciliation-lite senza mockup + trigger di ri-enumerazione ISA;
50
+ gate quantificatore-universale sugli AC epic (`invariant_owners:`); stamp holistic
51
+ **evidence-gated** (`stamp-holistic-audit.js --audit-report` → `agents_run`; senza
52
+ artefatto → `status: skipped` = ASSENTE per `/new`); card-writer 1.2.0
53
+ (self-validation eseguita, epic `ac_verification` obbligatoria, mai count hardcodato,
54
+ `links.design` solo su card UI, invariante baseline snapshot); HARD RULE mw-docs
55
+ (+ guardia `/mw` su `kind: docs`); EDIT CONTRACT dello state file (5 retry ciechi
56
+ da ~250k); `research-protocol.md` pre-flight libreria in difesa.
57
+ - **Tooling maintainer**: `new-session-audit.mjs` guadagna **EPIC_AC_UNMET** (FAIL —
58
+ retro-validato: il run FEAT-0068 ora fallisce l'audit invece di passarlo) e
59
+ **GATE_DISABLED_BY_FIX** (WARN) + fix del digest-writer su id workflow con `/`;
60
+ `check-new-parity.mjs` esteso a 21 policy (chiusura epic, PARTIAL, mockup semantico —
61
+ con token FORBIDDEN sui meccanismi ritirati).
62
+ - Deliberatamente rinviati (MED/LOW non verificati dal pass avversariale): digest
63
+ per-batch dei moduli di riferimento (tassa cache ~3-5M), card shape
64
+ VERIFY-ONLY/MECHANICAL-MIGRATION, sampling visivo del goal epic in final review,
65
+ script `update-state.mjs` per `/prd` (mitigato dall'EDIT CONTRACT).
66
+ - Codex parity: **portable** — le policy vivono nei moduli prose SSOT (`/new`, `/prd`)
67
+ e in script zero-dep/shell; i workflow Claude-only (`new2*`, `new-final-review`)
68
+ hanno ricevuto le STESSE policy dei twin prose (guardia di parity estesa a 21 regole).
69
+ Nessuna nuova chiave `baldart.config.yml` → la schema-change propagation rule non si
70
+ applica.
71
+
8
72
  ## [5.5.0] - 2026-07-03
9
73
 
10
74
  **Migration deploy chain hardened (FEAT-0067 post-mortem — mayo).** The batch's DB
package/VERSION CHANGED
@@ -1 +1 @@
1
- 5.5.0
1
+ 5.6.0
@@ -7,6 +7,22 @@ per-item agent merge, the doctor probes, and the discovery hooks — like
7
7
 
8
8
  Formato: [Keep a Changelog](https://keepachangelog.com/) · per-agent SemVer.
9
9
 
10
+ ## [prd-card-writer v1.2.0] — 2026-07-04 (framework v5.6.0 — FEAT-0068 post-mortem)
11
+
12
+ - **Epic AC rules**: mai count di figli hardcodato ("le 6 sub-card"); AC con
13
+ quantificatore universale su una superficie ⇒ `invariant_owners:` dal census
14
+ registry/ISA; `ac_verification` OBBLIGATORIA sull'epic (≥1 oracolo per goal AC —
15
+ consumata dal gate di chiusura epic).
16
+ - **`links.design` FORBIDDEN su card che non costruiscono UI mockata** (docs/test/
17
+ meta-only ⇒ owner coder, riferimenti di contesto in `canonical_docs.additional_refs`)
18
+ — un pointer vagante clampava la build a ui-expert (6.55M su una card docs).
19
+ - **Invariante snapshot-suite**: una card che CREA una suite visual-regression dichiara
20
+ il lifecycle delle baseline (generate+committate in-card, o assertion-based) — pena
21
+ BLOCKER di authoring.
22
+ - **Self-validation**: il writer ESEGUE `validate-card-baseline.js --prd` sul proprio
23
+ output e ritorna solo su exit 0 (token EARS sempre inglesi verbatim) — mai asserire
24
+ "YAML-valid" senza aver eseguito il validator.
25
+
10
26
  ## [prd-card-writer v1.1.0] — 2026-07-03 (framework v5.4.0)
11
27
 
12
28
  - **Proportionality lower-bound** in Card Atomicity Rules: a Tier-Gate TRIVIAL/LIGHT
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: prd-card-writer
3
- version: 1.1.0
3
+ version: 1.2.0
4
4
  description: "Generates atomic backlog cards from an approved PRD. Handles card YAML writing, dependency graph computation, parallel group assignment, and traceability matrices (FR/NFR, ISA, UI). Invoked by the /prd skill at Step 5 to offload the heaviest mechanical phase from the main context. Also runs in Standalone Single-Card Mode to author ONE baseline-conformant follow-up/CHORE/BUG/issue card (no epic+children) — invoked by /new (AC-deferral), new2/new2-resolve, and /issue-review. The canonical card writer: every card-creation path delegates here rather than hand-writing a partial stub."
5
5
  model: opus
6
6
  effort: high
@@ -335,7 +335,18 @@ applies even when N=1.
335
335
  - `business_rationale` (extended — full strategic context)
336
336
  - `scope.summary` + `scope_boundaries` (in_scope summary + out_of_scope deferrals)
337
337
  - `acceptance_criteria` as `[ ] [AC-EPIC-N]` items rolling up the PRD ACs (NOT
338
- duplicating child ACs — track end-to-end outcomes only)
338
+ duplicating child ACs — track end-to-end outcomes only). **v5.6.0 (FEAT-0068)
339
+ epic-AC rules:** (1) NEVER hardcode a child count ("all 6 sub-cards DONE" broke the
340
+ moment follow-ups appeared — write "all child cards **including any added later**");
341
+ (2) an AC with a universal quantifier over a surface ("every screen", "tutte le
342
+ route") MUST list its surface owners in `invariant_owners:` (from the ISA UI ROLE
343
+ FAMILY census) so the closure gate can re-verify them.
344
+ - `ac_verification` — **REQUIRED on the epic (v5.6.0)**: ≥1 executable oracle per
345
+ top-level goal AC (same grounding rule as child cards — only verified commands/greps;
346
+ else `manual:`). The `/new`/`new2` epic-closure gate RUNS these before flipping the
347
+ epic DONE — an epic with no oracle can only close via manual evidence, which is
348
+ slower. (FEAT-0068's goal was greppable at authoring time: "every top-edge family
349
+ member consumes `--safe-area-inset-top`".)
339
350
  - `definition_of_done`: epic-level (e.g. "all sub-card DONE", "PRD test scenarios
340
351
  executed", "ssot-registry entry added")
341
352
  - `execution_strategy` block with `recommended_mode`, `groups[]` (each with
@@ -505,6 +516,14 @@ Every card MUST include ALL fields from the template:
505
516
  `manual: <what a human checks>`. Genuinely undecidable → `[NEEDS CLARIFICATION: …]`.
506
517
  Non-manual oracles SHOULD also appear in `validation_commands` (the validator warns on
507
518
  drift). Consumed by `/new` Phase 2.5, which EXECUTES them.
519
+ - **Snapshot-suite lifecycle invariant (v5.6.0 — FEAT-0068):** a card whose scope CREATES a
520
+ visual-regression / snapshot / screenshot-diff suite MUST declare the baseline lifecycle in
521
+ its requirements + DoD: EITHER "baselines generated (`--update-snapshots` or equivalent) and
522
+ committed IN THIS CARD" (the generate command listed in `validation_commands`/MAY-EDIT
523
+ covering the baseline dir) OR "assertion-based checks with screenshots advisory-only". A
524
+ suite whose baselines "arrive later" with no declared bootstrap is a card-authoring BLOCKER
525
+ — on the real run it detonated as a merge-blocker and the 4.73M repair "fixed" it by
526
+ flag-gating the epic's only visual verification.
508
527
  - `definition_of_done` (checklist including doc invariants). **When `features.has_i18n: true`** AND the
509
528
  card introduces user-facing strings, the DoD MUST carry an i18n-completeness line — NOT merely "strings
510
529
  via `t()` + registered", but: *"new keys authored in the source locale + the i18n context registry (by
@@ -519,7 +538,12 @@ Every card MUST include ALL fields from the template:
519
538
  - `review_profile` (`skip`|`light`|`balanced`|`deep` — computed via Rule C, or honored if pre-set)
520
539
  - `files_likely_touched` (exact paths with NEW/MODIFY)
521
540
  - `links.prd` (path to PRD)
522
- - `links.design` (REQUIRED for any card with UI scope) the card's screen design
541
+ - `links.design` (REQUIRED for any card with UI scope; **FORBIDDEN on cards that build no
542
+ mocked UI — v5.6.0**: a docs/test/meta-only card, or one whose `files_likely_touched` has
543
+ no component/style path, must NOT carry `links.design` even "for context" — downstream the
544
+ `/new` mockup-first router treats it as a build target and clamps the card to `ui-expert`;
545
+ on FEAT-0068 the epic explainer stamped on a docs+ADR card cost 6.55M. Point context-only
546
+ references in `canonical_docs.additional_refs` instead) — the card's screen design
523
547
  reference, resolved to a path **that exists on disk in the worktree**:
524
548
  - **design.html when one was generated** for this screen (Step 3 produced it).
525
549
  - **ELSE the screen's canonical mockup PNG** under `${paths.prd_dir}/<slug>/mockups/`
@@ -732,6 +756,16 @@ The consumer (`new/SKILL.md`) reads `execution_strategy.groups[].level` and
732
756
 
733
757
  ## Output Format
734
758
 
759
+ **Self-validation BEFORE returning (v5.6.0 — you own your domain's deterministic hygiene).**
760
+ Run `node .framework/framework/scripts/validate-card-baseline.js --prd <your card files>` (or
761
+ the `VALIDATOR` path the caller passed) on your own output and fix every deterministic failure
762
+ (enum values, EARS keyword language — the tokens are ALWAYS English verbatim, only the clause
763
+ body follows `identity.language` —, forbidden patterns, missing required fields) IN THIS
764
+ CONTEXT before returning. Return only on exit 0, and state `validator: exit 0 (N cards)` in
765
+ the summary. NEVER assert "YAML-valid / pattern-clean" without having executed the validator —
766
+ on a real session that unexecuted claim cost a full orchestrator round-trip when Step 6
767
+ bounced all 6 children on Italian EARS tokens. Step 6 still re-runs it as the independent gate.
768
+
735
769
  Return to the caller a structured summary:
736
770
 
737
771
  1. **Card list** — IDs and titles of all cards created
@@ -2,6 +2,29 @@
2
2
 
3
3
  Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
4
4
 
5
+ ## 2.3.0 — 2026-07-04 (FEAT-0068 post-mortem: integrità dell'esito)
6
+
7
+ - **Chiusura epic: solo in merge phase, predicato esteso** — `commit.md` c-bis RITIRATO
8
+ (timing: chiudeva PRIMA che la final review potesse smentire il goal; anchor bug:
9
+ `^parent:` non matchava il `parent:` annidato sotto `group:` → gate no-op).
10
+ `merge-cleanup.md` 5e ora richiede: figli **e follow-up** DONE + nessun residual
11
+ bloccante/finding closure-invalidating + **AC epic verificati con evidenza**
12
+ (oracoli `ac_verification`); ramo **REOPEN** per un'epic DONE con scope aperto.
13
+ - **Classe finding "closure-invalidating"** (`final-review.md` F.4): un finding
14
+ "goal/AC epic non raggiunto" è un difetto di STATO — il fix testuale non basta,
15
+ l'epic non chiude (o riapre) nello stesso repair commit.
16
+ - **Verdetto PARTIAL calcolato** (`final-review.md` § Report verdict): residual HIGH+ /
17
+ epic aperta / AC epic `[ ]` ⇒ prima riga `⚠️ PARTIAL` col gap nominato; ✅ riservato.
18
+ - **Routing mockup semantico** (`implement.md` 6a): `HAS_MOCKUP` = la card COSTRUISCE
19
+ la UI mockata (design_src, o design + bindings/file UI) — un bare `links.design` su
20
+ card docs/meta va all'owner router (era: clamp a ui-expert, 6.55M su una card docs).
21
+ - **Stamp holistic evidence-gated** (`implement.md` step 4 P1): uno stamp senza
22
+ `agents_run` non vuoto = ASSENTE → il plan-auditor gira (phantom stamp FEAT-0068).
23
+ - **Suite aggiunte dalla card: eseguite, non solo parse** (`completeness.md` 1b): uno
24
+ spec ADDED va RUN targeted pre-commit; suite snapshot senza lifecycle baseline
25
+ dichiarato = `Partial` → gap-fix subito (era: merge-blocker + repair da 4.73M che
26
+ flag-gatava la verifica dell'epic).
27
+
5
28
  ## 2.2.1 — 2026-07-03 (FEAT-0067 post-mortem: migration vocabulary aliases)
6
29
 
7
30
  - Il gate di delega `-auto` (condizione 4) e il Migration Gate Phase 0 1b (setup.md
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: new
3
3
  effort: medium
4
- version: 2.2.1
4
+ version: 2.3.0
5
5
  description: >
6
6
  Orchestrate a team of specialized agents to implement one or more backlog cards
7
7
  end-to-end inside a dedicated worktree, with code review, doc review, QA, and
@@ -33,14 +33,18 @@
33
33
  a. Edit the backlog YAML (`${paths.backlog_dir}/<CARD-ID>.yml`): set `status: DONE`, add `completed_date: <today>`, and add `provenance.implementation_session: <SESSION-ID>` (this run's session UUID — get it with `Bash(echo "${CLAUDE_CODE_SESSION_ID:-}")`; if empty, omit the field). Merge into any existing `provenance:` block written by `/prd` — do NOT overwrite `planning_session`. This makes an implementation bug traceable back to the session that produced it (`~/.claude/projects/*/<id>.jsonl`).
34
34
  b. Add implementation notes summarizing what was built.
35
35
  c. **Update `${paths.references_dir}/ssot-registry.md`** — add/update the entry for this card's feature area. The pre-commit doc-freshness hook BLOCKS commits that touch `${paths.backlog_dir}/` without a corresponding ssot-registry update. Always include ssot-registry.md in the same commit as the backlog YAML.
36
- c-bis. **Epic-closure gate (deterministic since v4.99.0)**: if the card YAML declares a parent epic (`parent:` field), check AFTER step a set this card DONE — whether it was the LAST open child. Deterministic count, no judgement:
37
- ```bash
38
- PARENT=$(awk '/^parent:/ {print $2; exit}' ${paths.backlog_dir}/<CARD-ID>.yml)
39
- if [ -n "$PARENT" ]; then
40
- PENDING=$(grep -l "^parent: ${PARENT}" ${paths.backlog_dir}/*.yml | xargs grep -L "^status: DONE" | wc -l)
41
- fi
42
- ```
43
- When `PENDING` is `0`: flip the PARENT epic's status in its ssot-registry.md entry to DONE **in the SAME commit**, and if the parent epic has its own backlog YAML, set its `status: DONE` + `completed_date` too. Non-triggers (do nothing, idempotent): the card has no `parent:`; siblings still pending (`PENDING > 0`); the parent entry is already DONE. This closes a recurring consumer gap: step 28c alone updates the CARD's feature-area entry, so epics stayed `IN_PROGRESS` after their last child closed and were only caught by the nightly doc routine (3 confirmed occurrences across two audit windows).
36
+ c-bis. **Epic closure does NOT happen here (v5.6.0 — the v4.99.0 commit-time gate is RETIRED).**
37
+ Two proven defects (FEAT-0068 post-mortem): (1) **timing** — closing at the last card's
38
+ commit runs structurally BEFORE the final review can disprove the epic goal (the FEAT-0068
39
+ epic closed DONE 13 minutes before the review proved the goal unmet on ~90% of screens),
40
+ and the commit context cannot see follow-up cards materialized later in the batch;
41
+ (2) **anchor bug** — the retired gate grepped `^parent:` but the card schema nests
42
+ `parent:` indented under `group:`, so it was a silent no-op on schema-conformant cards.
43
+ Epic closure now lives ONLY in the merge phase (`merge-cleanup.md` Phase 6b step 5e,
44
+ extended predicate: children **and follow-ups** DONE + no blocking residual + **epic ACs
45
+ verified**), which runs AFTER the final review. At commit time the parent epic and every
46
+ sibling card YAML are READ-ONLY. The epic-staleness safety net the v4.99.0 gate closed
47
+ stays closed by Phase 6b 5e + the nightly doc routine.
44
48
  d. **Verify the write**: re-read the YAML file and confirm `status: DONE` is present. If not, retry the edit.
45
49
  e. Stage BOTH the updated YAML AND ssot-registry.md, then commit (or as an immediate follow-up commit if the Phase 4 implementation commit already happened). When this produces a SECOND commit for the card, record BOTH hashes in the tracker (`commit: <impl-hash> + <done-hash>`) so traceability/bisect is unambiguous.
46
50
  29. **Update tracker**: move card to `## Completed Cards` with commit hash(es), summary, flags, **and `card_status: DONE (verified)`** — the tracker is the only state surface (§ "State surface — the tracker only"); do not also emit a TaskUpdate or Progress-Bar turn for the transition. If you surface the card completion to the user at all, do it as a short prose line folded into the commit turn, never a dedicated turn.
@@ -36,6 +36,16 @@ Before triggering any review, you MUST verify that the coder agent implemented *
36
36
  `conditional_items` section → flag as `Missing` (the coder must document the branch taken).
37
37
 
38
38
  1. **Enter Phase 2.5 (completeness)** — **no standalone tracker write** for phase entry (§ "Context Tracking" allowlist; the tracker records only `2.5-completeness DONE` at step 6, co-emitted). Proceed directly to the next step.
39
+ 1b. **Card-added test suites must RUN, not just parse (v5.6.0 — FEAT-0068).** If the card's diff
40
+ ADDS or modifies a test/spec file, the completeness evidence for that item is a targeted
41
+ EXECUTION of exactly that file (`npx vitest run <file>` / `npx playwright test <file>` / the
42
+ toolchain equivalent), in the worktree, before commit — a `--list`/parse check only proves the
43
+ file compiles, not that the suite can pass. A suite that cannot run yet by design (e.g. a
44
+ visual-regression suite whose baselines are generated later) is only `Done` if the card
45
+ EXPLICITLY declares that lifecycle (see `card-schema.md` § snapshot-suite invariant) AND the
46
+ declared bootstrap command is exercised; otherwise classify `Partial` and route to the
47
+ gap-fix loop NOW — the FEAT-0068 card shipped a baseline-less suite that detonated as a
48
+ merge-blocker and was "fixed" by flag-gating the epic's only visual verification (4.73M).
39
49
  2. Re-read the backlog card (`requirements`, `acceptance_criteria`, `unknowns`, and `definition_of_done` fields).
40
50
  3. Build a checklist — one row per item:
41
51
 
@@ -334,6 +334,16 @@ that is a **gate violation**: log it as
334
334
  - **Each domain specialist OWNS its lane end-to-end** — it runs its OWN false-positive check in the finding pass (doc-reviewer for doc, api-perf-cost-auditor for api/perf/data, and the Codex/`code-reviewer`-fallback engine for code), so its surviving findings are **already validated**. Do **NOT** cross-validate a doc or api finding by spawning `code-reviewer` (that is the WRONG specialist judging another domain), and do **NOT** re-run the same specialist over its own findings (self-judge — no model diversity). Codex findings are likewise already FP-validated (Step F.3 protocol includes it).
335
335
  - **Residual only:** a finding the originating specialist explicitly leaves UNRESOLVED (`confidence < 80`) is routed to the finding's **domain specialist** (by `domain`: doc→doc-reviewer, api/perf→api-perf-cost-auditor, security/migration→security-reviewer, test→qa-sentinel, else code-reviewer) over the cited file:line. If that domain specialist **is** the originating finder (it already had its pass), classify `NEEDS_MANUAL_CONFIRMATION` instead of re-spawning it — never self-judge, never silently drop.
336
336
  - Classify: `VERIFIED` | `FALSE_POSITIVE` | `NEEDS_MANUAL_CONFIRMATION`.
337
+ - **Closure-invalidating findings (v5.6.0 — FEAT-0068 post-mortem).** A VERIFIED finding (any
338
+ severity) asserting that an **epic goal or epic AC is NOT met** while state says otherwise —
339
+ overclaim in the SSOT/registry, "DONE but unmet", "goal not achieved" — is a **STATE defect,
340
+ not a prose defect**. Tag it `closure_invalidating: true`. Its fix has TWO mandatory parts:
341
+ (1) the text correction (the doc fixer's job as usual), AND (2) the **state reversal** — the
342
+ named epic must NOT be closed in Phase 6b, or, if already `DONE` on disk, must be REOPENED to
343
+ `IN_PROGRESS` in the same repair commit (`merge-cleanup.md` 5e step a). Accepting the text
344
+ fix alone as resolution merges a registry that says "goal not achieved" alongside an epic
345
+ YAML that says DONE — the exact FEAT-0068 contradiction. The batch verdict becomes PARTIAL
346
+ (§ Report verdict below) whenever one of these stands.
337
347
  - `VERIFIED` findings proceed to fixes — **EXCEPT no-action ones** (`requires_action:false`, or a "no fix required" / "acceptable as-is" / "verified non-issue" direction; MEDIUM/LOW only). A no-action finding is TRUE but needs no change: record it (`decision=skipped | reason=no-action`) and do NOT route it to a writer. (The delegated `new-final-review` workflow returns these separately as `noActionFindings` + `summary.noAction`; the inline path classifies them here.) **`NEEDS_MANUAL_CONFIRMATION` findings are NOT discarded** — list them in `## Issues & Flags` and surface them to the user via `AskUserQuestion` (treat as VERIFIED, treat as FALSE_POSITIVE, or hand off) before merge. **When AUTONOMOUS, apply § "AUTONOMOUS RESOLUTION RULE" (SKILL.md): category=blocker; recommended=treat NEEDS_MANUAL_CONFIRMATION as VERIFIED (never as a false positive), else materialize a follow-up card.** Only `FALSE_POSITIVE` are dropped.
338
348
 
339
349
  ### Step F.5 — Apply fixes and final build
@@ -403,6 +413,19 @@ that is a **gate violation**: log it as
403
413
  4. Emit the **minimal end-of-batch result block** (per § "orchestrator is near-silent" in the core SKILL.md — this is NOT a per-phase report). ONE line per card and nothing more of the process telemetry:
404
414
  - `<CARD-ID>: merged @<sha> | failed: <one clause> | deferred → <followup-id>`
405
415
 
416
+ **Report verdict (v5.6.0 — HARD headline rule, FEAT-0068 post-mortem).** The FIRST line of the
417
+ result block is a verdict, and it is computed, not felt:
418
+ - `⚠️ PARTIAL — obiettivo epic NON raggiunto: <one clause naming the gap>` whenever ANY of:
419
+ a HIGH/BLOCKER residual or follow-up carrying substantive scope work exists; the parent epic
420
+ was left open (or reopened) by Phase 6b 5e; a closure-invalidating finding stands; any epic
421
+ AC is still `[ ]`. Name the gap and its follow-up card in the first three lines.
422
+ - `✅ COMPLETO` is RESERVED for: every card merged, no HIGH+ residual, epic (when present)
423
+ closed with ACs verified.
424
+ A run that ships 6/6 cards but leaves the epic goal on 10% of its surface is PARTIAL — the
425
+ FEAT-0068 report headlined "✅ batch completo" over a disclosed-but-buried HIGH gap, and the
426
+ user discovered it only by asking. "Nulla perso" (tracking) is never a substitute for "non
427
+ finito" (truth): a follow-up that holds the epic's remaining work is REMAINING WORK.
428
+
406
429
  Then, **only if non-empty**, the **actionable residue**: issues needing user attention (anything unresolved, partially wired, or flagged — terse, with `path:line` / `CARD-ID` exact). All process telemetry — files changed, test/build/lint status, fix cycles, review-finding counts, UX/QA verdicts, commit + merge hashes, card-status reconciliation, worktree cleanup, knowledge-corpus sync — is **NOT rendered**: it lives in the tracker and `$METRICS/skill-runs.jsonl`. (The Next Steps & Launch Command in 4b below and any Production Readiness manual steps ARE part of the actionable residue — keep them.)
407
430
 
408
431
  4b. **Next Steps & Launch Command** (MANDATORY section — always present):
@@ -11,10 +11,10 @@
11
11
  2b. **Claim** — only now set the card status to `IN_PROGRESS` (in the backlog YAML / tracker) and assign yourself. This is a state write, not a visibility emission — do not also spend a TaskUpdate / Progress-Bar turn (§ "State surface — the tracker only").
12
12
  2c. **Trivial-card classification (BLOCKING gate for steps 3–4)** — evaluate `IS_TRIVIAL(card)` per § "Trivial-card fast-lane". Note: condition 3 (non-source diff) cannot be fully evaluated until the coder has produced the diff, so at this point compute the **provisional** trivial flag from conditions 1+2 only (`review_profile == skip` AND no Step-A trigger sourced from the card YAML text + `files_likely_touched` extensions — if EVERY path in `files_likely_touched` is non-source, condition 3 is provisionally satisfied). If provisionally trivial → **SKIP steps 3, 3a, and 4** (architecture grounding); log `trivial: architecture grounding skipped (review_profile=skip + non-source files_likely_touched + 0 triggers)` and jump to Phase 2. Re-confirm `IS_TRIVIAL` on the ACTUAL committed diff at the review gates (Phase 2.55/3.5/3.7); if the coder unexpectedly touched a source file, the guard flips the card back onto the normal review path there. If NOT provisionally trivial → run steps 3, 3a, 4 as normal.
13
13
  2d. **Light-lane inline grounding (since v5.4.0 — BLOCKING gate for steps 3–4, after 2c)** — when the card is NOT provisionally trivial AND `review_profile == light`: **SKIP step 3 (the dedicated codebase-architect spawn) and step 4** — the OWNER agent grounds INLINE instead, per **AGENTS.md § Change Tiers, orchestrated-pipeline exception** (a light card is ≤3 files with no logic-risk signal by Rule C; a dedicated retriever at that scale costs more than the exploration it isolates). The understanding obligation is UNCHANGED — only the delegation is waived: the owner briefing (step 6/7) MUST carry the line "Ground INLINE before coding: read the files in scope + their immediate integration points; scoped exploration only". Step 3a still runs (write Active Code Context from the card YAML, noting `grounding: inline-light`). Log `phase1: INLINE-LIGHT (review_profile=light — no retriever spawn, owner grounds inline)`. `balanced`/`deep` cards run steps 3, 3a, 4 as written — this lane never applies to them.
14
- 3. **(skip when provisionally trivial — see 2c — or on the light lane — see 2d)** Invoke the **codebase-architect** agent (MUST per AGENTS.md) to understand the relevant codebase area, existing patterns, and architecture before any implementation. When `features.has_lsp_layer: true`, the architect uses LSP find-references for identifier-shaped lookups — this needs NO handoff from the orchestrator: the architect reads `features.has_lsp_layer` from `baldart.config.yml` directly (the flag is ambient) per `agents/code-search-protocol.md`. Likewise, when `features.has_code_graph: true`, the architect uses the Graphify code graph for structural/relational lookups (ambient flag) per `agents/code-graph-protocol.md`. The orchestrator does NOT propagate either flag. (Earlier doc versions numbered this step 4; the step that read project-status BEFORE the architect was removed because it persisted pre-analysis context — see step 3a.) **Pass `OUTPUT=terse` in the architect prompt (since v4.49.0)** — this is grounding for the coder/reviewer, not an explanation, so the architect returns its structured contract (reuse table + canonical evidence + `path:line — symbol` rows + `totals:`) without narrative prose. **Pass the analysis profile too (since v4.94.0)**: `PROFILE=ui` when the card has a non-empty `links.design` OR `links.design_src` (the same mockup bit that drives step 6a), else `PROFILE=feature` — contract and per-profile scoping in `agents/analysis-profiles.md` (its § 3 caller routing table is normative). The profile is deterministic from the card YAML; never leave the architect to infer it. The verbatim baseline persisted at step 5b keeps all the substance a lean `/codexreview` needs (paths, signatures, patterns, high-risk paths) while staying small in the orchestrator's context and on disk. **Baseline content contract (MANDATORY — since v4.56.0):** the architect prompt is grounding INPUT only — pass the card scope, `files_likely_touched`, and the question to map. NEVER put line-level fix directives ("remove lines 90-91", "Fix: use X"), the words "implement/fix/apply", or acceptance-criteria framed as a task into the architect prompt. The architect produces a read-only MAP (file/symbol locations, patterns, type signatures, integration points, high-risk paths); it does NOT implement and has no `Task`/Agent tool to delegate (see `codebase-architect.md` § Role boundary). The coder is a separate spawn the orchestrator owns.
14
+ 3. **(skip when provisionally trivial — see 2c — or on the light lane — see 2d)** Invoke the **codebase-architect** agent (MUST per AGENTS.md) to understand the relevant codebase area, existing patterns, and architecture before any implementation. When `features.has_lsp_layer: true`, the architect uses LSP find-references for identifier-shaped lookups — this needs NO handoff from the orchestrator: the architect reads `features.has_lsp_layer` from `baldart.config.yml` directly (the flag is ambient) per `agents/code-search-protocol.md`. Likewise, when `features.has_code_graph: true`, the architect uses the Graphify code graph for structural/relational lookups (ambient flag) per `agents/code-graph-protocol.md`. The orchestrator does NOT propagate either flag. (Earlier doc versions numbered this step 4; the step that read project-status BEFORE the architect was removed because it persisted pre-analysis context — see step 3a.) **Pass `OUTPUT=terse` in the architect prompt (since v4.49.0)** — this is grounding for the coder/reviewer, not an explanation, so the architect returns its structured contract (reuse table + canonical evidence + `path:line — symbol` rows + `totals:`) without narrative prose. **Pass the analysis profile too (since v4.94.0)**: `PROFILE=ui` when the card's `HAS_MOCKUP` bit is true (the same semantic mockup test that drives step 6a — v5.6.0: a bare `links.design` pointer on a docs/meta card does NOT make it a UI card), else `PROFILE=feature` — contract and per-profile scoping in `agents/analysis-profiles.md` (its § 3 caller routing table is normative). The profile is deterministic from the card YAML; never leave the architect to infer it. The verbatim baseline persisted at step 5b keeps all the substance a lean `/codexreview` needs (paths, signatures, patterns, high-risk paths) while staying small in the orchestrator's context and on disk. **Baseline content contract (MANDATORY — since v4.56.0):** the architect prompt is grounding INPUT only — pass the card scope, `files_likely_touched`, and the question to map. NEVER put line-level fix directives ("remove lines 90-91", "Fix: use X"), the words "implement/fix/apply", or acceptance-criteria framed as a task into the architect prompt. The architect produces a read-only MAP (file/symbol locations, patterns, type signatures, integration points, high-risk paths); it does NOT implement and has no `Task`/Agent tool to delegate (see `codebase-architect.md` § Role boundary). The coder is a separate spawn the orchestrator owns.
15
15
  3a. Update `${paths.references_dir}/project-status.md` Active Code Context (skip when the file does not exist in the project) — do this AFTER the codebase-architect run (step 3) so the "Active Code Context" reflects the architect's findings (which files are actually in scope), not just the card YAML's `files_likely_touched`. Writing it before the architect run would persist pre-analysis claims that downstream agents (e.g. a parallel card) would then read as truth.
16
16
  4. **Plan-auditor grounding check** — but first, a **provenance skip (since v4.7.0 — mirror of Step 3d)**: the `/prd` Step 6.6 holistic audit already ran a per-card grounding pass when the card was authored. Re-grounding here is only worth a plan-auditor spawn if something changed since. **This skip is the NORMAL path for a card implemented right after `/prd`, not an occasional optimization** — `/prd` now guarantees the `metadata.holistic_audit` stamp lands on every audited card (deterministic backstop at `validation-phase.md` Step 7 item 5b, since the v4.88.2 provenance-reliability fix), so P1 below holds whenever the batch is freshly authored. Skip the plan-auditor when **BOTH** hold:
17
- - **P1 — provenance present**: the card has `metadata.holistic_audit` with non-empty `audited_at`, `audited_commit`, `audited_set`.
17
+ - **P1 — provenance present AND evidence-backed (v5.6.0)**: the card has `metadata.holistic_audit` with non-empty `audited_at`, `audited_commit`, `audited_set`, **and a non-empty `agents_run` list** (the v5.6.0 evidence field the stamper writes only when it received a real audit report). A stamp with `status: skipped` or WITHOUT `agents_run` is treated as **ABSENT** — the FEAT-0068 `/prd` session stamped all 6 cards while the Step 6.2–6.6 audit team never spawned (phantom stamp), and P1∧P2 would have suppressed the plan-auditor on a false premise. Pre-v5.6.0 stamps (no `agents_run` field at all) therefore fall through to RUN the plan-auditor — deliberate fail-safe, not a bug.
18
18
  - **P2 — no drift on this card's claimed paths** since the audit baseline. With `$AC` = `metadata.holistic_audit.audited_commit`, `$MAIN`/`$TRUNK` from the tracker, and this card's `files_likely_touched`/claimed paths (`$CARD_PATHS`):
19
19
  ```bash
20
20
  # empty AC, AC not in history, or any git error → treat as drift (do NOT skip)
@@ -57,7 +57,17 @@
57
57
  If card is an epic (id ends "-00" OR filename ends "-epic.yml" OR review_profile == "skip"
58
58
  with no requirements): SKIP — log "[ROUTER] card <ID> is an epic (tracker) — skipped,
59
59
  not implemented" and move to the next card.
60
- If card has links.design OR links.design_src (a mockup exists):
60
+ # v5.6.0 (FEAT-0068): "has a mockup" is SEMANTIC, not a bare-field test. The /prd epic
61
+ # explainer html stamped on 5/6 cards clamped a docs+ADR card to ui-expert (6.55M, the
62
+ # run's costliest agent) and put a meta-only card through the fidelity lane.
63
+ HAS_MOCKUP := links.design_src is non-empty
64
+ OR ( links.design is non-empty
65
+ AND ( component_bindings is non-empty
66
+ OR files_likely_touched intersects component/style/UI source paths ) )
67
+ # A bare links.design on a docs/test/meta-only card is a POINTER to the epic's design
68
+ # explainer, not a build target → route by owner_agent (step 6b). Log the demotion:
69
+ # "[ROUTER] card <ID> carries links.design but builds no mocked UI → owner_agent router".
70
+ If HAS_MOCKUP:
61
71
  IF the /ui-implement skill is available:
62
72
  DELEGATE this card's mockup→code implement + fidelity verify to /ui-implement
63
73
  in programmatic mode (contract: ui-implement/references/integration.md).
@@ -82,7 +92,7 @@
82
92
  ```
83
93
 
84
94
  `owner_agent` is still honored for **review/ownership semantics** (AC-closure ownership, `security-reviewer` relevance, the mixed-card review cluster) — it simply no longer decides the BUILD agent when a mockup is present.
85
- 6b. **Agent dispatch (router — cards WITHOUT a mockup only).** Reached only when step 6a did not fire (the card has no `links.design`/`links.design_src`). Read `owner_agent` from the card YAML and resolve the spawn target per the table in § Agent Routing above:
95
+ 6b. **Agent dispatch (router — cards WITHOUT a mockup only).** Reached only when step 6a did not fire (`HAS_MOCKUP` false no mockup, or a bare `links.design` pointer on a card that builds no mocked UI). Read `owner_agent` from the card YAML and resolve the spawn target per the table in § Agent Routing above:
86
96
 
87
97
  ```
88
98
  Let raw = card.owner_agent (string, may be missing).
@@ -93,24 +93,45 @@ The most common failure mode is leaving cards IN_PROGRESS after merge. This crea
93
93
 
94
94
  For each **distinct parent epic** referenced by the batch — the set of `group.parent`
95
95
  values across the batch cards, PLUS any card in the batch that is itself an epic
96
- (`group.is_epic: true`) — do:
97
- a. Read the epic parent card `${paths.backlog_dir}/<EPIC-ID>.yml`. If `status: DONE`
98
- already skip (idempotent).
99
- b. Check that **every** child is DONE (not just the batch's subset children
100
- implemented in earlier sessions count too):
96
+ (`group.is_epic: true`) — close ONLY when the FULL predicate (a)–(d) holds
97
+ (v5.6.0, FEAT-0068 post-mortem: the old children-count-only predicate closed an epic whose
98
+ goal was unmet on ~90% of screens — the count was true and the DoD was false):
99
+ a. Read the epic parent card `${paths.backlog_dir}/<EPIC-ID>.yml`.
100
+ - `status: DONE` already **and** the checks below still hold → skip (idempotent).
101
+ - `status: DONE` already **but** (b)/(c)/(d) now FAIL (an open follow-up child appeared, a
102
+ closure-invalidating finding stands) → **REOPEN** it: set `status: IN_PROGRESS`, annotate
103
+ `completed_date`, add note `"REOPENED — closed against open scope (v5.6.0 guard)"`, fold
104
+ into the reconciliation commit. The ratchet is two-way: a false DONE never survives a
105
+ reconciliation pass.
106
+ b. Check that **every** child is DONE (not just the batch's subset — children implemented in
107
+ earlier sessions count too, and **follow-up cards count as children**: a
108
+ `<card>-followup-*.yml` carrying `parent: <EPIC-ID>` is an OPEN child until implemented):
101
109
  ```bash
102
110
  # Lists children of <EPIC-ID> that are NOT yet DONE. Empty output ⇒ all DONE.
111
+ # No ^ anchor — the schema nests `parent:` indented under `group:` (the v4.99.0
112
+ # commit-time gate anchored it and was a silent no-op; do not re-introduce that).
103
113
  grep -l "parent: <EPIC-ID>" ${paths.backlog_dir}/*.yml | xargs grep -L "status: DONE"
104
114
  ```
105
- c. **Only if** that command prints nothing (all children DONE) force-update the epic
106
- parent: set `status: DONE`, add `completed_date: <today>`, add note
107
- `"Marked DONE by epic-closure gate (Phase 6b) all children DONE"`. Stage it and
108
- **fold into the SAME reconciliation commit** as step 5 (or, if step 5 didn't run
109
- because all children were already DONE, make a dedicated
110
- `[BATCH] Close epic <EPIC-ID> all children DONE` commit, honoring the same
111
- COMMIT_LOCK / doc-freshness precautions as step 5a–5d).
112
- d. **If any child is still open** leave the epic untouched: it is genuinely still in
113
- progress, and step-4b's "Card rimanenti nell'epic" table already reports the gap.
115
+ c. **No blocking open work attached to the epic**: scan the tracker's `## Issues & Flags` +
116
+ residual/follow-up ledger for this batch — any open `scope-expansion` / `unresolved` /
117
+ `ac-unmet` item on a child of this epic, or any **closure-invalidating** final-review
118
+ finding (`final-review.md` § Closure-invalidating findings) naming this epic's goal
119
+ leave the epic OPEN (a text fix to the overclaiming doc does NOT clear this).
120
+ d. **Epic ACs verified**: read the epic YAML's own `acceptance_criteria`. For each `[ ]`
121
+ item, verify it with concrete evidence — run its `ac_verification` oracle when present,
122
+ else a targeted check and flip to `[x]` ONLY with evidence recorded in the tracker. An
123
+ AC you cannot verify stays `[ ]` and the epic stays OPEN (report which AC and why).
124
+ Never close an epic whose own checklist still has `[ ]` items.
125
+ e. **Only if (b)+(c)+(d) all pass** → update the epic parent: set `status: DONE`, add
126
+ `completed_date: <today>`, add note `"Marked DONE by epic-closure gate (Phase 6b) —
127
+ children+follow-ups DONE, ACs verified"`. Stage it and **fold into the SAME reconciliation
128
+ commit** as step 5 (or, if step 5 didn't run because all children were already DONE, make a
129
+ dedicated `[BATCH] Close epic <EPIC-ID>` commit, honoring the same COMMIT_LOCK /
130
+ doc-freshness precautions as step 5a–5d).
131
+ f. **If any check fails** → leave the epic untouched (or REOPEN per step a) and report the
132
+ concrete reason: step-4b's "Card rimanenti nell'epic" table + the final report's epic-goal
133
+ line must show it — the batch verdict is **PARTIAL**, never ✅, while the epic is open
134
+ (`final-review.md` § Report verdict).
114
135
  6. **Update tracker** with reconciliation results:
115
136
  ```
116
137
  ## Phase 6b — Status Reconciliation
@@ -2,6 +2,42 @@
2
2
 
3
3
  Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
4
4
 
5
+ ## 1.8.0 — 2026-07-04 (FEAT-0068 post-mortem: integrità dell'esito + igiene git)
6
+
7
+ - **`new2.js`**: step 4b (chiusura epic) RIMOSSO dal commit agent — la chiusura vive
8
+ SOLO nella merge phase 5e col predicato v5.6.0 (blocklist JS `closureBlockedEpics`
9
+ da residual/follow-up/finding closure-invalidating + figli/follow-up DONE + AC epic
10
+ verificati, `epicsLeftOpen` con motivi); **guardia post-merge** gemella di F-029
11
+ (`epic-closure-guard` VIOLATION + residual `false-epic-closure` HIGH → la skill
12
+ riapre); detection **closure-invalidating** sui finding della final review.
13
+ - **`buildReport`**: verdetto calcolato `⚠️ PARTIAL`/`✅ COMPLETO` come PRIMA sezione,
14
+ sezione `## Epic goal` per-epic, `severity` sui residui (`residualSeverity`),
15
+ "nulla perso" consentito solo senza HIGH.
16
+ - **Commit agent**: clausola WORKTREE ISOLATION estesa ai TOOL (Read/Edit/Write path
17
+ assoluti — il cwd HARD RULE v5.3.0 copriva solo Bash), path del card YAML del
18
+ worktree passato esplicito, sibling/epic READ-ONLY, evidenza porcelain
19
+ before/after sul main + **isolation-guard JS** (main sporcato = VIOLATION +
20
+ residual HIGH). Era: 79 turni/4.30M di detour + stash dangling.
21
+ - **Merge**: `merge-worktree.sh --safety-sweep allowlist:` (solo bookkeeping del
22
+ batch; codice non gated mai spazzato nel trunk — F-030 per costruzione); divieto
23
+ esplicito di `git stash` ovunque (ff-pull bloccato = leave+report).
24
+ - **`new2-resolve.js`**: esito **`refuted`** ratificato avversarialmente (premessa
25
+ falsa = zero edit — era: 4.73M per "fixare" un blocker falso disattivando la suite
26
+ visual-regression) + `terminalReason: verification-posture-change` (un fix che
27
+ skippa/flag-gata una verifica mandata da un AC è FORBIDDEN).
28
+ - **`new-final-review.js`**: relay Codex in **foreground `--wait`** (una sola Bash,
29
+ timeout 600000) — l'enforcement structured-output forzava un TIMEOUT a 83s/600s e
30
+ il processo moriva col relay → fallback freddo da 3.68M; reap dell'orfano scoped
31
+ al worktree su timeout reale.
32
+ - **Routing**: `hasMockup` semantico nel cardGraph (BUILDS the mocked UI) +
33
+ `parentEpic` per blocklist/report.
34
+ - **SKILL.md**: Step 2 predicato di chiusura completo + **ramo REOPEN** (ratchet
35
+ bidirezionale); Step 4 presenta il verdetto verbatim (mai ri-headline un PARTIAL);
36
+ **Step 4b audit igiene git** (commit non-card ispezionati con `git show --stat` —
37
+ mai attribuire provenienza senza check; stash del batch riconciliati, mai "non
38
+ miei"; residual `main-repo-contamination` ripristinati); pre-sha del trunk
39
+ registrato al kickoff.
40
+
5
41
  ## 1.7.0 — 2026-07-03 (FEAT-0067 post-mortem: migration mai deployata)
6
42
 
7
43
  - **Step 3.5 vocabulary aliases**: il Migration Gate ora riconosce, oltre a
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: new2
3
3
  effort: high
4
- version: 1.7.0
4
+ version: 1.8.0
5
5
  description: >
6
6
  Workflow-hosted batch engine for /new. Implements one or
7
7
  more backlog cards end-to-end by delegating the WHOLE batch to a background
@@ -202,7 +202,9 @@ Workflow({ name: 'new2', args: {
202
202
  ```
203
203
 
204
204
  Record the kickoff time (`ts`) before the call — Step 5 uses it to stamp the
205
- real wall-clock duration (the workflow cannot read a clock).
205
+ real wall-clock duration (the workflow cannot read a clock). Also record the
206
+ pre-batch trunk sha (`git -C $MAIN rev-parse ${trunk}`) — Step 5.4b audits every
207
+ non-card commit the batch introduced against it (v5.6.0).
206
208
 
207
209
  Capture `sessionId` the same way, in THIS skill's own context (not a subagent):
208
210
  `Bash(echo "${CLAUDE_CODE_SESSION_ID:-}")`. The workflow stamps it as
@@ -254,6 +256,10 @@ returns when the batch is done. It returns:
254
256
  step-1b HALT fields — the `/new` pre-flight back-fill (step 1b-ii) then normalizes its
255
257
  `review_profile`/`owner_agent` on first ingestion, so even the crisis stub self-heals. This
256
258
  main-repo, **disk-verified** write is the SSOT — nothing is dropped even on a non-merged batch.
259
+ **Exception (v5.6.0):** residuals of kind `false-epic-closure` and `main-repo-contamination` are
260
+ **reconciliation ACTIONS, not backlog work** — do NOT mint follow-up cards for them. The former
261
+ is handled by step 2's REOPEN branch; the latter by step 4b's hygiene audit (inspect + restore
262
+ the named main-repo paths).
257
263
  1b. **Migration Sync Tail (post-merge "branch B" — the interactive owner-gated deploy the
258
264
  autonomous batch could not run).** The workflow has returned and merged; you are the skill
259
265
  main loop, in `$MAIN`, typically on `$TRUNK` — exactly the precondition of the project's
@@ -310,11 +316,25 @@ returns when the batch is done. It returns:
310
316
  **Then re-run the epic-closure check for the cards just marked DONE.** The merge agent's
311
317
  epic-closure (Phase 6b step 5e) ran BEFORE this step, when the deferred cards were still
312
318
  NON-DONE — so an epic whose last open child was a deferred card is still TODO and nothing
313
- else will ever close it. For each distinct `group.parent` of the cards marked DONE here: if
314
- `grep -l "parent: <EPIC-ID>" ${paths.backlog_dir}/*.yml | xargs grep -L "status: DONE"`
315
- prints nothing, set the epic card `status: DONE` + `completed_date` + note
316
- `"epic-closure gate all children DONE (post-run, new2 skill)"`, folded into the SAME
317
- reconciliation commit. If any child is still open, leave the epic untouched.
319
+ else will ever close it. For each distinct `group.parent` of the cards marked DONE here,
320
+ close ONLY when the FULL v5.6.0 predicate holds (the same one the merge agent runs —
321
+ `merge-cleanup.md` § epic closure): (a) `grep -l "parent: <EPIC-ID>" ${paths.backlog_dir}/*.yml
322
+ | xargs grep -L "status: DONE"` prints nothing — **follow-up cards count as children** (step 1
323
+ just materialised them here in the main repo, so this grep now sees them); (b) no `residuals[]`
324
+ entry of a blocking class (`scope-expansion`/`unresolved`/`ac-unmet`/`out-of-ownership`) maps to
325
+ a child of this epic; (c) the epic YAML's own `acceptance_criteria` are all `[x]` — verify any
326
+ still-`[ ]` item with its `ac_verification` oracle (or a targeted check) and flip it ONLY with
327
+ evidence; an unverifiable AC leaves the epic OPEN. When all three hold, set the epic
328
+ `status: DONE` + `completed_date` + note `"epic-closure gate — children DONE + epic ACs
329
+ verified (post-run, new2 skill)"`, folded into the SAME reconciliation commit. Otherwise leave
330
+ the epic untouched and say why in the presentation.
331
+ **REOPEN branch (v5.6.0 — the ratchet is two-way now).** For every `false-epic-closure`
332
+ residual, AND for any epic found `status: DONE` on disk while the grep above still prints an
333
+ open child (follow-ups included) or a closure-invalidating finding stands: flip the epic back
334
+ to `status: IN_PROGRESS`, remove/annotate its `completed_date`, add note `"REOPENED — closed
335
+ against open scope (false DONE, v5.6.0 guard)"`, and fold into the same reconciliation commit.
336
+ An epic whose registry/ADR text says "goal not achieved" while its YAML says DONE is exactly
337
+ the FEAT-0068 contradiction this branch exists to prevent — the STATE wins, not the prose.
318
338
  3. **Resume if degraded — HARD-BOUNDED (v4.92.0).** If `degraded` is true, re-invoke
319
339
  the workflow with `Workflow({ scriptPath, resumeFromRunId })` (same `args` + the
320
340
  new `ts`). The per-card **skip-completed** guard makes the resume idempotent —
@@ -377,11 +397,31 @@ returns when the batch is done. It returns:
377
397
  (out of scope by design — the workflow is autonomous).
378
398
  - Record `escape_hatch: { eligible: N, offered: <bool>, ran_new: <bool>, followups: [...] }`
379
399
  in telemetry (step 5 below) so the A/B stays honest about when the hatch was used.
380
- 4. **Present.** Print `report` verbatim. Surface `residuals` prominently
381
- ("questi residui sono tracciati come follow-up: …") the post-run review that
382
- replaced the ~25 mid-run questions. If `degraded`, say so plainly (the run was
383
- incomplete and resumed). If the escape hatch ran `/new` (step 3b), fold its outcome
384
- into the presentation (which follow-ups were closed by `/new`).
400
+ 4. **Present.** Print `report` verbatim **including its verdict headline** (`⚠️ PARTIAL` /
401
+ `✅ COMPLETO`, v5.6.0): NEVER re-headline a PARTIAL run as a success in your own framing, and
402
+ never bury an epic-goal gap under a completion table (the FEAT-0068 report said "✅ 6/6 DONE"
403
+ over a disclosed-but-buried HIGH gap — the user discovered it only by asking). Surface
404
+ `residuals` prominently ("questi residui sono tracciati come follow-up: …") the post-run
405
+ review that replaced the ~25 mid-run questions; a HIGH residual is REMAINING WORK, present it
406
+ as such. If `degraded`, say so plainly (the run was incomplete and resumed). If the escape
407
+ hatch ran `/new` (step 3b), fold its outcome into the presentation (which follow-ups were
408
+ closed by `/new`).
409
+ 4b. **Batch git hygiene audit (v5.6.0 — never assert provenance without checking).** From the
410
+ kickoff you know the trunk's pre-batch sha (record `git -C $MAIN rev-parse ${trunk}` at Step 4
411
+ BEFORE launching the workflow). Now:
412
+ - `git -C $MAIN log --oneline <pre-sha>..HEAD` — for every commit whose subject does NOT
413
+ reference a batch card id (e.g. `[safety]`, unexpected chores), run `git show --stat <sha>`
414
+ and LIST it in the presentation with its files. Never attribute a commit to "a previous run"
415
+ without this check (the FEAT-0068 orchestrator asserted exactly that, and it was false).
416
+ A safety/sweep commit whose files are outside the batch's bookkeeping is a finding to
417
+ surface, not to normalize.
418
+ - `git -C $MAIN stash list` — any stash created during this batch (grep the batch/card ids and
419
+ the `baldart-new2-` label) MUST be reconciled NOW: inspect (`git stash show -p`), then apply
420
+ or drop with the user's standing policy — in AUTONOMOUS mode leave it but list it as a
421
+ PROMINENT pending item, never as a footnote and never "not mine to drop" (if this batch's
422
+ machinery created it, it IS yours to reconcile).
423
+ - Every `main-repo-contamination` residual: inspect the named paths in `$MAIN` and restore
424
+ them (they are the commit-agent detour's droppings), reporting what was restored.
385
425
  5. **Record truthful telemetry — reconciled against disk (F-040).** Before appending `telemetry`
386
426
  to `${metricsDir}/skill-runs.jsonl`, fill the fields the workflow could not compute and
387
427
  **reconcile the report against the real disk state** (agent `reason` strings can over-claim — a