Skip to content

Commit ff68b52

Browse files
feat(doctor): X6 — safe doctor --fix (non-destructive reconcile) (spec 13) (#49)
Wire `--fix` to the only strictly non-destructive remediation: the self-healing ledger reconcile (prune ref rows for projects no longer live), via Manager.Reconcile. It NEVER drops a volume/DB/container — those stay explicit- confirmation jobs (`shared gc`, `workspace destroy`). Best-effort: a down daemon or non-workspace dir is reported, not fatal. With the trust/dns/shared probes (PRs #33/#35/#48) this completes X6: the full doctor matrix + a safe --fix. Verified: `doctor --fix` outside a workspace prints "reconcile skipped" and exits 0. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 663ccbe commit ff68b52

1 file changed

Lines changed: 23 additions & 1 deletion

File tree

‎internal/cli/doctor.go‎

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func newDoctorCmd(g *GlobalOpts) *cobra.Command {
4040
}
4141
renderChecks(cmd, checks)
4242
if fix {
43-
fmt.Fprintln(cmd.OutOrStdout(), "\n(--fix has no automatic remediations wired yet; planned with M6 doctor)")
43+
doctorFix(cmd)
4444
}
4545
for _, c := range checks {
4646
if c.Status == docker.StatusFail {
@@ -55,6 +55,28 @@ func newDoctorCmd(g *GlobalOpts) *cobra.Command {
5555
return cmd
5656
}
5757

58+
// doctorFix applies the STRICTLY non-destructive remediations (spec 13): the
59+
// self-healing ledger reconcile (prune ref rows for projects no longer live). It
60+
// never drops a volume/DB/container — those are explicit-confirmation jobs
61+
// (`shared gc`, `workspace destroy`). Best-effort: a down daemon or no workspace
62+
// is reported, not fatal.
63+
func doctorFix(cmd *cobra.Command) {
64+
w := cmd.OutOrStdout()
65+
fmt.Fprintln(w, "\n--fix: applying safe remediations…")
66+
mgr, closeFn, err := buildManager(cmd)
67+
if err != nil {
68+
fmt.Fprintf(w, " reconcile skipped: %v\n", err)
69+
return
70+
}
71+
defer closeFn()
72+
pruned, err := mgr.Reconcile(cmd.Context())
73+
if err != nil {
74+
fmt.Fprintf(w, " reconcile skipped: %v\n", err)
75+
return
76+
}
77+
fmt.Fprintf(w, " reconciled the ledger: pruned %d stale ref row(s)\n", len(pruned))
78+
}
79+
5880
// rebuildLedger reconstructs the shared_service + ref ledger from on-disk config
5981
// intersected with live container labels (spec 09 §crash-recovery).
6082
func rebuildLedger(cmd *cobra.Command, g *GlobalOpts) error {

0 commit comments

Comments
 (0)