diff --git a/cmd/mesh-controller/plan.go b/cmd/mesh-controller/plan.go index a7149f4f..c648595e 100644 --- a/cmd/mesh-controller/plan.go +++ b/cmd/mesh-controller/plan.go @@ -512,6 +512,15 @@ func sortedKeysOf(m map[string]string) []string { return out } +// sayPlanDiff is `plan --diff`: what the declaration leaves out, then the diff. A module left out is not in +// the body, so the diff alone would say "nothing would change" for a module just assigned whose settings +// cannot compose — success-shaped silence. Said first, with why, as push and the plain plan say it +// (novox/hq ADR 0163, rule 6). +func sayPlanDiff(node string, declared sendable, diff func() error) error { + reportLeftOut(node, declared) + return diff() +} + // reportLeftOut says which of a machine's modules its declaration leaves out and why (novox/hq ADR // 0163, rule 6), one line each: the machine is told everything else, and is told it was left out. func reportLeftOut(node string, declared sendable) { @@ -1239,11 +1248,9 @@ func planCommand(ctx context.Context, args []string) error { if err != nil { return err } - // A module left out is not in the body, so the diff alone would say "nothing would change" for - // a module just assigned whose settings cannot compose — success-shaped silence. Said first, with - // why, as push and the plain plan say it (novox/hq ADR 0163, rule 6). - reportLeftOut(args[0], declared) - return writePlanDiff(ctx, open.inventory, args[0], body) + return sayPlanDiff(args[0], declared, func() error { + return writePlanDiff(ctx, open.inventory, args[0], body) + }) } if *asJSON { declared, err := declarationFor(ctx, open, args[0], plan, settings) diff --git a/cmd/mesh-controller/plan_diff_left_out_test.go b/cmd/mesh-controller/plan_diff_left_out_test.go new file mode 100644 index 00000000..29fbe855 --- /dev/null +++ b/cmd/mesh-controller/plan_diff_left_out_test.go @@ -0,0 +1,25 @@ +package main + +import ( + "os" + "strings" + "testing" +) + +// 2026-10-09: nfs-server was assigned to the home server, its file asked for a setting nothing set, and +// `plan --diff` said "nothing would change". The diff now says what is left out, and why, before the diff. +func TestPlanDiffSaysAModuleLeftOutBeforeTheDiff(t *testing.T) { + declared := sendable{LeftOut: []string{"nfs-server"}, + leftOutWhy: map[string]string{"nfs-server": `nothing sets "shares" for it`}} + said := printed(t, func() error { + return sayPlanDiff("home", declared, func() error { + writeDiff(os.Stdout, "home", sentDiff{}, nil) + return nil + }) + }) + left := strings.Index(said, "home: nfs-server left out") + nothing := strings.Index(said, "home: nothing would change") + if left < 0 || !strings.Contains(said, `nothing sets "shares" for it`) || nothing < left { + t.Errorf("the left-out module and why, then the diff:\n%s", said) + } +}