From 4efa26585abd2cb58fc70ef7c95e3ebc46ddf3d9 Mon Sep 17 00:00:00 2001 From: Bendik Aagaard Lynghaug Date: Tue, 11 Aug 2026 21:42:57 +0200 Subject: [PATCH] Add aggregates.yaml: the full state graph for applicants/subscribers/projects/organizations Portal's aggregate state machines used to be compiled Rust, duplicated across four files plus the bucket-name wiring that dispatched to them. This is now the single source of truth for what states exist, what event-type string represents each, and what transitions are legal - portal loads and validates it at boot/reload (see the paired portal change). Adding a new aggregate, or reshaping an existing one, is now a content-only change. Co-Authored-By: Claude Sonnet 5 --- aggregates.yaml | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 aggregates.yaml diff --git a/aggregates.yaml b/aggregates.yaml new file mode 100644 index 0000000..d871199 --- /dev/null +++ b/aggregates.yaml @@ -0,0 +1,47 @@ +# The full state graph for every bucket that wants event-sourced, +# CAS-protected transitions - see portal's src/aggregates/mod.rs. A +# bucket with no entry here still works (plain KV mutate-in-place, no +# event log). `bucket` doubles as both the KV bucket name and the +# JetStream event-subject segment (events..) - one name, +# not two. +# +# Pages (review.yaml, subscribed.yaml, index.yaml) reference these +# bucket names and `to:` state names by string; they never re-declare +# the graph itself. Add a new aggregate here alone - no portal code +# change needed. +aggregates: + - bucket: applicants + initial: open + states: + open: { event: applied } + invited: { event: invited } + declined: { event: declined } + transitions: + open: [invited, declined] + + - bucket: subscribers + initial: open + states: + open: { event: subscribed } + unsubscribed: { event: unsubscribed } + transitions: + open: [unsubscribed] + + - bucket: projects + initial: open + states: + open: { event: submitted } + accepted: { event: accepted } + declined: { event: declined } + transitions: + open: [accepted, declined] + + - bucket: organizations + initial: prospect + states: + prospect: { event: identified } + client: { event: became_client } + past_client: { event: churned } + transitions: + prospect: [client, past_client] + client: [past_client]