Force direct repairs after known draft guards

ober

eed4bcb734c7492addeb3cf5591a3222575a6a84

diff --git a/src/jcode/core/verified-run.ss b/src/jcode/core/verified-run.ss
index 42f764f..eaa9333 100644
--- a/src/jcode/core/verified-run.ss
+++ b/src/jcode/core/verified-run.ss
@@ -447,17 +447,28 @@
        (tool-specs-include? specs "edit")
        (openai-function-tool-choice "edit")))
 
+(def (rejected-draft-direct-edit-guard?)
+  (let ((msg (current-rejected-draft-guard-message)))
+    (and (string? msg)
+         (or (string-contains msg "list-set! is not available")
+             (string-contains msg "random-make-state")
+             (string-contains msg "Jerboa has no return form")
+             (string-contains msg "Jerboa has no break form")
+             (string-contains msg "Jerboa has no continue form")
+             (string-contains msg "Jerboa imports use")))))
+
 (def (force-staged-create-repair-tool-name specs)
   ;; Syntax-broken missing-file creates have no on-disk artifact yet. The
-  ;; retained draft is the only inspectable artifact, and weaker reasoning
-  ;; models often burn the retry turn manually counting parens from memory.
-  ;; Force one compact balance read first, then return the broader staged
-  ;; repair tools on the following turn.
+  ;; retained draft is the only inspectable artifact. Structural delimiter
+  ;; failures benefit from one compact balance read, but known forbidden-name
+  ;; failures already carry the exact repair class and should go straight to
+  ;; a corrected edit/write turn.
   (and (syntax-broken-missing-create-repair-mode?)
        (= (current-rejected-draft-inspections) 0)
        (= (current-rejected-draft-path-reject-count) 1)
-       (tool-specs-include? specs "balance")
-       "balance"))
+       (if (rejected-draft-direct-edit-guard?)
+         (and (tool-specs-include? specs "edit") "edit")
+         (and (tool-specs-include? specs "balance") "balance"))))
 
 (def (forced-first-edit-specs specs forced-choice)
   (if forced-choice
@@ -491,7 +502,11 @@
   (forced-tool-choice-response response forced-choice '("edit" "write")))
 
 (def (forced-staged-repair-response response staged-tool)
-  (forced-tool-choice-response response staged-tool (list staged-tool)))
+  (forced-tool-choice-response
+    response staged-tool
+    (if (string=? staged-tool "edit")
+      '("edit" "write")
+      (list staged-tool))))
 
 (def (forced-first-edit-messages messages specs forced-specs)
   (with-visible-tool-system-note messages specs forced-specs))
@@ -2507,6 +2522,9 @@
 (def current-rejected-draft-needs-inspection
   (make-parameter #f))
 
+(def current-rejected-draft-guard-message
+  (make-parameter #f))
+
 (def current-existing-ss-rewrite-reject-path
   (make-parameter #f))
 
@@ -2678,7 +2696,8 @@
   (current-rejected-draft-repeat-count 0)
   (current-rejected-draft-path #f)
   (current-rejected-draft-path-reject-count 0)
-  (current-rejected-draft-needs-inspection #f))
+  (current-rejected-draft-needs-inspection #f)
+  (current-rejected-draft-guard-message #f))
 
 (def (reset-existing-ss-rewrite-state!)
   (current-existing-ss-rewrite-reject-path #f)
@@ -5784,6 +5803,7 @@
       (when (source-ss-path? path)
         (when record-pending?
           (current-pending-ss-create-repair path))
+        (current-rejected-draft-guard-message msg)
         (record-rejected-ss-draft! path content))
       (raise-recoverable-tool-error
         (string-append msg
@@ -7486,6 +7506,7 @@
                    (current-rejected-draft-path #f)
                    (current-rejected-draft-path-reject-count 0)
                    (current-rejected-draft-needs-inspection #f)
+                   (current-rejected-draft-guard-message #f)
                    (current-existing-ss-rewrite-reject-path #f)
                    (current-existing-ss-rewrite-reject-count 0)
                    (current-existing-ss-rewrite-fingerprint #f)
diff --git a/test/run.ss b/test/run.ss
index be4d888..8ee6992 100644
--- a/test/run.ss
+++ b/test/run.ss
@@ -8881,6 +8881,7 @@
 		       [broken "(import (jerboa prelude))\n(def (main)\n  (list-set! xs 0 #t)\n  (displayln \"fixed\"))\n(main)\n"]
 			       [fixed "(import (jerboa prelude))\n(def (main)\n  (displayln \"fixed\"))\n(main)\n"]
 		       [tool-results '()]
+		       [tool-names-after-reject '()]
 		       [i 0]
 		       [slurp (lambda (p) (call-with-input-file p (lambda (in) (get-string-all in))))])
 		  (safe-delete-test-file! target-path)
@@ -8888,20 +8889,18 @@
 		         [provider
 		           (lambda (_messages _tool-specs _step)
 		             (set! i (+ i 1))
+		             (when (= i 2)
+		               (set! tool-names-after-reject
+		                 (map tool-spec-name _tool-specs)))
 		             (cond
 		               [(= i 1)
 		                (list (make-wtool-call "write"
 		                        (list (cons "path" target)
 		                              (cons "content" broken)) #f))]
 			               [(= i 2)
-			                (list (make-wtool-call "balance"
-			                        (list (cons "path" target)) #f))]
-			               [(= i 3)
-			                (list (make-wtool-call "write"
+			                (list (make-wtool-call "edit"
 			                        (list (cons "path" target)
 			                              (cons "content" fixed)) #f))]
-			               [(= i 4)
-			                (list (make-wtool-call "verify" '() #f))]
 			               [else
 			                (list (make-wtool-call "done"
 			                        '(("summary" . "full-retry-recovery-ok")) #f))]))]
@@ -8921,6 +8920,8 @@
 		                                 (cons (message-content m) tool-results)))))))])
 		    (check! "verified-run: hidden write after syntax create recovers"
 		            result "VERIFIED: exit 0\n")
+		    (check! "verified-run: known syntax create forces direct edit"
+		            tool-names-after-reject '("edit"))
 			    (check-pred! "verified-run: full write after syntax create promoted"
 			      (reverse tool-results)
 			      (lambda (xs)