Actor state machines: employee lifecycle, investors, organizations wired, development proposals
Lint and reload / lint-and-reload (push) Successful in 2s

- applicants grows past the one decision: invited -> active (onboarded)
  -> departed, one continuous graph per person.
- investors: open -> in_dialogue -> committed -> divested, with decline
  edges; submission via a new "I want to invest" alternative on /.
- organizations (prospect -> client -> past_client) finally gets pages:
  a review listing with its transitions and a record-a-prospect form.
- development_proposals: the question architecture dogfooding its own
  development - /develop-proposal takes proposals from anyone (human
  today, an LLM later through the same public form), /develop lets an
  owner approve or reject, and approval hands off to the automation
  that opens the PR (see infrastructure's development-commit workflow).
  The repo's own lint CI gates the merge, same as any human commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Bendik Aagaard Lynghaug
2026-08-12 22:04:11 +02:00
co-authored by Claude Sonnet 5
parent 7a6cfb5acb
commit 8d10e99726
7 changed files with 239 additions and 0 deletions
+34
View File
@@ -10,14 +10,22 @@
# 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
@@ -45,3 +53,29 @@ aggregates:
transitions:
prospect: [client, past_client]
client: [past_client]
- bucket: investors
initial: open
states:
open: { event: expressed_interest }
in_dialogue: { event: entered_dialogue }
committed: { event: committed }
declined: { event: declined }
divested: { event: divested }
transitions:
open: [in_dialogue, declined]
in_dialogue: [committed, declined]
committed: [divested]
# 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]
+41
View File
@@ -0,0 +1,41 @@
id: /develop-proposal
route: ^/develop-proposal
name: Propose a change
description: >
This site is built on the same question architecture you're using
right now — and changes to it are proposed through it. Describe what
should change and why; an owner reviews every proposal, and approved
ones become real commits.
alternatives:
- name: What should change?
description: >
The proposed content replaces (or creates) one file in the
questions repo, verbatim — so it has to be complete, valid YAML
for that file, not a diff.
action: /proposed
consequence: [Propose it]
record_as: development_proposals
encouragements:
- Every proposal gets read by a person before anything moves.
features:
- name: Who's proposing?
description: ""
requirements:
- name: submitted_by
label: Name (or what to call you)
- name: email
label: Email
type: email
optional: true
- name: The change
description: ""
requirements:
- name: target_path
label: File path
placeholder: questions/example.yaml
- name: rationale
label: Why this change?
type: textarea
- name: proposed_yaml
label: Proposed file content (complete YAML)
type: textarea
+26
View File
@@ -0,0 +1,26 @@
id: /develop
route: ^/develop
name: Development proposals
qualifies: owners
description: >
Proposed changes to the questions repo, awaiting a decision.
Approval hands off to the automation that opens the pull request —
lint gates the merge, not this page.
alternatives:
- name: Proposals
description: ""
action: /develop
consequence: [Confirm]
features:
- name: ""
description: ""
resource:
source:
kind: kv
bucket: development_proposals
requires_group: owners
transitions:
- to: approved
label: Approve
- to: rejected
label: Reject
+26
View File
@@ -64,6 +64,32 @@ alternatives:
label: Email
type: email
- name: I want to invest
description: >
We grow deliberately — distributed communication architecture,
built to last. If that's where you want your capital working,
let's talk.
action: /invested
consequence: [Open the conversation]
record_as: investors
encouragements:
- Serious inquiries get a serious answer.
features:
- name: Who are you?
description: ""
requirements:
- name: name
label: Name
- name: email
label: Email
type: email
- name: What brings you here?
description: ""
requirements:
- name: motivation
label: Why redoal, and what are you looking for?
type: textarea
- name: I want in
description: >
We onboard collaborators the same deliberate way we build —
+16
View File
@@ -0,0 +1,16 @@
id: /invested
route: ^/invested
name: What happens now?
description: >
Your interest is on its way to a real inbox. Expect a considered,
personal reply — not a pitch deck on autopilot.
alternatives:
- name: In short
description: ""
features:
- name: We read it
description: In full, before we write back.
- name: Dialogue before commitment
description: >
We'll want to understand each other properly first — what
you're looking for, and whether we're actually it.
+17
View File
@@ -0,0 +1,17 @@
id: /proposed
route: ^/proposed
name: Proposal received
description: >
It's in the queue. An owner reads every proposal in full — approved
ones open a real pull request, and the same checks every human
commit passes gate the merge.
alternatives:
- name: What happens now
description: ""
features:
- name: A person decides
description: Nothing merges on autopilot — approval is a deliberate act.
- name: The linter has no favorites
description: >
Approved or not, your YAML faces the same validation as
everything else in the repo.
+79
View File
@@ -22,6 +22,85 @@ alternatives:
label: Invite
- to: declined
label: Decline
- from: invited
to: active
label: Onboard
- from: invited
to: declined
label: Decline
- from: active
to: departed
label: Depart
- name: Investors
description: ""
action: /review
consequence: [Confirm]
features:
- name: ""
description: ""
resource:
source:
kind: kv
bucket: investors
requires_group: owners
transitions:
- to: in_dialogue
label: Open dialogue
- to: declined
label: Decline
- from: in_dialogue
to: committed
label: Commit
- from: in_dialogue
to: declined
label: Decline
- from: committed
to: divested
label: Divest
- name: Organizations
description: >
Client relationships as tracked state — advanced as the
relationship becomes real.
action: /review
consequence: [Confirm]
features:
- name: ""
description: ""
resource:
source:
kind: kv
bucket: organizations
requires_group: owners
transitions:
- from: prospect
to: client
label: Becomes client
- from: prospect
to: past_client
label: Archive
- from: client
to: past_client
label: Churn
- name: Record a prospect
description: ""
action: /review
consequence: [Record]
record_as: organizations
features:
- name: ""
description: ""
requirements:
- name: name
label: Organization
- name: contact
label: Contact
- name: note
label: Anything worth remembering
type: textarea
optional: true
- name: Projects
description: ""