Record list-set mutation guidance

ober

87a536b0485db7d13462168190bc012bc6ee7e59

diff --git a/data/anti-patterns.sexp b/data/anti-patterns.sexp
index 5bfb5c0..2cbab10 100644
--- a/data/anti-patterns.sexp
+++ b/data/anti-patterns.sexp
@@ -4632,4 +4632,21 @@
      "verify"
      "line_edit"
      "replace_range"
-     "jerboa_function_signature")))
+     "jerboa_function_signature"))
+ (("advice"
+    .
+    "Use a mutable vector for fixed-position state and update fields with vector-set!, or keep lists immutable and rebuild/replace the parent value. For 2-D boards, use a vector of row vectors and vector-set! on the row vector. After a list-set! rejection on a missing-file draft, rewrite the state representation and resubmit corrected file contents rather than inspecting dependencies.")
+   ("avoid"
+     .
+     "Do not represent mutable record-like game/application state as a list and mutate fields with list-set!. Jerboa does not provide list-set!, and local models often then lose a whole first draft to syntax-guard rejection.")
+   ("id" . "invented-list-set-mutation")
+   ("kinds" "script" "debug-error" "test")
+   ("pattern"
+     .
+     "list-set!|list-set! is not available in Jerboa")
+   ("severity" . "medium")
+   ("tags" "list-set" "vector-set" "mutation" "state"
+     "local-model" "repair")
+   ("title" . "Do Not Use Invented list-set! For Jerboa State")
+   ("tools" "verify" "edit" "write" "replace_range"
+     "jerboa_error_fix_lookup")))
diff --git a/data/error-fixes.sexp b/data/error-fixes.sexp
index 5103210..959ece3 100644
--- a/data/error-fixes.sexp
+++ b/data/error-fixes.sexp
@@ -2860,4 +2860,18 @@
    ("pattern"
      .
      "variable atom-(set!|val) is not bound|Exception: variable atom-(set!|val) is not bound")
+   ("type" . "module-api"))
+ (("code_example"
+    .
+    ";; Wrong\n(def st (list 0 0 #f))\n(list-set! st 2 #t)\n\n;; Right for fixed-position mutable state\n(def st (vector 0 0 #f))\n(vector-set! st 2 #t)\n(vector-ref st 2)")
+   ("explanation"
+     .
+     "Jerboa exposes vector mutation primitives, but no list-set! helper. Generated game code often treats lists as mutable records; that fails before verification or as an unbound variable.")
+   ("fix"
+     .
+     "Do not use list-set!. For fixed-position mutable state use a vector and vector-set!/vector-ref; for lists, rebuild the list and replace the parent binding/value. For boards, use a vector of row vectors and vector-set! the row cell.")
+   ("id" . "jerboa-list-set-unavailable")
+   ("pattern"
+     .
+     "list-set! is not available in Jerboa|variable list-set! is not bound|Exception: variable list-set! is not bound")
    ("type" . "module-api")))
diff --git a/tests/test-model-guidance-data.ss b/tests/test-model-guidance-data.ss
index bb48634..aea6acf 100644
--- a/tests/test-model-guidance-data.ss
+++ b/tests/test-model-guidance-data.ss
@@ -44,8 +44,10 @@
 
 (let* ([fixes (read-one "data/error-fixes.sexp")]
        [fix (entry-by-id fixes "jerboa-atom-accessor-unbound")]
+       [list-set-fix (entry-by-id fixes "jerboa-list-set-unavailable")]
        [anti-patterns (read-one "data/anti-patterns.sexp")]
-       [anti (entry-by-id anti-patterns "invented-jerboa-atom-accessors")])
+       [anti (entry-by-id anti-patterns "invented-jerboa-atom-accessors")]
+       [list-set-anti (entry-by-id anti-patterns "invented-list-set-mutation")])
   (check "atom unbound error fix exists"
          (and fix
               (string-contains? (entry-ref fix "pattern") "atom-")
@@ -55,7 +57,17 @@
          (and anti
               (string-contains? (entry-ref anti "avoid") "atom-set!")
               (string-contains? (entry-ref anti "avoid") "atom-val")
-              (string-contains? (entry-ref anti "advice") "verify immediately"))))
+              (string-contains? (entry-ref anti "advice") "verify immediately")))
+  (check "list-set unavailable error fix exists"
+         (and list-set-fix
+              (string-contains? (entry-ref list-set-fix "pattern") "list-set!")
+              (string-contains? (entry-ref list-set-fix "fix") "vector-set!")
+              (string-contains? (entry-ref list-set-fix "fix") "rebuild")))
+  (check "invented list-set anti-pattern exists"
+         (and list-set-anti
+              (string-contains? (entry-ref list-set-anti "avoid") "list-set!")
+              (string-contains? (entry-ref list-set-anti "advice") "vector-set!")
+              (string-contains? (entry-ref list-set-anti "advice") "missing-file draft"))))
 
 (if (= fail 0)
     (begin