# Transform-Rules Schema

Canonical schema for the migration-wide single source of truth: `tmp/${MIGRATION_ID}/transform-rules.md`. The orchestrator and user co-author this file during Phase 03. Every `migration-cluster-worker` invocation in Phase 04 reads it before applying any edit — there is exactly ONE rules file per migration, and it covers EVERY bucket the classifier emitted in Phase 02.

---

## Why a Single Rules File

A migration sweeps the same `from_pattern` across hundreds of files in N parallel clusters. If each cluster worker improvises its own bucket-to-transformation mapping, the workers drift: cluster `agents` rewrites the `LISTING` bucket one way and cluster `dev-skills` rewrites it another, and the dual-path tree ends up with semantically inconsistent prose. The `transform-rules.md` artifact eliminates that drift by encoding every decision as a directive every worker reads identically.

The file is purely declarative — pseudocode, not executable. The cluster workers translate each directive into concrete `Edit(old_string=…, new_string=…)` calls inside their own scope.

---

## Required Sections (in order)

A well-formed `transform-rules.md` MUST contain these top-level sections, in this order:

1. **Migration Intent Block** — restates the 5 fields from `intent.json` for in-file reference (no `Read` needed when workers consult the rules).
2. **Dual-Path Protocol Declaration** — names the mirror map this migration relies on and the byte-identity rule.
3. **Per-Bucket Transformation Directives** — one labelled subsection per recognised bucket from `classification.md`.
4. **Type-Change-Bug Declaration** — what type the noun was; what type it became; the FLAG-do-not-transform rule for type-coupled call sites.
5. **Exclusions Restated** — the migration-wide exclusion patterns (so a worker reading only this file knows which paths to drop).

A `transform-rules.md` missing any of these sections is malformed; Phase 04 entry gate refuses to dispatch workers until the rules artifact is complete.

---

## Section 1 — Migration Intent Block

```markdown
## Migration Intent

| Field | Value |
|-------|-------|
| `from_pattern` | `<literal string the migration replaces>` |
| `to_pattern` | `<replacement string>` |
| `to_type` | `file` \| `folder` \| `cli` |
| `scope_dirs` | `[".claude/X/", "src/claude/X/", …]` |
| `exclusions` | `["_archive/", "/plans/", …]` |
```

**Rules:**

- `from_pattern` and `to_pattern` are the literal strings the cluster workers feed to `Edit()` as `old_string` / `new_string` (modulo per-bucket transformation framing — see Section 3).
- `to_type` is the type-change signal — `file` (no type change), `folder` (file → directory), or `cli` (file/folder → CLI invocation). It drives Section 4's FLAGGING decisions.
- `scope_dirs` lists EVERY dual-path-paired directory in the migration's corpus. Workers walk only these.
- `exclusions` lists glob patterns that drop a file from scope outright (templates, archives, historical records). Restated in Section 5 for visibility.

---

## Section 2 — Dual-Path Protocol Declaration

```markdown
## Dual-Path Protocol

| Operational copy | Canonical copy |
|------------------|----------------|
| `.claude/X/Y` | `src/claude/X/Y` |

**Rule:** For every `Edit(file_path=".claude/X/Y", old_string=..., new_string=...)`, apply the IDENTICAL `old_string` and IDENTICAL `new_string` to `src/claude/X/Y` in the same logical step. Verify with `Bash("diff .claude/X/Y src/claude/X/Y")` — output MUST be empty before the worker returns its report.
```

A migration may legitimately declare itself single-path (e.g. a CLI-only sweep that only touches `src/claude/scripts/`). In that case, this section explicitly states `Single-path migration: dual-path protocol NOT applicable.` and identifies the one tree being edited. See `dual-path-protocol.md` for the byte-identity verification rule.

---

## Section 3 — Per-Bucket Transformation Directives

One subsection per bucket the classifier reported (a directive for an empty bucket is permissible but explicitly says "No occurrences in this migration"). The 6 canonical buckets — `READ-OP`, `LISTING`, `PATH-VALIDATION`, `DOC`, `ALREADY-MIGRATED`, `EXCLUDED` — are defined in `classification-bucket-catalog.md`; the DOC bucket has a `DOC-historical` sub-disposition that is also addressed here.

### Directive grammar

Every directive specifies, in plain pseudocode:

```markdown
### Bucket: <BUCKET_NAME>

**Disposition:** TRANSFORM | LEAVE | FLAG

**Recognition reminder:** <one-line summary; the SSOT is classification-bucket-catalog.md>

**Transformation:**
<find pattern>  →  <replacement pattern>

**Examples:**
- BEFORE: `Read(file_path="…<from_pattern>")`
- AFTER:  `Read(file_path="…<to_pattern_target>")`
```

### Worked directive — READ-OP (for `source-tree.md` → `source-tree/`)

```markdown
### Bucket: READ-OP

**Disposition:** TRANSFORM

**Recognition reminder:** A directive to Read/load the file (`Read(file_path="…/source-tree.md")`, prose "Read source-tree.md", `"source-tree.md"` as a load-array element).

**Transformation:**
`Read(file_path="devforgeai/specs/context/source-tree.md")`
  → `Read(file_path="devforgeai/specs/context/source-tree/_manifest.json")`

`"source-tree.md"` (load-array element)
  → `"source-tree/_manifest.json"`

**Examples:**
- BEFORE: `Read(file_path="devforgeai/specs/context/source-tree.md")`
- AFTER:  `Read(file_path="devforgeai/specs/context/source-tree/_manifest.json")`
```

### Worked directive — LISTING

```markdown
### Bucket: LISTING

**Disposition:** TRANSFORM (noun-swap only)

**Recognition reminder:** Pattern appears in an enumeration of related artifacts (e.g. context-file list, references table).

**Transformation:**
`source-tree.md`  →  `source-tree/`

**Examples:**
- BEFORE: `tech-stack.md, source-tree.md, dependencies.md`
- AFTER:  `tech-stack.md, source-tree/, dependencies.md`
```

### Worked directive — DOC + DOC-historical sub-disposition

```markdown
### Bucket: DOC

**Disposition:** TRANSFORM current-state prose; LEAVE DOC-historical prose.

**Recognition reminder:** Incidental mention — citation example, References list, descriptive prose. Sub-discrimination: a sentence recording a PAST event (changelog row, MIGRATION-NOTES entry, "STORY-NNN: …") is DOC-historical.

**Transformation (current-state DOC):**
`source-tree.md`  →  `source-tree/`

**Disposition (DOC-historical):** LEAVE. Historical records must read true at the time they were written.

**Examples:**
- DOC current-state — BEFORE: `…validates against source-tree.md.`
- DOC current-state — AFTER:  `…validates against source-tree/.`
- DOC-historical — UNCHANGED: `STORY-413 retired source-tree.md per ADR-071.`
```

### Worked directive — ALREADY-MIGRATED

```markdown
### Bucket: ALREADY-MIGRATED

**Disposition:** LEAVE.

**Recognition reminder:** The text already explains the migration and cites the new artifact + the superseding ADR (e.g. "source-tree.md was retired per ADR-071").

**Rationale:** Editing already-migrated prose corrupts the explanation of the migration itself. The classifier's `MIXED` list flags files that mix ALREADY-MIGRATED prose with other buckets — workers handle those carefully (LEAVE the migrated occurrences, TRANSFORM the others).
```

### Worked directive — EXCLUDED

```markdown
### Bucket: EXCLUDED

**Disposition:** LEAVE (file out of migration scope).

**Recognition reminder:** Path matched an `EXCLUSIONS` glob — workers never read or edit these files. EXCLUDED entries appear in the classification artifact for completeness; transform-rules restates them so workers know which paths to drop during their own re-grep.
```

---

## Section 4 — Type-Change-Bug Declaration

This section is the most consequential — it encodes the lesson from the source-tree migration where a pure noun-swap silently broke adjacent logic.

```markdown
## Type-Change-Bug Declaration

**Type transition:** `from_type` → `to_type` (e.g. `file (.md) → folder (directory)`).

**FLAG-do-not-transform rule:** Cluster workers FLAG (NOT transform) any occurrence whose surrounding code reads the noun's type, not just its value. The orchestrator hand-resolves FLAGGED sites in Phase 05. Categories cluster workers MUST flag:

1. `Glob("…<from_pattern>…*.md")` count/existence checks (a folder won't match `*.md`).
2. `[ -f path ]` shell tests on `<from_pattern>` (a directory fails `-f` — recommend `[ -d ]`).
3. "the X section of `<from_pattern>`" content-greps when X doesn't exist in the new artifact.
4. `Read(file_path="…<from_pattern>")` whose target file's content was redistributed (the Read returns content the consumer no longer parses correctly).

**Worker reporting:** Cluster workers list FLAGGED sites in section `(d)` of their cluster report; the orchestrator consolidates them in Phase 04 and hand-resolves (or defers with documented justification) in Phase 05.
```

See `type-change-bug-flagging.md` for the full trap catalog and the FLAGGING protocol.

---

## Section 5 — Exclusions Restated

```markdown
## Exclusions

These path patterns drop a file from migration scope outright. Workers re-grep their cluster's SCOPE_DIRS and drop any hit matching any pattern:

- `_archive/` — historical record snapshots; immutable by policy.
- `/plans/` — execution plans; record-keeping, not operational directives.
- `<migration-specific exclusion>` — `<one-line reason>`.
```

---

## Worked End-to-End Example

For the source-tree migration (PR #68, `MIGRATION_ID=source-tree-2026-05-19`), the populated `transform-rules.md` would read:

```markdown
# Transform Rules — source-tree.md → source-tree/

## Migration Intent

| Field | Value |
|-------|-------|
| `from_pattern` | `source-tree.md` |
| `to_pattern` | `source-tree/` |
| `to_type` | `folder` |
| `scope_dirs` | `[".claude/agents/", ".claude/skills/", ".claude/commands/", ".claude/rules/", "src/claude/agents/", "src/claude/skills/", "src/claude/commands/", "src/claude/rules/"]` |
| `exclusions` | `["_archive/", "/plans/", "/agent-memory/", "spec-driven-system-architecture/assets/context-templates/"]` |

## Dual-Path Protocol

| Operational copy | Canonical copy |
|------------------|----------------|
| `.claude/X/Y` | `src/claude/X/Y` |

(See Section 2 — apply IDENTICAL old/new strings to both copies in the same logical step.)

## Per-Bucket Transformation Directives

[READ-OP, LISTING, PATH-VALIDATION, DOC, ALREADY-MIGRATED, EXCLUDED — directives as shown above]

## Type-Change-Bug Declaration

**Type transition:** `file (.md)` → `folder (directory)`.
[FLAG categories 1-4 as shown above]

## Exclusions

- `_archive/` — historical snapshots.
- `/plans/` — execution plans.
- `/agent-memory/` — agent-private memory; out of scope.
- `spec-driven-system-architecture/assets/context-templates/` — template tree intentionally references the old artifact.
```

The cluster workers Read this single file at the start of Phase 04 and never improvise — every transformation traces back to one directive.

---

## Validation Checks

Before dispatching Phase 04 workers, the orchestrator confirms `transform-rules.md`:

- [ ] Contains all 5 required sections in order.
- [ ] Migration Intent block lists all 5 fields with non-empty values (`exclusions` may be `[]`).
- [ ] Per-bucket directives exist for EVERY bucket the classifier reported as non-zero in `classification.md`'s Summary Table.
- [ ] Dual-Path Protocol Declaration either declares the mirror map OR explicitly declares single-path.
- [ ] Type-Change-Bug Declaration is present even when `to_type == file` (in which case it says "No type change; FLAG categories not applicable to this migration").
- [ ] Exclusions section matches `intent.json` `exclusions` field byte-for-byte.

---

## References

- `classification-bucket-catalog.md` — the bucket SSOT consumed by Section 3.
- `dual-path-protocol.md` — the byte-identity rule referenced in Section 2.
- `type-change-bug-flagging.md` — the trap catalog referenced in Section 4.
- `assets/templates/transform-rules-template.md` — the empty Markdown skeleton Phase 03 populates.
- `migration-cluster-worker.md` — the agent that consumes this file in Phase 04.
- `[[feedback_dual_path_mechanical_migration]]` — the playbook this schema operationalises.
