Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions agent_sudo/doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,34 @@ def _install_health_checks(
return [
_staleness_check(identity, inventory_report),
_runtime_source_check(identity),
_duplicate_installs_check(inventory_report),
]


def _duplicate_installs_check(report: InventoryReport) -> DoctorCheck:
name = "single active install"
# Reuse inventory's classification (#101): an install is ACTIVE when a client
# config references it or it resolves on PATH. A pyenv shim is ACTIVE too but
# only *resolves to* a version install — counting both would double-count one
# install, so PYENV-SHIM records are excluded. Distinct roots means a shim and
# its target collapse to one, while two real installs (even same-version
# editables at different source roots) stay distinct.
roots = {
install.root
for install in report.installs
if "ACTIVE" in install.statuses and "PYENV-SHIM" not in install.statuses
}
if len(roots) <= 1:
detail = "one active install" if roots else "no active install detected"
return DoctorCheck(name, True, detail)
return DoctorCheck(
name,
False,
"Multiple active Agent_Sudo installs detected. Run `agent-sudo inventory` "
"to inspect and choose one canonical install.",
)


def _staleness_check(identity: SelfIdentity, report: InventoryReport) -> DoctorCheck:
name = "install up to date"
running = identity.version
Expand Down
3 changes: 3 additions & 0 deletions docs/command_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ and troubleshooting); they are listed under their primary use.
stale** (an older copy is resolving ahead of a newer one on the machine) or
when an **editable install has drifted** from its registered source — so a
shell silently running an out-of-date copy is caught here, not in production.
It also WARNs when **multiple active installs** are detected (more than one
Agent_Sudo resolving on PATH or referenced by client configs), pointing you
to `agent-sudo inventory` to pick one canonical install.
- **Example:** `agent-sudo doctor`
- **When to use:** right after install, or when something isn't working.
- **Common mistakes:** expecting it to validate your MCP client config — it checks the
Expand Down
76 changes: 76 additions & 0 deletions tests/test_doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,5 +292,81 @@ def test_ok_when_no_broad_delegation(self) -> None:
self.assertTrue(scope.ok)


class DuplicateInstallCheckTests(unittest.TestCase):
"""doctor surfaces multiple active installs (issue #111), WARN-only."""

def _identity(self) -> SelfIdentity:
return SelfIdentity(
version="0.5.6",
install_type="editable",
source_path="/repo/Agent_Sudo",
package_path="/repo/Agent_Sudo/agent_sudo",
python_executable="/py/bin/python",
python_prefix="/py",
python_version="3.11.14",
origin="console-script",
)

def _report(self, installs) -> InventoryReport:
records = [
InstallRecord(
root=root, executable="", version=version, statuses=list(statuses)
)
for root, version, statuses in installs
]
newest = max((r.version for r in records), default="")
return InventoryReport(
installs=records, configs=[], warnings=[], newest_version=newest
)

def _check(self, installs) -> DoctorCheck:
checks = run_doctor(
identity=self._identity(), inventory_report=self._report(installs)
)
return next(c for c in checks if c.name == "single active install")

def test_no_duplicates_is_ok(self) -> None:
check = self._check([("/repo/Agent_Sudo", "0.5.6", ["ACTIVE", "EDITABLE"])])
self.assertTrue(check.ok)

def test_duplicate_active_installs_warn(self) -> None:
installs = [
("/venv/a", "0.5.6", ["ACTIVE", "DUPLICATE INSTALL"]),
("/venv/b", "0.5.6", ["ACTIVE", "DUPLICATE INSTALL"]),
]
check = self._check(installs)
self.assertFalse(check.ok)
self.assertEqual(
check.detail,
"Multiple active Agent_Sudo installs detected. Run `agent-sudo inventory` "
"to inspect and choose one canonical install.",
)
# WARN-only: must not fail the exit code.
checks = run_doctor(
identity=self._identity(), inventory_report=self._report(installs)
)
self.assertEqual(doctor_exit_code(checks), 0)

def test_pyenv_shim_plus_resolved_install_not_double_counted(self) -> None:
# A shim resolves to its version install; counting both would be a false
# duplicate. The PYENV-SHIM record is excluded, leaving one real install.
installs = [
("/home/.pyenv/shims", "", ["ACTIVE", "PYENV-SHIM"]),
("/home/.pyenv/versions/3.11.14", "0.5.6", ["ACTIVE", "EDITABLE"]),
]
check = self._check(installs)
self.assertTrue(check.ok)

def test_same_version_editables_at_different_roots_warn(self) -> None:
# Two editable installs, same version, different source roots — still a
# duplicate because the roots differ.
installs = [
("/repo/A", "0.5.6", ["ACTIVE", "EDITABLE", "DUPLICATE INSTALL"]),
("/repo/B", "0.5.6", ["ACTIVE", "EDITABLE", "DUPLICATE INSTALL"]),
]
check = self._check(installs)
self.assertFalse(check.ok)


if __name__ == "__main__":
unittest.main()