<!-- 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.5 — Implementation Completeness Check (MANDATORY)

Before triggering any review, you MUST verify that the coder agent implemented **every requirement and acceptance criterion** in the backlog card. This is a blocking gate — do NOT proceed to Phase 3 with unimplemented items.

**Step-by-step**:

0. **Conditional requirements pre-scan** (BLOCKING — before building the main checklist):

   Scan the card's `requirements`, `acceptance_criteria`, `unknowns`, and `notes` fields for
   conditional language patterns: "verify if / verify whether", "if missing / if not present /
   if not found / if not already", "if already exists / if already included", "leave TODO /
   add TODO / leave a note", "add if needed / create if missing", "add support if missing".

   For each match, classify as a **binary-outcome item** and verify:
   a. Positive branch: grep changed files for the implementation.
   b. Negative branch (B-missing): grep codebase for `// TODO [CARD-ID]:` at the relevant location.

   If NEITHER branch artifact exists → classify as `Missing` (not Partial) and trigger the
   gap-resolution fix agent sub-loop immediately for these items (before continuing the checklist).
   **Loop-counter scope**: the max-2 gap-resolution cap is tracked PER ITEM, not per phase. The
   step-0 pre-scan and the step-5 main loop share the same per-item budget — an item that consumed
   both retries here is NOT given two fresh retries at step 5; an item never touched here keeps its
   full budget at step 5. This prevents both over-fixing (4 spawns) and starvation (0 retries left).

   Also check: if the card has conditional requirements but the completion report has no
   `conditional_items` section → flag as `Missing` (the coder must document the branch taken).

1. **Update tracker**: phase = "2.5-completeness".
2. Re-read the backlog card (`requirements`, `acceptance_criteria`, `unknowns`, and `definition_of_done` fields).
3. Build a checklist — one row per item:

   ```
   ## Completeness Check — <CARD-ID>
   | # | Requirement / Criterion | Status | Evidence |
   |---|------------------------|--------|----------|
   | 1 | [requirement text]     | Done / Missing / Partial | [file:line or "not found"] |
   | 2 | [acceptance criterion] | Done / Missing / Partial | [file:line or "not found"] |
   ```

4. For each item, verify by **reading the actual implementation code** — but read **narrowly** (per
   § "Context economy" → Targeted Reads): use the coder completion report's `evidence: file:line` as
   the index, then `Grep` (or `Read` with a tight `offset`/`limit` around that line) for the specific
   symbol/route/field — do NOT Read whole files into context to confirm one item. The report is your
   index of WHERE to look; you still verify independently (do NOT trust its `status` alone), just
   without pulling entire files inline.

   **Verification depth per requirement type:**
   - **"Create endpoint/route"** → verify: route file exists, handler has correct HTTP method,
     request validation present, response shape matches requirement, auth middleware applied.
   - **"Add UI component/page"** → verify: component file exists, renders the required elements,
     handles loading/error/empty states if specified, is wired into the page/layout.
   - **"Add field/collection"** → verify: type definition updated, field used in read AND write
     paths, validation present if specified.
   - **"Integrate with X"** → verify: import exists, function is called with correct parameters,
     error handling present.
   - **Data fields completeness** → if card has `data_fields` block:
     - For each `status: existing` field: grep the changed files to confirm the field name is
       referenced in the implementation. A declared-but-unused field is a red flag (wrong name
       or dead code). Log in checklist as Partial if not found.
     - For each `status: new` field: verify the field is BOTH written (setter/creator path)
       AND read (getter/reader path) in the changed files.
     - For each `status: modified` field: verify old-name references are removed and
       new-name references exist.
   - **DB compound query** → verify: if the card has a `db_indexes` (or legacy
     `firestore_indexes`) field, check that EVERY listed index has been shipped to
     the artifact matching `stack.database`:
     - `firestore` → entries exist in `firestore.indexes.json` (match collection +
       fields + order); also grep the query code to confirm `where()` + `orderBy()`
       field names match the index definition exactly. Missing index = CRITICAL
       (runtime 500 FAILED_PRECONDITION).
     - `postgres`/`supabase`/`mysql`/`sqlite` → a migration file exists declaring
       the index (or `prisma/schema.prisma` `@@index([...])`); grep the query to
       confirm the column names match. Missing migration = CRITICAL (silent
       Seq Scan in production on hot path).
     - `mongodb` → `createIndex` call exists in the bootstrap script (or migration);
       compound field order in the call matches equality → range → sort in the query.
       Missing = HIGH (no immediate error, but slow aggregation under load).
     - `dynamodb` → the GSI/LSI is declared in the IaC table definition AND the
       query uses the GSI name via `IndexName`. Missing = CRITICAL (throttle on
       hot partition).

   If the completion report has `items_skipped` that is not "none", immediately flag those
   items as Missing regardless of the agent's justification.

   - **[UI-ONLY] Design-system conformance** → run ONLY when `briefing = "ui-expert"` from step 6b
     AND `features.has_design_system: true` in `baldart.config.yml`:
     - Grep the changed files for hardcoded design values that bypass the registry:
       hex colors (`#[0-9a-fA-F]{3,8}`), `rgb(`/`rgba(`, `box-shadow:` with inline
       offsets/blurs, `border-radius:` with px/rem literals, `font-size:` with raw
       px/rem (outside the type scale), `padding`/`margin` with raw px (outside the
       spacing scale). Each hit on a NEW or MODIFIED line = Partial — the value
       should come from a token, not a literal.
     - Verify the completion report includes the `design_system_coherence:` block
       required by the UI-ONLY briefing. Missing block = Missing (orchestrator
       cannot confirm Post-Intervention Coherence Check ran).
     - For each new primitive declared by the report (or detected via Grep for new
       components in `${paths.components_primitives}`), verify the matching
       `${paths.design_system}/components/<Name>.md` spec was created in the same
       commit. Missing spec = Partial — fix by re-spawning `ui-expert` with the
       narrow instruction "ship the component spec for <Name> per
       design-system-protocol.md".
     - For each token change declared by the report, verify the entry exists in
       `${paths.design_system}/tokens-reference.md`. Missing entry = Partial.
     - If `features.has_design_system: false`, log "DS conformance skipped — flag is false" and continue.

5. Classify each item:
   - **Done** — code exists AND satisfies the FULL scope of the requirement (not just partial).
   - **Partial** — some code exists but the criterion is not fully satisfied (e.g., UI renders but no error state, endpoint exists but wrong response shape).
   - **Missing** — no evidence found in the codebase, OR the coder agent explicitly skipped it.

**Gap resolution** (for Partial and Missing items):

- Spawn a **targeted fix coder agent** with:
  - The exact list of unimplemented items (copy the checklist rows)
  - The file-ownership restrictions from `## File Ownership Map`
  - The instruction: "Implement ONLY these missing items. Do not refactor or expand scope."
- After the fix agent completes, re-run the static gates the fix could have broken — `npm run lint`, `npx tsc --noEmit` (when `stack.language` includes typescript), and `npm test` (when `has_toolchain`, the configured `toolchain.commands.{lint,typecheck,test}` verbatim — § "Toolchain gates") — not just build + lint (a gap-fix can introduce a type error or break a test that the earlier Phase 2 gate had passed). Redirect each to `/tmp/<gate>-<CARD-ID>.txt` per § "Context economy" (never inline).
- Re-verify each fixed item against the code — do NOT trust the agent's self-report.
- Repeat this sub-loop up to **2 times** (per-item budget, shared with step 0 — see "Loop-counter scope"). After 2 loops, if items remain Partial or Missing:
  - Log in `## Issues & Flags`: list each unimplemented requirement.
  - Ask the user whether to proceed to review despite the gap or stop.
  - **Handoff to Phase 2.5b**: any AC that is still `Partial` or `Missing` at this point (whether the user said "proceed" or not) MUST be seeded into the Phase 2.5b AC Closure Ledger as a `deferred` row with `User-approved? pending` — the user's "proceed to review" answer here does NOT auto-approve the deferral; Phase 2.5b Step 4 still asks per-AC. Do NOT let "proceed" silently mark these ACs `implemented`.

5b. **API contract check**:
    - From `git diff --name-only "$TRUNK...HEAD"`, identify `route.ts` files that existed before this card (MODIFIED) vs newly added (NEW).
    - **NEW routes** are exempt from the breaking-change comparison (no prior contract to violate) BUT must still be checked for a **path collision**: read `${paths.references_dir}/api/index.md` and confirm the new route's URL path is not already documented under a different module. A collision → `[API-CONTRACT] PATH-COLLISION` in `## Issues & Flags` (BLOCKER — a second handler for a documented path).
    - For each MODIFIED route:
      a. Derive the API module from path (e.g., `src/app/api/v1/booking/[id]/route.ts` → `booking`).
      b. Read `${paths.references_dir}/api/<module>.md` (or subdirectory match).
      c. Read the modified route, extract response shape from `NextResponse.json()` calls.
      d. Compare against documented schema.
      e. Flag: removed fields or changed types → `[API-CONTRACT] BREAKING` in `## Issues & Flags` (BLOCKER).
         Added fields only → `[API-CONTRACT] ADDITIVE` (informational).
         No doc found → `[API-CONTRACT] NO-DOC` (informational).

5c. **Reference-aliasing mutation check** (MANDATORY — deterministic detector, reference-aliasing mutation prevention).

Detect call sites that pair a helper invocation with in-place mutation of an input array
without an identity guard. Anti-pattern (the reference-aliasing mutation family):

```ts
const result = filterHelper(input, ...);
input.length = 0;          // ← wipes both input AND result when result === input
input.push(...result);     // ← pushes zero elements (result was just emptied)
```

This detector targets TypeScript/JavaScript array-mutation patterns. **Guard: run it only when `stack.language` includes `typescript` or `javascript`.** For other languages it does not apply — log `[ALIAS-MUTATION] skipped — stack.language not ts/js` and continue (do NOT report a non-TS diff as "clean", which would imply the check ran).

Run this exact bash block in the worktree (`$TRUNK` = the trunk branch resolved in Phase 0):

```bash
cd <worktree-path>
CHANGED_TS="$(git diff --name-only "$TRUNK...HEAD" -- '*.ts' '*.tsx' '*.js' '*.jsx' 2>/dev/null \
              || git diff --name-only HEAD~1..HEAD -- '*.ts' '*.tsx' '*.js' '*.jsx')"

ALIAS_HITS=()
for f in $CHANGED_TS; do
  [ -f "$f" ] || continue
  # Heuristic: find lines matching `arr.length = 0` followed within 3 lines by `arr.push(...result)`.
  # Capture the variable name; flag for human/agent review.
  python3 - "$f" <<'PY'
import re, sys
src = open(sys.argv[1]).read().splitlines()
for i, line in enumerate(src):
    m = re.search(r'(\w+)\.length\s*=\s*0', line)
    if not m: continue
    var = m.group(1)
    # Look back 5 lines for a const/let assignment from a function call returning to a different name,
    # then forward 3 lines for `var.push(...other)`.
    back = "\n".join(src[max(0,i-5):i])
    fwd  = "\n".join(src[i+1:i+4])
    assign = re.search(rf'const\s+(\w+)\s*=\s*\w+\([^)]*\b{re.escape(var)}\b', back)
    push   = re.search(rf'{re.escape(var)}\.push\(\.\.\.(\w+)\)', fwd)
    if assign and push and assign.group(1) == push.group(1):
        print(f"{sys.argv[1]}:{i+1}: ALIAS-MUTATION candidate — '{var}.length = 0' followed by '{var}.push(...{push.group(1)})' where {push.group(1)} comes from a call that received {var}")
PY
done
```

For each hit, classify by reading 10 lines of context around the match:

- **Guarded** — an `if (<assignedName> !== <var>)` check wraps the reset+push block. SAFE. Log `[ALIAS-MUTATION] guarded` (informational, no action).
- **Defensively cloned** — the helper was called with `[...var]` or `var.slice()` instead of `var`. SAFE. Log `[ALIAS-MUTATION] cloned` (informational).
- **Helper always returns new array** — read every `return` in the helper. If ALL paths return `[...candidates]` / `candidates.filter(...)` / a new array literal, identity collision is impossible. SAFE. Log `[ALIAS-MUTATION] helper-immutable` (informational).
- **Un-guarded** — none of the above. Flag `[ALIAS-MUTATION] BLOCKER` in `## Issues & Flags` with file:line and the captured pattern.

For each `[ALIAS-MUTATION] BLOCKER`:

- Spawn a targeted fix `coder` agent with the exact file:line and instruction: "Add identity guard `if (result !== input) { ... }` around the in-place reset, OR clone the input with `[...input]` before passing it to the helper. Add a regression test on the caller pattern, co-located with the helper's existing tests following this project's test convention (e.g. a `*-reference` negative-control test next to the helper's test file). Read `agents/coding-standards.md § Reference-Aliasing Mutation Patterns` before implementing."
- Re-run the detector after the fix. The hit MUST classify as Guarded, Cloned, or helper-immutable. Repeat **max 2 times**.
- If still un-guarded after 2 loops → log in `## Issues & Flags` and ask the user.

Rationale (reference-aliasing incident class): a helper that returns its same input reference on some code paths, combined with a call site that did `arr.length = 0; arr.push(...result)` without an identity guard, silently wiped the result. The helper passed unit tests in isolation; the bug only manifested end-to-end. This detector closes that detection gap. Full policy: `agents/coding-standards.md § Reference-Aliasing Mutation Patterns`.

5d. **Caller-pattern test coverage check** (MANDATORY — when card introduces or modifies an exported helper consumed by 1+ caller with in-place mutation).

If the card's diff includes BOTH (a) an `export function` / `export const` declaration in a shared-library file (under `${paths.lib}` if defined, else the project's lib/util dir) AND (b) at least one call site in another file that mutates an input array in place (detected via the bash block in 5c), verify that a test file exists that exercises the **caller pattern** — not only the helper in isolation.

Required test characteristics:

- At least one case asserts the post-call state of the input array (e.g. `assert.strictEqual(arr.length, N)` after the helper invocation).
- At least one **negative-control case** documents the failure mode that would occur if the identity guard / defensive clone were removed.

If the test is missing:

- Spawn a targeted fix `coder` agent to write the test. The test MUST live in a file co-located with similar helpers, following this project's test convention (do NOT invent a directory — mirror where the helper's existing tests live).
- This is BLOCKING for Phase 4 commit when the helper is on a high-risk path (a path listed in `paths.high_risk_modules` in `baldart.config.yml`, e.g. auth / payments / a shared scoring-or-ranking primitive). On non-high-risk paths it's a HIGH-severity flag but the orchestrator may proceed and defer to `/codexreview`.

Rationale: in the originating incident, the helper's caller pattern was never tested (tests were deferred to a later E2E card); the helper passed in isolation while the caller-side aliasing bug shipped. A caller-pattern test at the helper-card level catches this class earlier.

6. **Update tracker**: phase = "2.5-completeness DONE", log result (PASS / GAPS REMAIN + count).

### Phase 2.5b — AC-Closure Gate (BLOCKING — Scope Closure Discipline)

This gate enforces `framework/agents/workflows.md § Scope Closure Discipline` at the per-card level. It runs after Phase 2.5's gap-resolution loops have settled and BEFORE Phase 2.55 (Simplify). It is non-skippable.

**Why this exists**: Phase 2.5's fix loops handle items an agent can implement autonomously. They do NOT handle the case where the implementer (or you, the orchestrator) decides — under context pressure or perceived complexity — that an AC is "good enough as is" or "already covered by an existing component". Burying that decision in the card YAML's `implementation_notes`, in a commit message, or in a final recap is a silent scope reduction. The decision belongs to the user.

**Step-by-step**:

1. **Update tracker**: phase = "2.5b-ac-closure".

2. **Build the AC Closure Ledger** — one row per `acceptance_criteria` entry from the card YAML (NOT requirements — only the AC list, because ACs are the user-facing contract):

   ```
   ## AC Closure Ledger — <CARD-ID>
   | AC# | Text (verbatim) | Status | Evidence (file:line) | User-approved? |
   |-----|-----------------|--------|----------------------|----------------|
   | AC-1 | [text] | implemented | src/x.ts:42 | n/a |
   | AC-2 | [text] | deferred | n/a | pending |
   ```

   Status enum: `implemented` | `deferred`. The Phase 2.5 categories map as follows: `Done` → `implemented`; `Partial` or `Missing` (after gap-resolution exhausted retries) → `deferred`. There is no third option — an AC is either in the diff or it is being deferred.

3. **Rationalization scan (BLOCKING for every `deferred` row)** — read the coder's completion report and the card's `implementation_notes`. If the justification for a `deferred` AC matches any of the patterns below, classify the row as `RATIONALIZATION_NEEDS_PROOF`:

   - "covered by <X>" / "already covers" / "existing <X> handles" / "<X> already does"
   - "implicit in <X>" / "no longer needed" / "redundant with <X>"
   - "out of scope of this card" (when the AC is clearly listed in the card YAML)

   For each `RATIONALIZATION_NEEDS_PROOF` row, the orchestrator MUST verify by reading `<X>` and producing a line-for-line scope mapping between the AC text and the alleged coverage. The mapping goes in the tracker under `## Rationalization Verification`. If the mapping does not hold (or cannot be produced confidently), the row stays `deferred` and proceeds to Step 4 — the rationalization does NOT auto-approve the deferral.

4. **User gate (BLOCKING `AskUserQuestion` — one per `deferred` AC that is not pre-approved)**:

   For each `deferred` row whose `User-approved?` is `pending`, invoke `AskUserQuestion` with:
   - Question: `"AC-<N> of <CARD-ID> non implementata. Testo verbatim: '<AC text>'. Motivazione dell'implementer: '<reason or 'nessuna — silent skip'>'. Come procedo?"`
   - Options (max 4):
     1. **"Implementa adesso"** — spawn a targeted fix `coder` agent scoped to this card's MAY EDIT files (from `## File Ownership Map`) with the instruction "Implement ONLY AC-<N>: '<AC text>'. Do not refactor or expand scope." Re-run Phase 2.5 verification on the resulting diff — **including the mandatory sub-steps 5b (API contract), 5c (alias-mutation), 5d (caller-pattern test)** if the fix touched a route, an exported helper, or a call site (a single-AC fix can still introduce these). **Hard cap: 2 attempts on the same AC.** On the 2nd failure, do NOT re-offer "Implementa adesso" — re-invoke `AskUserQuestion` with only options 2/3/4 (approve deferral / follow-up card / stop), so the loop cannot recur unbounded.
     2. **"Approva il deferral"** — record `[USER-APPROVED DEFERRAL] <today>: <user-supplied reason>` on its own line in the card's `implementation_notes`. Mark the row `User-approved? yes`.
     3. **"Sposta su follow-up card"** — create the follow-up card by **delegating to the `prd-card-writer` agent in Standalone Single-Card Mode** (NOT a hand-written stub — backlog cards are owned by `prd-card-writer`, same discipline as `new2`/`new2-resolve`). Brief it: `MODE: standalone`, write `${paths.backlog_dir}/<CARD-ID>-followup-AC<N>.yml` with `status: TODO`, derived from the deferred AC — a non-empty `requirements` (≥1, derived from the AC), `acceptance_criteria` = the verbatim AC text (≥1), `files_likely_touched` (≥1, carried from the parent card's ownership map for this AC), `owner_agent` routed to the AC's domain, and `review_profile` per Rule C — i.e. the full STANDALONE baseline of `framework/agents/card-schema.md`. If `prd-card-writer` is unavailable (total outage), fall back to a minimal valid stub carrying at least the step-1b HALT fields (`requirements`/`acceptance_criteria`/`files_likely_touched`/`scope`); the consumer back-fill (step 1b-ii) then fills `review_profile`/`owner_agent` on first ingestion. Mark the row `User-approved? yes (follow-up: <new-card-id>)`. Do NOT proceed to Phase 2.55 until the follow-up file exists on disk and passes the step-1b field check.
     4. **"Ferma il batch"** — halt the orchestrator, leave the worktree intact, log the reason in `## Issues & Flags`. Do NOT commit.

   Do NOT batch the question across multiple ACs — one `AskUserQuestion` per AC, so the user sees each AC's text in isolation. Issue the questions sequentially.

5. **`implementation_notes` deferral audit (cheap text grep — also BLOCKING)** — before exiting the phase, scan the card YAML's `implementation_notes` field (post-edits, including any text added during this phase) for any of these phrases:

   - `deferred` / `deferral` / `defer to`
   - `out of scope` / `scope cut`
   - `covered by` / `already handles` / `redundant`
   - `skipped` / `dropped`

   When scanning a line, FIRST check for the `[USER-APPROVED DEFERRAL]` prefix; if present AND the corresponding AC row has `User-approved? yes`, the line is RESOLVED — do NOT re-match the rationalization keywords inside the user-supplied reason text (otherwise a freeform reason like "approve deferral: existing component already handles this" would re-flag an already-approved AC). Only lines WITHOUT the prefix (or whose AC row is not `yes`) are silent deferrals that escaped Step 4 — for those, trigger the Step 4 `AskUserQuestion` for the AC referenced (or for "the unspecified AC implied by this note" if no AC is named, asking the user to clarify which AC the note refers to). **If Step 5 triggers any new Step 4 question, fully resolve it (loop back into Step 4) BEFORE running Step 6** — do NOT write `2.5b-ac-closure DONE` while a triggered question is still open.

6. **Update tracker** (only after Steps 3–5 have fully settled, including any Step-5-triggered Step-4 question): phase = "2.5b-ac-closure DONE". Persist the AC Closure Ledger AND the Rationalization Verification (if any) in the tracker. Log counts on one line:

   ```
   ac-closure: implemented=N | user-approved deferrals=M | follow-up cards created=K | rationalization-verifications passed=L
   ```

The gate exits PASS only when every ledger row is either `implemented` or `deferred` with `User-approved? yes`. Phase 2.55 (Simplify) MUST NOT start until this gate exits PASS.

#### Context-pressure exception (still BLOCKING — no escape)

If at any point in Phases 2 / 2.5 / 2.5b you detect context-window pressure (your context is being summarized, you have ≥ 1 forced compaction, you are racing through verification, you are tempted to "just mark this AC as covered"), do NOT compensate by skipping ACs. The correct response is:

1. Stop the current card's pipeline.
2. Write the current state to the tracker under `## Issues & Flags` with the prefix `[CONTEXT-PRESSURE]` (which Phase 2 finished, which ACs are open, which files are dirty).
3. Invoke `AskUserQuestion` with options: (a) finish remaining ACs in a fresh `/new` session that resumes from the tracker, (b) commit the complete ACs and route the rest through this gate's Step 4, (c) hand off the worktree intact to the user.

Auto Mode does NOT override this rule. "Bias toward proceeding" applies to routine decisions ("should I rerun this check?", "should I confirm this safe rename?"), NEVER to scope reduction.
