Concept: governance & audit¶
Everything mokata does is reviewable and gated. It is local-first: nothing leaves the machine unless you wire it; there is no telemetry.
4-tier rules + constitution (G1)¶
| Tier | Source | Cap |
|---|---|---|
always_on |
the reflex rules injected each session | ≤ 60 lines |
agent_memory |
per-agent MEMORY.md |
≤ 200 lines |
steering |
optional .mokata/steering.md |
— |
articles |
the constitution's governing articles | — |
mokata rules shows the tiers and their line budgets; over-cap tiers are flagged.
Rules-vs-gates-vs-hooks taxonomy (G2)¶
A rule is advisory (stays prose), blocking (make it a gate), or event-driven (make it a hook). "Checkable → a gate or a hook, not prose."
Karpathy gates (G3, hybrid)¶
Four engine checks, each registered/toggleable/audited through the rules layer (reusing the shared gate type), firing at their pipeline point:
| Gate | Phase | Checks |
|---|---|---|
think-first |
analysis | a plan/approach exists before code |
simplicity |
strawman | complexity under a cap |
surgical-scope |
emit | touched files under a cap |
verify |
completeness_gate | success criteria defined + verified |
Toggle via settings.governance.karpathy.<id> (default on); a disabled gate does not fire
and is not audited.
Hooks (G4)¶
Sync hooks block (exit code 2); async hooks observe and never block (exceptions are
captured). Two sync PreToolUse hooks ship, and they differ in the kind of block they make:
| Sync hook | Matches | Kind of block |
|---|---|---|
mokata-hook secret-guard |
Write Edit MultiEdit Bash |
security — never overridable |
mokata-hook gate-guard |
Write Edit MultiEdit NotebookEdit |
methodology (run-state) — is overridable: explicitly, re-confirmed, and on the ledger |
Two async hooks ship alongside them: mokata-hook session-start (the SessionStart briefing)
and mokata-hook dirty-track (a PostToolUse observability lane that records touched paths for
the code graph's freshness check — it always exits 0 and can never block). All four are wired
by mokata setup claude (on by default; --no-hooks opts out) or by the plugin — and only
Claude Code declares the hooks capability, so on any other harness they are never wired and
the run-state gates below enforce nothing.
Secret protection (I1) — 4 layers¶
scan(text, path, for_send) runs four independent layers — signature (known
credential patterns), entropy (high-entropy tokens), path (.env, id_rsa,
*.pem, …), egress (any secret in outbound content is fatal) — catching secrets
before they're written, committed, or sent.
The entropy layer is identifier-aware, because the thing that makes a scanner get
switched off is flagging a variable name. A high-entropy token is only reported when it
also has a mixed character class and no word structure — where word structure means
human-placed boundaries (separators or casing transitions) splitting the token into
word-shaped segments, so getUserAuthTokenFromRequest and MOKATA_SESSION_ID_OVERRIDE
read as identifiers rather than credentials. Underneath that sits a known-shape floor:
a small set of anchored vendor formats (AWS, GitHub, OpenAI, Slack, Google, JWT) matched
against both the raw token and its separator-stripped form, so a real key cannot buy an
exemption by looking word-shaped or by being broken into chunks. A floor match blocks
without consulting any exemption.
Human-gated writes (I2) + trust dial (K3)¶
Every durable write goes through the WriteGate: secret scan (an un-overridable security
block) → human approval → commit → logged. The trust dial enforces read-only (cannot
write at all), propose-only (never auto-approved — an explicit human decision is required),
or gated-write (the default).
Set it per surface (settings.trust.mcp) or per tool
(settings.trust.remember), in one flat map; the tool's level overrides the surface's, and
anything unset is gated-write.
propose-only means no auto-approval, not "prompt at a terminal" — a prompt is only
one way to get a human's decision. An approval a human minted out-of-band with mokata approve
<id> (bound to that exact write's content hash, single-use, session-scoped) is a stronger one,
and it satisfies the rung. That distinction matters because the MCP server has no terminal
to prompt at.
The honest ladder, per surface. On mcp it is really read-only ▸ write-allowed:
every MCP write already needs that human-minted approval, so propose-only and gated-write
land on the same behaviour. propose-only pins that floor rather than adding a new one — we
say so instead of implying three rungs where there are two. On cli the dial is not yet
enforced. Two floors hold at every level and on every surface: a secret is hard-blocked
(approval cannot override it), and no trust level lets the model mint its own consent.
One-key approve / edit / reject (Stage 54c). Where a gated write offers an editable
value — a memory edit, or a self-healing old → new proposal — the human gate isn't just
yes/no: it's approve (apply the proposed value), edit (supply a different one), or
reject, with the safe default = no change (a blank answer or EOF rejects — nothing is
ever rewritten without your say-so). This is a richer prompt, not a weaker gate: it routes
through the same WriteGate, so the secret hard-block still fires — approve (or an
edited value containing a secret) can never override a security block. Plain
approve/decline stays everywhere else.
Run-state gates on native writes (the gate-guard hook)¶
The WriteGate above governs mokata's own durable writes. The gate-guard governs the
harness's native Write/Edit — the tool the model reaches for when it writes your code.
It is where the method stops being advice: a write that breaks the run's own methodology is
blocked, with a reason and the way out. It is the gate you actually see.
Four run-state gates, each blocking a native write to an implementation file:
| Gate | Blocks when… |
|---|---|
approach-approval |
a run is registered (brainstorm in progress) but no approach is approved yet — the idea→code jump |
spec-persisted |
an approach is approved for this run but no spec is emitted |
no-code-without-failing-test |
the spec is emitted but no failing test is on record |
spec-scope |
the write is outside the spec's authorized surface, spells a deferred marker, or a spec amend is in progress |
A block is one line on stderr, then exit 2. Every reason names the file, says what to do, and offers the override:
BLOCKED [no-code-without-failing-test] no failing test is on record for this run — auth.py is
implementation. Write the failing test first and watch it fail (/test), or override:
mokata gate override no-code-without-failing-test --reason "<why>"
Scope — what it deliberately does not police.
- It fires only inside an active mokata run (an approach approved and/or a spec emitted). Hand-editing a repo outside a run is never policed: guardrails on the pipeline, not house arrest.
- Test files are always writable. You must be able to write the failing test — RED is the permission to implement, not the prohibition.
- Ambiguity fails OPEN. If two runs have state in one repo and neither is pinned, mokata will
not guess which window your edit belongs to, so every run-state gate turns off for that
window — and says so, once. Pin one with
MOKATA_SESSION_IDto get enforcement back. - A known hole, stated plainly: the gate-guard matches
Write/Edit/MultiEdit/NotebookEditbut notBash— ased -ithrough the shell is not policed. (The secret-guard does matchBash.)
The override (P14). These are methodology gates, not security ones, so they are overridable — under exactly one discipline:
mokata gate status # read-only: what's enforced / overridden here
mokata gate override spec-scope --reason "<why>" # ONE gate, THIS session
mokata gate clear # drop this session's overrides
--reason is required, the override is re-confirmed interactively (it restates exactly
what stops being enforced), session-scoped (a new session enforces again — nothing to remember
to turn off), and ledgered (who · when · which gate · why, visible in mokata audit forever).
Overrides stack, one named gate at a time. There is deliberately no env-var kill switch — a
side door any process could open silently — and no MCP tool and no slash command, because a
model-invocable override would let the model clear its own constraint. The secret-guard is not on
this list and never will be.
Plan adherence — never silently deviate¶
A plan change is a durable change, so it's human-gated like any other. During implementation mokata sticks to the approved plan: the approved approach (brainstorm) or refinement set (refine), the emitted spec, and its acceptance criteria. It does not change scope, the chosen approach, the ACs, or the design beyond what was approved — and it never expands scope unasked.
If a deviation becomes necessary — an AC is wrong or infeasible, the approved approach doesn't work, a materially better design appears, or an unforeseen constraint blocks it — mokata STOPS and asks first: it surfaces the deviation (what changes · why · the options) and waits for explicit approval. An approved change re-enters the approval surface (re-approve the approach/refinements, or amend the spec so every AC still maps to a test), and the request and the decision are recorded in the audit ledger.
This is the forward guardrail. The backstop already exists: the two-pass review flags
any implementation that diverges from the approved plan, so an unapproved deviation fails
review. Together: mokata did exactly what you approved — or it asked.
Independent review — a fresh pair of eyes, not a self-check¶
The closing /review is the gate before you land — so by default it runs as a
fresh-context subagent, not as the builder re-reading its own work. mokata hands that
subagent a self-contained brief — the emitted spec + its acceptance criteria, the approved
approach/refinement set, the diff under review, and how to run the tests — and no builder
conclusions. The subagent re-derives the verdict from the code and its own test runs; it
must reach the two-pass verdict on its own, not ratify the builder's.
Where a harness has no subagents (or you set settings.review.independent = off), review
degrades cleanly to the inline two-pass and says so honestly — it prints
review: inline — this harness has no subagents, so this review shares the builder's context
(or the equivalent config note) and continues. Independence is the default, never a hard
requirement — mokata never blocks just because a harness can't spawn a subagent.
The verdict is persisted as evidence, and /ship reads it: ship blocks unless a
passing review is on record (no verdict, or a failed one, stops the finish), and it surfaces
whether that review was independent ✓ or merely inline so the strength of the signal is
always visible and logged. Turn independence off (or back on) with:
mokata config set settings.review.independent off # inline two-pass (default is on)
mokata config set settings.review.independent on
An absent, broken, or unrecognised value reads as on, so the stronger independent review is
never silently lost.
Spec-awareness — don't break a saved spec by mistake (Stage 37)¶
The deviation guard protects this story's plan; spec-awareness protects previously-approved work. Before a change, mokata checks it against the saved specs and recorded decisions: does it touch or contradict something already specified or decided? It computes the change's touch-set — the symbols/files in play, expanded through the code graph (a spec about a caller of the changed code is caught too) — and looks for overlap with the spec corpus and decision memory.
If it finds one, it does not silently proceed: it surfaces "this change affects spec X /
decision Y — here's where" and routes it through the same deviation gate — you confirm
(amend/supersede the affected spec) or re-plan. The conflict and your resolution are logged.
It's frugal (only the touch-set is checked, never the whole corpus) and degrade-clean:
no saved specs yet ⇒ a no-op (no false alarm); no code graph ⇒ a lexical/file-overlap check that
says so. Run it via mokata spec-check or the spec_check tool; spec/refine/develop
invoke it as part of grounding.
Audit ledger (I3)¶
An append-only .mokata/temp_local/audit/ledger.jsonl records every gate decision, tool
call, hook, write, savings event, subagent decision, healing/consolidation decision, and
deviation request/decision — each with a monotonic seq. mokata audit prints it;
mokata audit --why renders the read-only what + decision + why timeline (Stage 49).
Trust & visibility (Stage 60)¶
Three read-only, derived surfaces so you can always see what mokata did and why — none of them mutate the state they show:
- Live
mokata govern/mokata watch. The governance dashboard takes the same optional self-meta-refresh aswatch:mokata govern --livere-writes on a 2s interval and the page refreshes itself (honouringsettings.ux.progress; the static snapshot —mokata govern/--once— always works and is byte-identical/deterministic). Self-contained: inline CSS, no network, under gitignoredtemp_local/. - "What changed since last session." A concise diff of new/changed memory, new rules, and
the gate decisions made since a lightweight last-session snapshot. The snapshot is captured
at the session boundary (the SessionStart hook) into transient
temp_local/; the diff derives against it and never writes. It surfaces in thegovernview and as one bounded SessionStart briefing line (within the 2k budget; absent on a first session or when nothing changed). Read-only — it bumps no counter and the snapshot capture is read-only on the governed state. - End-of-run "what I changed and WHY." Finishing a run (
/ship) folds in a boundedaudit --whyrecap of this run — what changed and the why behind each gate decision — so landing it is a reviewed decision. Shipping stays human-gated: mokata records the landing choice but never merges / opens a PR / deletes on its own.
Reversibility (I5) & resume (I6)¶
ReversibleStateStore records each write's prior value to a durable undo log; revert
restores it. PipelineCheckpoint persists each passed gate so an interrupted run resumes
from the last passed gate — a crash never loses state.
Lethal-trifecta gate (I4)¶
When system access + private data + an outbound action coexist, the outbound action is gated behind explicit human approval (and logged). When the trifecta isn't active, no gate is imposed.
Diagnose & reset (K5/K6)¶
mokata doctor reports manifest errors, missing providers, broken adapters, role
conflicts, bad trust levels, and audit-ledger integrity. It also carries the honesty
lines — a degraded capability must never be a silent one:
- What degraded this session — the notices are remembered, not just printed, so
doctorcan list every capability that fell back to a floor (a team read served from local storage, a code graph fallen to grep), each naming its failure class. It's process-lifetime: nothing degraded ⇒ nothing printed. - Team mode — the preflight (shared-DB reachability + schema compatibility) and, when the local
write journal has unflushed entries, the count of approved team writes not yet flushed to the
team DB (with the oldest's age and the fix:
mokata sync). - Trust surface-truth — when
settings.trustis set, one line stating exactly where the dial has teeth (the MCP write surface) and where it does not.
mokata reset removes .mokata/ state (preview-able,
human-gated, optionally backed up — reversible-aware); --keep-config keeps the manifest.