Symptom
When a developer edits a path-installed Python bundle's source (via the workspace.json { "path": "/path/to/bundle" } install mode), the platform's bundle subprocess silently keeps loading the vendored snapshot from <bundle>/deps/<pkg>/ instead of the live source at <bundle>/src/<pkg>/. The dev's edits don't take effect; restart doesn't help; only manually deleting deps/<pkg>/ (or running make clean-bundle) does.
This caused several hours of confusion during local validation of Phase 2c (synapse-research adoption of the host-resources SDK) — every edit + bundle restart loop appeared to be a no-op until we noticed deps/mcp_research/ shadowing src/mcp_research/.
Root cause
src/bundles/startup.ts:762-769:
const pathParts: string[] = [];
const depsDir = join(resolvedDir, "deps");
if (existsSync(depsDir)) pathParts.push(depsDir);
const srcDir = join(resolvedDir, "src");
if (existsSync(srcDir)) pathParts.push(srcDir);
if (pathParts.length > 0) {
const existing = spawnEnv.PYTHONPATH;
spawnEnv.PYTHONPATH = existing ? `${pathParts.join(":")}:${existing}` : pathParts.join(":");
}
deps/ is pushed first, so it comes first on PYTHONPATH. Python's import resolver walks PYTHONPATH in order — deps/mcp_research/ wins over src/mcp_research/. For packed .mcpb bundles this is correct (the .mcpb may only have deps/, no src/). For path-installed bundles where the developer is iterating on src/, it silently shadows their work.
How deps/<pkg>/ gets there
Bundle authors typically run make bundle (or equivalent uv pip install --target ./deps .) to produce the packed .mcpb. That command installs the package itself into deps/, baking a snapshot of src/ from build time. The snapshot survives across edit/test cycles; only make clean-bundle removes it.
Suggested fixes
Pick one (or both):
- Reverse the order when
pyproject.toml exists alongside src/: developer dev-loop wins, packed bundles unaffected (they don't have a pyproject.toml at the bundle root). Concretely: if existsSync(join(resolvedDir, "pyproject.toml")) && existsSync(srcDir), push srcDir before depsDir.
- Or detect & warn: log a startup warning when
deps/<pkg>/ and src/<pkg>/ both exist with the same name, naming which one Python will load. Makes the shadowing visible even if it's the right behaviour.
Either alone is helpful; #1 fixes the dev loop, #2 documents reality.
Context
Surfaced during Phase 2c validation. Same root cause likely affects every NimbleBrain dev iterating locally on a Python bundle that's also been packed at least once.
Symptom
When a developer edits a path-installed Python bundle's source (via the workspace.json
{ "path": "/path/to/bundle" }install mode), the platform's bundle subprocess silently keeps loading the vendored snapshot from<bundle>/deps/<pkg>/instead of the live source at<bundle>/src/<pkg>/. The dev's edits don't take effect; restart doesn't help; only manually deletingdeps/<pkg>/(or runningmake clean-bundle) does.This caused several hours of confusion during local validation of Phase 2c (synapse-research adoption of the host-resources SDK) — every edit + bundle restart loop appeared to be a no-op until we noticed
deps/mcp_research/shadowingsrc/mcp_research/.Root cause
src/bundles/startup.ts:762-769:deps/is pushed first, so it comes first onPYTHONPATH. Python's import resolver walksPYTHONPATHin order —deps/mcp_research/wins oversrc/mcp_research/. For packed.mcpbbundles this is correct (the .mcpb may only havedeps/, nosrc/). For path-installed bundles where the developer is iterating onsrc/, it silently shadows their work.How
deps/<pkg>/gets thereBundle authors typically run
make bundle(or equivalentuv pip install --target ./deps .) to produce the packed.mcpb. That command installs the package itself intodeps/, baking a snapshot ofsrc/from build time. The snapshot survives across edit/test cycles; onlymake clean-bundleremoves it.Suggested fixes
Pick one (or both):
pyproject.tomlexists alongsidesrc/: developer dev-loop wins, packed bundles unaffected (they don't have apyproject.tomlat the bundle root). Concretely: ifexistsSync(join(resolvedDir, "pyproject.toml")) && existsSync(srcDir), pushsrcDirbeforedepsDir.deps/<pkg>/andsrc/<pkg>/both exist with the same name, naming which one Python will load. Makes the shadowing visible even if it's the right behaviour.Either alone is helpful; #1 fixes the dev loop, #2 documents reality.
Context
Surfaced during Phase 2c validation. Same root cause likely affects every NimbleBrain dev iterating locally on a Python bundle that's also been packed at least once.