docs: update Datomic comparison after join-engine work

ober

7c45c55d0d87090b6c45bd762d9ae16b69f260cb

diff --git a/docs/datomic-comparison.md b/docs/datomic-comparison.md
index cb93e2d..d7ecadf 100644
--- a/docs/datomic-comparison.md
+++ b/docs/datomic-comparison.md
@@ -18,32 +18,39 @@ selectivities are comparable.
 
 | Query | jerboa Datalog | jerboa + DuckDB | Datomic |
 |---|--:|--:|--:|
-| **Load** (~147k entities) | **1136** | — | 1745 |
+| **Load** (~147k entities) | **1011** | — | 1745 |
 | Q1 — artist exact name lookup | 0 | — | 0.7 |
 | Q2 — releases by artist name (2-hop) | 0 | — | 0.9 |
 | Q3 — `startYear < 1960` (range) | **1** | — | 4.8 |
-| Q4 — tracks > 240s on shared-artist releases (multi-join) | 1885 | 363 | **368** |
+| Q4 — tracks > 240s on shared-artist releases (multi-join) | 811 | **283** | 368 |
 | Q5 — releases per country (group-by) | **1** | — | 9.3 |
 | Q6 — reverse-ref releases for an artist | 0 | — | 0.4 |
 | Q7 — pull artist attributes | 0 | — | 0.0 |
-| Q8 — avg track duration by release status (join + agg) | 1314 | **12** | 566 |
+| Q8 — avg track duration by release status (join + agg) | **428** | 11 | 566 |
+
+*(jerboa Datalog figures are post-join-engine work: a per-query scan cache plus
+alist bindings with O(1) extension. Before that work Q4 was 1885 ms and Q8
+1314 ms.)*
 
 ## Findings
 
 1. **OLTP / point / index queries (Q1–Q3, Q5–Q7): jerboa is at parity or
-   faster.** It loads ~1.5× faster than Datomic's `mem` peer and beats it on the
+   faster.** It loads ~1.7× faster than Datomic's `mem` peer and beats it on the
    indexed scans (Q3 1 vs 5 ms, Q5 1 vs 9 ms); the rest are sub-millisecond on
    both. The ported index machinery — B+-tree memory index, real-cardinality
    planner, native group-by / pure-aggregate fast paths — holds up against
    Datomic.
 
-2. **Raw Datalog multi-way joins (Q4, Q8): Datomic is faster** — ~5× on Q4
-   (368 vs 1885 ms) and ~2.3× on Q8 (566 vs 1314 ms). Datomic's relational join
-   engine (with better join ordering) outclasses jerboa's nested-loop Datalog.
-   This is the real remaining engine gap.
+2. **Raw Datalog multi-way joins (Q4, Q8): now close, and jerboa wins Q8.**
+   After the join-engine work (per-query scan cache + alist bindings), jerboa is
+   **faster than Datomic on Q8** (428 vs 566 ms — a join + aggregate) and the Q4
+   gap shrank from ~5× to ~2.2× (811 vs 368 ms). The change that mattered was
+   representational: bindings are now O(1)-extend alists rather than copied
+   hashtables. Q4's residual gap is **result materialization** — it returns
+   428k rows, where Datomic's set-of-tuples result is more compact.
 
-3. **jerboa's DuckDB fallback erases that gap on analytics** — Q4 ties
-   Datomic-Datalog (363 vs 368 ms) and Q8 beats it 47× (12 vs 566 ms).
+3. **jerboa's DuckDB fallback still leads on analytics** — Q4 beats
+   Datomic-Datalog (283 vs 368 ms) and Q8 is ~51× faster (11 vs 566 ms).
 
 ## Honest caveats
 
@@ -62,12 +69,13 @@ selectivities are comparable.
 
 ## Net
 
-jerboa-db is **genuinely competitive with Datomic on OLTP** at this scale,
-**behind on Datalog multi-way joins** (where Datomic's engine wins 2–5×), and
-**ahead on analytics only because it offloads to DuckDB** — the same strategy
-Datomic uses with Presto. The gap worth closing is jerboa's Datalog join engine
-(hash/merge joins, better ordering); the analytics gap is already handled by the
-columnar fallback.
+jerboa-db is **competitive with Datomic across the board at this scale**: faster
+on load and OLTP/index queries, **faster on the Q8 join+aggregate** after the
+join-engine work, and within ~2.2× on the one remaining query (Q4, a wide
+428k-row result — a materialization cost, not a join-algorithm one). Its DuckDB
+columnar fallback still leads on the analytical shapes, the same way Datomic
+leans on Presto. The remaining lever is Q4-style result materialization
+(tuple/columnar result relations); the join algorithm and analytics are handled.
 
 ## Reproduce