<!-- 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 0 — Workspace Hygiene Pre-flight (BLOCKING — non-skippable)

**Why this exists**: FEAT-0006 incident left the main repo with an unpushed orphan commit (`c9d41f9`) and `develop` diverged from `origin/develop` because `/new` started work on a dirty/diverged repo without surfacing the situation. This phase reconciles the main repo state BEFORE any worktree is created, and snapshots any in-progress user work so Phase 6c can restore it after the batch.

**Auto Mode does NOT override this phase.** Every `AskUserQuestion` below is non-bypassable — the model has no authority to pick an option on the user's behalf.

**Steps:**

0. **Feature gate + config resolution (BLOCKING)** — before anything else:
   - Read `features.has_backlog` from `baldart.config.yml`. If it is `false` (or the key is absent and the user confirms there is no backlog), this skill REFUSES to run: print "`/new` operates on backlog cards and requires `features.has_backlog: true` — run `npx baldart configure` to enable." and HALT. Do NOT proceed to step 1.
   - Resolve `$TRUNK` = `git.trunk_branch` from `baldart.config.yml`. **When the key is absent** (consumer updated to ≥4.0.0 without re-running `configure`), do NOT hard-assume `develop` — autodetect the repo's real default branch exactly as worktree-manager does, so `/new` and `nw` agree on the base: `git -C "$MAIN" symbolic-ref --quiet refs/remotes/origin/HEAD` (strip `refs/remotes/origin/`), else the first existing local branch among `develop` / `main` / `master`. A `main`-trunk repo defaulted to `develop` here would diverge from the worktree base `nw` picks and break every `git diff "$TRUNK...HEAD"` gate. Only HALT ("Trunk branch unresolved — run `npx baldart configure`") if nothing resolves. Persist the resolved value as `Trunk branch:` in the tracker `## Worktree` section. **Every later Phase 0 / Phase 6c bash snippet that references the integration trunk MUST use `$TRUNK`, never a baked-in `develop`.** Begin every later consumer with a guard: if `$TRUNK` is empty → HALT with "Trunk branch unresolved — re-read `git.trunk_branch` from the tracker".
   - Resolve `$METRICS` = `paths.metrics` from `baldart.config.yml` (default `docs/metrics`). This is the framework-owned telemetry directory written by Phase 8 (tracker archive, `skill-runs.jsonl`, `sessions/`). The dirty-tree gate (step 3) reads `$METRICS` to recognise — and never surface — its own telemetry output. Persist it as `Metrics dir:` in the tracker `## Worktree` section.

1. **Resolve `$MAIN`** — the absolute path of the main repo (not a worktree). Use `git rev-parse --show-toplevel` when cwd is the main checkout. If `/new` was invoked from **inside a worktree**, that returns the worktree, so resolve the main root from the worktree's parent instead: `cd "$(git rev-parse --show-toplevel)/.." && git rev-parse --show-toplevel` (the worktree lives at `<main>/.worktrees/<name>`). Do **not** use `--git-common-dir` + `/..` (wrong under `git init --separate-git-dir`) or `git worktree list` (its main entry is the git dir, not the working tree, under a separate git dir). This is the same contract as worktree-manager § "Resolving the main repo root". Persist as `Main repo:` in the tracker `## Worktree` section. **Write `$MAIN` to the tracker the moment it is computed** — every later consumer (Phase 6c, Phase 6b) MUST re-read it from the tracker and HALT with "`$MAIN` absent from tracker" if the field is missing or empty, never silently use an undefined `$MAIN` (it does not survive context compaction).

1b. **Migration Gate (BLOCKING only when a migration is *declared* — else a silent no-op)** — resolve DB migrations interactively **before** the worktree exists, so the schema is live before any card builds against it. *Why this exists*: a migration applied to a shared/remote DB is owner-gated, so without this gate it is deferred to the END of the batch — and every downstream card in the batch is then built and verified against a schema that is not yet live (`validation_commands` / QA / E2E / DB-generated `tsc` types fail falsely → those cards cascade into deferral/blocked). Front-loading the migration removes that root cause. **The declaration lives in the EPIC card** (`migration_plan` block — project-specific, authored by the user, typically via the `.baldart/overlays/new.md` overlay). Steps:

   1. **Find the batch's epic card(s).** Collect the distinct `group.parent` of the batch cards (or, for a `-full` invocation, the parent already known). For each, locate the epic YAML in `${paths.backlog_dir}` (filename `<PARENT>-*-epic.yml`, or the card whose `id == <PARENT>` / `group.is_epic: true`) and Read it.
   2. **Look for `migration_plan` with `required: true`.** If **no** epic in the batch declares one → write `## Migration\nnone (no migration_plan declared)` to the tracker and **proceed to step 2** (behaviour is identical to today — zero extra prompts). This is the common case; do not surface anything.
   3. **(declared) Verify the artifacts exist on disk.** For each path in `migration_plan.artifacts`, check it exists under `$MAIN`. If **any is missing** → do **NOT** apply or prompt; write `## Migration\ndeclared but artifact(s) missing: <paths> — degraded to deferred (author the migration first)` to the tracker and **proceed to step 2** (the migration falls back to the current end-of-batch owner-gated deferral — no regression). *(Auto-generating a missing artifact is out of scope; the migration must already be authored to be applied up-front.)*
   4. **(declared + artifacts present) Assemble the apply modalities**, in this source order, de-duplicating by `id`: (a) `migration_plan.apply_modalities` from the epic block; (b) a `## Migration modalities` section in `.baldart/overlays/new.md` if present; (c) any project-memory note on how this project applies migrations; (d) the built-in tail `["Già applicata — prosegui", "Abort"]`. Each modality is `{ id, label, command? }`.
   5. **`AskUserQuestion`** — `"La epic dichiara una migrazione DB (<summary>). Va applicata PRIMA del batch così le card downstream verificano contro lo schema reale. Come procedo?"` with up to 4 of the assembled modalities (always include "Già applicata — prosegui" and "Abort" as the last options). This is a legitimate Phase 0 question (Auto Mode does not override it).
   6. **Execute the choice in `$MAIN`** (project env):
      - **Schema-deploy-from-trunk-only guard** (when `stack.schema_deploy_from_trunk_only: true`): a **command** modality is a remote schema deploy, so it may run only from trunk. Before executing one, check `git -C "$MAIN" rev-parse --abbrev-ref HEAD`; if it is **not** `git.trunk_branch`, do **NOT** apply or prompt for a command modality — write `## Migration\ndeclared but $MAIN is on '<branch>' (not trunk '<trunk>') — front-load deferred per schema_deploy_from_trunk_only (apply from trunk, then re-run)` to the tracker and **proceed to step 2** (the migration falls back to the end-of-batch owner-gated deferral — no regression). "Già applicata — prosegui" stays available (it executes no deploy). When the key is `false`/unset this guard is a no-op.
      - a **command** modality → run it with output to disk (`<cmd> > /tmp/migration-<FIRST-CARD-ID>.log 2>&1`), surface only the exit code; on exit 0 run the optional `migration_plan.verify` probe and require it green too. On **failure** → surface a bounded extract (`tail -n 30`) and re-ask (re-offer the modalities + Abort); never silently proceed against a non-live schema. **Never run a command without the user having selected it.**
      - **"Già applicata — prosegui"** → run the optional `verify` probe if present; otherwise trust the user.
      - **"Abort"** → HALT the batch cleanly (leave the tracker in place); do not create a worktree.
   7. **Record the manifest** in the tracker `## Migration` section: `status: applied|skipped|degraded`, `modality: <id>`, `artifacts: [...]`, `affects_cards: [...]`, `applied_at: <timestamp>`. Phase 1/2 briefings (implement.md) MUST surface, for any card in `affects_cards`, the note *"migration `<summary>` already applied to the active DB — run REAL validation (do not defer the schema AC)"*, so the live schema is actually exercised and the card's migration-apply AC is treated as satisfied, not deferred.

2. **Fetch remote state**:
   ```bash
   git -C "$MAIN" fetch origin --quiet
   ```

3. **Dirty-tree gate (BLOCKING — framework telemetry AND BALDART-managed artifacts are auto-ignored, never surfaced)** — capture the working tree state of the main repo, then **partition it** so the gate fires only on genuine in-progress user work:
   ```bash
   PORCELAIN="$(git -C "$MAIN" status --porcelain)"
   # Drop two classes of framework-owned noise that are NOT user work:
   #  (a) telemetry THIS skill writes in Phase 8 (tracker archive, skill-runs.jsonl,
   #      sessions/) under $METRICS and never commits;
   #  (b) BALDART-managed install artifacts written by `baldart update`/`add`/`push`:
   #      .baldart/generated/ (overlay-merged agents/commands), .baldart/state.json
   #      (versioning ledger), .baldart/skill-conflicts.json. These ARE meant to be
   #      committed, but a reinstall/update leaves them dirty BEFORE the consumer has
   #      committed them — they are framework output, not feature work.
   # NOTE: .baldart/overlays/ is deliberately NOT in this set — overlays are user-owned;
   #       an edited overlay IS genuine work and MUST still gate.
   # Porcelain lines are "XY path"; paths with special chars are quoted. $METRICS from step 0.
   USER_DIRTY="$(printf '%s\n' "$PORCELAIN" \
     | grep -vE "^.{3}\"?${METRICS}/" \
     | grep -vE "^.{3}\"?${METRICS}\"?$" \
     | grep -vE "^.{3}\"?\.baldart/(generated/|state\.json|skill-conflicts\.json)" \
     | grep -v '^[[:space:]]*$')"
   ```
   **Why**: Phase 8 copies the batch tracker into `$METRICS/archive/` and appends to `$METRICS/skill-runs.jsonl` *after* the merge, and never commits them. And `baldart update`/`add` regenerate `.baldart/generated/*` + bump `.baldart/state.json` on every framework sync — left uncommitted until the next reconcile. If either class tripped this gate, **every batch would block on the previous update's own output** — a self-inflicted loop. They are not user work and the user is never asked about them.
   - If `$PORCELAIN` is non-empty but `$USER_DIRTY` is **empty** (only telemetry under `$METRICS` and/or BALDART-managed `.baldart/` artifacts are dirty) → do **NOT** gate. Write `Dirty: framework-managed-only (auto-ignored)` to the tracker and proceed to step 4. No `AskUserQuestion`.
   - If `$USER_DIRTY` is **non-empty** (genuine uncommitted user work) → invoke `AskUserQuestion`:
   - Question: `"Main repo ($MAIN) ha modifiche non committate prima di partire con la batch. Come procedo?"`
   - Options (max 4):
     1. **"Stash automatico (restore in Phase 6c)"** — `$MAIN` is the main checkout (NOT a worktree), so a stash here is safe. Run `git -C "$MAIN" stash push -u -m "baldart-new-phase0-<FIRST-CARD-ID>-<timestamp>"` and write the **stash MESSAGE label** (`baldart-new-phase0-<FIRST-CARD-ID>-<timestamp>`) to the tracker under `## Workspace Snapshot: <message-label> (created <timestamp>)`. Phase 6c resolves the stash by this message — NEVER by a positional `stash@{N}`, because other stashes pushed during the batch (on the main checkout) would shift the index. Do NOT record `stash@{0}`.
     2. **"Committa adesso (apri editor)"** — halt the batch with a clear message asking the user to commit in their terminal and re-invoke `/new`. Do NOT proceed to step 4.
     3. **"Procedi senza stash (lavoro mio, lo gestisco io)"** — explicit user override. Log in tracker: `## Workspace Snapshot: dirty-tree override @ <timestamp> — user accepted responsibility`. Phase 6c will still check post-merge state but will NOT attempt restore.
     4. **"Abort batch"** — halt the batch, leave the tracker file in place, exit.

   Do NOT silently proceed. Do NOT auto-stash without the user picking option 1.

4. **Divergence gate (BLOCKING — framework-management commits do NOT count as orphan commits)** — compare local `$TRUNK` against `origin/$TRUNK` (the trunk resolved in step 0):
   ```bash
   read BEHIND AHEAD <<< "$(git -C "$MAIN" rev-list --left-right --count "origin/$TRUNK...$TRUNK")"
   ```
   `$BEHIND` / `$AHEAD` are the raw counts. Before branching, **reclassify the ahead commits**: commits produced by `baldart update`/`baldart add` (subtree merges into `.framework/`, the `[CHORE] baldart update …` commit, and the regenerated overlay files they carry) are *expected* divergence, not the FEAT-0006 orphan-commit pattern. Count only **genuine** unpushed commits:
   ```bash
   GENUINE_AHEAD=0; GENUINE_LIST=""; FW_SKIPPED=0
   for sha in $(git -C "$MAIN" rev-list "origin/$TRUNK..$TRUNK"); do
     subj="$(git -C "$MAIN" log -1 --format=%s "$sha")"
     # subtree merge (e.g. "Merge commit '…' as '.framework'") or baldart update/add chore
     if printf '%s' "$subj" | grep -qE "as '\.framework'" || printf '%s' "$subj" | grep -qiE "baldart (update|add)"; then
       FW_SKIPPED=$((FW_SKIPPED+1)); continue
     fi
     # touches ONLY framework-managed paths? (empty file list = merge commit, handled above)
     #   - .framework/ (subtree payload)
     #   - .baldart/generated/, .baldart/state.json, .baldart/skill-conflicts.json
     #     (artifacts a reinstall/update commits: regenerated overlay files + version ledger)
     # .baldart/overlays/ is intentionally EXCLUDED — an overlay edit is genuine user work.
     files="$(git -C "$MAIN" show --pretty=format: --name-only "$sha" | grep -v '^$')"
     if [ -n "$files" ] && ! printf '%s\n' "$files" | grep -qvE '^(\.framework/|\.baldart/(generated/|state\.json|skill-conflicts\.json))'; then
       FW_SKIPPED=$((FW_SKIPPED+1)); continue
     fi
     GENUINE_AHEAD=$((GENUINE_AHEAD+1)); GENUINE_LIST="$GENUINE_LIST $sha"
   done
   ```
   `$FW_SKIPPED` framework-management commits are auto-ignored (log them — see below). Branch on `$BEHIND` and the **genuine** ahead count `$GENUINE_AHEAD` (NOT the raw `$AHEAD`):

   - **`BEHIND=0` and `GENUINE_AHEAD=0`** — synchronized for `/new`'s purposes. If `$FW_SKIPPED > 0`, write `Divergence: $FW_SKIPPED framework-management commits ahead (auto-ignored — baldart update/subtree)` to the tracker. **No `AskUserQuestion`.** Proceed to step 5.
   - **`BEHIND>0` and `GENUINE_AHEAD=0`** (only behind) — invoke `AskUserQuestion`:
     - Question: `"Local $TRUNK è behind origin/$TRUNK di $BEHIND commit. Come procedo?"`
     - Options: `[Fast-forward pull adesso]` / `[Procedi senza pull]` / `[Abort]`.
     - On `Fast-forward pull adesso`: if `$MAIN` HEAD == `$TRUNK`, run `git -C "$MAIN" pull --ff-only origin "$TRUNK"`; otherwise log "main HEAD is `<branch>` — fetch already updated `refs/remotes/origin/$TRUNK`, local `$TRUNK` ref will be reconciled in Phase 6c".
   - **`BEHIND=0` and `GENUINE_AHEAD>0`** (genuine UNPUSHED LOCAL COMMITS, this is the FEAT-0006 pattern) — invoke `AskUserQuestion`:
     - Question: `"Local $TRUNK è ahead di origin/$TRUNK di $GENUINE_AHEAD commit locali non pushati (esclusi $FW_SKIPPED commit di gestione framework). Cosa faccio?"`
     - Show **only the genuine** commits inline: `git -C "$MAIN" show -s --oneline $GENUINE_LIST`.
     - Options: `[Push adesso (git push origin $TRUNK)]` / `[Cherry-pick a parte e continua]` / `[Lascia così e procedi (lo gestisco io)]` / `[Abort]`.
   - **`BEHIND>0` and `GENUINE_AHEAD>0`** (diverged both ways) — invoke `AskUserQuestion`:
     - Question: `"Local $TRUNK è diverged da origin/$TRUNK: $BEHIND behind, $GENUINE_AHEAD genuine ahead. Servono entrambe le riconciliazioni. Come procedo?"`
     - Options: `[Rebase local $TRUNK su origin/$TRUNK]` / `[Mostrami i commit e fammi decidere]` / `[Abort]`.

5. **Log and proceed** — write to the tracker:
   ```
   ## Phase 0 — Workspace Hygiene Pre-flight
   Status: PASS
   Main repo: $MAIN
   Trunk branch: $TRUNK
   Dirty: <none | framework-managed-only (auto-ignored) | stashed: <message-label> | user-override>
   Divergence (local…origin/$TRUNK): <0\t0 | framework-mgmt-only (auto-ignored: $FW_SKIPPED) | resolved: ff-pull | resolved: pushed | user-deferred>
   Completed: <timestamp>
   ```
   If any step ended in HALT, set `Status: HALT` and stop — do NOT continue to `## Pre-flight (once)`.

6. **Anti-bypass guard** — before exiting Phase 0, re-read the tracker section you just wrote. If `## Phase 0` is missing or shows `Status: HALT`, refuse to proceed. There is no `--skip-phase-0` flag; if you find yourself reaching for one, treat it as a self-correction trigger and stop.

---

## Pre-flight (once)

1. Read each backlog card from `${paths.backlog_dir}/*.yml` to understand scope and dependencies. Also read the project's canonical-docs registry — typically `${paths.references_dir}/ssot-registry.md` plus any linking-protocol guide — to understand which canonical docs exist for the feature area being implemented. Exact filenames are listed in `.baldart/overlays/new.md`; skip when absent.
1b. **Normalize & validate card baseline (pre-flight gate)** — `/new` is **type-blind**: it runs the same pipeline on `FEAT`/`CHORE`/`BUG`/`DOC`/`PERF`. The universal, profile-aware baseline is the SSOT in [`framework/agents/card-schema.md`](../../../agents/card-schema.md). Detect each card's profile (epic / child / standalone) per that module, then run three stages. (Epic-parent cards are excluded from the per-card loop — skip 1b-i/ii for them; the validator at 1b-iii still checks their EPIC column.)

   **1b-i — HALT on non-derivable fields (ask the user).** Verify the fields that cannot be safely synthesized are present and non-empty:
   - `requirements` (>=1), `acceptance_criteria` (>=1), `files_likely_touched` (>=1 file), and `scope`.
   - `scope_boundaries` is **NOT** in this set (it is conditional — legitimately omitted for standalone cards with no siblings; see card-schema.md).
   If any card fails: log the specific missing fields in `## Issues & Flags`, ask the user to fill them in before proceeding with that card, and continue pre-flight for any remaining valid cards.

   **1b-ii — Back-fill deterministically-computable fields (compute → persist → log).** For each non-epic card missing a derivable field, compute it and **write it back to the card on disk in `$MAIN/${paths.backlog_dir}`** (the main repo path resolved at Phase 0 step 1 — NOT the worktree copy; same F-040 discipline `new2` uses for follow-ups):
   - `review_profile` absent → compute via `prd-card-writer.md § Rule C` (the SSOT), write it.
   - `owner_agent` absent/empty/`claude` → default `coder`, write it. (Never back-fill an epic's `""`.)
   - **Check-and-skip (idempotent):** write ONLY when the field is genuinely absent. A card that already has the field is untouched — so a pre-flight re-entry after compaction never double-writes.
   - Log each write on its own line in `## Issues & Flags`: `[BACKFILL] <CARD-ID>: <field>=<value> (Rule C / coder-default)`.
   - **Commit discipline:** after all cards are processed, if ANY back-fill was written, commit them together in `$MAIN` with the COMMIT_LOCK + doc-freshness precautions of `merge-cleanup.md` Phase 6b (clear stale `COMMIT_LOCK`; `git add` only the back-filled card YAMLs; if the doc-freshness hook blocks, stage `ssot-registry.md`; ≤2 retries): `git commit -m "chore(backlog): normalise card baseline [BACKFILL]"`. Do NOT leave `$MAIN` dirty for the batch duration — an uncommitted backlog edit would surface as noise in the Phase 3/4 git-diff gates.
   - `canonical_docs` / `links.prd` absent on a standalone/non-PRD card → **WARN** only (log, do not block, do not back-fill).

   **1b-iii — Conformance validation.** Run the framework validator `node .framework/framework/scripts/validate-card-baseline.js <card-yaml-path>` (or the path resolved for your install) against each card. It is profile-aware (epic/child/standalone) and catches what 1b-i/1b-ii could not — invalid enum values (`review_profile` ∉ `{skip,light,balanced,deep}`, `owner_agent` ∉ Rule A enum), or a REQUIRED field still missing. Exit 1 → display the per-field errors in `## Issues & Flags` and HALT that card (continue others). If the validator is not reachable in this install, skip with a one-line note (the 1b-i/1b-ii gates still applied).

1c. **Field Registry Validation (pre-flight gate)** — for each card with a `data_fields` block, run the project's field-validation tool if available (path listed in `.baldart/overlays/new.md`; typically `python3 tools/validate-card-fields.py <card-yaml-path>`).
   - If exit 1: display the field errors in `## Issues & Flags` and HALT — ask the user to fix the card before proceeding. Do not start implementation until the card passes validation.
   - If the card has DB-index signals (`db_indexes` — or legacy `firestore_indexes` — or `data.collections`/`data.tables`) but NO `data_fields` block: log WARNING in `## Issues & Flags` — "Card `<ID>` touches the persistence layer but has no `data_fields` block. Field/column names are unvalidated."
   - Cards with no `data_fields` and no storage signals: skip silently.
   - If the project does not ship a field-validation tool: skip this step.
2. Check `${paths.references_dir}/project-status.md` for current state (skip when absent).
3. Determine which cards can run in **parallel** (no shared files/components) vs which must be **sequential** (dependencies or overlapping paths). Use `group.sequence` to determine execution order within a group.
3b. **Build a file-ownership map** — for each card, enumerate every source file it is expected to touch (from `claimed_paths`, `paths`, and the implementation plan). Assign each file to **exactly one agent**. If two cards claim the same file, force those cards sequential on a single agent — do NOT spawn them in parallel. Record this map in the tracker under `## File Ownership Map`. This map is the authoritative source for all agent file permissions in Phase 2.
3c. **Complexity assessment — team mode decision**

   Determine whether to use **team mode** (parallel coder agents with isolated contexts) or keep **sequential mode** (current behavior).

   **Decision logic:**

   1. Read `execution_strategy.recommended_mode` from the epic parent card (if it exists). If present, use it as the default. The `execution_strategy` block is authored by `prd-card-writer` with the canonical shape `groups:` (a list, each entry has `level:` (int) + `cards:` (list)) — consume that shape, not a flat `parallel_group` field.
   2. If no `execution_strategy` exists, compute:
      - Count total cards in batch
      - Count max cards in any single group (from `execution_strategy.groups[].cards`, i.e. cards sharing the same `level`)
      - If no `execution_strategy.groups` exist, compute the levels on-the-fly: build dependency graph from `depends_on`/`blocks`, add conflict edges for cards sharing `(MODIFY)` files, compute topological layers via BFS — each topological layer becomes one group `level`.
   3. Apply threshold:
      - **Sequential mode** if ANY of: total cards <= 3 — OR all cards are in different groups (max group size = 1, nothing to parallelize) — OR all cards form a linear dependency chain.
      - **Team mode** if: total cards > 3 AND at least one group has 2+ cards.
   4. Log decision in tracker:
      ```
      ## Execution Mode
      Mode: team | sequential
      Reason: [e.g., "6 cards, 3 parallel groups, max 3 cards in group 1"]
      ```
   5. Inform the user:
      ```
      Batch: N cards, M file unici
      Gruppi paralleli: K livelli, max P card in parallelo
      Modalita: **team mode** / **sequential mode** — [reason]
      ```
      Proceed without asking (the PRD already approved the strategy).

   When `mode == sequential`, the per-card pipeline below runs exactly as documented. The `execution_strategy.groups` levels are simply ignored. When `mode == team`, skip the per-card pipeline and follow the **Team Mode** section at the end of this document.

   **→ Create the Task spine now.** The execution mode and (in team mode) the wave layout are resolved — create the native Task spine per § "Progress Visibility" A: `Pre-flight` (→ `in_progress`) + one task per card (wave-labelled in team mode) + `Final review` + `Merge & cleanup`. Emit the first Progress Bar with this batch's table. Mark `Pre-flight` → `completed` at the pre-flight resume (step 6d), once both background ops have returned and the consolidated tracker flush is written.

3d. **Codex batch cross-card grounding check** (background — launched together with the worktree-setup subagent in step 4, then a single barrier in step 5)

   > **Why**: a non-Anthropic frontier model (Codex, via `codex-companion.mjs`) reviews the full card batch for cross-card conflicts that per-card plan-auditor checks cannot detect (each plan-auditor sees only one card).

   **Skip decision (provenance-aware — since v4.3.0).** The `/prd` Step 6.6 holistic audit (4-agent team + Codex adversarial pass) ALREADY ran this exact cross-card analysis over the cards when they were authored — its Codex prompt covers `files_likely_touched` conflicts, dependency shadows, implicit ordering, and shared-state mutation across the whole audited set. Re-running here is only worth the Codex call when **something changed between that audit and now**. Compute the skip before launching:

   1. **Always skip** if the batch has only 1 card (no cross-card conflicts possible).
   2. Otherwise, read `metadata.holistic_audit` from every card in the batch and evaluate the four SKIP conditions — **all must hold**:
      - **S1 — provenance present**: every batch card has `metadata.holistic_audit` with non-empty `audited_at`, `audited_commit`, and `audited_set`. (Missing/partial on any card ⇒ condition fails.)
      - **S2 — jointly audited**: all batch cards carry the **same** `audited_set` value (they were reviewed together, not stitched from separate PRD runs).
      - **S3 — batch ⊆ audited set**: every batch card ID is present in that shared `audited_set`.
      - **S4 — no drift on the claimed paths**: no commit touched any path the batch claims, between the audit baseline and the trunk this run grounds on. Using the shared `audited_commit` (`$AC`) and the batch's union of claimed/touched paths from the file-ownership map (step 3b, `$BATCH_PATHS`):
        ```bash
        # empty AC, or AC not in history → treat as drift (do NOT skip)
        DRIFT="$(git -C "$MAIN" log --oneline "$AC".."$TRUNK" -- $BATCH_PATHS 2>/dev/null || echo "DRIFT")"
        ```
        S4 holds **iff** `$AC` is non-empty AND the command succeeds AND `$DRIFT` is empty.
   3. **If S1–S4 all hold → SKIP the Codex cross-card check.** Log in the tracker under `## Cross-Card Conflicts (Codex)`:
      `SKIPPED (provenance) — batch ⊆ holistic_audit set <audited_set>, no drift since <AC short sha> (audited <audited_at>)`. Then proceed to step 4 with the file-ownership map unchanged. Do NOT present anything to the user — this is a non-decision.
   4. **Otherwise → RUN the check** (launch below). Record WHY in the same tracker section so the choice is auditable: `RUN — <reason>` where reason is one of `no/partial provenance` (S1) · `batch spans multiple audit sets` (S2) · `batch not a subset of audited set` (S3) · `drift on <first drifted path>` (S4).

   `$MAIN` and `$TRUNK` are the same variables resolved in Phase 0. This gate degrades safely in every direction: any missing signal, ambiguity, or git error falls through to RUN — never to a silent skip.

   Launch via `Bash` with `run_in_background: true` and `timeout: 300000`:

   ```bash
   AUDIT_FILE="/tmp/codex-crosscard-<FIRST-CARD-ID>-<SESSION-ID>.md" && \
   CODEX_SCRIPT="$(ls -d ~/.claude/plugins/marketplaces/openai-codex/plugins/codex/scripts/codex-companion.mjs ~/.claude/plugins/cache/openai-codex/codex/*/scripts/codex-companion.mjs 2>/dev/null | sort -V | tail -1)" && \
   [ -z "$CODEX_SCRIPT" ] && echo "CODEX_NOT_FOUND" > "$AUDIT_FILE" && exit 1; \
   node "$CODEX_SCRIPT" task --wait "
   Cross-card grounding check for a batch of backlog cards about to be implemented together.
   Your job is to find conflicts BETWEEN cards that per-card reviewers would miss.

   Cards to check (read each file):
   ${CARD_PATHS}

   File-ownership map:
   ${FILE_OWNERSHIP_MAP}

   Check:
   1. FILE CONFLICTS: Do two cards modify the same file in incompatible ways?
      (e.g., Card A adds field X to interface Foo, Card B restructures Foo entirely)
   2. IMPLICIT DEPENDENCIES: Does Card A change a type/function that Card B's
      requirements assume stays unchanged? Flag missing depends_on.
   3. EXECUTION ORDER RISKS: Given the execution_strategy group/level assignments, will parallel
      execution cause merge conflicts or type errors?
   4. SHARED STATE MUTATIONS: Do multiple cards write to the same persistence
      {entity} ({collection|table|item} per `stack.database`) in ways that could
      conflict at runtime?

   For each finding return:
   - **Cards involved**: CARD-A + CARD-B
   - **Conflict type**: FILE_CONFLICT | IMPLICIT_DEP | ORDER_RISK | STATE_MUTATION
   - **Evidence**: exact field/file from each card
   - **Fix**: concrete recommendation (add depends_on, force sequential, etc.)

   If no cross-card conflicts: return PASS.
   " > "$AUDIT_FILE" 2>&1
   ```

   **Variable interpolation**:
   - `${CARD_PATHS}`: newline-separated list of all `- ${paths.backlog_dir}/FEAT-XXXX-*.yml` paths in the batch
   - `${FILE_OWNERSHIP_MAP}`: the file-ownership map built in step 3b

   > `<SESSION-ID>` is the current session id (e.g. `$CLAUDE_CODE_SESSION_ID`, or a run UUID if unset). The task-scoped unique name prevents two concurrent `/new` sessions on the same day from clobbering each other's findings (a date-only name silently collides). Record the exact `$AUDIT_FILE` path in the tracker under `## Cross-Card Conflicts (Codex)` so the result-handling step reads the SAME path — never re-derive it by wildcard/timestamp.

   **Result handling** (read before Phase 1 of first card):
   - **Background completion barrier (BLOCKING):** record the background task id when you launch it; do NOT read `$AUDIT_FILE` until that task has completed. Because this gate's verdict steers Phase-1 card ordering, the first card MUST NOT start while the check is in flight.
   - **TIMED_OUT branch:** the launch sets `timeout: 300000` (5 min). If the background task hits the timeout without producing a terminal result (the tail of `$AUDIT_FILE` shows no `PASS` / conflict block / `CODEX_NOT_FOUND`), treat it as **TIMED_OUT** — do NOT leave the task "in-progress" and silently proceed. Log `cross-card-check: TIMED_OUT` in `## Cross-Card Conflicts (Codex)` and take the SAME fallback as `CODEX_NOT_FOUND`: spawn `code-reviewer` over the batch with the four conflict questions. Never start the batch with cross-card review in an unresolved/in-flight state.
   - **Verdict-extraction discipline (Context economy — the trace MUST NOT re-enter the orchestrator).** `$AUDIT_FILE` holds the full `[codex]` command trace (every `sed`/`grep`/`cat` Codex ran while investigating) *ahead of* the verdict — that trace is pure noise and must never land in the orchestrator's context. **NEVER `cat`/`tail`/`head` the raw file.** Read it only through a filter that strips the trace and keeps the terminal verdict:
     ```bash
     grep -vE '^\[codex\]' "$AUDIT_FILE" | tail -n 60
     ```
     Parse PASS / `CODEX_NOT_FOUND` / conflict-findings from that filtered output only. When logging to the tracker, write the **distilled findings** (one line per conflict: cards + type + one-line fix) — never paste the raw block, and never the trace.
   - Read the exact `$AUDIT_FILE` path recorded in the tracker after the background command completes (always via the trace-stripping filter above).
   - If the file contains **`CODEX_NOT_FOUND`** (Codex unavailable): do NOT silently proceed — **fallback**: spawn the `code-reviewer` agent over the full batch's card YAMLs + file-ownership map with the same cross-card conflict questions (FILE_CONFLICT / IMPLICIT_DEP / ORDER_RISK / STATE_MUTATION). If `code-reviewer` is also unavailable, surface `CAPABILITY_UNAVAILABLE: cross-card-review` to the user and ask whether to proceed without cross-card detection or halt. Never run a multi-card batch with cross-card review silently skipped.
   - If **PASS** or file empty: proceed normally.
   - If **conflicts found**: log in tracker under `## Cross-Card Conflicts (Codex)` and present to user. For each conflict:
     - `FILE_CONFLICT` / `ORDER_RISK` → force the conflicting cards sequential (update file-ownership map).
     - `IMPLICIT_DEP` → add `depends_on` entry to tracker notes (do NOT modify backlog YAML).
     - `STATE_MUTATION` → add warning to both cards' Phase 2 briefings.

4. **Worktree setup — delegate to a BACKGROUND subagent (Context economy: the worktree-manager skill body + the install/build walk-through must NOT enter the orchestrator context).** Do **NOT** invoke `/nw` inline from the orchestrator. Invoking the skill here loads the ~1200-line `worktree-manager` body into the orchestrator's **cached prefix** (re-read every later turn) and then steps through `npm install` → `tsc` → `lint` → `build` across ~15-20 foreground turns, each re-reading the whole growing prefix. Both costs are pure waste in the orchestrator — they belong in a context that is discarded. Instead:
   a. **Resolve the `slug`** (in-context, cheap — the only part the orchestrator keeps). Derive it deterministically: if the parent card (or the lead card) has `git_strategy.branch` set, parse the slug from it (`feat/<PARENT-ID>-<slug>` → `<slug>`); otherwise slugify the parent/lead card `title` (lowercase, non-alphanumerics → `-`, collapse repeats, trim, cap ~40 chars). Do NOT let `/nw` invent its own slug — pass it explicitly.
   a2. **Recovery/idempotency pre-check (BLOCKING — before any spawn; detect via GIT, not the registry).** A **code** worktree is NOT re-creatable: `/nw` **fails loudly on a slug/branch collision** (it does not resume like docs mode — see worktree-manager step 2 "code worktree collision → fail loudly"). And the registry is a **lagging** witness — worktree-manager writes its entry only at the END of `/nw`, *after* the full build (worktree-manager step 6), so for the whole barrier window a worktree can exist on disk while `.worktrees/registry.json` has NO entry for it. Detecting by registry presence would therefore be blind exactly during the compaction window this guard exists for. Use the **git-authoritative** check instead:
      ```bash
      # WT_BRANCH MUST be resolved EXACTLY as /nw will derive it (or detection misses a custom branch
      # and the re-spawn hits the fail-loud collision this guard exists to prevent):
      # use the parent/lead card's git_strategy.branch verbatim if set, else the fallback feat/<PARENT-ID>-<slug>.
      WT_BRANCH="<git_strategy.branch — or — feat/<PARENT-ID>-<slug>>"
      WT_PATH="$(git -C "$MAIN" worktree list --porcelain \
        | awk -v b="refs/heads/$WT_BRANCH" '$1=="worktree"{p=$2} $1=="branch"&&$2==b{print p}')"
      ```
      - **`WT_PATH` empty** (no worktree) → proceed to 4b (spawn fresh).
      - **`WT_PATH` set AND `registry.json` has a complete code entry for this slug** (a finished prior run — `buildVerified` recorded) → **resume**: read `path`/`branch`/`port`/`createdAt`/`buildVerified` from that entry, skip to step 6; re-run the baseline as a single background `Bash` (output to `/tmp`) **only if** `buildVerified` is not `true`.
      - **`WT_PATH` set but NO complete registry entry** (a prior attempt interrupted mid-setup — the normal compaction-mid-barrier state: worktree created, build unfinished, entry never written) → it is a half-built orphan with **no card work** (we are still in pre-flight, zero commits). **Reset clean and recreate**: `git -C "$MAIN" worktree remove --force "$WT_PATH"` then `git -C "$MAIN" branch -D "$WT_BRANCH"` (both ignore-if-absent), then proceed to 4b. A pre-flight worktree has nothing to lose, so a clean recreate is always safe — and it sidesteps the fail-loud collision a naive re-spawn would hit.
      This — detection by `git worktree list`, not the lagging registry — is what makes the deferred-flush pre-flight genuinely idempotent across compaction.
   b. **Spawn ONE background subagent** (Agent tool, **`mode: "bypassPermissions"`** — mandatory per the SKILL.md meta-rules; a background agent that hit a permission prompt with no human present would stall the barrier forever — `run_in_background: true`, `name: "worktree-setup-<FIRST-CARD-ID>"`, a subagent type that can use the Skill tool — `general-purpose`, **`model: "sonnet"`** — the mission runs the multi-step `/nw` skill (group cards, create the worktree, install deps, allocate the dev-server port, write the registry entry, run the baseline) **via the Skill tool**: a sustained tool-execution chain, NOT one-shot plumbing. A too-weak model pattern-matches the expected return block instead of doing the work — the observed 2/2 fabrication was a well-formed block reporting `baseline: pass` in ~6s with **no worktree on disk**. Sonnet executes it for real or fails honestly; either way the returned block is **never trusted as evidence** — the orchestrator's disk-verification gate (step 6a below) is the source of truth) whose ENTIRE mission is to run `/nw` and return the block below. Briefing:
      ```
      Invoke the worktree-manager skill in `/nw` programmatic mode with:
        { cards: [<all card IDs>], groupParent: <PARENT-ID|null>, slug: "<slug>" }
      Let it: group cards, derive the branch from git_strategy.branch (fallback
      feat/<PARENT-ID>-<slug>), create the worktree in .worktrees/, install deps,
      copy env files, assign a free port, update .worktrees/registry.json (all card
      IDs in the `cards` field), and run the baseline (tsc + lint + build)
      UNDER A HARD TIMEOUT — wrap the build step in `timeout 600 <build-cmd>`
      (10 min) so a hung or interactive build cannot stall the pre-flight barrier.
      Redirect ALL install/build/git output to files under /tmp — never inline it.
      Return ONLY this block, nothing else (no logs, no narration, no skill recap):
        worktree_path: <absolute path>
        branch: <branch>
        port: <n>
        created_at: <ISO-8601, stamped WHEN you create the worktree — before install/build>
        baseline: pass | fail | timeout
        baseline_log: <path on failure/timeout, else "-">
      If the build fails, return `baseline: fail` with the log path and STOP.
      If the timeout kills the build, return `baseline: timeout` with the partial
      log path and STOP — do not continue.
      ```
   c. The subagent's context (the worktree-manager skill body, install/build logs, git output) **lives and dies inside it** — the orchestrator receives only the structured block. Combined with the Codex check (3d, also background), this replaces the old ~30-turn foreground pre-flight tail with background ops and a single resume.
   d. **Fallback executor — when the subagent does not produce a VERIFIED worktree.** The subagent's returned block is **never trusted as evidence**; step 6a verifies the worktree on disk. Trigger this fallback when EITHER (i) the subagent returns empty / without the structured block (cannot invoke the Skill tool in this consumer, or the known "0 tool uses · Done" empty-result), OR (ii) it returns a well-formed block but the **step-6a disk gate is VERIFIED:false** (the observed 2/2 fabrication: `baseline: pass` in ~6s with no worktree on disk). In both cases do **NOT** strand the barrier: the orchestrator **falls back to invoking `/nw` inline itself** — a genuinely **DIFFERENT executor** (the full-model orchestrator interpreting the skill prose), **not** a re-spawn of the same subagent and **not** a frozen script, so neither a Skill-from-subagent capability gap nor a weak-model fabrication can recur on the fallback. Slug from 4a; record `worktree_path`/`branch`/`port` and stamp `created_at` from the inline return; then **re-run the step-6a disk gate ONCE**. **Cap (no loop):** the chain is strictly `subagent → inline /nw → HALT+report` — never re-spawn the subagent, never loop the inline path. If the inline attempt ALSO fails the gate, or the failure is **deterministic** (port exhaustion across 3001-3099, a corrupted lockfile, or a genuine `baseline: fail`/`timeout` build error — recreating cannot fix these), **HALT and report** rather than retry. You lose the prefix saving for this one run, but pre-flight completes or halts cleanly — never silently on a phantom worktree. This is the same opt-in-with-fallback discipline the Codex check (3d) and the dynamic-workflow gate use.
5. **End the turn — barrier on ALL launched background ops (wait for every one, not the first).** Having launched the Codex cross-card check (3d) and the worktree-setup subagent (4), the orchestrator has nothing to do until they return. **End the turn** — do NOT poll with `sleep`/`echo "waiting"` loops (§ "Context economy"; same rule as team-mode Step C). Background agents and background `Bash` re-invoke the orchestrator automatically on completion. **Wait for EVERY launched op before step 6**: each completion wakes you separately, so on each wake check whether *all* launched ops have returned — if one is still in flight, **end the turn again** and wait. Do NOT proceed to step 6 on the first completion, or you would read a half-written `$AUDIT_FILE` (and 3d's "If PASS or file empty: proceed normally" would silently swallow real conflicts) or a missing worktree block. (If 3d was SKIPPED by the provenance gate, the only op is the worktree subagent — or none, if step 4a2 resumed an existing worktree.) **Recovery**: a compaction mid-barrier re-enters pre-flight from step 4; the 4a2 git pre-check makes that safe (the worktree is detected via `git worktree list` and resumed-or-reset, never blindly re-created into a fail-loud collision).
6. **On resume — flush the pre-flight tracker sections in one pass (no incremental per-sub-step churn).** When all launched ops have returned:
   a. **Worktree integrity gate (BLOCKING — the disk is the source of truth, not the returned block).**
      - **Honest-failure signals first** (trust these — a reported failure is real, not a fabrication, and recreating the worktree would not fix it): if the block reports `baseline: fail` → STOP and report (point the user at `baseline_log`); if `baseline: timeout` (the build exceeded the launch timeout, §4b/§5) → STOP and report the timeout. Do NOT continue to Phase 1; do NOT recreate.
      - **On `baseline: pass`, VERIFY the worktree on disk YOURSELF** — these are orchestrator `Bash` calls run in this (full-model) context; **never** trust a `verified`-style field returned by a subagent (it is exactly as fabricable as the block itself). With `$WT_PATH`/`$WT_BRANCH` from the block and `$MAIN` from the tracker (`## Worktree` `Main repo:`):
        1. `git -C "$MAIN" worktree list --porcelain` lists `$WT_PATH`;
        2. `test -d "$WT_PATH"`;
        3. `git -C "$WT_PATH" rev-parse --abbrev-ref HEAD` equals `$WT_BRANCH`;
        4. `test -d "$WT_PATH/node_modules"` (proves install actually ran);
        5. *(corroboration)* the `.worktrees/registry.json` entry for this worktree has `buildVerified: true`, and — best-effort — a build artifact dir if the project emits one (`.next`/`dist`/`build`/`out`) is present with mtime newer than `$WT_PATH/package.json`. A missing build dir is **not** a hard fail on its own (a lib/CLI may emit none); checks 1-4 are the real evidence.
      - **All pass → VERIFIED**: proceed to (b). **Any of 1-4 fails →** the block was fabricated or setup is incomplete (the observed 2/2: `baseline: pass`, ~6s, nothing on disk) → route to the **4d fallback chain** (inline `/nw`, re-run this gate ONCE, then HALT — never loop). This gate is the actual fix for the haiku-fabrication failure; do not skip it on the happy path.
   b. **Codex verdict**: handle it via the verdict-extraction discipline in 3d (read `$AUDIT_FILE` through the `[codex]`-stripping filter; keep distilled findings only).
   c. **One-pass tracker flush (no round-trips).** Assemble the pre-flight sections **in-context** (they are all small) and fill them with **back-to-back `Edit`s and no intervening reads**. The win is killing the old read-modify-read-modify churn (~5 incremental edits), **not** the literal tool-call count. **Do NOT `Write`-overwrite the whole file from in-context memory**: Phase 0 already wrote `Main repo:` / `Trunk branch:` / `Metrics dir:` into `## Worktree` and the `Status`/divergence lines into `## Phase 0`, and after a barrier compaction you may no longer hold those in-context (`$MAIN` "does not survive context compaction" — § Phase 0 step 1) — an overwrite-from-memory would silently drop them and HALT later with "`$MAIN` absent from tracker". Surgical `Edit`s on the placeholder sections leave Phase 0's content intact. Sections filled here:
      - `## File Ownership Map` (3b).
      - `## Worktree` — path / branch / slug / port, plus `Created:` = **the subagent block's `created_at`** (worktree-creation time, NOT resume time, so Phase 8's `cycle_time_mins` still spans the build window). On the **4a2 resume** path, `Created:` = the registry entry's `createdAt`; on the **4d inline fallback**, stamp `date -u +%Y-%m-%dT%H:%M:%SZ` at creation. (Never leave `Created:` empty — `cycle_time_mins` anchors on it.)
      - `## Cross-Card Conflicts (Codex)` — distilled findings (the 3d skip-decision already wrote the `SKIPPED`/`RUN — reason` line; append the distilled findings on the RUN path, nothing to add on SKIP).
      - In team mode: `## Team Mode` + `## Parallel Groups` (per team-mode.md).
      `## Execution Mode` was already written at step 3c (it must exist before the Task spine) — do NOT rewrite it here. **Rationale**: pre-flight is idempotent and cheap to redo (step 4a2's git pre-check guards worktree re-creation), so the data sections do not need mid-flight persistence; per-phase incremental writes resume for card execution, where mid-phase recovery actually matters. The file already exists (the skeleton was created at batch start per § Context Tracking; Phase 0 wrote `## Phase 0`) — backfill, do NOT re-create.
   d. **→ Visibility**: mark the `Pre-flight` Task spine entry → `completed` and emit the first wave's Progress Bar.

---
