baldart 4.2.2 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/VERSION +1 -1
- package/framework/.claude/skills/new/SKILL.md +48 -10
- package/framework/.claude/skills/prd/SKILL.md +12 -6
- package/framework/.claude/skills/prd/assets/claude-design-handoff-prompt.template.md +5 -0
- package/framework/.claude/skills/prd/assets/state-template.md +15 -5
- package/framework/.claude/skills/prd/references/audit-phase.md +31 -2
- package/framework/.claude/skills/prd/references/canvas-intake-recipe.md +113 -0
- package/framework/.claude/skills/prd/references/discovery-phase.md +216 -38
- package/framework/.claude/skills/prd/references/ui-design-phase.md +13 -5
- package/framework/templates/feature-card.template.yml +10 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,51 @@ 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.5.0] - 2026-06-03
|
|
9
|
+
|
|
10
|
+
**`/new` Team Mode non revisiona più lo stesso diff fino a tre volte: la profondità della code-review ora scala col `review_profile` della card.** Lo Step D del team mode faceva passare ogni card "full" sotto code-review tre volte — `D.2` (code-reviewer di gruppo) → `D.4b` (`/codexreview` per-card, che rispawna code-reviewer + Codex + CoVe + FP-gate) → Final Review F.3 (Codex sull'intero batch) — e ogni card "light" due volte (D.2 + un D.4b che a `light` esegue solo code-reviewer + FP, puro doppione di D.2). Un nuovo step **D.1.5** calcola UNA volta il profilo effettivo per card (floor da `review_profile` + escalation-only dal detector Step-A sul diff reale) e partiziona il gruppo in `LIGHT_CARDS` / `FULL_CARDS`. **D.2** ora esegue il code-reviewer solo sull'union dei diff `LIGHT_CARDS` (skippato del tutto se non ce ne sono); **D.4b** gira `/codexreview` solo per i `FULL_CARDS`, mentre i `LIGHT_CARDS` lo saltano (la loro review adversarial è garantita dalla Final FULL gate, sempre on). Esito: ogni card riceve **una sola** code-review per-card/gruppo proporzionale al rischio + la Final batch gate — light: D.2 + Final; full: D.4b + Final (giù da 3). **D.3b Simplify** salta sulle card `review_profile == skip` (quality-only, niente diff sostanziale). Tutti gli skip sono **gate-reason enumerati e deterministici** (driven dal campo `review_profile` autorato in PRD), non skip "per time budget": una nuova clausola "REVIEW DEPTH SCALES WITH `review_profile`" in testa alla skill lo formalizza. Eliminato anche il polling `sleep N; echo "waiting..."` ad ogni barrier (i background agent re-invocano l'orchestratore automaticamente). **MINOR** (cambia il comportamento di review di `/new` team mode senza rimuovere agenti o rompere install; nessuna chiave `baldart.config.yml` nuova — si appoggia su `review_profile` già esistente nella card).
|
|
11
|
+
|
|
12
|
+
> **Why.** Il team mode parallelizza la parte economica (scrivere il codice) e serializzava la parte costosa (la coda di review): i coder partivano in parallelo, poi tutto convergeva in un imbuto sequenziale di 11 sotto-passi con la code-review ripetuta. La de-duplicazione si appoggia su due invarianti già presenti: (1) la **Final-review FULL gate** (dal v3.37.0) gira SEMPRE Codex (+ code-reviewer fallback) sull'intero diff di batch, senza riduzioni — quindi ogni riga di ogni card light riceve comunque Codex-adversarial prima del merge; (2) l'**escalation-only** del detector Step-A (`light`→`full`, mai l'inverso) garantisce che un path rischioso emerso a implementazione promuova comunque la card a `full`. Tradeoff accettato (scelta utente "aggressivo 3→2"): per i `FULL_CARDS` il rilevamento cross-card si sposta dal per-gruppo (D.2) al per-batch (Final F.3) — stessa sicurezza di merge-gate, feedback a fine batch invece che per-gruppo. `/codexreview` NON è toccato (il suo FP-gate dipende architetturalmente da `code-reviewer`, quindi la leva è l'invocazione condizionale di D.4b, non un flag interno). Il **percorso sequenziale è invariato** (non ha un code-reviewer di gruppo, quindi il suo `/codexreview` a Phase 3.7 resta l'unica code-review per-card). Allineato al principio "data-driven sopra threshold arbitrarie": nessuna soglia nuova, si onora un campo deterministico già esistente. AC-Closure (D.3a), D.4 QA e D.4a doc restano invariati.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- **`framework/.claude/skills/new/SKILL.md`** — (a) nuova clausola in testa "REVIEW DEPTH SCALES WITH `review_profile`" che enumera il profilo come gate-reason deterministico (distinto dallo skip "per time"); la clausola "NO PHASE SKIP" cita il profilo tra le ragioni valide e ammorbidisce "never reduce" in "never reduce for any model-invented reason". (b) Nuovo **D.1.5** (Effective per-card review profile) che calcola la partizione `LIGHT_CARDS`/`FULL_CARDS` una volta sola e la logga. (c) **D.2** code-reviewer scoped a `LIGHT_CARDS` (skip se vuoto); doc-reviewer invariato su tutto il gruppo; tradeoff cross-card→Final documentato inline. (d) **D.3b** salta su `review_profile == skip`. (e) **D.4b** condizionale: `/codexreview` solo per `FULL_CARDS` (profilo sempre `full`, niente recompute), `LIGHT_CARDS` skip con reason enumerata. (f) **Step D coverage assertion** accetta gli skip profilo-driven come entry valide documentate. (g) **Step C** direttiva anti-polling (no `sleep N; echo waiting`). (h) **D.3b Simplify** ora fa **fan-out in parallelo** su tutte le card eleggibili del gruppo (analisi read-only su diff file-disgiunti → niente più coda sequenziale per-card; i fix si applicano dopo, lint/tsc una volta sola); **D.3c E2E** valuta il gate per tutte le card insieme e parallelizza le eleggibili quando colpiscono route disgiunte (fallback sequenziale su contesa del dev-server). Percorso sequenziale, `codexreview.md`, AC-Closure D.3a (resta sequenziale per via dell'`AskUserQuestion`), D.4 QA, D.4a doc, Final Review F.3: non toccati.
|
|
17
|
+
|
|
18
|
+
> **Nota sullo scope dei tagli.** La de-dup si ferma qui di proposito. La **Final Review F.3** ri-revisiona l'intero batch (Codex + doc + perf + qa-sentinel suite completa) anche dopo il lavoro per-gruppo: è ridondante su batch a gruppo singolo MA è la rete di sicurezza incondizionata su cui ora si appoggiano le card light, e il framework ha **già tentato di ridurne lo scope in v3.35.0 e l'ha rollbackata in v3.37.0** dopo un incidente — quindi resta piena. Stessa logica per il doppio run della suite di test (D.4 per-gruppo + Final). Questi NON sono toccati.
|
|
19
|
+
|
|
20
|
+
## [4.4.0] - 2026-06-03
|
|
21
|
+
|
|
22
|
+
**`/prd` mockup intake ora risolve le schermate di una feature in modo *strutturale* da un bundle Claude Design, invece di tirare a indovinare con lo scoring per keyword.** L'export di Claude Design non è controllabile dall'utente: si scarica solo il bundle intero del workspace multi-feature. Finora Step 1.6.4b gestiva il bundle `.zip` con un solo scoring di rilevanza su filename/`<title>` (pieno di `[CALIBRATION-NEEDED]`), e la sola modalità d'ingresso era l'archivio `.zip`. Ora un **passaggio strutturale** (Step b2) sfrutta i segnali nativi che il bundle già porta: **un canvas `*.html` per feature** (anchor sul titolo feature), **slot `id="s<N>"` = screen-ID** del § 3 del prompt di handoff (`s31` ↔ schermata `3.1`), e il manifest nativo **`.design-canvas.state.json`** (arricchimento label, mai autorità). Le schermate che vivono solo come artboard nel canvas (non esportate come PNG) vengono **renderizzate deterministicamente** via http-server + Playwright (nuova `canvas-intake-recipe.md`), risolvendo il CORS che blocca Babel su `file://`. Il trigger è allargato anche alle **cartelle già estratte** (non solo `.zip`). A valle, la routing Hybrid di Step 3 passa da fuzzy substring match a **exact join** su `slot_id`. Lo scoring per keyword resta come **fallback** (bundle generico o canvas ambiguo) e il **gate di conferma STOP umano è invariato**. **MINOR** (nuovo reference `canvas-intake-recipe.md` + nuovi campi di state-schema `mockups.archive.bundle_kind`/`canvas_ref`/`discarded[]`, `mockups.canvas_render`, `screens_in_scope[].slot_id`; trigger allargato; NON una chiave `baldart.config.yml` → nessuna cascade di schema-propagation).
|
|
23
|
+
|
|
24
|
+
> **Why.** Il mapping 1-a-1 tra schermate prodotte da Claude Design e schermate del PRD non va *imposto* nell'handoff (l'export è monolitico e fuori controllo): **esiste già** nella struttura del bundle, semplicemente il framework non la leggeva. Spostare il peso dall'handoff (dove non si può fare nulla) all'intake (dove i segnali sono tutti presenti) elimina l'archeologia manuale — CORS, http-server e Playwright re-improvvisati a ogni rientro — e la sostituisce con un resolver ripetibile. Contratto fail-safe in ogni direzione: firma assente / slot non-`s<N>` / manifest parziale / feature su 2 canvas / Playwright assente / porta occupata ricadono tutti sul comportamento attuale (scoring + STOP) — **il PRD non aborta mai sull'intake**. Allineato al principio "data-driven sopra threshold arbitrarie": lo scoring `[CALIBRATION-NEEDED]` diventa il ripiego, non la prima linea.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **`framework/.claude/skills/prd/references/canvas-intake-recipe.md`** — ricetta archiviata per renderizzare gli artboard canvas-only di un bundle Claude Design in PNG: preflight (no auto-install), http-server con port-loop 8080→8090, render loop Playwright su `[data-dc-section]`/`[data-dc-slot]` (attributi runtime, non statici), dedup vs pre-render del designer, tabella di degradazione. Invocata da Step 1.6.4b/A5, mai standalone.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **`framework/.claude/skills/prd/references/discovery-phase.md`** — Step 1.6.4b: (a) Step a allargato per accettare una **directory** oltre al `.zip` (`mockups.archive.kind: zip | directory`, convergono su `root`); (b) nuovo **Step b2 — Claude-Design structural resolution** (A0 signature gate → A1 canvas anchor con `AskUserQuestion` su ambiguità → A2 estrazione slot `id="s<N>"` → screen-ID → A3 read manifest enrich-only → A4 sidecar jsx/PNG → A5 render via recipe → A6 discard-and-log) inserito tra enumerate e score; (c) Step c demoto a **fallback** esplicito; (d) Step d con variante tabella *screen-centric*; (e) Step e materializza anche jsx-deps + asset condivisi + PNG renderizzati e scrive `screens_in_scope[]` con `slot_id`/`screen_id`/`canvas_ref`. Appendice schema estesa coerentemente.
|
|
33
|
+
- **`framework/.claude/skills/prd/references/ui-design-phase.md`** — routing Hybrid (Step 2 della per-screen table): **exact-join-first** su `slot_id`/`screen_id` quando presenti (no STOP), fuzzy substring match conservato come ramo `else` con il suo STOP-and-ask invariato.
|
|
34
|
+
- **`framework/.claude/skills/prd/SKILL.md`** — HARD RULE 16: lead-in allargato a "`.zip` OPPURE cartella estratta di un bundle Claude Design (firma `.design-canvas.state.json` e/o `*.html` con slot `id="s<N>"`)" + frase su passaggio strutturale, scoring come fallback, STOP invariato, no-abort.
|
|
35
|
+
- **`framework/.claude/skills/prd/assets/state-template.md`** — `mockups.archive` estesa con `bundle_kind`/`canvas_ref`/`canvas_candidates[]`/`discarded[]`, nuovo blocco `mockups.canvas_render`, `kind` ora `zip | directory`; `screens_in_scope[]` con `slot_id`/`screen_id`/`canvas_ref`.
|
|
36
|
+
- **`framework/.claude/skills/prd/assets/claude-design-handoff-prompt.template.md`** — nudge minimo a fine § 3 (titola il canvas col nome feature; numera gli artboard coerenti con gli screen-ID): unica richiesta realistica dato che l'export non è controllabile.
|
|
37
|
+
|
|
38
|
+
## [4.3.0] - 2026-06-03
|
|
39
|
+
|
|
40
|
+
**`/new` skips its pre-flight cross-card Codex check when `/prd` already audited the same batch jointly and nothing drifted since.** The `/prd` Step 6.6 holistic audit (4-agent team + Codex adversarial pass, 6.6d) already reviews the full card set for `files_likely_touched` conflicts, dependency shadows, implicit ordering, and shared-state mutation — the *exact* lens of `/new` Step 3d's cross-card check. Running Step 3d unconditionally re-paid that Codex call on every multi-card batch, even when implementing the freshly-audited PRD set with zero intervening change. Now the audit phase **stamps provenance** on each card (`metadata.holistic_audit`: `audited_at` + `audited_commit` + `audited_set`), and `/new` Step 3d evaluates a four-condition skip: provenance present (S1), batch jointly audited (S2), batch ⊆ audited set (S3), and no commit touched the batch's claimed paths since the audit baseline (S4). All four hold → SKIP (logged, non-decision); any fails → RUN as before. **MINOR** (new card-template field + new conditional capability; NOT a `baldart.config.yml` key — `metadata.holistic_audit` is a backlog-card field written by the audit phase).
|
|
41
|
+
|
|
42
|
+
> **Why.** The two checks are not "different lenses" — Step 3d's `FILE_CONFLICT / IMPLICIT_DEP / ORDER_RISK / STATE_MUTATION` questions map 1:1 onto 6.6d's `attack_surface`, run over the same card set + adjacent cards. The only residual value of re-running at implementation time is the two cases the authoring-time audit *cannot* have covered: (a) the `/new` batch is not the audited set (a subset, or cards stitched from separate PRD runs), or (b) the codebase drifted on the claimed paths between audit and implementation. The gate isolates exactly those two cases via provenance + a `git log <audited_commit>..<trunk> -- <claimed_paths>` drift probe, and **degrades safely in every direction** — missing/partial provenance, mismatched sets, empty/unreachable commit, or any git error all fall through to RUN, never to a silent skip. Aligns with the standing "data-driven over arbitrary thresholds" and "no silent confirmation gates for non-decisions" principles: the skip is deterministic and auditable (the RUN/SKIP reason is logged in the tracker), not a heuristic.
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- **`framework/templates/feature-card.template.yml`** — documents the optional `metadata.holistic_audit` block (`audited_at` / `audited_commit` / `audited_set`), written by the audit phase (not at authoring), consumed by `/new` Step 3d.
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
|
|
50
|
+
- **`framework/.claude/skills/prd/references/audit-phase.md`** — Step 6.9 (Apply Findings to Cards) adds a "Holistic-audit provenance stamp" subsection: capture three run-level values once (`audited_at` = UTC now, `audited_commit` = `git merge-base HEAD <trunk>` fork point, `audited_set` = sorted jointly-audited card IDs) and write `metadata.holistic_audit` on each card. New per-card workflow step (was 5 steps, now 6). Fail-safe contract: the stamp is an optimization hint, never a gate — never blocks the PRD if it can't be written.
|
|
51
|
+
- **`framework/.claude/skills/new/SKILL.md`** — Step 3d replaces the flat "Skip if batch has only 1 card" with a provenance-aware four-condition skip decision (S1 provenance present, S2 jointly audited, S3 batch ⊆ audited set, S4 no drift on claimed paths via `git log <audited_commit>..<trunk> -- <paths>`). SKIP logs `SKIPPED (provenance) — …` and proceeds with the file-ownership map unchanged; otherwise RUN with the reason recorded. Tracker `## Cross-Card Conflicts (Codex)` placeholder updated to note the possible skip. Every ambiguity falls through to RUN.
|
|
52
|
+
|
|
8
53
|
## [4.2.2] - 2026-06-03
|
|
9
54
|
|
|
10
55
|
**`/new` Phase 0 stops blocking on framework-owned noise: telemetry-only dirty trees and `baldart update`/subtree commits are auto-ignored.** Every batch tripped its own Phase 0 gates: (1) the dirty-tree gate fired on `${paths.metrics}/archive/batch-tracker-*.md` + `skill-runs.jsonl` + `sessions/` — telemetry that Phase 8 writes *after* the merge and never commits, so the next batch always saw a "dirty" tree and forced a stash/commit decision; (2) the divergence gate fired on `.framework/` subtree commits + the `[CHORE] baldart update …` chore, treating expected framework-management divergence as the FEAT-0006 orphan-commit pattern and forcing a push/cherry-pick decision. Both gates now **partition the working tree / the ahead-commits before prompting** and surface only genuine in-progress user work. The FEAT-0006 protection is intact — real orphan commits and real uncommitted work still gate. **PATCH** (bugfix to an existing gate's classification; no new agent/skill/template, no `baldart.config.yml` key — `paths.metrics` already exists).
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.5.0
|
|
@@ -13,7 +13,9 @@ description: >
|
|
|
13
13
|
|
|
14
14
|
> **SCOPE CLOSURE DISCIPLINE (BLOCKING)**: This orchestrator NEVER unilaterally defers an acceptance criterion. Silent deferral via `implementation_notes`, commit message, or final recap is a protocol violation per `framework/agents/workflows.md § Scope Closure Discipline`. Under context-window pressure, time pressure, or unforeseen complexity, the correct action is to STOP and route the decision back to the user via `AskUserQuestion` — Auto Mode's "bias toward proceeding" applies to routine decisions, NOT to scope reduction. **Phase 2.5b (AC-Closure Gate)** enforces this at the per-card level before commit and is non-skippable.
|
|
15
15
|
|
|
16
|
-
> **NO PHASE SKIP FOR PERCEIVED TIME, EVER (BLOCKING)**: MANDATORY phases are non-negotiable in BOTH sequential mode and team mode. The phrases `time budget`, `context budget`, `context pressure as override`, `for time`, `to save tokens`, `to save iterations`, or any model-invented constraint are NOT valid skip reasons. The only valid skip reasons are those explicitly enumerated in each phase's Gate table (e.g. `features.has_e2e_review: false`, backend-only diff, card type `docs`/`chore`/`config`). Documenting skipped phases at the end of a batch as `"saltati per time budget"` is a protocol violation per the FEAT-0006 incident — treat any such phrase in your own output as a self-correction trigger: stop, surface the situation via `AskUserQuestion`, and resume only after explicit user direction. Auto Mode does NOT override this. Team-mode orchestrators (L0/L1) propagate every MANDATORY phase per-card and never aggregate, reduce, or pre-filter the per-card pipeline
|
|
16
|
+
> **NO PHASE SKIP FOR PERCEIVED TIME, EVER (BLOCKING)**: MANDATORY phases are non-negotiable in BOTH sequential mode and team mode. The phrases `time budget`, `context budget`, `context pressure as override`, `for time`, `to save tokens`, `to save iterations`, or any model-invented constraint are NOT valid skip reasons. The only valid skip reasons are those explicitly enumerated in each phase's Gate table (e.g. `features.has_e2e_review: false`, backend-only diff, card type `docs`/`chore`/`config`, **and the card's `review_profile`-driven review depth — see next paragraph**). Documenting skipped phases at the end of a batch as `"saltati per time budget"` is a protocol violation per the FEAT-0006 incident — treat any such phrase in your own output as a self-correction trigger: stop, surface the situation via `AskUserQuestion`, and resume only after explicit user direction. Auto Mode does NOT override this. Team-mode orchestrators (L0/L1) propagate every MANDATORY phase per-card and never aggregate, reduce, or pre-filter the per-card pipeline **for any model-invented reason**.
|
|
17
|
+
|
|
18
|
+
> **REVIEW DEPTH SCALES WITH `review_profile` (deterministic — NOT a time/token skip)**: the review *depth* of a card MAY scale with its `review_profile` (the PRD-authored field, escalation-only via the Step-A detector — `light`→`full` never the reverse), exactly as the QA tier (D.4) and Codex `light`/`full` depth (D.4b / Phase 3.7 Step C) already do. This is an **enumerated, deterministic gate reason**, the polar opposite of a `time budget` skip: it is driven by a field a human authored at PRD time, not by the model's perception of how long the batch is taking. In **team mode** this means a `light`/`skip` (0-trigger) card receives its single per-card code review at the **D.2 group pass** and defers Codex-adversarial to the unconditional **Final-review FULL gate** (so D.4b is skipped for it), while a `full` card receives its per-card code review at the **D.4b `/codexreview`** pass (so the D.2 group code-reviewer does not re-review it). Every card still gets ≥1 per-card/group code review **plus** the cross-batch Final FULL gate — coverage is preserved, redundancy is removed. The **sequential** per-card pipeline is unchanged (it has no D.2 group code-reviewer, so its Phase 3.7 `/codexreview` stays the single per-card code review). Skipping a gate WITHOUT an enumerated `review_profile`/Gate-table reason remains a protocol violation.
|
|
17
19
|
|
|
18
20
|
## Project Context
|
|
19
21
|
|
|
@@ -100,7 +102,7 @@ Trunk branch: [resolved git.trunk_branch — Phase 0 step 0 populates]
|
|
|
100
102
|
|------|--------------------------|
|
|
101
103
|
|
|
102
104
|
## Cross-Card Conflicts (Codex)
|
|
103
|
-
(pending —
|
|
105
|
+
(pending — for batches > 1 card; may be SKIPPED if the batch was already audited jointly by /prd Step 6.6 with no drift since — see Step 3d skip decision)
|
|
104
106
|
|
|
105
107
|
## Issues & Flags
|
|
106
108
|
(none yet)
|
|
@@ -276,7 +278,24 @@ Trunk branch: [resolved git.trunk_branch — Phase 0 step 0 populates]
|
|
|
276
278
|
|
|
277
279
|
> **Why**: a non-Anthropic frontier model (Codex, via `codex-companion.mjs`) reviews the full card batch for cross-card conflicts that per-card plan-auditor checks cannot detect (each plan-auditor sees only one card).
|
|
278
280
|
|
|
279
|
-
**Skip
|
|
281
|
+
**Skip decision (provenance-aware — since v4.3.0).** The `/prd` Step 6.6 holistic audit (4-agent team + Codex adversarial pass) ALREADY ran this exact cross-card analysis over the cards when they were authored — its Codex prompt covers `files_likely_touched` conflicts, dependency shadows, implicit ordering, and shared-state mutation across the whole audited set. Re-running here is only worth the Codex call when **something changed between that audit and now**. Compute the skip before launching:
|
|
282
|
+
|
|
283
|
+
1. **Always skip** if the batch has only 1 card (no cross-card conflicts possible).
|
|
284
|
+
2. Otherwise, read `metadata.holistic_audit` from every card in the batch and evaluate the four SKIP conditions — **all must hold**:
|
|
285
|
+
- **S1 — provenance present**: every batch card has `metadata.holistic_audit` with non-empty `audited_at`, `audited_commit`, and `audited_set`. (Missing/partial on any card ⇒ condition fails.)
|
|
286
|
+
- **S2 — jointly audited**: all batch cards carry the **same** `audited_set` value (they were reviewed together, not stitched from separate PRD runs).
|
|
287
|
+
- **S3 — batch ⊆ audited set**: every batch card ID is present in that shared `audited_set`.
|
|
288
|
+
- **S4 — no drift on the claimed paths**: no commit touched any path the batch claims, between the audit baseline and the trunk this run grounds on. Using the shared `audited_commit` (`$AC`) and the batch's union of claimed/touched paths from the file-ownership map (step 3b, `$BATCH_PATHS`):
|
|
289
|
+
```bash
|
|
290
|
+
# empty AC, or AC not in history → treat as drift (do NOT skip)
|
|
291
|
+
DRIFT="$(git -C "$MAIN" log --oneline "$AC".."$TRUNK" -- $BATCH_PATHS 2>/dev/null || echo "DRIFT")"
|
|
292
|
+
```
|
|
293
|
+
S4 holds **iff** `$AC` is non-empty AND the command succeeds AND `$DRIFT` is empty.
|
|
294
|
+
3. **If S1–S4 all hold → SKIP the Codex cross-card check.** Log in the tracker under `## Cross-Card Conflicts (Codex)`:
|
|
295
|
+
`SKIPPED (provenance) — batch ⊆ holistic_audit set <audited_set>, no drift since <AC short sha> (audited <audited_at>)`. Then proceed to step 4 with the file-ownership map unchanged. Do NOT present anything to the user — this is a non-decision.
|
|
296
|
+
4. **Otherwise → RUN the check** (launch below). Record WHY in the same tracker section so the choice is auditable: `RUN — <reason>` where reason is one of `no/partial provenance` (S1) · `batch spans multiple audit sets` (S2) · `batch not a subset of audited set` (S3) · `drift on <first drifted path>` (S4).
|
|
297
|
+
|
|
298
|
+
`$MAIN` and `$TRUNK` are the same variables resolved in Phase 0. This gate degrades safely in every direction: any missing signal, ambiguity, or git error falls through to RUN — never to a silent skip.
|
|
280
299
|
|
|
281
300
|
Launch via `Bash` with `run_in_background: true` and `timeout: 300000`:
|
|
282
301
|
|
|
@@ -2339,6 +2358,8 @@ Agent tool call:
|
|
|
2339
2358
|
|
|
2340
2359
|
The orchestrator waits for ALL background agents in the group to complete. It will be notified automatically as each finishes (`run_in_background`).
|
|
2341
2360
|
|
|
2361
|
+
> **Do NOT poll with `sleep N; echo "waiting..."` loops.** Background agents (and background `Bash` commands) re-invoke the orchestrator automatically when they finish — issuing `sleep`/`echo` "waiting" turns burns orchestrator turns and tokens for zero progress. Simply end your turn after spawning the background work; you will be woken on completion. This applies to EVERY barrier in Step D (D.1 build, D.2 reviewers, D.3/D.3b agents, D.4 qa-sentinel, D.4a doc-reviewer, D.4b `/codexreview`), not just Step C.
|
|
2362
|
+
|
|
2342
2363
|
For each completed agent:
|
|
2343
2364
|
1. Read the completion report from the agent's output.
|
|
2344
2365
|
2. Log to tracker: card ID, status, files changed, build/lint status.
|
|
@@ -2358,21 +2379,38 @@ After ALL agents in the group complete successfully:
|
|
|
2358
2379
|
|
|
2359
2380
|
1. **D.1 — Build verification (group)** — Run `npm run build` in the worktree to verify combined changes compile. If build fails, identify which card's changes broke it (from `git diff` per card), spawn a targeted fix-coder for those files only.
|
|
2360
2381
|
|
|
2361
|
-
|
|
2382
|
+
1.5. **D.1.5 — Effective per-card review profile (compute ONCE; drives D.2 + D.4b)** — For EACH card in the group, compute its **effective codex profile** with the SAME deterministic rule the sequential Phase 3.7 Step C uses, so the two paths never disagree:
|
|
2383
|
+
- **Floor**: read the card's `review_profile` field (`skip`/`light`/`balanced`/`deep`) per the QA Profile Selector (fallback-computed only for legacy cards lacking the field).
|
|
2384
|
+
- **Escalation-only (Step A detector)**: run the Phase 3.7 **Step A** high-risk detector (bash + grep) on THIS card's committed diff (`git diff "$TRUNK...HEAD" -- <card files from ownership map>`). A matched trigger promotes `light`→`full`; it can NEVER downgrade `full`→`light`. The card's `review_profile` is the floor; the detector is the safety net on top.
|
|
2385
|
+
- **Partition** the group into two lists and log them in the tracker under `## Team Mode`:
|
|
2386
|
+
- `LIGHT_CARDS` = cards whose effective profile is `light` or `skip` **AND** zero Step-A triggers.
|
|
2387
|
+
- `FULL_CARDS` = all others (effective profile `balanced`/`deep`, OR any Step-A trigger).
|
|
2388
|
+
- Log: `## D.1.5 Effective Profiles\n<CARD-ID>: profile=<floor> triggers=<n> → effective=<light|full> (<LIGHT_CARDS|FULL_CARDS>)` per card. This single computation is the SSOT for both D.2 (code-reviewer scoping) and D.4b (inclusion) — do NOT recompute it in either step.
|
|
2389
|
+
|
|
2390
|
+
2. **D.2 — Combined static review (group)** — Two reviewers, **scoped by the D.1.5 partition** so each card gets exactly ONE per-card/group code review proportional to its risk (the other is the Final FULL gate):
|
|
2391
|
+
- **doc-reviewer — over the WHOLE group, always** (unchanged). It runs **read-only here** (it cannot write while code-reviewer reads in parallel — parallel-safety), MUST **attribute every doc finding to a specific card** (by file → ownership map), and applies the full Phase 3 mandate INCLUDING the spec/docs-drift→bug lens (since v3.35.0). D.4a consumes these per-card findings and dispatches the doc-reviewer (now alone, in write mode) to apply them — no second AUDIT spawn, but the FIXES are still owned by doc-reviewer.
|
|
2392
|
+
- **code-reviewer — scoped to the diff-union of `LIGHT_CARDS` ONLY** (run it in parallel with the doc-reviewer over those files). `LIGHT_CARDS` cards skip D.4b, so D.2 is their single per-card code review (the cross-batch Codex pass at Final covers them adversarially). **If `LIGHT_CARDS` is empty → do NOT spawn code-reviewer at D.2** (every card is `FULL_CARDS` and receives its deeper code review — `code-reviewer` + Codex + CoVe + FP — at D.4b). Log the deterministic gate reason: `D.2 code-reviewer: scope=LIGHT_CARDS [<ids>] | SKIPPED (no light cards — code-review owned by D.4b per-card + Final FULL gate)`.
|
|
2393
|
+
- **Tradeoff (documented, accepted — see top-of-file `REVIEW DEPTH SCALES` clause)**: for `FULL_CARDS`, cross-card detection moves from the per-group D.2 pass to the per-batch **Final-review FULL gate** (Codex over the entire batch diff, plus code-reviewer fallback) — same merge-gate safety, feedback arrives at batch-end rather than per-group. This is the intended de-duplication: `FULL_CARDS` are NOT re-reviewed by a group-level code-reviewer on top of their D.4b `/codexreview`.
|
|
2362
2394
|
|
|
2363
2395
|
3. **D.3 — Apply fixes (group)** — If D.2 findings exist, spawn ONE fix-coder to apply all fixes in a single pass. Run build + lint after.
|
|
2364
2396
|
|
|
2365
2397
|
3a. **D.3a — Phase 2.5b AC-Closure Gate (per-card, BLOCKING — non-skippable)** — For EACH card in the group, **sequentially**, invoke the full Phase 2.5b gate as documented in `### Phase 2.5b — AC-Closure Gate (BLOCKING — Scope Closure Discipline)`. This includes: build the AC Closure Ledger from the card YAML, run the rationalization scan, invoke `AskUserQuestion` one-per-deferred-AC, run the `implementation_notes` deferral audit, and persist the ledger in the tracker. Until EVERY card in the group exits PASS, do NOT proceed to D.3b. Cards exiting with `not_implemented` ACs that the user routes to "Implementa adesso" must finish their fix-coder loop and re-pass the gate before D.3b starts for the next card. Log under `## AC Closure Ledger — <CARD-ID>` per card.
|
|
2366
2398
|
|
|
2367
|
-
3b. **D.3b — Phase 2.55 Simplify (per-card)** —
|
|
2399
|
+
3b. **D.3b — Phase 2.55 Simplify (per-card, FANNED OUT across the group)** — The Simplify agents are **read-only analysis on file-disjoint per-card diffs** (the orchestrator applies the fixes afterward), so there is NO reason to run them one card at a time. **Spawn the per-card Simplify analysis for ALL eligible cards in PARALLEL** — in a SINGLE message, fire each card's Phase 2.55 trio (Reuse / Quality / Efficiency) scoped to that card's diff (`git diff --name-only` filtered by the card's File Ownership Map). Per-card (not group-aggregate) so findings stay attributable. When all analyses return, **apply fixes per card** (file-disjoint → no write conflict), then re-run `npm run lint` and `npx tsc --noEmit` on the worktree ONCE for the whole group. (Concurrency is capped by the platform; passing N cards is safe — excess agents queue.)
|
|
2400
|
+
- **Gate (enumerated, `review_profile`-driven)**: SKIP D.3b for a card whose `review_profile == skip` (trivial / non-implementable card — Simplify is quality-only, so there is no merge-gate coverage to lose, and there is no substantive diff to simplify). Log `simplify: SKIPPED (review_profile=skip)`. For `light`/`balanced`/`deep` cards D.3b runs unchanged. This is the ONLY enumerated skip — never skip D.3b "for time" on a `light`+ card. (Skipped cards are simply omitted from the parallel fan-out.)
|
|
2368
2401
|
|
|
2369
|
-
3c. **D.3c — Phase 2.6 E2E-Review (per-card)** —
|
|
2402
|
+
3c. **D.3c — Phase 2.6 E2E-Review (per-card)** — First, evaluate the existing Gate table for EVERY card at once (skip when `features.has_e2e_review: false`, backend-only diff per the diff predicate documented in Phase 2.6, or card type in the Phase 2.6 skip set — `backend`/`api`/`db`/`infra`/`docs`/`chore`/`config`). In practice most cards in a group skip this gate (backend/db/api), so the eligible set is usually 0–1. For the cards that PASS the gate, invoke `/e2e-review` in programmatic mode with that card's payload. Each `/e2e-review` keeps its own isolated state dir (`.baldart/e2e-review/<CARD-ID>/`), so multiple runs do not clobber each other's artifacts.
|
|
2403
|
+
- **Parallel-when-safe**: if **two or more** cards pass the gate, you MAY fan them out in parallel ONLY when they hit **disjoint routes/pages** — the genuinely shared resource is the worktree's single dev-server port, and concurrent Playwright sessions against one server are fine for disjoint routes. If the eligible cards touch overlapping routes (or `/e2e-review` spins its own server and a port clash is possible), run those **sequentially** to avoid a flaky cross-test. Default to parallel for the common disjoint case; fall back to sequential on any contention.
|
|
2404
|
+
- BLOCKING per-card: if `/e2e-review` returns `"blocked"` or `"error"`, surface to the user via the same `AskUserQuestion` documented in Phase 2.6 — do NOT proceed to D.4 with an unresolved card. Skips are logged with the documented gate reason, never with `"time budget"` or similar.
|
|
2370
2405
|
|
|
2371
2406
|
4. **D.4 — QA gate (group)** — Read each card's `review_profile` field (fallback for legacy cards only: compute via the QA Profile Selector, whose criteria are the SSOT in `prd-card-writer.md § Rule C`). Select the HIGHEST profile across all cards in the group (e.g., if one card is BALANCED and another is DEEP, use DEEP) and pass it as the `QA profile` to **qa-sentinel** (invoked once for the group) using the same prompt contract as Phase 3.5 step 22 (`do NOT default to FULL`; balanced → SCOPED, deep → FULL). Note: a mixed group containing any DEEP card runs the group at FULL — the max-profile intentionally overrides the SCOPED tier the BALANCED cards would get individually, because the combined diff must be validated together.
|
|
2372
2407
|
|
|
2373
2408
|
4a. **D.4a — Per-card doc gate (consumes D.2, since v3.35.0; doc-reviewer-applied since v3.40.0)** — Do NOT re-run the doc AUDIT (D.2 already produced per-card-attributed findings). If any card in the group has doc findings, invoke the **doc-reviewer once in write mode** over the group, passing the per-card-attributed findings from D.2, to APPLY all doc fixes in a single pass (it is now alone — code-reviewer is done — so the D.2 parallel-safety constraint no longer applies). Do **NOT** spawn a fix-coder for doc findings: `doc` is owned by `doc-reviewer` (see "Domain-Override Domains"); a code-oriented coder lacks the doc-invariant contract. The only exception is a doc-drift→bug finding rooted in CODE — that follows the D.4b code fix path. (Previously D.4a spawned a fix-coder per card; the audit was already collapsed to a single attributed D.2 pass. D.4b's per-card `/codexreview` also skips its doc-reviewer via the lean contract, so the doc AUDIT runs once per group while the doc FIXES run once per group via doc-reviewer.) **Telemetry** — append one row per applied doc finding to `## Fix Application Log`: `D.4a | doc | est_lines=<n> | decision=doc-reviewer | applied_by=doc-reviewer | card=<ID> | finding=<1-line>`. **Phase-8 producer (named counter)** — ALSO record per card the `doc_gaps: found=<N> fixed=<M>` line in that card's `## Completed Cards` entry (same named counter as sequential Phase 3 step 15), so Phase 8 reads `doc_gaps_found`/`doc_gaps_fixed` from a structured field, not a free-form row.
|
|
2374
2409
|
|
|
2375
|
-
4b. **D.4b — Pre-Merge Codex Review Gate (per-card,
|
|
2410
|
+
4b. **D.4b — Pre-Merge Codex Review Gate (per-card, profile-gated via D.1.5)** — Invoke `/codexreview` for **EACH card in `FULL_CARDS`** (the partition computed at D.1.5), one at a time, BEFORE any commit. This mirrors Phase 3.7 of the sequential path.
|
|
2411
|
+
- **`LIGHT_CARDS` → SKIP D.4b** (enumerated `review_profile`-driven gate reason, NOT a time skip). A `light`/`skip` 0-trigger card already received its single per-card code review at the **D.2 group pass**, and its Codex-adversarial review is **guaranteed by the unconditional Final-review FULL gate** (Codex over the entire batch diff, post-batch). Running `/codexreview` here at `light` would only re-run `code-reviewer` + FP-gate (Codex is dropped at `light` since v3.38.0) — a pure duplicate of D.2. Log per card: `codex-review: SKIPPED (light, 0 triggers — D.2 group code-review covered + Codex adversarial guaranteed by Final FULL gate)`.
|
|
2412
|
+
- **`FULL_CARDS` → run `/codexreview` as today** (the per-card Codex adversarial + CoVe + FP-gate pass is exactly where the per-card code review for these cards lives — D.2 deliberately did NOT re-review them). The profile passed is always `full` (D.1.5 already resolved it; do NOT recompute). Keep the sequential one-at-a-time loop (avoid N concurrent `/codexreview`, each of which fans out multiple sub-agents → rate-limit risk).
|
|
2413
|
+
Apply the same fix sub-loop as sequential Phase 3.7 Step C.4: if the consolidated report shows verified BLOCKER/HIGH findings, spawn a fix-coder and — **re-writing the consumed-once lean contract first** — re-invoke `/codexreview` via the Skill tool with `args: <CARD-ID>` (max 2 retries per card; a bare prose mention or a missing card-ID would let the retry review the wrong card). If still BLOCKER/HIGH after retries, ask the user before proceeding to D.5. The D.5 commits MUST NOT happen until every card in the group has a PASS verdict (or explicit user override via `AskUserQuestion`). Log results in the tracker under `## Pre-Merge Codex Review` per card. **Lean + profile (since v3.35.0)**: before each card's `/codexreview`, apply the same Review Profile Selector and write the same `/tmp/codexreview-lean-<CARD-ID>.json` contract as sequential Phase 3.7 Step C — `arch_baseline_path` pointing at `/tmp/arch-baseline-group-<FIRST-CARD-ID>.md`, `skip_doc_reviewer: true`, and `profile: full` (every card that reaches D.4b is in `FULL_CARDS` per D.1.5 — `light`/`skip` 0-trigger cards were already partitioned out and skip D.4b entirely; do NOT recompute the profile here). `full` runs the standard pipeline (Codex adversarial + CoVe + FP-gate, minus the duplicate doc-reviewer). The merge-gate safety for the skipped `LIGHT_CARDS` is the post-batch **Final-review FULL gate** (a single FULL `/codexreview` over the entire batch diff, which team mode reaches via "Post-batch — same as sequential mode"). To make D.4b run for every card again (pre-v4.5.0 behavior), drop the D.1.5 partition and iterate over all group cards here at their per-card profile; the final full gate stays regardless.
|
|
2376
2414
|
|
|
2377
2415
|
5. **D.5 — Commit** — One commit per card using explicit staging from the ownership map. **NO stash in worktrees** (stashes are globally shared via `refs/stash` — see Phase 4 WORKTREE COMMIT RULE):
|
|
2378
2416
|
```bash
|
|
@@ -2400,13 +2438,13 @@ After ALL agents in the group complete successfully:
|
|
|
2400
2438
|
Before moving to Step E, the orchestrator MUST verify the tracker contains, for EACH card in the group, ALL of the following entries (or an explicit gate-skip log with the documented reason):
|
|
2401
2439
|
|
|
2402
2440
|
- `## AC Closure Ledger — <CARD-ID>` with `ac-closure: implemented=N | user-approved deferrals=M | follow-up cards created=K`
|
|
2403
|
-
- `simplify: <N fixes | clean — 0 fixes>` (per-card from D.3b)
|
|
2441
|
+
- `simplify: <N fixes | clean — 0 fixes | SKIPPED (review_profile=skip)>` (per-card from D.3b)
|
|
2404
2442
|
- `e2e-review: <status>` (with documented gate-skip reason if SKIP)
|
|
2405
2443
|
- `doc-review: <status>` (from D.4a)
|
|
2406
2444
|
- `qa: <profile=... | verdict=...>` (group-level from D.4 — same value cited per card)
|
|
2407
|
-
- `codex-review: <verdict>` (per-card from D.4b)
|
|
2445
|
+
- `codex-review: <verdict | SKIPPED (light, 0 triggers — D.2 group code-review covered + Final FULL gate)>` (per-card from D.4b — the SKIPPED form is valid ONLY for cards in `LIGHT_CARDS` per the D.1.5 partition)
|
|
2408
2446
|
|
|
2409
|
-
A missing entry means a sub-step was skipped. If any entry is missing, do NOT proceed to Step E — return to the missing sub-step and execute it. Documenting "skipped per time budget" or similar is a protocol violation per the top-of-file rigidity clause.
|
|
2447
|
+
A missing entry means a sub-step was skipped. An entry whose value is a **`review_profile`-driven SKIP** with the documented enumerated reason above (D.3b `skip` profile, D.4b `LIGHT_CARDS`) is a VALID, present entry — not a violation. What remains forbidden: a missing entry, or a skip whose reason is `time budget` / `to save tokens` / any model-invented constraint. If any entry is genuinely missing, do NOT proceed to Step E — return to the missing sub-step and execute it. Documenting "skipped per time budget" or similar is a protocol violation per the top-of-file rigidity clause.
|
|
2410
2448
|
|
|
2411
2449
|
#### Step E: Context purge + next group
|
|
2412
2450
|
|
|
@@ -103,15 +103,21 @@ message. You ask questions, wait for answers, and iterate.
|
|
|
103
103
|
structure and `assets/card-template.yml` for child structure.
|
|
104
104
|
16. **Mockup Intake (MANDATORY).** Subito dopo Kickoff (Step 1) e prima di entrare in
|
|
105
105
|
Discovery (Step 2), esegui Step 1.6 — chiedi all'utente se ha mockup a disposizione.
|
|
106
|
-
Se **sì**: chiedi formato (immagini in chat / path locali — singoli file **
|
|
107
|
-
|
|
106
|
+
Se **sì**: chiedi formato (immagini in chat / path locali — singoli file, **archivi
|
|
107
|
+
`.zip`**, **o una cartella già estratta di un bundle Claude Design** — / misti),
|
|
108
|
+
**STOP**, attendi. Sull'arrivo:
|
|
108
109
|
- **chat-images** / file singoli in **local-paths**: copia in `${paths.prd_dir}/<slug>/mockups/`,
|
|
109
110
|
passa subito all'analisi.
|
|
110
|
-
- **`.zip`
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
- **`.zip` OPPURE cartella bundle Claude Design** (firma: `.design-canvas.state.json`
|
|
112
|
+
e/o più `*.html` con slot `id="s<N>"`; es. export "Download as .zip" o la cartella
|
|
113
|
+
scaricata dell'intero workspace): esegui Step 1.6.4b. Prima fa un **passaggio
|
|
114
|
+
strutturale** (canvas-anchor sul titolo feature → estrazione slot `id="s<N>"` =
|
|
115
|
+
screen-ID → arricchimento dal manifest `.design-canvas.state.json` → render degli
|
|
116
|
+
artboard canvas-only via [canvas-intake-recipe.md](references/canvas-intake-recipe.md));
|
|
117
|
+
lo **scoring per keyword** resta solo come fallback (bundle generico o canvas
|
|
118
|
+
ambiguo). In entrambi i casi presenta la selezione, **STOP**, attendi conferma,
|
|
114
119
|
poi materializza solo il sottoinsieme confermato in `${paths.prd_dir}/<slug>/mockups/`.
|
|
120
|
+
Il PRD non aborta mai sull'intake: ogni fallimento ricade sul fallback + STOP gate.
|
|
115
121
|
|
|
116
122
|
Quindi analizza in dettaglio (vedi
|
|
117
123
|
[discovery-phase.md](references/discovery-phase.md) § "Mockup analysis schema"), popola
|
|
@@ -46,6 +46,11 @@
|
|
|
46
46
|
{{/if}}
|
|
47
47
|
|
|
48
48
|
{{/each}}
|
|
49
|
+
> **Nota per l'export.** Dai al canvas di questa feature un titolo che contenga il
|
|
50
|
+
> nome feature («{{feature_title}}»), e numera gli artboard in modo coerente con gli
|
|
51
|
+
> ID schermata qui sopra (es. schermata 3.1 → artboard `s31`). Scaricherò il bundle
|
|
52
|
+
> completo del workspace: questo mi permette di ritrovare esattamente le TUE schermate
|
|
53
|
+
> ed evitare ambiguità con le altre feature.
|
|
49
54
|
|
|
50
55
|
{{#if brand_voice}}
|
|
51
56
|
## 4. Brand & voice
|
|
@@ -88,12 +88,21 @@ mockups.format: pending # chat-images | local-paths | local-archive |
|
|
|
88
88
|
mockups.original_paths: [] # user-provided paths, chat://image-N, or archive://<rel-path>
|
|
89
89
|
mockups.canonical_paths: [] # mockups/<file> after copy
|
|
90
90
|
|
|
91
|
-
mockups.archive: # populated only when format includes local-archive
|
|
92
|
-
local_zip_path: pending # abs path of the .zip the user gave us
|
|
93
|
-
kind: pending # zip
|
|
94
|
-
extracted_dir: pending # abs path of _archive/extracted/
|
|
91
|
+
mockups.archive: # populated only when format includes local-archive OR a Claude-Design bundle directory
|
|
92
|
+
local_zip_path: pending # abs path of the .zip the user gave us (n/a when kind: directory)
|
|
93
|
+
kind: pending # zip | directory
|
|
94
|
+
extracted_dir: pending # abs path of _archive/extracted/ (zip) or the user dir (directory)
|
|
95
95
|
truncated: false # true when candidate list capped at 200
|
|
96
96
|
candidates: [] # see discovery-phase.md § 1.6.4b step b-c
|
|
97
|
+
bundle_kind: pending # zip | directory | claude-design | generic (set by step b2 A0)
|
|
98
|
+
canvas_ref: pending # chosen feature canvas *.html (claude-design only)
|
|
99
|
+
canvas_candidates: [] # [{ file, title, slot_count, score, anchor_confidence }]
|
|
100
|
+
discarded: [] # [{ path, reason }] reason: other-feature-canvas | support-asset | upload | unrelated
|
|
101
|
+
|
|
102
|
+
mockups.canvas_render: # populated only when step b2 A5 ran (claude-design + render)
|
|
103
|
+
served_port: pending # http-server port used for the render
|
|
104
|
+
rendered_slots: [] # [{ slot_id, png }]
|
|
105
|
+
render_status: pending # ok | partial | failed | skipped
|
|
97
106
|
|
|
98
107
|
mockup_analysis.screens: [] # see discovery-phase.md § Mockup analysis schema
|
|
99
108
|
mockup_analysis.user_flow: []
|
|
@@ -104,7 +113,8 @@ mockup_analysis.design_system_alignment:
|
|
|
104
113
|
step_3_mode: pending # full | hybrid | skipped | mockup-only
|
|
105
114
|
design.html_path: pending # path | n/a (mockup-only)
|
|
106
115
|
|
|
107
|
-
screens_in_scope: [] # [{ name, covered_by_mockups: bool, generated_in_step_3b: bool }]
|
|
116
|
+
screens_in_scope: [] # [{ name, slot_id, screen_id, canvas_ref, covered_by_mockups: bool, generated_in_step_3b: bool }]
|
|
117
|
+
# slot_id/screen_id/canvas_ref set only for claude-design bundles (enable exact join in ui-design-phase Hybrid routing)
|
|
108
118
|
|
|
109
119
|
ui_inventory: [] # populated by Step 3d
|
|
110
120
|
|
|
@@ -473,14 +473,43 @@ Applied N findings to structured fields (H high, M medium).
|
|
|
473
473
|
Manual review needed: [list [MANUAL] items, or "none"].
|
|
474
474
|
```
|
|
475
475
|
|
|
476
|
+
### Holistic-audit provenance stamp (since v4.3.0) — consumed by `/new` Step 3d
|
|
477
|
+
|
|
478
|
+
This audit (the 4-agent team + Codex adversarial pass of Step 6.6) IS the holistic cross-card review: Step 6.6d's Codex prompt already covers `files_likely_touched … conflicting across cards`, dependency shadows, implicit ordering, and shared-state mutation across the whole `${CARD_PATHS}` set. To let `/new` AVOID re-running its own pre-flight cross-card Codex check (Step 3d) when nothing has changed since this audit, stamp each audited card with a machine-readable provenance block.
|
|
479
|
+
|
|
480
|
+
**Capture three run-level values ONCE** (same for every card in this audit run):
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
HOLISTIC_AUDITED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
484
|
+
# Grounding baseline = the trunk commit the cards were authored against.
|
|
485
|
+
# The PRD docs worktree was forked from trunk, so the fork point is that baseline.
|
|
486
|
+
HOLISTIC_AUDITED_COMMIT="$(git merge-base HEAD "${git.trunk_branch}" 2>/dev/null || git rev-parse "${git.trunk_branch}" 2>/dev/null || echo "")"
|
|
487
|
+
# audited_set = sorted card IDs reviewed JOINTLY in this run (the Step 6.6d ${CARD_PATHS} set).
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
`audited_set` is the **same list for every card** — it records which cards were considered together, so `/new` can verify its batch is a subset of one coherent jointly-audited set (not a mix of cards audited in separate PRD runs).
|
|
491
|
+
|
|
492
|
+
Write under `metadata.holistic_audit` on each card (additive — never overwrite other `metadata` keys):
|
|
493
|
+
|
|
494
|
+
```yaml
|
|
495
|
+
metadata:
|
|
496
|
+
holistic_audit:
|
|
497
|
+
audited_at: "<HOLISTIC_AUDITED_AT>"
|
|
498
|
+
audited_commit: "<HOLISTIC_AUDITED_COMMIT>" # "" if git unavailable → /new treats as drift (runs Step 3d)
|
|
499
|
+
audited_set: ["FEAT-XXXX-01", "FEAT-XXXX-02", ...] # sorted; the joint cross-card scope
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
> **Fail-safe contract**: this block is an OPTIMIZATION hint, never a gate. If it is absent, partial, or has an empty `audited_commit`, `/new` Step 3d simply RUNS as before — there is no correctness risk, only a redundant Codex pass. Do NOT block the PRD if the stamp can't be written.
|
|
503
|
+
|
|
476
504
|
### Per-card workflow
|
|
477
505
|
|
|
478
506
|
For each card:
|
|
479
507
|
1. Read persisted report → collect all findings for this card ID.
|
|
480
508
|
2. Read current card YAML.
|
|
481
509
|
3. Apply HIGH findings first, then MEDIUM, then write audit trail.
|
|
482
|
-
4. Write
|
|
483
|
-
5.
|
|
510
|
+
4. Write the `metadata.holistic_audit` provenance block (see "Holistic-audit provenance stamp" above) using the run-level values captured once.
|
|
511
|
+
5. Write updated card YAML.
|
|
512
|
+
6. Re-read to verify edits landed correctly.
|
|
484
513
|
|
|
485
514
|
**Note**: No separate commit here — the validation-phase.md Step 7 handles committing all PRD artifacts together.
|
|
486
515
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Canvas Intake Recipe — rendering Claude-Design artboards to PNG
|
|
2
|
+
|
|
3
|
+
Deterministic recipe for capturing the screens of a Claude Design feature canvas
|
|
4
|
+
that exist **only** as artboards inside the canvas HTML (not exported as standalone
|
|
5
|
+
PNGs). Replaces the ad-hoc "spin up a server, debug CORS, script Playwright" dance
|
|
6
|
+
with a repeatable procedure.
|
|
7
|
+
|
|
8
|
+
## When this runs
|
|
9
|
+
|
|
10
|
+
Invoked by [discovery-phase.md](discovery-phase.md) § "Step 1.6.4b → b2 → A5",
|
|
11
|
+
once per resolved `canvas_ref`, only for slots lacking a confident pre-render PNG.
|
|
12
|
+
**Never run standalone** — it assumes A0–A4 already chose the canvas and its slot
|
|
13
|
+
set. Outputs feed `mockups.canvas_render` in the state file.
|
|
14
|
+
|
|
15
|
+
## Why a local HTTP server is mandatory
|
|
16
|
+
|
|
17
|
+
The canvas HTML loads its screens via Babel:
|
|
18
|
+
`<script type="text/babel" src="design-canvas.jsx">` (+ the feature's `*.jsx`
|
|
19
|
+
deps). Opening the file as `file://` makes the browser **block** those `src`
|
|
20
|
+
fetches under the CORS/`file:` origin policy → Babel never executes → the artboard
|
|
21
|
+
DOM is never created → `[data-dc-slot]` / `[data-dc-section]` do not exist. This is
|
|
22
|
+
exactly the `slots=0` failure seen when rendering from `file://`. Serving `root`
|
|
23
|
+
over `http://` lets the jsx load and Babel render.
|
|
24
|
+
|
|
25
|
+
> Slot attributes are **runtime-only**: the static HTML has `<DCSection id="s31">`
|
|
26
|
+
> JSX; the `data-dc-section` / `data-dc-slot` attributes only appear after Babel
|
|
27
|
+
> runs on the served page. Intake-time slot enumeration greps `id="s<N>"` (static);
|
|
28
|
+
> rendering selects `[data-dc-section]` / `[data-dc-slot]` (runtime).
|
|
29
|
+
|
|
30
|
+
## Selectors
|
|
31
|
+
|
|
32
|
+
| Selector | Granularity | Source JSX |
|
|
33
|
+
|-----------------------|------------------------------|-------------------|
|
|
34
|
+
| `[data-dc-section="s3x"]` | one **screen** (3.x) | `<DCSection id>` |
|
|
35
|
+
| `[data-dc-slot="…"]` | one **artboard / variant** within a screen | `<DCArtboard id>` |
|
|
36
|
+
|
|
37
|
+
Capture at screen granularity (`[data-dc-section]`) by default; drop to
|
|
38
|
+
`[data-dc-slot]` only when a screen needs per-variant PNGs.
|
|
39
|
+
|
|
40
|
+
## Preflight (degrade, never auto-install)
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npx playwright --version # must succeed
|
|
44
|
+
npx http-server --version # must succeed
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If either is missing: do **NOT** auto-install (this runs in consumer repos —
|
|
48
|
+
respect their toolchain). Emit a one-line hint
|
|
49
|
+
(`npm i -D playwright http-server && npx playwright install chromium`), set
|
|
50
|
+
`mockups.canvas_render.render_status: skipped`, and return control to A5, which
|
|
51
|
+
degrades to pre-render PNGs + `source: live-html`.
|
|
52
|
+
|
|
53
|
+
## Serve
|
|
54
|
+
|
|
55
|
+
Pick a free port (loop 8080→8090, advancing on `EADDRINUSE`):
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npx http-server "$root" -p <PORT> -c-1 --silent # run in background; -c-1 disables caching
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Record `mockups.canvas_render.served_port: <PORT>`.
|
|
62
|
+
|
|
63
|
+
## Render loop (Playwright)
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
// /tmp/canvas-shoot.mjs — node, run with the project's playwright
|
|
67
|
+
import { chromium } from 'playwright';
|
|
68
|
+
const PORT = process.env.PORT, CANVAS = process.env.CANVAS, OUT = process.env.OUT;
|
|
69
|
+
const SLOTS = JSON.parse(process.env.SLOTS); // [{ slot_id, screen_id }]
|
|
70
|
+
const browser = await chromium.launch();
|
|
71
|
+
const page = await browser.newPage({ deviceScaleFactor: 2 }); // ≥2x export
|
|
72
|
+
await page.goto(`http://127.0.0.1:${PORT}/${encodeURIComponent(CANVAS)}`);
|
|
73
|
+
await page.waitForSelector('[data-dc-section]', { timeout: 15000 }); // confirms Babel ran
|
|
74
|
+
await page.waitForLoadState('networkidle');
|
|
75
|
+
for (const { slot_id, screen_id } of SLOTS) {
|
|
76
|
+
const el = await page.$(`[data-dc-section="${slot_id}"]`) || await page.$(`[data-dc-slot="${slot_id}"]`);
|
|
77
|
+
if (!el) { console.log(`MISS ${slot_id}`); continue; }
|
|
78
|
+
await el.screenshot({ path: `${OUT}/${screen_id.replace('.', '-')}.png` }); // <slug>__3-1.png upstream
|
|
79
|
+
console.log(`OK ${slot_id} -> ${screen_id}`);
|
|
80
|
+
}
|
|
81
|
+
await browser.close();
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Name outputs `<slug>__<screen-id>.png` (e.g. `conferma-ordine__3-1.png`). Record
|
|
85
|
+
each in `mockups.canvas_render.rendered_slots[]`.
|
|
86
|
+
|
|
87
|
+
## Teardown
|
|
88
|
+
|
|
89
|
+
Kill the background `http-server` (by PID). Set `render_status`:
|
|
90
|
+
- `ok` — every requested slot captured.
|
|
91
|
+
- `partial` — some slots captured, some `MISS` (record which).
|
|
92
|
+
- `failed` — `waitForSelector('[data-dc-section]')` timed out (Babel never ran:
|
|
93
|
+
offline CDN, broken jsx). Degrade as in preflight.
|
|
94
|
+
|
|
95
|
+
## Dedup vs designer pre-renders
|
|
96
|
+
|
|
97
|
+
If a slot has BOTH a designer pre-render (A4) and a freshly rendered PNG, **prefer
|
|
98
|
+
the designer's pre-render** — keep our render only if it differs materially in
|
|
99
|
+
dimensions/content, and then name it `<screen-id>__rendered.png`. Never ship two
|
|
100
|
+
PNGs for the same screen state.
|
|
101
|
+
|
|
102
|
+
## Failure → degradation summary
|
|
103
|
+
|
|
104
|
+
| Failure | Detection | Degradation |
|
|
105
|
+
|----------------------------------|------------------------------------|--------------------------------------------------|
|
|
106
|
+
| Playwright / http-server missing | preflight version check fails | `skipped` → pre-render + `live-html`, install hint |
|
|
107
|
+
| All ports busy | EADDRINUSE through 8090 | try an ephemeral port; else `skipped` |
|
|
108
|
+
| Babel never runs (CORS/offline) | `[data-dc-section]` wait times out | `failed` → pre-render + `live-html` |
|
|
109
|
+
| Some artboards missing | `MISS` in loop | `partial` — capture the rest, log the misses |
|
|
110
|
+
|
|
111
|
+
Global rule: **never abort the PRD**. Every failure here returns to A5 with a
|
|
112
|
+
degraded `render_status`; intake proceeds with whatever PNGs exist plus live-HTML
|
|
113
|
+
references.
|
|
@@ -242,37 +242,49 @@ When the user replies with the mockups:
|
|
|
242
242
|
5. Update `mockups.status: provided` (or `partial` only if the user explicitly says
|
|
243
243
|
"ho solo questi 2 ma servono anche altri").
|
|
244
244
|
|
|
245
|
-
### 1.6.4b — Archive ingestion & relevance selection (when `mockups.format` includes `local-archive
|
|
245
|
+
### 1.6.4b — Archive ingestion & relevance selection (when `mockups.format` includes `local-archive`, OR the user gives a Claude-Design bundle directory)
|
|
246
246
|
|
|
247
247
|
Triggered when the user hands a local `.zip` path (typically a Claude Design
|
|
248
|
-
"Download as .zip" export already on disk)
|
|
249
|
-
|
|
250
|
-
the
|
|
248
|
+
"Download as .zip" export already on disk) **OR** a path to an already-extracted
|
|
249
|
+
**bundle directory** (the user downloaded the whole Claude Design workspace as a
|
|
250
|
+
folder). The flow resolves the subset of files belonging to THIS feature, then asks
|
|
251
|
+
the user to confirm before analysis.
|
|
251
252
|
|
|
252
|
-
|
|
253
|
+
Resolution is two-tier: a **structural pass** (step b2) that exploits Claude
|
|
254
|
+
Design's native bundle structure when present, with the **keyword scorer** (step c)
|
|
255
|
+
as the fallback. The STOP confirmation gate (step d) is identical in both modes.
|
|
253
256
|
|
|
254
|
-
|
|
257
|
+
#### Step a — Detect & accept root
|
|
258
|
+
|
|
259
|
+
For each path provided:
|
|
255
260
|
|
|
256
261
|
```bash
|
|
257
|
-
file "<user-
|
|
262
|
+
file "<user-path>" # only meaningful for files
|
|
258
263
|
```
|
|
259
264
|
|
|
260
|
-
-
|
|
261
|
-
|
|
262
|
-
-
|
|
265
|
+
- **Directory** (the path is a folder): skip unpacking. Record:
|
|
266
|
+
- `mockups.archive.kind: directory`
|
|
267
|
+
- `mockups.archive.extracted_dir: <the user dir>` (absolute)
|
|
268
|
+
- `mockups.archive.local_zip_path: n/a`
|
|
269
|
+
- **`.zip`** (`file` reports a Zip archive): record:
|
|
263
270
|
- `mockups.archive.local_zip_path: <user-zip-path>` (absolute)
|
|
264
271
|
- `mockups.archive.kind: zip`
|
|
265
|
-
- Unpack into a scratch directory under the PRD's mockup folder:
|
|
272
|
+
- Unpack into a scratch directory under the PRD's mockup folder:
|
|
266
273
|
|
|
267
|
-
```bash
|
|
268
|
-
mkdir -p "$WORKTREE_PATH/${paths.prd_dir}/<slug>/mockups/_archive"
|
|
269
|
-
unzip -q "<user-zip-path>" \
|
|
270
|
-
|
|
271
|
-
```
|
|
274
|
+
```bash
|
|
275
|
+
mkdir -p "$WORKTREE_PATH/${paths.prd_dir}/<slug>/mockups/_archive"
|
|
276
|
+
unzip -q "<user-zip-path>" \
|
|
277
|
+
-d "$WORKTREE_PATH/${paths.prd_dir}/<slug>/mockups/_archive/extracted"
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
- Record `mockups.archive.extracted_dir` (the `extracted/` path).
|
|
281
|
+
- If `unzip` fails (corrupted/encrypted): STOP, surface the exit code, ask the
|
|
282
|
+
user to verify the zip or provide an alternative.
|
|
283
|
+
- **Regular non-zip file**: treat as a single file — go back to step 1.6.4 point
|
|
284
|
+
2's "Otherwise" branch, no special handling.
|
|
272
285
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
user to verify the zip or provide an alternative.
|
|
286
|
+
Both directory and zip branches converge on a single `root` =
|
|
287
|
+
`mockups.archive.extracted_dir`. Everything below operates on `root`.
|
|
276
288
|
|
|
277
289
|
#### Step b — Enumerate candidates
|
|
278
290
|
|
|
@@ -309,8 +321,112 @@ top 200, recording `mockups.archive.truncated: true`.
|
|
|
309
321
|
|
|
310
322
|
Record the full index in state under `mockups.archive.candidates[]`.
|
|
311
323
|
|
|
324
|
+
#### Step b2 — Claude-Design structural resolution (Feature-Subset Resolution)
|
|
325
|
+
|
|
326
|
+
A Claude Design export is **not** a flat pile of files: it carries native
|
|
327
|
+
structure that pins each feature's screens deterministically. When that structure
|
|
328
|
+
is present, resolve the subset *structurally* and skip the keyword scorer. When it
|
|
329
|
+
is absent (or the canvas anchor stays ambiguous), fall through to step c unchanged.
|
|
330
|
+
|
|
331
|
+
> **Global contract.** The structural resolver is an **accelerator**. Any failure
|
|
332
|
+
> at any step (A0–A6) falls through to the keyword scorer (step c) + the STOP gate
|
|
333
|
+
> (step d), which is already proven. **The PRD never aborts on intake.**
|
|
334
|
+
|
|
335
|
+
**A0 — Signature gate.** On `root`, compute:
|
|
336
|
+
- `has_manifest` — `[ -f "$root/.design-canvas.state.json" ]`.
|
|
337
|
+
- `has_canvas_jsx` — a `design-canvas.jsx` exists, or some `*.html` loads it via
|
|
338
|
+
`<script type="text/babel" src=…>` and references `data-dc-section`.
|
|
339
|
+
- `feature_html_count` — number of top-level `*.html` whose **source** contains an
|
|
340
|
+
`id="s<N>"` slot (see A2 — this is the static signal; `data-dc-slot` only exists
|
|
341
|
+
at runtime after Babel renders, so do NOT grep for it here).
|
|
342
|
+
|
|
343
|
+
Set `mockups.archive.bundle_kind`:
|
|
344
|
+
- `claude-design` iff `has_manifest` OR (`has_canvas_jsx` AND `feature_html_count ≥ 1`) → run A1–A6.
|
|
345
|
+
- otherwise `generic` → **skip to step c** (keyword scorer), unchanged.
|
|
346
|
+
|
|
347
|
+
**A1 — Canvas anchor (feature_title → ONE canvas `*.html`).**
|
|
348
|
+
- Candidate set = top-level `*.html` MINUS the denylist of component/system
|
|
349
|
+
canvases: `Design System.html`, `BottomNav*.html`, `*-bundle-src.html`,
|
|
350
|
+
`* · standalone.html`. **Do NOT pre-filter by slot presence** — feature canvases
|
|
351
|
+
in the wild use mixed slot schemes (some `id="s<N>"`, some semantic like
|
|
352
|
+
`id="supplier-edit-a-add"`), and an `id="s<N>"`-only allowlist would wrongly drop
|
|
353
|
+
a feature whose canvas doesn't use that numbering. Slot presence is a *confidence
|
|
354
|
+
signal* (below), not a candidacy gate.
|
|
355
|
+
- For each candidate read its `<title>` + filename, score against `feature_title`
|
|
356
|
+
+ scope keywords (reuse step c's case-insensitive whole-word splitter): `<title>`
|
|
357
|
+
match `+50`, filename `+40`, `id="s<N>"` slot-range hint `+10` (boosts canvases
|
|
358
|
+
that expose clean screen-IDs, enabling A2's exact join). Record in
|
|
359
|
+
`mockups.archive.canvas_candidates[]`.
|
|
360
|
+
- **Resolution:**
|
|
361
|
+
- Exactly 1 candidate with a confident margin (top score ≥ 50 AND ≥ 2× the
|
|
362
|
+
runner-up) → `mockups.archive.canvas_ref = that file`, `anchor_confidence: high`.
|
|
363
|
+
- 0 clear, OR ≥ 2 within the 2× margin → **AMBIGUOUS**. Do NOT guess. Use
|
|
364
|
+
`AskUserQuestion` listing the candidate canvases (filename + `<title>` + slot
|
|
365
|
+
count): "Quale canvas è questa feature?". If the user says none/multiple →
|
|
366
|
+
fall through to step c over all candidates (`anchor_confidence: fallback-keyword`).
|
|
367
|
+
Multiple confirmed → A2 unions the slot sets and `canvas_ref` becomes a list.
|
|
368
|
+
|
|
369
|
+
**A2 — Slot extraction (canvas → screen IDs).** From `canvas_ref` source only.
|
|
370
|
+
Anchor on the **section** element that carries the screen title, NOT a loose
|
|
371
|
+
`title=` (sibling attributes like `subtitle="…"` contain the substring `title="`
|
|
372
|
+
and a greedy `id=…title=…` regex grabs the wrong one):
|
|
373
|
+
```bash
|
|
374
|
+
# DCSection is the per-screen container; its `title` is the screen label.
|
|
375
|
+
grep -oE '<DCSection id="s[0-9]+[a-z]*" title="[^"]*"' "<canvas_ref>"
|
|
376
|
+
# <DCSection id="s31" title="3.1 · Wizard Step 3"
|
|
377
|
+
```
|
|
378
|
+
→ `{ slot_id, screen_id, title_raw }` per section. (If the bundle uses a different
|
|
379
|
+
section tag, anchor on whatever element holds the `id`+dotted-`title` pair.)
|
|
380
|
+
- Derive `screen_id` from the title's dotted prefix (e.g. `"3.1 · …"` → `3.1`).
|
|
381
|
+
Cross-check against the slot number (`s31` → `3.1`). On disagreement, **trust the
|
|
382
|
+
`title=` prefix** and log `slot_id_number_mismatch` (handles non-`s<N>`
|
|
383
|
+
numbering). If neither yields a dotted id, leave `screen_id: null` — that screen
|
|
384
|
+
joins downstream by the fuzzy fallback, never blocks.
|
|
385
|
+
|
|
386
|
+
**A3 — Manifest read (enrich, never gate).** If `has_manifest`, parse
|
|
387
|
+
`.design-canvas.state.json`. For each slot in A2:
|
|
388
|
+
- `sections["s31"].labels` → attach as candidate `states_visible[]` / variant labels.
|
|
389
|
+
- `sections["s31"].title` → prefer as the human label; else keep the A2 `title=`.
|
|
390
|
+
- Manifest is **partial and mixed-keyspace** (some keys are slot-ids, some are
|
|
391
|
+
semantic section names belonging to OTHER canvases). Keys not in this canvas's
|
|
392
|
+
slot set are ignored. A slot missing from the manifest is the **normal case**,
|
|
393
|
+
not an error. Set `screens[].source` = `manifest` (labelled) or `title` (not).
|
|
394
|
+
|
|
395
|
+
**A4 — Sidecar gather.**
|
|
396
|
+
- **JSX deps** — the `<script type="text/babel" src="X.jsx">` list inside
|
|
397
|
+
`canvas_ref` IS the authoritative dependency set (needed for A5 render). Record
|
|
398
|
+
for materialization in step e.
|
|
399
|
+
- **Pre-rendered PNGs** — scan `$root/screenshots/` (and any image dir) for files
|
|
400
|
+
whose name confidently matches a slot number / slug (`ap-31.png` → `s31`). On a
|
|
401
|
+
confident match set `screens[].prerender_png` + `source: prerender`. Naming is
|
|
402
|
+
ad-hoc (`modale__valido.png`) so matching is **best-effort only**; unmatched
|
|
403
|
+
images are parked, never force-assigned.
|
|
404
|
+
|
|
405
|
+
**A5 — Render canvas-only artboards.** Screens that exist only inside the canvas
|
|
406
|
+
(no confident `prerender_png`) must be rendered. Invoke
|
|
407
|
+
[canvas-intake-recipe.md](canvas-intake-recipe.md) (http-server + Playwright over
|
|
408
|
+
`[data-dc-section="s<N>"]`). Output `mockups/<slug>__<screen-id>.png`, record under
|
|
409
|
+
`mockups.canvas_render`. If render is impossible (Playwright/server unavailable),
|
|
410
|
+
**degrade**: keep pre-render PNGs, mark remaining slots `source: live-html`
|
|
411
|
+
referencing the canvas HTML directly, set `render_status: failed|skipped`, and
|
|
412
|
+
continue — never abort.
|
|
413
|
+
|
|
414
|
+
**A6 — Discard-and-log the rest.** Everything in `root` NOT in { `canvas_ref`, its
|
|
415
|
+
jsx deps, matched `screenshots/*`, shared assets (`*tokens*.css`, `*icons*.jsx`,
|
|
416
|
+
`*shell*.jsx`) } is out-of-feature: the other feature canvases, their jsx,
|
|
417
|
+
`frames/`, `uploads/`, `docs/`. Record in `mockups.archive.discarded[]` with a
|
|
418
|
+
one-word `reason`. Shown for transparency at the STOP gate (step d) but **not
|
|
419
|
+
copied**.
|
|
420
|
+
|
|
421
|
+
After A6, build the screen-centric selection and go to **step d** (its structural
|
|
422
|
+
variant). Skip step c entirely — it is the fallback path only.
|
|
423
|
+
|
|
312
424
|
#### Step c — Score against feature scope
|
|
313
425
|
|
|
426
|
+
> **Fallback path.** Runs ONLY when step b2 set `bundle_kind: generic`, or the
|
|
427
|
+
> canvas anchor stayed unresolved (`anchor_confidence: fallback-keyword`). For a
|
|
428
|
+
> resolved Claude-Design bundle, skip directly to step d.
|
|
429
|
+
|
|
314
430
|
Build the scope vector from the Kickoff state (Step 1):
|
|
315
431
|
- `feature_title` (from `## Feature Description`).
|
|
316
432
|
- `slug`.
|
|
@@ -340,8 +456,34 @@ Record per-candidate: `score`, `matched_keywords[]`, `selected: bool`.
|
|
|
340
456
|
|
|
341
457
|
#### Step d — Confirmation gate (STOP)
|
|
342
458
|
|
|
343
|
-
|
|
344
|
-
|
|
459
|
+
Two table variants, same STOP/adjust/re-present loop:
|
|
460
|
+
|
|
461
|
+
**Structural variant** (step b2 resolved a `canvas_ref`). Present **screen-centric**
|
|
462
|
+
— one row per resolved screen, plus the discarded-canvas summary:
|
|
463
|
+
|
|
464
|
+
```
|
|
465
|
+
Canvas individuato: «Approvazione Ordini.html» (feature «<feature_title>») — <K> schermate.
|
|
466
|
+
|
|
467
|
+
**Schermate risolte** (slot → screen-ID):
|
|
468
|
+
| # | Slot | Screen | Label | Sorgente |
|
|
469
|
+
|---|------|--------|------------------------------------|-----------|
|
|
470
|
+
| 1 | s31 | 3.1 | Wizard Step 3 · CTA approvazione | manifest |
|
|
471
|
+
| 2 | s32 | 3.2 | Elenco approver | rendered |
|
|
472
|
+
…
|
|
473
|
+
|
|
474
|
+
**Scartati** (altre feature / asset di supporto):
|
|
475
|
+
| File / dir | Motivo |
|
|
476
|
+
|-------------------------------|-----------------------|
|
|
477
|
+
| Budget Pianificazione.html | other-feature-canvas |
|
|
478
|
+
| frames/, uploads/ | unrelated / upload |
|
|
479
|
+
|
|
480
|
+
Procedo all'analisi delle <K> schermate? Oppure indicami modifiche
|
|
481
|
+
(es. "togli s37", "il canvas giusto è Budget Pianificazione").
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
**Keyword-fallback variant** (step c ran). Present file-centric — selected
|
|
485
|
+
candidates first, then up to 10 discarded with the highest scores, then a footer
|
|
486
|
+
count for "altri N scartati":
|
|
345
487
|
|
|
346
488
|
```
|
|
347
489
|
Bundle scaricato e analizzato: <N> file candidati, <K> selezionati come rilevanti.
|
|
@@ -366,22 +508,37 @@ Procedo all'analisi visiva dei <K> selezionati? Oppure indicami modifiche
|
|
|
366
508
|
```
|
|
367
509
|
|
|
368
510
|
**STOP.** Wait for explicit confirmation or an adjustment list. On adjustment,
|
|
369
|
-
recompute `mockups.archive.candidates[].selected
|
|
370
|
-
until the
|
|
511
|
+
recompute the selection (`mockups.archive.candidates[].selected` in keyword mode,
|
|
512
|
+
the per-slot row set in structural mode), then re-present and STOP again until the
|
|
513
|
+
user confirms.
|
|
371
514
|
|
|
372
515
|
#### Step e — Materialize the confirmed subset
|
|
373
516
|
|
|
374
517
|
Copy ONLY the confirmed selection into `${paths.prd_dir}/<slug>/mockups/`,
|
|
375
|
-
preserving relative directory structure under
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
`
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
518
|
+
preserving relative directory structure under `root` so that relative asset
|
|
519
|
+
references (CSS, images) keep working. On filename collision, use the
|
|
520
|
+
`<name>-2.<ext>` rule from 1.6.4 point 2.
|
|
521
|
+
|
|
522
|
+
**Structural mode also copies the render dependencies**, so the canvas stays
|
|
523
|
+
renderable/analyzable inside the PRD folder:
|
|
524
|
+
- `canvas_ref` itself + every jsx dep from A4 + shared assets (`*tokens*.css`,
|
|
525
|
+
`*icons*.jsx`, `*shell*.jsx`), preserving relative paths so `<script src>` /
|
|
526
|
+
`<link>` keep resolving.
|
|
527
|
+
- the rendered PNGs (`mockups.canvas_render.rendered_slots[].png`) and matched
|
|
528
|
+
pre-render PNGs.
|
|
529
|
+
- **Write `screens_in_scope[]`** here — one row per resolved screen with `name`,
|
|
530
|
+
`slot_id`, `screen_id`, `canvas_ref`. This is what lets the Step 3 (UI Design)
|
|
531
|
+
Hybrid routing do an **exact join** instead of a fuzzy substring match (see
|
|
532
|
+
[ui-design-phase.md](ui-design-phase.md) § "Build the per-screen routing table").
|
|
533
|
+
|
|
534
|
+
Record `mockups.original_paths[]` as `archive://<relative-path-in-zip>` (zip) or
|
|
535
|
+
`archive://<relative-path-in-root>` (directory) and
|
|
536
|
+
`mockups.canonical_paths[]` as the new path under `mockups/`. When `kind: zip`,
|
|
537
|
+
the scratch `_archive/extracted/` directory is kept for the duration of the PRD
|
|
538
|
+
session (may be needed to resolve `<link>` / `<img src>` references during
|
|
539
|
+
analysis) and cleaned up at Step 7 (Commit & Merge) — add a `.gitignore` entry for
|
|
540
|
+
`mockups/_archive/` if not already present. When `kind: directory`, `root` is the
|
|
541
|
+
user's own folder outside the worktree — never modify or clean it; only read.
|
|
385
542
|
|
|
386
543
|
Then continue to Step 1.6.5 with the selected subset.
|
|
387
544
|
|
|
@@ -802,12 +959,23 @@ mockups:
|
|
|
802
959
|
- <user-provided path, chat://image-N, or archive://<rel-path-in-zip>>
|
|
803
960
|
canonical_paths:
|
|
804
961
|
- mockups/<file>.png # only for local-paths and local-archive
|
|
805
|
-
archive: # only when format includes local-archive
|
|
806
|
-
local_zip_path: <abs path to the user-provided .zip>
|
|
807
|
-
kind: zip
|
|
808
|
-
extracted_dir: <abs path to _archive/extracted
|
|
962
|
+
archive: # only when format includes local-archive OR a Claude-Design bundle dir
|
|
963
|
+
local_zip_path: <abs path to the user-provided .zip> # n/a when kind: directory
|
|
964
|
+
kind: zip | directory
|
|
965
|
+
extracted_dir: <abs path to _archive/extracted/ (zip) or the user dir (directory)>
|
|
809
966
|
truncated: false # true when candidate list capped at 200
|
|
810
|
-
|
|
967
|
+
bundle_kind: zip | directory | claude-design | generic # set by step b2 A0
|
|
968
|
+
canvas_ref: <chosen feature canvas *.html> # claude-design only
|
|
969
|
+
canvas_candidates: # claude-design only
|
|
970
|
+
- file: <*.html basename>
|
|
971
|
+
title: <HTML <title>>
|
|
972
|
+
slot_count: <int> # count of id="s<N>" slots
|
|
973
|
+
score: <0-100>
|
|
974
|
+
anchor_confidence: high | user-resolved | fallback-keyword
|
|
975
|
+
discarded: # claude-design only — out-of-feature, NOT copied
|
|
976
|
+
- path: <rel path under root>
|
|
977
|
+
reason: other-feature-canvas | support-asset | upload | unrelated
|
|
978
|
+
candidates: # full enumerated list (post-filter) — keyword-fallback path
|
|
811
979
|
- path: <rel path under extracted/>
|
|
812
980
|
filename: <basename>
|
|
813
981
|
ext: html | png | jpg | svg | …
|
|
@@ -817,10 +985,20 @@ mockups:
|
|
|
817
985
|
score: <0-100>
|
|
818
986
|
matched_keywords: [<keyword>, …]
|
|
819
987
|
selected: <bool> # true = part of the confirmed subset
|
|
988
|
+
canvas_render: # only when step b2 A5 rendered canvas-only artboards
|
|
989
|
+
served_port: <int>
|
|
990
|
+
rendered_slots:
|
|
991
|
+
- slot_id: s31
|
|
992
|
+
png: mockups/<slug>__3-1.png
|
|
993
|
+
render_status: ok | partial | failed | skipped
|
|
820
994
|
mockup_analysis:
|
|
821
995
|
screens:
|
|
822
996
|
- name: <inferred name, e.g. "Lista ordini">
|
|
997
|
+
slot_id: <s31, claude-design only — else omit>
|
|
998
|
+
screen_id: <"3.1" dotted, claude-design only — joins handoff prompt § 3 — else omit>
|
|
823
999
|
mockup_ref: <canonical_path or chat://image-N>
|
|
1000
|
+
source: manifest | title | rendered | prerender # claude-design only — provenance of the label/image
|
|
1001
|
+
prerender_png: <mockups/<file>.png — only when a designer pre-render matched this slot>
|
|
824
1002
|
purpose: <one line — what this screen lets the user do>
|
|
825
1003
|
components:
|
|
826
1004
|
- <component name from ${paths.design_system}/INDEX.md if matched, else free-form>
|
|
@@ -180,11 +180,19 @@ to the design system, approve, and inventory. Generate ONLY the screens not cove
|
|
|
180
180
|
- ISA `NAVIGATION` and `DASHBOARD` touchpoints that imply landing screens.
|
|
181
181
|
- The `mockup_analysis.screens[]` list itself (in case the mockups include
|
|
182
182
|
screens the user stories didn't make explicit).
|
|
183
|
-
2. For each entry, set `covered_by_mockups: true
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
183
|
+
2. For each entry, set `covered_by_mockups: true`. Match **exact-join-first**:
|
|
184
|
+
- **Exact join (Claude-Design bundles).** When the entry has a `slot_id` /
|
|
185
|
+
`screen_id` (populated by Step 1.6.4b structural resolution, see
|
|
186
|
+
[discovery-phase.md](discovery-phase.md) § "Step b2") AND a
|
|
187
|
+
`mockup_analysis.screens[]` entry shares the same `slot_id`/`screen_id`, join
|
|
188
|
+
them **deterministically** — no ambiguity, no STOP. This is the common case
|
|
189
|
+
for handoff bundles and skips the fuzzy guesswork entirely.
|
|
190
|
+
- **Fuzzy fallback.** Only when `slot_id` is absent (chat images, single files,
|
|
191
|
+
`generic` bundles) fall back to case-insensitive substring match on
|
|
192
|
+
`mockup_analysis.screens[].name` — and keep the STOP-and-ask on ambiguity:
|
|
193
|
+
"questa schermata `<X>` corrisponde al mockup `<Y>`?".
|
|
194
|
+
3. Persist `screens_in_scope[]` (with `slot_id`/`screen_id`/`canvas_ref` when
|
|
195
|
+
present) in the state file `## UI Design` section.
|
|
188
196
|
|
|
189
197
|
### Per-screen execution
|
|
190
198
|
|
|
@@ -130,3 +130,13 @@ metadata:
|
|
|
130
130
|
# Auto-computed by prd-card-writer for PRD-generated cards; set it by hand here
|
|
131
131
|
# when authoring a card manually (omit to let /new fall back to runtime compute).
|
|
132
132
|
review_profile: "" # skip | light | balanced | deep
|
|
133
|
+
# holistic_audit — provenance of the /prd Step 6.6 cross-model holistic audit
|
|
134
|
+
# (4-agent team + Codex adversarial pass). WRITTEN BY the audit phase, not at
|
|
135
|
+
# authoring — leave absent when creating a card by hand. /new reads it to decide
|
|
136
|
+
# whether its pre-flight cross-card check (Step 3d) can be skipped because the
|
|
137
|
+
# batch was already audited jointly AND nothing changed since. Fail-safe: if
|
|
138
|
+
# absent/partial, /new just runs Step 3d (no correctness risk).
|
|
139
|
+
# holistic_audit:
|
|
140
|
+
# audited_at: "" # ISO-8601-UTC of the audit
|
|
141
|
+
# audited_commit: "" # trunk commit the cards were grounded against
|
|
142
|
+
# audited_set: [] # card IDs audited jointly (the cross-card scope)
|