host: typed relations — Phase 1.5, durable edge store + boot replay

lib/relations holds the graph in memory only (a Datalog cache), so related/tags/
types were wiped on every restart while the posts (durable KV) survived — fatal
for a model where tags and types ARE relations. Make the host the durable source
of truth.

- every physical edge is also a KV row "edge:<src>|<kind>|<dst>" in the blog
  store (host/blog--add-edge!/--del-edge! wrap relations/relate+unrelate with
  kv-put/kv-delete). '|' is safe: slugs are [a-z0-9-], kinds are registry names.
- host/blog-load-edges! rebuilds the in-memory graph from edge:* keys; serve.sh
  calls it on boot right after pointing the store at the durable backend.
- lib/relations stays an in-memory cache; the durable KV is the source of truth
  (same shape as the blog pointing at the durable backend).

3 tests: KV row written on relate, replay rebuilds the graph after an in-memory
wipe (restart sim), unrelate deletes the row. 247/247.

Verified live: related welcome<->hello, force-recreated the container (wipes the
in-memory graph), the relation + its rendered block survived the restart.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-28 16:25:52 +00:00
parent dc0cf0b4cc
commit 71dd040d80
3 changed files with 71 additions and 11 deletions

View File

@@ -314,6 +314,24 @@
"application/x-www-form-urlencoded" "other=beta-post"))
(contains? (host/blog-out "alpha-post" "related") "beta-post"))
true)
(host-bl-test "edges are durable: KV row written on relate"
(begin
(host/blog-relate! "alpha-post" "gamma-post" "tagged")
(persist/backend-kv-has? host/blog-store (host/blog--edge-key "alpha-post" "tagged" "gamma-post")))
true)
(host-bl-test "replay rebuilds the graph after an in-memory wipe (restart sim)"
(begin
(relations/load! (list)) ;; simulate a fresh process
(host/blog-load-edges!) ;; replay from the durable store
(list (contains? (host/blog-out "alpha-post" "tagged") "gamma-post")
(contains? (host/blog-out "alpha-post" "related") "beta-post")
(contains? (host/blog-out "beta-post" "related") "alpha-post")))
(list true true true))
(host-bl-test "unrelate deletes the durable KV row"
(begin
(host/blog-unrelate! "alpha-post" "gamma-post" "tagged")
(persist/backend-kv-has? host/blog-store (host/blog--edge-key "alpha-post" "tagged" "gamma-post")))
false)
;; -- experimental unguarded create-only route (POST /new, no auth) --
(define host-bl-oapp (host/make-app (list host/blog-open-create-routes host/blog-routes)))