<!-- Modulo on-demand di /new — NON un file installato a sé. Caricato via Read dalla § "Routing" del SKILL.md core. -->

> **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**.

### Phase 2.5x — Review-cluster workflow delegation gate (v4.34.0 — opt-in, additive)

The per-card review cluster — **Phase 2.55 Simplify + Phase 3.5 QA + Phase 3.7 Codex** (the
*discovery* fan-out: Simplify trio, cross-model Codex/code-reviewer, qa-sentinel gates, security
review) plus the **fix application** — is a self-contained read-then-fix pass that touches no git
state outside the worktree and asks the user nothing on the happy path. That makes it a clean fit for a
**dynamic workflow** that runs the whole cluster OUTSIDE this orchestrator's context window (the single
biggest source of prefix growth on long epics). E2E (Phase 2.6, human-gated + nests a skill) and the
doc-review (Phase 3, write-mode, must see the FINAL code) stay in the skill.

**Branch (decide once, here — at the top of the review cluster, AFTER Phase 2.5b AC-Closure).**
This is a **MECHANICAL, BINARY decision** — evaluate the three conditions below and act on the result.
You have **NO discretion**: if the delegation conditions hold you **MUST** delegate. The factors that
feel like reasons to "just run it inline" — a single-card batch, a small/well-scoped diff, "the
context-economy benefit is marginal here", "a workflow once degenerated on this project", "inline is
safer / I have full control" — are **NOT inputs to this gate** and **MUST NOT** override it. (Those
degeneration reports concern the `new2`/`new2-resolve` whole-batch host, a *different* workflow; the
canonical guidance is that this read-then-fix fan-out is exactly where a workflow fits.) Delegating IS
the SSOT-sanctioned path: the workflow mirrors this prose, and the inline fallback exists **only** for
installs where the `Workflow` tool is genuinely absent — not as a judgment-call alternative. If you ever
run inline while the delegation conditions were met, that is a **gate violation**: log it explicitly as
`review-cluster: GATE VIOLATION — ran inline despite Workflow available + new-card-review.js linked + non-trivial`
so it surfaces in telemetry.

- **IF `IS_TRIVIAL`** (§ "Trivial-card fast-lane", re-confirmed on the committed diff) → do NOT delegate.
  Follow the trivial fast-lane in Phase 2.55 below (inline mechanical gates → Phase 3 doc → commit).

- **ELSE IF** the `Workflow` tool is available **AND** `.claude/workflows/new-card-review.js` is present
  (linked by the framework on Claude-enabled installs) → you **MUST delegate the cluster** to it (this
  branch is not optional — see the no-discretion rule above). First build the
  inputs (reusing the existing deterministic logic — do NOT re-implement it):
  - `qaTier` ← the **Phase 3.5 profile selection** (step 19-21b below: `review_profile` floor →
    `skip`/`light` ⇒ `qaTier:"light"` (qa-sentinel deferred to Final), `deep` **or any Phase 3.7 Step-A
    high-risk trigger on this card's diff** ⇒ `qaTier:"full"`). Compute the **Step-A detector**
    (`references/codex-gate.md` Step A) once here — it also tells the workflow's Codex pass the depth.
  - `scopeFiles` ← this card's committed diff (`git diff --name-only "$TRUNK...HEAD"`, fallback `HEAD~1..HEAD`). **Diffs to disk (context economy, v4.43.0):** write the card's full diff to `/tmp/diff-<CARD-ID>.txt` once and derive BOTH the Step-A grep (above) and this `--name-only` list from it — never read the raw diff inline (mirrors the team-mode D.1.5 batching discipline, so the two paths stay symmetric).
  - `editableFiles` ← this card's **File Ownership Map** entries (the coder's write scope; `setup.md` step 3b).
  - `archBaselinePath` ← `/tmp/arch-baseline-<CARD-ID>.md` (persisted at `implement.md` step 5b).
  - `hasSecurityFiles` ← any `scopeFiles` path matches `paths.high_risk_modules`.
  - `runSimplify` ← `true` (a non-trivial card always simplifies).

  ```
  Workflow({ name: 'new-card-review', args: {
    cards: [{ cardId, cardPath, scopeFiles, editableFiles, qaTier, hasSecurityFiles, runSimplify, archBaselinePath }],
    worktreePath, baseBranch, config            // from Phase 0 / tracker
  }})
  ```

  The workflow runs Simplify + Codex (agent-launched, code-reviewer fallback) + qa-sentinel + security,
  FP-checks each specialist's own findings, then the **domain writer applies its VERIFIED findings**
  (canonical writer map v4.26.1: `security` → `security-reviewer`; `code`/`perf`/`migration`/`test`/
  `simplify` → `coder`) — security-reviewer pass first, then the coder pass — and re-verifies
  lint/tsc/build. It returns
  `{ codexEngine, perCard: { <CARD-ID>: { fixesApplied, residual } }, gateTable, summary }`.
  **Skip the inline Phase 2.55 + Phase 3.5 below AND the Phase 3.7 gate in `codex-gate.md`** (all three
  are now done), then handle the workflow output HERE in the skill. **Process each `residual` finding by
  CLASSIFICATION FIRST, then domain** (so a needs-manual doc finding reaches the human gate, not the
  automated doc re-review):
  - `classification == NEEDS_MANUAL_CONFIRMATION` (any domain) → `AskUserQuestion` — the human gate the
    workflow cannot run. (`summary.needsManual` counts these, doc included.)
  - else `domain == doc` residual → carry into **Phase 3** (the doc-reviewer runs there, post-E2E, on final code).
  - else `security` residual (a fix not converged in 2 retries) → spawn a targeted `security-reviewer`
    now over this card's `editableFiles` (it owns the security-invariant contract — never a coder).
  - else `code`/`perf`/`migration` residual (a fix the coder could not converge in its 2 retries)
    → spawn a targeted `coder` now over this card's `editableFiles`.
  - **QA gate (BLOCKING — mirror of inline Phase 3.5 step 24)**: if `gateTable` has any `status:"FAIL"`
    **OR** `summary.checksFailed` is true, the merge gate is NOT satisfied. Spawn a `coder` on the
    failing gates / post-fix regressions (≤2 retries, re-run via the workflow or inline qa-sentinel),
    then if still failing → `AskUserQuestion` (proceed/stop). **Do NOT proceed to Phase 4 commit until
    `gateTable` is all PASS/SKIP and `checksFailed` is false** — a delegated QA FAIL must block exactly
    as the inline path does. (`gateTable` is **wave-scoped**, not per-card — qa-sentinel runs once over
    the worktree; a FAIL applies to the whole wave's commit.)
  - **Telemetry**: append the workflow's `fixesApplied` + `residual` to `## Fix Application Log`
    (`decision=workflow`). Derive the Phase-8 producers from the return: `qa_first_attempt` ←
    `summary.qaRan ? (summary.failingGates.length === 0 && !summary.checksFailed ? "pass" : "fail") : "n/a"`
    (n/a when qa was deferred to the Final); `doc_gaps` is produced by **Phase 3** (which still runs below).

  Then proceed to **Phase 2.6 (E2E)** → **Phase 3 (doc review, relevance-gated)** → **Phase 4 (commit)**.
  Log `review-cluster: delegated to new-card-review (engine: <codexEngine>, fixes: <n>, residual: <n>, qa: <pass|fail|deferred>)`.

- **ELSE** (no `Workflow` tool — older Claude Code, `disableWorkflows`, or a Codex install where
  workflows have no equivalent) → run the **inline Phase 2.55 → 2.6 → 3 → 3.5 → 3.7 exactly as written
  below**. This prose is the SSOT and the always-available fallback; the workflow only mirrors it.

> **Drift note (maintainers):** `new-card-review.js` encodes only the *orchestration shape* of the
> review cluster; its agent briefs cite THIS module for the review *semantics*, so the SSOT for what each
> finder checks stays here. When you change the Simplify lenses, the qa contract, the Codex/Step-A
> profile rule, or the FP-check/routing, update the workflow's phase/agent wiring to match. The
> delegated path runs the doc-review (Phase 3) on the FINAL post-fix/post-E2E code; the inline path runs
> it at its current position — both are doc-relevance-gated and both are backstopped by the Final FULL
> doc-reviewer.

### Phase 2.55 — Simplify (code cleanup before review)

> **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.

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.

**Step-by-step**:

1. **Update tracker**: phase = "2.55-simplify".
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).
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:

   - **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.
   - **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).
   - **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.

4. Aggregate findings from all three agents. For each finding:
   - **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 **writer** (canonical writer map v4.26.1): `doc` → `doc-reviewer` (write mode), `security` → `security-reviewer` (write mode — it owns the security-invariant contract a coder lacks), `migration` → `coder`. Even a one-line efficiency fix in `paths.high_risk_modules` (security) or a migration file goes to the owning agent — the orchestrator lacks that domain's invariant contract.
   - **Valid AND not in a Domain-Override domain** → fix directly (apply edits inline).
   - **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.

   **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>`.

5. After all fixes, run `npm run lint` and `npx tsc --noEmit` (when `has_toolchain`, the configured `toolchain.commands.{lint,typecheck}` verbatim — § "Toolchain gates") to confirm nothing broke (redirect to disk per § "Context economy"; surface only exit code + a bounded extract on failure).
   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.

6. **Update tracker**: phase = "2.55-simplify DONE", log count of fixes applied (or "clean — 0 fixes").
   If any valid finding revealed a reusable pattern or common mistake, append 1-line to `## Lessons Learned`:
   `SIMPLIFY: <pattern> — <file>`

### Phase 2.6 — End-to-End Review (BLOCKING, since v3.18.0)

This phase delegates to the `/e2e-review` skill — a deterministic, BLOCKING
orchestrator that combines functional E2E (Playwright spec written by `coder`,
executed via `playwright-skill`) with visual fidelity diff
(`visual-fidelity-verifier` multimodal agent), aggregates findings under a
strict severity gate, runs a bounded self-heal loop, and either passes /
blocks / accepts an explicit user override.

The skill replaces the previous advisory pair (legacy Phase 2.6 E2E testing
and Phase 2.7 visual design review). It runs by default on every UI card,
auto-skips on backend-only cards, and BLOCKS the commit on gating findings.

#### Gate (skip the whole phase when any of the below is true)

| Condition | Action |
|-----------|--------|
| `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. |
| 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.) |
| 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.) |

#### Invocation contract

When the gate above passes, the orchestrator invokes `/e2e-review` in
**programmatic mode**:

1. **Update tracker**: phase = "2.6-e2e-review".
2. Spawn the skill with the payload:

   ```json
   {
     "card_id": "<CARD-ID>",
     "worktree_path": "<absolute worktree path>",
     "mode": "programmatic"
   }
   ```

   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.

   The skill is responsible for: reading the card YAML + `test_plan`, deriving
   routes from the diff, walking the 4-level mockup cascade (Figma MCP →
   local image → compliance-only → skip), generating the Playwright spec via
   `coder`, executing it headless, invoking `visual-fidelity-verifier` per
   route, and returning a single JSON object.

3. Parse the returned JSON. The contract is:

   ```json
   {
     "status": "passed" | "blocked" | "overridden" | "skipped" | "error",
     "iterations": 0,
     "findings": [ ... ],
     "ds_drift_codes": [ "DS_TOKENS_DRIFT" | "DS_COMPONENT_STALE" | "DS_INDEX_DRIFT" ],
     "gaps": [ ... ],
     "transcript_paths": [ ... ]
   }
   ```

#### Gating logic (BLOCKING)

| `status` from `/e2e-review` | Orchestrator action |
|----|----|
| `"passed"` | Log result in tracker. Proceed to Phase 3 (doc review + code review). |
| `"skipped"` | Log skip reason. Proceed to Phase 3. |
| `"overridden"` | Log override + reason in `## Issues & Flags` as `[E2E-OVERRIDE] <reason>`. Proceed to Phase 3. |
| `"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. |
| `"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. |

#### Re-run trigger (after Phase 3.7 `/codexreview`)

If the mandatory `/codexreview` gate in Phase 3.7 modifies any file under
`${paths.app_dir}`, `${paths.components_primitives}`, or any `*.css` /
`*.scss`, the orchestrator MUST re-invoke `/e2e-review` BEFORE the Phase 4
commit. This protects against code-review changes that silently reintroduce
visual / functional drift. The re-run reuses the same
`.baldart/e2e-review/<CARD-ID>/` state directory and starts from Phase 3
of the skill (skipping plan extraction and mockup cascade).

#### Tracker output

Append to `## Current Card` and (on completion) carry into `## Completed Cards`:

```
e2e-review: <status> | iterations: <N> | findings: <X critical, Y major, Z minor> | drift: [<codes>] | tolerance: <strict|balanced|lenient>
```

#### Out of scope for this phase

- Mockup re-design (the skill never modifies the mockup — only the
  implementation).
- Code review or doc review (those remain Phase 3 / 3.7 responsibilities).
- Unit-test execution (that is `qa-sentinel`).
- Visual polish beyond gating findings (advisory Minor findings under
  balanced/lenient tolerance are logged but not actioned).

#### Legacy cards

Cards written before v3.18.0 (without a `test_plan` block) still work — the
skill's Phase 1 falls back to deriving Gherkin scenarios from
`acceptance_criteria` directly. The mockup cascade gracefully degrades when
`links.design` is missing.

---

### Phase 3 — Doc Review & Sync (code review handled by mandatory Phase 3.7 gate)

> **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).

> **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`.

12. **Update tracker**: phase = "3-doc-review".
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):
    ```bash
    git diff --name-only "$TRUNK...HEAD" 2>/dev/null || git diff --name-only HEAD~1..HEAD
    ```
    Use the output to build this block:
    ```
    ## Doc Sync Context

    ### Changed code files (for freshness mapping):
    [list all code files changed — src/, scripts/, etc.]

    ### Invariant checklist (Linking Protocol — docs/guides/linking-protocol-rollout-v1.md):
    - [ ] New route.ts added? → ${paths.references_dir}/api/<module>.md + api/index.md count
    - [ ] New page.tsx added? → ${paths.references_dir}/ui/<domain>.md + ui/index.md Route Summary
    - [ ] 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.)
    - [ ] 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)
    - [ ] Backlog card set to DONE? → ${paths.references_dir}/ssot-registry.md entry
    - [ ] New external dependency? → agents/architecture.md External Dependencies list
    - [ ] Card has `documentation_impact` field? → verify each listed doc is updated
    - [ ] New `process.env.VAR` added? → entry in `${paths.references_dir}/env-vars.md` (name, scope, required, feature/card, default)
    - [ ] Last usage of `process.env.VAR` removed? → mark `status: deprecated` in `${paths.references_dir}/env-vars.md` with the date
    - [ ] 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`
    - [ ] Card has an `env_vars` field populated? → verify each entry is tracked in `${paths.references_dir}/env-vars.md`

    ### Related docs to check (per convention map):
    - [derive from FRESHNESS_MAP: src/app/api/** → ${paths.references_dir}/api/; src/lib/booking/** → booking.md etc.]
    ```

    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:
    ```
    Doc Sync Context:
    [paste the Doc Sync Context block built above]

    Instructions for doc-reviewer:
    - You OWN the `doc` domain end-to-end: audit AND apply the fixes in THIS invocation.
      Do NOT defer doc writes to another agent — you are the agent with the doc-invariant
      contract and the full context. Write the missing/stale docs directly.
    - Set freshness_status: fresh and last_verified_from_code: <today's date> on any doc you touch
    - Check each invariant in the checklist above and fix any that are unmet
    - Update any doc in "Related docs to check" that should be marked stale/fresh
    - Run your standard doc audit on all changed files and apply the corrections
    - **Spec/docs-drift → bug lens (MANDATORY since v3.35.0)**: flag any place where the
      implementation contradicts a documented contract/spec in a way that can cause incorrect
      behavior (response shape diverging from `api/<module>.md`, field semantics diverging from
      `data-model.md`, etc.). This lens was previously exclusive to `/codexreview` agent #4; the
      per-card Phase 3.7 gate now skips that duplicate (lean mode), so THIS pass MUST carry it.
      A doc-drift→bug finding whose root cause is in CODE (not the doc) is the ONE thing
      doc-reviewer does NOT fix itself — report it with the conflicting code location + the doc
      it violates, and the orchestrator routes it to the `security` (→ security-reviewer) / code (→ coder) fix path as appropriate.
    ```
    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 `security-reviewer` with the finding now, in this phase (a security-class code fix is not deferrable to a `light` Phase 3.7, and security is owned by `security-reviewer` — never a coder); 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.
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.
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.)
16. Run `npm run lint` and `npx tsc --noEmit` (when `stack.language` includes typescript) — when `has_toolchain`, the configured `toolchain.commands.{lint,typecheck,build}` verbatim (§ "Toolchain gates") — 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).
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.
    If doc-reviewer found a recurring gap, append 1-line to `## Lessons Learned`:
    `DOC: <pattern>`

### Phase 3.5 — QA Validation

18. **Update tracker**: phase = "3.5-qa".
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).
20. **If profile is SKIP**: log "QA skipped — [reason]" in the tracker. Proceed to Phase 4.
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.
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.
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:

    ```
    Run QA gates on card <CARD-ID> at the tier dictated by the QA profile below (do NOT default to FULL).
    You are a MECHANICAL GATE-RUNNER: run the gates, return a PASS/FAIL gate table + confidence.
    Do NOT read source files to analyze logic, do NOT emit a BLOCKER/MAJOR/MINOR severity taxonomy,
    do NOT do collateral-impact analysis, and do NOT write tests.

    Context:
    - Worktree path: <worktree-path> — `cd` into it before running any gate
    - Branch: <branch-name>
    - Changed files: <list from implementation phase>
    - QA profile: [deep | balanced-escalated]

    Tier contract: deep → FULL suite. A BALANCED card only reaches this step when risk drift
    escalated it (step 21b) — run it at FULL too (the escalation reason is the fail-fast signal).
    A non-escalated balanced card never reaches here (its suite is deferred to the Final FULL gate
    per step 21b). File count never escalates.

    E2E is NOT your job (run by the /e2e-review skill in Phase 2.6) — do NOT run Playwright.
    Run the gates: lint, tsc (when stack.language includes typescript), test (per tier), build, markdownlint.
    Do NOT verify acceptance criteria (Phase 2.5 already did this).
    Do NOT analyze code for bugs/patterns (the per-card /codexreview gate in Phase 3.7 does this).

    Write the gate results + verdict to: /qa/<CARD-ID>.md
    Report should be under 40 lines.
    Return verdict: PASS or FAIL (+ the per-gate table).
    ```

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.
24. **If QA verdict is FAIL** (one or more gates failed):
    - 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.
    - After coder fixes, re-invoke `qa-sentinel` in the same mode to re-validate. Repeat up to **2 times**.
    - If still FAIL after 2 retries: log in `## Issues & Flags` and **ask the user** whether to proceed or stop.
    - The commit in Phase 4 MUST NOT happen until QA verdict is PASS (or user explicitly overrides).
    - **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`.
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.
