baldart 4.11.0 → 4.13.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,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.
@@ -0,0 +1,232 @@
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 7 — Production Readiness Checklist
6
+
7
+ After Phase 6 completes (or after the final summary report if Phase 6 is deferred), present a **Production Readiness Checklist** — a clear list of all manual or infrastructure actions required to launch the implemented changes in production.
8
+
9
+ ### How to detect items
10
+
11
+ Scan ALL files changed across the batch (use the tracker's completed cards + `git diff` against the base branch) and check for the items below.
12
+
13
+ **The "Action to report" column shows the command for the deployment target
14
+ matching `stack.deployment` in `baldart.config.yml`.** When `stack.deployment`
15
+ is empty, infer from `stack.database` + presence of config files
16
+ (`vercel.json`, `firebase.json`, `wrangler.toml`, `fly.toml`) and fall back to
17
+ asking the user.
18
+
19
+ | Category | Detection signal | Action to report (per `stack.deployment`) |
20
+ |----------|-----------------|-------------------------------------------|
21
+ | **Database indexes** | New/modified index config file (`firestore.indexes.json`, SQL migration with `CREATE INDEX`, `prisma migrate` artifact, `db.createIndex` call, CDK/Terraform GSI) | firebase: `firebase deploy --only firestore:indexes`; vercel+postgres: run migrations during deploy; aws+dynamodb: apply CDK/Terraform; generic: run the project's migration command |
22
+ | **Persistence-layer access rules** | New/modified `firestore.rules`, Supabase RLS migration, Mongo validator, DynamoDB IAM policy | firebase: `firebase deploy --only firestore:rules`; supabase: `supabase db push`; aws+dynamodb: apply IAM via CDK/Terraform |
23
+ | **Storage access rules** | New/modified `storage.rules` or equivalent bucket policy | firebase: `firebase deploy --only storage:rules`; aws: apply S3 bucket policy; gcp: apply GCS IAM |
24
+ | **Environment variables** | New `process.env.*` references not in the base branch, new entries in `.env.example` or `.env.local` | 1. Add to the deployment platform (Vercel/Firebase/Cloudflare/AWS Param Store) — list each var + target environments 2. Update `${paths.references_dir}/env-vars.md` Change Log |
25
+ | **Feature-flag / remote config** | New flag keys in code, new `remoteConfig` calls, growthbook/launchdarkly keys | Add keys in the project's flag system (Firebase Remote Config / Growthbook / LaunchDarkly / Unleash) |
26
+ | **Auth provider configuration** | New auth provider initialization or scope change | Configure in the auth provider console matching `stack.auth_provider` (Firebase Console / Supabase dashboard / Clerk dashboard / Auth0 / Cognito) |
27
+ | **Scheduled functions / cron** | New or modified cron/scheduled jobs | firebase: `firebase deploy --only functions`; vercel: deploy includes cron from `vercel.json`; aws: deploy EventBridge rule + Lambda |
28
+ | **Database migrations** | New {entities}, field renames, data backfills referenced in code or ADRs | Run the project's migration command — varies by stack (firebase admin script / `supabase db push` / `prisma migrate deploy` / Mongo migration tool / SQL migrator) |
29
+ | **New API endpoints** | New route files (`src/app/api/` Next.js, `app/routes/api.*` Remix, `src/routes/api/+server.ts` SvelteKit, etc.) | Verify CORS/auth config; update API docs if public |
30
+ | **Third-party services** | New API keys, webhook URLs, or external service integrations | Configure in provider dashboard + add secrets to the deployment platform |
31
+ | **DNS / domain changes** | Hosting or redirect config changes | Update DNS records on registrar; update domain settings on the deployment platform |
32
+ | **Package upgrades with breaking changes** | Major version bumps in `package.json` / `pyproject.toml` / `Gemfile` | Verify compatibility; check migration guides |
33
+
34
+ ### Output format
35
+
36
+ Present the checklist as a clearly formatted section in the final report.
37
+ The example below assumes `stack.deployment: firebase` + `stack.database: firestore`;
38
+ adapt commands and tags to the project's resolved stack.
39
+
40
+ ```
41
+ ## Production Readiness Checklist
42
+
43
+ ### Required before deploy
44
+ 1. **[DB Indexes — firestore]** Deploy composite indexes
45
+ - Command: `firebase deploy --only firestore:indexes`
46
+ - Reason: New compound query on `<entity>` (`<field1>` + `<field2>` + `<field3>`)
47
+
48
+ 2. **[Environment Variable]** Add `<VAR_NAME>` to the deployment platform
49
+ - Go to: <Vercel/Firebase/Cloudflare/AWS> > Project Settings > Environment Variables
50
+ - Required for: Production, Preview
51
+ - Value: (obtain from <provider> dashboard)
52
+
53
+ 3. **[DB Access Rules — firestore]** Deploy updated security rules
54
+ - Command: `firebase deploy --only firestore:rules`
55
+ - Reason: New {entity} `<name>` access rules added
56
+
57
+ # Equivalent commands for other stacks:
58
+ # stack.database: supabase → `supabase db push` (RLS migration)
59
+ # stack.database: postgres → `prisma migrate deploy` (or project migration tool)
60
+ # stack.database: mongodb → run the index/validator bootstrap script
61
+ # stack.database: dynamodb → `cdk deploy` / `terraform apply` for the table+GSI
62
+
63
+ ### No action needed
64
+ - No new scheduled functions
65
+ - No database migrations
66
+ - No DNS changes
67
+
68
+ ### Notes
69
+ - DB index propagation can take time (firestore 5-10 min, postgres CONCURRENTLY minutes-to-hours on large tables, dynamodb GSI minutes); deploy BEFORE releasing the code
70
+ - Environment variables must be set on the deployment platform BEFORE the deploy triggers
71
+ ```
72
+
73
+ ### Auto-execution of agent-doable tasks
74
+
75
+ Before presenting the checklist, **auto-execute** all items that can be performed by the agent
76
+ without manual intervention. Do NOT ask the user for approval — just run them.
77
+
78
+ **Auto-executable items** (run via Bash tool, no confirmation needed). Pick the
79
+ command matching `stack.deployment` + `stack.database`. If the inferred command
80
+ is wrong for the project, ask the user — never auto-execute a guess.
81
+
82
+ | Category | Command per `stack.deployment` | Auto-execute? |
83
+ |----------|-------------------------------|--------------|
84
+ | Database indexes | firebase → `firebase deploy --only firestore:indexes --project <id>`; supabase → `supabase db push`; vercel+postgres → `prisma migrate deploy` (or project-specific) | YES (when stack-matched) |
85
+ | Persistence access rules | firebase → `firebase deploy --only firestore:rules --project <id>`; supabase → `supabase db push` (RLS migration); aws → CDK / Terraform apply | YES (when stack-matched) |
86
+ | Storage access rules | firebase → `firebase deploy --only storage:rules --project <id>`; aws → S3 bucket policy apply via IaC | YES (when stack-matched) |
87
+ | Scheduled functions | firebase → `firebase deploy --only functions --project <id>`; vercel → deploy step already includes cron from `vercel.json`; aws → CDK / Terraform apply | YES (when stack-matched) |
88
+
89
+ **Manual-only items** (report to user, do NOT auto-execute):
90
+
91
+ | Category | Why manual |
92
+ |----------|-----------|
93
+ | Environment variables | Requires deployment-platform dashboard access or secret values |
94
+ | Feature-flag / remote config | Requires flag-system console (Firebase Remote Config / GrowthBook / LaunchDarkly / Unleash) |
95
+ | Auth provider configuration | Requires console UI of the provider matching `stack.auth_provider` |
96
+ | Database migrations / backfills | Risk of data loss — needs human judgment |
97
+ | Third-party service config | Requires external dashboards and secrets |
98
+ | DNS / domain changes | Risk of downtime — needs human judgment |
99
+
100
+ **Auto-execution procedure:**
101
+
102
+ 1. For each auto-executable item detected, run the command immediately.
103
+ 2. Log the result (success/failure) in the tracker under `## Production Readiness`.
104
+ 3. In the final checklist output, mark auto-executed items with their result:
105
+ ```
106
+ 1. **[DB Indexes — <stack.database>]** Deploy indexes
107
+ - Command: <stack-matched command from the Auto-executable table>
108
+ - Result: DEPLOYED (took 45s) | FAILED (error: ...)
109
+ ```
110
+ 4. If an auto-execution FAILS: log the error, mark it as `MANUAL FALLBACK NEEDED`,
111
+ and include it in the "Required before deploy" section for the user to handle.
112
+
113
+ ### DB Index Verification (MUST — after deploy)
114
+
115
+ After the deploy command succeeds, verify the indexes are actually live before
116
+ reporting success. The deploy command typically returns before index propagation
117
+ completes (Firestore: 5–10 minutes; Postgres `CREATE INDEX CONCURRENTLY` on a
118
+ large table: minutes to hours; DynamoDB GSI: minutes; Mongo background index:
119
+ minutes to hours).
120
+
121
+ **Skip this section entirely when:**
122
+ - the card has no `db_indexes` / `firestore_indexes` block, OR
123
+ - `stack.database` is unset / `none`, OR
124
+ - the card's index artifact is a SQL migration on a small table (the migration
125
+ itself is synchronous and self-verifying — the deploy step already failed if
126
+ the index didn't build).
127
+
128
+ **Verification procedure — pick the variant matching `stack.database`:**
129
+
130
+ #### Variant — Firestore (`stack.database: firestore`)
131
+
132
+ After `firebase deploy --only firestore:indexes` succeeds:
133
+
134
+ 1. **Extract expected collection groups** from the local `firestore.indexes.json`:
135
+ ```bash
136
+ cat firestore.indexes.json | python3 -c "
137
+ import sys, json
138
+ data = json.load(sys.stdin)
139
+ groups = sorted(set(i['collectionGroup'] for i in data.get('indexes', [])))
140
+ print('\n'.join(groups))
141
+ "
142
+ ```
143
+
144
+ 2. **Check index states** via Firestore REST API for each collection group:
145
+ ```bash
146
+ TOKEN=$(gcloud auth print-access-token) && \
147
+ for CG in <collection_groups>; do
148
+ curl -s "https://firestore.googleapis.com/v1/projects/<your-firebase-project>/databases/(default)/collectionGroups/$CG/indexes" \
149
+ -H "Authorization: Bearer $TOKEN" 2>/dev/null
150
+ done | python3 -c "
151
+ import sys, json, re
152
+ raw = sys.stdin.read()
153
+ creating = []
154
+ for match in re.finditer(r'\{[^{}]*\"indexes\"[^}]*\}', raw, re.DOTALL):
155
+ try:
156
+ data = json.loads(match.group())
157
+ for idx in data.get('indexes', []):
158
+ state = idx.get('state', 'UNKNOWN')
159
+ if state != 'READY':
160
+ fields = ' + '.join(f.get('fieldPath','?') for f in idx.get('fields',[]))
161
+ cg = idx.get('name','').split('/collectionGroups/')[1].split('/')[0] if '/collectionGroups/' in idx.get('name','') else '?'
162
+ creating.append(f'{state}: {cg} ({fields})')
163
+ except: pass
164
+ if creating:
165
+ print(f'NOT_READY ({len(creating)} indexes still building):')
166
+ for c in creating: print(f' - {c}')
167
+ else:
168
+ print('ALL_READY')
169
+ "
170
+ ```
171
+
172
+ 3. **Poll if NOT_READY** — re-check every 30 seconds, up to 10 retries (5 minutes max).
173
+
174
+ 4. **Final status**:
175
+ - All `READY` → `VERIFIED READY`.
176
+ - Still `CREATING` after 10 retries → `DEPLOYED BUT BUILDING` (warning).
177
+ - Any `NEEDS_REPAIR` / `ERROR` → `INDEX ERROR — manual intervention required`.
178
+
179
+ #### Variant — Postgres / Supabase (`stack.database` ∈ {postgres, supabase})
180
+
181
+ After the migration / `supabase db push` succeeds:
182
+
183
+ ```bash
184
+ # For each index named in the migration:
185
+ psql "$DATABASE_URL" -c "SELECT indexname, indexdef FROM pg_indexes WHERE indexname = '<index_name>';"
186
+ # If the migration used CREATE INDEX CONCURRENTLY on a large table, also check:
187
+ psql "$DATABASE_URL" -c "SELECT pid, phase, blocks_total, blocks_done FROM pg_stat_progress_create_index;"
188
+ ```
189
+
190
+ If `pg_stat_progress_create_index` returns rows → index still building, poll every
191
+ 30s. If `pg_indexes` returns 0 rows for an expected name → CRITICAL (migration
192
+ silently skipped or rolled back).
193
+
194
+ #### Variant — MongoDB (`stack.database: mongodb`)
195
+
196
+ After the bootstrap script / migration runs:
197
+
198
+ ```bash
199
+ mongosh "$MONGO_URL" --eval "db.<entity>.getIndexes()"
200
+ ```
201
+
202
+ Confirm every expected `name`/`key` is present. For background-built indexes, watch
203
+ `db.currentOp({'$or':[{op:'command','command.createIndexes':{$exists:true}}]})` until empty.
204
+
205
+ #### Variant — DynamoDB (`stack.database: dynamodb`)
206
+
207
+ After `cdk deploy` / `terraform apply`:
208
+
209
+ ```bash
210
+ aws dynamodb describe-table --table-name <table> \
211
+ --query 'Table.GlobalSecondaryIndexes[].{Name:IndexName,Status:IndexStatus}'
212
+ ```
213
+
214
+ All GSIs must show `ACTIVE`. `CREATING` → poll; `DELETING` / `UPDATING` → manual review.
215
+
216
+ **Checklist output format with verification:**
217
+ ```
218
+ 1. **[DB Indexes — <stack.database>]** Deploy indexes
219
+ - Command: <stack-matched command>
220
+ - Deploy: SUCCESS (took 45s)
221
+ - Verification: ALL READY (5/5) | BUILDING (2 still creating after 5min) | ERROR (details)
222
+ ```
223
+
224
+ ### Rules
225
+
226
+ - **Always present this section**, even if the checklist is empty (in that case, state "No infrastructure changes required — deploy is code-only").
227
+ - Order items by **deployment sequence** (items that must happen first go first — e.g., indexes before code deploy, env vars before code deploy).
228
+ - For each item, include the **reason** (which card/feature requires it) and the **exact command or UI path**.
229
+ - If an item is **uncertain** (e.g., you suspect a new index might be needed but aren't sure), mark it with `VERIFY` and explain what to check.
230
+ - **Update the tracker** with the full checklist under a new `## Production Readiness` section.
231
+
232
+ ---