Update Jerboa knowledge catalogs

ober

c8a03e82813ec7b36e98a9a2884fcc0089b6d969

diff --git a/data/anti-patterns.sexp b/data/anti-patterns.sexp
index f71cdee..5ebc73c 100644
--- a/data/anti-patterns.sexp
+++ b/data/anti-patterns.sexp
@@ -4081,87 +4081,185 @@
     .
     "Put page upsert, generation advance, outgoing-link replacement, and frontier enqueue in one store-owned transaction. Mutate disposable derived indexes only after commit, drop their cache on update failure, and force a late statement failure in a regression test to prove every authoritative table rolls back.")
    ("avoid"
-    .
-    "Do not commit page metadata and generation in one transaction, then replace links or enqueue discovered frontier URLs in a second transaction. A crash between commits publishes a page without its crawl graph and adds an extra durable flush.")
+     .
+     "Do not commit page metadata and generation in one transaction, then replace links or enqueue discovered frontier URLs in a second transaction. A crash between commits publishes a page without its crawl graph and adds an extra durable flush.")
    ("id" . "split-transactions-for-one-logical-ingest")
    ("kinds" "module" "test")
    ("pattern"
-    .
-    "page.*transaction.*links|links.*second transaction|generation.*frontier")
+     .
+     "page.*transaction.*links|links.*second transaction|generation.*frontier")
    ("severity" . "high")
-   ("tags" "transaction" "ingestion" "crawler" "frontier" "links"
-    "crash-consistency")
+   ("tags" "transaction" "ingestion" "crawler" "frontier"
+     "links" "crash-consistency")
    ("title"
-    .
-    "Do Not Split One Logical Ingest Across Durable Transactions")
+     .
+     "Do Not Split One Logical Ingest Across Durable Transactions")
    ("tools"
-    "transaction rollback regression"
-    "fault injection"
-    "file-backed A/B benchmark"
-    "jerboa_security_scan"))
+     "transaction rollback regression"
+     "fault injection"
+     "file-backed A/B benchmark"
+     "jerboa_security_scan"))
  (("advice"
     .
     "Start from the validated dictionary document frequency and subtract only excluded document IDs found with binary search in the sorted postings. Bound the exclusion set with compaction policy, verify equivalence across replacements and tombstones, benchmark the frequency operation separately, and state that scoring still visits accepted postings.")
    ("avoid"
-    .
-    "Do not rescan an entire immutable sorted posting list merely to adjust a stored document frequency for a small bounded set of replacements or tombstones. This can double common-term posting traversal on every query.")
+     .
+     "Do not rescan an entire immutable sorted posting list merely to adjust a stored document frequency for a small bounded set of replacements or tombstones. This can double common-term posting traversal on every query.")
    ("id" . "rescanning-postings-for-bounded-exclusions")
    ("kinds" "module" "test")
    ("pattern"
-    .
-    "for-each-posting.*(?:override|tombstone|excluded).*(?:count|frequency)")
+     .
+     "for-each-posting.*(?:override|tombstone|excluded).*(?:count|frequency)")
    ("severity" . "high")
    ("tags" "mmap" "postings" "document-frequency" "overrides"
-    "binary-search" "performance")
+     "binary-search" "performance")
    ("title"
-    .
-    "Do Not Rescan Immutable Postings for Bounded Exclusions")
+     .
+     "Do Not Rescan Immutable Postings for Bounded Exclusions")
    ("tools"
-    "jerboa_verify"
-    "hybrid equivalence test"
-    "focused A/B benchmark"
-    "jerboa_security_scan"))
+     "jerboa_verify"
+     "hybrid equivalence test"
+     "focused A/B benchmark"
+     "jerboa_security_scan"))
  (("advice"
     .
     "Stream complete candidate scores directly into a deterministic worst-first heap bounded by the result limit. Keep document-score accumulation for multi-contribution cases such as multi-term queries, preserve score and document-ID tie ordering, verify against the materialized reference, and benchmark score production plus selection.")
    ("avoid"
-    .
-    "Do not allocate one score-table entry per match when each candidate is visited exactly once and that visit yields its complete score. A bounded top-k API does not save O(matches) memory if an O(matches) accumulator is still built first.")
+     .
+     "Do not allocate one score-table entry per match when each candidate is visited exactly once and that visit yields its complete score. A bounded top-k API does not save O(matches) memory if an O(matches) accumulator is still built first.")
    ("id" . "materializing-single-contribution-score-table")
    ("kinds" "module" "test")
    ("pattern"
-    .
-    "single-term.*(?:score|scores).*(?:hash|table)|hash-put!.*score.*top-k")
+     .
+     "single-term.*(?:score|scores).*(?:hash|table)|hash-put!.*score.*top-k")
    ("severity" . "medium")
-   ("tags" "search" "top-k" "streaming" "heap" "score-accumulator"
-    "memory")
+   ("tags" "search" "top-k" "streaming" "heap"
+     "score-accumulator" "memory")
    ("title"
-    .
-    "Do Not Materialize Scores When Each Candidate Contributes Once")
+     .
+     "Do Not Materialize Scores When Each Candidate Contributes Once")
    ("tools"
-    "jerboa_verify"
-    "ranking equivalence test"
-    "focused accumulator benchmark"
-    "jerboa_security_scan"))
+     "jerboa_verify"
+     "ranking equivalence test"
+     "focused accumulator benchmark"
+     "jerboa_security_scan"))
  (("advice"
     .
     "At map-open time recompute every pruning summary from validated postings and document metadata, require exact equality, and reject the entire disposable index on any mismatch. Use only mathematically safe upper bounds under live corpus statistics, preserve deterministic tie rules, and regression-test pruned results against an unpruned reference across replacements and tombstones.")
    ("avoid"
-    .
-    "Do not use persisted block maxima, score upper bounds, or skip metadata to omit candidates after checking only offsets and a file checksum. A corrupt value or writer bug can understate the bound and silently remove valid results.")
+     .
+     "Do not use persisted block maxima, score upper bounds, or skip metadata to omit candidates after checking only offsets and a file checksum. A corrupt value or writer bug can understate the bound and silently remove valid results.")
    ("id" . "trusting-unvalidated-pruning-metadata")
    ("kinds" "module" "test" "security")
    ("pattern"
+     .
+     "(?:block|max|upper).*(?:bound|summary).*(?:skip|prun)(?!.*validat)")
+   ("severity" . "high")
+   ("tags" "search" "block-max" "mmap" "validation"
+     "upper-bound" "correctness")
+   ("title"
+     .
+     "Do Not Trust Persisted Pruning Bounds Without Recomputing Them")
+   ("tools"
+     "jerboa_verify"
+     "corrupt-index regression"
+     "ranking equivalence test"
+     "jerboa_security_scan"))
+ (("advice"
     .
-    "(?:block|max|upper).*(?:bound|summary).*(?:skip|prun)(?!.*validat)")
+    "Construct each WAND term bound from independently safe maxima for every score-increasing component and a safe minimum for every score-decreasing component, using the same global corpus statistics as exact scoring. Verify pruned top-k against exhaustive ranking over sparse mixtures and exclusions. Prune equality only when cursors advance monotonically and the deterministic tie rule guarantees every later document loses the tie.")
+   ("avoid"
+     .
+     "Do not derive a WAND or block-max bound from one representative posting, average document statistics, shard-local BM25 statistics, or maxima that omit title/body weighting. An underestimated bound silently prunes valid top-k results; an equality cutoff can also drop a preferred tie when traversal order is not monotone.")
+   ("id" . "underestimating-wand-score-bounds")
+   ("kinds" "module" "test")
+   ("pattern"
+     .
+     "(?:WAND|block.max).*(?:average|representative|local).*(?:bound|score)|upper.bound.*(?:omit|underestimate)")
    ("severity" . "high")
-   ("tags" "search" "block-max" "mmap" "validation" "upper-bound"
-    "correctness")
+   ("tags" "search" "WAND" "BM25" "upper-bound" "top-k"
+     "tie-order")
+   ("title" . "Do Not Underestimate WAND Score Bounds")
+   ("tools"
+     "jerboa_verify"
+     "exhaustive ranking oracle"
+     "sparse posting regression"
+     "jerboa_security_scan"))
+ (("advice"
+    .
+    "Bound the half-open skip range by the minimum of every contributing cursor's block-exclusive end and every noncontributing cursor's current document ID. Seek only cursors below that boundary, require the boundary to advance, and regression-test a high-scoring future term whose first posting lies inside the contributing blocks.")
+   ("avoid"
+     .
+     "Do not choose a block-max WAND skip target from only the cursors contributing at the current candidate. A different term whose cursor first appears inside that range can add enough score to enter top-k and would be skipped silently.")
+   ("id" . "wand-range-skip-ignores-future-cursors")
+   ("kinds" "module" "test")
+   ("pattern"
+     .
+     "(?:WAND|block.max).*(?:skip|seek).*(?:block.end|range)(?!.*(?:next|future|nonparticipating).*(?:cursor|document))")
+   ("severity" . "high")
+   ("tags" "search" "WAND" "block-max" "cursor" "range-skip"
+     "top-k")
+   ("title" . "Do Not Skip WAND Ranges Past Future Cursors")
+   ("tools"
+     "jerboa_verify"
+     "exhaustive ranking oracle"
+     "seek-count regression"
+     "jerboa_security_scan"))
+ (("advice"
+    .
+    "Use a unique private sentinel object and compare it with eq?. Add regressions for bare #f, nested list/vector positions, first/middle/last positions, and comment handling adjacent to #f.")
+   ("avoid"
+     .
+     "Do not use #f to mean no datum, skipped comment, or absent token when #f is also a valid parsed value. Truthiness checks silently drop literal false values in lists, vectors, arguments, and branches.")
+   ("id" . "false-value-as-parser-sentinel")
+   ("kinds" "module" "debug-error" "test")
+   ("pattern"
+     .
+     "(?:hash|reader|parser).*(?:sentinel|no.value).*#f|if\\s+.*datum.*cons")
+   ("severity" . "high")
+   ("tags" "reader" "parser" "sentinel" "false" "datum"
+     "comments")
+   ("title" . "Do Not Use #f As A Parser No-Value Sentinel")
+   ("tools" "jerboa_eval" "jerboa_run_tests" "jerboa_verify"))
+ (("advice"
+    .
+    "For runtime eval/import, compile the optional library instances and include their .so files in an auxiliary boot file used by the image. Alternatively replace runtime lookup with generated direct static dispatch. Prove availability with a clean binary command smoke; a successful WPO link is insufficient.")
+   ("avoid"
+     .
+     "Do not assume that importing and referencing one binding from an optional library makes that library available later through eval/import in a compile-whole-program standalone image.")
+   ("id" . "wpo-static-reference-is-not-runtime-import-bundle")
+   ("kinds" "all" "ffi")
+   ("pattern" . "eval.*import|environment.*optional.*library")
+   ("severity" . "high")
+   ("tags" "WPO" "runtime-import" "eval" "boot-file"
+     "static-binary")
    ("title"
+     .
+     "A WPO reference does not preserve runtime eval imports")
+   ("tools"
+     "jerboa_howto"
+     "jerboa_make"
+     "jerboa_verify"
+     "make-boot-file smoke"))
+ (("advice"
     .
-    "Do Not Trust Persisted Pruning Bounds Without Recomputing Them")
+    "Preserve a non-empty explicit environment override; otherwise pass the configured feature-file path through unchanged. Verify both all-feature and zero-feature builds by checking the generated closure count, not only selector output.")
+   ("avoid"
+     .
+     "Do not hard-code all features in a pre-build or staging hook after an interactive selector or config file has chosen a subset.")
+   ("id" . "build-stage-overwrites-feature-selection")
+   ("kinds" "all" "test")
+   ("pattern"
+     .
+     "export JSH_FEATURES=[\"']?all|FEATURES=.*:-all")
+   ("severity" . "high")
+   ("tags" "features" "build-stage" "environment"
+     "configuration" "jerbuild")
+   ("title"
+     .
+     "Build staging must not overwrite selected features")
    ("tools"
-    "jerboa_verify"
-    "corrupt-index regression"
-    "ranking equivalence test"
-    "jerboa_security_scan")))
+     "jerboa_howto"
+     "jerboa_make"
+     "jerboa_verify"
+     "focused binary smoke")))
diff --git a/data/api-signatures.sexp b/data/api-signatures.sexp
index fbedc49..941d0fc 100644
--- a/data/api-signatures.sexp
+++ b/data/api-signatures.sexp
@@ -676,6 +676,8 @@
       "kt-function-return-type" "kt-function-visibility"
       "kt-function?" "kt-if-else" "kt-if-test" "kt-if-then"
       "kt-if?" "kt-import-alias" "kt-import-path" "kt-import?"
+      "kt-index-get-index" "kt-index-get-target" "kt-index-get?"
+      "kt-lambda-body" "kt-lambda-params" "kt-lambda?"
       "kt-lit-type" "kt-lit-value" "kt-lit?" "kt-member-call-args"
       "kt-member-call-name" "kt-member-call-target"
       "kt-member-call?" "kt-member-get-name"
@@ -695,19 +697,19 @@
       "make-kt-assign" "make-kt-binary" "make-kt-block"
       "make-kt-call" "make-kt-class" "make-kt-expr-stmt"
       "make-kt-file" "make-kt-for-range" "make-kt-function"
-      "make-kt-if" "make-kt-import" "make-kt-lit"
-      "make-kt-member-call" "make-kt-member-get" "make-kt-name"
-      "make-kt-new" "make-kt-param" "make-kt-property"
-      "make-kt-return" "make-kt-type" "make-kt-unary"
-      "make-kt-val" "make-kt-when" "make-kt-when-branch")
+      "make-kt-if" "make-kt-import" "make-kt-index-get"
+      "make-kt-lambda" "make-kt-lit" "make-kt-member-call"
+      "make-kt-member-get" "make-kt-name" "make-kt-new"
+      "make-kt-param" "make-kt-property" "make-kt-return"
+      "make-kt-type" "make-kt-unary" "make-kt-val" "make-kt-when"
+      "make-kt-when-branch")
      ("file" . "lib/jerboa/typed/kotlin/ast.ss")
      ("tier" . "core"))
    ("(jerboa typed kotlin lower)"
-     ("exports"
-       "typed-library-form->kotlin-string"
-       "typed-module->kotlin-file"
-       "typed-module->kotlin-string"
-       "typed-type->kotlin-type")
+     ("exports" "typed-library-form->kotlin-string"
+       "typed-library-forms->kotlin-files"
+       "typed-module->kotlin-file" "typed-module->kotlin-string"
+       "typed-modules->kotlin-files" "typed-type->kotlin-type")
      ("file" . "lib/jerboa/typed/kotlin/lower.ss")
      ("tier" . "core"))
    ("(jerboa typed kotlin print)"
@@ -722,8 +724,9 @@
        "kotlin-file->string" "kotlin-package-name"
        "kotlin-string-literal" "kotlin-symbol-name"
        "kotlin-type->string" "typed-library-form->kotlin-string"
+       "typed-library-forms->kotlin-files"
        "typed-module->kotlin-file" "typed-module->kotlin-string"
-       "typed-type->kotlin-type")
+       "typed-modules->kotlin-files" "typed-type->kotlin-type")
      ("file" . "lib/jerboa/typed/kotlin.ss")
      ("tier" . "core"))
    ("(jerboa typed llvmir)"
@@ -7077,13 +7080,13 @@
      ("file" . "lib/std/zipper.ss")
      ("tier" . "stable")))
   ("source_root" . "/Users/user/mine/jerboa")
-  ("stats" ("modules" . 718) ("parse_errors" . 0) ("symbols" . 10485)
+  ("stats" ("modules" . 718) ("parse_errors" . 0) ("symbols" . 10494)
     ("tiers"
       ("compat" . 47)
       ("core" . 38)
       ("stable" . 555)
       ("unstable" . 78))
-    ("total_exports" . 13712))
+    ("total_exports" . 13723))
   ("symbol_index" ("%chan-enqueue-raw!" "(std csp)")
    ("&actor-dead" "(std error conditions)")
    ("&actor-timeout" "(std error conditions)")
@@ -12164,6 +12167,12 @@
    ("kt-import-alias" "(jerboa typed kotlin ast)")
    ("kt-import-path" "(jerboa typed kotlin ast)")
    ("kt-import?" "(jerboa typed kotlin ast)")
+   ("kt-index-get-index" "(jerboa typed kotlin ast)")
+   ("kt-index-get-target" "(jerboa typed kotlin ast)")
+   ("kt-index-get?" "(jerboa typed kotlin ast)")
+   ("kt-lambda-body" "(jerboa typed kotlin ast)")
+   ("kt-lambda-params" "(jerboa typed kotlin ast)")
+   ("kt-lambda?" "(jerboa typed kotlin ast)")
    ("kt-lit-type" "(jerboa typed kotlin ast)")
    ("kt-lit-value" "(jerboa typed kotlin ast)")
    ("kt-lit?" "(jerboa typed kotlin ast)")
@@ -13086,6 +13095,8 @@
    ("make-kt-function" "(jerboa typed kotlin ast)")
    ("make-kt-if" "(jerboa typed kotlin ast)")
    ("make-kt-import" "(jerboa typed kotlin ast)")
+   ("make-kt-index-get" "(jerboa typed kotlin ast)")
+   ("make-kt-lambda" "(jerboa typed kotlin ast)")
    ("make-kt-lit" "(jerboa typed kotlin ast)")
    ("make-kt-member-call" "(jerboa typed kotlin ast)")
    ("make-kt-member-get" "(jerboa typed kotlin ast)")
@@ -18546,6 +18557,9 @@
      "(jerboa typed wrapper)")
    ("typed-library-forms->jerboa-wrapper-library-files"
      "(jerboa typed wrapper)")
+   ("typed-library-forms->kotlin-files"
+     "(jerboa typed kotlin lower)"
+     "(jerboa typed kotlin)")
    ("typed-library-forms->llvmir-module"
      "(jerboa typed llvmir)")
    ("typed-library-forms->llvmir-string"
@@ -18580,6 +18594,9 @@
      "(jerboa typed wrapper)")
    ("typed-modules->jerboa-wrapper-library-files"
      "(jerboa typed wrapper)")
+   ("typed-modules->kotlin-files"
+     "(jerboa typed kotlin lower)"
+     "(jerboa typed kotlin)")
    ("typed-modules->llvmir-string" "(jerboa typed llvmir)")
    ("typed-modules->rust-crate-files" "(jerboa typed rust)")
    ("typed-modules->rust-crate-string" "(jerboa typed rust)")