Scope source layout assumptions to workspace
ober
6837adecfb59332c5a06d9aa3d3a0d37742109e4
--- a/src/jcode/core/agent.ss +++ b/src/jcode/core/agent.ss @@ -186,7 +186,10 @@ You have access to these tools: ~a IMPORTANT RULES: -- Source code lives in src/ with extension .ss. NEVER edit files in lib/ — those are generated build artifacts (.sls/.so). +- Follow this repository's instructions and actual file layout for source paths, + generated artifacts, and build commands. More specific repo-local instructions, + explicit task paths, and files you find on disk override generic/global + conventions when they conflict. - ALWAYS read a file (with the read tool) before editing it. Do not invent file contents. - The edit tool requires old_str to match the file BYTE-FOR-BYTE. If edit returns 'old_str not found', do NOT retry with similar text — read the file to see real content, then edit. - Do NOT call the same tool repeatedly with the same or similar arguments. Use grep/glob to locate code instead of guessing paths. @@ -200,7 +203,7 @@ Text-form tool invocations are not part of the protocol and may be silently dropped — emit a real tool_call message instead. When the user asks you to do something: -1. Use grep or glob to FIND the relevant source files in src/ +1. Use grep, glob, ls, or repomap to FIND the relevant source files for this repository 2. READ those files to see what's actually there 3. Make minimal targeted edits with the edit tool 4. Report back with results --- a/src/jcode/tool/file.ss +++ b/src/jcode/tool/file.ss @@ -166,20 +166,55 @@ ;;; WRITE ;;; +(def (collapse-leading-slashes p) + (let loop ((s p)) + (if (and (string? s) + (> (string-length s) 1) + (string-prefix? "//" s)) + (loop (substring s 1 (string-length s))) + s))) + +(def (workspace-relative-path path) + (let* ((p0 (collapse-leading-slashes (normalize-path path))) + (p (if (and (string? p0) + (or (string-prefix? "/" p0) + (string-prefix? "~" p0))) + (collapse-leading-slashes (path-expand p0)) + p0)) + (cwd (strip-trailing-slash (collapse-leading-slashes (current-directory)))) + (prefix (string-append cwd "/"))) + (cond + ((not (string? p)) p) + ((string-prefix? prefix p) + (substring p (string-length prefix) (string-length p))) + ((string-prefix? "./" p) + (substring p 2 (string-length p))) + (else p)))) + +(def (generated-sls-source-path path) + ;; In jerboa-code, lib/jcode/foo.sls is generated from src/jcode/foo.ss. + ;; Other workspaces may legitimately use .sls as source, so only classify a + ;; file as generated when that corresponding .ss source actually exists. + (let ((rel (workspace-relative-path path))) + (and (string? rel) + (string-prefix? "lib/" rel) + (string-suffix? ".sls" rel) + (let* ((stem (substring rel 4 (- (string-length rel) 4))) + (src (string-append "src/" stem ".ss"))) + (and (file-exists? src) src))))) + (def (generated-artifact-error tool path) ;; Block writes to generated build artifacts so the model can't "fix" the ;; bug in the compiled output. Returns an error string or #f. (let ((p (or path ""))) (cond - ((string-suffix? ".sls" p) - (format "Error: ~a refused — ~a is a generated .sls build artifact. Edit the corresponding .ss file under src/ instead." tool p)) + ((generated-sls-source-path p) + => (lambda (src) + (format "Error: ~a refused — ~a is a generated .sls build artifact. Edit ~a instead." tool p src))) ((string-suffix? ".so" p) - (format "Error: ~a refused — ~a is a compiled binary. Edit the corresponding .ss source under src/ instead." tool p)) - ((or (string-contains p "/lib/jcode/") - (string-contains p "/lib/std/") - (string-prefix? "lib/" p) - (string-prefix? "./lib/" p)) - (format "Error: ~a refused — ~a is under lib/ (generated). Edit the corresponding source under src/ instead." tool p)) + (format "Error: ~a refused — ~a is a compiled binary. Edit the source file instead." tool p)) + ((string-suffix? ".wpo" p) + (format "Error: ~a refused — ~a is a compiled artifact. Edit the source file instead." tool p)) (else #f)))) (def (handle-write args) --- a/test/run.ss +++ b/test/run.ss @@ -217,6 +217,26 @@ [r (tool-execute "edit" (args "path" tmp "old_str" "zzz" "new_str" "q"))]) (check-pred! "edit missing string returns error" r (lambda (s) (str-prefix? "Error:" s)))) +;; generated artifact guard is workspace-aware +(let ([root (format "/tmp/jcode-artifact-generated-~a" (random 100000000))]) + (mkdir root) + (parameterize ([current-directory root]) + (tool-execute "write" (args "path" "src/pkg/foo.ss" "content" "source")) + (let ([r (tool-execute "write" (args "path" "lib/pkg/foo.sls" "content" "generated"))]) + (check-pred! "generated .sls with src counterpart refused" r + (lambda (s) (and (str-prefix? "Error:" s) + (str-contains? s "src/pkg/foo.ss"))))))) + +(let ([root (format "/tmp/jcode-artifact-source-~a" (random 100000000))]) + (mkdir root) + (parameterize ([current-directory root]) + (let ([r (tool-execute "write" (args "path" "lib/pkg/foo.sls" "content" "source-sls"))]) + (check-pred! "source .sls without src counterpart allowed" r + (lambda (s) (str-prefix? "Successfully wrote" s)))) + (check! "source .sls write persisted" + (tool-execute "read" (args "path" "lib/pkg/foo.sls")) + "source-sls"))) + ;; glob (let ([r (tool-execute "glob" (args "pattern" "*.ss" "path" (current-directory)))]) (check-pred! "glob finds root .ss files" r