---
name: new2
effort: high
description: >
  EXPERIMENTAL workflow-hosted variant of /new (A/B testing). Implements one or
  more backlog cards end-to-end by delegating the WHOLE batch to a background
  dynamic workflow — so subagent output never enters the main orchestrator
  context. Fully autonomous (zero AskUserQuestion): every /new gate is replaced by
  a deterministic policy + a self-healing resolution pass. Claude-only (needs the
  Workflow tool). Usage: /new2 CARD-IDS (same arg grammar as /new). Triggers on:
  /new2, "implementa le card con workflow", "new2".
---

> **EXPERIMENT — not a replacement for `/new`.** `new2` exists to A/B-test the
> context-economy of hosting the batch orchestration inside a Claude Code dynamic
> workflow instead of the main loop. `/new` remains the SSOT, the cross-tool
> default, and the recovery-safe path. Do NOT route to `new2` unless the user
> explicitly asks for it.

> **ZERO-ASK CONTRACT.** A dynamic workflow cannot prompt the user mid-run. `new2`
> therefore runs the entire batch autonomously: every `/new` `AskUserQuestion`
> gate is replaced by a deterministic policy (auto-resolve seamless defaults, or
> fail → self-healing `new2-resolve`, or — last resort — auto-materialise a
> tracked follow-up card). Destructive/outward ops (`reset --hard`, force-push,
> stash drop) are NEVER auto-run; they degrade to "leave intact + report".

## Project Context

**Reads from `baldart.config.yml`:** `paths.backlog_dir`, `paths.references_dir`, `paths.metrics`, `paths.high_risk_modules`, `git.trunk_branch` (autodetected from `origin/HEAD` when absent), `git.merge_strategy`, `stack.*` (incl. `stack.schema_deploy_from_trunk_only` — the trunk-only schema-deploy gate guard), and `features.has_e2e_review` / `features.has_design_system` / `features.has_lsp_layer`.
**Gated by features:** `features.has_backlog` (REFUSES to run when `false` — operates on backlog cards by definition).
**Overlay:** loads `.baldart/overlays/new.md` if present (shared with `/new`: canonical-docs registry, tooling paths).
**On missing/empty keys:** the deterministic pre-flight autodetects (trunk via `origin/HEAD`); only HALT when nothing resolves. See `framework/agents/project-context.md` § 3.

## Effort

**Baseline:** `effort: high` (frontmatter — the workflow runs a full implement+review
batch autonomously, so it warrants deep reasoning). **Inline override:** pass
`effort=<low|medium|high|xhigh|max>` anywhere in the invocation; detect once at
kickoff, strip the token, and forward it to the workflow via `args.flags.effort`
(agents inside the workflow honour it). Mapping/parsing: `framework/agents/effort-protocol.md`.

---

## What this skill does (thin coordinator — keep it out of the way)

`new2` holds **almost no content in the main loop**. Its entire job is: parse the
invocation, gather project facts, hand the batch to the `new2` background workflow,
and present the report it returns. All the heavy orchestration (architect, coder,
reviewers, Codex, QA, E2E, merge) lives in the workflow's isolated runtime — its
output never lands in your context.

### Step 1 — Degradation gate (BLOCKING, first thing)

`new2` is **useless without the `Workflow` tool** (its whole point is the background
runtime). If the `Workflow` tool is **not available** in this session (OpenAI Codex
install — workflows are Claude-only; `disableWorkflows` set; or a Claude Code older
than the dynamic-workflows research preview), do NOT attempt any fallback:

> Print: `"/new2 richiede i dynamic workflow di Claude Code (non disponibili in questa sessione). Usa /new — stessa semantica, esecuzione nel main loop."` and **HALT**.

Also verify the workflow script is linked: if `.claude/workflows/new2.js` is absent,
print the same guidance and HALT (the framework did not link it — Codex tree, or a
pre-v4.16 install).

### Step 2 — Parse the invocation (same grammar as `/new`)

Parse `CARD-IDS` exactly as `/new` does — space-separated, hyphen-range
(`GLOB-001-GLOB-008`), comma-separated, and **epic expansion** (`-full` / `--full`
expands all children of a parent: filename prefix `<PARENT>-` ∪ cards whose
`group.parent == <PARENT>`, parent excluded, sorted by `group.sequence`). Strip the
`-full` / `--full` and `-stats` / `--stats` tokens and the `effort=<level>` token
before treating the rest as card IDs. If no card IDs are given, ask which cards
(this is the ONE pre-launch question — it is NOT a mid-run gate). Resolve each card
to its backlog YAML path under `${paths.backlog_dir}`.

### Step 3 — Feature gate + gather facts

- Read `features.has_backlog`. If `false` → print the same refusal as `/new`
  (`"requires features.has_backlog: true — run npx baldart configure"`) and HALT.
- Read `baldart.config.yml` and resolve: `$MAIN` (the main repo absolute path — walk
  up from a worktree if needed), `$TRUNK` (`git.trunk_branch`, else autodetect via
  `git symbolic-ref refs/remotes/origin/HEAD` → `develop`/`main`/`master`), `$METRICS`
  (`paths.metrics`, default `docs/metrics`), and the `paths.*` / `stack.*` /
  `features.*` the agents will need. Resolve `refModulesBase` = the directory holding
  `/new`'s reference modules (`.claude/skills/new/references` — the workflow's agents
  Read them for the review SEMANTICS, so `new2` stays a thin orchestration host and
  the A/B test isolates the host, not the protocol).

### Step 3.5 — Migration Gate (BLOCKING only when a migration is *declared* — else a silent no-op)

The workflow runs autonomously and **cannot** apply an owner-gated DB migration mid-run, so today a
declared migration is deferred to the end of the batch — and every downstream card is 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). This gate front-loads the migration: it runs
**here, in the skill** (the main loop — it can interact; the workflow's zero-ask contract is
untouched), so the schema is live **before** the workflow starts. It mirrors `/new` Phase 0 step 1b
(`references/setup.md`) — same algorithm, same `migration_plan` epic-card convention.

1. **Find the batch's epic card(s)** — distinct `group.parent` of the resolved cards (or the `-full`
   parent). Read each epic YAML in `${paths.backlog_dir}` (`<PARENT>-*-epic.yml`, or the card whose
   `id == <PARENT>` / `group.is_epic: true`).
2. **Look for `migration_plan` with `required: true`.** None → set `migration = { status: 'none' }`
   and go to Step 4 (identical to today — zero extra prompts; do not surface anything).
3. **(declared) Verify `migration_plan.artifacts` exist on disk under `$MAIN`.** Any missing → do NOT
   apply or prompt; set `migration = { status: 'degraded', reason: 'artifact missing', artifacts }`
   and go to Step 4 (the migration falls back to the current end-of-batch owner-gated deferral — no
   regression).
4. **(declared + present) Assemble the apply modalities**, de-duped by `id`, in source order:
   (a) `migration_plan.apply_modalities`; (b) a `## Migration modalities` section in
   `.baldart/overlays/new.md`; (c) any project-memory note on how this project applies migrations;
   (d) built-in tail `["Già applicata — prosegui", "Abort"]`.
5. **`AskUserQuestion`** — `"La epic dichiara una migrazione DB (<summary>). La applico PRIMA del
   batch così le card downstream verificano contro lo schema reale. Come procedo?"` with up to 4
   modalities (always include "Già applicata — prosegui" and "Abort"). This is the SAME class as the
   Step-2 "ONE pre-launch question" — pre-launch, not a mid-run gate; the zero-ask contract is about
   the *workflow*, which is untouched.
6. **Execute the choice in `$MAIN`**:
   - **Schema-deploy-from-trunk-only guard** (when `stack.schema_deploy_from_trunk_only: true`): a
     **command** modality is a remote schema deploy → run it only from trunk. Check
     `git -C "$MAIN" rev-parse --abbrev-ref HEAD`; if it is not `git.trunk_branch`, do NOT apply a
     command modality — set `migration = { status: 'degraded', reason: 'off-trunk: schema_deploy_from_trunk_only' }`
     and skip to Step 4 (the migration falls back to the workflow's end-of-batch owner-gated deferral →
     a `db-migration-deploy` residual; apply it from trunk, then re-run). "Già applicata — prosegui"
     stays valid (no deploy). No-op when the key is `false`/unset.
   - a **command** modality → run with output to `/tmp/migration-<firstCard>.log`, surface only the
     exit code; on exit 0 run the optional `migration_plan.verify` probe and require it green. On
     failure → bounded extract (`tail -n 30`) + re-ask. **Never run a command the user did not pick.**
   - **"Già applicata — prosegui"** → run `verify` if present; else trust the user.
   - **"Abort"** → HALT cleanly; do NOT call the workflow.
7. **Build the manifest** to pass to the workflow:
   `migration = { status: 'applied'|'skipped', modality: <id>, summary, artifacts: [...], affects_cards: [...], appliedAt: ts }`.

### Step 4 — Delegate the whole batch to the workflow

Call the `Workflow` tool:

```
Workflow({ name: 'new2', args: {
  cardIds,            // expanded list (ranges/-full already resolved here)
  cardPaths,          // resolved backlog YAML paths
  mainRepo,           // $MAIN absolute path
  trunk,              // resolved $TRUNK
  metricsDir,         // $METRICS
  refModulesBase,     // .claude/skills/new/references (semantic SSOT)
  config,             // the parsed baldart.config.yml (paths.*/stack.*/features.*/git.*)
  ts,                 // ISO timestamp NOW — the workflow has no clock (Date.now() unavailable there)
  migration,          // Step-3.5 manifest: { status:'none'|'applied'|'skipped'|'degraded', modality?, summary?, artifacts?, affects_cards? }
  flags: { stats, effort, full }
}})
```

Record the kickoff time (`ts`) before the call — Step 5 uses it to stamp the
real wall-clock duration (the workflow cannot read a clock).

The workflow runs the entire batch in the background. **Do not poll it** — it
returns when the batch is done. It returns:

```
{ report, perCardResults, gateLedger, residualFollowups, residuals,
  degraded, degradationReasons, telemetry }
```

- `report` — ready-to-show markdown batch summary.
- `residuals` — the **OFFLINE-SAFE ledger of record**: every residual the workflow
  could not finish, each `{ card, kind, evidence, materialized }`. The `materialized`
  flag is **advisory only** — `true` means the workflow *attempted* a write (possibly
  into a worktree that never merged), not that a card exists on disk in the main repo.
  **You (the skill) must reconcile EVERY residual against the main-repo disk** (Step 5.1).
  **v4.31.0 — the workflow now self-corrects this ledger before returning**: it re-verifies
  every still-pending code/doc residual against the worktree HEAD and **retracts the ones a
  later in-batch commit already satisfied** (telemetry `ledger_reconciled`), and **collapses
  duplicate owner-gated residuals that map to one external action** (e.g. one migration's
  `db:push` re-raised per-card + batch-wide → telemetry `owner_gated_deduped`). So `residuals[]`
  arrives pre-cleaned of false-open and duplicate entries; your Step 5.1 disk reconciliation is
  now a **safety net** over a clean ledger (it still runs — the self-correction is conservative
  and F-040 worktree-not-merged still applies), not the sole defence.
- `degraded` / `degradationReasons` — the batch stopped early under a sustained
  outage (or another degradation). The batch is NOT complete; it must be resumed.
- `telemetry` — the Phase-8 record (`variant:"new2"`).

### Step 5 — Reconcile, resume, present, record

1. **Materialise follow-ups in the MAIN repo — verify on disk, do NOT trust `materialized`
   (F-040).** The workflow's agents run cd'd into the *worktree*, so any follow-up they wrote
   may live in a worktree that was NOT merged (and is now gone) — `materialized:true` only means
   "the workflow attempted a write", never proof on disk. So for **every** `residuals[]` entry
   (regardless of the `materialized` flag), check whether a matching follow-up card actually exists
   on disk under `${paths.backlog_dir}` in the **MAIN repo** (`<card>-followup-*.yml`). If it is
   absent, create it by **delegating to the `prd-card-writer` agent in Standalone Single-Card Mode**
   — the same owner the workflow uses (full STANDALONE baseline of `framework/agents/card-schema.md`:
   Rule C `review_profile`, `owner_agent` routed to the residual's domain, `scope`, traceability) —
   derived from the residual (≥1 requirement; `acceptance_criteria` = the verbatim residual;
   `files_likely_touched` from the card's ownership). Do NOT hand-write a minimal stub — the offline
   path must match agent-path quality (F-039); it MUST pass the `/new` pre-flight field check. If
   `prd-card-writer` is unavailable (total outage), fall back to a minimal valid stub carrying the
   step-1b HALT fields — the `/new` pre-flight back-fill (step 1b-ii) then normalizes its
   `review_profile`/`owner_agent` on first ingestion, so even the crisis stub self-heals. This
   main-repo, **disk-verified** write is the SSOT — nothing is dropped even on a non-merged batch.
2. **Mark deferred cards DONE — only after their follow-up exists AND every deferral class allows
   it (F-040/H + A3).** Some committed cards were intentionally left **NON-DONE** because they carry
   an open deferred AC: they are `perCardResults[]` entries with `deferred:true` plus a
   `deferredClasses[]` array (also `cards_deferred_done_pending` in telemetry + the `F040-deferred`
   ledger row). For each such card, **check the classes first**:
   - **Every class ∈ {`owner-gated`, `not-a-code-defect`, `policy-deferred-ac`}** → the card's own
     code is complete; the residual is an external/infra step. Now that step 1 guaranteed its
     deferral's follow-up exists on disk in the main repo, set the card `status: DONE` +
     `completed_date` + an implementation_note (`"DONE post-run (new2) — AC deferred to follow-up
     <id>"`) in `${paths.backlog_dir}/<card>.yml`, and fold all of them into ONE reconciliation
     commit in the MAIN repo.
   - **ANY class ∈ {`unresolved`, `out-of-ownership`, `outage`} (or missing)** → the card's DoD is
     genuinely NOT met (an AC the workflow tried and failed to implement). Leave it **IN_PROGRESS**
     and surface it explicitly in the presentation: `"commit landed, DoD NON soddisfatta —
     follow-up <id>"`. NEVER auto-DONE it — a follow-up tracks the gap, but DONE would lie (F-029).
   **If a card's follow-up could NOT be created in step 1, leave it NON-DONE and surface it** —
   fail-loud; NEVER mark a card DONE with a silently-dropped requirement (F-029).
   **Then re-run the epic-closure check for the cards just marked DONE.** The merge agent's
   epic-closure (Phase 6b step 5e) ran BEFORE this step, when the deferred cards were still
   NON-DONE — so an epic whose last open child was a deferred card is still TODO and nothing
   else will ever close it. For each distinct `group.parent` of the cards marked DONE here: if
   `grep -l "parent: <EPIC-ID>" ${paths.backlog_dir}/*.yml | xargs grep -L "status: DONE"`
   prints nothing, set the epic card `status: DONE` + `completed_date` + note
   `"epic-closure gate — all children DONE (post-run, new2 skill)"`, folded into the SAME
   reconciliation commit. If any child is still open, leave the epic untouched.
3. **Resume if degraded.** If `degraded` is true, re-invoke the workflow with
   `Workflow({ scriptPath, resumeFromRunId })` (same `args` + the new `ts`). The
   per-card **skip-completed** guard makes the resume idempotent — already-committed
   cards are skipped, only the incomplete/blocked ones run. Repeat until `degraded`
   is false (or the same cards stall twice → surface to the user).
4. **Present.** Print `report` verbatim. Surface `residuals` prominently
   ("questi residui sono tracciati come follow-up: …") — the post-run review that
   replaced the ~25 mid-run questions. If `degraded`, say so plainly (the run was
   incomplete and resumed).
5. **Record truthful telemetry — reconciled against disk (F-040).** Before appending `telemetry`
   to `${metricsDir}/skill-runs.jsonl`, fill the fields the workflow could not compute and
   **reconcile the report against the real disk state** (agent `reason` strings can over-claim — a
   residual may say "AC PASS / migration created" about a change a rollback later erased). Verify:
   every `perCardResults` entry marked `committed` actually has a commit in `${trunk}`
   (`git -C $MAIN log --oneline ${trunk} | grep <card>`); annotate any divergence and never present
   progress the disk does not show. Then fill `wall_clock_s` (now − kickoff `ts`) and
   `followups_on_disk` (count the actual follow-up files on disk in the main repo, NOT
   `residualFollowups.length` — which double-counts). **Cost (the A/B's whole point) — do NOT trust
   the workflow's `total_tokens`/`agent_count` as the headline figure.** They are **partial**:
   `total_tokens` is `budget.spent()` output-only AND the agent counters do not include the subagents
   spawned inside the nested workflows (`new2-resolve`, `new-final-review`) — on the real FEAT-0022 run
   the workflow reported `total_tokens=476k` / `agent_count=30` while the harness saw ~4.15M subagent
   tokens / 64 agents (≈8× under). So **always** run the `/new` Phase-8 `-stats` script to read the
   real transcript `usage` (not only when `total_tokens` is null), record it as the headline cost, and
   keep the workflow's figures clearly labelled as `total_tokens_workflow`/`agent_count_workflow`
   (partial, output-only, excludes nested workflows). Keep `degraded`/`degradation_reasons` +
   `cards_deferred_done_pending` in the record so
   the A/B comparison stays honest. Also record `migration_gate: <migration.status>`
   (`none`|`applied`|`skipped`|`degraded`) — the Step-3.5 gate is a pre-launch interaction, NOT a
   mid-batch question, so it does not break the zero-ask-during-batch invariant; logging it keeps the
   A/B honest about when a migration was front-loaded. Keep `deferral_breakdown` (per-class counts
   of WHY residuals became follow-ups instead of in-batch fixes) in the record — a class dominating
   it is a root-cause signal (many `out-of-ownership` → PRD MAY-EDIT too narrow · many `unresolved` →
   fixes genuinely hard · many `scope-expansion` → cards under-specified upstream), and it is the
   data to consult BEFORE proposing any change to the deferral logic. Keep `cross_card_integrated`
   too (count of residuals the pre-final-review Cross-Card Integration Pass implemented in-batch —
   out-of-ownership-within-batch + outage retries — instead of leaving as follow-ups); with
   `deferral_breakdown` it shows how many deferrals were genuinely undeferrable vs absorbed in-batch.
   Keep `ledger_reconciled` + `owner_gated_deduped` (v4.31.0) too — they quantify the ledger
   self-correction: `ledger_reconciled` > 0 means the workflow retracted residuals a later commit had
   already satisfied (work the skill used to suppress by hand; a persistently high value signals
   deferrals resolving too late — order the dependent card earlier), and `owner_gated_deduped` > 0
   means N defers were collapsed to one external action.
   Do NOT re-summarise the cards — the workflow already did.
6. **Process hygiene — reap orphaned Codex MCP servers (NON-BLOCKING).** The batch's per-card Codex
   finder calls drive `codex app-server`, whose broker spawns the `~/.codex/config.toml` MCP servers
   (Playwright, …) as children; when a broker dies they leak to init (ppid 1) and keep burning CPU.
   Sweep them in the main context (the workflow sandbox cannot run Bash, so this MUST run here, after
   the workflow returns) so the run ends clean:
   ```bash
   npx baldart reap-orphans 2>/dev/null || true
   ```
   Reaps ONLY orphaned MCP servers (ppid 1 ⇒ broker dead); NEVER kills a live `codex app-server`
   broker. Non-blocking — any error / "nothing to reap" is fine; fold its one-line summary into the
   record (`codex_mcp_reaped`).
