v1.1.0 — Doctor for the channels that fail silently
Summary: mpg setup writes four delivery channels and every way they decay is silent, so the only symptom is that guidance stops arriving. mpg doctor reads each one back and says which are working, which are broken, and which could not be determined — reporting only, never repairing.
Added
mpg doctorreports the state of each delivery channelsetupwrites — the MCP registration, the Agent Skills symlink, the rule symlink, and the PostToolUse hook — and repairs none of them. Every way those decay is silent: a flattened symlink still has content, a link whose target moved still reads as a name, an unregistered hook simply never fires. Nothing raises, so the only visible symptom is that guidance stops arriving, with no signal pointing at the cause — the workspace this was measured against had all four broken for two and a half months while looking, from the outside, like a tool that did not work. Channels read aspresent,degraded,absent, orunknown.absentis deliberately not a failure:--mcp-only,--skills-only, and--no-hookeach make an unwritten channel a configuration someone chose, and counting it as breakage would make a red result mean nothing.unknownis deliberately not health: an empty report set exits 2 rather than 0, because a run that evaluated nothing has not established that anything works. The MCP verdict is read fromclaude mcp get'sStatus:line rather than by comparing the registered command against this installation's paths. That comparison was written first and measured wrong — it describes whichever interpreter invokeddoctor, so a user who installs with uv and runsdoctorout of a source checkout sees healthy links reported as degraded, which is what the first run against an already-repaired workspace did. Whether the registration connects is a property of the registration, so the answer does not depend on who asks. The cost is thatclaude mcp getstarts the server to answer (1.5s, against 0.05s forclaude --version);claude mcp listwould connect to every configured server and took 30s, so it is not used. (closes #211)
Changed
- The predicate
setupuses to decide whether a delivery symlink is already the one it would write is now a named function shared withdoctor, rather than an inline test duplicated acrosssetup_skillsandsetup_rules. The two readers ask different questions of the same classification —setupreplaces a link pointing anywhere else,doctoraccepts one pointing into another working installation — and having that difference is only safe because the classification itself lives in one place. One behaviour changed in the extraction: the inline form calledPath.resolve()without catching anything, so a symlink loop raised out ofmpg setuprather than replacing the link. How that surfaces depends on the interpreter — measured, 3.12.12 raises RuntimeError while 3.13.14 and 3.14.6 return the input unchanged and reach the same verdict without an exception — so the regression test asserts the outcome rather than the mechanism, and only demonstrates the fix on the versions that raise. That case, and the OSError a hostile or racing tree can raise, now classify as stale and are replaced. No existing test covered the path.