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,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
+ ---
@@ -0,0 +1,247 @@
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 2.55 — Simplify (code cleanup before review)
6
+
7
+ > **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.
8
+
9
+ 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.
10
+
11
+ **Step-by-step**:
12
+
13
+ 1. **Update tracker**: phase = "2.55-simplify".
14
+ 2. Capture the current card's changes to disk — `git diff > /tmp/diff-<CARD-ID>.txt` in the worktree (**redirect, not inline** — per § "Context economy" → Diffs to disk).
15
+ 3. Launch **three agents in parallel** (single message). Each receives the **path** `/tmp/diff-<CARD-ID>.txt` and Reads the diff itself — do NOT paste the full diff into the prompts:
16
+
17
+ - **Reuse agent** — search the codebase for existing utilities/helpers that could replace newly written code. Flag duplicated functionality, inline logic that could use existing utils.
18
+ - **Quality agent** — flag redundant state, parameter sprawl, copy-paste with slight variation, leaky abstractions, stringly-typed code where constants/enums exist, unnecessary JSX nesting, unnecessary comments (WHAT comments, narration, task references).
19
+ - **Efficiency agent** — flag unnecessary work (redundant computations, duplicate API calls, N+1), missed concurrency, hot-path bloat, recurring no-op updates without change-detection guards, TOCTOU existence checks, memory issues (unbounded structures, missing cleanup), overly broad operations.
20
+
21
+ 4. Aggregate findings from all three agents. For each finding:
22
+ - **Valid AND in a Domain-Override domain** (the finding's target file matches the `doc`, `security`, or `migration` match rule in "Domain-Override Domains") → do NOT apply inline. Delegate to the domain owner: `doc` → `doc-reviewer` (write mode), `security`/`migration` → `coder`. Even a one-line efficiency fix in `paths.high_risk_modules` or a migration file goes to the owning agent — the orchestrator lacks that domain's invariant contract.
23
+ - **Valid AND not in a Domain-Override domain** → fix directly (apply edits inline).
24
+ - **False positive / not worth addressing** → skip, BUT record it (see telemetry). If the skip rests on a "covered by X" / "redundant" / "not needed" rationalization (the same family the AC-Closure Gate guards against), do NOT discard silently — verify the rationale by reading `X`, and if it does not hold, treat the finding as valid.
25
+
26
+ **Telemetry (Fix Application Log)** — for EVERY finding (valid OR skipped) append one row to the tracker's `## Fix Application Log` section per the schema above. Use `domain=simplify-{reuse|quality|efficiency}` matching the originating agent. Include the `severity` trailing key. Inline: `decision=inline | applied_by=orchestrator | est_lines=<n> | severity=<HIGH|MEDIUM> | finding=<1-line>`. Delegated (domain-override): `decision=<coder|doc-reviewer> | applied_by=<coder|doc-reviewer> | est_lines=<n> | severity=<...> | finding=<1-line>`. Skipped: `decision=skipped | applied_by=- | est_lines=0 | reason=<false-positive|not-worth-addressing>`.
27
+
28
+ 5. After all fixes, run `npm run lint` and `npx tsc --noEmit` to confirm nothing broke (redirect to disk per § "Context economy"; surface only exit code + a bounded extract on failure).
29
+ If either fails, fix the regression (up to **2 retries**). **If it still fails after 2 retries**: do NOT silently continue to Phase 2.6 with a broken tree — log the failure in `## Issues & Flags` as `[SIMPLIFY-REGRESSION]` and invoke `AskUserQuestion` (revert the simplify fixes / keep and have me fix manually / stop the card), mirroring the Phase 3.5 escalation.
30
+
31
+ 6. **Update tracker**: phase = "2.55-simplify DONE", log count of fixes applied (or "clean — 0 fixes").
32
+ If any valid finding revealed a reusable pattern or common mistake, append 1-line to `## Lessons Learned`:
33
+ `SIMPLIFY: <pattern> — <file>`
34
+
35
+ ### Phase 2.6 — End-to-End Review (BLOCKING, since v3.18.0)
36
+
37
+ This phase delegates to the `/e2e-review` skill — a deterministic, BLOCKING
38
+ orchestrator that combines functional E2E (Playwright spec written by `coder`,
39
+ executed via `playwright-skill`) with visual fidelity diff
40
+ (`visual-fidelity-verifier` multimodal agent), aggregates findings under a
41
+ strict severity gate, runs a bounded self-heal loop, and either passes /
42
+ blocks / accepts an explicit user override.
43
+
44
+ The skill replaces the previous advisory pair (legacy Phase 2.6 E2E testing
45
+ and Phase 2.7 visual design review). It runs by default on every UI card,
46
+ auto-skips on backend-only cards, and BLOCKS the commit on gating findings.
47
+
48
+ #### Gate (skip the whole phase when any of the below is true)
49
+
50
+ | Condition | Action |
51
+ |-----------|--------|
52
+ | `features.has_e2e_review: false` in `baldart.config.yml` | **SKIP** — log `"e2e-review: SKIP (feature disabled — run `npx baldart configure` to enable)"`. Preserves backwards compatibility for pre-3.18 consumers. |
53
+ | Card has no `links.design` AND the card's diff (`git diff --name-only "$TRUNK...HEAD"` for this card's committed files, falling back to `HEAD~1..HEAD`) shows no file under `${paths.app_dir}` AND no file under `${paths.components_primitives}` AND no `*.tsx` / `*.css` / `*.scss` / `*.svelte` / `*.vue` anywhere | **SKIP** — log `"e2e-review: SKIP (backend-only card)"`. (Diff against the trunk, NOT a bare `HEAD` — the coder already committed, so a working-tree diff is empty and would false-skip.) |
54
+ | Card type is `backend`, `api`, `db`, `infra`, `docs`, `chore`, or `config` | **SKIP** — same log. (This list mirrors `/e2e-review`'s OWN Pre-flight skip set so `/new` does not invoke the skill only to have it immediately return `skipped`. A card with no `type` field falls through to the diff-based backend-only check above — do NOT assume a missing type means skip.) |
55
+
56
+ #### Invocation contract
57
+
58
+ When the gate above passes, the orchestrator invokes `/e2e-review` in
59
+ **programmatic mode**:
60
+
61
+ 1. **Update tracker**: phase = "2.6-e2e-review".
62
+ 2. Spawn the skill with the payload:
63
+
64
+ ```json
65
+ {
66
+ "card_id": "<CARD-ID>",
67
+ "worktree_path": "<absolute worktree path>",
68
+ "mode": "programmatic"
69
+ }
70
+ ```
71
+
72
+ Do NOT pass `tolerance` / `max_self_heal_iterations` in the payload — `/e2e-review` reads those from `features.e2e_review.*` in `baldart.config.yml` itself (they are NOT parsed from the invocation payload). Passing them here would be dead keys that silently diverge from the config the skill actually uses.
73
+
74
+ The skill is responsible for: reading the card YAML + `test_plan`, deriving
75
+ routes from the diff, walking the 4-level mockup cascade (Figma MCP →
76
+ local image → compliance-only → skip), generating the Playwright spec via
77
+ `coder`, executing it headless, invoking `visual-fidelity-verifier` per
78
+ route, and returning a single JSON object.
79
+
80
+ 3. Parse the returned JSON. The contract is:
81
+
82
+ ```json
83
+ {
84
+ "status": "passed" | "blocked" | "overridden" | "skipped" | "error",
85
+ "iterations": 0,
86
+ "findings": [ ... ],
87
+ "ds_drift_codes": [ "DS_TOKENS_DRIFT" | "DS_COMPONENT_STALE" | "DS_INDEX_DRIFT" ],
88
+ "gaps": [ ... ],
89
+ "transcript_paths": [ ... ]
90
+ }
91
+ ```
92
+
93
+ #### Gating logic (BLOCKING)
94
+
95
+ | `status` from `/e2e-review` | Orchestrator action |
96
+ |----|----|
97
+ | `"passed"` | Log result in tracker. Proceed to Phase 3 (doc review + code review). |
98
+ | `"skipped"` | Log skip reason. Proceed to Phase 3. |
99
+ | `"overridden"` | Log override + reason in `## Issues & Flags` as `[E2E-OVERRIDE] <reason>`. Proceed to Phase 3. |
100
+ | `"blocked"` | **STOP the card**. Log findings in tracker + `## Issues & Flags` as `[E2E-BLOCKED] <count> gating findings (<categories>)`. Ask the user whether to (a) override with reason, (b) escalate (open follow-up card), or (c) abandon this card and continue the batch. Do NOT proceed to Phase 3 silently. |
101
+ | `"error"` | Log error in `## Issues & Flags`. Ask the user whether to retry, skip, or abandon. Do NOT proceed silently. **Retry re-entry (defined path, capped):** on "retry", re-enter at step 1 of this Invocation contract — reset the `.baldart/e2e-review/<CARD-ID>/` state dir, re-spawn `/e2e-review` with the SAME payload, and re-parse. **Cap: 2 error-retries per card** (central repair cap, consistent with the other loops). On the 2nd `error`, do NOT re-offer "retry" — re-invoke `AskUserQuestion` with only skip/abandon, so the loop cannot recur unbounded. |
102
+
103
+ #### Re-run trigger (after Phase 3.7 `/codexreview`)
104
+
105
+ If the mandatory `/codexreview` gate in Phase 3.7 modifies any file under
106
+ `${paths.app_dir}`, `${paths.components_primitives}`, or any `*.css` /
107
+ `*.scss`, the orchestrator MUST re-invoke `/e2e-review` BEFORE the Phase 4
108
+ commit. This protects against code-review changes that silently reintroduce
109
+ visual / functional drift. The re-run reuses the same
110
+ `.baldart/e2e-review/<CARD-ID>/` state directory and starts from Phase 3
111
+ of the skill (skipping plan extraction and mockup cascade).
112
+
113
+ #### Tracker output
114
+
115
+ Append to `## Current Card` and (on completion) carry into `## Completed Cards`:
116
+
117
+ ```
118
+ e2e-review: <status> | iterations: <N> | findings: <X critical, Y major, Z minor> | drift: [<codes>] | tolerance: <strict|balanced|lenient>
119
+ ```
120
+
121
+ #### Out of scope for this phase
122
+
123
+ - Mockup re-design (the skill never modifies the mockup — only the
124
+ implementation).
125
+ - Code review or doc review (those remain Phase 3 / 3.7 responsibilities).
126
+ - Unit-test execution (that is `qa-sentinel`).
127
+ - Visual polish beyond gating findings (advisory Minor findings under
128
+ balanced/lenient tolerance are logged but not actioned).
129
+
130
+ #### Legacy cards
131
+
132
+ Cards written before v3.18.0 (without a `test_plan` block) still work — the
133
+ skill's Phase 1 falls back to deriving Gherkin scenarios from
134
+ `acceptance_criteria` directly. The mockup cascade gracefully degrades when
135
+ `links.design` is missing.
136
+
137
+ ---
138
+
139
+ ### Phase 3 — Doc Review & Sync (code review handled by mandatory Phase 3.7 gate)
140
+
141
+ > **Note**: Code review is NOT performed in this phase — it is handled by the **mandatory unconditional `/codexreview` gate in Phase 3.7**, which runs per-card BEFORE the Phase 4 commit. The post-batch `/codexreview` in the Final review remains as a final FULL-diff sweep over the entire batch (since v3.37.0 — the guaranteed full-depth merge gate). This Phase 3 focuses exclusively on documentation sync, which must happen per-card (tied to the specific commit).
142
+
143
+ > **Deferral gate (since v4.7.0 — enumerated, `review_profile`-driven)**: SKIP per-card doc-review and defer it to the **Final Review F.3 doc-reviewer** (which runs over the entire batch) when the card is **`review_profile == light` AND its committed diff touches NO documentation file** (no `.md`, no path under `${paths.references_dir}`, no data-model/ssot/api doc). These are small code changes with no doc surface of their own; their only doc concern is drift, which the batch-wide Final doc-reviewer catches. Log `doc-review: DEFERRED to Final FULL gate (light, no doc files in diff)` and proceed to Phase 3.5. **KEEP per-card doc-review** for: `balanced`/`deep` cards, ANY card whose diff touches a doc file (the change IS doc-relevant), and trivial cards (doc-review is their primary gate — § "Trivial-card fast-lane", unchanged). Never defer "for time" — the only enumerated reason is `light + non-doc diff`.
144
+
145
+ 12. **Update tracker**: phase = "3-doc-review".
146
+ 13. Build a **Doc Sync Context** block. The coder committed in Phase 2, so detect the card's changed files against the trunk (a bare `HEAD` diff would be empty post-commit):
147
+ ```bash
148
+ git diff --name-only "$TRUNK...HEAD" 2>/dev/null || git diff --name-only HEAD~1..HEAD
149
+ ```
150
+ Use the output to build this block:
151
+ ```
152
+ ## Doc Sync Context
153
+
154
+ ### Changed code files (for freshness mapping):
155
+ [list all code files changed — src/, scripts/, etc.]
156
+
157
+ ### Invariant checklist (Linking Protocol — docs/guides/linking-protocol-rollout-v1.md):
158
+ - [ ] New route.ts added? → ${paths.references_dir}/api/<module>.md + api/index.md count
159
+ - [ ] New page.tsx added? → ${paths.references_dir}/ui/<domain>.md + ui/index.md Route Summary
160
+ - [ ] New persistence-layer {entity} ({collection|table|item} per `stack.database`)? → ${paths.references_dir}/data-model.md + the entity's reference doc per project convention (`collections/<domain>.md`, `tables/<domain>.md`, etc.)
161
+ - [ ] Compound query needing a multi-attribute index? → the matching index artifact MUST be staged in the same commit, per `stack.database`: `firestore.indexes.json` (firestore), migration file or Prisma `@@index` (SQL), `createIndex` script (mongo), GSI/LSI in IaC (dynamodb)
162
+ - [ ] Backlog card set to DONE? → ${paths.references_dir}/ssot-registry.md entry
163
+ - [ ] New external dependency? → agents/architecture.md External Dependencies list
164
+ - [ ] Card has `documentation_impact` field? → verify each listed doc is updated
165
+ - [ ] New `process.env.VAR` added? → entry in `${paths.references_dir}/env-vars.md` (name, scope, required, feature/card, default)
166
+ - [ ] Last usage of `process.env.VAR` removed? → mark `status: deprecated` in `${paths.references_dir}/env-vars.md` with the date
167
+ - [ ] Default value changed in the project's env module (e.g. `src/lib/env.ts`)? → update the Default column in `${paths.references_dir}/env-vars.md`
168
+ - [ ] Card has an `env_vars` field populated? → verify each entry is tracked in `${paths.references_dir}/env-vars.md`
169
+
170
+ ### Related docs to check (per convention map):
171
+ - [derive from FRESHNESS_MAP: src/app/api/** → ${paths.references_dir}/api/; src/lib/booking/** → booking.md etc.]
172
+ ```
173
+
174
+ Invoke the **doc-reviewer** agent in **audit-and-apply mode** (single invocation — it both finds the gaps AND fixes them, per `doc-reviewer.md` § Constraints), passing the Doc Sync Context:
175
+ ```
176
+ Doc Sync Context:
177
+ [paste the Doc Sync Context block built above]
178
+
179
+ Instructions for doc-reviewer:
180
+ - You OWN the `doc` domain end-to-end: audit AND apply the fixes in THIS invocation.
181
+ Do NOT defer doc writes to another agent — you are the agent with the doc-invariant
182
+ contract and the full context. Write the missing/stale docs directly.
183
+ - Set freshness_status: fresh and last_verified_from_code: <today's date> on any doc you touch
184
+ - Check each invariant in the checklist above and fix any that are unmet
185
+ - Update any doc in "Related docs to check" that should be marked stale/fresh
186
+ - Run your standard doc audit on all changed files and apply the corrections
187
+ - **Spec/docs-drift → bug lens (MANDATORY since v3.35.0)**: flag any place where the
188
+ implementation contradicts a documented contract/spec in a way that can cause incorrect
189
+ behavior (response shape diverging from `api/<module>.md`, field semantics diverging from
190
+ `data-model.md`, etc.). This lens was previously exclusive to `/codexreview` agent #4; the
191
+ per-card Phase 3.7 gate now skips that duplicate (lean mode), so THIS pass MUST carry it.
192
+ A doc-drift→bug finding whose root cause is in CODE (not the doc) is the ONE thing
193
+ doc-reviewer does NOT fix itself — report it with the conflicting code location + the doc
194
+ it violates, and the orchestrator routes it to the `security`/code fix path as appropriate.
195
+ ```
196
+ Doc-reviewer applies all doc-domain fixes itself. The orchestrator does NOT spawn a coder for doc fixes (since v3.40.0 — `doc` is owned by `doc-reviewer`, see "Domain-Override Domains"). The only doc-reviewer output that leaves this phase unfixed is a **doc-drift→bug finding rooted in CODE** (the implementation contradicts a documented contract). Route it explicitly: if the conflicting code file matches the `security` Domain-Override match rule (`paths.high_risk_modules`) → spawn `coder` with the finding now, in this phase (a security-class code fix is not deferrable to a `light` Phase 3.7); otherwise carry the finding into the Phase 3.7 `/codexreview` input as a known code-drift bug and let the Phase 3.7 fix sub-loop apply it. Either way, append a Fix Application Log row with `domain=codex-correctness` (NOT `doc`) so telemetry attributes it as a code fix. Do NOT leave it accumulating in the tracker with no fix owner.
197
+ 14. **Knowledge-corpus sync (OPTIONAL — only if the project ships a corpus-sync agent)**: There is NO shipped `obsidian-sync` agent — do NOT dispatch one (a hard dispatch to a non-existent subagent fails silently). Only when the project provides its own knowledge-corpus sync agent (declared in `.baldart/overlays/new.md`) AND doc-reviewer's findings indicate a corpus impact, invoke that agent with the listed paths after the doc fixes are applied. Otherwise skip with a one-line notice (`knowledge-corpus sync: skipped (no corpus-sync agent configured)`). Non-blocking either way.
198
+ 15. **Telemetry** — after doc-reviewer returns, append one row per doc finding to `## Fix Application Log`: `3 | doc | est_lines=<n> | decision=doc-reviewer | applied_by=doc-reviewer | finding=<1-line>`. If 0 findings, append one row: `3 | doc | est_lines=0 | decision=skipped | applied_by=- | reason=no-findings`. **Phase-8 producer (named counter)** — ALSO record the per-card doc-gap counts as a structured line in `## Current Card` (carried into `## Completed Cards` at Phase 5): `doc_gaps: found=<N> fixed=<M>` where `N` = total doc findings doc-reviewer raised and `M` = those it applied. This is the single named producer for Phase 8's `doc_gaps_found` / `doc_gaps_fixed` fields — without it those fields have no upstream write and Phase 8 would hard-code zeros. (D.4a is the team-mode producer of the same counter — see Phase 7 § D.4a.)
199
+ 16. Run `npm run lint` and `npx tsc --noEmit` (when `stack.language` includes typescript) to verify nothing broke (redirect to disk per § "Context economy"). If doc-reviewer touched any source-adjacent file (a `.ts`/`.tsx` helper, a co-located doc export), also run `npm run build`. If any check fails, apply the self-healing retry loop (up to 3 times, no user prompt). **If still failing after 3 retries**: do NOT fall through silently to Phase 3.5 — log `[DOC-PHASE-REGRESSION]` in `## Issues & Flags` and invoke `AskUserQuestion` (revert the doc-phase edits that broke the build / keep and fix manually / stop the card).
200
+ 17. **Telemetry for the step-16 self-heal** — if the retry loop spawned any fix (a code edit to recover from a doc-phase regression), append a Fix Application Log row for it AFTER the loop settles (the step-15 doc telemetry row was written before this loop ran, so it does not capture step-16 fixes). Then update tracker: phase = "3-doc-review DONE", log doc findings count, fixes applied.
201
+ If doc-reviewer found a recurring gap, append 1-line to `## Lessons Learned`:
202
+ `DOC: <pattern>`
203
+
204
+ ### Phase 3.5 — QA Validation
205
+
206
+ 18. **Update tracker**: phase = "3.5-qa".
207
+ 19. **Select QA profile**: READ the card's `review_profile` field (use verbatim); only compute via the QA Profile Selector above (whose criteria SSOT is `prd-card-writer.md § Rule C`) when the field is absent (legacy card). Log the chosen profile and its source (`from card` | `computed`) in the tracker (1 line).
208
+ 20. **If profile is SKIP**: log "QA skipped — [reason]" in the tracker. Proceed to Phase 4.
209
+ 21. **If profile is LIGHT**: SKIP qa-sentinel. Phase 2 step 8 ran lint/tsc/test/build, AND the two code-mutating phases that follow it (Phase 2.55 Simplify, Phase 3 doc-reviewer) each re-run lint + tsc on their post-mutation output (so the static checks cover the committed code, not just the pre-mutation code). Log "QA LIGHT skipped — lint/tsc/test/build passed in Phase 2 and lint/tsc re-ran post-mutation in 2.55/3" in the tracker. Proceed to Phase 4.
210
+ 21b. **If profile is BALANCED (since v4.7.0 — deferral to Final, enumerated `review_profile`-driven)**: SKIP per-card qa-sentinel and **defer the test suite to the Final Review F.3 qa-sentinel** (which runs the FULL suite + build + audit over the entire batch — the unconditional merge gate). Phase 2 step 8 already ran lint/tsc/test/build on this card's diff. **Exception — risk escalation runs FULL per-card anyway**: if the Phase 3.7 Step A detector matched any high-risk trigger on this card's diff (auth/permission/payment/schema/migration/API-contract), do NOT defer — run qa-sentinel at FULL here for fail-fast. Log `QA balanced deferred to Final FULL gate (no risk escalation)` or `QA balanced escalated to FULL (risk drift: <trigger>)`. ⚠️ Most `feature`/`enhancement` cards are `balanced` (Rule C), so a balanced-only batch first runs the suite at the Final gate; the merge gate stays safe (Final = full suite over the whole batch). Proceed to Phase 4 when deferred.
211
+ 22. **If profile is DEEP (or BALANCED escalated by risk drift — see 21b)**: invoke the **`qa-sentinel`** agent (subagent_type: `qa-sentinel`) via Task tool with the following context:
212
+
213
+ ```
214
+ Run QA gates on card <CARD-ID> at the tier dictated by the QA profile below (do NOT default to FULL).
215
+ You are a MECHANICAL GATE-RUNNER: run the gates, return a PASS/FAIL gate table + confidence.
216
+ Do NOT read source files to analyze logic, do NOT emit a BLOCKER/MAJOR/MINOR severity taxonomy,
217
+ do NOT do collateral-impact analysis, and do NOT write tests.
218
+
219
+ Context:
220
+ - Worktree path: <worktree-path> — `cd` into it before running any gate
221
+ - Branch: <branch-name>
222
+ - Changed files: <list from implementation phase>
223
+ - QA profile: [deep | balanced-escalated]
224
+
225
+ Tier contract: deep → FULL suite. A BALANCED card only reaches this step when risk drift
226
+ escalated it (step 21b) — run it at FULL too (the escalation reason is the fail-fast signal).
227
+ A non-escalated balanced card never reaches here (its suite is deferred to the Final FULL gate
228
+ per step 21b). File count never escalates.
229
+
230
+ E2E is NOT your job (run by the /e2e-review skill in Phase 2.6) — do NOT run Playwright.
231
+ Run the gates: lint, tsc (when stack.language includes typescript), test (per tier), build, markdownlint.
232
+ Do NOT verify acceptance criteria (Phase 2.5 already did this).
233
+ Do NOT analyze code for bugs/patterns (the per-card /codexreview gate in Phase 3.7 does this).
234
+
235
+ Write the gate results + verdict to: /qa/<CARD-ID>.md
236
+ Report should be under 40 lines.
237
+ Return verdict: PASS or FAIL (+ the per-gate table).
238
+ ```
239
+
240
+ 23. **Read qa-sentinel's output.** Verify the findings file was written to `/qa/<CARD-ID>.md`. **If the file is absent** (qa-sentinel returned a verdict but did not write it): re-invoke qa-sentinel once asking it to (re-)write the file; if still absent, treat the verdict from the return message as authoritative, log `[QA] findings file missing — used return verdict` in `## Issues & Flags`, and pass the captured gate output (not a missing path) to any fix coder.
241
+ 24. **If QA verdict is FAIL** (one or more gates failed):
242
+ - Spawn the **coder** agent to fix the FAILING GATES (pass it the findings file path + the failing-gate output — NOT a severity-ranked finding list; qa-sentinel does not emit one). Do NOT ask the user.
243
+ - After coder fixes, re-invoke `qa-sentinel` in the same mode to re-validate. Repeat up to **2 times**.
244
+ - If still FAIL after 2 retries: log in `## Issues & Flags` and **ask the user** whether to proceed or stop.
245
+ - The commit in Phase 4 MUST NOT happen until QA verdict is PASS (or user explicitly overrides).
246
+ - **Telemetry** — after each coder spawn, append one row per failing-gate fix to `## Fix Application Log`: `3.5 | qa-blocker | est_lines=<n> | decision=coder | applied_by=coder | retry=<0|1|2>`. After PASS without any spawn (initial verdict PASS), append one row: `3.5 | qa-none | est_lines=0 | decision=skipped | applied_by=- | reason=qa-pass`.
247
+ 25. **Update tracker**: phase = "3.5-qa DONE", log: profile used, tier run (SCOPED/FULL) + any risk-drift escalation, verdict (PASS/FAIL/SKIP), confidence %, failing gates (which of lint/tsc/test/build/markdownlint), findings file path. (qa-sentinel returns a per-gate PASS/FAIL table, not a blockers/majors/minors split — do not invent one.) **Phase-8 producer (named flag)** — ALSO record `qa_first_attempt: <pass|fail>` on the card's `## Completed Cards` QA line: `pass` ⟺ the FIRST qa-sentinel invocation (step 22, retry=0) returned PASS with no step-24 coder spawn; `fail` ⟺ a retry was needed (or the profile reached PASS only after a fix). For `SKIP`/`LIGHT` cards (qa-sentinel not run) record `qa_first_attempt: n/a`. This is the single named producer for Phase 8's `qa_pass_first_attempt_rate` — the tracker otherwise logs only the final verdict, so the first-attempt signal must be tagged here at the source.