Changelog
All notable changes to @metacoding.io/regulator are recorded here. The format follows Keep a Changelog; versions follow Semantic Versioning. Until 1.0.0, a minor version may change the CLI or the definition's file formats; a patch version does not.
Unreleased
Changed
- The registry cards speak the product's vocabulary (#93). Where a card said "lesson 12" or "checkpoint 9" it now names the extension, the regulator, the debt row or the release; the course is cited only where it is the source (a drill, the retired lesson 02 gate).
ownership.ownerisregulator-maintainerson every record instead of the design-phasecourse-lab.docs/GLOSSARY.md's "where it lives" column drops the module numbers, and its diagnostic table andPATHOLOGIES.mdsay which column is the course's. - A registry record added between releases says
introducedIn: "unreleased"(#92). The schema admits the word beside semver,REGULATORS.mdrenders it as "unreleased", the release commit rewrites it to the version being tagged (CONTRIBUTING.md § Releasing), and the release workflow refuses to publish while any record still says so.pre-merge-trialis the first: it said0.1.4before any release existed. - The identity's context limit is the budget policy's, and the default is 12000 characters (#91).
renderIdentitySectioncut at 6000, a constant; the seed the definition ships renders to 7005, so the tail ofGLOSSARY.mdwas not reaching units (found by #51). Rather than shorten the seed, the default is raised to 12000 (about 3000 tokens) and a policy may declareidentity.maxChars; the identity extension,regulator check,regulator doctorand the warning all measure against the limit that applies. The shipped definition passescheckwith no warning.
Added
- The identity's context budget is measured, and the cut is reported (#51, DEBT row 25).
renderIdentitySectiontruncates at 6000 characters; nothing said when it did.identityContextBudget(core) now measures the whole rendering against the limit and names the files cut;regulator checkandregulator doctorwarn with the size, the limit and the files — a warning, not a problem, since a long set is valid — anddoctor --jsoncarries it on theidentitycheck (warning) with awarningscount on the report; the session's status line saystruncated at 6000 of N characters, and the identity extension records one advisoryoperational-signalper session on the unit, with the files cut as evidence, so the unit's record shows it ran with part of its identity unseen. Found on the way: the seed the definition ships renders to 7005 characters, so the tail of GLOSSARY.md has not been reaching units;regulator checknow says so, and what to do about it (shorten the seed, or make the limit a policy field) is a decision recorded in the open-questions backlog rather than taken here, since the live runs (#46) are measuring the prompt as it is.
Changed
- The post-merge check is replaced by a pre-merge trial (#48, DEBT row 37). Reintegration now merges the unit's branch in a temporary worktree detached at the base's HEAD, runs the workload's
run_testsandrun_checkson that merged tree, and lands a passing tree by fast-forward to the very commit that was verified; a failing tree lands nothing, the base is exactly as it was, and the unit is blocked with the causeconflict(repair, under the shipped policy) with the failing checks as its hint. The base is never red; the instance-level obligation the post-merge check opened is no longer produced.unit finishruns the same trial with the project's own two checks. The evidence ispre-merge:*, bound to the trial commit; a closed outcome carriespreMergein place ofpostMerge; a base that moves during the trial is refused asbase-moved. Thepost-merge-checkrecord is retired as history andpre-merge-trial(44 records, 42 active) states the cost, measured.
Added
- A workload's check entry may carry options, and
glossary-linttakes one:{ "name": "glossary-lint", "options": { "commitMessages": "advisory" } }makes a refused word in a commit message evidence and anaudit-findingatadvisoryinstead of a failed check, while the comment half stays blocking (#47, DEBT row 36).software-developmentand the drift suite's arms declare it;personal-financedoes not.regulator checkrefuses an option a check does not take. The regenerated sloppy report shows the cost that was being paid: the four units whose only drift was a word in the commit message now close on their first attempt, and the two whose comments drifted are still refused. waitCeilingMson the interaction policy (#52, DEBT row 30). A unit paused on a question nobody has answered for that long is routed under the recovery policy'stimeoutcause (retry, retry, escalate by the shipped rules; each routing past the ceiling is one occurrence) and its lease is released, so a unit going nowhere does not hold the worktree. The question stays open with its veto and its history says why; nothing answers on anyone's behalf. The field is optional and the shipped policy leaves it unset: the honest default is to wait.
Changed
- A registry record's enforcement points are typed (#76).
mechanism.enforcementPoints[]was free text with the point as a prefix; it is now{ where, point, note? }withwhereone ofhost(a session event the Pi host subscribes to, ordispatcher),tool(a registered tool's execute),loop(a step of the S3 loop or the unit lifecycle),cli(aregulatorsubcommand) orcheck, andpointfrom that place's closed list (ENFORCEMENT_POINTSin@metacoding.io/regulator-protocol).regulator checkrefuses an unknown place or point by name; tests hold each list against the code, so a host event, a tool, a loop step or a subcommand that is not on its list fails the build.REGULATORS.md,BOUNDARY.mdand the control room printhost:tool_call (write, edit: prepareWritePath). This is the first slice of the figures generated from the definition (ROADMAP.md, threads). - A registry record's
introducedInis the package version that first shipped it (0.1.0for the forty-three records that exist), not the design phase's checkpoint number (M03); the schema requires semver andREGULATORS.mdand the control room render it as "since 0.1.0". A record added after this carries the version it lands in (#56).
Fixed
- The usage line and the CLI reference name
identity promote --definition <dir>andeval --host <package>, which the commands accepted undocumented.REGULATORS.mdopens with an introduction to what a record is and how to read one. regulator unit acceptrecorded--byas given, while thedisposition-authoritycard said the name was checked against the interaction policy. An acceptance is a disposition (it satisfies a criterion no check can observe, and the closeout gate counts it), so the command now goes through the same check asanswerandobligation resolve, atblocking— the conservative choice for a criterion that has no severity of its own — and a name the policy does not list is refused before anything is written;--rejectis checked the same way.initandevalstill record--byas provenance without the check (#77).regulator unit startprinted api -ecommand namingdist/tools.jsanddist/coordination.jsunder the control plane, where the session extensions have not lived since the host seam split; copied from an install, the command failed. The line now asks the host for its extension paths (host.extensionPath), honouring--hostandREGULATOR_HOSTasunit dispatchdoes. With no host resolvable the start still takes the lease and makes the worktree, and says a session there needs a host instead of printing a command that cannot work (#79).
0.1.3 — 2026-09-29
The second live drift run (issue #46, on 0.1.2) closed ten of twelve units and found what happens after the budget guard halts one.
Fixed
- A halt by the budget guard was failed over as a provider failure. The guard aborts the session when a ceiling is crossed and Pi records the abort as an errored assistant message; the dispatcher read the message alone, opened a fresh session on each declared fallback, and reported "every model in the route failed" when the last one did. Each fallback session also started a new ledger for the same attempt, so the primary's spend and the exhausted marker were overwritten and the loop classed the unit as a dispatch error (recovery cause
environment) instead ofbudget-exhausted, which the recovery policy routes differently. The dispatcher now reads the attempt's ledger before deciding (attemptEnd): a halt is returned to the loop as the attempt's end and is never failed over; only an errored message without a halt is a provider failure. A session that opens on an attempt another session already metered resumes that ledger (BudgetMeterresume), so the attempt has one ledger whichever sessions ran it, the primary's spend counts against the same ceiling, and a halt stays a halt. - The default policy's last fallback for every route,
google/gemini-2.5-flash, is retired by Google for new keys and answered every call with a 404; the routes now fall back togoogle/gemini-3.8-flash, which the pinned Pi registry knows. Nothing checks a route against the provider before dispatch; themodel-routercard says so. regulator unit show … | headcrashed the CLI withEPIPEonce the reader closed the pipe; the CLI now exits cleanly.
0.1.2 — 2026-09-28
The first live drift run (issue #46) found the live dispatch path had never been exercised end to end.
Fixed
- Live units ran with every session-bound regulator inert. The dispatcher created a Pi session and prompted it, but never called
session.bindExtensions(...), which is what emitssession_startin Pi's own modes. The contract, the profile grant, the budget guard, the identity section and the algedonic path all initialize on that event, so a live unit ran as a plain coding agent: no contract in its prompt,report_resultrefused for want of a contract, nothing metered, every unit blocked asno-report.openUnitSessionnow opens and binds the session the waypi -pdoes, emitssession_shutdownbefore disposing, and a headless test drives the real path with no model and asserts the contract is bound, the profile's tool surface is set and the ledger is written before the first prompt. - Binding the session exposed the next gap: the
implement,research,bookkeeperandauditorprofiles did not grantreport_result, so a bound unit could never close either. Every profile a contracted unit type runs under now grants it,regulator checkrefuses a contracted unit type whose profile does not (the contract extension also puts the tool on the surface when a contract loads, as a backstop), and the effect declarations distinguish a control-plane record (a report, a proposal, intelligence, a fact: the domain untouched, the record reversible) from a domain write, so a read-only profile can still report.
Changed
regulator checkrefuses a committed eval report underevals/reports/that still carries the placeholder interpretationregulator evalwrites without--interpretation(by "nobody yet", or "not yet interpreted by a person" in the text). The placeholder is exported from the protocol asUNINTERPRETED_BY,UNINTERPRETED_MARKERandisUninterpreted.
0.1.1 — 2026-09-25
The first install from npm found two things the workspace never exercised.
Fixed
regulatoron the path:dist/cli.jshad no shebang, so thebinlink ran it as a shell script.node dist/cli.jswas unaffected.regulator doctorfrom an installed package reported every registry record's implementation and cited tests as missing: the paths name source files the package does not ship. The registry check now verifies paths only in a source checkout (asrc/beside the registry) anddoctorsays when it did not;pnpm checkat the release is where they are verified. Theregulator-lifecyclecard states the limit.
0.1.0 — 2026-09-24
The first versioned release: the course's reference build, relocated from course/lab to packages/regulator unchanged, with the regulator status read model folded in from the former @metacoding/vsm-pi-cli.
Added
- The package scope
@metacoding.io:@metacoding.io/regulator(this package),-pi,-protocol,-core,-checksand-control-room, released together from one tag. - The
regulatorCLI:init,doctor,unit start|dispatch|drive|route|show|close|accept|evidence|finish|status,contract check,obligations,obligation show|ack|resolve|escalate,answer,remind,memory,identity accept|reject|promote,signals route,effects,watch,eval,spans,review,status,fixture. - The host seam (
Host,loadHost): the control plane resolves a host package by name and runs live units through its dispatcher;@metacoding.io/regulator-piis the Pi host, with the eleven session extensions (tools,profiles,coordination,contract,budget,recovery,evidence,authority,intelligence,identity,algedonic) loaded bypi install, the dispatcher, the write gate and the typed reporting tools folded in from the former@metacoding/vsm-pi-extension. - The definition beside the code: forty-three registry records (forty-two active; the lexical vendor write gate of lesson 02 retired, superseded by the authority extension and the manifest's protected prefixes) with
REGULATORS.mdandBOUNDARY.mdgenerated from them, the identity seed, five profiles, five policies, two workloads (software development, personal finance), the drift eval suite with four committed reports, and the contracts the lessons run. - Host checks at closeout:
run_checks,run_tests,inherited-tests,identity-untouched,export-signature,glossary-lint.