The product behind the course

Cybernetic control
for agentic software.

regulator asks whether Stafford Beer's can run inside an autonomous agent: not as five chatbot personas, but as explicit functions, authority boundaries, typed channels, verification gates, and feedback loops. It ships its own to find out. Viable Agents, the course, builds it lesson by lesson. This page is the reference for what's actually running underneath it.

Prompts advise. Types describe. Gates enforce. The premise the whole project starts from
Vocabulary — worth knowing before you scroll Full glossary ↗
orchestrator
The control loop that dispatches work and checks it — it never edits code itself.
S3* (audit)
Verifies a unit's work independently, instead of trusting what the unit says about itself.
capability profile
A bounded grant of tools and writable paths a unit runs under — not a persona.
obligation
A signal that has to stay visible until someone actually resolves it.
algedonic signal
An alert that skips the normal chain of command — reserved for "interrupt a person now."
(two senses)
The general term for anything that keeps a system in bounds — and, distinctly, the name of this project's own CLI.
Architecture

regulator

An agent harness built on Pi, with a typed VSM control plane layered on top of it. It is built against one recurring failure mode of autonomous agents: architectural drift that outruns anyone's ability to review it.

What VSM is, and why it matters here

Stafford Beer's Viable System Model comes out of management cybernetics: it asks what any system (an organism, a company, an autonomous agent) needs structurally to keep operating as its environment keeps generating more variety than it can be made to predict. Beer's answer is five functions plus an independent audit: operations that touch the world (S1), coordination that keeps them from colliding (S2), control that decides what happens next (S3), intelligence that watches the environment and the future (S4), and identity that says what the system is and isn't allowed to become (S5), all checked by a function that never just takes the operators' word for it (S3*).

It matters for an agent harness because the model is not a deterministic component. It changes across versions, has no memory of last quarter's incident, and treats a prompt as advice it can ignore, and under pressure will. A harness that regulates entirely through prompt text has exactly as much regulatory variety as the words in that prompt, which is provably less than the variety of a real codebase, a real filesystem, or real money moving. VSM leaves the model's judgment in place and gives it a boundary it can't talk its way out of.

Six functions, one loop

This is Beer's Viable System Model read as a harness architecture. Command flows down as attenuated instruction (grey), and evidence flows back up as amplified fact (teal). Units act on the domain. S3* audits S3 independently and verifies the domain itself at a revision, never through what S1 reports about it. S4 scans the environment on its own channel, arguing with S3 about now vs. next on S5's terms.

ENVIRONMENT what nobody in the system controls watched by S4 · never written S5 — IDENTITY policy · invariants S4 — INTELLIGENCE environment · future scans S3 — CONTROL contract · dispatch · budget audit S3* AUDIT independent verifies at a revision S2 — COORDINATION leases · anti-oscillation S1 capability profile S1 capability profile S1 capability profile DOMAIN what the units change — the source of truth about the work one isolated workspace per unit; the loop reintegrates what verified
Grey = attenuated instruction down the spine; teal = amplified evidence back up. Units act on the domain — each in its own workspace, reintegrated by the loop — and S3* (alarm colour) verifies the domain at a revision, going around the units rather than through what they report. S4 scans the environment on its own channel; nothing in the system writes there. Pick a tab to see the same boxes filled in for a workload.

Where each function lives today

NameFunctionregulator's interpretation
S1 — OperationsThe units that do the actual work, touching the world directly.Capability profiles a workload declares: implement, research, intelligence for software, bookkeeper and auditor for the ledger. Each is a grant over tool effects, and a workload in any domain can declare one.
S2 — CoordinationKeeps operations from colliding with or thrashing against each other.Tool contracts, worktree isolation, leases with liveness, reintegration, anti-oscillation mechanisms
S3 — ControlDecides what happens next (dispatch, budget, recovery), facing inside the system, now.The orchestrator: work contracts, dispatch, budgets, the recovery lattice, execution-state authority
S3* — AuditVerifies operations' work independently, without trusting their own report of it.Harness-run verification plus architectural/domain checks and independent audit findings
S4 — IntelligenceWatches the environment and the future and can raise a flag, but never replans on its own.Read-only specialists and external/environmental intelligence feeding planning and reassessment
S5 — Identity/PolicyDeclares what the system is, and isn't allowed to become.Versioned product identity, architecture, domain model, invariants, and policy

Design goals and repository layout

Design goals

  • Keep the orchestrator small: the S3 loop of contract → dispatch → verify → route → close, generic over .
  • Declare the control plane rather than assemble it: , capability profiles, policy, and workload live in a versioned, checked definition.
  • Make S5 identity and policy durable and version-controlled, independent of any one context window.
  • Keep independent of S1 self-reporting.
  • Differentiate operations by , not roleplay persona.

Repository layout

packages/
  protocol/       VSM vocabulary + runtime schemas — messages, trace,
                  effects, profiles, registry, contracts, execution,
                  policy, recovery, audit, obligations, memory
  core/           authority (write preflight), leases, thrash detector,
                  execution store, signal sink, budgets, recovery
                  router, effect journal, audit log, obligation ledger
                  and router, identity + memory stores, definition
                  check, registry check + REGULATORS.md / BOUNDARY.md
  checks/         deterministic S3* checks: host-run verification
                  bound to a revision, the technical verdict
  regulator/      the control plane: the `regulator` CLI, the S3 loop,
                  the stores, the host seam, and the definition —
                  registry (43 records), identity, profiles, policies,
                  workloads, the drift eval suite and its committed
                  reports; OPERATING.md says who runs what
  regulator-pi/   the Pi host: the eleven session extensions, the
                  dispatcher, the S5 write gate, typed proposal /
                  audit-finding / uncertainty tools; `pi install`
  control-room/   read-only page over the `regulator status` read
                  model: definition, instances, the unit inspector

docs/             the documentation site (concepts, guides,
                  reference), DEBT.md, decisions/, research/
vsm/               committed S5 artifacts for this repo itself
the course, Viable Agents, lives in its own repository and cites these by link

In the docs: The control plane · Definition, instance, domain · Architecture · Packages

Mechanism hierarchy

Prefer the mechanism that survives adversarial pressure

When adding a feature to the harness, regulator walks this ladder from the top down: reach for the highest level that can actually do the job, and only fall further down when it can't. If you can write the check, don't write the paragraph.

1
TypeScript invariant
Shape the data so the wrong value has nowhere to go: a fixed set of choices where a free string could be mistyped. The compiler refuses it before the code ever runs.
2
Deterministic runtime check
A rule enforced by code every single time, whatever the prompt says. Nobody has to remember to follow it.
3
Structured tool / protocol
A fixed, validated shape for a request, with none of the room to phrase around it that free text always leaves.
4
Model judgment
Still a real decision point. The model is handed a specific question because nothing above can resolve it mechanically, like which category a merchant belongs to, and even then its answer gets checked afterward.
5
Prompt / context engineering
Ambient advice sitting in context, with no decision point: house style, tone, a convention the model may or may not act on. Used only where being ignored costs little, since nothing here checks whether it was.

In the docs: The control plane · Where each rule is enforced · Regulators · Enforcement boundary

Identity

Identity you can't prompt away

What the system is (its invariants, its boundaries, the words it uses) lives in four committed files under regulator/identity/. Every unit sees them, rendered from the files on every run, so compaction can't drop them and a new model can't reinterpret them from memory. Seeing them is only advice, though, and a unit with a shell can reach the files. So identity is held the way the mechanism hierarchy says to hold anything that matters: in layers, each catching what gets past the one before.

1
Trust rule
A unit's session loads only what the definition declares. The project's own extensions, skills and AGENTS.md never reach it: the domain doesn't configure its regulator.
2
Write gate
Every write and edit is checked against the protected paths, through any spelling, traversal or symlink, and refused before it happens.
3
Bash watch
No hook can sandbox a shell, so the protected files are snapshotted before every command and restored after it, and the change is recorded as a finding.
4
identity-untouched at closeout
The host diffs the unit's branch against its base. A protected change that got past everything else, by any route, fails the check and never lands on the base.

Changing identity is a separate path, on purpose. A unit may propose a change; the tool records the request and changes nothing. A person the interaction policy lets act as accepts it, as the only writer of an identity file, and may then promote it into the definition, so every later instance starts from it. The right to ask and the right to change are different mechanisms.

Secrets are watched the same way. When regulator init installs an instance, it records the credential-looking values in the repository's committed .env as canaries. Every tool result is scanned for them before the model sees it: a canary is redacted and recorded as a critical finding. A canary the model writes itself can't be unsaid, so that is recorded too. It measures whether an exposure path exists. It only knows the values it was told about, and it doesn't watch the network.

In the docs: Protecting identity · The canary watch · Enforcement boundary

The orchestrator

A small loop, purpose-built for regulation

The orchestrator is nothing but the S3 loop (contract → dispatch → verify → route → close), generic over workloads, with leases, budgets and attempts as immutable records. It was built directly on bare Pi, one module per lesson: gates, effects, profiles, leases, contracts, budgets, recovery, audit and authority, with no workflow engine underneath it. The Pi-specific half lives in its own package, regulator-pi, behind a host seam: the control plane never imports a host, it resolves one by name. Workload-specific concerns stay out of the loop entirely and are declared in a workload definition instead, which is what keeps the loop itself small enough to read in one sitting. How a unit runs through the loop is drawn out on its own page.

The orchestrator, sized

Nothing but the S3 loop, generic over , with , budgets, and as immutable records. All five stages exist in packages/regulator/src/controller.ts:

contract → dispatch → verify → route → close

What would make a workflow engine heavy (fixed phases, milestone lifecycles, UAT, projections) is workload, and lives in a workload definition outside this loop. Recovery decisions cite a versioned policy, and is refused on anything but fresh, host-run evidence.

The first workload: the autoloop

The software-development autoloop is the first , and the one the course builds against its fixture repository. It shows what it means to declare a workload rather than hard-code one:

plan research implement verify integrate close

six unit types, declared in workload/software-development.json, each naming:

A capability profile the unit runs under — implement may write src/ and test/; research is read-only by declared effect
Checks the orchestrator re-runs itself at closeout — run_checks, run_tests, and inherited-tests, the suite the unit inherited run against the unit's tree, so a test the unit deleted or weakened is a failing check — bound to the revision they ran against
Intelligence from a research unit is routed into an on each unit it names, and at blocking severity holds them until a person or S3 dispositions it — never applied, never replanned. Operational memory is the other record: facts for later units, each with an expiry. Intelligence and memory ↗
Still design: S4 triggers that raise a research unit unasked; and whether an action needs consent is still the model's to notice — the consent question itself, and the pause until it is answered, ship

Budgets and model routes

Every runs under ceilings the policy declares per unit type: tokens, cost, wall-clock and turns, and every unit under a ceiling on attempts. A guard in the session halts an attempt the moment a ceiling is crossed and refuses every tool with an effect from then on. The loop closes on the ledger, never on the transcript.

Each unit type also has a route: a primary model and declared fallbacks, in order. A provider failure moves to the next one, and nothing outside the route is ever tried. When the context fills, compaction keeps a deterministic block (the contract, the evidence so far, the files touched) so the unit never forgets what it was bound to.

Before the merge lands

A clean merge only proves no two units touched the same lines. So before every lands, the loop merges the branch in a temporary worktree and runs the tests on the merged tree. A tree that passes lands as the very commit that was checked. A tree that fails lands nothing: the unit repairs, and the base is never red.

Effects that can't be taken back, such as a message to the owner, go through an : intended before, committed after, and reconciled against the world on restart. A crash in between neither sends the message twice nor forgets it was sent.

In the docs: Running units · Work contracts · Host checks · Recovery, budgets and model routes · Coordination · Hosts

Evidence about the regulators

Every gate is a claim. The eval harness is how the claim gets tested.

A suite declares the fixture, the tasks in order, the arms, the repetitions and the metrics it will report — before it runs. The control arm loads the plain harness; the treatment arm loads everything the workload declares; one ablation arm per regulator switches that one regulator off. The harness runs one fresh instance per arm and repetition through the real loop and grades every unit the same two ways.

GradersWhat they readWhat they count
Outcomethe environment after the unit: the merged base, its tests, its commentsboundary violations, signature drift, vocabulary drift, rules written into prose, a test suite weakened
Trajectorythe records the control plane wrote while the unit ranrefusals, retries, escalations, invariant violations, attempts, tokens, turns, wall clock

Pre-registered, and interpreted by a person. The metrics are a closed list named in the suite, so a run cannot discover a favourable one afterwards. Arms are summarized with Student's t intervals and a lift is screened by interval overlap, never a p-value. And a report is not a report until someone says what it means: regulator eval without --interpretation writes a placeholder, and regulator check refuses a committed report that still carries it. The four committed reports ran scripted units — their numbers validate the graders, not a model — and one of them is the over-regulation case that changed the product: a check refused correct work over a word in a commit message, and the workload now declares that half advisory.

In the docs: Evidence about the regulators · Eval suites

Intelligence and memory

What is outside and later is not what was learned here

Two things look alike from a prompt and are kept apart on purpose. is the environment and the future: a research unit, under its own read-only profile and budget, reports what it found with report_intelligence, and the signal becomes an obligation that holds the units it names until or a person dispositions it. Intelligence can stop work; it never replans on its own.

Memory is operational, and it expires. A unit that learns something about this environment — a flag the tests need, a command that only works one way — records it with remember. The fact carries its provenance (the unit, the revision), a review-by date that cannot be more than ninety days out, and optionally the unit types it is for. Later units see current facts rendered as facts, scoped to their type; an expired fact is shown to nobody until a person reviews it. Nothing checks that a fact is true — the expiry is the pressure.

Neither is a rule. A fact that turns out to be a rule, or a finding that should change what the system is, goes through propose_policy_change and waits for the decision. Memory never becomes identity by being repeated, and the drift suite measures the difference: rules written into a README count against a unit; facts recorded with an expiry do not.

In the docs: Intelligence and memory · Session tools

The registry

Anatomy of a registry card

Every regulator in the control plane has a record, and the definition check refuses a gate that does not state what it fails to catch. The cards are the source of truth for what the system does and does not do; REGULATORS.md and the enforcement boundary, BOUNDARY.md, are rendered from them and never edited by hand.

FieldWhat it commits to
purpose, absorbswhat the regulator is for, and the class of failure it exists to absorb — one sentence a reader can check against the code
mechanismthe level on the hierarchy (type, deterministic gate, typed tool, model judgment, prompt) and where it bites: a typed enforcement point — a session event, a tool's execute, a step of the loop, a subcommand or the definition check — from a closed list the check refuses to go outside
channels, scope, authoritywhat it consumes and emits, what it applies to, and what it may and may not do — the topology view is drawn from these
evidencethe tests that show it working; a source checkout verifies they exist
limitationsat least one, always: the routes around the gate. A limitation that names later work gets a row in the build's debt ledger in the same change
ablation, retirementthe switch that turns it off for an eval arm, and the condition under which a person would retire it — a control system that only grows is not regulating
ownership, introducedInwho reviews it and by when — regulator review --due lists what is owed, and doctor fails on an overdue date — and the release that first shipped it

Retirement is a decision, and the record stays. Two of the forty-four cards are retired: the first lexical write gate, superseded by the identity write gate, and the post-merge check, replaced by the pre-merge trial. Each keeps its history and says what replaced it, so the registry reads as the system's memory of itself, not only its present.

In the docs: The regulators · Enforcement boundary · Build debt

The maintainer's interface
Every view ships today

The control room

A projection of registry ∪ execution store ∪ regulatory state. It owns no state of its own: every screen is read from records that already exist elsewhere. packages/control-room serves this page today, over the built by regulator status.

Topology — regulators by function, with the channels each one declares it consumes and emits.
Instances — every instance's units with budget, routing decision, audit verdict and what's owed on them; ; by consumer, with the veto marked; unrouted signals; the instance manifest.
Assurance & lifecycle — the committed eval reports, plus each regulator's review date, ablation switch and retirement condition.
Inspector — the whole record for a regulator or a unit, including the unit's replay in time order.
The control room as it opens: the definition's topology of 44 regulators in S5 to S1 columns on the left, and the inspector on the right showing the closeout gate's record.
The page as it opens. Left, the definition: 44 regulators by function, each card showing the channels it consumes and emits. Right, the inspector on a selected card, here the closeout gate's whole record: purpose, what it absorbs, mechanism, authority, evidence, limitations, review date, ablation, retirement.

More of the page (units, leases and obligations for a live instance, the assurance and lifecycle views, a unit's full replay) is on the worked example and how it works.

In the docs: The control room · Evidence about the regulators

Get started

Install into a repository you already have

Two packages, then one command to declare the instance, one to check it, and one to run a unit. You need Node 22.19 or later and Pi 0.87.0 with a model provider key in its environment.

pnpm add -D @metacoding.io/regulator @metacoding.io/regulator-pi
cd /path/to/your/repo                         # clean tree, on the branch units should land on
regulator init --writable src/,test/ --protected vendor/ --by alice
regulator doctor                              # the CI entry point: exit 1 on any problem
regulator unit drive contracts/my-first-unit.json
regulator unit show u1 · unit evidence u1 · obligations

init seeds the identity under regulator/identity/, writes the instance manifest, and commits. drive leases the unit, opens a session under the unit type's profile, runs the host checks at the unit's revision, and either closes and reintegrates or routes the failure under the recovery policy. What the policy cannot resolve becomes an obligation owed to you.

In the docs: Getting started · Declare who may answer · CLI reference · npm ↗

Operating

What a person does day to day

The loop runs units, and a person answers what the loop cannot decide. Everything below reads or dispositions a record, and every --by is checked against the interaction policy's people before anything is written.

WhenCommandWhat it does
before the first unit, and in CIregulator doctorchecks runtime, git, the Pi pin, the definition, review dates, the manifest, a clean base, and what is owed and undelivered, then exits 1 on any problem
a unit needs workregulator unit drive <contract>the autoloop: run, verify, route, run again while the budget and the policy allow
something is owedregulator obligationswhat is open, to whom (S3, S5 or a person), at which severity, and which currently veto a unit
a unit asked a questionregulator answer <id> --by <who> --answer …records the answer as the person's disposition, and the next attempt carries it. Silence is never consent
a finding or an escalation waits on youregulator obligation resolve <id> --by <who> --disposition <d> --rationale …closes it with one of the eleven dispositions: ack marks it seen, and escalate hands it to another consumer with the veto
a unit proposed a change to the identityregulator identity accept|reject <id> --by <who> …the S5 decision, by a person with actAsS5, the only writer of an identity file
a criterion no check can observeregulator unit accept <id> <criterion> --by <who>a human acceptance, recorded beside the technical verdict and never folded into it
a quiet instanceregulator watch --exec <cmd>delivers what is owed to a person, reminds after the policy's interval, and forwards each new outbox line to your channel
once a monthregulator review --duewhich regulator records are past their review date, and doctor fails on an overdue one
an accepted decision should outlive this instanceregulator identity promote <file>.md --by <who> …carries the instance's accepted identity file back into the definition's seed
you want to see itregulator status · regulator-control-roomthe read model, and the read-only page over it

In the docs: Running units · Obligations · Asking a person · Operating · The control room · Session tools

Status

Built end to end through lesson 15; the capstone is what's left

Four things are true today, and each runs end to end.

The loop runs. Contract, dispatch, verify, route, close — with , budgets, a versioned recovery policy, and a that trusts host-run evidence, never a unit's own claim about itself. An identity set is rendered from the files, write-protected, and checked at closeout against the branch.
The path is in. A unit can ask a person through one typed tool; only a nonblocking recap continues without an answer. An unanswered question pauses the unit by gate; what's owed to a person is delivered and reminded; who may answer is declared and checked.
The regulators owe evidence now. An eval harness runs the drift scenario under declared arms with outcome and trajectory graders. Four committed reports, one per scripted unit — the reference, the drifter, the sloppy one, and the self-certifier that deletes the tests it cannot pass — say where the gated arm loses. Every record carries an switch and a retirement condition, and every instance store reads as OpenTelemetry spans. Evidence about the regulators ↗
And it operates. The lab installs as a Pi package; regulator init installs it into an existing repository under a declared manifest; doctor checks the instance before the first unit and in CI; the base is re-verified after every merge and a failure holds the whole instance; refused words are linted out; the outbox is watched; an identity decision promotes back into the definition.

docs/DEBT.md tracks every gap the build has taken on deliberately, one row per item: forty-one rows so far, most already paid as later lessons closed them. What's still open, and worth knowing before you rely on this:

A live eval report is still owed. The four committed drift-scenario reports ran scripted units; the session's own gates — profile grants, the write gate, the bash watch, the budget guard — weren't exercised, so the numbers validate the graders, not yet a model against the harness.
The capstone rubric is specified, not calibrated. It hasn't yet been checked against a real learner submission.
Isolation is the container's job, not the harness's. bash sits outside the thrash detector, the effect journal, and the failure observer by design; real sandboxing is left to the operator, not built by the lab.
One machine at a time. Leases and the effect journal live on a single host; two harnesses on two machines can't yet see each other's intentions — that's deferred with full VSM recursion.
--by is asserted, not authenticated. Disposition authority checks whether the named person may decide, not that the caller is that person; authentication is the deployment's to add.
6
Packages, released together (0.1.4 is current) under Apache-2.0: @metacoding.io/regulator (the control plane and CLI), regulator-pi (the Pi host), regulator-protocol, regulator-core, regulator-checks and regulator-control-room, tested without a model under CI on two Node versions
44
Records in the , 42 active and two retired, each with a stated limitation, an ablation switch and a retirement condition, with REGULATORS.md and BOUNDARY.md generated from the records
15 / 15
Lessons written end to end, with the capstone viability case specified and its rubric awaiting calibration

In the docs: Build debt · Roadmap · Changelog