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.

Human landing rule — applies to both systems

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

Entry (Reflection)
Entry (Contribution)
Entry (Exchange)
Entry (Clearing)
Matter (open)
Matter (landed)

Common Work

WorkItem (captured)
WorkItem (offered)
WorkItem (held)
WorkItem (standing)
WorkItem (socialized)
WorkItem (resolved)
WorkItem (composted)

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
ConstraintRule
UUID generationv4 random; never v1 time-based (leaks server MAC)
TimestampsUTC always; local display is a presentation concern
MemberId scopeMust exist in the cooperative's member registry before use
Deleted recordsNeither 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]
Reflection
observermember

Witness and perception. What was noticed, felt, or understood. Not a claim on resources. Can be made private.

Contribution
member only

Labor, skill, material, or resource given to the cooperative. The primary input for patronage accounting.

Exchange
member only

A transfer of goods or services between members or between the cooperative and the outside world.

Clearing
member only

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
}
FieldTypeConstraint
idEntryIdSystem-generated; never supplied by client
kindDaybookKindImmutable after creation; check MEMBERSHIP_GATE on write
authorMemberIdMust exist in member registry; must satisfy MEMBERSHIP_GATE[kind]
bodystringMin 1 char; no max defined (practical limit TBD)
visibilityVisibilityStarts 'private'; transition to 'public' is one-way
published_byMemberId | nullMust equal author; agents may not publish on behalf
matter_refMatterId | nullIf set, must reference an existing, unlanded matter
work_refWorkId | nullIf kind is 'contribution', strongly recommended
supersedesEntryId | nullIf 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
}
FieldTypeConstraint
headingstringShould be phrased as a question or open statement
ripeRipeScoreInteger 0–100; may only increase; never decreases
facestringHuman-readable name; display label for face_member_id
face_member_idMemberIdMust match an active member; checked at landing
landed_byMemberId | nullMust equal face_member_id; agents may not land matters
landing_entryEntryId | nullMust be a Clearing entry by the same author as landed_by

Logic

Entry state machine

Visibility
private
──── author publishes ────›
public
✕ no return
Matter
open
──── face_member lands ────›
landed
✕ immutable
Entry body
draft
──────────────────────────›
saved
✕ body is immutable

Landing rule

Invariant DB-L1 — The 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.

canLand(matter, actor, entry):
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
canPublish(entry, actor):
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:

setRipe(matter, newScore):
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
Invariant DB-L2 — Ripe does not auto-land

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]
capturednoticed
offeredproposed
heldin work
standingfor review
socializedacknowledged
resolvedcomplete
compostedretired

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
}
FieldConstraint
fromMust match WorkItem.stage at time of transition; null only for initial capture
toMust be a valid next stage per the advancement rules below
byMust satisfy the actor rule for this transition (see advancement rules)
rationaleRequired when to === 'composted'
entry_refIf set, the Daybook entry must reference this work_id

Logic

Stage state machine

Primary arc
captured
offered
held
standing
socialized
resolved
Exit arc
any stage
──── coordinator ────›
composted
✕ terminal
Return arc
standing
──── blocked ────›
held
(only allowed return)
Invariant CW-L1 — Terminal stages

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

TransitionWho may actCondition
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
canAdvance(item, actor, to):
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
isTerminal(stage):
stage === 'resolved' OR stage === 'composted'
isValidTransition(from, to):
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

When a member makes a Contribution Daybook entry, 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

To land a matter, the actor submits a Clearing entry with 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

To resolve a work item, the actor submits a Clearing entry with 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

ReferenceIntegrity 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.

INV-01 — Human landing

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.

INV-02 — Immutability of settled records

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.

INV-03 — Membership gate enforcement

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.

INV-04 — Ripe monotonicity

A Matter's ripe score may only increase. Any attempted decrease must be rejected. The score is frozen once the matter is landed.

INV-05 — Transition log completeness

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.

INV-06 — Atomic cross-system mutations

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.

INV-07 — Socialization threshold

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.

#QuestionImpact
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.