Make doctor runtime checks external-project aware

This commit is contained in:
Himanshu Dongre 2026-05-17 16:14:32 +05:30
parent 2bbb7d27bc
commit c43310caa1
3 changed files with 224 additions and 33 deletions

View file

@ -763,6 +763,8 @@ def format_doctor(report: dict) -> str:
"""Readable diagnostics for backend/runtime consistency."""
backend = report.get("backend") or {}
local = report.get("local") or {}
source = report.get("source") or local
cwd = report.get("cwd") or {}
cli = report.get("cli") or {}
checks = report.get("checks") or {}
hints = report.get("hints") or []
@ -819,14 +821,36 @@ def format_doctor(report: dict) -> str:
parts.append("PATH matches executable: unknown")
parts.append("")
parts.append("## Local repo")
parts.append(f"Branch: `{local.get('branch') or 'unknown'}`")
parts.append(f"HEAD: `{local.get('git_sha_short') or local.get('git_sha') or 'unknown'}`")
parts.append("## Smriti source")
if source.get("git_root"):
parts.append(f"Repo: `{source.get('git_root')}`")
parts.append(f"Branch: `{source.get('branch') or 'unknown'}`")
source_head = source.get("git_sha_short") or source.get("git_sha") or "unknown"
parts.append(f"HEAD: `{source_head}`")
else:
parts.append("Repo: not detected (installed package or non-git install)")
parts.append("HEAD: not available")
parts.append("")
parts.append("## Current directory")
parts.append(f"Path: `{cwd.get('path') or 'unknown'}`")
if cwd.get("git_root"):
parts.append(f"Git repo: `{cwd.get('git_root')}`")
parts.append(f"Branch: `{cwd.get('branch') or 'unknown'}`")
cwd_head = cwd.get("git_sha_short") or cwd.get("git_sha") or "unknown"
parts.append(f"HEAD: `{cwd_head}`")
if source.get("git_root") and cwd.get("git_root") != source.get("git_root"):
parts.append(
"Runtime match is checked against the Smriti source above, "
"not this project repo."
)
else:
parts.append("Git repo: none detected")
parts.append("")
parts.append("## Checks")
runtime_match = checks.get("runtime_match") or "unknown"
parts.append(f"- runtime match: {runtime_match}")
parts.append(f"- runtime match: {str(runtime_match).replace('_', ' ')}")
missing = checks.get("missing_capabilities")
if missing is None:
parts.append("- missing capabilities: unknown (backend unreachable)")

View file

@ -123,11 +123,14 @@ EXPECTED_HEALTH_CAPABILITIES = {
}
def _git_output(*args: str) -> str | None:
def _git_output_at(cwd: str | os.PathLike[str] | None, *args: str) -> str | None:
"""Return trimmed git output for diagnostics, or None outside a git repo."""
cmd = ["git", *args]
if cwd is not None:
cmd = ["git", "-C", str(cwd), *args]
try:
result = subprocess.run(
["git", *args],
cmd,
check=True,
capture_output=True,
text=True,
@ -137,6 +140,65 @@ def _git_output(*args: str) -> str | None:
return result.stdout.strip() or None
def _git_output(*args: str) -> str | None:
"""Return trimmed git output for the current directory."""
return _git_output_at(None, *args)
def _git_context(cwd: str | os.PathLike[str] | None = None) -> dict:
root = _git_output_at(cwd, "rev-parse", "--show-toplevel")
sha = _git_output_at(cwd, "rev-parse", "HEAD")
short = _git_output_at(cwd, "rev-parse", "--short", "HEAD")
branch = (
_git_output_at(cwd, "branch", "--show-current")
or _git_output_at(cwd, "rev-parse", "--abbrev-ref", "HEAD")
)
return {
"git_root": root,
"git_sha": sha,
"git_sha_short": short,
"branch": branch,
}
def _is_smriti_source_root(path: str | None) -> bool:
if not path:
return False
root = Path(path)
return (
(root / "cli" / "smriti_cli" / "main.py").exists()
and (root / "backend" / "app" / "main.py").exists()
)
def _build_cwd_info() -> dict:
info = _git_context()
info["path"] = os.getcwd()
info["is_smriti_source"] = _is_smriti_source_root(info.get("git_root"))
return info
def _build_smriti_source_info() -> dict:
"""Detect the Smriti source checkout backing this CLI, if available.
`smriti doctor` is often run from a user's project repo. Runtime freshness
must compare the backend to Smriti's own source checkout, not the caller's
app repo. Editable installs have `__file__` inside the Smriti checkout; a
wheel/global install may not, in which case the git comparison is simply
unavailable rather than a mismatch.
"""
module_dir = Path(__file__).resolve().parent
info = _git_context(module_dir)
info["path"] = str(module_dir)
info["is_smriti_source"] = _is_smriti_source_root(info.get("git_root"))
if not info["is_smriti_source"]:
info["git_root"] = None
info["git_sha"] = None
info["git_sha_short"] = None
info["branch"] = None
return info
def _git_sha_matches(backend_sha: str | None, local_sha: str | None) -> bool | None:
if not backend_sha or not local_sha:
return None
@ -268,12 +330,11 @@ def _background_provider_check(providers: dict) -> str:
def _build_doctor_report(client: SmritiClient) -> dict:
"""Build a small diagnostics report without attempting repairs."""
local_sha = _git_output("rev-parse", "HEAD")
local_branch = (
_git_output("branch", "--show-current")
or _git_output("rev-parse", "--abbrev-ref", "HEAD")
)
local_short = _git_output("rev-parse", "--short", "HEAD")
source_info = _build_smriti_source_info()
cwd_info = _build_cwd_info()
if not source_info.get("git_sha") and cwd_info.get("is_smriti_source"):
source_info = dict(cwd_info)
source_sha = source_info.get("git_sha")
report = {
"api_url": client.base_url,
@ -284,11 +345,11 @@ def _build_doctor_report(client: SmritiClient) -> dict:
"capabilities": [],
"error": None,
},
"local": {
"git_sha": local_sha,
"git_sha_short": local_short,
"branch": local_branch,
},
# Backward-compatible alias: historically "local" meant cwd. It now
# means the local Smriti source/install used for runtime comparison.
"local": source_info,
"source": source_info,
"cwd": cwd_info,
"cli": _build_cli_info(),
"checks": {
"runtime_match": "unknown",
@ -322,7 +383,7 @@ def _build_doctor_report(client: SmritiClient) -> dict:
database = health.get("database") or {}
providers = health.get("providers") or {}
missing = sorted(EXPECTED_HEALTH_CAPABILITIES - set(capabilities))
match = _git_sha_matches(backend_sha, local_sha)
match = _git_sha_matches(backend_sha, source_sha)
report["backend"].update({
"reachable": True,
@ -354,14 +415,16 @@ def _build_doctor_report(client: SmritiClient) -> dict:
elif match is False:
report["checks"]["runtime_match"] = "mismatch"
report["hints"].append(
"Backend git_sha differs from local HEAD; restart the backend "
"after syncing code, or check out the commit the backend is running."
"Backend git_sha differs from the local Smriti source HEAD; "
"restart the backend after syncing code, or check out the commit "
"the backend is running."
)
else:
report["checks"]["runtime_match"] = "unknown"
report["hints"].append(
"Could not compare backend git_sha with local HEAD."
)
report["checks"]["runtime_match"] = "not_applicable"
if source_info.get("git_root"):
report["hints"].append(
"Could not compare backend git_sha with the local Smriti source checkout."
)
if health.get("status") != "ok":
report["hints"].append(

View file

@ -17,15 +17,43 @@ def _client(health: dict | None = None) -> MagicMock:
return client
def _patch_git(monkeypatch: pytest.MonkeyPatch, *, sha: str = "c470947abcdef", branch: str = "main") -> None:
def _patch_git(
monkeypatch: pytest.MonkeyPatch,
*,
sha: str = "c470947abcdef",
branch: str = "main",
cwd_sha: str | None = None,
cwd_branch: str = "main",
source_detected: bool = True,
) -> None:
short = sha[:7]
values = {
("rev-parse", "HEAD"): sha,
("rev-parse", "--short", "HEAD"): short,
("branch", "--show-current"): branch,
("rev-parse", "--abbrev-ref", "HEAD"): branch,
}
monkeypatch.setattr(cli_main, "_git_output", lambda *args: values.get(args))
cwd_sha = cwd_sha if cwd_sha is not None else sha
cwd_short = cwd_sha[:7] if cwd_sha else None
monkeypatch.setattr(
cli_main,
"_build_smriti_source_info",
lambda: {
"path": "/repo/cli/smriti_cli",
"git_root": "/repo" if source_detected else None,
"git_sha": sha if source_detected else None,
"git_sha_short": short if source_detected else None,
"branch": branch if source_detected else None,
"is_smriti_source": source_detected,
},
)
monkeypatch.setattr(
cli_main,
"_build_cwd_info",
lambda: {
"path": "/work/project",
"git_root": "/work/project" if cwd_sha else None,
"git_sha": cwd_sha,
"git_sha_short": cwd_short,
"branch": cwd_branch if cwd_sha else None,
"is_smriti_source": False,
},
)
def _patch_cli(
@ -110,10 +138,84 @@ def test_doctor_report_flags_mismatch_and_missing_capability(
assert report["checks"]["runtime_match"] == "mismatch"
assert report["checks"]["missing_capabilities"] == ["worktree_binding"]
assert any("differs from local HEAD" in hint for hint in report["hints"])
assert any(
"differs from the local Smriti source HEAD" in hint
for hint in report["hints"]
)
assert any("missing capabilities" in hint for hint in report["hints"])
def test_doctor_report_ignores_external_project_git_head(
monkeypatch: pytest.MonkeyPatch,
):
_patch_git(monkeypatch, sha="c470947abcdef", cwd_sha="deadbeefeedface")
_patch_cli(monkeypatch)
client = _client(_health(git_sha="c470947"))
report = cli_main._build_doctor_report(client)
assert report["checks"]["runtime_match"] == "ok"
assert report["source"]["git_sha_short"] == "c470947"
assert report["cwd"]["git_sha_short"] == "deadbee"
assert not any(
"differs from the local Smriti source HEAD" in hint
for hint in report["hints"]
)
def test_doctor_report_does_not_mismatch_without_source_repo(
monkeypatch: pytest.MonkeyPatch,
):
_patch_git(
monkeypatch,
sha="ignored",
cwd_sha="deadbeefeedface",
source_detected=False,
)
_patch_cli(monkeypatch)
client = _client(_health(git_sha="c470947"))
report = cli_main._build_doctor_report(client)
assert report["checks"]["runtime_match"] == "not_applicable"
assert report["source"]["git_sha"] is None
assert report["cwd"]["git_sha_short"] == "deadbee"
assert not any(
"differs from the local Smriti source HEAD" in hint
for hint in report["hints"]
)
def test_doctor_report_uses_cwd_when_it_is_smriti_source(
monkeypatch: pytest.MonkeyPatch,
):
_patch_git(
monkeypatch,
sha="ignored",
cwd_sha="c470947abcdef",
source_detected=False,
)
monkeypatch.setattr(
cli_main,
"_build_cwd_info",
lambda: {
"path": "/repo",
"git_root": "/repo",
"git_sha": "c470947abcdef",
"git_sha_short": "c470947",
"branch": "main",
"is_smriti_source": True,
},
)
_patch_cli(monkeypatch)
client = _client(_health(git_sha="c470947"))
report = cli_main._build_doctor_report(client)
assert report["checks"]["runtime_match"] == "ok"
assert report["source"]["git_root"] == "/repo"
def test_doctor_report_flags_cli_path_mismatch(monkeypatch: pytest.MonkeyPatch):
_patch_git(monkeypatch)
_patch_cli(
@ -205,3 +307,5 @@ def test_cmd_doctor_prints_activation_details(
assert "Background intelligence: ready (`openai` / `gpt-4o-mini`)" in out
assert "## CLI" in out
assert "PATH matches executable: yes" in out
assert "## Smriti source" in out
assert "## Current directory" in out