# Dual-Path Protocol

DevForgeAI2 is the source repository for the DevForgeAI framework, and every editable framework file exists as a **dual-path pair**: a `src/claude/X` canonical copy and a `.claude/X` operational mirror. The two MUST stay byte-for-byte identical after every edit. This reference defines the protocol every `migration-cluster-worker` follows in Phase 04 and the verification commands the orchestrator runs in Phase 05.

Validated 2026-05-19 on PR #68 (`source-tree.md` → `source-tree/`, 324 files, 0 dual-path drift) — see `[[feedback_dual_path_mechanical_migration]]` and `[[feedback_operational_file_protection]]`.

---

## The Byte-Identity Rule

**For every `Edit(file_path=".claude/X/Y", old_string=A, new_string=B)`, apply the IDENTICAL `old_string=A` and IDENTICAL `new_string=B` to `src/claude/X/Y` in the SAME logical step.**

The mirrors start byte-identical (CI enforces this in the main branch); identical edits keep them so. Any divergence between the two trees is **drift** — a contract breach that blocks Phase 04 exit until reconciled.

---

## The Mirror Map

The migration's `transform-rules.md` Section 2 declares the mirror map for the specific migration. The framework's standing dual-path pairs are:

| Operational copy | Canonical copy | Migration relevance |
|------------------|----------------|---------------------|
| `.claude/agents/` | `src/claude/agents/` | Subagent prompts |
| `.claude/skills/` | `src/claude/skills/` | Skill definitions + phase files + references |
| `.claude/commands/` | `src/claude/commands/` | Slash command definitions |
| `.claude/rules/` | `src/claude/rules/` | Auto-loaded framework rules |
| `.claude/hooks/` | `src/claude/hooks/` | Shell hook scripts |
| `.claude/scripts/` (where present) | `src/claude/scripts/` | Operational scripts |
| `CLAUDE.md` (repo root) | `src/CLAUDE.md` | Project-level steering (dual-path despite living outside any mirrored subtree) |
| `DevForgeAI.md` (repo root) | `src/DevForgeAI.md` | Framework-level steering (likewise) |

A migration that touches only one tree (e.g. a CLI sweep across `src/claude/scripts/`) declares itself single-path in `transform-rules.md` Section 2 and skips the byte-identity step.

---

## The Mechanism

The two-step `Edit` sequence inside the cluster worker:

```
# Step 1: read both copies (Claude Code requires Read before Edit on a per-file basis)
Read(file_path=".claude/agents/migration-classifier.md")
Read(file_path="src/claude/agents/migration-classifier.md")

# Step 2: apply IDENTICAL edits to both
Edit(
  file_path=".claude/agents/migration-classifier.md",
  old_string="source-tree.md",
  new_string="source-tree/"
)
Edit(
  file_path="src/claude/agents/migration-classifier.md",
  old_string="source-tree.md",
  new_string="source-tree/"
)
```

Both `Edit` calls use the SAME `old_string` and the SAME `new_string`. The cluster worker MUST NOT compose them differently — never use shell-style substitution on one side and exact-match on the other.

---

## Verification Commands

### Per-pair byte-identity (cluster worker, self-verify)

After the last `Edit` on a pair, the cluster worker runs:

```bash
diff .claude/X/Y src/claude/X/Y
```

The expected output is **EMPTY**. The worker MUST NOT return its report while any pair has non-empty diff output. The worker's report section `(b) Dual-path verification` lists every edited pair with a `diff` result column.

### Cluster-wide sweep (orchestrator, Phase 04 spot-verify)

The orchestrator samples 5 random edited pairs (or all if fewer than 5) and runs `diff` independently — the cluster worker's self-verification is not blindly trusted. See `phase-04-cluster-execution.md` Step 4.4.

### Subtree-wide sweep (orchestrator, Phase 05 final)

```bash
diff -rq .claude/agents/ src/claude/agents/
diff -rq .claude/skills/ src/claude/skills/
diff -rq .claude/commands/ src/claude/commands/
diff -rq .claude/rules/ src/claude/rules/
```

Each subtree sweep returns EMPTY on a clean migration. Any output line is drift — `Phase 05 — Global Verification` HALTs until resolved.

---

## Common Drift Causes and Recovery

| Drift cause | Symptom | Recovery |
|-------------|---------|----------|
| Worker accidentally edited one copy with a different string | `diff` shows the edited line on both sides with differing content | Re-`Edit` the divergent copy to match the other side; re-`diff`. |
| Worker edited only one copy (missed the mirror) | `diff` shows the edit on one side, original text on the other | Apply the same `Edit` to the missed copy; re-`diff`. |
| Whitespace difference introduced by editor (trailing newline, CRLF vs LF) | `diff` shows the file but no visible character differences | Re-Read both copies to identify the exact divergence; re-`Edit` the divergent copy to match the canonical (`src/`) side. |
| Pre-existing drift in main branch (e.g. the known `CLAUDE.md` 2-line drift) | `diff` shows pre-migration drift on lines the migration didn't touch | NOT a migration drift — verify the lines the migration touched are identical, document the pre-existing drift in the migration report, do NOT attempt to "fix" pre-existing drift inside a migration. |
| File exists in `.claude/` but not `src/claude/` (or vice versa) | `diff` reports the missing file | Out of scope for `migration-cluster-worker` — flag in `(d) FLAGGED` for orchestrator resolution; the absent mirror is a pre-existing repo defect, not migration drift. |

---

## What the Protocol Does NOT Cover

- **The `post-edit-write-check.sh` warning.** Editing operational `.claude/` files triggers a non-blocking warning. This is EXPECTED for an intentional dual-path edit — the cluster worker (and orchestrator) proceed. Do NOT export `DEVFORGEAI_ALLOW_OPERATIONAL_EDIT=1` session-wide; the warning is the intended signal that you are editing both copies (which is correct).
- **The `pre-context-file-guard.sh` hook.** Migrations that touch `devforgeai/specs/context/` are out of scope for this skill — context files are immutable per Critical Rule 4. A migration whose `scope_dirs` includes a context-file path HALTs in Phase 01 — the change requires an ADR, not a sweep.
- **`.sh` exec-bit drift.** When the migration touches a `.sh` hook script, the WSL exec-bit may need `git update-index --chmod=+x` after staging — see `[[feedback_wsl_hook_exec_bit]]`. The `diff` byte-identity check does NOT cover exec-bit drift; it covers file content only.

---

## References

- `[[feedback_operational_file_protection]]` — the user directive that established dual-path editing as the standing rule.
- `[[feedback_dual_path_mechanical_migration]]` — the playbook proving the protocol on PR #68.
- `transform-rules-schema.md` Section 2 — the per-migration declaration of the mirror map.
- `migration-cluster-worker.md` — the agent that applies the protocol per-cluster.
- `phase-04-cluster-execution.md` Step 4.4 — orchestrator's spot-`diff` verification.
- `verification-protocol.md` — Phase 05's global verification flow.
