Skip to content

fix(adapters): isolate imports when switching projects (MUT-021) - #119

Merged
CodewithJha merged 3 commits into
CodewithJha:mainfrom
BasilZafar11:codex/issue-69-project-imports
Oct 12, 2026
Merged

CodewithJha merged 3 commits into
CodewithJha:mainfrom
BasilZafar11:codex/issue-69-project-imports

Conversation

@BasilZafar11

@BasilZafar11 BasilZafar11 commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #69

Summary

  • Clear cached project modules when switching roots, including helper and namespace packages, and prioritize the selected project's import path.
  • Restrict eviction to paths whose first segment under the previous root matches the module's own top-level name (for example agent.py or project_helpers/). Virtualenv packages and editable installs on separate import paths retain module and class identity.
  • Preserve cached imports on same-root selection. Deferred imports use the currently selected project; Hosted observe-only behavior is unchanged.
  • Add ten loader regression cases covering sequential loads, deferred imports, helper packages, missing agents, module references, virtualenv dependencies, editable dependencies, and switching away from this checkout.

Review follow-up

Addressed the dependency identity regression in commit 3740c80. Both new fake SDK cases failed before narrowing the purge and now pass. A checkout regression also verifies mutiny_core and pydantic remain the same modules after switching away. Merged latest upstream main without rewriting branch history.

Type of change

Checklist

  • Read CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md, and SUPPORT.md
  • Linked related issue
  • Core stays free of framework SDK imports
  • Tests added for behavioral changes
  • One concern per PR
  • Authorized local/offline testing only

Test plan

  • Python lint: uv run ruff check passed after uv sync --extra dev.
  • Full backend suite: MUTINY_SAMPLE_OFFLINE=1 uv run pytest -q --tb=short: 666 passed on local Python 3.13. CI covers Python 3.11 and 3.12.
  • Focused loader, adapter, and Hosted restriction tests: 22 passed before dependency refresh; all included in the subsequent full suite.
  • Offline CLI smoke: sample mutiny --help, mutiny init, and mutiny run succeeded with empty API keys; a violation regression was saved.
  • Web typecheck/build and the shell-based isolated artifact gate are left to CI for this Python loader change. Package release unit tests are included in the full suite.

Previously verified sample mutiny test correctly returns exit 1 for the deliberately vulnerable sample's refund violations, rather than a loader error. Branch has zero commits behind the fetched upstream main at push time.

@vercel

vercel Bot commented Oct 11, 2026

Copy link
Copy Markdown

@BasilZafar11 is attempting to deploy a commit to the priyanshu's projects Team on Vercel.

A member of the Team first needs to authorize it.

@CodewithJha CodewithJha left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @BasilZafar11, this is close. The core of #69 is fixed, and the tests are strong. One real regression needs fixing before merge.

What is working

  • On 8c5a474, sequential A → B → A loads return the right agent each time, including deferred imports, regular and namespace helper packages, and agent:support_agent refs.
  • Mutation check: with loader.py reverted to main, all 7 new tests fail. With your change they pass. Full offline suite: 663 passed, and CI is green.
  • The CLI path (run_cmd / test_cmd) selects the same root twice, so nothing is purged. One-shot CLI behaviour is unchanged, and Hosted never reaches this code.
  • test_missing_agent_does_not_fall_back_to_previous_project is a nice catch. It covers the silent wrong-agent case that the issue is really about.

What still needs to change

The purge condition pops every module whose file lives anywhere under the previous root, not just modules imported from that root. When a project directory contains its own virtualenv (the default with uv / python -m venv .venv), switching projects also purges installed packages.

Reproduced on this branch:

  • Project A has .venv/lib/python3.12/site-packages/fakesdk on sys.path. Load A, then B, then re-import fakesdk: it is a new module object, and isinstance(old_obj, fakesdk.Agent) is False.
  • Select this repository checkout as a project, then switch to a temp project: 1,403 modules are purged, including pydantic and all of mutiny_core (its editable install lives under the repo root). Objects created before the switch no longer match their classes.

That is the in-process multi-project scenario #69 targets, so it trades one wrong-identity bug for another.

Important

Please keep the scope you already have: same-root reselect stays a no-op, sys.path ordering stays as is, and no change to Hosted.

Suggested direction

Only drop modules that were imported with the previous root as their sys.path entry, meaning the first path segment under the root is the module's own top-level name. I tried this locally: all 7 of your tests still pass, and both reproductions above purge nothing (fakesdk / pydantic / mutiny_core keep their identity):

top = name.partition(".")[0]
for path in paths:
    resolved = Path(path).resolve()
    if resolved.is_relative_to(_project_root):
        first = resolved.relative_to(_project_root).parts[:1]
        if first in ((top,), (f"{top}.py",)):
            names.append(name)
            break

Please add one regression test: a project whose root contains a package directory that is on sys.path separately (for example .venv/.../site-packages/fakesdk). After switching to another project, sys.modules["fakesdk"] should still be the same object.

module_file = getattr(module, "__file__", None)
if module_file:
paths.append(module_file)
if any(Path(path).resolve().is_relative_to(_project_root) for path in paths):

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This matches any file under the previous root, including <root>/.venv/.../site-packages/* and editable installs such as <repo>/packages/*/src. Switching away from the repo checkout purges 1,403 modules here, including pydantic and mutiny_core. Narrow it to modules whose first path segment under the root is their own top-level name (agent.py, project_helpers/...); see the review summary for a snippet that keeps all 7 tests green.

@BasilZafar11

Copy link
Copy Markdown
Contributor Author

Addressed the review in commit 3740c80. The purge now targets modules imported directly from the project root, preserving virtualenv and editable dependency identities.

Added regression coverage for both dependency layouts and switching away from the repository checkout. All 666 tests pass; Ruff and offline CLI smoke also pass.

PR #119 is updated and ready for another review. Thanks for catching this.

@CodewithJha CodewithJha left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @BasilZafar11, great turnaround. The narrowing is exactly right, and the new tests cover the regression well. Approving and merging.

Re-review of 3740c80

  • ✓ .venv / editable repro: I selected this repository checkout as a project, then switched to a temp project. 0 modules are purged now, down from 1,403. pydantic and mutiny_core keep their identity.
  • ✓ Original fix still holds: all 7 A → B → A tests pass.
  • ✓ Mutation checks:
    • Broadening the condition back to "anything under the root" fails all 3 new regression tests: both fakesdk layouts and the checkout-switch test.
    • Reverting loader.py to main fails 9 of 10 tests in the file.
  • ✓ Scope is unchanged: same-root reselect is still a no-op, so one-shot CLI behaviour is untouched, and Hosted never reaches this code.
  • ✓ Checks: full suite 666 passed, Ruff clean, the offline CLI sample run / test works, and GitHub Actions is green.

This closes #69.

If you'd like to keep going

  • #75 (MUT-027/MUT-048/MUT-052): CLI and loader unit coverage gaps. You now know the loader better than anyone.
  • #78 (MUT-035/MUT-050/MUT-051): version drift, the 3.13 classifier, and mutiny db being advertised without mutiny-api. It's packaging/DX work close to what you just touched.

@CodewithJha
CodewithJha merged commit 62df8d4 into CodewithJha:main Oct 12, 2026
6 of 7 checks passed
CodewithJha pushed a commit that referenced this pull request Oct 12, 2026
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] MUT-021: Second project load can reuse the first project's agent module

2 participants