Layout and move
layout_and_move
Tier-layout reshape + safe move wrapper.
Splits into four concerns:
relocate_non_canonical_tiers— Stage 0.5 (B4 fix): movetests/functional/and any other non-canonical tier subdir intotests/integration/so the rest of the pipeline only ever sees canonical tiers (unit / integration / e2e).flatten_tier_layout+_flatten_single_tier+_prune_empty_test_subdirs— Stage 1.5: collapse nestedtests/integration/<subdir>/to flat layout the AXM convention requires._rewrite_cross_test_imports— when a file moves and changes its dotted module path, rewrite everyfrom <old_module> import ...in the project so importers don't break._safe_move_units+_resolve_helper_conflicts+_resolve_conftest_shadowing— wrap axm-anvil'smove_symbolswith collision detection, helper-body conflict resolution, and conftest shadowing guards. The bulk of the proto's "magic" lives here.
flatten_tier_layout(project_path)
Flatten tests/integration/ and tests/e2e/ subdirectories.
The AXM convention (CLAUDE.md) requires integration and e2e tests
to live directly under their tier directory — no nested
tests/integration/hooks/test_x.py. This stage moves every
nested test_*.py up to the tier root, renames on collision
by prefixing the subdirectory name (hooks/test_x.py →
test_hooks_x.py), rewrites importers via
_rewrite_cross_test_imports, and removes the now-empty
subdirectories (preserving __init__.py / conftest.py by
skipping the prune if those remain).
Runs AFTER Stage 1 (RELOCATE) so it acts on the final tier classification, and BEFORE Stages 2-4 (SPLIT/MERGE/RENAME) which assume a flat layout.
Unit tests intentionally MIRROR the source layout — nested
subdirectories are correct there, so this stage skips tests/unit.
Source code in packages/axm-audit/src/axm_audit/core/fix/layout_and_move.py
relocate_non_canonical_tiers(project_path)
Move tests/<non_canonical>/test_*.py into tests/integration/.
Tiers unit/, integration/, e2e/ are the only canonical
pyramid directories (per CLAUDE.md). Files living under any other
direct child of tests/ — e.g. tests/functional/,
tests/hooks/, tests/tools/ — cannot be processed by SPLIT /
MERGE / RENAME because tier_for_path returns None for them.
Default destination is tests/integration/ (the tier for real I/O
+ first-party import), which is the most common landing for legacy
functional/ tests. Stage 1 RELOCATE will subsequently re-tier
each file to its correct level based on PYRAMID_LEVEL findings.
Runs BEFORE Stage 1 so RELOCATE sees only canonical-tier paths.