Skip to content

MOO-72 Commit 5: dependency and runtime health checks - #15

Merged
OwenTanzer merged 2 commits into
mainfrom
moo72-commit5-dependency-health-checks
Jul 29, 2026
Merged

MOO-72 Commit 5: dependency and runtime health checks#15
OwenTanzer merged 2 commits into
mainfrom
moo72-commit5-dependency-health-checks

Conversation

@OwenTanzer

Copy link
Copy Markdown
Owner

Summary

  • /readyz gains cacheStorage/nodeRuntime (readiness-gating) and pythonRuntime/pythonTreeSitter/githubReachable (feature-specific, non-gating) checks, alongside the existing pyan3/codeVisualizer checks — all normalized to one {ok, detail, version, checkedAt} status shape.
  • cacheStorage round-trips against a dedicated, isolated GraphCache instance, never the live request-serving cache — a naive self-test against production cache state would perturb real LRU order, consume capacity, and risk a key collision on every /readyz hit.
  • nodeRuntime enforces the actual declared range in package.json's engines.node (^20.19.0 || >=22.12.0) via a small, pure, unit-tested isSupportedNodeVersion(version) helper — not just a bare major-version floor, which would wrongly accept 20.0.0/21.x/22.0.0.
  • pyan3/pythonRuntime/githubReachable are periodically re-verified, folded into the existing 5-minute metrics-summary interval (one fewer new timer) via a new, independently-unit-testable refreshDependencyStatuses() (server/lib/dependency-status.js), reentrancy-guarded so an overlapping tick is skipped rather than run concurrently. codeVisualizer stays startup-only — its parser-init memoization lives inside the vendored, commit-pinned @codevisualizer/core package, not something this codebase controls or should patch just to add live re-verification.
  • pythonTreeSitter surfaces the existing PYTHON_TREE_SITTER_CAPABLE constant (the repository layer's own, separate Python-grammar probe) as its own readiness entry — a second, independent tree-sitter dependency from CodeVisualizer's that a first plan draft missed.
  • graphvizDot reports {applicable: false} (not ok: true) — pyan3 emits DOT text directly, parsed via the ts-graphviz JS library; no external graphviz/dot binary is ever invoked.
  • Sensitive detail/version/checkedAt fields are only included when the request carries the same Authorization: Bearer <AUTH_TOKEN> used for /api/* — an unauthenticated /readyz only ever sees ok/gatesReadiness.
  • New verifyGithubReachable (github-analyzer-bridge.js) checks credential validity/reachability via GET /rate_limit (doesn't consume the caller's own rate-limit quota), deliberately independent of the shared, per-request-mutable GitHub.token singleton to avoid a background-check-races-real-request hazard. Injectable fetchImpl/apiBase for hermetic unit testing.
  • New verifyPythonRuntime/recheckPyan3Available (pyan3Adapter.js) — separate from pyan3's own pinned-version check per the checklist's two distinct bullets, and a production-facing (not test-only-named) re-check for the periodic refresh.

Test plan

  • npm test — 654/654 passing (baseline 623 + 31 new)
  • tests/dependency-status.test.mjs (new) — proves one failing check (bad Python binary, bad GitHub token) never affects the others, and refreshDependencyStatuses never rejects as a whole.
  • tests/server-health.test.mjs — extended: cache-storage-never-touches-the-live-cache (asserts a real live GraphCache's size/bytes/entry are completely unchanged after hitting /readyz), Node version boundary cases, auth-gated detail presence/absence, every new check's shape.
  • tests/server-github-bridge.test.mjsverifyGithubReachable against a mocked fetchImpl: 200, 401, non-401 non-2xx, network-error, abort, and header-shape assertions.
  • tests/pyan3-adapter.test.mjsrecheckPyan3Available genuinely re-runs (forces a failure, then recovers against the real binary, not a stale memoized rejection); verifyPythonRuntime against both a real interpreter and a missing one.
  • Ran the real end-to-end smoke test (tests/server-smoke.mjs) against the actual server process, the real pinned pyan3, and a real GitHub token — all 22 steps pass, including new assertions that the real /readyz response has every new check ok:true, graphvizDot.applicable:false, and that detail/version are absent unauthenticated and present authenticated.

🤖 Generated with Claude Code

Adds Node runtime, cache storage, Python runtime, GitHub reachability/
credential validity, and Python tree-sitter grammar checks to /readyz,
alongside the existing pyan3/CodeVisualizer checks -- normalized to one
{ok, detail, version, checkedAt} status shape and periodically
re-verified (folded into the existing 5-minute metrics interval, with a
reentrancy guard) where the underlying dependency actually supports it.
readiness-gating checks (buildOutput, workspaceRoot, cacheStorage,
nodeRuntime) determine the 200/503 status; feature-specific checks
(pyan3, pythonRuntime, codeVisualizer, pythonTreeSitter, githubReachable)
are reported but never take the whole service out of rotation.
Sensitive detail/version fields are only exposed to an authenticated
caller (the existing AUTH_TOKEN), never publicly.

The cache-storage check runs against a dedicated, isolated GraphCache
instance -- never the live, request-serving cache, which a naive
round-trip self-test would otherwise perturb (LRU eviction, capacity,
key-collision risk) on every /readyz hit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Jul 29, 2026

Copy link
Copy Markdown

MOO-72

@OwenTanzer OwenTanzer left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

One runtime-health mismatch to address.

Comment thread server/lib/pyan3Adapter.js Outdated
if (!match) {
return { ok: false, version: null, detail: `could not parse a version from output: ${JSON.stringify(stdout || stderr)}` };
}
return { ok: true, version: match[1], detail: null };

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

[P2] Reject non-Python-3 runtimes

This reports any parseable Python X.Y.Z output as healthy, so an operator-configured Python 2 executable returns pythonRuntime.ok: true. The adapter's existing contract requires a valid Python 3 interpreter (and the setup scripts prefer python3), so /readyz can give a misleading runtime-health signal while analysis cannot run. Please require major version 3 here and cover the Python 2 output case.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed. verifyPythonRuntime's parse/gate logic is extracted into a pure evaluatePythonVersionOutput({stdout,stderr}) helper (server/lib/pyan3Adapter.js) that now checks the parsed major version is exactly 3, rejecting Python 2 (and any other major) with an actionable detail ("found Python X.Y.Z, but a Python 3 interpreter is required") while still reporting the detected version so an operator can see what's actually installed. Verified with real Python 2 and Python 3 --version output shapes (stderr for 2.x, stdout for 3.x, matching the real interpreters' actual behavior) rather than trying to spawn a fake Python 2 binary -- a .bat-based stand-in turned out to be unreliable on Windows (execFile rejects it without shell:true, which this codebase deliberately never sets for security). Full suite: 658/658.

…check

verifyPythonRuntime accepted any parseable "Python X.Y.Z" output, so an
operator-configured Python 2 interpreter (which prints its version to
stderr in the identical shape) was reported ok:true even though
pyan3/the whole adapter require Python 3 -- a misleading /readyz signal
(healthy runtime, broken analysis).

Extracted the parse/gate logic into a pure evaluatePythonVersionOutput()
helper, now checking the major version is 3, and covered it directly
with real Python 2 and Python 3 --version output shapes -- avoids an
unreliable fake-executable workaround (Windows' execFile rejects a .bat
stand-in without shell:true, which this codebase deliberately never sets).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@OwenTanzer
OwenTanzer merged commit 6f2bb6a into main Jul 29, 2026
3 checks passed
@OwenTanzer
OwenTanzer deleted the moo72-commit5-dependency-health-checks branch July 29, 2026 13:21
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.

1 participant