Skip to content

Reference: CLI

The CLI is the engine's mechanics — best for scripting, CI, and inspection. It runs the deterministic engine with no LLM attached: pip install mokata gives you the mokata command in your terminal only. It does not put mokata inside Claude Code — to drive the gated workflow with Claude as the brain, install the Claude Code plugin or run mokata setup claude. (mokata setup itself is documented below.) Why two ways: How mokata uses an LLM: harness vs CLI.

Slash-command form ↔ install route. Where this page cross-references a /mokata:<name> slash command, that is the plugin render; via the pip-first mokata setup claude path (the supported route today) the same command is bare/<name> (drop the mokata: prefix).

Invoke as mokata <command> (console script) or python -m mokata <command>. mokata --version prints the version. Most commands accept a shared --path PATH (repo root to operate on; default the current directory). Commands that need an initialized repo load the Surface and exit non-zero with a clear error if .mokata/ is missing.

Spine (Part A)

mokata init

Scaffold a valid config: detect installed tools, pick a profile, write .mokata/manifest.json + .mokata/constitution.md. Human-gated (shows a preview and waits for confirmation).

Magical first run (Stage 56): run interactively on a fresh repo (or mokata init --wizard) and init becomes a guided Q&A wizard — it asks the profile, detects your integrations (graph backend, memory backend, Postgres / vector), asks which to wire, then wires them with your approval (orchestrating init + config + setup). mokata detects → recommends → runs with approval — it never silently installs a third-party tool (an absent one is recommended, not installed). It finishes with a 30-second "here's what I just did" recap + the next step.

🔴 --yes also wires the harness (changed in 0.0.19) — a behaviour change for scripted callers. A non-interactive init now runs setup claude at project scope as part of the init: --yes is consent to the whole init plan and wiring the run-state gate is part of it. It writes .claude/commands/, .claude/skills/, .claude/settings.json (briefing hook, secret-guard, gate-guard, the MCP permission grant, the status line) and .mcp.json, and it spawns a short-lived mokata-mcp subprocess to verify the server answers and serves the same version as the CLI. Existing JSON is merged, not overwritten; reverse with mokata unsetup claude --scope project. A wiring failure never fails an init that already succeeded. If you time, sandbox or diff a scripted mokata init, expect those files and that subprocess.

Every init that does not wire prints one line saying whether the run-state gate is enforcing here — in three states: enforcing, not wired, or could not be verified (which is not a verdict either way; treat it as not enforcing until you have checked with mokata doctor --wiring).

Flag Meaning
--profile {minimal,standard,full,custom} starting profile (default: standard)
--mode {seatbelt,memory,full} graduated adoption on-ramp (mutually exclusive with --profile)
--yes non-interactive; skip the write prompt (no wizard)
--force overwrite an existing manifest
--preview print the plan and exit without writing (dry-run for the human gate)
--wizard force the guided interactive first-run wizard
--setup-harness in the wizard, also wire mokata into the harness (commands + MCP + hooks)

Graduated adoption (--mode): three named on-ramps, each landing a visible win in under five minutes and finishing with a printed quickstart naming the ONE command that proves it.

Mode Wires Quickstart proves it with
seatbelt the standard profile — the engine, the governance gates, and the built-in AST code graph the blast-radius gate needs to answer structurally /brainstorm
memory the standard profile, plus the offer of the local embeddings model /onboard then mokata memory
full the full profile, plus the embeddings and code-graph offers mokata query blast_radius <symbol>

A mode is an alias for a profile plus an onboarding flavour — the manifest it writes is identical to the same init via --profile, and nothing named "mode" is persisted. seatbelt never offers the embeddings model; memory and full offer it through the same consented, interactive-only flow as setup (a --yes or non-TTY init never asks and never installs).

--preview is the side-effect-free dry-run the /mokata:init plugin command runs before asking you to approve the real write, and it covers the whole write. Alone it renders the .mokata/ plan and states that harness wiring is not part of that run; with --yes it also renders the harness plan — the commands, the Agent Skills, .mcp.json and .claude/settings.json — through the same renderer the wiring itself uses. A harness half that cannot be planned says so and names the reason rather than being omitted.

mokata tour

A 60-second, read-only demo of mokata on a tiny sample — a structural graph query, a memory recall (in an in-memory store), and a gate catch (a real secret scan that hard- blocks). Writes nothing to your repo; safe to run anytime. --ascii for ASCII-only glyphs. Also available as the read-only tour MCP tool and the /mokata:tour slash command.

mokata reconfigure

The re-runnable reconfigure wizard (Stage 56b): the same guided Q&A as first-run setup, run any time on an already-initialized repo to change what's wired — add/remove an integration, switch a backend, change profile, or pick up a newly-installed tool. Composes init / config / setup / unsetup (nothing rebuilt). It re-detects your tools, shows a current→proposed diff, then applies behind one human gate.

  • Idempotent — re-running with no changes is a no-op (nothing written).
  • Human-gated — decline and nothing changes.
  • Reversible--remove cleanly unwinds an integration with no residue (gone from the capability chain and the tools table; ties to unsetup/reset).
  • Never silently installs — an absent --add tool is recommended (e.g. pip install 'mokata[postgres]'), not installed.
Flag Meaning
--profile {minimal,standard,full,custom} switch the profile (default: keep current)
--add TOOL wire a detected integration (repeatable; absent → recommended)
--remove TOOL cleanly unwire an integration (repeatable; no residue)
--set KEY=VALUE switch a backend setting in the manifest (repeatable; gated)
--wire-harness / --unwire-harness add/remove the harness wiring (commands + MCP + hooks)
--scope {project,user} harness scope for the harness flags
--yes non-interactive; apply the explicit changes without prompting

Inside Claude Code this is the /mokata:reconfigure slash command and the gated reconfigure MCP tool — which returns the diff plus a proposal_id and writes nothing until you approve it with mokata approve <id>.

mokata setup <harness>

One command to use mokata in a harness without the plugin: runs init (if needed), materializes the /mokata: command set into the harness's NATIVE surface, registers the mokata-mcp server where the agent's MCP schema matches, and wires the hooks where supported — the SessionStart briefing plus both PreToolUse blocks, the secret-guard and the gate-guard (--no-hooks opts out). Only the claude harness declares the hooks capability. Human-gated; JSON files are merged (never clobbered); idempotent; reversible (unsetup leaves no residue). Setup is capability-aware: it wires ONLY what a harness actually supports and states the rest clearly, never silently skipped or pretended.

Supported harnesses (Stage 52 + Stage 63): claude, codex, cursor, copilot, windsurf, gemini, aider. Each maps to its native command surface — e.g. Cursor .cursor/commands/*.md, Copilot .github/prompts/*.prompt.md, Windsurf .windsurf/workflows/*.md, Gemini .gemini/commands/*.toml, Aider reference prompts (Aider has no native slash-command files). MCP is auto-registered for claude, cursor, and gemini (mcpServers schema); a documented manual step for codex/copilot/windsurf. See Use mokata with other AI agents. Inside Claude Code, the /mokata:setup guided wizard (Stage 56) drives the same detect → ask → wire flow.

Flag Meaning
--scope {project,user} install into this repo (default) or ~/.claude (every project)
--profile {minimal,standard,full,custom} profile to init with if not already set up
--no-hooks wire only commands + MCP; skip the hooks
--yes non-interactive; skip the confirmation prompt
--force re-init even if a manifest already exists

mokata unsetup <harness>

Reverse mokata setup: remove the copied commands, the mokata MCP entry, and the mokata hook entries (other entries are preserved). Leaves .mokata/ config intact. Flags: --scope {project,user}, --yes.

mokata bootstrap

Print the SessionStart briefing (which stack you're in, live capabilities, inviolable gates), capped at a 2,000-token budget. --show-tokens prints the token estimate + budget check to stderr; exit is non-zero if over budget.

mokata validate

Parse + validate the committed manifest; prints a one-line summary. Exit non-zero on an invalid manifest.

mokata release-check [version] [--root <checkout>]

Release plumbing (pure/offline). Assert every version field — pyproject.toml, .claude-plugin/plugin.json, .claude-plugin/marketplace.json (metadata + plugins[0]), src/mokata/__init__.py __version__, and the published mokata-check@vX.Y.Z action pins — equals the intended tag (default: this package's version; --root checks another checkout before you tag it). Exits non-zero naming each offender — the release preflight that refuses to tag a commit whose versions lag the tag (the 0.0.4 lesson).

It tells you which mokata answered, and refuses if it is the wrong one. The first line names the package that produced the result, where it was imported from, and how many fields that code knows to guard:

answered by mokata 0.0.18 from /path/to/checkout/src/mokata — 7 guarded fields; checking /path/to/checkout

Run bare from a checkout with an older mokata installed, the command used to import the INSTALLED package, read this checkout's files, and print PASS against its own, smaller field set — a green computed by code that predates the fields it was not checking. So when the answering package lives outside --root it now refuses (exit 3) and prints the remedy, and a package with no importable location refuses too (exit 4). Exit 1 still means, and only means, that the version fields disagree.

PYTHONPATH=/path/to/checkout/src python3 -m mokata release-check 0.0.18 --root /path/to/checkout

mokata release-notes-check [version] [--root <checkout>]

Release plumbing (pure/offline). The disclosure half of release-check: assert that RELEASE_NOTES.md announces the intended tag and carries every fact CHANGELOG.md records under ### Known limitations for that version — the measurements, the code identifiers and the versions it names. The wording is deliberately not matched, so the notes can be written in their own voice; what may not happen is a number quietly going missing. Run it before you tag:

mokata release-notes-check 0.0.16

Since 0.0.19 it grades a second axis: does every published schedule still resolve? A disclosure that says "scheduled for 0.0.19" can go on being printed long after 0.0.19 stopped containing it — presence is not truth — so each scheduling claim in the shipped surfaces is looked up against the planning corpus and reported as resolving, unresolved, spent (the release it names is at or before the one being cut), or accounted (it slipped and the slip was published, or a ruling holds it where it is).

Three exit codes, and 2 is the one to read carefully:

Exit Meaning
0 the notes are right and every schedule resolved, was accounted for, or is spent
1 the notes are wrong (a limitation went undisclosed), or a published schedule no longer resolves — each offender is named
2 NOT CHECKABLE HERE — neither a pass nor a failure. A schedule could not be resolved from this artefact at all

Exit 2 is the normal answer when you run this command yourself: the planning corpus it would resolve against is maintainer-internal and does not ship, so the released package cannot read it. It is deliberately non-zero — a caller that has not been taught what it means reads it as a refusal, which is the safe way round — and nothing treats it as green. The resolving verdict is made by the release gate that runs where the planning corpus exists.

mokata branch-protection-check [--repo <owner/repo>] [--branch <name>]

Release plumbing (fail-closed). Verify the public mirror's default branch is protected — no force-push, no deletion, required status checks — by reading protection via the gh CLI (which supplies its own auth: your login locally, GH_TOKEN in CI). The release preflight runs this so no release proceeds onto an unprotected main. Defaults: --repo JasGujral/mokata-oss --branch main. No token is hard-coded or accepted.

"I could not read it" and "it is not protected" are different answers, and there is an exit code for each. Exit 0 is protected. Exit 1 means, and only means, that a read succeeded and showed protection absent or too weak — the one state that prints the apply-protection remedy, because it is the one state where applying it destroys nothing. Exit 2 means the protection detail could not be obtained at all (gh missing or unauthed, a 5xx, an unparseable body) and could not be corroborated — a read failure, so its remedy is retry, never a write. Exit 3 is a degraded pass: the detail was unreadable but positively corroborated by reads that succeeded (repos/<repo>/branches/<branch> says protected: true and repos/<repo>/rulesets parses with every ruleset actively enforced). It is deliberately non-zero — a caller that has not been taught what 3 means reads it as a refusal, which is the safe way round — and it prints a dated notice naming the four assurances it did not obtain. A failed corroborating read does not buy a pass; it refuses.

⚠ Before 0.0.18 this command had two codes and exit 1 covered unprotected or unverifiable. A caller that special-cases exit 1 must be updated: what 1 used to catch is now split across 1 and 2, and a caller that treats every non-zero alike will refuse a cut this check passed.

mokata route [need]

Resolve a capability to its tool, showing the attempted fallback chain and the reason. With no need, resolves every declared capability.

mokata detect

Show tool-presence for the whole catalog (present/absent) — no manifest required.

mokata status

One-line stack summary: version, profile, and what each capability resolves to right now.

Engine & pipeline (Parts D, L)

mokata brainstorm [--status]

Launch the Socratic pre-spec brainstorm (the clean-room protocol + live grounding). --status instead reports whether an approved approach is persisted.

mokata spec emit --file <spec.json> [--yes] · mokata spec show

Emit the spec — the only path a spec reaches disk. In normal use you never hand-write the JSON: the model composes the spec during /mokata:spec and emits it for you. emit --file is the advanced / CI escape hatch for a scripted flow (--file - reads stdin).

emit puts the spec through two gates: the completeness gate first — every acceptance criterion must map to a test, or the emit is refused and nothing is written — then the human write gate (off a TTY it fails closed; --yes is a human's own non-interactive flow). A committed emit writes both this run's emitted_spec and the shared spec_corpus in one gated commit, so a spec on record is always a spec spec-check can see.

This is what unblocks implementation: the spec-persisted run-state gate reads exactly the key this writes. show prints the run's persisted spec (read-only). (MCP: spec_emit — human-gated: propose → mokata approve <id> → commit.)

A spec with no approved approach behind it is SUPPORTED, and says so. Emitting without a prior brainstorm approval or refinement set is a legitimate path, not an error — so the completeness gate reports it rather than failing or staying silent: "STANDALONE: no approved brainstorm approach or refinement set is on record for this spec — it stands alone. That is a SUPPORTED path, not an error and not a failure." It then names the road to an approved direction (/mokata:brainstorm or /mokata:refine, approve an approach, then emit). The note is informational only and never blocks the emit.

Emitting twice on one run REPLACES, and the old spec is superseded — never overwritten. A second emit on a run that already has a spec is a versioned replace: the previous spec is archived as v<N> and the new one lands as v<N+1>, with the archive kept on the record. The approval preview says so before you approve it — it heads the diff with REPLACES v<N> -> v<N+1> and shows what changes (title, criteria counts, and the per-criterion delta), in the same vocabulary an amend preview uses, so a replace can never look like a first emit. If the existing spec is present but unreadable, the preview says that instead of pretending to diff it. Use spec amend rather than a re-emit when you are widening scope on work already under way: amend forces the phase regression and re-earns the gates, whereas a re-emit is the spec being rewritten before that point.

Scope (what the spec-scope gate reads). A spec can carry a machine-checkable scope: the authorized surface (where this change is allowed to land) and the deferred items — the things you and the model explicitly agreed not to build, each with the paths they'd live in and the literal markers they'd spell in code (e.g. batch_update, matched case-insensitively as a substring of the content being written). Both are authored by the model as it emits the spec, from what you agreed in brainstorm. The gate-guard hook then enforces it: a write outside the authorized surface, or one whose content spells a deferred marker — even inside an otherwise authorized file — is an exit-2 block naming the deferred item. Your levers when it fires: mokata spec amend (re-approve the wider scope — see below), mokata spec amend --abort, or mokata gate override spec-scope --reason "<why>". A spec with no scope section is not policed (fail-open) — every spec written before this feature is in that case.

mokata spec amend --file <spec.json> --reason <why> [--item <id>] [--yes] · mokata spec amend --abort

A FORCED PHASE REGRESSION — not a text edit. The only road back when a write is out of scope. Calling it regresses the run develop → SPEC immediately: development writes are blocked until the amendment lands. The amended spec (send the WHOLE spec, not a patch) must then re-earn every gate — completeness (each criterion, old and new, maps to a test), the blast-radius lens (re-run only when the scope widens), and a human approval. It persists as vN+1 with vN superseded (never deleted) and the diff on the audit ledger; the run then resumes from the last passed gate (P17), and the new criteria owe a failing test before implementation of them proceeds. --abort abandons an amendment in progress and unblocks writes (changes no spec; ledgered). (MCP: spec_amend — human-gated; the model may propose an amendment, never approve it.)

mokata plan [list | show [<slug>] | export [<slug>] [--to <dir>] [--force]]

Browse the brainstorm plan files. On approach approval mokata saves the design write-up to an internal .mokata/temp_local/plans/<slug>.md (before any spec). list shows the saved plans; show prints one (defaults to the sole plan); export copies it to a project-visible plans/<slug>.md you can commit (default dir plans/, override with --to). Export is user-initiated — it never silently clobbers an existing copy; pass --force to overwrite.

mokata enter <phase> [--to <phase>]

Enter the pipeline at <phase> (one of the 7 PIPELINE_PHASES); --to extends to a slice. Applies only the run phases' gates; upstream phases are skipped explicitly.

mokata preview [--start <phase>] [--to <phase>]

Dry-run: list planned actions, gates, and file touches with zero side effects.

mokata playbook [--parallel] [--fanout] [--dense]

Run the full story end-to-end on this repo (brainstorm → completeness gate → tests → implement → review). Prints PASS/FAIL per checkpoint; exit non-zero on failure. --parallel uses subagents (degrades to sequential without a harness); --fanout runs concurrently. --dense turns on output-density compression of sub-agent handbacks (whitespace/dupe-only, content-preserving) — frugal, OFF by default; also settable via settings.governance.output_density.

mokata progress [--lanes] [--run <id>] [--ascii]

Read-only run-progress tracker (done/current/pending + [done/total]). --lanes renders the parallel-aware multi-lane view (one line per concurrent subagent lane; sequential → one lane), derived from run-state + the execmode ledger records. Degrades cleanly with no active run.

mokata sessions

List past + active runs — for each: the run id, [done/total] phases, the last passed gate, and the resume point (or complete ✓), with the active run flagged. Read-only, bounded (one row per recorded run), friendly empty state when there are none.

mokata windows

List the live Claude Code windows on this repo — each open window is its own MCP process, otherwise invisible to the others. For each: the short session id, when it started, live/stale (a stale window's process has exited), and its current pipeline phase. Read-only (the calling window self-registers and dead-pid windows are pruned lazily; the registry is transient state under .mokata/temp_local/, never a gated durable write). Distinct from mokata sessions, which lists pipeline runs (and from mokata session, which manages shareable session bundles).

Each window row also shows its worktree (main for the primary checkout, or the worktree's relative path) and its scope (what that session is working on, if recorded). When another mokata window is live on the same repo, windows also surfaces a one-time, human-gated worktree offer — a suggestion to isolate your working tree; it never creates anything.

mokata worktree create [<topic>] [--yes]

Create a git worktree to isolate this session's working tree, so two Claude Code windows on one repo stop colliding on the files themselves. mokata asks what this session is working on (the scope), recommends a topic-aware branch/dir name (derived from the topic and the other live sessions' phases/scopes), and confirms explicitly before running git worktree add — the only durable action here (fail-closed: a non-interactive session declines unless --yes). Refuses politely outside a git repo. Two worktrees of one repo keep one team project identity, so team memory/journal/audit never fork across them.

mokata resume [<id>]

Preview where a run continues: the phase to resume at (the first phase after the last passed gate) and the gate that still applies there — mokata never auto-runs the pipeline, so the gates hold on resume. Defaults to the active/most-recent run; pass an <id> to target one. Read-only; degrades cleanly with no run (and reports a complete run as nothing to resume). Continue the run with mokata enter <phase> (or the /mokata:<phase> command). A mid-brainstorm checkpoint is also resumable: an in-progress /mokata:brainstorm (answered questions + the approaches being weighed) can be left at any step and resumed later — the HARD-GATE still holds (no spec until an approach is explicitly approved).

mokata watch [--once] [--open] [--run <id>]

Write a self-contained clickable local HTML dashboard of the active run (parallel lanes + 7-phase pipeline + a bounded gate/decision feed) under gitignored .mokata/temp_local/watch.html. --once writes one snapshot; otherwise it live-refreshes every 2s; --open opens it in a browser. Read-only (never writes durable state / never gates). Respects settings.ux.progress — with the default terminal it writes no HTML. Set the tier with mokata config set settings.ux.progress {terminal|dashboard|both}.

Composability (Part L)

mokata skills [name] · mokata skills search <query>

List the skill/command catalog (cheap — names + summaries). With a name, reveal that skill's gate, phase, and full prompt (progressive disclosure). search <query> filters the catalog by keyword — a discoverable skill catalog (Stage 70). Read-only.

mokata skill author <name> --content-file <f> [--require DOC:MUST-CONTAIN …] [--summary …] [--gate-desc …] [--out …] [--yes]

Author a new skill via RED-GREEN-for-docs: declare doc requirements (--require, RED), the --content-file content must satisfy them (GREEN), then the rendered command template is written through the universal human-gated WriteGate (--yes approves non-interactively). A RED draft (unmet requirements) writes nothing; on approval it lands at .mokata/skills/<name>.md.

mokata run <name>

Run a skill standalone (no pipeline prerequisite). name is any skill listed by mokata skills. Works with no init (grounding degrades cleanly).

mokata chain <skill> [<skill> …]

Plan a manual chain of skills; each step keeps its own gate (gates are never bypassed).

mokata suggest [flags]

Suggest a relevant command for the context — suggest only, never runs. Flags (all boolean): --fresh, --spec, --failing-test, --implementation, --diff, --bug, --stacktrace, --perf.

Knowledge (Part B)

mokata query <kind> <target> [--depth N]

Run a structural query. Navigation kinds — defs (where a symbol is defined) / refs (everywhere it is referenced) / callers / callees / implementers / imports; impact kind — blast_radius, to which --depth (default 2) applies. Uses the graph if present, else the AST floor, else the grep floor; the answer always names the backend that produced it.

mokata index

Build/refresh the per-file freshness index (incremental); report added/changed/removed and current stale files.

mokata lat-check

Scan @lat anchors and flag concept drift. Exit 1 on drift (gate-usable), exit 0 when clean or inactive (degrades when no anchors/registry).

mokata graph

Adopt or inspect the code graph. mokata graph adopt [code-review-graph|serena] (default code-review-graph) pins a real structural graph into the manifest through the human gate (the embedded AST floor stays the fallback, so adoption is recommended, never required). mokata graph status reports which backend actually answers today (graph or floor), and whether semantic search is available.

mokata spec-check --symbols <a,b> [--files <x,y>] [--text <desc>] [--phase <p>] [--allow-degraded] [--reason <why>] [--yes]

Regression guard (Stage 37). Cross-check a change's touch-set against the saved specs (emitted spec + archive) and decision memory; the touch-set is graph-expanded so a spec about an impacted caller is caught. On a hit it surfaces the conflict and routes it through the deviation gate: exit 1 (BLOCKED) until you confirm with --yes (amend/supersede) or re-plan; the conflict and resolution are logged. Exit 0 with no conflict. Degrade-clean: no saved specs ⇒ a no-op (no false alarm); no code graph ⇒ a lexical/file-overlap check that says so. Only the touch-set is checked (frugal). --allow-degraded (GR.S3): with graph.required on (the default), a touch-set that fell to the grep floor is refused as decision input; --allow-degraded explicitly accepts the degraded evidence for the session — TTY-reconfirmed, ledgered, and the result stays marked degraded — with --reason <why> recording why. (MCP: spec_check, propose-only — blocked without confirm.)

mokata ci-check [--files <a,b>] [--base <ref>] [--symbols <s,…>] [--comment-file <p>] [--no-fail] [--ascii]

mokata as a CI / PR check (Stage 58). Runs two gates over a pull request's changed files and reports PASS/BLOCK (exit non-zero on a real block): the completeness gate (does the saved spec still map every acceptance criterion to a test?) and the spec-awareness regression guard (does the change touch a previously saved spec/decision?). Changed files come from --files (comma-separated) or --base <ref> (via git diff); --symbols default to the symbols defined in those files. --comment-file writes the PR review-comment body (markdown); --no-fail makes it report-only (always exit 0). READ-ONLY — it surfaces blocks and produces the comment; it never posts to GitHub (the workflow's own GITHUB_TOKEN does). DEGRADE-CLEAN — it never false-blocks: an uninitialized repo, no saved spec, no spec corpus, or a repo that doesn't tag tests with AC ids all PASS. Used by the reusable mokata-check GitHub Action. (MCP: ci_check, read-only.)

Memory (Part C)

mokata memory [--kind <k>] [--all] [--project <id>] [--list-projects]

Read-only: surface the project "brain" grouped by kind (rule / guardrail / best-practice / context / reference / decision / episodic), the read/write ratio, and any pending self-healing proposals. --kind filters to one category. Commits nothing.

Project scoping (Stage 71a). On a shared backend (a team Postgres DSN that can host many projects), review defaults to the current project — no cross-project bleed. --all reviews across every project; --project <id> reviews a specific one; --list-projects prints the projects present on the shared backend and exits. The local SQLite store is already per-repo and ignores these. See Multi-project on one shared backend.

mokata memory edit <subject> --value <new> [--kind <k>] [--yes]

Update an entry (a formula changes, a guardrail is revised). Human-gated and routed through self-healing: the old value is superseded (kept in the record), the new becomes active — surfaced, never silently overwritten. --kind optionally retypes the entry.

mokata onboard

Launch the guided, LLM-driven capture of typed project knowledge (rules, guardrails, conventions, domain context, reference docs) — the same protocol as /mokata:onboard. Inputs are distilled, typed, deduped, and human-gated before they are stored. Re-runnable any time.

mokata memory export [file] · mokata memory import <file> [--yes]

Back up and restore memory. export writes a readable, committable backup — default <path>/.mokata/backups/memory-<UTC>.json (at the .mokata/ root, not temp_local/, and UTC-stamped so a new backup never clobbers a prior one) — carrying the active items with provenance; it's read-only on the source. import is the human-gated restore/merge: it dedups, gate-adds new items, and routes a same-subject-different-value conflict through the self-healing old→new surface — never a silent overwrite; provenance is preserved. (MCP: memory_export / memory_import, propose-only without confirm.)

The older .mokata/memory-share.json file-share channel was removed in 0.0.18. Nothing was deleted: that file is a mokata memory backup, so mokata memory import --file .mokata/memory-share.json restores it — previewed, gated and secret-scanned, with no downgrade.

mokata memory reembed [--yes]

Re-compute the vector index with the configured embedder (settings.memory.embedder) and re-stamp it. This is the way out of a stamp mismatch — the runtime refuses to rank a query against another embedder's vectors (the semantic tier simply turns off), and reembed rebuilds them. Human-gated, previewed with a count first; a decline writes nothing. It builds the vector store non-degrading (a silent fall to the SQLite floor would report a successful re-embed of a store with no vectors at all). See the retrieval stack lines in mokata doctor for which tiers are actually ranking your recall.

mokata memory promote <id> --to advisory|soft|hard [--yes]

Change a rule's enforcement binding — the one gated moment that moves a remembered rule between advisory, soft, and hard. Binding only: the rule text is untouched. Human-gated and ledgered; a lower --to is a (still-gated) demotion, reported honestly.

mokata memory review [--submit ID | --approve ID | --reject ID | --publish ID | --rollback ID]

Walk a memory proposal through its review states — Draft → In-Review → Approved → Published (--submit / --approve / --publish), with --reject terminal and --rollback restoring the prior published value. Approval requires proposer ≠ approver. With no flag it lists the pending proposals (read-only).

mokata memory consolidate

Surface proposal-only consolidations of the memory store — merges of duplicate facts, summaries, and prunes — one bounded line each (silent when there's nothing to propose). Read-only: it writes nothing. Applying a proposal stays the existing human-gated path (apply_consolidation); this command only shows what could be consolidated.

mokata memory migrate --to <backend> [--from <backend>] [--drop-source] [--yes]

Port the live store between backends (sqlite / postgres / pgvector) via the MemoryBackend contract — e.g. local SQLite → a shared Postgres, and back. Reads all items and writes them with provenance into the destination (resolved from the manifest's tools.<backend>.config). Human-gated (previews count + destination), idempotent (re-run upserts by id — no duplicates), and non-destructive: the source is left intact unless you pass --drop-source (separately gated). Degrade-clean — if the destination can't be built (e.g. Postgres unreachable) it reports and writes nothing; the source is never partially migrated. export/import shares content as a file; migrate moves the store between databases.

mokata migrate <channel>

It no longer migrates anything, and it is still here on purpose. Every deprecated channel that had a migration has now been removed (obsidian, native-memory, memory-share and vault), so this command exists to answer the command a year of deprecation notices told you to run: it names what was removed, in which release, where your data still is, and the one way to bring it across. It writes nothing and always exits 1.

Deleting the command instead would answer those users with invalid choice: 'migrate' — which is what a parser says about a typo — so a removed channel is answered by name here rather than refused. A channel removed in a later release is answered automatically; the list is derived from the removal registry, not typed.

Each channel's answer differs because the remedies differ: a removed backend points at the last release that shipped it, while a removed file channel points at the command in this release that reads your file. See configure storage backends and portable sessions.

Design vault (Part 35d)

mokata vault push <name> <file> [--kind brainstorm|spec] [--author NAME] [--force] [--yes]

Share a brainstorm-plan or spec markdown under <name> in the committed/synced vault at .mokata/vault/ (the .mokata/ root, not temp_local/), carrying provenance (author, source, kind, timestamps) + a content hash. Human-gated (secret-scan + approval, audit- logged). Never a silent clobber: identical re-push is a no-op; a changed re-push is refused unless --force, which versions it (keeping prior-version metadata).

mokata vault list

List entries (name · kind · version · author · date). Read-only.

mokata vault search <query>

Rank entries by name/title/body overlap (quote a multi-word query). Read-only.

mokata vault pull <name> [--dest FILE]

Write the named artifact to a file for review (default <name>.md); verifies the content hash. Read-only on the vault; provenance preserved. (MCP: vault_list / vault_search / vault_pull read-only; vault_push propose-only without confirm.)

mokata session save

Snapshot this session's in-flight state — the brainstorm progress, the approved approach (if one is approved), and the pipeline resume checkpoints — so mokata resume can continue it after an interruption. Ungated and local-only: a local save is your own transient state, and mokata's human gate sits at the share boundary (session push), not here. It writes nothing durable, sends nothing anywhere, reports the keys + counts it saved (never the content), and is idempotent. A session with nothing to save is an honest empty result, not an error. mokata also autosaves as you work — that is model-driven, silent on success, and surfaces only on failure; you never have to call it.

mokata session push <tag> [--to local|postgres] [--file] [--run ID] [--author NAME] [--save-first] [--allow-in-progress] [--requirements-only] [--force] [--yes]

Package the current session (the resumable run checkpoint(s) + approved approach + emitted spec + in-progress brainstorm) into a machine-path-free, versioned bundle carrying provenance (author, source, created) + a content hash + a repo fingerprint, shared over a transport: local (.mokata/session-bundles/<tag>.json) or postgres (a shared, owned DB table reached by $MOKATA_SESSION_PG_DSN / $MOKATA_PG_DSN). ⚠ The vault transport was removed in 0.0.18; passing it prints where your bundles are and how to reach them rather than invalid choice.

The transport is DERIVED from the repo's mode by default — a team-connected repo pushes to postgres, a solo repo to local — so you don't have to remember which one this repo is on. An explicit --to is honoured verbatim, and --file forces the local file transport regardless of mode (the explicit escape hatch on a team-connected repo). Human-gated + secret-scanned on EVERY transport (secret = hard block, audit-logged). Never a silent clobber: an identical re-push is a no-op; a changed re-push is refused unless --force. No session in progress → a friendly no-op. --run scopes to one run id (default: every recorded run). The Postgres leg is opt-in & local-first — no psycopg/DSN → degrades clean (clear message, no crash, never a silent fallback to a less-secure store).

Flag Meaning
--save-first run the ungated session save first, then bundle — one atomic action, no gap between what you see and what you share
--allow-in-progress consent to share unfinished thinking (a brainstorm with no approved approach). Required: without it, such a push is refused and nothing is written
--requirements-only bundle only the distilled requirements (anchor + goal + constraints + requirement lines) as a cross-repo handoff — no approaches, no approval, no transcript; the repo-fingerprint check is replaced by an origin label. An alternative consent to --allow-in-progress

mokata session pull <tag> [--from local|postgres] [--file] [--into REPO] [--force] [--yes]

Read the tagged bundle over the chosen transport (--from, derived from the repo mode by default, same as push; --file forces local), verify its content hash (corruption caught from any source, not served), then re-hydrate it into the target repo (--into, default this repo) so mokata resume continues. The bundle is untrusted, so this is human-gated + secret-scanned on pull, on every transport (a secret is a hard block approval can't override). A cross-codebase fingerprint mismatch is surfaced and not applied unless --force.

Bundles are schema v2; a v1 bundle still pulls fine, and a bundle newer than this reader is refused, never silently downgraded. The approach approval never crosses machines: on pull the approved_approach record is dropped and the brainstorm's approved flag is cleared, with imported: approval not transferred — re-approve on this machine (HARD-GATE) appended to the record. Be precise about the boundary — the emitted spec does cross intact (it is not de-approved), and write proposals, gate overrides, and TDD red/green state are never bundled at all.

mokata session name <tag> <new> [--to local|postgres] [--force] [--yes]

Rename a tagged session to a human-friendly name (what push/pull/resume and the status badge read). Human-gated where it writes durable; idempotent (renaming to the current name is a no-op); a name collision is refused unless --force (never a silent clobber). Provenance is preserved (original author/source/created + a prior_names trail) and the content-hash is untouched.

mokata session list [--all] [--project <id>] [--list-projects]

List the tagged bundles, spanning local + the committed vault (+ shared Postgres when a DSN is set) — each row tagged with its transport (tag @transport · resume point · author · date). Read-only; degrade-clean (an unavailable remote is skipped). (MCP: session_list read-only and transport-spanning; session_push / session_pull / session_name propose-only without confirm.)

Project scoping (Stage 71a). On a shared Postgres backend the listing defaults to the current project (a tag like auth never collides across projects). --all spans every project; --project <id> selects one; --list-projects enumerates the projects present. Run from outside a project (a bare directory) against a shared DSN, mokata refuses to dump every project's sessions — it asks you to choose --all / --project or run --list-projects.

Governance & token (Parts F, G, I)

mokata gate status|override|clear

The run-state gates — the ones the gate-guard PreToolUse hook enforces on native Write/Edit, so writing code before the spec, before a failing test, or outside the spec's approved scope is blocked by an exit code rather than a sentence the model can ignore.

  • mokata gate status — what is enforced here, and what is overridden. Read-only.
  • mokata gate override <gate> --reason "<why>" — stop enforcing one gate for this session.
  • mokata gate clear — drop this session's overrides and enforce again.

Four gates are enforced, and only inside an active mokata run (a repo you're hand-editing outside a run is never policed):

gate blocks a native write to an implementation file when…
approach-approval the run is registered but still in brainstorm — no approach approved and no spec emitted — so an implementation write is running ahead of the design decision
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 falls outside the spec's authorized surface, its content spells a deferred marker (even inside an authorized file), or a mokata spec amend is in progress (the run has regressed to SPEC — every development write is blocked until the amendment lands or is aborted)

A test file is always writable — you must be able to write the failing test. RED is the permission to implement, not the prohibition.

A block is a single stderr line and exit code 2BLOCKED [<gate>] <reason> — naming the file, what to do, and the override. Two honest limits: the gate-guard hook matches Write|Edit|MultiEdit|NotebookEdit and not Bash, so a shelled sed -i is not policed by the run-state gates (the secret-guard does match Bash); and only the claude harness declares the hooks capability, so on cursor/gemini/cowork/windsurf the gate-guard is never wired and the run-state gates enforce nothing (mokata harness shows the matrix).

Overrides follow P14 exactly: explicit (you name the gate and give a --reason), re-confirmed (an interactive y/N), session-scoped (it expires with the session — nothing to remember to turn off), and ledgered (mokata audit shows who/when/which gate/why, forever). There is deliberately no env-var kill switch: an env var is a side door any process can open silently, not a human decision. The secret-guard is never overridable — a security block is not a methodology gate.

Ambiguity fails open. If two mokata runs have state in one repo and none is pinned, the gates turn off for that window and say so once: mokata will not guess which run your edits belong to, because guessing could block on another window's state. Two ways out — give each window its own working tree (mokata worktree create), or pin the run by exporting MOKATA_SESSION_ID, which restores enforcement when you do want two windows on one tree. mokata windows lets you see you're in that situation; it is a visibility tool, not the disambiguator.

mokata approve [<proposal-id>] [--yes] [--actor <who>]

Approve one proposed durable write — the act an MCP tool cannot perform.

mokata's write tools (remember, config_set, session_push, init, …) are propose-only: calling one stages the change, returns a proposal_id, and writes nothing. Only an approval you mint here — in your own terminal, in a process the model is not driving — lets that write land.

mokata approve                    # list what is waiting on you
mokata approve p-3f9a2c11b4de     # see the write in full, then approve it

The model can only reference an approval by id; it can never mint one. approve=true on a tool call is accepted for schema stability but commits nothing — it never was consent, it was a flag the model typed itself.

An approval is single-use (it licenses exactly one commit, then it is burned), content-hashed (change an argument and the id changes, so "get X approved, then commit Y" is arithmetically impossible), session-scoped, and expires (15 minutes). It is ledgeredmokata audit shows the proposal hash, who approved it, and the write it licensed, forever.

Off a TTY it fails closed: a non-interactive shell cannot approve by accident. A genuinely non-interactive human flow (CI, a script) passes --yes — that is your own environment saying it, which is exactly the thing a tool parameter is not. The secret-guard still hard-blocks an approved write: approval is a methodology gate, never a security override.

mokata rules

Show the 4-tier rules and their line budgets; exit non-zero if a tier is over cap.

Show the append-only audit ledger (every gate decision, tool call, write, …). Add --why for a readable what + decision + why timeline of the run — for each entry, what happened, the decision, and the reason (the deviation's why, the spec-conflict's affected spec/decision, the self-healing rationale, the gate's message). It is read-only and bounded (--tail, default 50 — a tail, not the whole history); local-first, and degrades clean when there's no ledger yet.

Team audit / shared activity log (Stage 71) — shared OR local, conflict-free, NO telemetry. By default your audit log is LOCAL (the JSONL above). A team can optionally publish those same entries to the team's OWN managed Postgres (Stage 69's BYO DB — an env-var DSN) so everyone can see who did what across the governed brain — without anything ever being phoned home to mokata/Anthropic. The data is the team's, on the team's storage.

  • mokata audit --team — the team-wide who-did-what / why over the shared log (spans all actors). Read-only; degrades clean (sharing off / backend absent → a clear message, your local log unaffected).
  • mokata audit --share [--yes] — publish your new local entries to the team's shared log. Opt-in (mokata config set settings.audit.shared true, plus settings.audit.dsn_env for the env-var name). The publish is the only moment data leaves the machine, so it is human-gated + secret-scanned (a secret is a hard block). Entries are append-only + per-actor + namespaced, so concurrent teammates never clobber each other. The DSN secret is never stored (only the env-var name). No driver/DSN → it stays LOCAL (degrade-clean, no crash). See Team audit / shared activity log.

Project scoping of the shared read (Stage 71a). The team read is namespaced by the same stable project key every shared backend uses, so mokata audit --team defaults to the current project. Add --all to span every project, --project <id> for a specific one, or --list-projects to see the projects present on the shared log.

Standing audit-publish consent (TM.S4). mokata audit --consent show|grant|revoke manages the revocable standing consent for the batched publish. Granting it once (human-gated + ledgered, captured during mokata team join) lets the batched publish proceed without re-prompting per batch — while the per-publish secret-scan still hard-blocks (never a governance bypass). Revoke any time to return to per-batch human-gating. See Team mode — setup & operations.

mokata budget

Show token savings — a live budget readout (aggregated from the ledger) + a statusline.

mokata bench

Measure wall-clock latency of the hot paths (statusline, briefing, secret scan, grep query, recall, status) against their budget — read-only, dependency-free (median of N). Distinct from mokata budget (tokens). --repeat N sets the sample count. See performance / latency budget.

Adapters & distribution (Parts A6/H, J)

mokata coverage

Report capability coverage + unmet gaps + role overlaps (resolved by precedence).

mokata mcp

Discover MCP servers (from .mokata/mcp.json) and map them to roles; degrades cleanly ("no servers discovered") when none are present.

mokata harness [<name>]

List the available harnesses and their capability matrix (commands / hooks / context_injection / subagents) — the reference claude (all four), the portable codex (commands + context_injection), cowork (commands + context_injection + subagents, but not the PreToolUse hook — see Use mokata in Cowork), and the Stage-63 agents cursor / copilot / windsurf / gemini (commands + context_injection) and aider (context_injection only — no native slash commands). Add a <name> to show just one. The engine is harness-agnostic: a harness lacking a capability degrades with a clear message, never a crash and never a silent no-op of a gate. See Use mokata with other AI agents.

mokata export [file]

Export the current manifest as a shareable stack file (default <path>/mokata-stack.json).

mokata import <file> [--yes] [--force]

Validate + apply a shared stack manifest as this repo's config (human-gated; rejects an invalid manifest with exit 1; --force overwrites an existing config).

mokata stacks <list|search|show|install> [target] [--source <dir>] [--yes] [--force]

Community stacks & skill marketplace (Stage 70) — no hosted marketplace; publish over git/the vault, discover a reviewable versioned index.json, install via the gated adopt path. list (default) / search <query> / show <name> read the curated catalog (bundled, or a git-org/vault one via --source); read-only, degrade-clean (no index/source → a clear message). install <name> is the human-gated, secret-scanned adopt path: it secret-scans the stack manifest (a secret is hard-blocked), then applies it as your config (--yes approves; declining writes nothing; --force overwrites an existing config). The curated guardrails + recommended skills land in your manifest's settings.stack (reviewable). See community stacks and install mokata.

mokata team <init|join|status|adopt|connect|disconnect>

init (TM.S3) is first-time team setup — the sole owner of DDL. It guides a backend pick (--backend managed|compose|local; managed DSN is the golden path), fails closed with a named fix when $MOKATA_PG_DSN is unset (writing nothing), runs one idempotent provision pass that creates the shared tables (mokata_memory, mokata_session_bundle, mokata_audit_log, mokata_events) + the mokata_schema_version row on vanilla Postgres ≥15, no extensions (the ≥15 floor is enforced from 0.0.19 — a below-floor server warns today and is refused from 2026-11-12, PostgreSQL 14's upstream end-of-life, falling back to the local store; see the PostgreSQL floor), pins the team project identity (settings.project.id, human-gated) so clients don't split by path-hash, and runs the live CONNECTED test (the same probe mode set team uses). The DSN value is never persisted (env-var only, secret-scanned). Re-running is safe (idempotent). After a green init, mokata mode set team activates team mode. Zero-setup team sync.

DDL is team init's alone. Runtime connects run zero DDL (the schema check is a cached, SELECT-only probe), so a least-privilege DML-only runtime role is sufficient — and revoking UPDATE, DELETE on mokata_audit_log makes the audit log append-only by grant, not by convention. (mokata does not create or manage roles or grants; team init prints the guidance.) The shared schema is compatible over a range, not an exact match: this build speaks v3 and serves schemas back to v2, and a difference in either direction only warns and keeps working — a version bump must not partition a team mid-upgrade. Only two states are hard refusals, each with its named fix: a schema below the floor (mokata team init to upgrade it) and one ahead of this build (pip install -U mokata). team init likewise refuses to rewrite a schema newer than itself. join <source> (the new-member onboarding path — a joiner never runs init/DDL) takes a teammate from a DSN to CONNECTED without reading source: it runs adoptconnectactivate → vault pullonboardconsentdoctor in order, each a confirmable step, and prints a "here's what you're now wired to" summary. The joiner INHERITS the pinned team project id from the adopted stack (never re-pins/forks it); the activate step runs the TM.S2 preflight and reaches CONNECTED or fails closed with the S2 named fix (unreachable/pooler-trap, schema-absent → ask whoever ran team init, incompatible version), writing nothing on failure; and the consent step captures the revocable standing audit-publish consent. The --dsn-env value must be the env-var NAME (e.g. MOKATA_PG_DSN), never an inline DSN — an inline DSN is refused (fail-closed). Options: --dsn-env <ENV> (shared memory), --vault <repo-or-dir> (pull the shared design/spec vault), --yes (non-interactive), --force (overwrite config on adopt). Every writing step is human-gated, the untrusted pulls are secret-scanned, and a step whose source/backend/driver is absent is skipped with a note (never a blocker); it is idempotent and reversible. Every team-mode error links Team mode — setup & operations. The individual steps still exist: status (read-only) shows whether shared memory/sessions are local-only or pointed at a managed Postgres; adopt <source> pulls a teammate's governed stack (shared manifest + vault + shared-memory pointer) in one human-gated, secret-scanned step (--force to overwrite); connect --dsn-env <ENV> points shared memory + sessions at your own managed Postgres via an env-var DSN (the DSN value is never stored — only the env-var name); disconnect reverses it. mokata hosts nothing; degrade-clean with no driver/DSN. See team setup.

mokata mode · mokata mode set <local|team> [--yes]

Show or set the run mode — a first-class, visible property of every session. mokata mode (no argument) prints the current mode line + the team-readiness preflight (every prerequisite, each blocker with its actionable fix). mokata mode set local is the zero-config default: on an already-local repo it is a no-op that writes nothing (local stays byte-for-byte unchanged); it only writes — through the same human-gated config set WriteGate — when switching back from a non-local setting. mokata mode set team runs the fail-closed preflight — a usable run identity, $MOKATA_PG_DSN present, the shared DB reachable within a ≤500ms probe, and a compatible mokata_schema_version — and only then performs the human-gated mode write. Any failure is fail-closed with its own named fix (unreachable/pooler-trap, schema absent → mokata team init, incompatible version), links Team mode — setup & operations, and writes nothing; team mode is never half-activated. The mode is also surfaced in the statusline badge, the SessionStart briefing, and mokata doctor — a session is never ambiguous about which mode it's in.

mokata sync [--yes]

Flush + reconcile the team write journal (team mode only; a no-op in local). Every durable team write lands in a crash-safe local journal first, so offline never blocks and nothing is lost — mokata sync is the manual flush that pushes those writes to the shared database and reconciles conflicts. Three guarantees ride it: (1) each flushed write carries the ledger id of its original human approval, so the flush inherits that approval (never a governance bypass) and a per-publish secret-scan still applies; (2) each memory write is compare-and-set on a revision column — a concurrent-writer conflict surfaces and is resolved through the human gate (keep-local overwrites remote, keep-remote drops the local write), never a silent last-writer-wins (a conflict you don't decide stays deferred for a later interactive run); and (3) rows the old fallback stranded in the local floor are recovered through the same gated path. It leads with the connection health verdict — the SAME one shown in the statusline badge (⚠), mokata mode, mokata doctor, and the SessionStart briefing (one probe, cached). When the connection isn't healthy the flush is skipped (work-locally; nothing lost) and the state is reported. --yes is non-interactive (flush without prompting; conflicts are deferred, never silently overwritten). See Team mode — setup & operations.

Lifecycle (Part K)

mokata menu

The command palette — every shipped /mokata: command and every bundled skill on one screen, each with a one-line description and a marker for the ones that carry a gate. Read-only; enumerated from the installed command/skill files (single source, never a hand-maintained list). Colour + a Unicode box on a real terminal; plain-ASCII with zero escape codes when piped, redirected, or NO_COLOR is set. Backs /mokata:menu.

mokata docs [topic]

A pointer to the published docs site (https://mokata.ai/). With no topic it lists the top-level topics with their site URLs; with a topic (e.g. getting-started, concepts/execution-model) it prints that page's URL and title. Read-only and local-first — it resolves and prints URLs, it never fetches the page, and no doc content ships in the package (the docs live at the repo-root docs/ tree that mkdocs builds into the site). An unknown topic re-lists the topics with a hint and exits non-zero. Colour + a Unicode box on a real terminal; plain-ASCII with zero escape codes when piped, redirected, or NO_COLOR is set. Backs /mokata:docs.

mokata docsync [path] [--reconcile] [--yes]

Keep the docs true to the code. With no target it sweeps the public doc tree and drift-detects; with a path it audits exactly that doc. The audit is read-only — it cross-references each claim against the code (skill counts, command names, install/getting-started path, version examples, and — with a code graph wired — symbols) and reports every discrepancy with a severity (Blocking / Minor / Info), highlighting the stale section. A Blocking finding exits non-zero so a release doc gate can act on it. With --reconcile it proposes the fixes, previews the diff, and writes ONLY on approval through the universal write gate (--yes approves non-interactively); a decline writes nothing — there is no silent-write path, and it reconciles the doc to match the code, never the reverse. Backs /mokata:docsync.

mokata doctor [--wiring] [--matrix]

Diagnose the manifest/config: missing providers, broken adapters, role conflicts, bad trust levels, oversized rule tiers, and a broken audit-ledger hash-chain. Exit non-zero if any error. Read-only.

--wiring narrows it to one question — are mokata's gates wired, launchable, and current? — and answers only that:

✓ hooks: wired, launchable, and current.

It checks that every wired hook command resolves (a hook Claude Code cannot launch is dropped silently, so wired has never implied firing) and that the wiring is the wiring this version of mokata writes (a pip install -U leaves settings.json as the previous version wrote it, so a hook added or a matcher widened since your last mokata setup claude is simply absent). Exits non-zero if either answer is no. Unlike the full doctor it needs no initialized repo, so it is runnable the moment a pip install finishes — which is exactly when mokata upgrade runs it. See mokata-hook: command not found.

It is also where a degrade stops being invisible. Seven honesty surfaces ride it:

  • What degraded this session. Degrade notices are remembered, not just printed once into a scrollback, so doctor can report them:
    ⚠ degraded this session (2) — a capability fell back to a floor:
    
    A team read served from the local floor, a code graph that fell to grep, a secret-scanner that could not import. Each names its failure class and — for team — the resolved env-var NAME, never its value. It is process-lifetime: it reports what degraded in this process (a notice emitted inside a short-lived hook process isn't in it), and an empty registry prints nothing at all (no "0 degrades" line to train you to skip the section that matters).
  • Run mode + the team preflight — the mode line plus every team prerequisite, each blocker with its named fix. Its team-schema check reports the range: schema v3 in range (this mokata speaks v3). A version difference in either direction is a warning that keeps working (a bump must not partition a team mid-upgrade); only a schema below the floor (mokata team init) or one that no longer serves this build (pip install -U mokata) is a refusal.
  • The team write-flush backlog (team mode only) — approved team writes land in a crash-safe local journal first, so doctor says when they haven't reached the shared DB yet:
    team pending: ⚠ 3 approved write(s) journaled locally and NOT yet flushed to the team DB;
    oldest waiting 12m; last failure: <class>; auto-retry paused (cap reached) — run `mokata sync` to flush.
    
    (This is not a count of pending approvals — those are listed only by bare mokata approve.)
  • The DSN deep-check (team-connected repos only; a local / no-DSN repo is silent) — the full database (team DSN) section, classifying the shared-DB connection into named, secret-free findings (driver · network · auth · pooler · schema-version), each with its concrete fix and the env-var NAME, never the DSN value. The pooler axis is the one that catches the #1 silent team-DB failure: a transaction-mode pooler connection string (a Supabase *.pooler.supabase.com:6543, a Neon -pooler host, a PgBouncer endpoint) breaks LISTEN/NOTIFY and session state, so doctor names it and tells you to use the provider's direct/session string instead. A pooler finding is a warning (it keeps working); a driver/network/auth failure is an error that exits non-zero.
  • Are mokata's skills actually visible here? — a new session on a git worktree, a second checkout, or a root that was never set up shows an empty / menu, and this says why:
    mokata skills: NOT VISIBLE ✗ — no mokata skills or commands are wired in this root
      expected 26 skills at <root>/.claude/skills and 37 commands at <root>/.claude/commands — none found.
      Fix: run `mokata setup claude` in this repo, then restart Claude Code.
    
    The restart hint rides every state, because Claude Code reads the skill/command list once per session. Informational — it never changes doctor's exit code.
  • The retrieval stack — which engines actually rank a recall in this repo, reported on two axes because they degrade independently:
    retrieval stack
      - semantic: off  (no embedder configured — ranking is lexical only)
      - lexical:  fts5  (SQLite FTS5 + bm25, ranked in the database)
    
    Semantic is model2vec:<model> (the mokata[embeddings] extra — real meaning), hashing (the zero-dep floor, honestly labelled token-hash overlap, NOT meaning), or off. Lexical is fts5 (SQLite) / tsvector (Postgres) — ranked in the database — or jaccard, the Python keyword-overlap floor. Every state it prints is a supported configuration (hashing + jaccard is a working zero-dependency install), so it emits no finding and never touches the exit code; saying which one you have is the deliverable.
  • The trust surface truth (an info line, only when settings.trust is set) — trust is enforced on the mcp write surface; there the real ladder is read-only ▸ write-allowed (propose-only == gated-write, because every MCP write already needs a human-minted mokata approve <id>). CLI writes carry their tool identity but are not yet governed by the dial. See manifest → settings.

--matrix additionally prints the full capability coverage matrix — every harness wiring point and every declared capability classified pass / degraded / fail (degraded = resolved via a fallback; fail = no present provider). It reuses the same resolver the diagnosis does (one source of truth), is read-only, and does not change the exit code.

mokata baseline [--cmd <test command>]

Report whether the test suite is green or red at baseline before you start — so any new failure is attributable to your change. Read-only; uses settings.baseline.test_command (or --cmd). Degrades clean if no test command is known (mokata never guesses a framework); exit non-zero only on a red baseline.

mokata config get <key> · mokata config set <key> <value> [--yes]

Read or update a dotted manifest key — e.g. backend paths (tools.sqlite.config.path, tools.postgres.config.dsn_env). set is human-gated (preview → confirm; --yes skips), validates the result, and hard-blocks any secret (an inline DSN/credential is refused — use an env-var reference). get exits non-zero if the key is unset. See configure storage backends & paths.

mokata config wizard

An interactive, gated walk through mokata's user-facing settings. For each setting it shows the current value, a one-line description, the allowed values, and the default; you can keep, skip, or edit it. Every change is routed through the same human-gated write path as config set (preview → confirm → secret-scan → schema-validate → ledger) — the wizard is a front-end, never a second write authority. Fail-closed: on a non-TTY / unreadable stdin it makes no change and says so (it never hangs or silently writes). Reject leaves the manifest byte-unchanged; each committed change is recorded in the audit ledger.

mokata secret ignore --token <string> --file <path> --reason <why> · mokata secret ignores

Record that a secret-scan finding is a false positive, for one exact string in one exact file. --reason is required.

Only the entropy backstop — the layer that guesses a string looks generated — is negotiable. A finding from the signature layer or the known-shape floor (AWS / GitHub / GitLab / Slack / GCP / Stripe / PEM / JWT / connection strings) is refused by name and can never be ignored; those are documented credential formats, not a guess.

The ignore list is .mokata/secret-ignores.jsonversion-controlled on purpose, so a suppressed secret finding shows up in your PR diff with the reason you gave. It stores a sha256 of the flagged string, never the string itself. It carries an integrity checksum: a hand-edit is refused with "re-add it via the CLI" — a speed bump and an audit trail, not a security boundary. Entries do not expire on a clock; they expire on content — rename the identifier or delete the line and the entry stops matching anything and is reported inert.

mokata secret ignores lists what is recorded (hash, file, reason, age, and whether it is inert). mokata secret ignore --remove --token <string>|--hash <h> --file <path> revokes one. Both add and remove are written through the human-gated write path and recorded in the audit ledger, and mokata doctor names the active count so ignores cannot accumulate unseen.

When the entropy backstop blocks a write, the block message prints the exact command for that finding — the same wording on the CLI hook and the MCP write gate.

mokata reset [--keep-config] [--backup DIR] [--yes]

Remove mokata state (.mokata/). --keep-config keeps manifest.json + constitution.md and removes only memory/, state/, audit/. --backup DIR moves state there instead of deleting (reversible). Human-gated unless --yes.

mokata exec [--parallel] [--isolation] [--fanout]

Show/select the execution mode for a run (default: sequential gated flow).

mokata decompose [--run] [--ascii] [--yes]

Propose an independent-subtask split of the emitted spec's acceptance criteria (one subtask per AC) plus a dependency plan — subtasks that touch the same symbol/file are kept ordered (depends_on), using the code graph to verify independence when one is wired, the lexical floor otherwise. With no flags it prints the read-only split. --run human-gates the confirm, then feeds the confirmed tasks into the existing flow (resolve_execution_choicerun_tasks): the cost estimate is shown, parallel-vs-sequential is asked (default sequential), isolation + two-stage review apply, and it degrades to sequential when subagents are unavailable. Conservative: it never silently parallelizes work that might be dependent — when independence is unverified (no graph) or dependencies exist, concurrent fan-out is withheld and isolated tasks run in declared order. Inside Claude Code: the decompose MCP read tool (proposes the split) and /mokata:decompose. Degrades clean with no spec/ACs.

mokata version [--check]

Print the installed version, the project profile, the install method (pip / plugin / source), and the Python version. Offline by default — local-first, zero network. Add --check to opt in to a single outbound call that compares your version to the latest published release; it is accounted in the audit ledger and degrades clean offline (a blocked/failed check just says it couldn't check — it never errors the command).

mokata upgrade [--check] [--method auto|pip|plugin] [--yes] [--scope project|user] [--no-refresh]

Upgrade mokata — and finish the job. Installing the package was never the whole upgrade: pip install -U mokata replaces the code and leaves .claude/settings.json carrying the wiring the previous version wrote, so a gate added since your last mokata setup claude is silently missing. So the pip path runs three steps, each ending where you decide:

  1. it proposes pip install -U mokata and runs it only after you confirm;
  2. it re-runs mokata setup claude, which previews the settings change and asks again — the same gate setup always uses; nothing is ever written silently;
  3. it runs mokata doctor --wiring and reports.

Human-gated end to end — decline either gate and nothing is written. --yes approves both non-interactively (the plan is still printed); --no-refresh skips steps 2–3 and prints the commands you now owe; --scope picks the scope for the re-wiring (default project).

The re-wiring runs as a subprocess of the freshly-installed mokata, not in this process: after pip install -U, the running process still holds the old modules in memory, so re-wiring in-place would faithfully write the wiring of the version you just upgraded away from.

The plugin path only prints the Claude Code steps (/plugin marketplace update mostack + reinstall), because the CLI cannot upgrade the plugin itself; a source checkout likewise only prints its steps (git pull + reinstall). Both printed recipes end with the same verification step the pip path runs. --check is the one outbound call — it just reports whether a newer release exists (the same opt-in check as version --check) and upgrades nothing. --method overrides install-method detection. Inside Claude Code, the /mokata:version command surfaces the same. Full runbook: Which setup command do I need?

mokata govern [--open] [--live] [--once]

Write a self-contained, clickable local HTML view of the governed state — the same read-only engine/constraints as mokata watch (inline CSS, no network/server/assets, under gitignored .mokata/temp_local/). It shows: the "what changed since last session" diff (new/changed memory, new rules, and the gate decisions made since the last session baseline), the always-on rules & guardrails (with line-budget usage), memory grouped by kind (rule / guardrail / best-practice / context / reference / decision — each item with subject, value, and provenance), the read/write ratio + memory-health nudge, and any pending self-healing proposals. Each item surfaces its gated manage command (mokata memory edit "<subject>") — the dashboard never performs a write. --open opens it in your browser. --live auto-refreshes (re-writes on a 2s interval + a self meta-refresh, honouring settings.ux.progress — the dashboard tier; Ctrl-C to stop); --once forces a single static snapshot. Degrades clean (no memory → a friendly empty state; first session → "no prior snapshot to compare yet").