# CONVERGE SPEC TEMPLATE

Use this template with /architect to retire deprecated paths from
ssot_manifest.yaml. One capability per build.

## Two modes

**Single-path mode (default):** retire exactly ONE deprecated path. Use the template
body as-is. Phases 0/1/2/3 each fire once.

**All-deprecated mode (when invoked with `--mode all-deprecated`):** retire EVERY
deprecated path for the chosen capability in one phased build. Expansion rules:

1. **Phase 0 runs once** — caller graph audit covers all deprecated paths at once.
   Output: a per-path caller map AND a topological order (leaf-first if any caller
   is itself on a deprecated path within the same capability).

2. **Phases 1/2/3 repeat per deprecated path**, in the topological order from Phase 0.
   So for N deprecated paths, the build has `1 + 3N` total phases:
   - Phase 0: Caller graph audit (all paths)
   - Phase 1.1, 2.1, 3.1: Migrate/audit/delete deprecated path #1
   - Phase 1.2, 2.2, 3.2: Migrate/audit/delete deprecated path #2
   - ...
   - Phase 1.N, 2.N, 3.N: Migrate/audit/delete deprecated path #N

3. **Each trio (1.k, 2.k, 3.k) is independent of other trios.** A failure in 1.3
   does NOT roll back 1.1 + 1.2. Phase 3.k commits separately, so partial completion
   is durable. Resume by re-running `/converge [capability]` — the manifest reflects
   what's done, and the next /converge picks up the remaining deprecated paths.

4. **Audit_dispatch.py runs at the end of each trio (Phase 2.k)**, not once at the
   end. This catches per-path regressions immediately instead of after all migrations.

5. **The DEPRECATED_PATHS placeholder** is a JSON array of full paths, e.g.
   `["recoil/.../foo.py::bar", "recoil/.../baz.py::qux"]`. /architect expands the
   template body once per element.

Fill in CAPS-bracketed placeholders before dispatching.

---

# BUILD_SPEC — converge/[CAPABILITY_NAME]

**Goal:** Retire `[DEPRECATED_PATH]` by migrating all callers to the canonical path
`[CANONICAL_PATH]` and deleting the deprecated code.
**Capability:** [CAPABILITY_NAME] from ssot_manifest.yaml
**Rationale:** [WHY THIS CAPABILITY FIRST — e.g., "highest accretion velocity", "blocking new modalities"]

---

## Phase 0: Caller Graph Audit
depends_on: (none)

**Description:** Map all callers of `[DEPRECATED_PATH]` before writing any code.
Check if any callers are themselves on deprecated paths — if so, order migrations
leaf-first (topological sort). A partial migration leaves the system in broken state.

**Implementation:**
```bash
# Find all callers in production code
grep -rn "[DEPRECATED_FUNCTION_NAME]" recoil/ --include="*.py" \
    | grep -v "tests\|mock\|fixture\|archive\|__pycache__"
```

For each caller found:
1. Check if the caller's module is itself listed in ssot_manifest.yaml `deprecated_paths`
2. If YES: this caller must be migrated FIRST (leaf-first ordering)
3. Document the migration order in this phase's output

**Validation:**
```bash
# Confirm caller count matches Phase 0 findings before proceeding
CALLER_COUNT=$(grep -rn "[DEPRECATED_FUNCTION_NAME]" recoil/ --include="*.py" \
    | grep -v "tests\|mock\|fixture\|archive" | wc -l)
echo "Callers found: $CALLER_COUNT"
# Sub-agent MUST pause here and verify count matches Phase 0 findings before Phase 1 starts
```

---

## Phase 1: Migrate Callers
depends_on: 0

**Description:** Migrate each caller of `[DEPRECATED_PATH]` to `[CANONICAL_PATH]`.
One caller per phase sub-step if there are >3 callers. All callers must use the
canonical path exclusively after this phase.

**Files to modify:** [LIST EACH CALLER FILE]

**For each caller:**
- Replace `[DEPRECATED_FUNCTION_NAME]([ARGS])` with `[CANONICAL_FUNCTION_NAME]([MAPPED_ARGS])`
- Verify the mapped args produce identical output (same field set, same values)
- Add a `# migrated from [DEPRECATED_PATH] in converge/[CAPABILITY_NAME]` comment at each migration site

**Validation:**
```bash
# No production code should import or call the deprecated function after this phase
grep -rn "[DEPRECATED_FUNCTION_NAME]" recoil/ --include="*.py" \
    | grep -v "tests\|mock\|fixture\|archive\|sidecar_writer.py\|[DEPRECATED_FILE_ITSELF]"
# Must return 0 results (or only results in the deprecated file itself)
```

---

## Phase 2: Audit Dispatch Validation
depends_on: 1

**Description:** Run the full audit_dispatch.py fixture suite to confirm output
correctness is identical before and after the migration.

```bash
python3 recoil/pipeline/tools/audit_dispatch.py
# Must exit 0 — all assertions pass
```

If any assertion fails: diagnose before proceeding. The canonical function must
produce bitwise-identical output to the deprecated function for all covered paths.

---

## Phase 3: Delete Deprecated Code + Update Manifest
depends_on: 2

**Description:** Delete the deprecated function/file and remove it from the manifest.

**For the deprecated function:**
- If it's the ONLY function in the file: delete the file; add a `# tombstoned: [DATE]` comment
  to `[DEPRECATED_FILE]` (or leave a tombstone file if the file had other code)
- If it shares a file with other functions: delete only the deprecated function body;
  add tombstone comment at the deletion site

**Update ssot_manifest.yaml:**
- Move `[DEPRECATED_PATH]` from `deprecated_paths` to `tombstoned_paths` with `tombstoned_on: [DATE]`
- Set `state: tombstoned` if the last deprecated path is removed
- OR: Remove the entire entry if `tombstoned_paths` list is now also empty

**Validation:**
```bash
# Deprecated function must not be callable
python3 -c "
from [DEPRECATED_MODULE] import [DEPRECATED_FUNCTION_NAME]
" 2>&1 | grep -q "ImportError\|cannot import name" && echo "DELETE CONFIRMED: PASS"

# Manifest is valid
python3 recoil/architecture/tools/validate_manifest_ast.py \
    --manifest recoil/architecture/ssot_manifest.yaml && echo "Manifest: PASS"

# Audit dispatch still passes
python3 recoil/pipeline/tools/audit_dispatch.py && echo "Audit dispatch: PASS"
```
