doctor: promote to a top-level verb and give it runtime evidence - #351
Merged
Conversation
`gortex init doctor` was scoped wrong in its name and in what it could
see. The name reads repo-local while the report has always been
machine-wide, and the report only ever described configuration — which
is exactly the half that cannot answer the question people run doctor to
answer.
An agent config is not evidence. Codex records trust against each hook's
hash and skips new or changed hooks until the user approves them in
/hooks, so a fully populated config.toml and a completely inert
integration are byte-identical on disk. Doctor would have called that
machine healthy. The distinguishing signal already existed and was never
read: every hook process appends a row to hook-effectiveness.jsonl
whether or not it had anything to inject, so an event with zero rows did
not run.
`gortex doctor` now reports, alongside the adapter drift it always did:
hook activity runs, injections, and daemon reachability per event.
Configured with zero runs on a trust-gating agent is
reported as a blocker carrying the /hooks remedy; a
single event at zero while others ran is the same
finding narrowed to one upgraded hook definition.
adoption gortex tool calls vs shell calls from the agent's own
session transcripts.
savings what the ledger recorded, and how much of it carries
no client or model attribution.
Exit status is non-zero when a blocker is found, so CI can gate on it,
and --redact hashes repo paths and branch names so the report can be
pasted into an issue. `gortex init doctor` still works, marked
deprecated.
The adoption section deliberately carries no "did the hook fire" signal:
Codex does not persist hook-injected context into its transcripts, so an
absent orientation block proves nothing and must not be reported as if
it did. Hook liveness comes from the invocation log alone.
Splits the probes into internal/doctor (transcripts, findings) and a
reader in internal/hooks that lives beside the writer, so the record
shape has one definition. codex.Inspect reuses the adapter's own hook
recognisers, and a test installs then inspects, so a writer that changes
shape without the reader following fails rather than silently reporting
a healthy install as unconfigured.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #348 (now merged): that PR fixed the Codex install, this one makes the failure it fixed visible.
Why
gortex init doctorwas scoped wrong twice over. The name reads repo-local while the report has always been machine-wide. And it only ever described configuration — which is exactly the half that cannot answer the question people run doctor to answer.An agent config is not evidence. Codex records trust against each hook's hash and skips new or changed hooks until the user approves them in
/hooks, so a fully populatedconfig.tomland a completely inert integration are byte-identical on disk. The old doctor would have called the machine in #348's report healthy.The distinguishing signal already existed and nothing read it: every hook process appends a row to
hook-effectiveness.jsonlwhether or not it had anything to inject, so an event with zero rows did not run.gortex auditis a different thing entirely — a repo-level A–F code-health grade from graph topology, for the README badge — so it was not the right home for any of this.What
gortex doctoris now top-level, withgortex init doctorkept as a deprecated alias. Alongside the adapter drift it always reported:Configured-with-zero-runs on a trust-gating agent is a blocker carrying the remedy:
A single event at zero while others ran is the same finding narrowed to one upgraded hook definition — trust is per-hash, so an upgrade silences exactly the hooks whose definitions changed.
Exit status is non-zero on a blocker so CI can gate on it.
--redacthashes repo paths and branch names so the output can be pasted into an issue;--jsonfor machines;--dayssets the window.Structure
internal/hooks/telemetry_read.go— the reader lives beside the writer, so the record shape has exactly one definition. A diagnostic that mis-parses its own telemetry is worse than no diagnostic.internal/doctor— transcript scanning and the findings table. Data in, findings out: the whole decision table is testable without a machine that has Codex, hooks, or a daemon on it.codex.Inspect— real TOML parsing that reuses the adapter's own hook recognisers, with a test that installs and then inspects. IfApply's hook shape ever changes without the reader following, that test fails instead of doctor quietly reporting a correct install as unconfigured.One deliberate omission
The adoption section carries no "did the hook fire" signal. I had planned to look for the SessionStart orientation block in transcripts — then verified Codex 0.145.0 does not persist hook-injected context into rollouts at all. An absent block proves nothing, so the section reports adoption only and says so in its own output, rather than inviting a zero there to be read as "the hook never fired".
Tests
37 new cases across three packages:
LastSeentracked across the whole log (so "last ran 3d ago" survives a narrow window), torn-line tolerance, missing log is not an error.go build ./...,go vet ./...,go test ./internal/... ./cmd/gortex/— 12,012 tests across 169 packages, green.Follow-up this exposes
hook-effectiveness.jsonlhas no agent dimension, so on a machine running both Claude Code and Codex the counts are the sum and doctor has to caveat itself. The hook command already receives--agent=codex; the log just does not record it. Small change, and it would make this diagnostic unambiguous.