You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
DoctorContext exists and carries opts, a monotonic now, and the memo that keeps hook-runtime running exactly once. The registry, the runner, crash containment and durationMs are all in.
What it does not
PRD §2.3 names the effect set a check should receive — cwd, git runner, spawn, clock, index opener. Only the clock is injected. Every check still calls execGit, spawnSync and openIndex directly against the process.
That was a deliberate scope call: threading five effects through thirteen checks touches every one of them, and #463's own constraint is that the text stays byte-identical. A half-injected context — some checks taking effects, others reaching around them — is worse than neither, because a test that stubs the context would then be testing an arbitrary subset.
test/doctor-invariants.test.ts stubs net.Socket and dns.lookup at the Node level precisely because there is no seam closer to the check. An injected spawn would let the no-network fence cover the spawned-git half it currently cannot see.
The suite spends real seconds building git repositories for assertions that are about branching logic, not about git.
Scope
Extend DoctorContext with git, spawn, env, openIndex, defaulting to the real implementations.
Migrate the thirteen checks to take them from the context.
One test per check that exercises a branch through a stubbed context with no repository on disk.
Acceptance
No check references execGit, spawnSync or openIndex directly; the greppable form is that those identifiers appear only in the default context.
Split out of #463 rather than half-implemented inside it.
What #463 landed
DoctorContextexists and carriesopts, a monotonicnow, and the memo that keepshook-runtimerunning exactly once. The registry, the runner, crash containment anddurationMsare all in.What it does not
PRD §2.3 names the effect set a check should receive — cwd, git runner, spawn, clock, index opener. Only the clock is injected. Every check still calls
execGit,spawnSyncandopenIndexdirectly against the process.That was a deliberate scope call: threading five effects through thirteen checks touches every one of them, and #463's own constraint is that the text stays byte-identical. A half-injected context — some checks taking effects, others reaching around them — is worse than neither, because a test that stubs the context would then be testing an arbitrary subset.
Why it is worth doing
runbecomes testable in isolation (ADR-0032 §4's stated benefit, currently unrealised). Today a check's test needs a real repository on disk; doctor's read-only and no-network promises are documented invariants with no test that would catch a violation #461's invariant tests build four of them.test/doctor-invariants.test.tsstubsnet.Socketanddns.lookupat the Node level precisely because there is no seam closer to the check. An injected spawn would let the no-network fence cover the spawned-git half it currently cannot see.Scope
DoctorContextwithgit,spawn,env,openIndex, defaulting to the real implementations.Acceptance
execGit,spawnSyncoropenIndexdirectly; the greppable form is that those identifiers appear only in the default context.Out of scope
Changing any check's logic or output. This is a seam, not a rewrite.
Depends on #463.