uhhm.no rebuilt from a trainer plan: trials, and the redoal custodians
Lint and reload / lint (push) Skipped

Built by portal-trainer from plans/uhhm.yaml (uhhm/portal-trainer,
runs/uhhm-2026-09-23): the five live buckets keep their state and
event names, so every record on the owners' desk stays. New: `trials`,
a business trying the trainer on its own workflow, approved by an
owner before anything is built (the n8n preview workflow comes later,
noted as attended_by); `custody_requests`, what reaches UHHM as the
custodian of the redoal network, on a custodians' desk owners can
watch. Owners invite into both desks. needs.yaml holds the site to
its needs from here on; the lint reads it.

Gone from the live site: the starred-repos card, the proposal editor
with its file picker and /review/[record], and the /applied, /project
and /subscribed followups (now /received/<need>; the unsubscribe move
is /decide/subscribe). The pins move to v0.3.42: `invite:` and
`attended_by` need a portal newer than the v0.3.36 this site runs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Bendik Aagaard Lynghaug
2026-09-23 15:09:48 +02:00
co-authored by Claude Fable 5.1
parent 58225386b0
commit 5b657828e1
29 changed files with 950 additions and 539 deletions
+112 -67
View File
@@ -1,68 +1,113 @@
# 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.<bucket>.<id>) - 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:
# One continuous graph per person, applicant through employee - the
# bucket name stays "applicants" even as a record progresses past the
# applying stage (a deliberate trade: one chain of events per person,
# no identity-linking between separate buckets).
- bucket: applicants
initial: open
states:
open: { event: applied }
invited: { event: invited }
declined: { event: declined }
active: { event: onboarded }
departed: { event: departed }
transitions:
open: [invited, declined]
invited: [active, declined]
active: [departed]
- 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]
# Proposals to change this very repo - the question architecture
# dogfooding its own development. Approval hands off to an n8n
# automation that opens a PR against this repo (see the infrastructure
# repo's development-commit workflow); the lint CI gates the merge.
- bucket: development_proposals
initial: open
states:
open: { event: proposed }
approved: { event: approved }
rejected: { event: rejected }
transitions:
open: [approved, rejected]
- bucket: applicants
initial: open
states:
active:
event: onboarded
declined:
event: declined
departed:
event: departed
invited:
event: invited
open:
event: applied
transitions:
active:
- departed
- declined
invited:
- active
- declined
open:
- invited
- declined
- bucket: custody_requests
initial: open
states:
declined:
event: declined
in_dialogue:
event: in_dialogue
open:
event: received
resolved:
event: resolved
transitions:
in_dialogue:
- resolved
- declined
open:
- in_dialogue
- declined
- bucket: development_proposals
initial: open
states:
approved:
event: approved
open:
event: proposed
rejected:
event: rejected
transitions:
open:
- approved
- rejected
- bucket: organizations
initial: prospect
states:
client:
event: became_client
past_client:
event: churned
prospect:
event: identified
transitions:
client:
- past_client
prospect:
- client
- past_client
- bucket: projects
initial: open
states:
accepted:
event: accepted
declined:
event: declined
open:
event: submitted
transitions:
open:
- accepted
- declined
- bucket: subscribers
initial: open
states:
open:
event: subscribed
unsubscribed:
event: unsubscribed
transitions:
open:
- unsubscribed
- attended_by: |
approved -> previewed: the n8n workflow that builds the draft site from an approved intake (docker, rauthy, portal content) and sends the preview link. Until it exists, owners run portal-trainer by hand and press the button.
bucket: trials
initial: open
states:
approved:
event: approved
declined:
event: declined
open:
event: received
previewed:
event: previewed
transitions:
approved:
- previewed
- declined
open:
- approved
- declined