Recover from empty verified read calls
ober
43455391416ea1b1a0bb01fb99366e3158317235
--- a/src/jcode/core/verified-run.ss +++ b/src/jcode/core/verified-run.ss @@ -1606,6 +1606,9 @@ (def default-read-start-limit 120) +(def (missing-read-path-message) + "read requires a path. Do not retry empty read(). Use list(path=\".\") to inspect the current directory, or read(path=\"Makefile\"), read(path=\"README.md\"), read(path=\"AGENTS.md\"), or read(path=\"test/run-tests.sh\") for a specific file that exists. If you already understand the repo, use edit/write with the first complete draft, then verify().") + (def (slice-content content args) (let* ((line-no (arg-int args "line" 0)) (start-line (arg-int args "start" 0)) @@ -1634,7 +1637,8 @@ (def (do-read-current args cwd) (let ((path (arg-path args #f))) - (if (not path) (error 'read "missing path arg") + (if (not path) + (raise-recoverable-tool-error (missing-read-path-message) 'read) (let ((p (abs-path cwd path))) (cond ((rejected-draft-read-message cwd path args) => (lambda (msg) msg)) --- a/test/run.ss +++ b/test/run.ss @@ -2351,6 +2351,44 @@ (str-contains? s "Do not retry run/bash/shell"))))) (let* ([vr-dir "/tmp"] + [target "jcode-verified-empty-read-recovery.txt"] + [target-path (string-append vr-dir "/" target)] + [tool-results '()] + [wf (coding-workflow (string-append "grep -q ok " target) vr-dir)] + [resp (scripted-responder + (list + (list (make-wtool-call "read" '() #f)) + (list (make-wtool-call "read" '() #f)) + (list (make-wtool-call "read" '() #f)) + (list (make-wtool-call "edit" (list (cons "path" target) + (cons "content" "ok\n")) #f)) + (list (make-wtool-call "verify" '() #f)) + (list (make-wtool-call "done" '(("summary" . "empty-read-recovered")) #f))))]) + (safe-delete-test-file! target-path) + (let ([result (run-workflow wf "recover from empty read calls" resp + (list (cons 'max-iterations 10) + (cons 'max-tool-errors 0) + (cons 'on-message + (lambda (m) + (when (equal? (message-role m) "tool") + (set! tool-results + (cons (message-content m) tool-results)))))))]) + (check! "verified-run: repeated empty read recovers with zero hard errors" + result "empty-read-recovered") + (check-pred! "verified-run: empty read gives recoverable concrete path guidance" + (reverse tool-results) + (lambda (xs) + (let loop ([ys xs]) + (cond + [(null? ys) #f] + [(and (str-contains? (car ys) "[ToolRecoverableError]") + (str-contains? (car ys) "read requires a path") + (str-contains? (car ys) "list(path=\".\")")) + #t] + [else (loop (cdr ys))]))))) + (safe-delete-test-file! target-path)) + +(let* ([vr-dir "/tmp"] [fixture "jcode-verified-run-budget-fixture.txt"] [fixture-path (string-append vr-dir "/" fixture)] [target "jcode-verified-run-budget-out.txt"]