updates
ober
2eff0a36659af4d976f785d91c0758fcf1d0d88d
--- a/AGENTS.md +++ b/AGENTS.md @@ -425,6 +425,7 @@ Jerboa is a niche Scheme dialect with limited training data. **Never guess — a - **`jerboa_howto_add`**: Save new patterns to cookbook (MANDATORY when you discover something non-trivial) - **`jerboa_howto_run`** / **`jerboa_howto_verify`**: Validate recipes still work - **`jerboa_error_fix_add`**: Save error→fix mappings for common mistakes +- **`jerboa_anti_pattern_lookup`**: Search reusable local-model mistakes and failed strategies **The knowledge base is `data/*.sexp` in THIS repo**, embedded into `jmcp` at build time. The write tools above edit it live — the server reads `data/` from disk first, with the embedded copy as fallback (`JERBOA_MCP_REPO` points every client at this repo). When you add a stdlib/language feature, also update `data/` (a cookbook recipe + `api-signatures.sexp` + `changelog.sexp`) and **commit it**. Run `make jmcp` (or `make jmcp-portable`) only to refresh the embedded copy shipped in portable binaries. @@ -451,6 +452,25 @@ Jerboa is niche — every non-trivial pattern you discover prevents future sessi **Recipe format**: `id` (kebab-case), `tags` (4-6 search keywords incl. module name), `imports` (all required), `code` (complete working example), `notes` (gotchas/alternatives). +### Save anti-patterns (`data/anti-patterns.sexp`) whenever you: +- See a plausible local-model strategy that failed verification +- Find a weak verifier pattern that allowed false success +- See a repeated repair loop, such as broad-reading after a concrete error +- Find a generic runtime mistake, such as missing lower-bound checks before vector access + +**Before saving**: check `jerboa_anti_pattern_lookup` to avoid duplicates. If none exists, call `jerboa_anti_pattern_add`; only edit `data/anti-patterns.sexp` directly if the writer tool is unavailable. Save the normalized reusable mistake, not the whole trace or benchmark name. + +**Anti-pattern format**: `id`, `title`, `kinds`, `severity`, `tags`, `pattern`, `avoid`, `advice`, `tools`. + +### Save error fixes (`jerboa_error_fix_add`) whenever you: +- See exact compiler/runtime/verifier text with a repeatable repair +- Hit an error that `jerboa_failure_advisor` should classify better next time +- Debug a local-model generated-code failure where a short diagnosis prevents another failed iteration + +**Before saving**: check `jerboa_error_fix_lookup` with the exact error text. **Do NOT save**: one-off project business-logic mistakes. + +**Error-fix format**: `id`, `pattern`, `fix`; optional `type`, `explanation`, `code_example`. + ### Suggest tooling improvements (`jerboa_suggest_feature`) whenever you: - Make multiple sequential tool calls that could be one tool - Fall back to bash because an MCP tool is missing or insufficient @@ -458,7 +478,7 @@ Jerboa is niche — every non-trivial pattern you discover prevents future sessi **Before suggesting**: check `jerboa_list_features`; vote with `jerboa_vote_feature` if it already exists. ### Save Discoveries Mechanisms -- **`/save-discoveries` skill**: invoke anytime to review session and save patterns + suggestions +- **`/save-discoveries` skill**: invoke anytime to review session and save recipes, anti-patterns, error fixes, feature suggestions, and security patterns - **PreCompact hook**: add `PreCompact` hook with `type: "prompt"` in `.claude/settings.json` to auto-save before context compaction --- --- a/data/anti-patterns.sexp +++ b/data/anti-patterns.sexp @@ -52,6 +52,19 @@ ("tools" "jerboa_verify_plan" "jerboa_verify" "jerboa_run_tests")) (("advice" . + "Check both lower and upper bounds before computing a vector index or calling `vector-ref`/`vector-set!`. Prefer an `in-bounds?` helper such as `(and (>= x 0) (< x width) (>= y 0) (< y height))`.") + ("avoid" + . + "Do not check only `(< x width)` and `(< y height)` before vector-backed grid access; negative coordinates still pass those checks.") + ("id" . "upper-bound-only-grid-check") + ("kinds" "script" "module" "debug-error" "test") + ("pattern" . "vector-ref|vector-set!|not a valid index|grid|neighbor") + ("severity" . "high") + ("tags" "vector" "grid" "bounds" "index" "runtime") + ("title" . "Checking only upper bounds before vector grid access") + ("tools" "jerboa_failure_advisor" "jerboa_error_fix_lookup" "jerboa_howto" "jerboa_verify")) + (("advice" + . "Run `jerboa_check_balance`, read the enclosing top-level form, and replace the whole broken span.") ("avoid" . --- a/data/error-fixes.sexp +++ b/data/error-fixes.sexp @@ -2023,4 +2023,16 @@ "Run `jerboa_check_balance` on the generated file and inspect the reported line. For AI-copied code blocks, compare the local form with the verified reference shape and prefer replacing the whole malformed top-level form or file instead of making repeated tiny edits. In `jcode verified`, continue with `read`, `edit`, and `verify`; do not switch to unavailable shell/run tools.") ("id" . "unexpected-close-paren-copy-error") ("pattern" . "unexpected close parenthesis") - ("type" . "syntax"))) + ("type" . "syntax")) + (("code_example" + . + ";; Bad: upper bounds only; negative x/y still pass.\n(when (and (< x width) (< y height))\n (vector-ref grid (idx x y)))\n\n;; Good: lower and upper bounds before idx/vector-ref/vector-set!.\n(def (in-bounds? x y width height)\n (and (>= x 0) (< x width)\n (>= y 0) (< y height)))\n\n(when (in-bounds? x y width height)\n (vector-ref grid (idx x y)))") + ("explanation" + . + "Vector-backed grids often compute indexes from coordinates. Checking only the upper bounds allows negative coordinates through, producing negative row-major indexes such as -6.") + ("fix" + . + "Add both lower and upper coordinate bounds before computing the index or calling vector-ref/vector-set!: `(and (>= x 0) (< x width) (>= y 0) (< y height))`. Prefer a reusable `in-bounds?` helper and use it in neighbor loops and grid accessors.") + ("id" . "vector-ref-negative-grid-index") + ("pattern" . "vector-ref: -?[0-9]+ is not a valid index|not a valid index for") + ("type" . "runtime-vector-bounds"))) --- a/docs/mcp-advisor-plan.md +++ b/docs/mcp-advisor-plan.md @@ -260,24 +260,28 @@ Current landed slice: - `data/anti-patterns.sexp` stores reusable local-model mistakes with avoid/do guidance and follow-up tools. - `jerboa_anti_pattern_lookup` exposes the anti-pattern data directly. +- `jerboa_anti_pattern_add` adds or updates reusable anti-pattern entries. +- `/save-discoveries` now treats anti-patterns and error fixes as first-class + outputs: use `jerboa_error_fix_add` for reusable exact error repairs and + `jerboa_anti_pattern_add` for reusable local-model mistakes. - Recipe selection now expands task queries by inferred kind so tasks can find useful script/module/docs/debug recipes even when the exact user wording is not in the cookbook. -- Protocol tests cover tool visibility, advisor output, failure diagnosis, and - the upgraded cookbook bundle. +- Protocol tests cover tool visibility, advisor output, failure diagnosis, the + upgraded cookbook bundle, and anti-pattern add/lookup round-trips. - `jcode verified` now calls a registered MCP `jerboa_request_advisor` before - Jerboa-looking runs and appends `jerboa_failure_advisor` output to failed - verifier details when that MCP tool is available. + Jerboa-looking runs and appends source targets, `jerboa_failure_advisor`, + `jerboa_error_fix_lookup`, and `jerboa_anti_pattern_lookup` output to failed + verifier details when those MCP tools are available. Remaining work: -- Add writer tooling for anti-patterns only if the read path proves useful - enough to need live updates. +- Convert advisor outputs to optional structured JSON blocks so `jcode` can + enforce repair actions without parsing prose. ## Open Questions - Should advisor outputs be pure text, pure JSON, or text plus a JSON block? -- Should `jmcp` own a data-backed anti-pattern registry from the start? - Should jcode call the failure advisor automatically, or only inject the request advisor up front? - How much cookbook code is safe to include by default for very small context --- a/mcp/server.ss +++ b/mcp/server.ss @@ -45,6 +45,7 @@ "jerboa_verify_plan" "jerboa_failure_advisor" "jerboa_anti_pattern_lookup" + "jerboa_anti_pattern_add" "jerboa_task_workflow_advisor" "jerboa_compact_tool_manifest" "jerboa_script_scaffold_verify" @@ -1069,6 +1070,7 @@ ("verify_plan" "verify command plan oracle jcode verified write scope") ("failure_advisor" "failure advisor repair error verify output compiler diagnosis next action") ("anti_pattern_lookup" "anti pattern lookup local model mistakes avoid guidance failure") + ("anti_pattern_add" "add anti pattern antipattern local model mistake avoid guidance failure writer") ("task_workflow_advisor" "workflow plan advisor local model agent mcp next tools") ("compact_tool_manifest" "manifest guide local model small context tool order") ("script_scaffold_verify" "script command scaffold cli args verify write file"))) @@ -5381,6 +5383,41 @@ (bullet-lines (fallback-anti-patterns kind)) (bullet-lines (map anti-pattern-line matches)))))))) +(def (missing-required-string? value) + (or (not (string? value)) + (= (string-length (string-trim value)) 0))) + +(def (tool-anti-pattern-add args) + (let* ([path (hash-get* args "anti_patterns_path" (data-path "anti-patterns.sexp"))] + [id (hash-get* args "id" #f)] + [title (hash-get* args "title" #f)] + [avoid (hash-get* args "avoid" #f)] + [advice (hash-get* args "advice" #f)] + [kinds (hash-get* args "kinds" (list "all"))] + [severity (hash-get* args "severity" "medium")] + [tags (hash-get* args "tags" '())] + [pattern (hash-get* args "pattern" "")] + [tools (hash-get* args "tools" '())] + [entries (load-anti-patterns path)]) + (if (or (missing-required-string? id) + (missing-required-string? title) + (missing-required-string? avoid) + (missing-required-string? advice)) + (text-result "id, title, avoid, and advice are required." #t) + (let ([entry (jhash "id" id + "title" title + "kinds" kinds + "severity" severity + "tags" tags + "pattern" pattern + "avoid" avoid + "advice" advice + "tools" tools)]) + (let ([updated (upsert-by-id entries entry)]) + (write-json-file path updated) + (text-result + (string-append "Added/updated anti-pattern \"" id "\" in " path "."))))))) + (def (tool-verify-plan args) (let* ([task-raw (task-text-raw args)] [task (string-downcase task-raw)] @@ -5577,6 +5614,7 @@ "jerboa_verify_plan" "jerboa_failure_advisor" "jerboa_anti_pattern_lookup" + "jerboa_anti_pattern_add" "jerboa_task_workflow_advisor" "jerboa_compact_tool_manifest" "jerboa_cookbook_task_bundle" @@ -5607,6 +5645,7 @@ [(string=? name "jerboa_verify_plan") "recommend verifier command and write scope"] [(string=? name "jerboa_failure_advisor") "diagnose verifier failure and next repair move"] [(string=? name "jerboa_anti_pattern_lookup") "task-specific mistakes to avoid"] + [(string=? name "jerboa_anti_pattern_add") "save reusable local-model mistakes"] [(string=? name "jerboa_task_workflow_advisor") "first call for a task-specific plan"] [(string=? name "jerboa_compact_tool_manifest") "small-context catalog and call order"] [(string=? name "jerboa_cookbook_task_bundle") "package relevant cookbook recipes for any task"] @@ -7790,6 +7829,21 @@ '()) tool-anti-pattern-lookup #t '("anti_pattern_lookup" "anti_patterns" "antipatterns")) + (register-tool "jerboa_anti_pattern_add" "Anti-Pattern Add" + "Add or update a reusable local-model anti-pattern." + (schema (list (list "anti_patterns_path" (property "string" "Anti-pattern data path")) + (list "id" (property "string" "Anti-pattern id")) + (list "title" (property "string" "Title")) + (list "kinds" (property "array" "Applicable task kinds")) + (list "severity" (property "string" "Severity")) + (list "tags" (property "array" "Tags")) + (list "pattern" (property "string" "Search or regex pattern")) + (list "avoid" (property "string" "Mistake to avoid")) + (list "advice" (property "string" "Repair guidance")) + (list "tools" (property "array" "Recommended follow-up tools"))) + '("id" "title" "avoid" "advice")) + tool-anti-pattern-add #t + '("anti_pattern_add" "anti_patterns_add" "antipattern_add")) (register-tool "jerboa_security_scan" "Security Scanner" "Static security scanner for Jerboa, C, and Rust code." (schema (list (list "file_path" (property "string" "Single file")) @@ -8177,7 +8231,7 @@ (list "search_all" (property "boolean" "Return all matches")) (list "error_fixes_path" (property "string" "Error fixes path"))) '("error_message")) - tool-error-fix-lookup #f '()) + tool-error-fix-lookup #t '()) (register-tool "jerboa_error_fix_add" "Error Fix Add" "Add or update an error-fix mapping." (schema (list (list "error_fixes_path" (property "string" "Error fixes path")) --- a/mcp/test/protocol-test.ss +++ b/mcp/test/protocol-test.ss @@ -101,6 +101,7 @@ (def cookbook-file (path-join "/tmp" "jerboa-mcp2-cookbook.sexp")) (def features-file (path-join "/tmp" "jerboa-mcp2-features.sexp")) (def errors-file (path-join "/tmp" "jerboa-mcp2-errors.sexp")) +(def anti-patterns-file (path-join "/tmp" "jerboa-mcp2-anti-patterns.sexp")) (def doc-file (path-join "/tmp" "jerboa-mcp2-doc.md")) (def security-rules-file (path-join "/tmp" "jerboa-mcp2-rules.sexp")) (def ffi-file (path-join "/tmp" "jerboa-mcp2-ffi.ss")) @@ -117,6 +118,7 @@ (def auto-src-dir (path-join auto-project "src" "tmp" "auto")) (def auto-lib-file (path-join auto-src-dir "lib.sls")) (def auto-use-file (path-join auto-src-dir "use.ss")) +(guard (e [else #f]) (delete-file anti-patterns-file)) (write-file-string doc-file "Done mcp/server.ss\n") (write-file-string ffi-file "(load-shared-object \"libx\")\n(def c-read (foreign-procedure \"read\" () int))\n(set! x 1)\n(system \"ls\")\n#\\escape\n") (write-file-string variadic-file "(import (jerboa prelude))\n(def c-fcntl (foreign-procedure \"fcntl\" (int int int) int))\n") @@ -622,9 +624,27 @@ (alist->hash-table (list (cons "task" "write a command line script in Jerboa") (cons "file_path" "tool.ss") + (cons "max_results" 3)))) + (call-tool 124 "jerboa_anti_pattern_add" + (alist->hash-table + (list (cons "anti_patterns_path" anti-patterns-file) + (cons "id" "tmp-anti-pattern") + (cons "title" "Temporary anti-pattern") + (cons "kinds" (list "debug-error")) + (cons "severity" "medium") + (cons "tags" (list "tmp" "bounds")) + (cons "pattern" "tmp-vector-error") + (cons "avoid" "Do not repeat the temporary failing edit.") + (cons "advice" "tmp anti-pattern advice") + (cons "tools" (list "jerboa_failure_advisor"))))) + (call-tool 125 "jerboa_anti_pattern_lookup" + (alist->hash-table + (list (cons "anti_patterns_path" anti-patterns-file) + (cons "task" "tmp-vector-error while debugging") + (cons "kind" "debug-error") (cons "max_results" 3))))))) -(check "expected 123 responses" (= (length responses) 123)) +(check "expected 125 responses" (= (length responses) 125)) (def init-result (result (car responses))) (check "initialize server name" @@ -646,6 +666,7 @@ (check "tools/list includes verify plan" (has-tool? tools "jerboa_verify_plan")) (check "tools/list includes failure advisor" (has-tool? tools "jerboa_failure_advisor")) (check "tools/list includes anti-pattern lookup" (has-tool? tools "jerboa_anti_pattern_lookup")) +(check "tools/list includes anti-pattern add" (has-tool? tools "jerboa_anti_pattern_add")) (check "eval returns 3" (string-contains (content-text (result (list-ref responses 2))) "3")) @@ -1031,5 +1052,12 @@ (and (string-contains (content-text (result (list-ref responses 122))) "Jerboa anti-pattern lookup") (string-contains (content-text (result (list-ref responses 122))) "script-written-as-library"))) +(check "anti-pattern add writes temp mapping" + (string-contains (content-text (result (list-ref responses 123))) "tmp-anti-pattern")) + +(check "anti-pattern lookup finds temp mapping" + (and (string-contains (content-text (result (list-ref responses 124))) "tmp-anti-pattern") + (string-contains (content-text (result (list-ref responses 124))) "tmp anti-pattern advice"))) + (display "protocol-test: PASS") (newline)