host P0.3b: durable flow log — survives restart (LIVE-VERIFIED)
The driver now persists each effect record to the blog store (string-keyed to dodge the keyword/ persist top-level split), and host/blog-load-flowlog! rebuilds the in-memory log on boot (wired into serve.sh after load-edges!). So /flows survives a restart — closing the P0.3 gap. LIVE PROOF: published a post on blog.rose-ash.com → /flows showed validate+notify → RESTARTED the container (in-memory log lost) → /flows STILL showed them, reloaded from the durable store. Round-trip also covered by a conformance test (persist → clear → reload → identical). blog 208/208, full host conformance 599/599. Note: whole-list rewrite per effect — fine at P0 volume, cap/rotate later. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -161,12 +161,22 @@
|
||||
{:register! (fn (spec dag hint) nil)
|
||||
:match (fn (a) (if (and (= (get a :type) "create") (= (get (get a :object) :type) "article"))
|
||||
(list {:dag host/blog--publish-dag}) (list)))})
|
||||
;; P0.3b: the flow log is DURABLE — string-keyed records (dodge the keyword/persist top-level split),
|
||||
;; persisted to the blog store under one key, so /flows survives a restart. Boot-loaded via
|
||||
;; host/blog-load-flowlog!. (Whole-list rewrite per effect — fine at P0 volume; cap/rotate later.)
|
||||
(define host/blog--flowlog-key "flowlog")
|
||||
(define host/blog--driver
|
||||
{:dispatch (fn (eff)
|
||||
(begin (set! host/blog--flow-log
|
||||
(concat host/blog--flow-log
|
||||
(list {:verb (get eff :verb) :args (get eff :args)})))
|
||||
(list)))}) ;; record the effect; no follow-up activities (P0)
|
||||
(list {"verb" (get eff :verb) "args" (get eff :args)})))
|
||||
(persist/backend-kv-put host/blog-store host/blog--flowlog-key host/blog--flow-log)
|
||||
(list)))}) ;; record the effect (durably); no follow-up activities (P0)
|
||||
;; rebuild the in-memory flow log from the durable store (call on boot, like host/blog-load-edges!).
|
||||
(define host/blog-load-flowlog!
|
||||
(fn ()
|
||||
(let ((v (persist/backend-kv-get host/blog-store host/blog--flowlog-key)))
|
||||
(when (and v (= (type-of v) "list")) (set! host/blog--flow-log v)))))
|
||||
(define host/blog--publish-engine
|
||||
(behavior/make-engine {:triggers host/blog--triggers :runner host/flow--exec-runner
|
||||
:transport host/blog--transport :driver host/blog--driver
|
||||
@@ -2667,8 +2677,8 @@
|
||||
(quote (p (em "No flows yet — publish a post to fire the on-publish DAG.")))
|
||||
(cons (quote ul)
|
||||
(map (fn (e)
|
||||
(quasiquote (li (strong (unquote (get e :verb))) " "
|
||||
(unquote (if (> (len (get e :args)) 0) (str (first (get e :args))) "")))))
|
||||
(quasiquote (li (strong (unquote (get e "verb"))) " "
|
||||
(unquote (if (> (len (get e "args")) 0) (str (first (get e "args"))) "")))))
|
||||
host/blog--flow-log))))))))))
|
||||
|
||||
;; ── routes ──────────────────────────────────────────────────────────
|
||||
|
||||
@@ -149,6 +149,11 @@ EPOCH=1
|
||||
echo "(epoch $EPOCH)"
|
||||
echo "(eval \"(host/blog-load-edges!)\")"
|
||||
EPOCH=$((EPOCH+1))
|
||||
# P0.3b: rebuild the in-memory publish flow log from the durable store, so /flows
|
||||
# survives a restart (the driver persists each effect record under "flowlog").
|
||||
echo "(epoch $EPOCH)"
|
||||
echo "(eval \"(host/blog-load-flowlog!)\")"
|
||||
EPOCH=$((EPOCH+1))
|
||||
# Sessions on the DURABLE store, LAZILY: only a logged-in session (one that
|
||||
# writes a field) persists, so a login survives a restart while anonymous /
|
||||
# crawler traffic leaves no rows. host/session-init! bumps the per-boot epoch
|
||||
|
||||
@@ -1223,8 +1223,22 @@
|
||||
(host/blog-put! "p03b" "U" "(article (h1 \"u\"))" "published")
|
||||
(host/blog--set-field-values! "p03b" {"category" "urgent"})
|
||||
(host/blog--maybe-publish! "p03b" nil "published")
|
||||
(map (fn (e) (get e :verb)) host/blog--flow-log))
|
||||
(map (fn (e) (get e "verb")) host/blog--flow-log))
|
||||
(list "validate" "digest" "validate" "notify"))
|
||||
;; P0.3b: the flow log is DURABLE — it round-trips through the blog store (survives a restart).
|
||||
(host-bl-test "P0.3b: the flow log persists + reloads from the store (string-keyed, no split)"
|
||||
(begin
|
||||
(set! host/blog--flow-log (list))
|
||||
(persist/backend-kv-put host/blog-store host/blog--flowlog-key (list)) ;; reset durable
|
||||
(host/blog-put! "p03d" "D" "(article (h1 \"d\"))" "published")
|
||||
(host/blog--set-field-values! "p03d" {"category" "newsletter"})
|
||||
(host/blog--maybe-publish! "p03d" "draft" "published") ;; fires → persists
|
||||
(let ((before (map (fn (e) (get e "verb")) host/blog--flow-log)))
|
||||
(begin
|
||||
(set! host/blog--flow-log (list)) ;; simulate a restart
|
||||
(host/blog-load-flowlog!) ;; reload from the store
|
||||
(list before (map (fn (e) (get e "verb")) host/blog--flow-log)))))
|
||||
(list (list "validate" "digest") (list "validate" "digest")))
|
||||
|
||||
(define
|
||||
host-bl-tests-run!
|
||||
|
||||
@@ -214,6 +214,11 @@ covers everything until a DAG's cost/latency/placement forces the substrate.
|
||||
activities), so business logic can change state, which federates, which triggers more flows.
|
||||
|
||||
## Progress log (newest first)
|
||||
- 2026-07-02 — P0.3b DONE + LIVE-VERIFIED. The flow log is now DURABLE: the driver
|
||||
persists string-keyed effect records to the blog store (dodging the keyword/persist top-level
|
||||
split); host/blog-load-flowlog! rebuilds it on boot (serve.sh). Proof: published on
|
||||
blog.rose-ash.com, RESTARTED the container, /flows still showed validate+notify (reloaded from the
|
||||
store). blog 208/208, conformance 599/599. Whole-list rewrite per effect — cap/rotate later.
|
||||
- 2026-07-02 — P0.3 DONE + LIVE-VERIFIED. The seam wired into the live publish path: on-publish
|
||||
registry + in-process transport + host driver + the execute-fold runner, fired by the draft→
|
||||
published transition in both write handlers. Published a real post on blog.rose-ash.com → /flows
|
||||
|
||||
Reference in New Issue
Block a user