baldart 4.3.0 → 4.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.
- package/CHANGELOG.md +40 -0
- package/VERSION +1 -1
- package/framework/.claude/skills/new/SKILL.md +98 -13
- 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/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/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,46 @@ 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.6.0] - 2026-06-03
|
|
9
|
+
|
|
10
|
+
**`/new` ora ha una corsia "trivial" per le card minime: niente review di codice su un diff che non contiene codice.** Dopo il de-dup v4.5.0 (profondità scalata su `review_profile`), restava un pavimento alto: una card `skip`-profile ma implementabile — un fix di copy in un `.md`, un update di una riga di `ssot-registry`, un bump di config non-API — passava comunque per codebase-architect + plan-auditor + code-reviewer + AC-closure + QA + doc + Final. Una nuova **fast-lane** scatta quando `IS_TRIVIAL(card)` = `review_profile == skip` **AND** 0 trigger del detector Step-A **AND** diff **non-source** (check deterministico per estensione, riusando il predicato "backend-only" di Phase 2.6 — solo `.md`/`.json`/`.yml`/`.txt`/`.csv`). Una card trivial esegue solo il tail minimo sicuro: `coder → gate meccanici inline (markdownlint/lint/build) → AC-closure → doc-review → commit`, saltando **codebase-architect + plan-auditor + code-reviewer + simplify + Codex + la suite di test QA**. Vale in **entrambe le modalità** (sequential: gate in Phase 1 step 2c + 2.55/3.5/3.7; team: sotto-classe `TRIVIAL_CARDS ⊆ LIGHT_CARDS` esclusa dallo scope del code-reviewer a D.2). **MINOR** (nuova capability per-card; nessuna chiave `baldart.config.yml` — si appoggia su `review_profile` già autorato in PRD).
|
|
11
|
+
|
|
12
|
+
> **Why.** `review_profile: skip` NON è riservato agli epic: `prd-card-writer.md § Rule C` lo assegna deterministicamente a card `docs`/`chore`/`config` o puramente cosmetiche (typo/rename/copy) senza logica. È quindi un segnale autorato in PRD, persistito nello YAML, su cui ancorare la corsia **senza inventare una soglia a righe** (coerente col principio "data-driven sopra threshold arbitrarie"). Il classificatore è un AND a 3 condizioni con **guard fail-safe**: un solo file source nel diff, un trigger Step-A, o un profilo non-`skip` → `IS_TRIVIAL=false` e la card torna al percorso normale. La triviality può solo **togliere review a una card che non ha codice da revisionare**, mai il contrario — una `skip` mis-autorata su codice, o una il cui testo cita `auth`/`payment`, viene sempre ricatturata. Gli invarianti restano: **AC-closure** (scope discipline) e **doc-review** non si saltano mai, e la **Final Review F.3** copre incondizionatamente ogni card trivial nel gate cross-batch prima del merge (stessa rete su cui si appoggia già il percorso `light`). Tutti gli skip sono gate-reason enumerati deterministici, mai `time budget`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- **`framework/.claude/skills/new/SKILL.md`** — (a) nuova sotto-sezione SSOT "Trivial-card fast-lane" (classificatore `IS_TRIVIAL` a 3 condizioni AND + guard + tabella di cosa salta/resta). (b) Clausola in testa: `IS_TRIVIAL` aggiunto all'elenco dei gate-reason enumerati. (c) Sequential Phase 1: nuovo step **2c** che salta codebase-architect + plan-auditor (step 3/3a/4) per card provvisoriamente trivial; il briefing del coder degrada a card-spec + target file. (d) Phase 2.55/3.5/3.7: gate `IS_TRIVIAL` ri-confermato sul diff reale → skip Simplify + QA-suite + Codex, gate meccanici inline; carve-out esplicito nell'"UNCONDITIONAL" di Phase 3.7. (e) Team D.1.5: sotto-classe `TRIVIAL_CARDS ⊆ LIGHT_CARDS`; D.2 esclude le trivial dallo scope del code-reviewer. (f) Step D coverage assertion: skip trivial come entry valide documentate. Non toccati: `codexreview.md`, `prd-card-writer.md`, AC-closure, doc-review, Final Review F.3, percorso light/full di v4.5.0.
|
|
17
|
+
|
|
18
|
+
## [4.5.0] - 2026-06-03
|
|
19
|
+
|
|
20
|
+
**`/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).
|
|
21
|
+
|
|
22
|
+
> **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.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **`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.
|
|
27
|
+
|
|
28
|
+
> **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.
|
|
29
|
+
|
|
30
|
+
## [4.4.0] - 2026-06-03
|
|
31
|
+
|
|
32
|
+
**`/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).
|
|
33
|
+
|
|
34
|
+
> **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.
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- **`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.
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- **`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.
|
|
43
|
+
- **`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.
|
|
44
|
+
- **`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.
|
|
45
|
+
- **`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`.
|
|
46
|
+
- **`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.
|
|
47
|
+
|
|
8
48
|
## [4.3.0] - 2026-06-03
|
|
9
49
|
|
|
10
50
|
**`/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).
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.6.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`, **the card's `review_profile`-driven review depth — see next paragraph — and the `IS_TRIVIAL` fast-lane (`review_profile=skip` + non-source diff + 0 high-risk triggers), defined once in § "Trivial-card fast-lane"**). 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
|
|
|
@@ -386,6 +388,63 @@ disagree.
|
|
|
386
388
|
|
|
387
389
|
---
|
|
388
390
|
|
|
391
|
+
## Trivial-card fast-lane (since v4.6.0 — SSOT; phases below CITE this, never redefine it)
|
|
392
|
+
|
|
393
|
+
Some cards are genuinely too small to justify the review machine: a `docs`/`chore`/`config`
|
|
394
|
+
card, a copy fix in a `.md`, a one-line `ssot-registry` update, a non-API `.yml` bump. Running
|
|
395
|
+
codebase-architect + plan-auditor + code-reviewer + Codex + the QA test suite on a diff that
|
|
396
|
+
contains **no source code** is pure waste — there is nothing for those agents to review. The
|
|
397
|
+
fast-lane routes such cards through the minimal safe tail. It is a **per-card** property
|
|
398
|
+
(applies in BOTH sequential and team mode), and it is the polar opposite of a `time budget`
|
|
399
|
+
skip: it is gated entirely on deterministic, PRD-authored signals.
|
|
400
|
+
|
|
401
|
+
### The classifier — `IS_TRIVIAL(card)` (ALL three must hold — AND)
|
|
402
|
+
|
|
403
|
+
1. **`review_profile == skip`** — read verbatim from the card YAML. Per `prd-card-writer.md § Rule C`,
|
|
404
|
+
`skip` is assigned to `docs`/`chore`/`config` cards, all-non-logic-file cards, and pure-cosmetic
|
|
405
|
+
cards (typo/rename/copy/wording/style). This is NOT the epic guard (an epic is never implemented;
|
|
406
|
+
a trivial card IS implemented, just with a minimal tail). **AND**
|
|
407
|
+
2. **Zero Step-A high-risk triggers** — run the Phase 3.7 **Step A** detector (the same bash+grep:
|
|
408
|
+
high-risk module from `paths.high_risk_modules` / dead-code resurrection / reference-aliasing) on
|
|
409
|
+
this card's diff. Any trigger ⇒ NOT trivial. **AND**
|
|
410
|
+
3. **Non-source diff** — `git diff --name-only` (working-tree pre-commit in sequential; `$TRUNK...HEAD`
|
|
411
|
+
post-commit in team mode) contains **NO file matching the source-extension predicate**
|
|
412
|
+
(reuse/extend the Phase 2.6 backend-only predicate):
|
|
413
|
+
```bash
|
|
414
|
+
SRC_RE='\.(tsx?|jsx?|mjs|cjs|vue|svelte|css|scss|py|go|rs|java|rb|php|sql|prisma)$'
|
|
415
|
+
if echo "$CHANGED" | grep -qE "$SRC_RE"; then IS_TRIVIAL=0; else IS_TRIVIAL=1; fi
|
|
416
|
+
```
|
|
417
|
+
Only `.md` / `.json` / `.yml` / `.yaml` / `.txt` / `.csv` and doc paths qualify.
|
|
418
|
+
|
|
419
|
+
**Fail-safe guard (degrade toward MORE review, never less)**: if ANY condition fails — a single
|
|
420
|
+
source file in the diff, one Step-A trigger, or a non-`skip` profile — `IS_TRIVIAL=false` and the
|
|
421
|
+
card follows its normal path (in team mode that is `LIGHT_CARDS`: D.2 code-reviewer + Final; in
|
|
422
|
+
sequential, the full per-card pipeline). A mis-authored `skip` on a code card, or a `skip` card
|
|
423
|
+
whose text mentions `auth`/`payment`/etc., is therefore always caught. The classifier can only ever
|
|
424
|
+
*remove* review from a card that demonstrably has no code to review.
|
|
425
|
+
|
|
426
|
+
### What a trivial card runs vs skips
|
|
427
|
+
|
|
428
|
+
| Step | Trivial card |
|
|
429
|
+
|------|--------------|
|
|
430
|
+
| codebase-architect grounding (Phase 1 step 3) | **SKIP** (non-source, self-contained) |
|
|
431
|
+
| plan-auditor grounding (Phase 1 step 4) | **SKIP** (depends on architect findings) |
|
|
432
|
+
| coder (Phase 2) | **RUN** — briefing degrades to card-spec + target file from `files_likely_touched` (no architect section) |
|
|
433
|
+
| mechanical gates | **RUN INLINE** — `markdownlint` on changed `.md`, `lint` on lintable files, `build` as a sanity check (NO qa-sentinel spawn, NO test suite) |
|
|
434
|
+
| Phase 2.5b / D.3a AC-Closure | **RUN** (scope discipline, inline, cheap — never skipped) |
|
|
435
|
+
| Phase 2.55 / D.3b Simplify | **SKIP** |
|
|
436
|
+
| Phase 2.6 / D.3c E2E | **SKIP** (already gated by `docs`/`chore`/`config` type) |
|
|
437
|
+
| Phase 3 / D.4a doc-review | **RUN** (the relevant reviewer for docs/ssot) |
|
|
438
|
+
| Phase 3.5 / D.4 QA test suite | **SKIP** (covered by inline mechanical gates above) |
|
|
439
|
+
| Phase 3.7 / D.4b Codex | **SKIP** |
|
|
440
|
+
| commit + backlog | **RUN** |
|
|
441
|
+
| **Final Review F.3** | **RUNS (unconditional net)** — covers every trivial card in the batch-wide gate before merge |
|
|
442
|
+
|
|
443
|
+
Every skip above is logged with the enumerated reason `trivial: review_profile=skip + non-source
|
|
444
|
+
diff + 0 triggers` — a deterministic, card-authored gate reason. Never `time budget`.
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
389
448
|
## Agent Routing (Phase 2 dispatcher)
|
|
390
449
|
|
|
391
450
|
Phase 2 reads the card's `owner_agent` field and dispatches to the matching
|
|
@@ -435,7 +494,8 @@ For each card, execute these phases in order:
|
|
|
435
494
|
- If NOT `DONE` → HALT: log in `## Issues & Flags` and ask the user: "Card <CARD-ID> depends on <DEP-ID> which is `<status>`. Proceed anyway, or wait?" Do not start implementation until the user responds explicitly. **The card status is NOT written until this gate passes** — so a HALT leaves the card untouched (no stale `IN_PROGRESS` for the next run to mis-read).
|
|
436
495
|
- If `DONE` (or the user chose "proceed anyway") → continue.
|
|
437
496
|
2b. **Claim** — only now set the card status to `IN_PROGRESS` and assign yourself.
|
|
438
|
-
|
|
497
|
+
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.
|
|
498
|
+
3. **(skip when provisionally trivial — see 2c)** 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`. The orchestrator does NOT propagate the 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.)
|
|
439
499
|
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.
|
|
440
500
|
4. **Plan-auditor grounding check** — invoke the **plan-auditor** agent in **QUICK mode** (pass `mode: QUICK`; its contract runs only the grounding checks, and because the codebase-architect findings are passed in below it does NOT re-spawn codebase-architect for this narrow check):
|
|
441
501
|
```
|
|
@@ -1088,6 +1148,8 @@ Domains NOT listed here remain governed by the per-phase rules of the correspond
|
|
|
1088
1148
|
|
|
1089
1149
|
### Phase 2.55 — Simplify (code cleanup before review)
|
|
1090
1150
|
|
|
1151
|
+
> **Trivial fast-lane gate**: re-confirm `IS_TRIVIAL` on the ACTUAL committed diff (§ "Trivial-card fast-lane" — all 3 conditions, now including the real non-source diff check). If trivial → **SKIP this phase** AND Phase 3.5 QA + Phase 3.7 Codex; instead run the **inline mechanical gates** (`markdownlint` on changed `.md`, `lint` on lintable changed files, `build` as a sanity check — no qa-sentinel, no test suite), then proceed to Phase 3 (doc review, which DOES run) and Phase 4 (commit). Log `simplify/qa/codex: SKIPPED (trivial — non-source diff)`. If the actual diff turned out to contain a source file → NOT trivial → run this phase and the normal review path. AC-Closure (Phase 2.5b) already ran and is never skipped.
|
|
1152
|
+
|
|
1091
1153
|
After completeness is verified, clean up the implementation before it reaches reviewers and E2E tests. This phase launches three parallel agents on the card's diff, then applies fixes directly.
|
|
1092
1154
|
|
|
1093
1155
|
**Step-by-step**:
|
|
@@ -1326,7 +1388,9 @@ skill's Phase 1 falls back to deriving Gherkin scenarios from
|
|
|
1326
1388
|
|
|
1327
1389
|
### Phase 3.7 — Pre-Merge Codex Review Gate (MANDATORY — UNCONDITIONAL)
|
|
1328
1390
|
|
|
1329
|
-
**This gate
|
|
1391
|
+
**This gate runs for EVERY card with code to review before the Phase 4 commit.** A per-card `/codexreview` MUST run BEFORE the Phase 4 commit, regardless of file paths or perceived risk. The historical conditional High-Risk Path detector is preserved below — but only as a **signal-logging step** to record *which* triggers matched in the tracker. It NEVER suppresses the `/codexreview` invocation. Even if zero triggers match, `/codexreview` runs.
|
|
1392
|
+
|
|
1393
|
+
> **Single carve-out — `IS_TRIVIAL` fast-lane (since v4.6.0)**: a card that is `IS_TRIVIAL` on its ACTUAL committed diff (`review_profile=skip` + non-source diff + 0 Step-A triggers — § "Trivial-card fast-lane") **skips this gate**, because there is no code for Codex to review. Its adversarial coverage is carried by the SAME unconditional **Final-review FULL gate** the `light` path already relies on (it runs Codex over the entire batch diff, including every trivial card's files, before merge). This is NOT a `time budget` skip — it is the deterministic `IS_TRIVIAL` gate reason, and the guard means a single source file in the diff flips the card back onto this gate. Log `codex-review: SKIPPED (trivial — non-source diff; covered by Final FULL gate)`.
|
|
1330
1394
|
|
|
1331
1395
|
**Rationale**: leaving the gate conditional caused it to be silently skipped on the majority of cards, defeating the AGENTS.md "MUST run BEFORE merge" requirement. The gate itself is unconditional — what varies is its DEPTH (`light` vs `full`). The protection against a missed blocker on a "low-risk" path (BUG-0530-class regressions) is carried by two mechanisms, not by an always-on per-card Codex pass: (1) the Step A detector escalates any risky diff to `full` per-card, and (2) the unconditional **Final-review FULL gate** runs Codex adversarial over the entire batch diff before merge. So `light` can safely run `code-reviewer` + FP-gate only (since v3.38.0) without weakening the merge gate.
|
|
1332
1396
|
|
|
@@ -2356,6 +2420,8 @@ Agent tool call:
|
|
|
2356
2420
|
|
|
2357
2421
|
The orchestrator waits for ALL background agents in the group to complete. It will be notified automatically as each finishes (`run_in_background`).
|
|
2358
2422
|
|
|
2423
|
+
> **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.
|
|
2424
|
+
|
|
2359
2425
|
For each completed agent:
|
|
2360
2426
|
1. Read the completion report from the agent's output.
|
|
2361
2427
|
2. Log to tracker: card ID, status, files changed, build/lint status.
|
|
@@ -2375,21 +2441,39 @@ After ALL agents in the group complete successfully:
|
|
|
2375
2441
|
|
|
2376
2442
|
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.
|
|
2377
2443
|
|
|
2378
|
-
|
|
2444
|
+
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:
|
|
2445
|
+
- **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).
|
|
2446
|
+
- **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.
|
|
2447
|
+
- **Partition** the group into two lists and log them in the tracker under `## Team Mode`:
|
|
2448
|
+
- `LIGHT_CARDS` = cards whose effective profile is `light` or `skip` **AND** zero Step-A triggers.
|
|
2449
|
+
- `FULL_CARDS` = all others (effective profile `balanced`/`deep`, OR any Step-A trigger).
|
|
2450
|
+
- **Sub-classify `TRIVIAL_CARDS` ⊆ `LIGHT_CARDS`** = cards that are `IS_TRIVIAL` on the committed diff (§ "Trivial-card fast-lane": `review_profile == skip` AND 0 Step-A triggers AND **non-source diff**). These are the LIGHT cards that have nothing for `code-reviewer` to review. (A `skip` card whose diff DID touch a source file is in `LIGHT_CARDS` but NOT `TRIVIAL_CARDS` — the guard keeps it on the code-review path.)
|
|
2451
|
+
- Log: `## D.1.5 Effective Profiles\n<CARD-ID>: profile=<floor> triggers=<n> diff=<source|non-source> → effective=<light|full> (<LIGHT_CARDS|FULL_CARDS>)<, TRIVIAL if applicable>` per card. This single computation is the SSOT for D.2 (code-reviewer scoping), D.3b/D.3c (already skipped for trivial), and D.4b (inclusion) — do NOT recompute it downstream.
|
|
2452
|
+
|
|
2453
|
+
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):
|
|
2454
|
+
- **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.
|
|
2455
|
+
- **code-reviewer — scoped to the diff-union of `LIGHT_CARDS` MINUS `TRIVIAL_CARDS`** (run it in parallel with the doc-reviewer over those files). These light non-trivial cards skip D.4b, so D.2 is their single per-card code review (the cross-batch Codex pass at Final covers them adversarially). **`TRIVIAL_CARDS` are excluded from the code-reviewer scope** — their diff is non-source, so there is nothing to code-review (doc-reviewer still covers them via the whole-group pass above; the Final FULL gate covers them adversarially). **If `LIGHT_CARDS \ TRIVIAL_CARDS` is empty → do NOT spawn code-reviewer at D.2** (every remaining card is either `FULL_CARDS` — deeper review at D.4b — or `TRIVIAL_CARDS` — nothing to review). Log the deterministic gate reason: `D.2 code-reviewer: scope=[<ids>] | excluded-trivial=[<ids>] | SKIPPED (no code-bearing light cards)`.
|
|
2456
|
+
- **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`.
|
|
2379
2457
|
|
|
2380
2458
|
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.
|
|
2381
2459
|
|
|
2382
2460
|
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.
|
|
2383
2461
|
|
|
2384
|
-
3b. **D.3b — Phase 2.55 Simplify (per-card)** —
|
|
2462
|
+
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.)
|
|
2463
|
+
- **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.)
|
|
2385
2464
|
|
|
2386
|
-
3c. **D.3c — Phase 2.6 E2E-Review (per-card)** —
|
|
2465
|
+
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.
|
|
2466
|
+
- **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.
|
|
2467
|
+
- 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.
|
|
2387
2468
|
|
|
2388
2469
|
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.
|
|
2389
2470
|
|
|
2390
2471
|
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.
|
|
2391
2472
|
|
|
2392
|
-
4b. **D.4b — Pre-Merge Codex Review Gate (per-card,
|
|
2473
|
+
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.
|
|
2474
|
+
- **`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)`.
|
|
2475
|
+
- **`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).
|
|
2476
|
+
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.
|
|
2393
2477
|
|
|
2394
2478
|
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):
|
|
2395
2479
|
```bash
|
|
@@ -2416,14 +2500,15 @@ After ALL agents in the group complete successfully:
|
|
|
2416
2500
|
|
|
2417
2501
|
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):
|
|
2418
2502
|
|
|
2419
|
-
- `## AC Closure Ledger — <CARD-ID>` with `ac-closure: implemented=N | user-approved deferrals=M | follow-up cards created=K`
|
|
2420
|
-
- `simplify: <N fixes | clean — 0 fixes>` (per-card from D.3b)
|
|
2503
|
+
- `## AC Closure Ledger — <CARD-ID>` with `ac-closure: implemented=N | user-approved deferrals=M | follow-up cards created=K` (NEVER skipped — applies to trivial cards too)
|
|
2504
|
+
- `simplify: <N fixes | clean — 0 fixes | SKIPPED (review_profile=skip)>` (per-card from D.3b)
|
|
2421
2505
|
- `e2e-review: <status>` (with documented gate-skip reason if SKIP)
|
|
2422
|
-
- `doc-review: <status>` (from D.4a)
|
|
2423
|
-
- `qa: <profile=... | verdict
|
|
2424
|
-
- `
|
|
2506
|
+
- `doc-review: <status>` (from D.4a — runs for trivial cards too)
|
|
2507
|
+
- `qa: <profile=... | verdict=... | mechanical-only (trivial)>` (group-level from D.4; for a `TRIVIAL_CARDS`-only ride-along the trivial card cites the group value or `mechanical-only`)
|
|
2508
|
+
- `code-review: <covered at D.2 | covered at D.4b | SKIPPED (trivial — non-source diff)>` (per-card — the trivial SKIP is valid ONLY for `TRIVIAL_CARDS` per the D.1.5 partition)
|
|
2509
|
+
- `codex-review: <verdict | SKIPPED (light, 0 triggers — D.2 group code-review covered + Final FULL gate) | SKIPPED (trivial — non-source diff; covered by Final FULL gate)>` (per-card from D.4b — the SKIPPED forms are valid ONLY for `LIGHT_CARDS` / `TRIVIAL_CARDS` respectively)
|
|
2425
2510
|
|
|
2426
|
-
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.
|
|
2511
|
+
A missing entry means a sub-step was skipped. An entry whose value is a **`review_profile`-driven SKIP** or **`IS_TRIVIAL` SKIP** with the documented enumerated reason above (D.3b `skip` profile, D.4b `LIGHT_CARDS`, code/codex `TRIVIAL_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.
|
|
2427
2512
|
|
|
2428
2513
|
#### Step E: Context purge + next group
|
|
2429
2514
|
|
|
@@ -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
|
|
|
@@ -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
|
|