# Phase 04 — Parallel Cluster Execution

**Mode:** DELEGATE × N → `migration-cluster-worker` (parallel; one `Task()` per cluster in a SINGLE message).
**Workflow CLI param:** `--workflow=migration`.
**Inputs (must exist on entry):** `tmp/${MIGRATION_ID}/{intent,classification,transform-rules,clusters}.{json,md}` (from Phases 01–03); the design-sensitive files (if any) have been hand-edited inline by the orchestrator in Phase 03.
**Outputs (must exist on exit):** N cluster reports collected; every reported `.claude/X` ↔ `src/claude/X` pair is `diff`-empty; the set of flagged type-sensitive sites + uncertain calls is consolidated for Phase 05 hand-resolution.

---

## Entry Gate [MANDATORY]

```bash
devforgeai-validate phase-check ${SESSION_ID} --workflow=migration --from=03 --to=04
```
IF exit != 0: HALT — Phase 03 incomplete or skipped.

---

## Reference Loading [MANDATORY]

```
# No required references for this phase. Phase 04 reads the workflow's
# clusters.json + transform-rules.md artifacts (both authored in Phase 03)
# and then dispatches N migration-cluster-worker subagents in parallel
# via a single multi-Task() message. Each worker is a fresh-context
# terminal agent that loads its own references; the orchestrator only
# verifies the returned cluster reports and spot-diffs a 5-pair sample.
# No skill reference Reads are required by the orchestrator.
```

---

## Dry-Run Pass-Through

If `intent.json.dry_run == true`, Phase 03 Step 3.3 wrote `clusters.json` as `[]`
(empty array). This phase does NOT skip; it executes a **vacuous pass** that
satisfies the CLI gates honestly:

**EXECUTE:**

1. Verify clusters.json is empty:
   ```
   Read(file_path="tmp/${MIGRATION_ID}/clusters.json")
   ```
   Confirm the parsed content is `[]`.

2. Invoke `migration-cluster-worker` ONCE on the empty cluster list. The worker
   reads clusters.json, finds no clusters, and reports "0 clusters processed."
   This invocation is **structurally necessary** (the CLI's `phase-complete`
   gate enforces `migration-cluster-worker` is in `subagents_invoked` for
   Phase 04) and **substantively honest** (the worker did its assigned
   contract: it processed every cluster in its input — which was zero).

   ```
   Task(
     subagent_type="migration-cluster-worker",
     description="Vacuous pass: process empty clusters.json for ${MIGRATION_ID}",
     prompt="Read tmp/${MIGRATION_ID}/clusters.json. Process every cluster per the migration-cluster-worker contract. If clusters.json is `[]`, report '0 clusters to process' and exit cleanly. Do not Edit any file. Do not invoke other subagents."
   )
   ```

3. Record the 4 steps (`preconditions-ok`, `clusters-dispatched`,
   `consolidated`, `spot-verified`) — all are vacuously true on an empty
   cluster set. Record the subagent invocation.

   ```bash
   devforgeai-validate phase-record ${SESSION_ID} --workflow=migration --phase=04 --step=preconditions-ok
   devforgeai-validate phase-record ${SESSION_ID} --workflow=migration --phase=04 --step=clusters-dispatched
   devforgeai-validate phase-record ${SESSION_ID} --workflow=migration --phase=04 --step=consolidated
   devforgeai-validate phase-record ${SESSION_ID} --workflow=migration --phase=04 --step=spot-verified
   devforgeai-validate phase-record ${SESSION_ID} --workflow=migration --phase=04 --subagent=migration-cluster-worker
   ```

**VERIFY:**
- The `Task()` returned a one-paragraph report stating zero clusters were
  processed.
- All 4 steps + the subagent are recorded in the phase-state JSON.
- No files in the working tree were modified by Phase 04 in dry-run.

**RECORD:** Skip Steps 4.1–4.4 below entirely; proceed to the Exit Gate.

```bash
devforgeai-validate phase-complete ${SESSION_ID} --workflow=migration --phase=04 --checkpoint-passed
```

**Note:** This is intentionally NOT a "skip" — the FOR-loop in SKILL.md runs
Phase 04 normally, the entry/exit gates fire, and the subagent is honestly
invoked. The "vacuous-pass" name reflects that the WORK is empty, not that
the workflow ceremony is.

---

## Step 4.1: Load Cluster Plan + Confirm Preconditions

**EXECUTE:**
```
Read(file_path="tmp/${MIGRATION_ID}/clusters.json")
Read(file_path="tmp/${MIGRATION_ID}/transform-rules.md")
```
Parse `clusters.json` — expect an array of cluster objects, each with `cluster_id`, `scope_dirs` (array of dual-path-paired dirs, e.g. `[".claude/agents/", "src/claude/agents/"]`), `exclusions`, and approximate file count.

**VERIFY:**
- `transform-rules.md` exists and is non-empty.
- `clusters.json` parses and contains at least one cluster.
- Every cluster's `scope_dirs` list includes BOTH the `.claude/` and `src/claude/` mirror for any covered subtree (or the project has been declared single-path in `intent.json`).
- The design-sensitive set named in Phase 03 has been hand-edited and committed (orchestrator state).

IF any precondition unmet: HALT.

**RECORD:**
```bash
devforgeai-validate phase-record ${SESSION_ID} --workflow=migration --phase=04 --step=preconditions-ok
```

---

## Step 4.2: Dispatch ALL Clusters in a SINGLE Message [MANDATORY PARALLEL]

This is the highest-value context-preservation step in the entire skill. The N cluster workers run in fresh, isolated contexts in parallel — their token budgets do not consume yours. **You MUST issue all N `Task()` calls in a single assistant message**; sequential dispatch defeats the parallelism and risks token-bias rationalization between workers.

**EXECUTE:**

Issue N `Task()` calls — one per cluster — in a SINGLE message. The template for each call:

```
Task(
  subagent_type="migration-cluster-worker",
  description="Migrate cluster <CLUSTER_ID> for <MIGRATION_ID>",
  prompt="Migrate cluster CLUSTER_ID=<CLUSTER_ID> for MIGRATION_ID=${MIGRATION_ID}.

SCOPE_DIRS: <JSON-encoded scope_dirs list from clusters.json for this cluster>.
EXCLUSIONS: <JSON-encoded migration-wide exclusions>.
FROM_PATTERN: <from_pattern from intent.json>.
TRANSFORM_RULES_PATH: tmp/${MIGRATION_ID}/transform-rules.md.
DUAL_PATH_MAP: .claude/X <-> src/claude/X (byte-identical; identical old/new strings to both copies).

Workflow:
1. Read TRANSFORM_RULES_PATH; internalize the per-bucket transformations.
2. Read tmp/${MIGRATION_ID}/classification.md; locate the files in your SCOPE_DIRS and identify any MIXED or FILES-NEEDING-NO-EDIT entries that affect your cluster.
3. For each in-scope file: classify every occurrence per the rules, apply the bucket transformation, and edit BOTH dual-path copies with the IDENTICAL old_string and new_string in the same logical step.
4. FLAG (do not transform) any type-sensitive site per the type-change-bug protocol — surface them in your report under FLAGGED.
5. Self-diff every edited pair with Bash(\"diff <copy_a> <copy_b>\"); the output MUST be empty. If a pair drifts, reconcile and re-diff before returning.
6. Re-grep your SCOPE_DIRS for FROM_PATTERN; classify every residual hit (ALREADY-MIGRATED / DOC-historical / EXCLUDED / FLAGGED). There must be NO residual classifiable as READ-OP / LISTING / PATH-VALIDATION / DOC-current-state.
7. Return the cluster report in the exact Output Format from your prompt (sections (a)-(e)).

Terminal worker: do NOT invoke other subagents, do NOT Write any file (Edit only), do NOT AskUserQuestion. Do not weaken or rewrite test assertions to mask failures — flag instead."
)
```

Issue all N of these in one message. Example for 3 clusters:

```
Task(... cluster_id="agents" ...)
Task(... cluster_id="dev-skills" ...)
Task(... cluster_id="qa-skills" ...)
```

(All three Task tool uses in the SAME assistant turn — that is what triggers parallel scheduling.)

**VERIFY:**
- N `Task()` results returned (one per cluster). None empty.
- Each result is a Markdown report with sections (a)–(e) present:
  ```
  For each report:
    contains "## (a) Files edited" — pass
    contains "## (b) Dual-path verification" — pass
    contains "## (c) Residual occurrences" — pass
    contains "## (d) FLAGGED type-sensitive sites" — pass
    contains "## (e) Uncertain calls" — pass
  ```
- For each report, the `(b)` section states "All K pairs byte-identical" (or equivalent affirmative).

IF any cluster's report lacks the diff-empty confirmation: HALT. Do NOT trust the report. Run `diff` yourself on a sample of that cluster's edited pairs; if drift exists, the orchestrator hand-reconciles or re-invokes that specific worker.

**RECORD:**
```bash
devforgeai-validate phase-record ${SESSION_ID} --workflow=migration --phase=04 --step=clusters-dispatched
```

---

## Step 4.3: Consolidate Reports

**EXECUTE:**

For each cluster report, extract:
- The list of files edited + occurrence counts (aggregate across clusters).
- The list of FLAGGED type-sensitive sites (consolidate into a single list with cluster attribution).
- The list of Uncertain calls (consolidate similarly).
- The list of residual occurrences (consolidate; this becomes Phase 05's starting point).

Write the consolidated report to:
```
Write(file_path="tmp/${MIGRATION_ID}/phase-04-cluster-reports.md", content=<consolidated report>)
```

The consolidated report MUST list every cluster, its file count, and per-cluster verdict (clean / has-flagged / has-uncertain).

**VERIFY:**
- `tmp/${MIGRATION_ID}/phase-04-cluster-reports.md` exists.
- Every cluster from `clusters.json` is present in the consolidated report.
- The set of FLAGGED sites is non-null (could be empty list — record explicitly).

**RECORD:**
```bash
devforgeai-validate phase-record ${SESSION_ID} --workflow=migration --phase=04 --step=consolidated
```

---

## Step 4.4: Spot-Verify Dual-Path on a Sample [MANDATORY]

The cluster workers self-diff'd, but the orchestrator MUST do its own independent global verification — never trust truncated or optimistic worker reports.

**EXECUTE:**

Sample 5 random file pairs across the edited set (or all of them if fewer than 5 edited files exist). For each:
```
Bash("diff <.claude/path> <src/claude/path>")
```
The expected output is EMPTY for every sampled pair.

**VERIFY:**
- Every sampled diff returns empty.

IF any sampled pair drifts: HALT and escalate via AskUserQuestion. The cluster worker breached its contract; you must NOT proceed to Phase 05 with drifted dual-path mirrors.

**RECORD:**
```bash
devforgeai-validate phase-record ${SESSION_ID} --workflow=migration --phase=04 --step=spot-verified
```

---

## Exit Gate [MANDATORY]

```bash
devforgeai-validate phase-complete ${SESSION_ID} --workflow=migration --phase=04 --checkpoint-passed
```
IF exit != 0: HALT.

---

## Phase 04 Success Criteria

- [ ] N cluster workers dispatched in a SINGLE assistant message (parallel scheduling triggered).
- [ ] N cluster reports collected; each contains sections (a)–(e) per the worker's Output Format.
- [ ] Every reported edited pair is dual-path byte-identical per the worker's own `diff` step.
- [ ] Orchestrator's independent spot-`diff` on a 5-pair sample confirms zero drift.
- [ ] `tmp/${MIGRATION_ID}/phase-04-cluster-reports.md` written and consolidates all reports.
- [ ] The FLAGGED type-sensitive set and Uncertain-call set are consolidated for Phase 05 hand-resolution.
- [ ] No file edits were inlined by the orchestrator during this phase — workers did 100% of the cluster work.
