name: polylith-migrate-extract-standalone-modules
description: "[Internal sub-skill of polylith-migrate-orchestrator. Do not load directly — load polylith-migrate-orchestrator first, which drives all phases.] Extract foundational modules (e.g., consts.py, exceptions.py, or similar) from the residual component into standalone components."
Skill: polylith-migrate-extract-standalone-modules
Goal
Extract foundational modules (e.g., consts.py, exceptions.py, models.py) from the residual component into standalone components. This skill is for zero-dependency or low-dependency modules that serve as building blocks for other components.
Inputs
From migration/<PROJECT>/state.md:
TARGET_TOP_NSINITIAL_BASE_NAME- Verification commands.
From migration/<PROJECT>/manifest.md:
- Current module map, including what remains in the residual component.
All inputs from
state.mdare assumed to satisfy the validation rules inpolylith-migrate-discover(### Validation rules). Validate before proceeding.
Steps
1. Analyze the Residual Component
- Use
directory_treeandgrepto list modules remaining in the residual component. - Classify each module:
- Zero internal deps: Modules with only stdlib/third-party imports (e.g.,
exceptions.py,consts.py). - Low internal deps: Modules that depend on already extracted or zero-dep modules (e.g.,
models.py). - App-wiring: Modules that compose infrastructure setup. These stay in the residual.
- Zero internal deps: Modules with only stdlib/third-party imports (e.g.,
2. Extract Modules in Dependency Order
- Extract zero-dep modules first, followed by modules that depend on them.
3. For Each Extraction
- Check for naming collisions.
- Create the component directory with
__init__.pyandcore.py. - Update imports in all consumers.
- Add the new brick to
pyproject.toml. - Run verification.
- Delete the original module from the residual component.
Verify
RUN_TEST_CMDsucceeds.- If set,
RUN_LINT_CMDandRUN_TYPECHECK_CMDsucceed. - Run
POLY_CMD_PREFIX checkto validate the workspace structure. - Run
POLY_CMD_PREFIX syncto synchronize the[tool.polylith.bricks]table with actual imports.
Common failure modes
| Symptom | Likely cause | Remediation |
|---|---|---|
| Module classified as zero-dep actually pulls in a transitive runtime dependency (e.g., reads an env var via the residual's config module) | The classification missed an indirect import. | Reclassify as low-dep, extract the config module first, then retry. |
Two consumers now import the same constant via different paths (e.g., from <ns>.consts and from <ns>.<INITIAL_BASE_NAME>.consts) |
The original module wasn't deleted from the residual after extraction. | Delete the original from the residual (step 3.6), pick one canonical import path, and update every caller. Verify with grep -r '<ns>.<INITIAL_BASE_NAME>.consts'. |
Extracting exceptions.py causes except clauses elsewhere to stop catching what they used to |
Exception classes are identity-based: two definitions are two different classes. | Ensure the new standalone module is the only definition. Delete the residual copy and update every raise/except site. |
| Verification fails and you can't quickly diagnose | Phase commit not yet made. | git reset --hard HEAD to roll back to the previous phase's commit and consult the user. |
Commit
After verification passes, commit this phase to the migration branch:
git add -A && git commit -m "migrate(<PROJECT>): phase <N> — extract-standalone-modules"
Substitute <PROJECT>, <N>, and <phase-name> from state.md and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables.