Record jsqlite and tooling discoveries

ober

d54563e6d6358e973d46b62b80cda0b1976d7cc5

diff --git a/data/cookbooks.sexp b/data/cookbooks.sexp
index 3817284..e548189 100644
--- a/data/cookbooks.sexp
+++ b/data/cookbooks.sexp
@@ -5409,4 +5409,42 @@
      "immutable" "hamt-empty" "hamt-ref")
    ("title"
      .
-     "Use HAMT persistent maps from (std misc persistent)")))
+     "Use HAMT persistent maps from (std misc persistent)"))
+ (("code"
+    .
+    "(import (chezscheme) (jsh ffi))\n\n;; Direct foreign-procedure bindings outside (jsh ffi) need libjsh-ffi loaded\n;; first in dynamic/dev builds. Calling any existing (jsh ffi) wrapper triggers\n;; that module's guarded loader. In static builds this is harmless because the\n;; symbols are already registered.\n\n(def *my-c-symbol* #f)\n\n(def (my-c-symbol arg-bv arg-len)\n  (unless *my-c-symbol*\n    (ffi-getpid) ; forces (jsh ffi)'s libjsh-ffi loader path in dynamic builds\n    (set! *my-c-symbol*\n          (foreign-procedure \"ffi_my_new_symbol\" (u8* int) int)))\n  (*my-c-symbol* arg-bv arg-len))") ("id" . "jsh-direct-ffi-after-loader")
+   ("imports" "(chezscheme)" "(jsh ffi)")
+   ("notes"
+     .
+     "Prefer adding exported bindings inside (jsh ffi) when that module is the source being edited. If binding a new ffi-shim.c symbol from jsh.ss or another script-level file, resolve it lazily after an existing (jsh ffi) call such as ffi-getpid; otherwise dynamic builds can raise `Exception in foreign-procedure: no entry for ...` even though the symbol exists in libjsh-ffi. Regenerate ffi-shim-symbols.list for static builds.")
+   ("tags" "jsh" "ffi" "foreign-procedure" "libjsh-ffi"
+     "load-shared-object" "dynamic")
+   ("title"
+     .
+     "Bind a new jsh ffi-shim symbol outside (jsh ffi)"))
+ (("code"
+    .
+    "(import (except (jerboa prelude)\n                sqlite-open sqlite-close sqlite-exec sqlite-execute sqlite-query\n                sqlite-prepare sqlite-finalize sqlite-step sqlite-bind))\n(import (jsqlite api))\n\n(def (escape-like-pattern s)\n  (let ((len (string-length s)))\n    (let loop ((i 0) (parts '()))\n      (if (>= i len)\n        (apply string-append (reverse parts))\n        (let ((c (string-ref s i)))\n          (loop (+ i 1)\n                (cons (cond\n                        ((char=? c #\\\\) \"\\\\\\\\\")\n                        ((char=? c #\\%) \"\\\\%\")\n                        ((char=? c #\\_) \"\\\\_\")\n                        (else (string c)))\n                      parts)))))))\n\n(def (contains-pattern s)\n  (string-append \"%\" (escape-like-pattern s) \"%\"))\n\n(def db (sqlite-open \":memory:\"))\n(sqlite-exec db \"CREATE TABLE notes (body TEXT)\")\n(sqlite-exec db \"INSERT INTO notes (body) VALUES (?)\" \"100% literal\")\n(sqlite-query db \"SELECT body FROM notes WHERE body LIKE ? ESCAPE '\\\\'\" (contains-pattern \"100%\"))\n(sqlite-close db)") ("id" . "jsqlite-escaped-like-search")
+   ("imports"
+     "(except (jerboa prelude) sqlite-open sqlite-close sqlite-exec sqlite-execute sqlite-query sqlite-prepare sqlite-finalize sqlite-step sqlite-bind)"
+     "(jsqlite api)")
+   ("notes"
+     .
+     "jsqlite does not provide SQLite FTS virtual tables. For simple literal substring search, keep the SQL fixed, pass the pattern as a parameter, and escape backslash, percent, and underscore before wrapping with % wildcards. If importing jsqlite API alongside the prelude, exclude prelude sqlite names to avoid duplicate bindings.")
+   ("tags" "jsqlite" "sqlite" "like" "escape" "search"
+     "parameters")
+   ("title"
+     .
+     "Literal substring search in jsqlite with LIKE ESCAPE"))
+ (("code"
+    .
+    "(import (except (jerboa prelude)\n                sqlite-open sqlite-close sqlite-exec sqlite-execute sqlite-query\n                sqlite-prepare sqlite-finalize sqlite-step sqlite-bind))\n(import (jsqlite api))\n\n(def db (sqlite-open \":memory:\"))\n(sqlite-exec db \"CREATE TABLE terminal_size (cols INTEGER, \\\"rows\\\" INTEGER)\")\n(sqlite-exec db \"INSERT INTO terminal_size (cols, \\\"rows\\\") VALUES (?, ?)\" 80 24)\n(sqlite-query db \"SELECT cols, \\\"rows\\\" FROM terminal_size\")\n(sqlite-close db)") ("id" . "jsqlite-quote-keyword-identifiers")
+   ("imports"
+     "(except (jerboa prelude) sqlite-open sqlite-close sqlite-exec sqlite-execute sqlite-query sqlite-prepare sqlite-finalize sqlite-step sqlite-bind)"
+     "(jsqlite api)")
+   ("notes"
+     .
+     "Names such as rows can be parsed as SQL keywords by jsqlite. Quote keyword-like identifiers consistently in CREATE TABLE, INSERT, UPDATE, and SELECT statements. This is preferable to relying on a parser accepting bare identifiers that happen to work in another SQLite implementation.")
+   ("tags" "jsqlite" "sqlite" "sql" "identifier" "keyword"
+     "schema")
+   ("title" . "Quote SQL keyword column names in jsqlite")))
diff --git a/data/features.sexp b/data/features.sexp
index 31a3fb8..7194190 100644
--- a/data/features.sexp
+++ b/data/features.sexp
@@ -1954,4 +1954,24 @@
    ("use_case"
      .
      "Debugging static Chez/Jerboa binaries that fail at startup with foreign-procedure: no entry for ...")
+   ("votes" . 0))
+ (("description"
+    .
+    "`jerboa_function_signature` can report `Could not find` for symbols that `jerboa_module_exports` confirms are exported from imported project modules, such as `(jsh ffi)` or `(jsh signals)`. It should evaluate the imports and inspect the binding when static metadata is unavailable, or clearly distinguish non-procedure bindings from unresolved symbols.")
+   ("estimated_token_reduction"
+     .
+     "~300-700 tokens per affected API check by avoiding fallback searches/evals.")
+   ("example_scenario"
+     .
+     "While adding a jsh meta-command, `module_exports` showed `ffi-kill-pid` and `signal-name->number`, but `function_signature` could not find either with imports supplied.")
+   ("id" . "function-signature-imported-export-resolution")
+   ("impact" . "medium")
+   ("tags" "function_signature" "module_exports" "arity"
+     "imports" "project-modules")
+   ("title"
+     .
+     "Resolve imported exported bindings in function_signature")
+   ("use_case"
+     .
+     "When writing Jerboa code, agents are required to verify API arities before editing. This currently forces fallback to source search or ad hoc eval for some project exports.")
    ("votes" . 0)))