carlos / platform Public

copy gate: scan internal/host/movesoffline.go; its RetiredPath Reason is a diagnostic

The park report's Reason for a path left in place is the OS error of the
rename, not copy (Paul's rule, 2026-10-16). One narrowly scoped entry in
movesDiagnosticWrites leaves that write (retire, RetiredPath.Reason) out of
the field's writes; the Phase 1 Reason reads in move.go keep their own rows.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev b865d0212f7e8c30ef302db1a06d842b3235c354 parent 6a9ddc9
2 files changed, +41 −1
  • approved_moves_copy_scan_test.go +10 −1
  • approved_moves_copy_test.go +31 −0
diff --git a/approved_moves_copy_scan_test.go b/approved_moves_copy_scan_test.go
index 1de9a25b0..27019c88b 100644
--- a/approved_moves_copy_scan_test.go
+++ b/approved_moves_copy_scan_test.go
@@ -373,6 +373,9 @@ type movesUniverse struct {
paramSinks map[string]bool // functions whose parameter reaches a sink
files []string
steps int
+ // diagSkipped counts, per movesDiagnosticWrites entry, the writes the
+ // universe left out of the field's writes.
+ diagSkipped map[string]int
}
func (u *movesUniverse) at(p string) string {
@@ -386,7 +389,7 @@ func newUniverse(t *testing.T, root string, files []string) *movesUniverse {
t.Helper()
u := &movesUniverse{t: t, root: root, fieldWrites: map[string][]*fieldWrite{}, fieldTypes: map[string]map[string]bool{},
structFields: map[string][]string{}, fieldTypeOf: map[string]map[string]ast.Expr{}, results: map[string]*fieldResult{}, fieldTexts: map[string]string{}, used: map[string]bool{},
- paramSinks: map[string]bool{}, files: files}
+ paramSinks: map[string]bool{}, files: files, diagSkipped: map[string]int{}}
parsed := map[string]*ast.File{}
for _, f := range files {
af, err := parser.ParseFile(movesFset, u.at(f), nil, 0)
@@ -468,6 +471,12 @@ func newUniverse(t *testing.T, root string, files []string) *movesUniverse {
for i, el := range n.Elts {
if kv, ok := el.(*ast.KeyValueExpr); ok {
if k, ok := kv.Key.(*ast.Ident); ok {
+ if fd != nil {
+ if key := fd.Name.Name + "|" + ctype + "." + k.Name; movesDiagnosticWrites[f][key] != "" {
+ u.diagSkipped[f+"|"+key]++
+ continue
+ }
+ }
u.fieldWrites[k.Name] = append(u.fieldWrites[k.Name], &fieldWrite{file: f, fd: fd, expr: kv.Value, ctype: ctype})
}
} else if i < len(order) {
diff --git a/approved_moves_copy_test.go b/approved_moves_copy_test.go
index e9ac74371..1622610d5 100644
--- a/approved_moves_copy_test.go
+++ b/approved_moves_copy_test.go
@@ -1261,6 +1261,7 @@ var movesScanFiles = []string{
"internal/host/movesparks.go",
"internal/host/movespaging.go",
"internal/host/movespass.go",
+ "internal/host/movesoffline.go",
"internal/host/movesphase1.go",
"internal/host/movespolicy.go",
"internal/host/movesprobe.go",
@@ -1524,6 +1525,20 @@ var movesSinkAllow = map[string]map[string]string{
},
}
+// movesDiagnosticWrites are field writes the scan leaves out of a field's
+// writes because what they write is an OS diagnostic for the operator's
+// eye, not copy. Paul's rule (2026-10-16): internal error fragments and
+// diagnostics stay out of copy review. The key is "<function>|<struct>.<field>"
+// and the value says why. An entry leaves out only the composite-literal
+// writes of that field in that function of that file, so the field's other
+// writers (the Phase 1 Reason reads in move.go among them) are judged as before.
+// TestMovesDiagnosticWritesAreReal holds each entry to a write it leaves out.
+var movesDiagnosticWrites = map[string]map[string]string{
+ "internal/host/movesoffline.go": {
+ "retire|RetiredPath.Reason": "the parked report's reason for a path left in place is the OS error of the rename (leftCannot or leftNotYet plus err.Error()), a box diagnostic that `carlos move offline status` shows as MV-O58's <reason>; it is not copy",
+ },
+}
+
// movesServerText names, per file, the type that carries text the console
// wrote (fix round 2 of Plan 6 Task 1a, ruling 1). It is made in one function
// that reads an HTTP response body, and said through one sink; the gate
@@ -2471,3 +2486,19 @@ func TestMovesServerTextRefusesForgedValues(t *testing.T) {
}
}
}
+
+// Every movesDiagnosticWrites entry leaves out a write that exists, and the
+// reason it gives is a sentence.
+func TestMovesDiagnosticWritesAreReal(t *testing.T) {
+ u := newUniverse(t, ".", movesScanFiles)
+ for f, keys := range movesDiagnosticWrites {
+ for key, why := range keys {
+ if len(why) < 40 {
+ t.Errorf("%s %s: the reason is too short to be one", f, key)
+ }
+ if u.diagSkipped[f+"|"+key] == 0 {
+ t.Errorf("%s %s leaves out no write: drop the entry", f, key)
+ }
+ }
+ }
+}