|
| 1 | +--- |
| 2 | +name: migrate-analyze-imports |
| 3 | +description: "[Internal sub-skill of `migrate-orchestrator`. Do not load directly — load `migrate-orchestrator` first, which drives all phases.] Analyze the project's import graph to find how the original namespace is referenced, choose the namespace-rewrite strategy (shim vs shimless), detect circular imports, and list symbols exported by the original namespace." |
| 4 | +--- |
| 5 | + |
| 6 | +# Skill: migrate-analyze-imports |
| 7 | + |
| 8 | +## Goal |
| 9 | +Analyze the project's import graph to: |
| 10 | +1. Find **every** reference to the original namespace (in all three forms — see below). |
| 11 | +2. Choose the namespace-rewrite strategy: `SHIM_STRATEGY=shim|shimless`. |
| 12 | +3. List symbols exported by the original namespace's `__init__.py`. |
| 13 | +4. Detect potential circular imports. |
| 14 | + |
| 15 | +This drives the namespace rewrite (phase 4) and, when `SHIM_STRATEGY=shim`, the |
| 16 | +shim sub-track (phase 4b). See the `migrate-orchestrator` table for phase numbers. |
| 17 | + |
| 18 | +## Inputs |
| 19 | +- Project name (from `migration/<project-name>/state.md`) |
| 20 | +- Original namespace `ORIG_TOP_NS` (from `migration/<project-name>/state.md`) |
| 21 | + |
| 22 | +## The three reference forms (cover all of them) |
| 23 | +A namespace rewrite is **incomplete** unless it covers every form below. A naive |
| 24 | +"replace `from <ns>.` " misses forms 2 and 3: |
| 25 | + |
| 26 | +1. **Dotted import** — `from ${ORIG_TOP_NS}.<sub> import …`, `import ${ORIG_TOP_NS}.<sub>`. |
| 27 | +2. **Bare submodule import** — `from ${ORIG_TOP_NS} import <sub>` — single, multi-name |
| 28 | + (`a, b, c`), and **mixed** lines where only some names move. Easy to miss: there is |
| 29 | + no dot after the namespace. |
| 30 | +3. **Quoted string module paths** — `mock.patch("${ORIG_TOP_NS}.x.Y")`, logging |
| 31 | + dict-config `"${ORIG_TOP_NS}.logging.HealthFilter"`, `importlib` / `getattr` |
| 32 | + targets. Not import statements, but they must be rewritten too. |
| 33 | + |
| 34 | +> ⚠ Do **not** rewrite unquoted local variables that merely share a name with the |
| 35 | +> namespace (e.g. a FastAPI `app = FastAPI()` instance's `app.include_router(...)`). |
| 36 | +> Target import statements and quoted module paths only. |
| 37 | +
|
| 38 | +## Steps |
| 39 | + |
| 40 | +### 1. Identify references to the original namespace |
| 41 | +1. Search for all three forms above across **all** `.py` files in the project (and |
| 42 | + `pyproject.toml` / config files for string paths). |
| 43 | +2. Record the paths and statements in `migration/${PROJECT}/import_analysis.md`, |
| 44 | + grouped as: **internal** (inside `${ORIG_TOP_NS}/`), **external consumers** |
| 45 | + (entrypoints, `alembic`, scripts), and **tests**. The external + test groups are |
| 46 | + what the strategy decision below hinges on. |
| 47 | + |
| 48 | +### 2. List symbols exported by the original namespace |
| 49 | +1. Inspect `${ORIG_TOP_NS}/__init__.py` (typically `projects/${PROJECT}/src/${ORIG_TOP_NS}/__init__.py` |
| 50 | + or `projects/${PROJECT}/${ORIG_TOP_NS}/__init__.py`) and list public symbols |
| 51 | + (not starting with `_`). |
| 52 | +2. Record them, and **note whether `__init__.py` is effectively empty** (docstring only). |
| 53 | + |
| 54 | +### 3. Decide the rewrite strategy (`SHIM_STRATEGY`) |
| 55 | +Choose based on the findings and record it in `state.md`: |
| 56 | + |
| 57 | +- **`shimless`** — when imports are predominantly **submodule-qualified** |
| 58 | + (`from ${ORIG_TOP_NS}.<sub> import …` / `from ${ORIG_TOP_NS} import <sub>`) and |
| 59 | + `${ORIG_TOP_NS}/__init__.py` exports little or nothing. A single top-level |
| 60 | + re-export shim would resolve **none** of those submodule paths, and a full package |
| 61 | + shim mirroring every module is high-effort/high-risk. Phase 4 rewrites **all** |
| 62 | + references (internal + external + tests) directly to the new namespace, and the |
| 63 | + **phase 4b sub-track is skipped**. |
| 64 | +- **`shim`** — when consumers import top-level symbols (`from ${ORIG_TOP_NS} import X`) |
| 65 | + that a single `${ORIG_TOP_NS}/__init__.py` re-export can satisfy, and you want to |
| 66 | + defer rewriting external consumers. Run the phase 4b sub-track after phase 4. |
| 67 | +- **When in doubt, prefer `shimless`** — it leaves no transitional shim to remove |
| 68 | + later (the definition-of-done forbids undocumented shims), at the cost of a wider |
| 69 | + but mechanical rewrite. Confirm with the user if the consumer surface is large. |
| 70 | + |
| 71 | +Record: |
| 72 | +``` |
| 73 | +SHIM_STRATEGY=<shim|shimless> |
| 74 | +``` |
| 75 | + |
| 76 | +### 4. Detect potential circular imports |
| 77 | +1. Inspect the import graph for cycles — especially base ↔ shim once a shim is in |
| 78 | + place (`shim` strategy only). |
| 79 | +2. Record any chains in `import_analysis.md`. A pure namespace rename (shimless) |
| 80 | + preserves the original graph, so cycles there usually mean the project already had |
| 81 | + them. |
| 82 | + |
| 83 | +## Output |
| 84 | +- `migration/<project-name>/import_analysis.md` with: references by form & group, |
| 85 | + exported symbols, the chosen strategy + rationale, and circular-import chains (if any). |
| 86 | +- `SHIM_STRATEGY` set in `migration/<project-name>/state.md`. |
| 87 | + |
| 88 | +## Verify |
| 89 | +1. `migration/<project-name>/import_analysis.md` exists and is not empty. |
| 90 | +2. It records all three reference forms, the exported symbols, and any cycles. |
| 91 | +3. `SHIM_STRATEGY` is set in `state.md` (`shim` or `shimless`) with a recorded rationale. |
| 92 | + |
| 93 | +## Commit |
| 94 | +```bash |
| 95 | +git add migration/${PROJECT}/import_analysis.md migration/${PROJECT}/state.md |
| 96 | +git commit -m "migrate(${PROJECT}): phase <N> — analyze-imports" |
| 97 | +``` |
| 98 | +> `<N>` is this phase's number from the `migrate-orchestrator` table (the single |
| 99 | +> source of truth) — do not hardcode it. |
0 commit comments