[BUG] code-modernization plugin: make_shards.py contained() uses os.path.realpath() — breaks topology sharding on Windows junction-based shadows (0 shards produced)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
contained() in scripts/make_shards.py (lines 84–85) uses os.path.realpath(), which follows Windows directory junctions. LAMP's ensureLegacyShadow creates legacy/<system>/ as a real directory with Windows junctions for each top-level subdirectory pointing to the actual source. When os.path.realpath() resolves a module path through one of those junctions, the resolved path lands outside legacy/<system>/ — every topology module is classified as "escaped" and dropped. from_topology() returns an empty list with no exception, so the from_tree fallback never triggers.
Result: 0 shards, extract-rules.modules.json never written, /modernize-extract-rules produces no output.
Environment:
- Plugin: code-modernization (anthropics/claude-plugins-official), version 1.0.0
- Claude Code: 2.1.283
- OS: Windows 11 Enterprise
- File:
scripts/make_shards.py, lines 84–85
Root cause:
root = os.path.realpath(source) # stays at legacy/myapp/ (real dir)
real = os.path.realpath(os.path.join(source, rel)) # follows junction → resolves outside legacy/myapp/
# real.startswith(root + sep) → False → module classified escaped
Fix: replace both os.path.realpath calls with os.path.abspath:
def contained(source, rel):
root = os.path.abspath(source)
real = os.path.abspath(os.path.join(source, rel))
return real == root or real.startswith(root + os.sep)
abspath resolves .. traversal (the original safety intent) without following junctions.
What Should Happen?
make_shards.py should produce the correct number of shards (one per topology module) when legacy/<system>/ is a Windows junction-based shadow. from_topology() should classify all topology modules as contained and write extract-rules.modules.json. The contained() check must not follow Windows directory junctions.
Error Messages/Logs
Steps to Reproduce
- Create source with subdirectories on Windows:
````
mkdir C:\repro\source\myapp\src
mkdir C:\repro\source\myapp\domain
- Create the junction-based shadow (what LAMP's
ensureLegacyShadowdoes on Windows):
````
mkdir C:\repro\project\legacy\myapp
cmd /c mklink /J C:\repro\project\legacy\myapp\src C:\repro\source\myapp\src
cmd /c mklink /J C:\repro\project\legacy\myapp\domain C:\repro\source\myapp\domain
- Create
C:\repro\project\analysis\myapp\topology.json:
``json``
{"root":{"kind":"domain","name":"root","children":[
{"kind":"module","name":"order-service","file":"src/","loc":500,"children":[]},
{"kind":"module","name":"claim-domain","file":"domain/","loc":300,"children":[]}
]}}
- Run:
````
python scripts/make_shards.py myapp --workspace C:\repro\project\
Expected: 2 shards from 2 map modules -> analysis/myapp/extract-rules.modules.json
Actual: 0 shards: the pattern matched nothing, or no source files were found. Nothing was written.
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.283
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
PowerShell
Additional Information
_No response_