updates for jerbuild
ober
932603ad5267af5074fd59b05423838046deab68
new file mode 100644 --- /dev/null +++ b/.jerbuild @@ -0,0 +1,7 @@ +;; Build a standalone jerbuild binary from this checkout. + +(entry "jerbuild.ss") +(output "jerbuild-local") +(requires "cc") +(notes "jerbuild itself is pure Scheme. jerboa-native-rs and DuckDB are not required.") +(libdirs "lib") --- a/data/cookbooks.sexp +++ b/data/cookbooks.sexp @@ -4484,67 +4484,77 @@ "Native jerbuild binary using TLS/wasm needs libjerboa_native at runtime")) (("code" . - ";; Compact MCP discovery keeps local-model prompt context small.\n;; In mini mode only the jerboa dispatcher is advertised:\n;; JERBOA_MCP_MODE=mini jmcp\n\n;; Discover likely tools by keyword. Results include stable short ids like t:abc123.\n;; JSON-RPC args shape:\n;; {\"name\":\"jerboa\",\"arguments\":{\"tool\":\"search\",\"args\":{\"query\":\"benchmark\",\"max_results\":5}}}\n\n;; Fetch the compact catalog when a client wants all ids/names without schemas:\n;; {\"name\":\"jerboa\",\"arguments\":{\"tool\":\"catalog\",\"args\":{\"compact\":true}}}\n\n;; Describe only the selected tool when needed:\n;; {\"name\":\"jerboa\",\"arguments\":{\"tool\":\"describe\",\"args\":{\"name\":\"benchmark\"}}}\n;; {\"name\":\"jerboa\",\"arguments\":{\"tool\":\"describe\",\"args\":{\"id\":\"t:abc123\"}}}\n\n;; Invoke by canonical name, alias, or t: id:\n;; {\"name\":\"jerboa\",\"arguments\":{\"tool\":\"t:abc123\",\"args\":{\"expression\":\"(+ 1 2)\"}}}") - ("id" . "mcp-compact-tool-discovery") - ("imports") + ";; Compact MCP discovery keeps local-model prompt context small.\n;; In mini mode only the jerboa dispatcher is advertised:\n;; JERBOA_MCP_MODE=mini jmcp\n\n;; Discover likely tools by keyword. Results include stable short ids like t:abc123.\n;; JSON-RPC args shape:\n;; {\"name\":\"jerboa\",\"arguments\":{\"tool\":\"search\",\"args\":{\"query\":\"benchmark\",\"max_results\":5}}}\n\n;; Fetch the compact catalog when a client wants all ids/names without schemas:\n;; {\"name\":\"jerboa\",\"arguments\":{\"tool\":\"catalog\",\"args\":{\"compact\":true}}}\n\n;; Describe only the selected tool when needed:\n;; {\"name\":\"jerboa\",\"arguments\":{\"tool\":\"describe\",\"args\":{\"name\":\"benchmark\"}}}\n;; {\"name\":\"jerboa\",\"arguments\":{\"tool\":\"describe\",\"args\":{\"id\":\"t:abc123\"}}}\n\n;; Invoke by canonical name, alias, or t: id:\n;; {\"name\":\"jerboa\",\"arguments\":{\"tool\":\"t:abc123\",\"args\":{\"expression\":\"(+ 1 2)\"}}}") ("id" . "mcp-compact-tool-discovery") ("imports") ("notes" . "Use JERBOA_MCP_MODE=mini for constrained local LLMs that cannot afford every tool schema in prompt/k-v cache. The dispatcher supports search, catalog, and describe; ids are stable hashes derived from canonical tool names, and describe returns the full argument schema only for the selected tool.") ("tags" "mcp" "jmcp" "dispatcher" "catalog" "local-llm" "tool-discovery") - ("title" . "Compact jmcp tool discovery with search/catalog/describe")) + ("title" + . + "Compact jmcp tool discovery with search/catalog/describe")) (("code" . - ";; Project-aware verification: compile/check a file that imports sibling modules.\n;; The MCP tool derives libdirs from:\n;; 1. explicit extra_libdirs\n;; 2. Makefile LIBDIRS in project_path\n;; 3. project_path/lib\n;; 4. the source file's containing lib root\n\n;; Example MCP args for compile_check:\n;; {\"file_path\":\"src/app/main.ss\",\"project_path\":\"/path/to/project\"}\n\n;; Example with explicit libdirs:\n;; {\"file_path\":\"src/app/main.ss\",\n;; \"extra_libdirs\":[\"src\",\"/Users/user/mine/jerboa/lib\"]}\n\n;; Bundle the normal pre-commit sequence for one changed file:\n;; {\"name\":\"jerboa_verify_changes\",\n;; \"arguments\":{\"file_path\":\"src/app/main.ss\",\n;; \"project_path\":\"/path/to/project\",\n;; \"run_tests\":true}}") - ("id" . "mcp-project-libdirs-verify-changes") - ("imports") + ";; Project-aware verification: compile/check a file that imports sibling modules.\n;; The MCP tool derives libdirs from:\n;; 1. explicit extra_libdirs\n;; 2. Makefile LIBDIRS in project_path\n;; 3. project_path/lib\n;; 4. the source file's containing lib root\n\n;; Example MCP args for compile_check:\n;; {\"file_path\":\"src/app/main.ss\",\"project_path\":\"/path/to/project\"}\n\n;; Example with explicit libdirs:\n;; {\"file_path\":\"src/app/main.ss\",\n;; \"extra_libdirs\":[\"src\",\"/Users/user/mine/jerboa/lib\"]}\n\n;; Bundle the normal pre-commit sequence for one changed file:\n;; {\"name\":\"jerboa_verify_changes\",\n;; \"arguments\":{\"file_path\":\"src/app/main.ss\",\n;; \"project_path\":\"/path/to/project\",\n;; \"run_tests\":true}}") ("id" . "mcp-project-libdirs-verify-changes") ("imports") ("notes" . "jerboa_compile_check, jerboa_verify, and jerboa_run_tests now accept project_path and/or extra_libdirs. jerboa_verify_changes composes compile_check, security_scan, and optional run_tests into one compact report, which reduces missed verification steps and avoids repeated library-resolution failures in multi-module projects.") ("tags" "mcp" "verify" "compile_check" "run_tests" "libdirs" "project") - ("title" . "Project-aware MCP verification with derived libdirs")) + ("title" + . + "Project-aware MCP verification with derived libdirs")) (("code" . - ";; Regenerate the API map consumed by jmcp and jlsp:\n;; make api-signatures\n\n;; Then regenerate LSP completion tables from the refreshed map:\n;; make lsp-gen\n\n;; The generator scans library-form .sls/.ss files under lib/ and lsp/,\n;; prefers generated .sls exports when present, uses .ss for source-only\n;; modules, writes data/api-signatures.sexp, and reports parse errors.\n;; A clean run should end with parse_errors = 0.") - ("id" . "regenerate-mcp-lsp-api-signatures") - ("imports") + ";; Regenerate the API map consumed by jmcp and jlsp:\n;; make api-signatures\n\n;; Then regenerate LSP completion tables from the refreshed map:\n;; make lsp-gen\n\n;; The generator scans library-form .sls/.ss files under lib/ and lsp/,\n;; prefers generated .sls exports when present, uses .ss for source-only\n;; modules, writes data/api-signatures.sexp, and reports parse errors.\n;; A clean run should end with parse_errors = 0.") ("id" . "regenerate-mcp-lsp-api-signatures") ("imports") ("notes" . "data/api-signatures.sexp is the shared API catalog used by MCP symbol lookup and by lsp/gen-completion-tables.ss. Refresh it after adding stdlib, jerboa, or lsp library modules, then run make lsp-gen so lsp/analysis/completion-tables.ss follows the same source of truth.") - ("tags" "api-signatures" "lsp" "mcp" "completion" - "data") + ("tags" "api-signatures" "lsp" "mcp" "completion" "data") ("title" . "Regenerate MCP/LSP API signatures from source")) (("code" . - ";; Check whether a module exists without importing it.\n;; Accepts canonical, slash, and colon module path forms:\n;; {\"name\":\"jerboa_module_exists\",\n;; \"arguments\":{\"module_path\":\"(std sort)\"}}\n;; {\"name\":\"jerboa_module_exists\",\n;; \"arguments\":{\"module_path\":\":std/text/json\"}}\n\n;; For project-local modules, include local libdirs:\n;; {\"name\":\"jerboa_module_exists\",\n;; \"arguments\":{\"module_path\":\"(app core)\",\n;; \"project_path\":\"/path/to/project\"}}\n\n;; The response is compact text with exists: true/false and the first matching .ss/.sls/.so path.") - ("id" . "mcp-module-exists") - ("imports") + ";; Check whether a module exists without importing it.\n;; Accepts canonical, slash, and colon module path forms:\n;; {\"name\":\"jerboa_module_exists\",\n;; \"arguments\":{\"module_path\":\"(std sort)\"}}\n;; {\"name\":\"jerboa_module_exists\",\n;; \"arguments\":{\"module_path\":\":std/text/json\"}}\n\n;; For project-local modules, include local libdirs:\n;; {\"name\":\"jerboa_module_exists\",\n;; \"arguments\":{\"module_path\":\"(app core)\",\n;; \"project_path\":\"/path/to/project\"}}\n\n;; The response is compact text with exists: true/false and the first matching .ss/.sls/.so path.") ("id" . "mcp-module-exists") ("imports") ("notes" . "Use jerboa_module_exists before jerboa_module_exports when you only need a clean boolean. It resolves candidate files in extra_libdirs, project_path-derived libdirs, and JERBOA_HOME/lib without running an import, so absent modules do not produce noisy reader/import errors.") ("tags" "mcp" "module" "exists" "stdlib" "libdirs" "discovery") - ("title" . "Check whether a Jerboa module exists without importing it")) + ("title" + . + "Check whether a Jerboa module exists without importing it")) (("code" . - ";; Report only findings on changed lines by passing explicit lines:\n;; {\"name\":\"jerboa_security_scan\",\n;; \"arguments\":{\"file_path\":\"src/ffi.ss\",\n;; \"changed_lines\":[42,\"50-55\"]}}\n\n;; Or pass file-qualified line specs for multi-file scans:\n;; {\"name\":\"jerboa_security_scan\",\n;; \"arguments\":{\"project_path\":\"/path/to/project\",\n;; \"changed_lines\":[{\"file\":\"src/ffi.ss\",\"start\":42,\"end\":55}]}}\n\n;; Or derive changed lines from a unified diff/base ref:\n;; {\"name\":\"jerboa_security_scan\",\n;; \"arguments\":{\"project_path\":\"/path/to/project\",\n;; \"base_ref\":\"main\",\n;; \"changed_only\":true}}") - ("id" . "mcp-security-scan-changed-lines") - ("imports") + ";; Report only findings on changed lines by passing explicit lines:\n;; {\"name\":\"jerboa_security_scan\",\n;; \"arguments\":{\"file_path\":\"src/ffi.ss\",\n;; \"changed_lines\":[42,\"50-55\"]}}\n\n;; Or pass file-qualified line specs for multi-file scans:\n;; {\"name\":\"jerboa_security_scan\",\n;; \"arguments\":{\"project_path\":\"/path/to/project\",\n;; \"changed_lines\":[{\"file\":\"src/ffi.ss\",\"start\":42,\"end\":55}]}}\n\n;; Or derive changed lines from a unified diff/base ref:\n;; {\"name\":\"jerboa_security_scan\",\n;; \"arguments\":{\"project_path\":\"/path/to/project\",\n;; \"base_ref\":\"main\",\n;; \"changed_only\":true}}") ("id" . "mcp-security-scan-changed-lines") ("imports") ("notes" . "The changed-line mode still scans with the normal rules, then filters findings by line number and reports total/outside-filter counts. Use this when a large legacy file has historical findings and you only need to know whether the current patch introduced a new issue.") ("tags" "mcp" "security-scan" "diff" "changed-lines" "baseline" "review") - ("title" . "Limit MCP security scan output to changed lines")) + ("title" + . + "Limit MCP security scan output to changed lines")) (("code" . - ";; #!chezscheme library files are verified with the Chez reader path:\n;; {\"name\":\"jerboa_verify\",\n;; \"arguments\":{\"file_path\":\"lib/std/ergo.ss\"}}\n\n;; Lint fixed-arity FFI declarations for known variadic libc symbols:\n;; {\"name\":\"jerboa_variadic_ffi_check\",\n;; \"arguments\":{\"file_path\":\"lib/std/net/io.ss\"}}\n\n;; A finding includes file, line, symbol, declared arity, and the fixed-prefix threshold.") - ("id" . "mcp-chez-reader-and-variadic-ffi-checks") + ";; #!chezscheme library files are verified with the Chez reader path:\n;; {\"name\":\"jerboa_verify\",\n;; \"arguments\":{\"file_path\":\"lib/std/ergo.ss\"}}\n\n;; Lint fixed-arity FFI declarations for known variadic libc symbols:\n;; {\"name\":\"jerboa_variadic_ffi_check\",\n;; \"arguments\":{\"file_path\":\"lib/std/net/io.ss\"}}\n\n;; A finding includes file, line, symbol, declared arity, and the fixed-prefix threshold.") ("id" . "mcp-chez-reader-and-variadic-ffi-checks") ("imports") ("notes" . "jerboa_verify and jerboa_compile_check auto-route .sls and #!chezscheme sources through Chez datum reading, which avoids false failures on library-form .ss files with colon-prefixed Chez identifiers. jerboa_variadic_ffi_check complements security_scan by reporting known variadic libc foreign-procedure declarations with arity details and C-shim guidance.") ("tags" "mcp" "verify" "chez-reader" "ffi" "variadic" "foreign-procedure") - ("title" . "Verify Chez-reader libraries and lint variadic FFI declarations"))) + ("title" + . + "Verify Chez-reader libraries and lint variadic FFI declarations")) + (("code" + . + ";; .jerbuild\n(entry \"main.ss\")\n(output \"my-tool\")\n(requires \"cargo\" \"cc\")\n(notes \"Build only the native Rust surface this project imports.\")\n(libdirs \".\")\n(rust-crates\n (\"vendor/jerboa-native-rs/Cargo.toml\"\n features: \"tls,crypto\"\n no-default-features: #t))") ("id" . "jerbuild-rust-crate-no-default-features") + ("imports") + ("notes" + . + "Use structured rust-crates entries when a project must avoid Cargo defaults. The legacy string form \"path/Cargo.toml:features\" still builds with default features; the list form can set no-default-features: #t or default-features: #f. This is useful for preventing unused native surfaces such as DuckDB from being linked into tools that only need TLS, crypto, SQLite, or wasm.") + ("tags" "jerbuild" "rust-crates" "cargo" + "no-default-features" "static-binary") + ("title" + . + "Build a Rust crate from .jerbuild without default Cargo features"))) --- a/data/features.sexp +++ b/data/features.sexp @@ -8,11 +8,12 @@ . "Building jemacs-qt static: (chez-qt ffi) calls load-shared-object at top level. Without JEMACS_STATIC=1 the compilation step fails with a dlopen error inside the container. Finding this required reading vendor/chez-qt/chez-qt/ffi.ss manually. A tool scan would have flagged it immediately.") ("id" . "static-build-lso-guard-audit") - ("impact" . "medium") - ("status" . "partial") + ("impact" . "medium") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "jerboa_static_lso_guard_audit") - ("implemented_in" . "mcp/server.ss") - ("note" . "Implemented as a conservative static scanner for load-shared-object / FFI guard patterns; deeper project-specific static-link policy remains future work.") + ("note" + . + "Implemented as a conservative static scanner for load-shared-object / FFI guard patterns; deeper project-specific static-link policy remains future work.") + ("status" . "partial") ("tags" "static" "build" "load-shared-object" "ffi" "audit" "musl") ("title" @@ -22,9 +23,12 @@ . "When preparing a Jerboa project for static binary compilation (musl or otherwise), before spending 30+ minutes on a Docker build that fails at runtime.") ("votes" . 1)) - (("description" + (("closed_reason" . - "Scan a .md file (or directory) for fenced ```scheme / ```jerboa code blocks, extract each one, run it through the compiler, and optionally evaluate it. Report per-block status: compiled/failed/ran/threw, with line number of the fence and the exact error. Support an `:imports` metadata line before the fence (or a doc-level header) so the tool knows which modules each block expects. For blocks that are partial (no imports, no top-level wrapping), auto-wrap in `(import (jerboa prelude)) ...`. Intended for keeping design docs, cookbook sources, and README examples in lockstep with reality.") + "MCP tool verifies fenced Scheme/Jerboa code blocks with optional execution and import defaults.") + ("description" + . + "Scan a .md file (or directory) for fenced ```scheme / ```jerboa code blocks, extract each one, run it through the compiler, and optionally evaluate it. Report per-block status: compiled/failed/ran/threw, with line number of the fence and the exact error. Support an `:imports` metadata line before the fence (or a doc-level header) so the tool knows which modules each block expects. For blocks that are partial (no imports, no top-level wrapping), auto-wrap in `(import (jerboa prelude)) ...`. Intended for keeping design docs, cookbook sources, and README examples in lockstep with reality.") ("estimated_token_reduction" . "~1500 tokens per doc-ship session (replaces: write tmp file → read → run → parse error → re-write → re-run → parse → fix → re-run cycle). Multiplicative across every code block in a large doc.") @@ -32,10 +36,9 @@ . "docs/core-async.md Section 17 contains a 20-line worked example that ports Clojure's scatter/gather pattern to Jerboa. Two lines use `@out` (Clojure atom-deref reader sugar that doesn't exist in Jerboa) and one uses `(swap! out cons v)` instead of `(swap! out (cut cons v <>))`. A `jerboa_doc_verify docs/core-async.md` call would have compiled the block, noted \"variable @out is not bound\" at the fence's line, and caught both errors in one shot. Instead I wrote a /tmp/test-async-example.ss file, ran it by hand, saw the first error, fixed it, re-ran, saw the second, fixed it, re-ran again. Three iterations that one tool call could collapse into one. Same friction applies to every cookbook recipe on disk and every README code snippet.") ("id" . "doc-code-block-verify") ("impact" . "high") - ("status" . "implemented") - ("implemented_tool" . "jerboa_doc_verify") ("implemented_in" . "mcp/server.ss") - ("closed_reason" . "MCP tool verifies fenced Scheme/Jerboa code blocks with optional execution and import defaults.") + ("implemented_tool" . "jerboa_doc_verify") + ("status" . "implemented") ("tags" "doc" "markdown" "verify" "code-block" "drift" "example") ("title" @@ -53,10 +56,10 @@ "Claude writes (import (jerboa prelude)) then (sqlite-open \"db\") — this silently calls safe-sqlite-open which validates types, and (with-resource ...) is the documented pattern. No separate (import (std safe)) needed.") ("id" . "jerboa-safe-prelude-default") ("impact" . "critical") - ("status" . "core-backlog") ("note" . "Requires Jerboa core change (prelude.sls), not implementable in jerboa-mcp.") + ("status" . "core-backlog") ("tags" "safety" "prelude" "default" "claude" "api" "import") ("title" . "Make safe APIs the default prelude exports") @@ -116,12 +119,12 @@ . "Query: \"Handle JSON errors safely.\" Returns: json-parse with try/catch examples from cookbook.") ("id" . "jerboa-semantic-search-stdlib") ("impact" . "high") - ("status" . "partial") - ("implemented_tool" . "jerboa_semantic_search") ("implemented_in" . "mcp/server.ss") + ("implemented_tool" . "jerboa_semantic_search") ("note" . "Keyword-based version implemented as jerboa_stdlib_search. True semantic/embeddings search requires a vector store or embedding API.") + ("status" . "partial") ("tags" "search" "semantic" "ai" "discovery") ("title" . @@ -140,9 +143,9 @@ . "jerboa-code TUI spawned a worker thread that called (hash-put! tool-counts name ...) and mutated msg-block lists via a parameterized callback. The main event-loop thread read these same structures to render each frame. Under load the TUI would hang randomly, losing ~1 in 3 events. Took Gemini 3.1 Pro pseudocode analysis to diagnose. A linter that recognized \"callback defined inside parameterize inside spawn\" + \"mutates value reachable from outer scope\" would have flagged this immediately.") ("id" . "detect-worker-shared-mutation") ("impact" . "high") - ("status" . "partial") - ("implemented_tool" . "jerboa_worker_mutation_check") ("implemented_in" . "mcp/server.ss") + ("implemented_tool" . "jerboa_worker_mutation_check") + ("status" . "partial") ("tags" "thread-safety" "race" "spawn" "hash-put" "concurrency" "lint") ("title" @@ -152,9 +155,12 @@ . "Any Jerboa project using (spawn ...) with worker threads that drive a UI or long-running loop. The bug is invisible in development (messages appear to work) but manifests as silent data loss or hangs under load. A static pass that flags this pattern at lint time would save hours of debugging per occurrence.") ("votes" . 0)) - (("description" + (("closed_reason" . - "A tool that takes an array of symbol names (function/macro/variable names) and returns, for each, whether it exists anywhere in `(jerboa prelude)` + all `(std ...)` modules, with its defining module, kind (proc/macro/syntax/var), and arity if known. Equivalent to running `jerboa_apropos` (or the existing function_signature tool) N times in parallel, but in a single call. Results would look like `[{name: \"merge-with\", exists: false}, {name: \"select-keys\", exists: true, module: \"(std clojure)\", kind: \"proc\", arity: \"2\"}, ...]`. Should default to an exact-name lookup, with an optional `fuzzy` flag to also include close matches.") + "Batch lookup now checks data/api-signatures.sexp and can fall back to selected runtime modules with optional fuzzy suggestions.") + ("description" + . + "A tool that takes an array of symbol names (function/macro/variable names) and returns, for each, whether it exists anywhere in `(jerboa prelude)` + all `(std ...)` modules, with its defining module, kind (proc/macro/syntax/var), and arity if known. Equivalent to running `jerboa_apropos` (or the existing function_signature tool) N times in parallel, but in a single call. Results would look like `[{name: \"merge-with\", exists: false}, {name: \"select-keys\", exists: true, module: \"(std clojure)\", kind: \"proc\", arity: \"2\"}, ...]`. Should default to an exact-name lookup, with an optional `fuzzy` flag to also include close matches.") ("estimated_token_reduction" . "~600 tokens per doc-verification pass (replaces 4-6 grep calls + result inspection with 1 structured tool call). Higher for porting workflows that cross-check dozens of names.") @@ -162,11 +168,9 @@ . "While updating `docs/clojure-vs-jerboa.md` to add status markers, I needed to verify whether Clojure names like `merge-with`, `zipmap`, `reduce-kv`, `memoize`, `iterate`, `repeatedly`, `fnil`, `every-pred`, `some-fn`, `trampoline`, `min-key`, `max-key`, `min-by`, `max-by`, `iterate-n` exist in Jerboa. I ran 5 separate `Grep` calls across `lib/` to check — a total of ~15 symbol lookups. A single `jerboa_symbol_exists_batch` call with the list would have returned `{select-keys: exists, merge: exists, everything-else: absent}` in one shot. Also discovered that the existing doc had several stale claims about non-existent functions (iterate-n, min-by, max-by, fnil-in-prelude, every-pred-in-prelude) because no one had cheap way to sanity-check a batch of names.") ("id" . "batch-symbol-existence-check") - ("impact" . "medium") - ("status" . "implemented") + ("impact" . "medium") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "jerboa_symbol_exists_batch") - ("implemented_in" . "mcp/server.ss") - ("closed_reason" . "Batch lookup now checks data/api-signatures.sexp and can fall back to selected runtime modules with optional fuzzy suggestions.") + ("status" . "implemented") ("tags" "apropos" "symbol" "lookup" "batch" "existence") ("title" . @@ -185,9 +189,9 @@ . "jerboa-edge Phase 2: eval-user-filter was initially implemented using run-safe-eval inside the transducer (which runs during fiber >!! calls). Filters appeared to register but never dropped events. No error logged anywhere. Root cause only found after ~15 tool calls investigating the filter logic, format strings, and alist key types — all red herrings. Switching to restricted-eval-string fixed it immediately. A static flag at authoring time would have prevented the entire debugging session.") ("id" . "detect-fork-in-fiber-context") ("impact" . "high") - ("status" . "partial") - ("implemented_tool" . "jerboa_fork_in_fiber_check") ("implemented_in" . "mcp/server.ss") + ("implemented_tool" . "jerboa_fork_in_fiber_check") + ("status" . "partial") ("tags" "fork" "fiber" "run-safe-eval" "scheduler" "static-analysis") ("title" @@ -236,9 +240,12 @@ . "When writing code that uses a stdlib module for the first time, especially modules with variadic signatures or unusual argument ordering (registry-first, config-last, etc.).") ("votes" . 1)) - (("description" + (("closed_reason" . - "When a .ss file starts with a shebang line (#!/usr/bin/env ... or #!/usr/bin/env -S scheme ...), jerboa_verify reports \"invalid syntax scheme\" and jerboa_compile_check (with force_chez_reader) reports \"invalid syntax #!/usr/bin/env at char 0\". The tools should strip the shebang before analysis, the same way the Chez scheme interpreter does when running --script. Script files (.ss with shebang) are a common deployment pattern for single-file services — all jerboa-edge phases use this form.") + "verify/compile_check strip or tolerate shebang lines before handing source to the reader/compiler.") + ("description" + . + "When a .ss file starts with a shebang line (#!/usr/bin/env ... or #!/usr/bin/env -S scheme ...), jerboa_verify reports \"invalid syntax scheme\" and jerboa_compile_check (with force_chez_reader) reports \"invalid syntax #!/usr/bin/env at char 0\". The tools should strip the shebang before analysis, the same way the Chez scheme interpreter does when running --script. Script files (.ss with shebang) are a common deployment pattern for single-file services — all jerboa-edge phases use this form.") ("estimated_token_reduction" . "~300 tokens per verify session on script files (eliminates fallback to make check + bash startup test)") @@ -246,11 +253,9 @@ . "jerboa-edge/edge.ss starts with #!/usr/bin/env -S scheme --libdirs lib --script. Running jerboa_verify on it returns \"Exception: invalid syntax scheme\" — a false positive. The only way to confirm the file is valid is to run make check (which only checks that the scheme binary runs, not the file itself) or start the server via bash. This costs 2-3 extra tool calls per edit cycle.") ("id" . "verify-shebang-script-support") - ("impact" . "medium") - ("status" . "implemented") + ("impact" . "medium") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "jerboa_verify") - ("implemented_in" . "mcp/server.ss") - ("closed_reason" . "verify/compile_check strip or tolerate shebang lines before handing source to the reader/compiler.") + ("status" . "implemented") ("tags" "verify" "shebang" "script" "compile-check" "edge") ("title" . @@ -259,9 +264,12 @@ . "Verifying a single-file Jerboa service that uses a shebang for direct execution (chmod +x edge.ss && ./edge.ss). Currently jerboa_verify always fails on these files, forcing fallback to make check + bash.") ("votes" . 0)) - (("description" + (("closed_reason" . - "A lightweight tool that checks whether a module path like (std crdt) or (std actor transport) exists in the Jerboa stdlib, without loading or importing it. Returns exists: true/false plus the .sls file path if found. Currently the only way to check is jerboa_module_exports (which errors loudly if absent) or grepping lib/. The tool should accept both (std ...) and :std/... path forms.") + "Added jerboa_module_exists with direct libdir path resolution for (std ...), :std/..., project_path, extra_libdirs, and jerboa_home without importing the module.") + ("description" + . + "A lightweight tool that checks whether a module path like (std crdt) or (std actor transport) exists in the Jerboa stdlib, without loading or importing it. Returns exists: true/false plus the .sls file path if found. Currently the only way to check is jerboa_module_exports (which errors loudly if absent) or grepping lib/. The tool should accept both (std ...) and :std/... path forms.") ("estimated_token_reduction" . "~150 tokens per check (converts loud error from module_exports into clean boolean; batchable for multiple modules)") @@ -269,12 +277,9 @@ . "Phase 4 of jerboa-edge planned to use (std crdt) for replicated state. Confirmed non-existence required attempting jerboa_module_exports which returned an error message — one extra tool call plus a confusing error. A jerboa_module_exists (std crdt) returning {exists: false} in 0 tokens of error noise would be cleaner and could be batched with other checks.") ("id" . "module-exists-check") ("impact" . "low") - ("status" . "implemented") - ("implemented_tool" . "jerboa_module_exists") ("implemented_in" . "mcp/server.ss") - ("closed_reason" - . - "Added jerboa_module_exists with direct libdir path resolution for (std ...), :std/..., project_path, extra_libdirs, and jerboa_home without importing the module.") + ("implemented_tool" . "jerboa_module_exists") + ("status" . "implemented") ("tags" "module" "exists" "stdlib" "check" "discovery") ("title" . @@ -293,11 +298,12 @@ . "Building jerboa-edge static binary: the C main had `jerboa_sha`, `jerboa_hmac_sha`, `jerboa_argon`, `jerboa_pbkdf`, `jerboa_chacha` as registered names. The actual archive had `jerboa_sha256`, `jerboa_hmac_sha256`, `jerboa_argon2id_hash`, etc. The linker errored on all of them as undefined references (15+ symbols). Additionally `getsockname` and `getpeername` were used by (std net io) but not registered, causing runtime \"no entry\" crashes. Finding each missing symbol required: (1) Docker build → linker error or runtime crash → (2) grep stdlib source for foreign-procedure uses → (3) nm archive to verify actual name → (4) edit C main → repeat. A coverage check tool would have reported all gaps in one shot before the first build attempt.") ("id" . "static-binary-sforeign-symbol-coverage-check") - ("impact" . "high") - ("status" . "partial") + ("impact" . "high") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "jerboa_static_symbol_audit") - ("implemented_in" . "mcp/server.ss") - ("note" . "Implemented as static FFI registration/reference coverage audit; archive nm cross-checking remains future work.") + ("note" + . + "Implemented as static FFI registration/reference coverage audit; archive nm cross-checking remains future work.") + ("status" . "partial") ("tags" "static-binary" "Sforeign_symbol" "foreign-procedure" "FFI" "coverage" "musl") ("title" @@ -307,9 +313,12 @@ . "When writing or updating the C main for a static Jerboa binary, before spending 30 minutes on a Docker build that then crashes at runtime with \"no entry for getsockname\".") ("votes" . 0)) - (("description" + (("closed_reason" . - "Scan a markdown design document for implementation status claims (tables with \"Done\"/\"Complete\" markers, file path references like `src/foo/bar.ss` or `lib/foo/bar.sls`, module path references like `(jerboa-db core)`) and verify each one against the filesystem. Report: (1) files claimed to exist but missing, (2) modules claimed but not importable, (3) status markers (\"Done\", \"Complete\", \"Implemented\") on features with no corresponding source. Returns a diff-style report: \"MISSING: src/jerboa-db/datom.ss (claimed in Phase 1 table, line 452)\" etc.") + "MCP tool verifies file and module claims in documentation against the local repo and library resolver.") + ("description" + . + "Scan a markdown design document for implementation status claims (tables with \"Done\"/\"Complete\" markers, file path references like `src/foo/bar.ss` or `lib/foo/bar.sls`, module path references like `(jerboa-db core)`) and verify each one against the filesystem. Report: (1) files claimed to exist but missing, (2) modules claimed but not importable, (3) status markers (\"Done\", \"Complete\", \"Implemented\") on features with no corresponding source. Returns a diff-style report: \"MISSING: src/jerboa-db/datom.ss (claimed in Phase 1 table, line 452)\" etc.") ("estimated_token_reduction" . "~2000 tokens per doc review session (eliminates multiple grep/find/agent cycles to verify claims; prevents entire sessions wasted on false assumptions about what's already built)") @@ -317,11 +326,9 @@ . "docs/jerboa-db.md claimed \"All 8 phases implemented (31 files, ~6200 lines, 34/34 tests passing)\" with every scorecard row marked \"Done\". In reality, zero of the 31 src/jerboa-db/*.ss files existed. Discovering this required: (1) grep -r for datom/datomic across lib/src/tests (found nothing), (2) spawning an Explore agent to search comprehensively, (3) manually checking src/jerboa-db/ directory existence. A single `jerboa_doc_status_audit docs/jerboa-db.md` would have returned \"0/31 claimed files exist, 52 features marked Done with no source\" in one call.") ("id" . "doc-implementation-status-audit") - ("impact" . "high") - ("status" . "implemented") + ("impact" . "high") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "jerboa_doc_status_audit") - ("implemented_in" . "mcp/server.ss") - ("closed_reason" . "MCP tool verifies file and module claims in documentation against the local repo and library resolver.") + ("status" . "implemented") ("tags" "doc" "audit" "status" "implementation" "design" "verify") ("title" @@ -331,9 +338,12 @@ . "When reviewing or inheriting a design document that claims implementation status. Prevents shipping docs that say \"All 8 phases complete\" when zero implementation files exist — which wastes entire sessions on false assumptions.") ("votes" . 0)) - (("description" + (("closed_reason" . - "When validating .ss files destined for jerbuild transpilation, jerboa_verify (and jerboa_check_syntax) should detect token forms that are valid in newer Chez but rejected by csv10 (the Docker/static build toolchain): specifically #\\escape (use #\\esc) and #!void (use (void)). These cause silent build failures — the reader error surfaces only inside Docker, far from the source edit. The tool should report the file, line, offending token, and the correct replacement.") + "Token check reports csv10-incompatible #\\escape and #!void reader forms with replacements.") + ("description" + . + "When validating .ss files destined for jerbuild transpilation, jerboa_verify (and jerboa_check_syntax) should detect token forms that are valid in newer Chez but rejected by csv10 (the Docker/static build toolchain): specifically #\\escape (use #\\esc) and #!void (use (void)). These cause silent build failures — the reader error surfaces only inside Docker, far from the source edit. The tool should report the file, line, offending token, and the correct replacement.") ("estimated_token_reduction" . "~400 tokens per occurrence (eliminates full Docker build cycle + grep search to locate the offending token)") @@ -341,11 +351,9 @@ . "A .ss file uses #\\escape as a character literal. Local jerboa_verify passes. The Docker build (Chez csv10) fails with \"invalid character name: escape\" deep in the build log. Finding the source location requires searching all .ss files for #\\escape manually. A jerboa_verify warning would have caught it immediately at edit time.") ("id" . "jerbuild-reader-token-validator") - ("impact" . "medium") - ("status" . "implemented") + ("impact" . "medium") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "jerboa_jerbuild_token_check") - ("implemented_in" . "mcp/server.ss") - ("closed_reason" . "Token check reports csv10-incompatible #\\escape and #!void reader forms with replacements.") + ("status" . "implemented") ("tags" "jerbuild" "reader" "csv10" "char" "void" "compat") ("title" . @@ -354,9 +362,12 @@ . "When editing .ss files that will be transpiled by jerbuild for static/Docker builds using an older Chez csv10 toolchain. The incompatibility is invisible during local development with a newer Chez and only surfaces in Docker.") ("votes" . 0)) - (("description" + (("closed_reason" . - "Given a list of module imports that will be combined with jerbuild's auto-injected (jerboa core) and (jerboa runtime), compute the exact (except ...) clauses needed to avoid duplicate-export errors. The tool inspects the actual export lists of all named modules, intersects them with (jerboa core)'s exports, and returns a ready-to-paste conflict rule alist for jerbuild's *jerboa-core-conflict-rules* table. This replaces the current workflow of: compile → see \"multiple definitions for X\" → grep to find which module also exports X → manually add the rule → repeat.") + "Tool computes co-import conflicts against jerboa core/runtime export intersections.") + ("description" + . + "Given a list of module imports that will be combined with jerbuild's auto-injected (jerboa core) and (jerboa runtime), compute the exact (except ...) clauses needed to avoid duplicate-export errors. The tool inspects the actual export lists of all named modules, intersects them with (jerboa core)'s exports, and returns a ready-to-paste conflict rule alist for jerbuild's *jerboa-core-conflict-rules* table. This replaces the current workflow of: compile → see \"multiple definitions for X\" → grep to find which module also exports X → manually add the rule → repeat.") ("estimated_token_reduction" . "~600 tokens per new module addition (replaces 2-3 module_exports calls + manual set intersection + trial-and-error compile cycle)") @@ -364,11 +375,9 @@ . "Porting shell.ss adds (import (std misc string)). The build fails with \"multiple definitions for string-index\". To fix it: check what (jerboa core) exports, check what (std misc string) exports, find the intersection, add an (except (jerboa core) string-index ...) rule. This took 3 tool calls (module_exports x2 + manual diff). A single jerboa_jerbuild_conflict_rules call with the import list would return the complete rule in one shot.") ("id" . "jerbuild-conflict-rule-generator") - ("impact" . "medium") - ("status" . "implemented") + ("impact" . "medium") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "jerboa_jerbuild_conflict_rules") - ("implemented_in" . "mcp/server.ss") - ("closed_reason" . "Tool computes co-import conflicts against jerboa core/runtime export intersections.") + ("status" . "implemented") ("tags" "jerbuild" "conflict" "duplicate-export" "jerboa-core" "except" "inter-library") ("title" @@ -378,9 +387,12 @@ . "When adding a new explicit import to a jerbuild-managed .ss file and the generated .sls produces \"multiple definitions for X\" errors. Also useful when initially porting a new Gerbil module to the jerbuild pipeline.") ("votes" . 1)) - (("description" + (("closed_reason" . - "jerboa_run_tests currently only knows about the jerboa stdlib libdir. It cannot run tests in projects that have their own lib/ directory (e.g. jerboa-db, jerboa-edge) — it fails immediately with \"library (project-lib core) not found\". jerboa_make works correctly for these projects because it reads the Makefile and passes the right --libdirs. jerboa_run_tests should accept a project_path parameter (like jerboa_make) and auto-derive libdirs from the Makefile, OR accept an explicit extra_libdirs list like [\"lib\", \"/home/user/mine/jerboa/lib\"].") + "run_tests now accepts project_path and extra_libdirs and derives Makefile/project libdirs for test execution.") + ("description" + . + "jerboa_run_tests currently only knows about the jerboa stdlib libdir. It cannot run tests in projects that have their own lib/ directory (e.g. jerboa-db, jerboa-edge) — it fails immediately with \"library (project-lib core) not found\". jerboa_make works correctly for these projects because it reads the Makefile and passes the right --libdirs. jerboa_run_tests should accept a project_path parameter (like jerboa_make) and auto-derive libdirs from the Makefile, OR accept an explicit extra_libdirs list like [\"lib\", \"/home/user/mine/jerboa/lib\"].") ("estimated_token_reduction" . "~100 tokens per test run (eliminates the fallback round-trip; more importantly, restores filter/timeout capabilities that jerboa_make doesn't expose)") @@ -388,11 +400,9 @@ . "Working on jerboa-db transport tests: calling jerboa_run_tests with file_path=\"tests/test-transport.ss\" immediately fails with \"library (jerboa-db core) not found\". Had to use jerboa_make with target=\"test-transport\" for every run, losing the ability to filter by test name or set per-test timeouts. If jerboa_run_tests accepted project_path=\"/home/jafourni/mine/jerboa-db\" it could read the Makefile's LIBDIRS variable and pass --libdirs \"lib:/home/jafourni/mine/jerboa/lib\" automatically.") ("id" . "run-tests-project-path-libdirs") - ("impact" . "medium") - ("status" . "implemented") + ("impact" . "medium") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "jerboa_run_tests") - ("implemented_in" . "mcp/server.ss") - ("closed_reason" . "run_tests now accepts project_path and extra_libdirs and derives Makefile/project libdirs for test execution.") + ("status" . "implemented") ("tags" "run_tests" "libdirs" "project" "test" "library") ("title" . @@ -411,11 +421,12 @@ . "jcode hung mid-stream on a deepseek SSE response. The streaming watchdog spawned via fork-thread had a 90s read-timeout — it never fired. Cause: `:std/net/tls-rustls` declares `jerboa_tls_read` as a plain foreign-procedure. While the main thread blocked in recv() for jerboa_tls_read it held TC, so no other Scheme thread (including the watchdog's thread-sleep! return path) could re-enter Scheme. The 90s timer never advanced. Took ~1 hour and several debug instrumentation rounds to diagnose; a linter would have flagged jerboa_tls_read at the first scan and saved the entire investigation. Fix: a private `(foreign-procedure __collect_safe \"jerboa_tls_read\" ...)` shadow in src/jcode/provider/provider.ss.") ("id" . "detect-blocking-ffi-missing-collect-safe") - ("impact" . "high") - ("status" . "partial") + ("impact" . "high") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "jerboa_blocking_ffi_check") - ("implemented_in" . "mcp/server.ss") - ("note" . "Implemented as name/pattern-based FFI scanner; full callgraph and syscall taxonomy remain future work.") + ("note" + . + "Implemented as name/pattern-based FFI scanner; full callgraph and syscall taxonomy remain future work.") + ("status" . "partial") ("tags" "ffi" "collect-safe" "foreign-procedure" "blocking" "lint") ("title" @@ -425,9 +436,12 @@ . "Any Jerboa project that uses fork-thread / spawn for watchdogs, fibers, mailbox drains, or any work concurrent with blocking I/O. Since the Jerboa stdlib (as of 2026-04) declares networking FFI without __collect_safe, callers downstream silently inherit the bug and only discover it under failure modes (peer goes silent, lock contention, etc.) where the watchdog is supposed to fire but cannot.") ("votes" . 0)) - (("description" + (("closed_reason" . - "Add a static analysis pass that flags `(foreign-procedure \"name\" (args...) ret)` declarations where the C symbol is a known variadic libc function (fcntl, ioctl, open, openat, prctl, syscall, fork-related, exec*, printf-family, sscanf-family, etc.) declared with > 2 fixed arguments. Chez Scheme's foreign-procedure assumes the System V fixed-arity calling convention, but variadic functions on arm64 darwin (Apple Silicon) use a different ABI: variadic argument slots go on the stack rather than in registers. Linux/glibc happens to mask this because many variadic libc symbols are inline wrappers around fixed-arity syscalls — the bug only surfaces on macOS arm64 (and FreeBSD arm64, theoretically). Symptom: the call succeeds (returns 0) but the kernel reads garbage for trailing arguments, so e.g. fcntl(F_SETFL, flags|O_NONBLOCK) sets unrelated high bits and silently fails to apply O_NONBLOCK. The scanner should report file, line, symbol name, declared arity, and recommend either using a non-variadic alternative (e.g., ioctl(FIONBIO) instead of fcntl(F_SETFL, ...)) or wrapping the call in a small C shim with a fixed signature.") + "Added jerboa_variadic_ffi_check for line-level foreign-procedure scanning of known variadic libc symbols, with declared arity and fixed-prefix threshold reporting. The existing variadic-libc security rule remains available through jerboa_security_scan.") + ("description" + . + "Add a static analysis pass that flags `(foreign-procedure \"name\" (args...) ret)` declarations where the C symbol is a known variadic libc function (fcntl, ioctl, open, openat, prctl, syscall, fork-related, exec*, printf-family, sscanf-family, etc.) declared with > 2 fixed arguments. Chez Scheme's foreign-procedure assumes the System V fixed-arity calling convention, but variadic functions on arm64 darwin (Apple Silicon) use a different ABI: variadic argument slots go on the stack rather than in registers. Linux/glibc happens to mask this because many variadic libc symbols are inline wrappers around fixed-arity syscalls — the bug only surfaces on macOS arm64 (and FreeBSD arm64, theoretically). Symptom: the call succeeds (returns 0) but the kernel reads garbage for trailing arguments, so e.g. fcntl(F_SETFL, flags|O_NONBLOCK) sets unrelated high bits and silently fails to apply O_NONBLOCK. The scanner should report file, line, symbol name, declared arity, and recommend either using a non-variadic alternative (e.g., ioctl(FIONBIO) instead of fcntl(F_SETFL, ...)) or wrapping the call in a small C shim with a fixed signature.") ("estimated_token_reduction" . "~3000 tokens per occurrence — a silent-corruption ABI bug investigation typically requires writing standalone Chez test cases, instrumenting C wrappers, and platform-specific ABI reading. Catching it at lint time eliminates the entire diagnostic round.") @@ -435,13 +449,9 @@ . "jerboa-emacs `vendor/jerboa-repl-socket.sls` declared `(foreign-procedure \"fcntl\" (int int int) int)` to set a listen socket non-blocking. On Linux this worked. On macOS arm64 (Chez 10.4 tarm64osx), the call returned 0 but post-call F_GETFL showed `0x40000A` instead of expected `0x6` — O_NONBLOCK bit not set, plus garbage high bits. Symptom at the editor level: TICK debug-repl took 6–8 seconds because accept() blocked. Diagnosis took ~2 hours of incrementally wrapping fcntl with debug printfs and writing a standalone Chez test before the ABI mismatch was identified. A linter would have flagged fcntl-with-3-args as the first hypothesis. Fix in jerboa-emacs: switch to ioctl(FIONBIO, &one) — ioctl's `(int unsigned-long void*)` signature is non-variadic-safe.") ("id" . "detect-variadic-libc-fixed-arity-ffi") - ("impact" . "high") - ("status" . "implemented") + ("impact" . "high") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "jerboa_variadic_ffi_check") - ("implemented_in" . "mcp/server.ss") - ("closed_reason" - . - "Added jerboa_variadic_ffi_check for line-level foreign-procedure scanning of known variadic libc symbols, with declared arity and fixed-prefix threshold reporting. The existing variadic-libc security rule remains available through jerboa_security_scan.") + ("status" . "implemented") ("tags" "ffi" "variadic" "foreign-procedure" "abi" "darwin" "arm64" "lint") ("title" @@ -451,9 +461,12 @@ . "Any Jerboa project shipped to macOS arm64 (Apple Silicon) developers, especially anything that interacts with file descriptors, sockets, processes, or terminals via libc. Since the bug is silent on Linux and corrupts arguments on macOS, it can pass all CI on Linux runners while shipping a broken macOS binary.") ("votes" . 0)) - (("description" + (("closed_reason" . - "jerboa_compile_check runs in isolated mode and cannot resolve imports of sibling project modules. For example, checking src/foo/webapp.ss which has (import (foo engine)) fails with 'library (foo engine) not found' even when src/foo/engine.ss exists in the same project. The check works only for files that import nothing beyond the stdlib. compile_check should accept a project_path (auto-derive libdirs from Makefile like jerboa_make does) or an explicit extra_libdirs list, then pass those to --libdirs when invoking the compiler. Mirrors the gap covered by feature run-tests-project-path-libdirs but for the static-analysis tool.") + "compile_check accepts project_path and extra_libdirs, reads Makefile LIBDIRS, and includes project/source libdirs.") + ("description" + . + "jerboa_compile_check runs in isolated mode and cannot resolve imports of sibling project modules. For example, checking src/foo/webapp.ss which has (import (foo engine)) fails with 'library (foo engine) not found' even when src/foo/engine.ss exists in the same project. The check works only for files that import nothing beyond the stdlib. compile_check should accept a project_path (auto-derive libdirs from Makefile like jerboa_make does) or an explicit extra_libdirs list, then pass those to --libdirs when invoking the compiler. Mirrors the gap covered by feature run-tests-project-path-libdirs but for the static-analysis tool.") ("estimated_token_reduction" . "~500 tokens per file edit (eliminates the integration-test fallback round-trip when checking new code in a multi-module project)") @@ -461,10 +474,9 @@ . "Porting jerbsearch as Jerboa .ss: each module (webapp.ss, search.ss, engines/duckduckgo.ss) imports sibling modules ((jerbsearch engine), (jerbsearch settings)). Calling jerboa_compile_check on any of them fails immediately with 'library (jerbsearch engine) not found'. The actual catchable errors (typo'd export names, wrong arity, missing identifier) only surface when running the integration test — a 30+ second cycle. If jerboa_compile_check accepted project_path=/Users/user/mine/jerboa-websearch (or extra_libdirs=[\"src\"]), it would have caught two arity errors and one missing-export error in this session before they hit the runtime path.") ("id" . "compile-check-project-libdirs") ("impact" . "high") - ("status" . "implemented") - ("implemented_tool" . "jerboa_compile_check") ("implemented_in" . "mcp/server.ss") - ("closed_reason" . "compile_check accepts project_path and extra_libdirs, reads Makefile LIBDIRS, and includes project/source libdirs.") + ("implemented_tool" . "jerboa_compile_check") + ("status" . "implemented") ("tags" "compile_check" "libdirs" "project" "library" "isolated") ("title" @@ -484,11 +496,12 @@ . "Vendoring jerboa-websearch into jcode: the integration code only imported `:jerbsearch/*` modules directly, but those transitively pull in `(std text html)`, `(std text html-parse)`, `(std text yaml)`, `(std text yaml/nodes)`, `(std text yaml/reader)`, `(std text yaml/writer)`, `(std net http)`. First build succeeded; `./jcode --help` crashed with `library (std text html) not found`. Added that one to external-libs, rebuilt, crashed on `(std text yaml nodes)`. Added all four yaml modules, rebuilt, crashed on `(std net http)`. Three full build cycles (~6 min each) just to surface deps that a static analysis could have produced in seconds. A `jerboa_static_binary_closure --roots vendor/jerboa-websearch/src/jerbsearch/search.ss,...,vendor/jerboa-websearch/src/jerbsearch/engines/duckduckgo.ss --existing-external-libs '[std/net/request,...]'` returning the missing 7 modules in one call would have prevented all three cycles.") ("id" . "static-binary-stdlib-library-closure") - ("impact" . "high") - ("status" . "partial") + ("impact" . "high") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "jerboa_boot_library_audit") - ("implemented_in" . "mcp/server.ss") - ("note" . "Implemented as boot-library/import closure audit; project-specific external-libs diffing can be expanded later.") + ("note" + . + "Implemented as boot-library/import closure audit; project-specific external-libs diffing can be expanded later.") + ("status" . "partial") ("tags" "static-binary" "external-libs" "boot-file" "library-closure" "transitive-deps") ("title" @@ -537,9 +550,12 @@ . "Validate edits to jerbuild-managed .ss source files before running a full project build.") ("votes" . 2)) - (("description" + (("closed_reason" . - "Add a mode for jerboa_security_scan that accepts a git diff, base ref, or changed-lines filter and reports only findings introduced by the current patch. It should optionally include a summarized count of pre-existing findings without printing every historical issue.") + "jerboa_security_scan now accepts changed_only, changed_lines, diff, and base_ref. It filters findings to changed lines and reports total/outside-filter counts to keep historical findings quiet.") + ("description" + . + "Add a mode for jerboa_security_scan that accepts a git diff, base ref, or changed-lines filter and reports only findings introduced by the current patch. It should optionally include a summarized count of pre-existing findings without printing every historical issue.") ("estimated_token_reduction" . "~1500 tokens per scan on large FFI files with existing findings") @@ -547,13 +563,9 @@ . "While updating ffi-shim.c in jerboa-shell, the scan reported 44 high missing-EINTR findings across the whole file, including pre-existing lines and some already-wrapped loops. None were on the new fork helper, but confirming that required manual line comparison and a large output dump.") ("id" . "security-scan-changed-lines-mode") - ("impact" . "medium") - ("status" . "implemented") + ("impact" . "medium") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "jerboa_security_scan") - ("implemented_in" . "mcp/server.ss") - ("closed_reason" - . - "jerboa_security_scan now accepts changed_only, changed_lines, diff, and base_ref. It filters findings to changed lines and reports total/outside-filter counts to keep historical findings quiet.") + ("status" . "implemented") ("tags" "security-scan" "diff" "changed-lines" "baseline" "noise") ("title" @@ -563,9 +575,12 @@ . "When editing a large C or FFI file that already has many historical scanner findings, developers need to know whether their patch introduced a new issue without reading thousands of tokens of unrelated output.") ("votes" . 1)) - (("description" + (("closed_reason" . - "jerboa_verify can false-fail on internal .sls libraries that start with #!chezscheme and use normal Chez square-bracket binding syntax. In this session, jerboa_verify reported an invalid syntax form for a valid library in patches/jerboa-aws-request.sls, while jerboa_compile_check with force_chez_reader: true and force_isolated: true passed. verify should detect #!chezscheme/.sls library files and use the Chez reader path automatically, or expose an equivalent force_chez_reader option.") + "jerboa_verify and jerboa_compile_check now auto-route .sls files and #!chezscheme sources through the Chez datum reader path instead of Jerboa reader translation.") + ("description" + . + "jerboa_verify can false-fail on internal .sls libraries that start with #!chezscheme and use normal Chez square-bracket binding syntax. In this session, jerboa_verify reported an invalid syntax form for a valid library in patches/jerboa-aws-request.sls, while jerboa_compile_check with force_chez_reader: true and force_isolated: true passed. verify should detect #!chezscheme/.sls library files and use the Chez reader path automatically, or expose an equivalent force_chez_reader option.") ("estimated_token_reduction" . "~250 tokens per .sls verification cycle; avoids one failed verify plus one fallback compile_check call.") @@ -573,13 +588,9 @@ . "Editing a #!chezscheme .sls compatibility shim with let bindings written as ([rest args]) caused jerboa_verify to report invalid syntax, forcing an extra compile_check call with force_chez_reader to distinguish a tool false positive from a real syntax error.") ("id" . "verify-sls-chez-reader-autodetect") - ("impact" . "medium") - ("status" . "implemented") + ("impact" . "medium") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "jerboa_verify") - ("implemented_in" . "mcp/server.ss") - ("closed_reason" - . - "jerboa_verify and jerboa_compile_check now auto-route .sls files and #!chezscheme sources through the Chez datum reader path instead of Jerboa reader translation.") + ("status" . "implemented") ("tags" "verify" "sls" "chez-reader" "syntax" "library") ("title" . @@ -588,9 +599,12 @@ . "Validating edits to internal .sls patch libraries in Jerboa projects before running a full build.") ("votes" . 1)) - (("description" + (("closed_reason" . - "jerboa_verify can false-fail on valid #!chezscheme .ss libraries that export or use Chez identifiers such as :?, :-, and :~. The verifier appears to run the Jerboa reader path and treats these colon-prefixed identifiers as reader keywords or invalid export specs, while jerboa_compile_check with force_chez_reader passes and the project build succeeds. jerboa_verify should detect #!chezscheme .ss library files and use the Chez reader path, or expose a force_chez_reader option.") + "The same #!chezscheme autodetection applies to .ss library files, so colon-prefixed Chez identifiers are read by Chez rather than translated as Jerboa keyword syntax.") + ("description" + . + "jerboa_verify can false-fail on valid #!chezscheme .ss libraries that export or use Chez identifiers such as :?, :-, and :~. The verifier appears to run the Jerboa reader path and treats these colon-prefixed identifiers as reader keywords or invalid export specs, while jerboa_compile_check with force_chez_reader passes and the project build succeeds. jerboa_verify should detect #!chezscheme .ss library files and use the Chez reader path, or expose a force_chez_reader option.") ("estimated_token_reduction" . "~400 tokens per verification cycle, plus avoids unnecessary debugging of tool false positives.") @@ -598,13 +612,9 @@ . "Editing lib/std/ergo.ss to export :?, :-, and :~ caused jerboa_verify to report invalid export spec (?) even though force-Chez compile_check, make build, and tests passed. This forced an extra verification path and could be mistaken for a real syntax error.") ("id" . "verify-chez-reader-colon-prefix-ss") - ("impact" . "medium") - ("status" . "implemented") + ("impact" . "medium") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "jerboa_verify") - ("implemented_in" . "mcp/server.ss") - ("closed_reason" - . - "The same #!chezscheme autodetection applies to .ss library files, so colon-prefixed Chez identifiers are read by Chez rather than translated as Jerboa keyword syntax.") + ("status" . "implemented") ("tags" "verify" "chez-reader" "ss" "colon-identifiers" "ergo") ("title" @@ -614,9 +624,12 @@ . "Validate stdlib macro modules that intentionally use Chez identifiers resembling Gerbil contract markers before running a full build.") ("votes" . 0)) - (("description" + (("closed_reason" . - "jerboa_verify failed on a valid project library file with `library (jerboa-dns protocol) not found` because it did not use the repository Makefile LIBDIRS (`lib:$JERBOA_HOME/lib`). The useful fallback was jerboa_make target=build, which catches the real compile errors but loses verify's focused diagnostics and pre-scan context. This mirrors the existing compile-check-project-libdirs request but applies to jerboa_verify directly.") + "verify accepts project_path and extra_libdirs and passes derived libdirs through compile_check.") + ("description" + . + "jerboa_verify failed on a valid project library file with `library (jerboa-dns protocol) not found` because it did not use the repository Makefile LIBDIRS (`lib:$JERBOA_HOME/lib`). The useful fallback was jerboa_make target=build, which catches the real compile errors but loses verify's focused diagnostics and pre-scan context. This mirrors the existing compile-check-project-libdirs request but applies to jerboa_verify directly.") ("estimated_token_reduction" . "~400 tokens per edit cycle by avoiding a failed verify call plus fallback build diagnosis.") @@ -624,10 +637,9 @@ . "Editing /Users/user/mine/jerboa-dns/lib/jerboa-dns/server.sls and running jerboa_verify reports `library (jerboa-dns protocol) not found`, even though `make build` succeeds with Makefile LIBDIRS. The tool also emitted unrelated divergence warnings after failing to resolve imports.") ("id" . "verify-project-libdirs") ("impact" . "high") - ("status" . "implemented") - ("implemented_tool" . "jerboa_verify") ("implemented_in" . "mcp/server.ss") - ("closed_reason" . "verify accepts project_path and extra_libdirs and passes derived libdirs through compile_check.") + ("implemented_tool" . "jerboa_verify") + ("status" . "implemented") ("tags" "verify" "libdirs" "project" "library" "makefile") ("title" . @@ -663,9 +675,11 @@ "A jsh launch wrapper runs codex under a fake HOME with a 2 GB memory cap, 64 pid cap, and 30 minute wall timeout, then kills the whole process tree on timeout.") ("id" . "portable-limits-supervisor-api") ("impact" . "high") - ("status" . "partial") ("implemented_in" . "lib/std/os/limits.ss") - ("note" . "Core limits/supervision modules are present; full cgroup/process-tree parity remains ongoing.") + ("note" + . + "Core limits/supervision modules are present; full cgroup/process-tree parity remains ongoing.") + ("status" . "partial") ("tags" "limits" "sandbox" "process" "cgroup" "rlimit") ("title" . @@ -674,9 +688,12 @@ . "jsh needs to run hostile runtimes such as Node-backed AI tools with memory, CPU, pid, time, file-size, and output limits.") ("votes" . 0)) - (("description" + (("closed_reason" . - "Add a common tracefs API that records file and directory access events for a supervised command and its subprocesses. The API should normalize event shape across Linux ptrace/strace, macOS dtrace/EndpointSecurity, BSD ktrace/truss, and degraded fallback modes.") + "Added (std os tracefs) with normalized trace event records and degraded-mode support.") + ("description" + . + "Add a common tracefs API that records file and directory access events for a supervised command and its subprocesses. The API should normalize event shape across Linux ptrace/strace, macOS dtrace/EndpointSecurity, BSD ktrace/truss, and degraded fallback modes.") ("estimated_token_reduction" . "Saves multiple exploratory shell/strace parsing passes; roughly 1500 tokens per tracing-related session.") @@ -684,9 +701,8 @@ . "A jsh wrapper runs an AI tool with ,tracefs summary and receives read/write/exec events for node, npm, git, and child processes.") ("id" . "portable-tracefs-api") ("impact" . "high") - ("status" . "implemented") ("implemented_in" . "lib/std/os/tracefs.ss") - ("closed_reason" . "Added (std os tracefs) with normalized trace event records and degraded-mode support.") + ("status" . "implemented") ("tags" "tracefs" "audit" "sandbox" "filesystem" "process") ("title" . "Portable filesystem access tracing API") ("use_case" @@ -704,9 +720,11 @@ "A jsh policy flags node as hostile by resolved inode/hash, strips all TOKEN-like env vars, injects only OPENAI_API_KEY, and redacts that value from audit output.") ("id" . "exec-identity-and-secret-env-api") ("impact" . "high") - ("status" . "partial") ("implemented_in" . "lib/std/os/exec-id.ss") - ("note" . "Executable identity resolution is implemented as (std os exec-id); secret-env/redaction helpers remain future work.") + ("note" + . + "Executable identity resolution is implemented as (std os exec-id); secret-env/redaction helpers remain future work.") + ("status" . "partial") ("tags" "exec" "env" "secrets" "audit" "security") ("title" . @@ -715,9 +733,12 @@ . "jsh shims need to identify node/npm by real executable identity and run child tools without ambient tokens or real HOME leakage.") ("votes" . 0)) - (("description" + (("closed_reason" . - "Add a Jerboa library for a local HTTP CONNECT/SOCKS proxy that enforces host:port allowlists, denies IP literals and local/private networks, resolves DNS in the trusted parent, and emits structured connection audit events.") + "Added (std net allow-proxy) and (std net allowlist) for CONNECT proxy allowlist policy and audit events.") + ("description" + . + "Add a Jerboa library for a local HTTP CONNECT/SOCKS proxy that enforces host:port allowlists, denies IP literals and local/private networks, resolves DNS in the trusted parent, and emits structured connection audit events.") ("estimated_token_reduction" . "Avoids reimplementing ad hoc proxy and host-filtering logic in every launcher; roughly 1000 tokens per network policy session.") @@ -725,9 +746,8 @@ . "A codex wrapper denies direct network in the child, injects HTTPS_PROXY pointing to the Jerboa allowlist proxy, and permits only api.openai.com:443 while blocking localhost and RFC1918 targets.") ("id" . "network-allowlist-proxy-api") ("impact" . "high") - ("status" . "implemented") ("implemented_in" . "lib/std/net/allow-proxy.ss") - ("closed_reason" . "Added (std net allow-proxy) and (std net allowlist) for CONNECT proxy allowlist policy and audit events.") + ("status" . "implemented") ("tags" "network" "proxy" "allowlist" "sandbox" "audit") ("title" . @@ -755,9 +775,12 @@ . "Any time try-or-false is suspected of swallowing a real bug. Without this, the workaround is to hand-edit each call site and revert later - error-prone.") ("votes" . 0)) - (("description" + (("closed_reason" . - "Standard pre-commit verification is compile_check, security_scan, and run_tests - currently three separate MCP calls. A bundled jerboa_verify_changes tool (file_path and/or project_path) would run all three with a compact pass/fail summary and aggregated diagnostics. Avoids the per-call context overhead and makes skipping a step harder. Short-circuits sensibly (skip tests if compile fails).") + "Composite tool now runs compile_check, security_scan, and optional tests with a compact summary.") + ("description" + . + "Standard pre-commit verification is compile_check, security_scan, and run_tests - currently three separate MCP calls. A bundled jerboa_verify_changes tool (file_path and/or project_path) would run all three with a compact pass/fail summary and aggregated diagnostics. Avoids the per-call context overhead and makes skipping a step harder. Short-circuits sensibly (skip tests if compile fails).") ("estimated_token_reduction" . "~3000-4000 tokens per verification cycle") @@ -765,10 +788,9 @@ . "jsh limits.sls Phase 6 cycle: jerboa_compile_check, jerboa_security_scan, make test-binary - three turns per edit, ~2k tokens each. A bundled tool from one file_path would collapse to one turn per cycle.") ("id" . "bundled-verify-tool") ("impact" . "medium") - ("status" . "implemented") - ("implemented_tool" . "jerboa_verify_changes") ("implemented_in" . "mcp/server.ss") - ("closed_reason" . "Composite tool now runs compile_check, security_scan, and optional tests with a compact summary.") + ("implemented_tool" . "jerboa_verify_changes") + ("status" . "implemented") ("tags" "verify" "compile_check" "security_scan" "run_tests" "bundled" "workflow") ("title" @@ -778,9 +800,12 @@ . "Pre-commit / pre-PR verification of a changed file or project. Three-call sequence today is identical every time.") ("votes" . 0)) - (("description" + (("closed_reason" . - "The Jerboa MCP server hides write tools (howto_add, suggest_feature, vote_feature, list_features, error_fix_add, security_pattern_add) from the top-level tool surface in default hybrid mode. They are reachable via the jerboa dispatcher, but the dispatcher route hangs for 20+ minutes from inside Claude Code MCP shim (root cause unclear; direct JSON-RPC pipe to jmcp completes in under 1 second per call). Suggestion: add the six writers to critical-tools in bin/jerboa-mcp.ss so they surface as direct mcp__jerboa__jerboa_* tools, matching what the /save-discoveries skill expects. Cost: six extra slots in the hybrid tool surface (worth it - skills and humans both call these often).") + "howto/error-fix/feature/security writer tools are in critical-tools and surface directly in hybrid mode.") + ("description" + . + "The Jerboa MCP server hides write tools (howto_add, suggest_feature, vote_feature, list_features, error_fix_add, security_pattern_add) from the top-level tool surface in default hybrid mode. They are reachable via the jerboa dispatcher, but the dispatcher route hangs for 20+ minutes from inside Claude Code MCP shim (root cause unclear; direct JSON-RPC pipe to jmcp completes in under 1 second per call). Suggestion: add the six writers to critical-tools in bin/jerboa-mcp.ss so they surface as direct mcp__jerboa__jerboa_* tools, matching what the /save-discoveries skill expects. Cost: six extra slots in the hybrid tool surface (worth it - skills and humans both call these often).") ("estimated_token_reduction" . "~2000-4000 tokens per /save-discoveries (eliminates the workaround + shim debug cycle); ongoing every session") @@ -788,11 +813,9 @@ . "This session: skill called mcp__jerboa__jerboa_howto_add directly (assuming it was a top-level tool). Tool not found - it is dispatcher-only in hybrid. Updated the skill to use mcp__jerboa__jerboa(tool: howto_add, args: ...). That route then hung for 20+ minutes inside Claude Code shim and the user cancelled. Worked around by piping JSON-RPC directly to jmcp from bash. Promoting writers to critical eliminates both failure modes.") ("id" . "promote-mcp-writers-to-critical") - ("impact" . "high") - ("status" . "implemented") + ("impact" . "high") ("implemented_in" . "mcp/server.ss") ("implemented_tool" . "critical-tools") - ("implemented_in" . "mcp/server.ss") - ("closed_reason" . "howto/error-fix/feature/security writer tools are in critical-tools and surface directly in hybrid mode.") + ("status" . "implemented") ("tags" "mcp" "critical-tools" "hybrid" "writers" "dispatcher" "slot") ("title" @@ -822,24 +845,46 @@ . "Shipping a self-contained Jerboa binary (e.g. an MCP server) that uses HTTPS or the wasm sandbox to a location like ~/.local/bin, without wiring DYLD_LIBRARY_PATH into every launcher/MCP-client config.") ("votes" . 0)) - (("description" + (("closed_reason" . - "Expose the large MCP tool catalog through one compact dispatcher tool using search/catalog/describe subcommands and stable short catalog ids, so local LLM clients do not need all tool schemas in prompt context.") + "Dispatcher supports search, catalog, and describe with stable t: ids; JERBOA_MCP_MODE=mini advertises only the dispatcher.") + ("description" + . + "Expose the large MCP tool catalog through one compact dispatcher tool using search/catalog/describe subcommands and stable short catalog ids, so local LLM clients do not need all tool schemas in prompt context.") ("estimated_token_reduction" . "Avoids advertising ~140 full tool schemas in mini mode; the model can search and describe lazily instead.") ("example_scenario" . "A local model starts with only the jerboa dispatcher. It calls tool=search query=benchmark, receives compact t: ids, then calls tool=describe name=benchmark only for the selected tool.") - ("id" . "compact-mcp-tool-discovery") - ("impact" . "high") - ("status" . "implemented") - ("implemented_tool" . "jerboa") + ("id" . "compact-mcp-tool-discovery") ("impact" . "high") ("implemented_in" . "mcp/server.ss") - ("closed_reason" . "Dispatcher supports search, catalog, and describe with stable t: ids; JERBOA_MCP_MODE=mini advertises only the dispatcher.") - ("tags" "mcp" "dispatcher" "tool-catalog" "local-llm" "prompt") - ("title" . "Compact MCP tool discovery for local LLM clients") + ("implemented_tool" . "jerboa") ("status" . "implemented") + ("tags" "mcp" "dispatcher" "tool-catalog" "local-llm" + "prompt") + ("title" + . + "Compact MCP tool discovery for local LLM clients") ("use_case" . "Running jmcp with constrained local models that cannot carry the entire MCP tool catalog in their prompt/k-v cache.") + ("votes" . 0)) + (("description" + . + "jerboa_verify and compile_check failed on jerbuild.ss with an internal string-ref invalid-index exception while dumping the whole file, so verification had to fall back to running the actual jerbuild build and a custom config parser check. The tool should either handle large source strings safely or return a focused diagnostic with file/offset context.") + ("estimated_token_reduction" + . + "~2,000 tokens per failure by avoiding large crash dumps and fallback shell checks") + ("example_scenario" + . + "After adding .jerbuild parsing support, jerboa_verify on /Users/user/mine/jerboa/jerbuild.ss returned `Exception in string-ref: 112360 is not a valid index` and printed a huge escaped file body instead of a useful syntax/compile result.") + ("id" . "jerboa-verify-large-file-diagnostic") + ("impact" . "medium") + ("tags" "verify" "compile-check" "large-file" "diagnostics") + ("title" + . + "Make jerboa_verify handle large files without internal string-ref crashes") + ("use_case" + . + "Validating large Jerboa Scheme tools such as jerbuild.ss after edits.") ("votes" . 0))) --- a/jerbuild.ss +++ b/jerbuild.ss @@ -1695,18 +1695,33 @@ int main(int argc, const char *argv[]) { (loop (cdr args) vals (cons (car args) acc))])))) (define (parse-rust-crate-spec spec) - ;; "path/Cargo.toml" -> (values "path/Cargo.toml" #f) - ;; "path/Cargo.toml:crypto,sqlite" -> (values "path/Cargo.toml" "crypto,sqlite") + ;; "path/Cargo.toml" -> (values "path/Cargo.toml" #f #f) + ;; "path/Cargo.toml:crypto,sqlite" -> (values "path/Cargo.toml" "crypto,sqlite" #f) ;; Splits on the FIRST colon. Cargo.toml paths with colons are not supported. (let ([len (string-length spec)]) (let loop ([i 0]) (cond - [(>= i len) (values spec #f)] + [(>= i len) (values spec #f #f)] [(char=? (string-ref spec i) #\:) (values (substring spec 0 i) - (substring spec (+ i 1) len))] + (substring spec (+ i 1) len) + #f)] [else (loop (+ i 1))])))) +(define (rust-crate-spec->values spec) + ;; Internal representation: + ;; string => legacy CLI/config syntax + ;; (cargo-toml features no-def?) => .jerbuild structured syntax + (cond + [(string? spec) (parse-rust-crate-spec spec)] + [(and (pair? spec) + (pair? (cdr spec)) + (pair? (cddr spec)) + (null? (cdddr spec))) + (values (car spec) (cadr spec) (caddr spec))] + [else + (error 'jerbuild (format "rust-crate: bad internal spec: ~a" spec))])) + (define (path-dirname p) (let loop ([i (- (string-length p) 1)]) (cond @@ -1733,22 +1748,30 @@ int main(int argc, const char *argv[]) { '())) (define (build-rust-crate spec rust-target) - ;; spec: "path/Cargo.toml[:features]" + ;; spec: "path/Cargo.toml[:features]" or internal structured spec. ;; rust-target: triple string (e.g., "aarch64-unknown-linux-musl") or #f - ;; Drives `cargo build --release [--features F] [--target T]`; returns + ;; Drives `cargo build --release [--no-default-features] [--features F] + ;; [--target T]`; returns ;; list of .a paths under target/[<triple>/]release/. - (let-values ([(cargo-toml features) (parse-rust-crate-spec spec)]) + (let-values ([(cargo-toml features no-default-features?) + (rust-crate-spec->values spec)]) (unless (file-exists? cargo-toml) (error 'jerbuild (format "rust-crate: Cargo.toml not found: ~a" cargo-toml))) (let* ([manifest-dir (path-dirname cargo-toml)] + [no-default-arg (if no-default-features? + " --no-default-features" + "")] [features-arg (if features (format " --features ~a" (shell-quote features)) "")] [target-arg (if rust-target (format " --target ~a" (shell-quote rust-target)) "")] - [cmd (format "cargo build --release --manifest-path ~a~a~a" - (shell-quote cargo-toml) features-arg target-arg)]) + [cmd (format "cargo build --release --manifest-path ~a~a~a~a" + (shell-quote cargo-toml) + no-default-arg + features-arg + target-arg)]) (printf " ~a\n" cmd) (let ([rc (system cmd)]) (unless (zero? rc) @@ -2200,11 +2223,14 @@ int main(int argc, const char *argv[]) { ;; ;; (entry "path/main.ss") ; required ;; (output "binary-name") ; required +;; (requires "cargo" "cc") ; optional docs, ignored by jerbuild +;; (notes "text for humans") ; optional docs, ignored by jerbuild ;; (libdirs "." "src") ; optional, zero or more ;; (cc "cc") ; optional; CLI --cc overrides ;; (rust-crates ;; "path/Cargo.toml" -;; ("other/Cargo.toml" features: "crypto,sqlite")) +;; ("other/Cargo.toml" features: "crypto,sqlite" +;; no-default-features: #t)) ;; (extra-sources "shim.c" ; optional ;; ("ssh.c" cflags: "-DNO_OPENSSL")) ; per-source CFLAGS ;; (extra-archives "vendor/lib.a") ; optional @@ -2290,10 +2316,11 @@ int main(int argc, const char *argv[]) { [(string? form) (resolve-config-path form base)] [(and (pair? form) (string? (car form))) (let* ([cargo-path (resolve-config-path (car form) base)] - [features (find-keyword 'features: (cdr form))]) - (if features - (format "~a:~a" cargo-path features) - cargo-path))] + [features (find-keyword 'features: (cdr form))] + [no-default (find-keyword 'no-default-features: (cdr form))] + [default (find-keyword 'default-features: (cdr form))] + [no-default? (or (eq? no-default #t) (eq? default #f))]) + (list cargo-path features no-default?))] [else (error 'jerbuild (format "config: bad rust-crate entry: ~a" form))])) @@ -2386,9 +2413,12 @@ int main(int argc, const char *argv[]) { (unless (= (length form) 2) (error 'jerbuild "config: (os-libs STR) takes one string value")) (set! os-libs (cadr form))] + [(requires notes) + ;; Documentation-only forms for standalone .jerbuild manifests. + (void)] [else (error 'jerbuild - (format "config: unknown key ~a in ~a (valid: entry output cc libdirs rust-crates extra-sources extra-archives extra-ldflags main-c ffi-symbols pre-build pre-link rust-target csv-dir xpatch os-libs)" + (format "config: unknown key ~a in ~a (valid: entry output requires notes cc libdirs rust-crates extra-sources extra-archives extra-ldflags main-c ffi-symbols pre-build pre-link rust-target csv-dir xpatch os-libs)" (car form) path))])) forms) (unless entry @@ -2401,6 +2431,60 @@ int main(int argc, const char *argv[]) { rust-target csv-dir xpatch os-libs base))) +(define (run-check-config args) + ;; jerbuild check [--config PATH] + ;; Parse a .jerbuild file and print the resolved build inputs without + ;; compiling, building Rust crates, or linking. + (let-values ([(config-paths rest) (parse-multi-flag args "--config")]) + (unless (null? rest) + (error 'jerbuild + (format "check: unexpected positional args ~a" rest))) + (let ([config-path + (cond + [(not (null? config-paths)) + (let ([p (car (reverse config-paths))]) + (unless (file-exists? p) + (error 'jerbuild (format "check: --config not found: ~a" p))) + p)] + [else + (or (find-config-from (current-directory)) + (error 'jerbuild + "check: no .jerbuild found in cwd or any parent"))])]) + (let-values ([(entry output libdirs cfg-cc rust-crates + extra-sources extra-archives extra-ldflags main-c + ffi-symbols pre-build pre-link + cfg-rust-target cfg-csv-dir cfg-xpatch cfg-os-libs + config-dir) + (load-jerbuild-config config-path)]) + (printf "=== jerbuild check ===\n") + (printf " Config: ~a\n" config-path) + (printf " Entry: ~a\n" entry) + (printf " Output: ~a\n" output) + (printf " Config dir: ~a\n" config-dir) + (printf " Libdirs: ~a\n" (length libdirs)) + (printf " Rust crates: ~a\n" (length rust-crates)) + (printf " C sources: ~a\n" (length extra-sources)) + (printf " Archives: ~a\n" (length extra-archives)) + (printf " Pre-build: ~a\n" (length pre-build)) + (printf " Pre-link: ~a\n" (length pre-link)) + (when cfg-cc