Technical Architecture
Pre-implementation specification for the Daybook and Common Work systems. This document defines constants, data schema, state machines, business logic, and cross-system bindings. It is technology-agnostic — no database engine, API protocol, or language is assumed.
These two systems form the cooperative's accounting layer: Common Work tracks what the group is doing and how work flows; the Daybook records what individuals have done and observed. Together they produce the audit trail needed for patronage accounting under Subchapter K and RegenHub's member capital account rules.
Agents prepare. People land. No entry moves from private to public, no matter closes, and no work item resolves without a named human performing that action. This constraint is architectural, not advisory.
System map
The Daybook records individual events. Common Work tracks collective work items. A Contribution entry links a person's action to a work item. A Clearing entry closes a matter or resolves a work item. The systems share no tables but reference each other by ID.
Daybook
→ WorkItem.id
→ Matter.id
→ Entry.id[]
Common Work
Shared primitives
Both systems use these base types. Implementations may alias these to native types; the semantics are fixed.
// Scalar types shared across both systems type UUID = string // RFC 4122 v4; canonical form with dashes type Timestamp = string // ISO 8601 UTC, e.g. "2026-06-26T17:30:00Z" type MemberId = UUID // references a registered cooperative member type EntryId = UUID // primary key for a Daybook entry type MatterId = UUID // primary key for an open Daybook matter type WorkId = UUID // primary key for a Common Work item // Ripe score: integer percentage, 0 = just opened, 100 = ready to land type RipeScore = number // constrained to 0 ≤ n ≤ 100; integer only
| Constraint | Rule |
|---|---|
| UUID generation | v4 random; never v1 time-based (leaks server MAC) |
| Timestamps | UTC always; local display is a presentation concern |
| MemberId scope | Must exist in the cooperative's member registry before use |
| Deleted records | Neither system uses hard deletes; see composted and visibility |
Constants
DaybookKind
Four kinds. Three require membership. One is open to observers.
const DAYBOOK_KINDS = [ 'reflection', // witness and perception; open to observers 'contribution', // labor, skill, resource given; members only 'exchange', // goods or services traded; members only 'clearing', // debt / credit resolved; members only ] as const type DaybookKind = typeof DAYBOOK_KINDS[number]
Witness and perception. What was noticed, felt, or understood. Not a claim on resources. Can be made private.
Labor, skill, material, or resource given to the cooperative. The primary input for patronage accounting.
A transfer of goods or services between members or between the cooperative and the outside world.
Settlement of a debt, credit, or obligation. Closes open matters. May resolve work items in Common Work.
Visibility states
const VISIBILITY_STATES = ['private', 'public'] as const type Visibility = typeof VISIBILITY_STATES[number] // Transition: private → public is one-way and requires a human action. // There is no mechanism to move a public entry back to private. // (Correction: a new Clearing entry supersedes; the original stays.)
Membership gate
const MEMBERSHIP_GATE: Record<DaybookKind, boolean> = { reflection: false, // observer may write contribution: true, // member required exchange: true, // member required clearing: true, // member required } // "Member" means: registered, dues current, no suspension. // "Observer" means: registered with name, not yet a member, or // not logged in (read-only view of public entries only).
Ripe threshold
const RIPE_THRESHOLD = 100 // score at which a matter is ready to land const RIPE_FLOOR = 0 // minimum; newly opened matters start here // Ripe score is an integer. Non-integer inputs must be floored. // Score may not decrease once set (matters do not un-ripen). // Score reaching RIPE_THRESHOLD does not auto-land the matter — // it only signals readiness. A human must still act.
Schema
Entry
The atomic unit of the Daybook. Every recorded event is an entry.
interface Entry { id: EntryId // required; system-generated on create kind: DaybookKind // required; immutable after creation author: MemberId // required; who wrote this created_at: Timestamp // required; system-set on create body: string // required; the content of the entry visibility: Visibility // required; starts 'private', may go 'public' published_at: Timestamp | null // set when visibility flips to 'public' published_by: MemberId | null // must equal author (self-publish only) matter_ref: MatterId | null // if this entry addresses an open matter work_ref: WorkId | null // contribution entries should reference work supersedes: EntryId | null // if correcting a prior entry }
| Field | Type | Constraint |
|---|---|---|
| id | EntryId | System-generated; never supplied by client |
| kind | DaybookKind | Immutable after creation; check MEMBERSHIP_GATE on write |
| author | MemberId | Must exist in member registry; must satisfy MEMBERSHIP_GATE[kind] |
| body | string | Min 1 char; no max defined (practical limit TBD) |
| visibility | Visibility | Starts 'private'; transition to 'public' is one-way |
| published_by | MemberId | null | Must equal author; agents may not publish on behalf |
| matter_ref | MatterId | null | If set, must reference an existing, unlanded matter |
| work_ref | WorkId | null | If kind is 'contribution', strongly recommended |
| supersedes | EntryId | null | If set, prior entry is marked superseded but remains in log |
Matter
An open question that must be decided by a named person. Matters exist in the Daybook's second layer — visible as outline petals in the bloom until landed.
interface Matter { id: MatterId // required; system-generated kind: DaybookKind // required; sets bloom petal color heading: string // required; the open question ripe: RipeScore // required; starts 0, rises toward 100 awaits: string // required; the decision or action needed face: string // required; name of person who must land face_member_id: MemberId // required; machine-readable identity of face cite: string // optional; bylaw / PRD / agreement reference act: string // required; describes the landing action opened_at: Timestamp // required; system-set opened_by: MemberId // required; who raised this matter landed_at: Timestamp | null // set when landed landed_by: MemberId | null // must equal face_member_id landing_entry: EntryId | null // the Clearing entry that closed it }
| Field | Type | Constraint |
|---|---|---|
| heading | string | Should be phrased as a question or open statement |
| ripe | RipeScore | Integer 0–100; may only increase; never decreases |
| face | string | Human-readable name; display label for face_member_id |
| face_member_id | MemberId | Must match an active member; checked at landing |
| landed_by | MemberId | null | Must equal face_member_id; agents may not land matters |
| landing_entry | EntryId | null | Must be a Clearing entry by the same author as landed_by |
Logic
Entry state machine
Landing rule
A matter lands if and only if: (1) the requesting actor's MemberId equals the matter's face_member_id, and (2) the actor is a human operating the system directly (not an agent acting on their behalf), and (3) a valid Clearing entry is submitted simultaneously by the same actor.
actor.id === matter.face_member_id
AND actor.type === 'human'
AND entry.kind === 'clearing'
AND entry.author === actor.id
AND entry.visibility === 'public' // clearing entries must be public
AND matter.landed_at === null // already landed matters cannot be re-landed
actor.id === entry.author
AND actor.type === 'human'
AND entry.visibility === 'private' // already-public entries: no-op
Ripening
Ripe score expresses how close a matter is to being ready to land. The mechanism for advancing it is deliberately left open — different matters may ripen by different signals (votes, time elapsed, entries added). What is fixed:
newScore === Math.floor(newScore) // integers only
AND newScore >= matter.ripe // score may not decrease
AND newScore >= RIPE_FLOOR // 0
AND newScore <= RIPE_THRESHOLD // 100
AND matter.landed_at === null // landed matters are frozen
A matter reaching ripe === 100 does not automatically land. It becomes landable. The landing action remains a human decision.
Bloom rendering constants
The bloom is a clock-face SVG with one petal per entry kind per time segment. Solid petals are public entries. Outline petals with a fill bar are open matters (fill % = ripe score). These are presentation constants only.
const BLOOM_PETALS = 12 // clock positions, one per hour segment const PETAL_RADIUS = 80 // SVG units from center to petal tip const PETAL_OPEN_PCT = 35 // % of PETAL_RADIUS for open-matter fill base const PETAL_COLORS: Record<DaybookKind, string> = { reflection: '#2563eb', // blue contribution: '#d97706', // gold exchange: '#9333ea', // violet clearing: '#16a34a', // green } // Solid petal: fully colored, opacity 0.85, stroke none // Open-matter petal: stroke only (1.5px), fill = color at (ripe/100)*opacity // Current-time marker: thin radial line at clock position for current hour
Constants
Stages
const STAGES = [ 'captured', // noticed; not yet offered to the group 'offered', // proposed for collective attention 'held', // assigned; someone is working it 'standing', // work done; standing for review 'socialized', // reviewed and acknowledged by at least one other 'resolved', // formally complete; human landing required 'composted', // intentionally retired without resolution ] as const type Stage = typeof STAGES[number]
Stage colors
const STAGE_COLORS: Record<Stage, string> = { captured: '#b45309', // amber-700 offered: '#d97706', // amber-500 held: '#ea580c', // orange-600 standing: '#dc2626', // red-600 socialized: '#9333ea', // violet-600 resolved: '#2563eb', // blue-600 composted: '#52525b', // zinc-600 }
WorkKind
const WORK_KINDS = [ 'task', // discrete action to be done 'decision', // something the collective must decide 'question', // inquiry that needs resolution 'project', // multi-step container; may have child tasks ] as const type WorkKind = typeof WORK_KINDS[number]
Schema
WorkItem
interface WorkItem { id: WorkId // required; system-generated title: string // required; short human-readable name description: string // required; full description of the work kind: WorkKind // required; immutable after creation stage: Stage // required; current stage created_by: MemberId // required; original author created_at: Timestamp // required; system-set assignee: MemberId | null // set when stage reaches 'held' parent_id: WorkId | null // for child tasks within a project transitions: StageTransition[] // full stage history; append-only daybook_entries: EntryId[] // linked Daybook entries; audit trail resolved_at: Timestamp | null // set when stage reaches 'resolved' resolved_by: MemberId | null // the human who landed it composted_at: Timestamp | null // set if composted instead of resolved composted_by: MemberId | null // coordinator who retired it compost_reason: string | null // required when composted }
StageTransition
interface StageTransition { id: UUID // required; system-generated work_id: WorkId // required; parent item from: Stage | null // null for initial capture (no prior stage) to: Stage // required; target stage by: MemberId // required; who made this move at: Timestamp // required; system-set rationale: string | null // optional; required for compost transitions entry_ref: EntryId | null // Daybook entry accompanying this move }
| Field | Constraint |
|---|---|
| from | Must match WorkItem.stage at time of transition; null only for initial capture |
| to | Must be a valid next stage per the advancement rules below |
| by | Must satisfy the actor rule for this transition (see advancement rules) |
| rationale | Required when to === 'composted' |
| entry_ref | If set, the Daybook entry must reference this work_id |
Logic
Stage state machine
resolved and composted are terminal. A work item in either stage accepts no further transitions. Any attempt to advance or return a terminal item must be rejected.
Advancement rules
| Transition | Who may act | Condition |
|---|---|---|
null → captured |
Any member | Initial creation; no prior stage |
captured → offered |
Author or any member | Work is ready for collective attention |
offered → held |
Assignee (self-claim) or coordinator | Someone takes responsibility; assignee must be set |
held → standing |
Assignee only | Assignee signals work is done and ready for review |
standing → held |
Assignee or coordinator | Review found blocking issues; item returns to work |
standing → socialized |
Any member except assignee | At least one other member reviews and acknowledges |
socialized → resolved |
Named human (coordinator or designated face) | Human landing required; should accompany a Clearing entry |
any → composted |
Coordinator | rationale required; no Daybook entry required but recommended |
to !== item.stage // must actually change
AND NOT isTerminal(item.stage) // terminal stages are frozen
AND isValidTransition(item.stage, to)// must follow the allowed graph
AND actorSatisfiesRule(item, actor, to) // actor has permission for this move
stage === 'resolved' OR stage === 'composted'
ALLOWED_TRANSITIONS[from].includes(to)
const ALLOWED_TRANSITIONS: Record<Stage, Stage[]> = { captured: ['offered', 'composted'], offered: ['held', 'composted'], held: ['standing', 'composted'], standing: ['held', 'socialized', 'composted'], socialized: ['resolved', 'composted'], resolved: [], // terminal composted: [], // terminal }
Coordinator role
Certain transitions require a "coordinator." The coordinator role is not modeled as a static attribute — it is a capability check at transition time. Its definition is an open question (see below), but the required interface is:
interface CoordinatorCheck { isCoordinator(member: MemberId, context: WorkItem): boolean } // Implementations may define coordinator as: board role, sprint steward, // designated work-item owner, or by bylaw resolution. Not specified here.
Bindings
The systems reference each other by ID. There is no foreign key enforcement at the data layer — binding integrity is a business logic concern.
Contribution → WorkItem
entry.work_ref SHOULD be set to the WorkId of the item being contributed to. The corresponding WorkItem.daybook_entries array SHOULD include this EntryId. Both sides of this reference must be kept consistent.
Clearing entry → Matter landing
entry.matter_ref pointing to the matter. The landing action sets matter.landed_at, matter.landed_by, and matter.landing_entry atomically with the entry's visibility being set to 'public'. These three mutations must be a single atomic operation.
Clearing entry → WorkItem resolution
entry.work_ref pointing to the work item. The item's stage transitions to 'resolved', resolved_at and resolved_by are set, and the entry is appended to daybook_entries. These mutations must be atomic.
Binding invariants
| Reference | Integrity rule |
|---|---|
Entry.work_ref |
If set, must reference an existing, non-composted WorkItem |
Entry.matter_ref |
If set, must reference an existing, unlanded Matter |
WorkItem.daybook_entries |
All IDs must reference existing, public Entries that reference this WorkId |
Matter.landing_entry |
Must reference a public Clearing entry authored by landed_by |
StageTransition.entry_ref |
If set, must reference an entry with work_ref === work_id |
Invariants
These must hold at all times across both systems. An implementation that violates any of these is incorrect.
No Entry may transition from private to public without a human actor. No Matter may land without a human actor. No WorkItem may reach resolved without a human actor. Agent-initiated calls to any of these transitions must be rejected.
A public Entry's body, kind, and author are immutable. A landed Matter's landed_at, landed_by, and landing_entry are immutable. A terminal WorkItem accepts no further transitions. Corrections are made by new entries with supersedes references.
An Entry of kind contribution, exchange, or clearing may only be authored by an active cooperative member. This check must occur at write time. A member who loses good standing after writing an entry does not retroactively invalidate that entry.
A Matter's ripe score may only increase. Any attempted decrease must be rejected. The score is frozen once the matter is landed.
Every stage a WorkItem has occupied must be represented in its transitions array. The current stage must always equal the to field of the most recent StageTransition. There must be no gaps or unexplained jumps in the transition history.
When a Clearing entry both publishes an entry and lands a matter (or resolves a work item), both mutations must succeed or both must fail. Partial application is not permitted.
A WorkItem may only advance from standing to socialized if the actor is a member other than the current assignee. Self-socialization is not permitted.
Open questions
These are architectural decisions deferred to implementation or board resolution. They are flagged here so they are not accidentally assumed.
| # | Question | Impact |
|---|---|---|
| Q1 | What defines the coordinator role? Board appointment, bylaw section, or runtime assignment per work item? | Blocks implementation of isCoordinator() |
| Q2 | What is the mechanism for ripening a matter? Who may call setRipe()? Is it member vote, elapsed time, or agent-assessed signal? |
Blocks Matter lifecycle implementation |
| Q3 | May a Daybook entry reference both a matter_ref and a work_ref simultaneously? |
Affects Entry validation rules |
| Q4 | What is the patronage accounting formula? How are Contribution entries aggregated into member capital account credits? | Blocks patronage reporting |
| Q5 | Can a WorkItem have child items (project → tasks)? If so, does a project resolve when all children resolve, or does it require its own explicit resolution? | Affects parent_id semantics |
| Q6 | What constitutes "agent type === human" in the system? OAuth session, hardware token, signature, or something else? | Critical path for INV-01 enforcement |
| Q7 | May a private Reflection be permanently deleted by its author, or is it retained but hidden? | Affects member data rights and audit trail |
Techne Studio · RegenHub, LCA · Boulder, Colorado
Pre-implementation architecture · Last revised 2026-06-26
Not a legal document. Schema and logic subject to board resolution before implementation.