name: polylith-migrate-analyze-imports
description: "[Internal sub-skill of polylith-migrate-orchestrator. Do not load directly — load polylith-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."
Skill: polylith-migrate-analyze-imports
Goal
Analyze the project's import graph to:
- Find every reference to the original namespace (in all three forms — see below).
- Choose the namespace-rewrite strategy:
SHIM_STRATEGY=shim|shimless. - List symbols exported by the original namespace's
__init__.py. - Detect potential circular imports.
This drives the namespace rewrite (phase 4) and, when SHIM_STRATEGY=shim, the
shim sub-track (phase 4b). See the polylith-migrate-orchestrator table for phase numbers.
Inputs
- Project name (from
migration/<project-name>/state.md) - Original namespace
ORIG_TOP_NS(frommigration/<project-name>/state.md)
The three reference forms (cover all of them)
A namespace rewrite is incomplete unless it covers every form below. A naive
"replace from <ns>. " misses forms 2 and 3:
- Dotted import —
from ${ORIG_TOP_NS}.<sub> import …,import ${ORIG_TOP_NS}.<sub>. - Bare submodule import —
from ${ORIG_TOP_NS} import <sub>— single, multi-name (a, b, c), and mixed lines where only some names move. Easy to miss: there is no dot after the namespace. - Quoted string module paths —
mock.patch("${ORIG_TOP_NS}.x.Y"), logging dict-config"${ORIG_TOP_NS}.logging.HealthFilter",importlib/getattrtargets. Not import statements, but they must be rewritten too.
⚠ Do not rewrite unquoted local variables that merely share a name with the namespace (e.g. a FastAPI
app = FastAPI()instance'sapp.include_router(...)). Target import statements and quoted module paths only.
Steps
1. Identify references to the original namespace
- Search for all three forms above across all
.pyfiles in the project (andpyproject.toml/ config files for string paths). - Record the paths and statements in
migration/${PROJECT}/import_analysis.md, grouped as: internal (inside${ORIG_TOP_NS}/), external consumers (entrypoints,alembic, scripts), and tests. The external + test groups are what the strategy decision below hinges on.
2. List symbols exported by the original namespace
- Inspect
${ORIG_TOP_NS}/__init__.py(typicallyprojects/${PROJECT}/src/${ORIG_TOP_NS}/__init__.pyorprojects/${PROJECT}/${ORIG_TOP_NS}/__init__.py) and list public symbols (not starting with_). - Record them, and note whether
__init__.pyis effectively empty (docstring only).
3. Decide the rewrite strategy (SHIM_STRATEGY)
Choose based on the findings and record it in state.md:
shimless— when imports are predominantly submodule-qualified (from ${ORIG_TOP_NS}.<sub> import …/from ${ORIG_TOP_NS} import <sub>) and${ORIG_TOP_NS}/__init__.pyexports little or nothing. A single top-level re-export shim would resolve none of those submodule paths, and a full package shim mirroring every module is high-effort/high-risk. Phase 4 rewrites all references (internal + external + tests) directly to the new namespace, and the phase 4b sub-track is skipped.shim— when consumers import top-level symbols (from ${ORIG_TOP_NS} import X) that a single${ORIG_TOP_NS}/__init__.pyre-export can satisfy, and you want to defer rewriting external consumers. Run the phase 4b sub-track after phase 4.- When in doubt, prefer
shimless— it leaves no transitional shim to remove later (the definition-of-done forbids undocumented shims), at the cost of a wider but mechanical rewrite. Confirm with the user if the consumer surface is large.
Record:
SHIM_STRATEGY=<shim|shimless>
4. Detect potential circular imports
- Inspect the import graph for cycles — especially base ↔ shim once a shim is in
place (
shimstrategy only). - Record any chains in
import_analysis.md. A pure namespace rename (shimless) preserves the original graph, so cycles there usually mean the project already had them.
Output
migration/<project-name>/import_analysis.mdwith: references by form & group, exported symbols, the chosen strategy + rationale, and circular-import chains (if any).SHIM_STRATEGYset inmigration/<project-name>/state.md.
Verify
migration/<project-name>/import_analysis.mdexists and is not empty.- It records all three reference forms, the exported symbols, and any cycles.
SHIM_STRATEGYis set instate.md(shimorshimless) with a recorded rationale.
Commit
git add migration/${PROJECT}/import_analysis.md migration/${PROJECT}/state.md
git commit -m "migrate(${PROJECT}): phase <N> — analyze-imports"
<N>is this phase's number from thepolylith-migrate-orchestratortable (the single source of truth) — do not hardcode it.