Summary
ProjectStateFileCheck has a complete, working fix() that rebuilds .mcs-project by inferring packs from CLAUDE.local.md section markers. It is dead code: the check returns .warn in both branches where that fix applies, and DoctorRunner.runChecks appends to pendingFixes only from the case .fail arm. A .warn never reaches executePendingFixes, so the fix is never invoked.
Both warning messages tell the user to do exactly the thing that will not happen:
"corrupt .mcs-project: … — run 'mcs doctor --fix'"
"CLAUDE.local.md exists but .mcs-project missing — run 'mcs doctor --fix'"
Symptom
mcs doctor reports a corrupt or missing .mcs-project, instructs the user to run mcs doctor --fix, and --fix does nothing about it. The recovery has to be done by hand (delete the file and re-run mcs sync) despite the automated path existing.
Two gates, both need fixing
Returning .fail alone is not sufficient. executePendingFixes then partitions on fixCommandPreview, and ProjectStateFileCheck does not override it, so it would land in the "unfixable" bucket where only .notFixable is surfaced — its .fixed result would be dropped without incrementing fixedCount. That is the same failure mode as PR #162.
So:
- return
.fail instead of .warn in the two recoverable branches (leave the .skip branch alone — no state and no CLAUDE.local.md is a legitimate un-synced project, not a fault)
- add a
fixCommandPreview describing the rebuild, e.g. "rebuild .mcs-project from CLAUDE.local.md section markers"
Severity note: DoctorCommand.perform() discards the DoctorSummary and sets no exit code, so promoting these to .fail only moves them into the "Issues" tally — it does not change any command's exit status.
Acceptance criteria
Summary
ProjectStateFileCheckhas a complete, workingfix()that rebuilds.mcs-projectby inferring packs fromCLAUDE.local.mdsection markers. It is dead code: the check returns.warnin both branches where that fix applies, andDoctorRunner.runChecksappends topendingFixesonly from thecase .failarm. A.warnnever reachesexecutePendingFixes, so the fix is never invoked.Both warning messages tell the user to do exactly the thing that will not happen:
"corrupt .mcs-project: … — run 'mcs doctor --fix'""CLAUDE.local.md exists but .mcs-project missing — run 'mcs doctor --fix'"Symptom
mcs doctorreports a corrupt or missing.mcs-project, instructs the user to runmcs doctor --fix, and--fixdoes nothing about it. The recovery has to be done by hand (delete the file and re-runmcs sync) despite the automated path existing.Two gates, both need fixing
Returning
.failalone is not sufficient.executePendingFixesthen partitions onfixCommandPreview, andProjectStateFileCheckdoes not override it, so it would land in the "unfixable" bucket where only.notFixableis surfaced — its.fixedresult would be dropped without incrementingfixedCount. That is the same failure mode as PR #162.So:
.failinstead of.warnin the two recoverable branches (leave the.skipbranch alone — no state and noCLAUDE.local.mdis a legitimate un-synced project, not a fault)fixCommandPreviewdescribing the rebuild, e.g."rebuild .mcs-project from CLAUDE.local.md section markers"Severity note:
DoctorCommand.perform()discards theDoctorSummaryand sets no exit code, so promoting these to.failonly moves them into the "Issues" tally — it does not change any command's exit status.Acceptance criteria
mcs doctor --fixin a project withCLAUDE.local.mdand no.mcs-projectrebuilds the state file.mcs-project.skip, not a failure