baldart 4.12.0 → 4.14.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.
@@ -0,0 +1,353 @@
1
+ <!-- Modulo on-demand di /new — NON un file installato a sé. Caricato via Read dalla § "Routing" del SKILL.md core. -->
2
+
3
+ > **Modulo `/new`** — eseguilo, poi torna al core § "Routing" per la fase successiva. I `§ "..."` (Context economy, Context Tracking, Trivial-card fast-lane, Risk-signal detector, Fix Application Log) vivono nel **core SKILL.md**.
4
+
5
+ ### Phase 1 — Claim & Context
6
+ 1. **Update tracker**: set current card, phase = "1-claim".
7
+ 2. **`depends_on` gate (BEFORE claiming)** — read the card's `depends_on` field. For each listed card ID:
8
+ - Read that card's backlog YAML and check its `status` field.
9
+ - 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).
10
+ - If `DONE` (or the user chose "proceed anyway") → continue.
11
+ 2b. **Claim** — only now set the card status to `IN_PROGRESS` and assign yourself. **→ Visibility**: TaskUpdate this card's spine task → `in_progress`, and emit a Progress Bar (card change) per § "Progress Visibility".
12
+ 2c. **Trivial-card classification (BLOCKING gate for steps 3–4)** — evaluate `IS_TRIVIAL(card)` per § "Trivial-card fast-lane". Note: condition 3 (non-source diff) cannot be fully evaluated until the coder has produced the diff, so at this point compute the **provisional** trivial flag from conditions 1+2 only (`review_profile == skip` AND no Step-A trigger sourced from the card YAML text + `files_likely_touched` extensions — if EVERY path in `files_likely_touched` is non-source, condition 3 is provisionally satisfied). If provisionally trivial → **SKIP steps 3, 3a, and 4** (architecture grounding); log `trivial: architecture grounding skipped (review_profile=skip + non-source files_likely_touched + 0 triggers)` and jump to Phase 2. Re-confirm `IS_TRIVIAL` on the ACTUAL committed diff at the review gates (Phase 2.55/3.5/3.7); if the coder unexpectedly touched a source file, the guard flips the card back onto the normal review path there. If NOT provisionally trivial → run steps 3, 3a, 4 as normal.
13
+ 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.)
14
+ 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.
15
+ 4. **Plan-auditor grounding check** — but first, a **provenance skip (since v4.7.0 — mirror of Step 3d)**: the `/prd` Step 6.6 holistic audit already ran a per-card grounding pass when the card was authored. Re-grounding here is only worth a plan-auditor spawn if something changed since. Skip the plan-auditor when **BOTH** hold:
16
+ - **P1 — provenance present**: the card has `metadata.holistic_audit` with non-empty `audited_at`, `audited_commit`, `audited_set`.
17
+ - **P2 — no drift on this card's claimed paths** since the audit baseline. With `$AC` = `metadata.holistic_audit.audited_commit`, `$MAIN`/`$TRUNK` from the tracker, and this card's `files_likely_touched`/claimed paths (`$CARD_PATHS`):
18
+ ```bash
19
+ # empty AC, AC not in history, or any git error → treat as drift (do NOT skip)
20
+ DRIFT="$(git -C "$MAIN" log --oneline "$AC".."$TRUNK" -- $CARD_PATHS 2>/dev/null || echo "DRIFT")"
21
+ ```
22
+ P2 holds **iff** `$AC` is non-empty AND the command succeeds AND `$DRIFT` is empty.
23
+
24
+ (No S2/S3 cross-card conditions — grounding is per-card, and the holistic audit grounded each card individually.) If **P1 ∧ P2 → SKIP** the plan-auditor: log `plan-auditor grounding: SKIPPED (holistic_audit provenance, no drift on claimed paths since <AC short sha>)`, carry the card requirements unchanged into Phase 2, and proceed to step 5. **Fail-safe**: missing/partial provenance, any drift, or any git error → fall through to RUN the plan-auditor as written below (never a silent skip). Independent of the trivial fast-lane (a trivial card already skipped steps 3–4 at step 2c). Otherwise — 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):
25
+ ```
26
+ mode: QUICK
27
+ Grounding check only (NOT a full audit — do not run the full AUDIT CHECKLIST A-H, and do not
28
+ re-invoke codebase-architect; the findings are pasted below). Verify this backlog card's
29
+ requirements are grounded in the actual codebase.
30
+
31
+ Check:
32
+ 1. Do all paths in files_likely_touched actually exist?
33
+ 2. Are all type/field references in requirements correct per the codebase findings below?
34
+ 3. Are any [ASSUMED] items answerable by reading the listed files (i.e., should be verified facts)?
35
+ 4. Do any requirements conflict with known anti-patterns from your memory?
36
+
37
+ Card YAML:
38
+ [paste full card YAML]
39
+
40
+ Codebase-architect findings:
41
+ [paste key findings from step 3]
42
+
43
+ Return: PASS | FIXES NEEDED (list exact corrections). No full audit report needed.
44
+ ```
45
+ - If **FIXES NEEDED**: apply corrections to the tracker notes for this card (do NOT modify the backlog YAML). Carry the corrected requirements into the Phase 2 briefing.
46
+ - If **PASS**: proceed.
47
+ 5. **Update tracker**: phase = "1-claim DONE", log codebase-architect key findings (1-2 lines) and plan-auditor result (PASS or corrections applied).
48
+ 5b. **Persist the architecture baseline for reuse (since v3.35.0)** — write the **untruncated codebase-architect task result verbatim** (the full Task-tool output: file paths, type signatures, patterns, high-risk paths) to `/tmp/arch-baseline-<CARD-ID>.md`. This is NOT the 1-2-line tracker summary from step 5 — serialize the agent's complete output to disk BEFORE you summarize it for the tracker, so Phase 3.7's per-card `/codexreview` (lean mode, reuses this file instead of re-spawning `codebase-architect`) gets enough detail to ground a reviewer. If you summarize first and write the summary, the lean contract is defeated.
49
+
50
+ ### Phase 2 — Implement (self-healing, up to 3 retries)
51
+ 6. **Update tracker**: phase = "2-implement".
52
+ 6b. **Agent dispatch (router)** — read `owner_agent` from the card YAML and resolve the spawn target per the table in § Agent Routing above:
53
+
54
+ ```
55
+ Let raw = card.owner_agent (string, may be missing).
56
+ # Epic guard FIRST — an epic is a tracker, not implementable work.
57
+ If card is an epic (id ends "-00" OR filename ends "-epic.yml" OR review_profile == "skip"
58
+ with no requirements): SKIP — do NOT dispatch to any agent. Log
59
+ "[ROUTER] card <ID> is an epic (tracker) — skipped, not implemented" and move to the next card.
60
+ Switch on raw:
61
+ - "coder" → spawn = "coder", briefing = "coder"
62
+ - "ui-expert" → spawn = "ui-expert", briefing = "ui-expert"
63
+ - "plan" → spawn = "coder", briefing = "coder"
64
+ + log: "[ROUTER] plan-only briefing not yet implemented — using coder"
65
+ - "visual-designer" → spawn = "coder", briefing = "coder"
66
+ + log: "[ROUTER] visual-designer briefing not yet implemented — using coder"
67
+ - "motion-expert" → spawn = "coder", briefing = "coder"
68
+ + log: "[ROUTER] motion-expert briefing not yet implemented — using coder"
69
+ - "" | missing | "claude"
70
+ → spawn = "coder", briefing = "coder"
71
+ + log: "[ROUTER] card <ID> has no valid owner_agent (got '<raw>'); defaulting to coder"
72
+ - anything else → HALT. Log: "[ROUTER] card <ID> has unknown owner_agent '<raw>' — not in {coder, ui-expert, plan, visual-designer, motion-expert}. Ask the user how to proceed."
73
+ ```
74
+
75
+ Log the resolved `spawn` and `briefing` in the tracker (1 line: `"router: owner_agent=<raw> → spawn=<agent>, briefing=<variant>"`). This is the audit trail for which specialist actually handled the card.
76
+
77
+ 7. Spawn the **resolved agent** (from step 6b) using this **standardized mission briefing** — fill in each section from the card YAML, tracker, and codebase-architect findings. When `briefing = "ui-expert"`, ALSO include the additional UI sections marked **[UI-ONLY]** below:
78
+
79
+ ```
80
+ ## MISSION BRIEFING — <CARD-ID>
81
+
82
+ ### Card Specification (verbatim — do not paraphrase)
83
+ Requirements:
84
+ [copy full requirements list from card YAML, including any corrections from the plan-auditor grounding check (step 4)]
85
+
86
+ Acceptance Criteria:
87
+ [copy full acceptance_criteria list from card YAML]
88
+
89
+ ### Unknowns & Conditional Requirements (MANDATORY — resolve before coding)
90
+ [If card has `unknowns` field: copy verbatim. If empty or absent: "None."]
91
+
92
+ Each unknown that says "verify X; if missing → do Y" is a BINARY-OUTCOME ITEM.
93
+ You MUST actively verify and produce one of the two outcomes (implementation OR TODO comment).
94
+ See `coder.md § Conditional Requirements — Binary-Outcome Items`.
95
+
96
+ ### Business Context (WHY this feature exists)
97
+ [paste business_rationale field from card YAML]
98
+ [if field is empty/missing, read PRD Section 1b from card's links.prd path]
99
+
100
+ ### Codebase Context
101
+ [paste codebase-architect key findings: file paths, exact line numbers, type signatures, existing patterns to follow]
102
+ [include any corrections or anti-patterns flagged by plan-auditor grounding check]
103
+
104
+ ### Design Reference (UI cards only — include if card has links.design)
105
+ Design file: [path from card's links.design field]
106
+ Read the design.html file and use it as the visual reference for your implementation.
107
+ The design was approved by the user — your implementation MUST match it.
108
+
109
+ ### [UI-ONLY] Registry-first BLOCKING pre-work (include ONLY when briefing = "ui-expert")
110
+ You are spawned as `ui-expert`. Before writing any UI code, you MUST complete
111
+ the registry-first cascade defined in
112
+ `framework/.claude/agents/ui-expert.md` § "Authoritative Style Reference —
113
+ Registry-First Protocol" (the SSOT lives in
114
+ `framework/agents/design-system-protocol.md`). Reproduce its 5 steps
115
+ verbatim, do NOT paraphrase or skip:
116
+
117
+ 1. Read `${paths.design_system}/INDEX.md` (component index + Canonical
118
+ Authority Matrix).
119
+ 2. Read `${paths.ui_guidelines}` (visual language, typography,
120
+ accessibility, brand voice).
121
+ 3. Read `${paths.design_system}/tokens-reference.md` (token contract —
122
+ SSOT for every color / spacing / shadow / radius / motion value).
123
+ 4. For EACH primitive in scope on this card, read
124
+ `${paths.design_system}/components/<Name>.md`. "In scope" = any
125
+ component named in the mockup, the requirements, or `files_likely_touched`.
126
+ 5. For EACH pattern in scope, read the relevant
127
+ `${paths.design_system}/patterns/<topic>.md`.
128
+
129
+ State, in your first response, which Authority Matrix rows govern this
130
+ card, which component specs you read (step 4), and which patterns you
131
+ read (step 5). Skipping this declaration = the orchestrator will reject
132
+ the implementation and re-spawn.
133
+
134
+ The `features.has_design_system: true` flag in `baldart.config.yml` is
135
+ the precondition for the full cascade. When `features.has_design_system:
136
+ false`, only step 2 applies — log the gap, recommend `/design-system-init`
137
+ if drift is visible, and proceed with coder-level diligence (no cascade
138
+ enforced).
139
+
140
+ ### [UI-ONLY] Implementation-only constraint (include ONLY when briefing = "ui-expert")
141
+ This `/new` invocation gives you the role of IMPLEMENTER, not designer:
142
+
143
+ - DO NOT redesign the mockup. The mockup at `links.design` is the
144
+ user-approved visual contract for this card.
145
+ - DO NOT silently adapt a mockup detail that conflicts with the registry
146
+ (e.g. a custom shadow not in `tokens-reference.md`, a font size off the
147
+ type scale, a color outside the palette). If you find such a conflict:
148
+ STOP, log it in the tracker as `[DS-DRIFT] mockup-vs-registry`, and ask
149
+ the user which side to honor.
150
+ - DO NOT extend scope into business logic, API calls, state management, or
151
+ validation rules. Those belong to a sibling `coder` card (see
152
+ `backlog-phase.md § Rule B`). If a requirement appears to need logic,
153
+ STOP and ask — do not implement it.
154
+
155
+ ### [UI-ONLY] Post-Intervention Coherence Check (include ONLY when briefing = "ui-expert")
156
+ Before declaring the card done, you MUST reconcile the three design-system
157
+ sources in the SAME commit as your UI change, per
158
+ `framework/agents/design-system-protocol.md § Post-Intervention Coherence Check`:
159
+
160
+ - `${paths.design_system}/INDEX.md` — add / update entries for any primitive
161
+ introduced or modified. Drift code: `DS_INDEX_DRIFT`.
162
+ - `${paths.design_system}/components/<Name>.md` — ship the per-component
163
+ spec for any new primitive; update the spec when a primitive's API or
164
+ visual changes. Drift code: `DS_COMPONENT_STALE`.
165
+ - `${paths.design_system}/tokens-reference.md` — add / update token entries
166
+ when introducing new semantic tokens. Drift code: `DS_TOKENS_DRIFT`.
167
+
168
+ The four conditions defined in `design-system-protocol.md` are the SSOT —
169
+ reproduce them in your completion report under a `design_system_coherence:`
170
+ block listing which conditions applied and which artifacts you updated.
171
+ Omitting the block = the orchestrator treats the card as not done.
172
+
173
+ ### File Permissions (ENFORCED — no exceptions)
174
+ MAY EDIT — your files for this card:
175
+ [list from ownership map for this card]
176
+
177
+ MUST READ ONLY — shared dependencies:
178
+ [list from ownership map: files owned by other cards that this card reads]
179
+
180
+ FORBIDDEN:
181
+ - Do NOT edit any file outside the MAY EDIT list above
182
+ - Do NOT refactor unrelated code
183
+ - Do NOT add unrequested features or extra error handling beyond what's specified
184
+ - Do NOT modify test files unless the card explicitly requires it
185
+
186
+ ### Database Indexes (if card has `db_indexes` — or legacy `firestore_indexes` — field)
187
+ This card requires the following indexes shipped in the SAME commit as the query
188
+ code. Missing indexes degrade performance or break production (variant per
189
+ `stack.database` — see `framework/.claude/agents/coder.md § Database Index Invariant`).
190
+
191
+ [paste db_indexes entries from card YAML, formatted as:]
192
+ | Entity | Dialect | Fields | Kind | Query Location | PRD Ref |
193
+ |--------|---------|--------|------|----------------|---------|
194
+ | [entity] | [stack.database] | [field1 ASC, field2 DESC] | [composite/covering/GIN/GSI] | [file path] | [IDX-N] |
195
+
196
+ **Where to ship the index — pick the artifact matching `stack.database`:**
197
+
198
+ - `firestore` → append to `firestore.indexes.json` under `indexes[]`:
199
+ ```json
200
+ {
201
+ "collectionGroup": "<entity>",
202
+ "queryScope": "COLLECTION",
203
+ "fields": [
204
+ { "fieldPath": "<field1>", "order": "ASCENDING" },
205
+ { "fieldPath": "<field2>", "order": "DESCENDING" }
206
+ ]
207
+ }
208
+ ```
209
+ Stage `firestore.indexes.json` together with the query code.
210
+ - `postgres` / `supabase` / `mysql` / `sqlite` → add a migration file under the
211
+ project's migrations dir (`migrations/`, `db/migrate/`, `supabase/migrations/`,
212
+ `prisma/migrations/` per project convention) with `CREATE INDEX ...` or the
213
+ Prisma `@@index([...])` directive. For Supabase, RLS policies live in the same
214
+ migration when relevant.
215
+ - `mongodb` → add `db.<entity>.createIndex(...)` to the project's index-bootstrap
216
+ script (path declared in `.baldart/overlays/new.md § Schema Bootstrap`), or run
217
+ it as part of the migration command.
218
+ - `dynamodb` → update the table definition in the project's IaC (CDK / Terraform /
219
+ SAM / SST) with the matching GSI/LSI.
220
+ - Stack `none` or unset → if you still see `db_indexes` on the card, ask the user
221
+ to confirm the persistence target before staging anything.
222
+
223
+ Stage the index artifact alongside the query code in the SAME commit. Verification
224
+ (post-deploy index propagation) is handled in the Production Readiness Checklist
225
+ section below, branched by `stack.deployment`.
226
+
227
+ ### Expected Output Locations
228
+ [pre-fill from codebase-architect: for each requirement, the file:line where the change should go]
229
+
230
+ ### Project Anti-Patterns & NFRs (ENFORCED)
231
+ Violating these is a BLOCKER in code review.
232
+
233
+ Deprecated patterns — the orchestrator MUST fill this from the project's own AGENTS.md / MEMORY.md / coding-standards.md before sending the briefing. If the project documents none, write "None documented." Do NOT pass placeholder example text to the coder (it wastes tokens and confuses the agent):
234
+ [resolved list of this project's deprecated/anti-patterns, or "None documented."]
235
+
236
+ Performance MUST rules (adapt to your stack):
237
+ - Bounded reads on every database query (`.limit()` / equivalent)
238
+ - Cursor-based pagination, not offset
239
+ - No per-row fetches in loops — batch instead
240
+ - Database indexes declared in schema config (same commit as the query that needs them)
241
+
242
+ Security MUST rules:
243
+ - No stack traces in HTTP responses — generic messages + error codes only
244
+ - No PII in console.log or error tracking
245
+ - No hardcoded secrets — env vars only
246
+ - ALL non-public routes MUST use the project's auth middleware
247
+
248
+ Coding standards (agents/coding-standards.md):
249
+ - Use the project's canonical terminology (document it in coding-standards.md)
250
+ - Honor the design-system theming contract (e.g. text/background pairing rules)
251
+ - Honor the project's image-format and asset-pipeline rules
252
+
253
+ Design System SSOT (MANDATORY for UI cards):
254
+ - Master reference: `${paths.design_system}/INDEX.md` (component index + Canonical Authority
255
+ Matrix + Quick rules MUST). Read this BEFORE touching any UI file.
256
+ - For each UI component you modify or create, read `${paths.design_system}/components/<Name>.md`.
257
+ - Merchant-themed surfaces MUST follow the pairing rule in
258
+ `the project's theming pattern doc (listed in `.baldart/overlays/ui-design.md`)`.
259
+ - Motion MUST follow `the project's motion pattern doc (listed in `.baldart/overlays/ui-design.md`)` (reduced-motion variants
260
+ required). The coder agent's own hardening (see `.claude/agents/coder.md`) already enforces
261
+ this — this briefing is an explicit reminder for UI-touching cards.
262
+ - No hardcoded hex/shadow/border values in component styling — canonical tokens only.
263
+
264
+ ### Batch Lessons (from prior cards in this batch)
265
+ [If tracker `## Lessons Learned` has entries, paste them here.
266
+ If empty or first card: "First card — no lessons yet."]
267
+
268
+ ### MANDATORY: Numbered Requirements Checklist (anti-skip measure)
269
+ Before you start coding, print this checklist to confirm you see ALL requirements:
270
+ ```
271
+ REQUIREMENTS TO IMPLEMENT:
272
+ [x] R1: [requirement text]
273
+ [x] R2: [requirement text]
274
+ ...
275
+ ACCEPTANCE CRITERIA TO SATISFY:
276
+ [x] AC1: [criterion text]
277
+ [x] AC2: [criterion text]
278
+ ...
279
+ CONDITIONAL / BINARY-OUTCOME ITEMS (from unknowns + requirements with "verify if / if missing"):
280
+ [ ] C1: [item text] → Branch to take: A (found+implement) | B (missing+TODO)
281
+ [ ] C2: [item text] → Branch to take: A (found+implement) | B (missing+TODO)
282
+ ...
283
+ Total: N requirements + M acceptance criteria + K conditional items = X items
284
+ ```
285
+ You MUST implement ALL items. Skipping even one is a failure.
286
+ For each conditional item: verify actively, then produce the correct branch artifact.
287
+
288
+ ### MANDATORY: Completion Report
289
+ When implementation is done, output this block in EXACTLY this format.
290
+ This report is NOT optional — if you omit it, the orchestrator will flag
291
+ your implementation as incomplete and spawn a fix agent.
292
+
293
+ ```completion-report
294
+ card: <CARD-ID>
295
+ requirements:
296
+ - id: 1
297
+ text: "[verbatim requirement text]"
298
+ status: done | not_implemented
299
+ evidence: "src/path/to/file.ts:LINE_NUMBER"
300
+ notes: "[if not_implemented: explain why]"
301
+ - id: 2
302
+ [repeat for each requirement]
303
+ acceptance_criteria:
304
+ - id: 1
305
+ text: "[verbatim criterion text]"
306
+ status: done | not_implemented
307
+ evidence: "src/path/to/file.ts:LINE_NUMBER"
308
+ items_total: [N]
309
+ items_done: [M]
310
+ items_skipped: [list of skipped item IDs, or "none"]
311
+ conditional_items:
312
+ - item: "[verbatim text of conditional requirement]"
313
+ branch_taken: "A-found" | "B-missing"
314
+ evidence: "src/path/to/file.ts:LINE_NUMBER"
315
+ notes: "[if B-missing: exact TODO location and text left; otherwise omit]"
316
+ ```
317
+
318
+ Per-requirement / per-AC `status` is BINARY: `done` or `not_implemented` (the legacy
319
+ `partial`/`blocked` values are removed — see `coder.md`). The orchestrator re-derives the
320
+ finer Done/Partial/Missing classification itself in Phase 2.5 by reading the code; the coder
321
+ report is a starting signal, not ground truth.
322
+ If `items_skipped` is not "none", you MUST explain why each was skipped.
323
+ The orchestrator treats any `not_implemented` or skipped item as a gap requiring a fix agent.
324
+ `conditional_items` MUST list every binary-outcome item from the card. If the card has no
325
+ conditional requirements, omit the field. `branch_taken: B-missing` MUST have a `notes`
326
+ field with the exact file path and line where the TODO comment was written.
327
+ ```
328
+
329
+ 8. **Run the verification gates and CAPTURE their output to disk** (so step 9 can pass it to a fix agent) — **redirect, never `tee`/stream inline** (per § "Context economy" → Gate-output discipline). Each is its own gate:
330
+ ```bash
331
+ cd <worktree-path>
332
+ npm run lint > /tmp/lint-<CARD-ID>.txt 2>&1; echo "lint:$?"
333
+ # tsc is its OWN blocking gate — run it ONLY when stack.language includes "typescript"
334
+ # (do NOT assume `npm run build` covers tsc — Next.js dev builds and many setups skip it):
335
+ npx tsc --noEmit > /tmp/tsc-<CARD-ID>.txt 2>&1; echo "tsc:$?" # guard: when stack.language includes "typescript"
336
+ npm test > /tmp/test-<CARD-ID>.txt 2>&1; echo "test:$?" # if tests exist
337
+ npm run build > /tmp/build-<CARD-ID>.txt 2>&1; echo "build:$?"
338
+ ```
339
+ The `echo "<gate>:$?"` lines surface only the **exit code** inline — the full log stays on disk. When `stack.language` does NOT include `typescript`, skip the `tsc` line (no equivalent gate). Capturing to `/tmp/*-<CARD-ID>.txt` guarantees the failing output is available for step 9. On a non-zero exit, read a **bounded** extract only (`tail -n 30 /tmp/<gate>-<CARD-ID>.txt`), never the whole log — do NOT run the gates without redirect-to-file capture.
340
+ 9. **If any check fails**: categorize the error (`lint | TypeScript | test | build`), log it in the tracker as `retry-cause: <category>`.
341
+ **Code-recovery check (MUST do before rewriting)**: before spawning a fix agent or rewriting code, check whether lint-staged or a pre-commit hook removed code that is actually needed (e.g. a field "unused" at commit time but consumed by later code). Inspect the captured diff and `git diff`/`git log -p` for the worktree branch and restore the needed code by an explicit file write. **Do NOT `git stash pop` inside the worktree** — `refs/stash` is globally shared across worktrees (`$GIT_COMMON_DIR`), so a stash created or popped in a worktree can corrupt another worktree's state (see Phase 4 WORKTREE COMMIT RULE). If you need to set work aside in a worktree, make a clearly-labelled WIP commit and record its hash in the tracker for later squash, never a stash.
342
+ When spawning a fix agent: scope it exclusively to this card's Edit-allowed files (from `## File Ownership Map`) — it MUST NOT touch files owned by other cards. Pass the fix agent: the **path** to the captured gate log (`/tmp/<gate>-<CARD-ID>.txt` — it Reads the log itself; do NOT inline-paste the full log into its prompt), the error category, and the explicit list of files it may edit. Do NOT ask the user — just fix and re-run. Fix the code, not the tests (unless the test itself is wrong). Repeat up to **3 times**. **Stuck-loop guard**: compare the failing error's fingerprint (file:line + message) between retries; if the SAME error reproduces on 2 consecutive retries, the fix is not converging (often a constraint outside the card's ownership map) — stop early, log `[STUCK-LOOP] <error>` in `## Issues & Flags`, and escalate to the user rather than burning the 3rd retry on an identical failure.
343
+ 10. If still failing after 3 retries (or on a stuck loop), log the failure in `## Issues & Flags` and ask the user before continuing.
344
+ 11. **Update tracker**: phase = "2-implement DONE", log files changed (short list), retry count, retry causes (e.g., `"2 retries: TypeScript x1, lint x1"`), and test results (new + existing test count, pass/fail).
345
+ 11b. **File diff gate** — verify the coder only touched its allowed files. The coder commits its work (per `coder.md`), so a bare `git diff --name-only HEAD` is vacuous (clean tree). Detect what the card's commits actually changed against the trunk, with an uncommitted-fallback:
346
+ ```bash
347
+ cd <worktree-path>
348
+ git diff --name-only "$TRUNK...HEAD" 2>/dev/null || git diff --name-only HEAD~1..HEAD
349
+ ```
350
+ (`$TRUNK` is the trunk branch resolved in Phase 0; if the worktree branched from a different base, the `HEAD~1..HEAD` fallback covers the last commit.)
351
+ Compare against this card's allowed files in `## File Ownership Map`.
352
+ - **All within allowed set** → proceed to Phase 2.5.
353
+ - **Any file outside allowed set** → log the violation in `## Issues & Flags` (`"unauthorized file: <path>"`), then spawn a **targeted revert coder agent** with instruction: "Revert ONLY these files to their pre-commit state. Do not touch any other file: [list unauthorized files]". Re-run build + lint to confirm clean state after revert. Update tracker with revert outcome, then proceed to Phase 2.5.
@@ -0,0 +1,175 @@
1
+ <!-- Modulo on-demand di /new — NON un file installato a sé. Caricato via Read dalla § "Routing" del SKILL.md core. -->
2
+
3
+ > **Modulo `/new`** — eseguilo, poi torna al core § "Routing" per la fase successiva. I `§ "..."` (Context economy, Context Tracking, Trivial-card fast-lane, Risk-signal detector, Fix Application Log) vivono nel **core SKILL.md**.
4
+
5
+ ## Phase 6 — Post-batch merge & cleanup (delegated to worktree-manager skill)
6
+
7
+ **→ Visibility (batch transition)**: TaskUpdate `Merge & cleanup` → `in_progress` and emit a Progress Bar per § "Progress Visibility". Mark it → `completed` at the end of Phase 6c (merge done, worktree removed, workspace reconciled).
8
+
9
+ After the final review passes AND all cards are committed in the worktree, delegate the entire merge and cleanup to the **worktree-manager** skill (`/mw` in programmatic mode):
10
+
11
+ 1. **BEFORE invoking /mw** — verify no uncommitted files remain in the worktree:
12
+ ```bash
13
+ cd <worktree-path>
14
+ git status --porcelain
15
+ ```
16
+ If ANY uncommitted files exist (staged, unstaged, or untracked), commit them NOW with `[safety] Auto-commit remaining files before merge`. Do NOT proceed to `/mw` with a dirty worktree — files WILL be lost during rebase.
17
+ 2. Invoke `/mw` with:
18
+ - The worktree path and branch from the tracker
19
+ - `checksAlreadyPassed: true` (final review + QA already validated the build)
20
+ - All card IDs for the commit message
21
+ 3. The skill handles: safety commit of any remaining uncommitted files (step 3), rebasing onto the latest trunk (`$TRUNK` = `git.trunk_branch`) (step 4b — auto-resolves doc conflicts, stops on code conflicts), merging into the trunk via the configured `git.merge_strategy` (step 4c — `pr` uses `gh pr merge`, `local-push` does a direct FF push to `origin/$TRUNK`; NEITHER runs `git checkout` of the trunk on the main repo, respecting the absolute terminal-isolation rule), post-merge verification, worktree removal, registry cleanup, and remote branch deletion.
22
+ 4. **If code merge conflicts** → the skill STOPs and reports. Doc-only conflicts (ssot-registry.md, project-status.md, etc.) are auto-resolved by keeping both sides.
23
+ 5. **If post-merge build fails** → the skill STOPs and keeps the worktree intact for investigation.
24
+ 6. Record the merge commit hash and result in the tracker. **Also record the merge timestamp** under `## Worktree Merges` (`merge_ts: <ISO-8601>`): for `local-push` use the local merge-commit time (`git log --format=%ci <hash>`); for `pr` (`gh pr merge`) use the PR merge completion time, or note `merge_ts: pending` if GitHub has not yet finalized the merge. Phase 8's `cycle_time_mins` reads THIS field as the strategy-independent end anchor (never a raw hash that may be the PR HEAD).
25
+
26
+ ### Phase 6b — Backlog Card Status Reconciliation (MANDATORY — ZERO TOLERANCE)
27
+
28
+ **This gate runs IMMEDIATELY after Phase 6 merge completes, BEFORE presenting any summary to the user. It is NON-SKIPPABLE.**
29
+
30
+ The most common failure mode is leaving cards IN_PROGRESS after merge. This creates SSOT drift and confuses downstream agents (codebase-architect, doc-reviewer). This gate prevents that.
31
+
32
+ **Steps:**
33
+
34
+ 1. **Read the tracker file** to get the full list of card IDs in the batch.
35
+ 2. **For EACH card in the batch**, read its backlog YAML (`${paths.backlog_dir}/<CARD-ID>.yml`) and check the `status` field:
36
+ - If `status: DONE` → OK, skip.
37
+ - If `status` is anything else (`IN_PROGRESS`, `READY`, `TODO`, etc.) → **FORCE UPDATE to DONE immediately**.
38
+ 3. **Early exit — all cards already DONE**: if step 2 found ALL cards are already `status: DONE`, skip steps 4-5 entirely. Log in the tracker:
39
+ ```
40
+ ## Phase 6b — Status Reconciliation
41
+ Cards checked: N
42
+ Already DONE: N (all cards marked DONE by coder agents)
43
+ Force-updated to DONE: 0
44
+ Reconciliation commit: not needed
45
+ ```
46
+ Proceed directly to step 6.
47
+ 4. **Force update procedure** for non-DONE cards:
48
+ ```bash
49
+ # In the MAIN repo (not worktree — worktree is already cleaned up)
50
+ cd <main-repo-path>
51
+ ```
52
+ Edit the backlog YAML: set `status: DONE`, add `completed_date: <today>`, add implementation note: `"Marked DONE by post-merge reconciliation gate (Phase 6b)"`.
53
+ 5. **If ANY card was force-updated**: commit in the main repo with these precautions:
54
+ a. **Clear stale COMMIT_LOCK** (common when coder agents crash or timeout):
55
+ ```bash
56
+ # Remove any stale COMMIT_LOCK from main repo or worktrees
57
+ rm -f <main-repo-path>/.git/COMMIT_LOCK 2>/dev/null
58
+ find <main-repo-path>/.git/worktrees -name COMMIT_LOCK -delete 2>/dev/null || true
59
+ ```
60
+ b. **Stage backlog YAMLs AND related docs** (pre-commit hook requires doc updates):
61
+ ```bash
62
+ # Stage only the force-updated backlog YAMLs
63
+ git add ${paths.backlog_dir}/CARD-001.yml ${paths.backlog_dir}/CARD-002.yml
64
+ # If ssot-registry.md was modified during the batch, stage it too
65
+ # (doc-freshness hook blocks commits that touch ${paths.backlog_dir}/ without ssot-registry.md)
66
+ git diff --name-only ${paths.references_dir}/ssot-registry.md && git add ${paths.references_dir}/ssot-registry.md || true
67
+ ```
68
+ c. **Commit with explicit file list**:
69
+ ```bash
70
+ git commit -m "[BATCH] Mark cards DONE — post-merge reconciliation"
71
+ ```
72
+ d. **If commit fails due to pre-commit hook** (lint-staged, doc-freshness): check the error.
73
+ - If "no staged files" → all changes were already committed. Log as "not needed".
74
+ - If doc-freshness requires ssot-registry.md → update its `last_verified_from_code` date, stage it, and retry.
75
+ - If COMMIT_LOCK → re-run step 5a and retry.
76
+ - Do NOT retry more than 2 times. Log the failure and move on.
77
+ 6. **Update tracker** with reconciliation results:
78
+ ```
79
+ ## Phase 6b — Status Reconciliation
80
+ Cards checked: N
81
+ Already DONE: M
82
+ Force-updated to DONE: K [list card IDs]
83
+ Reconciliation commit: <hash> (or "not needed")
84
+ ```
85
+ 7. **HALT condition**: if a card cannot be updated (e.g., file read error, YAML parse error), log it in `## Issues & Flags` and continue with the remaining cards. Report the failure in the final summary.
86
+
87
+ **Why this exists**: Agents frequently skip the DONE marking in Phase 4 (step 27) due to context compaction, commit failures that interrupt the flow, or team mode where Step D.6 gets lost. This gate is the safety net that catches ALL of these cases.
88
+
89
+ ### Phase 6c — Workspace Hygiene Post-merge (BLOCKING — non-skippable)
90
+
91
+ **Why this exists**: FEAT-0006 incident closed the batch leaving the main repo with an unpushed orphan commit (`c9d41f9`) and local `develop` diverged from `origin/develop` for a manual fix. This phase closes the loop opened by Phase 0: it verifies the main repo is clean and synchronized, parses the `[SYNC-DEFERRED]` marker emitted by `/mw` when HEAD ≠ `$TRUNK`, and restores any stash saved in Phase 0.
92
+
93
+ **Auto Mode does NOT override this phase.** Every `AskUserQuestion` below is non-bypassable.
94
+
95
+ **Steps:**
96
+
97
+ 1. **Fetch remote state**:
98
+ ```bash
99
+ git -C "$MAIN" fetch origin --quiet
100
+ ```
101
+
102
+ 2. **Parse `mw-docs` sync markers** — scan the captured stdout from every `/mw` invocation in this batch (kept in the tracker under `## Worktree Merges`):
103
+ - **`[SYNC-DEFERRED] main repo HEAD=<branch>`** (main repo HEAD ≠ `$TRUNK`) — surface via `AskUserQuestion`:
104
+ - Question: `"Una o più merge worktree hanno deferito la sincronizzazione di local $TRUNK (main repo HEAD non era $TRUNK). Come riconcilio adesso?"`
105
+ - Options: `[Ora HEAD è $TRUNK → ff-pull adesso]` / `[Lascia deferred (riconcilio io manualmente)]` / `[Mostra dettagli e fammi decidere]`.
106
+ - **`[SYNC-NEEDS-DECISION] …`** (HEAD was `$TRUNK` but the ff was blocked by a **foreign** uncommitted file, or by a divergence/rebase conflict `/mw` would not auto-commit) — this is NEVER a passive note. Surface via `AskUserQuestion`:
107
+ - Question: `"Il $TRUNK locale non si è sincronizzato: <dettaglio dal marker>. Come chiudo?"`
108
+ - Options: `[Committa tu adesso (descrivi cosa)]` / `[Lo gestisco io — chiudi senza sync locale]` / `[Mostrami il diff e decidiamo]`.
109
+ - **No marker** — `/mw` fast-forwarded (or auto-reconciled the framework telemetry log); nothing to parse.
110
+
111
+ 3. **Clean-tree assertion (BLOCKING — same framework-managed partition as Phase 0 step 3)**:
112
+ ```bash
113
+ POST_DIRTY="$(git -C "$MAIN" status --porcelain \
114
+ | grep -vE "^.{3}\"?${METRICS}/" \
115
+ | grep -vE "^.{3}\"?${METRICS}\"?$" \
116
+ | grep -vE "^.{3}\"?\.baldart/(generated/|state\.json|skill-conflicts\.json)" \
117
+ | grep -v '^[[:space:]]*$')"
118
+ ```
119
+ (Apply the **same** partition as Phase 0 step 3: telemetry under `$METRICS` and BALDART-managed `.baldart/` artifacts — `generated/`, `state.json`, `skill-conflicts.json`, but NOT `overlays/` — are auto-ignored, never surfaced. Otherwise a reinstall mid-batch would block the close on its own output.)
120
+ If `$POST_DIRTY` is non-empty AND no Phase 0 stash is pending → invoke `AskUserQuestion`:
121
+ - Question: `"Main repo ha modifiche non committate dopo il batch (non c'era stash di Phase 0). Cosa faccio?"`
122
+ - Options: `[Mostrami il diff e fammi decidere]` / `[Stash adesso con label baldart-post-batch-<timestamp>]` / `[Lascia così (lo gestisco io)]` / `[Halt]`.
123
+
124
+ 4. **Divergence assertion (BLOCKING — this catches the FEAT-0006 orphan-commit pattern)**. `$MAIN` and `$TRUNK` are read from the tracker (`## Worktree` section); HALT if either is absent/empty.
125
+ ```bash
126
+ read BEHIND AHEAD <<< "$(git -C "$MAIN" rev-list --left-right --count "origin/$TRUNK...$TRUNK")"
127
+ ```
128
+ Compute `$GENUINE_AHEAD` / `$GENUINE_LIST` / `$FW_SKIPPED` with the **exact same reclassification loop as Phase 0 step 4** (framework-management commits — subtree merges into `.framework/`, `baldart update`/`add` chores, `.framework/`-only commits — are auto-ignored, NOT orphan commits). Then branch on `$BEHIND` and `$GENUINE_AHEAD`:
129
+ - **`BEHIND=0` and `GENUINE_AHEAD=0`** — synchronized. If `$FW_SKIPPED > 0`, log `Divergence: $FW_SKIPPED framework-management commits ahead (auto-ignored)` and proceed. No `AskUserQuestion`.
130
+ - **Ahead only (`GENUINE_AHEAD > 0`, genuine unpushed commits)** — invoke `AskUserQuestion`:
131
+ - Question: `"Local $TRUNK ha $GENUINE_AHEAD commit genuini non pushati su origin/$TRUNK al termine del batch (esclusi $FW_SKIPPED di gestione framework). Lista: <git show -s --oneline $GENUINE_LIST>. Cosa faccio?"`
132
+ - Options: `[Push adesso (git push origin $TRUNK)]` / `[Cherry-pick selettivo su origin/$TRUNK]` / `[Reset --hard origin/$TRUNK (richiede conferma esplicita, DISTRUTTIVO)]` / `[Halt con stato preservato]`.
133
+ - On `Reset --hard origin/$TRUNK` re-ask `AskUserQuestion` to confirm: `"Confermi reset --hard? I commit <list> verranno persi se non pushati altrove."` Solo dopo conferma esplicita esegui il reset.
134
+ - **Behind only (`BEHIND > 0`, `GENUINE_AHEAD=0`)** — invoke `AskUserQuestion`:
135
+ - Question: `"Local $TRUNK è behind origin/$TRUNK di $BEHIND commit (qualcuno ha pushato durante il batch). Faccio ff-pull adesso?"`
136
+ - Options: `[Fast-forward pull]` / `[Lascia behind (lo gestisco io)]`.
137
+ - **Diverged both ways (`BEHIND > 0` and `GENUINE_AHEAD > 0`)** — invoke `AskUserQuestion`:
138
+ - Question: `"Local $TRUNK è diverged ($BEHIND behind, $GENUINE_AHEAD genuine ahead). Servono entrambe le riconciliazioni. Come procedo?"`
139
+ - Options: `[Rebase local $TRUNK su origin/$TRUNK]` / `[Mostrami i commit locali e decidiamo]` / `[Halt]`.
140
+
141
+ 5. **Restore Phase 0 stash (if any)** — read the tracker for `## Workspace Snapshot: <message-label>`:
142
+ - If the snapshot is `dirty-tree override` → skip restore, log "user retained dirty-tree responsibility".
143
+ - If it is a stash MESSAGE label (e.g. `baldart-new-phase0-<FIRST-CARD-ID>-<timestamp>`) — resolve the stash by its message, NEVER by positional `stash@{N}` (other stashes may have shifted the index since Phase 0). This runs on `$MAIN` (the main checkout, not a worktree), so a stash op here is safe:
144
+ ```bash
145
+ REF="$(git -C "$MAIN" stash list | grep -F "<message-label>" | head -1 | cut -d: -f1)"
146
+ [ -n "$REF" ] && git -C "$MAIN" stash pop "$REF" \
147
+ || echo "stash '<message-label>' not found — was it already restored?"
148
+ ```
149
+ On conflict, do NOT silent-drop — invoke `AskUserQuestion`:
150
+ - Question: `"Restore dello stash di Phase 0 ha generato conflitti. Lo stash è ancora presente (NON eliminato). Come procedo?"`
151
+ - Options: `[Lascia lo stash + apri istruzioni per merge manuale]` / `[Mostrami il conflitto inline]` / `[Halt]`.
152
+
153
+ 6. **Log and exit**:
154
+ ```
155
+ ## Phase 6c — Workspace Hygiene Post-merge
156
+ Status: PASS
157
+ Clean-tree: <yes | resolved: stashed | user-retained>
158
+ Divergence (local…origin/$TRUNK): <0\t0 | resolved: pushed/cherry-picked/ff-pulled/rebased>
159
+ Sync-deferred markers: <none | reconciled | user-retained>
160
+ Phase 0 snapshot restore: <n/a | popped clean | conflict-deferred-to-user>
161
+ Completed: <timestamp>
162
+ ```
163
+ If any step ended in HALT, set `Status: HALT` and report — Phase 7 must NOT start with an unclean main repo unless the user explicitly chose `[Lascia così]`.
164
+
165
+ 7. **Anti-bypass guard** — like Phase 0, before exiting Phase 6c re-read the tracker section you just wrote. Missing `## Phase 6c` → refuse to proceed to Phase 7. There is no `--skip-phase-6c` flag; treat any urge to skip as a self-correction trigger.
166
+
167
+ ### Fail-safe rules (enforced by worktree-manager skill)
168
+ - **Never `git checkout`, `git switch`, `git checkout -b`, or `git branch` on the main repo** from inside this orchestrator. The main repo is shared across parallel terminals. Use the configured `git.merge_strategy` for trunk merges and `git -C <main> pull --ff-only` (only when `HEAD = $TRUNK` already) for sync. See `/mw` step 4c.
169
+ - Never merge into a release/production branch — only into the integration trunk (`$TRUNK` = `git.trunk_branch`), via the configured `git.merge_strategy` (NOT local checkout).
170
+ - Never force push to the trunk. `--force-with-lease` on feature branches after rebase is allowed.
171
+ - Never delete a branch before successful merge verification.
172
+ - Never remove a worktree before confirming the trunk is stable post-merge.
173
+ - Stop execution immediately if any command fails.
174
+
175
+ ---
@@ -0,0 +1,72 @@
1
+ <!-- Modulo on-demand di /new — NON un file installato a sé. Caricato via Read dalla § "Routing" del SKILL.md core. -->
2
+
3
+ > **Modulo `/new`** — eseguilo, poi torna al core § "Routing" per la fase successiva. I `§ "..."` (Context economy, Context Tracking, Trivial-card fast-lane, Risk-signal detector, Fix Application Log) vivono nel **core SKILL.md**.
4
+
5
+ ## Phase 8 — Metrics Log (MONITORING SIGNAL — non-blocking — runs after Phase 7)
6
+
7
+ After the Production Readiness Checklist is complete, log this batch run to the skill
8
+ effectiveness registry. This enables tracking of first-attempt success rate, actionability,
9
+ and QA quality over time. This is a telemetry write, NOT a gate — it never blocks the batch (see
10
+ the fail-safe note at the end of the phase). It was previously labelled "MANDATORY", but since it
11
+ has no blocking consequence the honest label is "monitoring signal".
12
+
13
+ **Steps:**
14
+
15
+ 1. Read the batch tracker file (`/tmp/batch-tracker-<FIRST-CARD-ID>.md`) to extract — every field below has a NAMED upstream producer; read the structured key, do NOT re-parse prose:
16
+ - Start timestamp (from `Started:` field)
17
+ - Worktree-creation timestamp (from `## Worktree` — the time Phase 0/Pre-flight created the worktree; the strategy-independent cycle-time anchor, see step 2)
18
+ - Card list and total count
19
+ - For each completed card:
20
+ - `fix_cycles` (from the per-phase retry counts logged in `## Completed Cards`)
21
+ - `qa_profile`, `qa_verdict` (Phase 3.5 step 25 / D.4 QA line)
22
+ - `qa_first_attempt` (Phase 3.5 step 25 named flag — `pass`/`fail`/`n/a`)
23
+ - `doc_gaps: found=<N> fixed=<M>` (Phase 3 step 15 / D.4a named counter)
24
+ - Batch-level `review_findings: total / verified / blockers` (Final Review F.5 step 13 named counters — the canonical source for `findings_total` / `findings_verified` / `blockers_count`)
25
+ - Merge commit hash (and, when `git.merge_strategy: pr`, the PR merge timestamp recorded by Phase 6 — see step 2)
26
+
27
+ If any named field is absent (e.g. a legacy tracker), record `0`/`n/a` for that field and append `metrics_gaps: <field>` to the JSONL row's notes — do NOT invent a value by re-parsing free-form prose.
28
+
29
+ 2. Compute aggregate metrics:
30
+ - `first_attempt_success_rate`: cards with fix_cycles == 0 / total_cards
31
+ - `mean_fix_cycles`: mean of per-card fix cycle counts
32
+ - `qa_profiles`: count of {skip, light, balanced, deep}
33
+ - `qa_pass_first_attempt_rate`: cards with `qa_first_attempt == pass` / cards where qa ran (exclude `n/a` SKIP/LIGHT cards from the denominator) — read the named flag from step 1, do NOT re-infer from `## Fix Application Log` rows
34
+ - `findings_total` / `findings_verified`: from the batch-level `review_findings` named counter (Final Review), NOT a per-card re-sum (which would conflate QA gate counts with codex review findings)
35
+ - `actionability_rate`: findings_verified / findings_total (conservative proxy — a lower-bound on reviewer precision, since some VERIFIED findings end as NEEDS_MANUAL_CONFIRMATION and never produce a fix; 0 if no findings)
36
+ - `severity_p0_pct`: blockers_count / findings_total (0 if no findings)
37
+ - `cycle_time_mins`: minutes from the **strategy-independent anchor** to batch completion. Use the **worktree-creation timestamp** (Phase 0/Pre-flight) as the start, NOT `Started:` (which precedes workspace hygiene) and NOT a merge-commit hash (which varies by `git.merge_strategy` — under `pr` the merge commit is created asynchronously by GitHub CI and may not exist locally at Phase 8 time). End anchor: read the `merge_ts` field Phase 6 step 6 recorded under `## Worktree Merges` (it is already strategy-resolved there — local merge-commit time for `local-push`, PR merge completion time for `pr`). If `merge_ts: pending` (a `pr` merge GitHub has not yet finalized), fall back to "now" (`date -u`). Never recompute from a raw merge-commit hash that may be the PR HEAD rather than the actual merge commit.
38
+
39
+ > `$METRICS` = `paths.metrics` from `baldart.config.yml` (default `docs/metrics`). All paths below resolve under `$METRICS` — never a hardcoded `docs/metrics`.
40
+
41
+ 3. **Assemble the full JSONL record in memory** (single atomic append — JSONL is append-only, so the row must be COMPLETE before it touches the file). If `STATS=true` (step 6), the `"cost"` key is part of THIS record — do NOT write a baseline row now and rewrite it later (a mid-file rewrite corrupts the JSONL; there is no `sed -i`/`jq` rewrite step in this skill). When `STATS=true`, defer the single append to step 6 after the cost object is computed; when `STATS=false`, append now. The record shape:
42
+
43
+ ```json
44
+ {"ts":"<ISO-8601-UTC>","skill":"new","run_id":"batch-<FIRST-CARD-ID>","cards":["FEAT-XXX"],"total_cards":N,"first_attempt_success_rate":0.0,"mean_fix_cycles":0.0,"qa_profiles":{"skip":0,"light":0,"balanced":0,"deep":0},"qa_pass_first_attempt_rate":0.0,"findings_total":0,"findings_verified":0,"actionability_rate":0.0,"severity_p0_pct":0.0,"doc_gaps_found":0,"doc_gaps_fixed":0,"cycle_time_mins":0,"worktree_branch":"","merge_commit":""}
45
+ ```
46
+
47
+ Use `date -u +%Y-%m-%dT%H:%M:%SZ` for the timestamp. Append the assembled line ONCE with `echo '<full-record>' >> "$METRICS/skill-runs.jsonl"` via Bash — never append a partial row then mutate it.
48
+
49
+ 4. Copy the batch tracker to archive:
50
+
51
+ ```bash
52
+ cp /tmp/batch-tracker-<FIRST-CARD-ID>.md "$METRICS/archive/"
53
+ ```
54
+
55
+ 5. Note in tracker: `## Metrics Log: WRITTEN (run_id: batch-<FIRST-CARD-ID>)`
56
+
57
+ 6. **Session token telemetry (ONLY when `STATS=true`)** — if the run was invoked with `-stats` / `--stats`:
58
+
59
+ Measure REAL per-role token + wall-clock cost by post-processing the session transcripts (zero model-token cost — runs in Bash). Invoke:
60
+
61
+ ```bash
62
+ node .framework/framework/scripts/analyze-session-tokens.js \
63
+ --skill new --run-id "batch-<FIRST-CARD-ID>" --out-dir "$METRICS"
64
+ ```
65
+
66
+ The script reads `$CLAUDE_CODE_SESSION_ID` from the environment and derives the transcript paths itself (do NOT pass `--session` / `--project-dir` — those are test-only overrides). It writes a human-readable breakdown to `$METRICS/sessions/batch-<FIRST-CARD-ID>.md` and prints a summary plus a final `TELEMETRY_JSON=<json>` line.
67
+
68
+ Parse the `TELEMETRY_JSON=` line and add a `"cost"` key (holding that JSON object: `by_role` + `totals` + `run_wall_ms`) to the in-memory record from step 3, then **append the complete record ONCE** to `$METRICS/skill-runs.jsonl` (this is the deferred single atomic append from step 3 — there is NO already-written row to rewrite, so JSONL semantics stay intact: exactly one line per `run_id`). If the script printed `stats: SKIPPED (...)` instead, set `"cost":{"skipped":"<reason>"}` and append the single record. Echo the script's summary to the user so they see the per-role breakdown.
69
+
70
+ **If `$METRICS/skill-runs.jsonl` does not exist**: create it first with `mkdir -p "$METRICS" && touch "$METRICS/skill-runs.jsonl"`.
71
+ **If batch tracker is missing or unreadable**: log "Metrics Log: SKIPPED (tracker not found)" and proceed without blocking.
72
+ **This phase is NON-BLOCKING** — if it fails for any reason, do not abort the run. The `-stats` step in particular is best-effort: the script is fail-safe (always exits 0), so never let it block the commit.