worlddelta (Engine B) — reasoning
Class:
reasoning. Why Engine B is shaped this way, and the forks not taken. The descriptive shape is inworlddelta-design.md; the full analysis catalog + prior-art survey inengine-b-analyses.mdandmodgunn-master-plan.md§3/§7.
Why a behaviour-delta at all (history almost nobody has)
The field is fragmented: SCA tools own the dep-graph, SAST tools own the call-graph, GUAC owns metadata aggregation, Socket owns behavioral malware — and almost no one owns history. Point-in-time is the industry norm. modgunn historizes the whole world and diffs any two snapshots, so it can emit a signal the standard stack structurally cannot: "a 5th-level jar, unchanged for years, silently changed behaviour" (A22). That signal only exists if you keep a per-symbol behaviour history — which is exactly what the skade/Iceberg warehouse is for, and why Engine B exists as a distinct engine rather than a bigger scanner.
Why one engine fuses call-graph + dep-graph
Reachability-gated vuln (A8) — the commercial differentiator (Snyk/Endor) — needs both the call-graph (is the vulnerable function reached?) and the dep-graph (which dep, which version?). Tools that own only one can't do it well; Snyk's reachability is a 3-language add-on. modgunn carries both in one WorldSnapshot vocabulary, so A8 is first-class, multi-ecosystem and offline. That fusion is the reason the vocabulary is modgunn-owned (Symbol/CallEdge/ DepNode/DepEdge) rather than two borrowed graphs.
Why modgunn owns its SCIP vocabulary (lifted, not imported)
The SCIP/knowledge machinery originally lived in nornir. Importing it would make modgunn → nornir — the forbidden cycle. So the graph vocabulary is lifted into modgunn as plain data types with no nornir dependency; nornir remains the consumer (viz pane + tripwire feed). Leaf-cleanliness wins over code reuse here because the alternative reintroduces the exact cycle the crate was built to break.
Why the tripwire battery is a trait, not a fixed function
Tripwire is a trait and diff runs a Vec<Box<dyn Tripwire>> so the cheap default battery (default_tripwires) and the heavy world-delta site's extended set share one fold via diff_with. This let the analyses land incrementally — all 25 of 25 now carry real logic (the seven pairwise tripwires fire by default; the declarative PolicyTripwire / ThresholdTripwire are plugged in with rules at the heavy site) — without ever changing diff's signature or the BehaviorDelta type. A fixed function would force every analysis into one build; the trait lets the lean and heavy planes differ by data.
Why FingerprintDriftTripwire fires only on the silent case
A version bump or a public-API change is already visible to every consumer — flagging it would be noise. The dangerous case is a dependency whose effective behaviour (transitive call closure) changed while its version and public signature stayed identical: nothing in the manifest moved, so nothing else would notice. Restricting the finding to same package version && same display && changed fingerprint isolates exactly that silent signal and keeps the tripwire quiet on the visible changes other tools already catch.
Why heavy graph algorithms are materialized off the CDC delta (not recomputed)
Betweenness is O(V·E), PageRank O(E·iters), min-cut O(V·E²) — recomputing them per query over the whole transitive world is unaffordable at S3/S4. The design materializes them into Iceberg tables and refreshes only the neighborhood a read_delta touched (see warehouse-reasoning.md). That is why the warehouse's CDC seam and Engine B are co-designed: the delta is what makes whole-graph analytics incremental instead of batch.
Why heavy and off by default
Full SCIP closure + whole-graph algorithms + (eventually) embeddings need 128 GB–1 TB RAM. holger's gate and static-holger must stay pure-leaf. So Engine B is --features scip, never compiled into the lean gate, and its verdict still folds into the same core::Verdict so the split is invisible to consumers. The general two-engine rationale is in reasoning.md.