Save Jerboa knowledge base updates

ober

f59a519b96a2c6940283c4c9b01082df2ffb74d1

diff --git a/data/anti-patterns.sexp b/data/anti-patterns.sexp
index 398720a..e3be7cb 100644
--- a/data/anti-patterns.sexp
+++ b/data/anti-patterns.sexp
@@ -756,4 +756,66 @@
    ("severity" . "medium")
    ("tags" "release" "version" "lsp" "multicall" "smoke")
    ("title" . "Release Version Bump Misses Hidden Entrypoints")
-   ("tools" "rg" "jerboa_verify" "jerboa_make")))
+   ("tools" "rg" "jerboa_verify" "jerboa_make"))
+ (("advice"
+    .
+    "Stage optional libraries by deleting stale .so/.wpo first, then compile each module only when its .so is still missing, or topologically order dependencies before dependents. Preserve the same compiled instances for compile-program, compile-whole-program, and make-boot-file.")
+   ("avoid"
+     .
+     "Do not loop over optional package modules and call compile-library on every source file after compile-imported-libraries can already compile dependencies transitively. A dependent module can record one compilation instance, then an explicit later compile of its dependency overwrites that dependency's .wpo.")
+   ("id" . "wpo-recompile-transitive-optional-module")
+   ("kinds" "debug-error" "module")
+   ("pattern"
+     .
+     "compile-whole-program: .*\\.wpo.*does not define expected compilation instance|for-each.*compile-library.*optional.*modules")
+   ("severity" . "high")
+   ("tags" "wpo" "compile-whole-program" "compile-library"
+     "optional-import" "boot-file" "static-binary")
+   ("title"
+     .
+     "Recompiling transitive optional modules breaks WPO instances")
+   ("tools"
+     "jerboa_howto"
+     "jerboa_verify"
+     "jerboa_make"
+     "jerboa_failure_advisor"))
+ (("advice"
+    .
+    "Make build scripts derive `all` from the canonical feature definition source, or add an explicit synchronization check/test comparing the duplicate list to the canonical `feature-names`. If a temporary unsupported list is needed, make the omission visible with a warning and a test fixture.")
+   ("avoid"
+     .
+     "Do not maintain a second hard-coded list of optional features for `all` without deriving it from the canonical feature metadata. The duplicate list will drift and silently omit a feature from builds or docs.")
+   ("id" . "duplicate-feature-manifest-drift")
+   ("kinds" "all" "script" "docs")
+   ("pattern"
+     .
+     "cross-supported-all-features|supported-all-features|unsupported-features.*\\(|JSH_FEATURES=all")
+   ("severity" . "high")
+   ("tags" "features" "manifest" "build-script"
+     "optional-features" "configuration" "all")
+   ("title"
+     .
+     "Duplicate feature manifests drift from canonical feature list")
+   ("tools" "jerboa_howto" "jerboa_verify" "jerboa_make" "rg"))
+ (("advice"
+    .
+    "Use a small shell-quote helper or a structured process API for each complete argument. Quote the whole path after appending suffixes like `/.`, and keep command syntax separate from data arguments.")
+   ("avoid"
+     .
+     "Do not protect shell command arguments by writing literal single quotes around `~a` substitutions. It fails for paths containing apostrophes and makes it easy to accidentally quote only part of an argument such as `'/path'/.'`.")
+   ("id" . "ad-hoc-single-quote-shell-interpolation")
+   ("kinds" "security" "script")
+   ("pattern"
+     .
+     "system \\(format \"[^\"]*'~a|format \"[^\"]*'~a|cp -a '~a|rm -rf '~a")
+   ("severity" . "high")
+   ("tags" "shell" "quoting" "system" "paths" "injection"
+     "build-script")
+   ("title"
+     .
+     "Ad hoc single-quote shell interpolation breaks paths")
+   ("tools"
+     "jerboa_security_scan"
+     "jerboa_howto"
+     "jerboa_verify"
+     "rg")))
diff --git a/data/cookbooks.sexp b/data/cookbooks.sexp
index 29413ac..610f4f6 100644
--- a/data/cookbooks.sexp
+++ b/data/cookbooks.sexp
@@ -6172,4 +6172,16 @@
      "setUpdatesEnabled" "buffer-switch" "flicker")
    ("title"
      .
-     "Batch QStackedWidget and editor updates during Qt buffer switches")))
+     "Batch QStackedWidget and editor updates during Qt buffer switches"))
+ (("code"
+    .
+    "(import (chezscheme))\n\n;; Pattern for a static/cross build where the app lazy-loads an optional\n;; library with (eval '(import (vendor feature)) ...). Stage the library,\n;; add the stage dir before compile-program, compile each module only once,\n;; and include the resulting .so files in the auxiliary boot file.\n\n(define (script->wpo entry-script)\n  (let ([n (string-length entry-script)])\n    (if (and (> n 3)\n             (string=? (substring entry-script (- n 3) n) \".ss\"))\n        (string-append (substring entry-script 0 (- n 3)) \".wpo\")\n        (string-append entry-script \".wpo\"))))\n\n(define (compile-missing! stage-dir module-path)\n  (let ([src (format \"~a/~a.sls\" stage-dir module-path)]\n        [so  (format \"~a/~a.so\" stage-dir module-path)])\n    (cond\n      [(file-exists? so)\n       (printf \"    keep ~a\\n\" so)]\n      [(file-exists? src)\n       (compile-library src)]\n      [else\n       (printf \"    skip missing ~a\\n\" src)])))\n\n(define (optional-runtime-so-files stage-dir optional-modules)\n  (filter file-exists?\n    (map (lambda (m) (format \"~a/~a.so\" stage-dir m)) optional-modules)))\n\n(define (bundle-optional-runtime-lib! stage-dir optional-modules entry-script libs-boot)\n  ;; In a real build, copy the optional package into stage-dir and delete stale\n  ;; .so/.wpo before this function runs.\n  (library-directories\n    (cons (cons stage-dir stage-dir) (library-directories)))\n  (parameterize ([compile-imported-libraries #t]\n                 [generate-wpo-files #t]\n                 [optimize-level 2]\n                 [generate-inspector-information #f])\n    (for-each (lambda (m) (compile-missing! stage-dir m)) optional-modules)\n    (compile-program entry-script)\n    (compile-whole-program (script->wpo entry-script) \"app.wp.so\" #t))\n  (apply make-boot-file libs-boot '(\"petite\" \"scheme\")\n    (optional-runtime-so-files stage-dir optional-modules)))\n\n;; Example call shape, not run by this recipe:\n;; (bundle-optional-runtime-lib!\n;;   \"feature-stage-cross\"\n;;   '(\"vendor-feature/dependency\" \"vendor-feature/main\")\n;;   \"app-generated.ss\"\n;;   \"app-libs.boot\")") ("id" . "static-cross-bundle-optional-runtime-lib")
+   ("imports" "(chezscheme)")
+   ("notes"
+     .
+     "Use this when a feature is retained for whole-program optimization but is also imported later at runtime with eval/import. Add the stage library directory before compile-program so the WPO graph sees the same library instances that make-boot-file will embed. Do not blindly recompile every optional module after transitive compilation; if module A compiled module B, recompiling B later can produce 'does not define expected compilation instance' during compile-whole-program. Compile only missing .so files or use strict dependency-before-dependent ordering. For static musl binaries, pair this with the static dlopen/Sforeign_symbol recipe when the optional library uses foreign-procedure.")
+   ("tags" "static-binary" "cross-build" "optional-import"
+     "make-boot-file" "wpo" "runtime-import")
+   ("title"
+     .
+     "Bundle lazy optional libraries in static cross builds")))
diff --git a/data/features.sexp b/data/features.sexp
index 7ac8a04..b31251a 100644
--- a/data/features.sexp
+++ b/data/features.sexp
@@ -2932,4 +2932,44 @@
    ("use_case"
      .
      "When debugging GUI complaints where normal unit tests pass but the user sees distracting pops, redraws, or splitter jitter.")
+   ("votes" . 0))
+ (("description"
+    .
+    "Add a Jerboa MCP/build helper that compares canonical feature metadata such as features.def feature-names against per-target build-script supported-all/unsupported lists and generated manifests. It should report features present canonically but omitted from JSH_FEATURES=all for a target, and optionally explain whether omission is intentional.")
+   ("estimated_token_reduction"
+     .
+     "~1000 tokens per feature-gating debugging session; eliminates manual rg/diff checks across feature defs, build scripts, and generated manifests.")
+   ("example_scenario"
+     .
+     "build-jsh-cross.ss had a separate cross-supported-all-features list that omitted vault and listed it as unsupported, while canonical features.def included vault. A drift checker would flag vault as omitted from the cross all build before users noticed ,vault missing.")
+   ("id" . "feature-manifest-drift-check")
+   ("impact" . "medium")
+   ("tags" "features" "manifest" "build" "cross-build"
+     "configuration")
+   ("title"
+     .
+     "Check feature manifests for drift from canonical definitions")
+   ("use_case"
+     .
+     "Use before or during release builds to verify that JSH_FEATURES=all means the same feature set across native, cross, and generated manifests unless a target explicitly excludes a feature.")
+   ("votes" . 0))
+ (("description"
+    .
+    "Teach jerboa_security_scan to recognize findings that occur inside Scheme string literals used to generate source code, and either scan the emitted code as a separate virtual file or mark the finding as generated-code context. This prevents misleading line references when the apparent source line is a display string rather than executable code in the scanned file.")
+   ("estimated_token_reduction"
+     .
+     "~500 tokens per generated-source security review; avoids manual explanation and repeated inspection of string-emitted code.")
+   ("example_scenario"
+     .
+     "build-jsh-cross.ss emits a vault crypto library as strings. jerboa_security_scan reported foreign-alloc-no-free at the generator line, even though the generated code frees the pointer in dynamic-wind. A generated-code-aware scan could report the virtual generated file or suppress the false positive.")
+   ("id" . "security-scan-generated-code-context")
+   ("impact" . "medium")
+   ("tags" "security-scan" "generated-code" "false-positive"
+     "ffi" "source-context")
+   ("title"
+     .
+     "Distinguish generated code strings in security scan findings")
+   ("use_case"
+     .
+     "Use when build scripts generate Scheme or C source via display/fprintf. The scanner should still catch issues in emitted code, but it should evaluate cleanup patterns within the emitted code instead of treating each string literal line as executable source in the generator.")
    ("votes" . 0)))