Skip to content

Hook contract

Mossbear’s agent lifecycle is four commands. This page is the contract behind them — what each is for, when it fires, and what running it is allowed to change. Flags and syntax are in CLI reference; which harness writes which config file is in Local agents. Neither lives here twice.

Every one of the four is a hook a harness runs unattended, and also a command a person can type by hand. What each does does not change between the two callers.

CommandFiresMay doMust never do
mossbear-hook [--harness <id>]After every tool callAppend one action record to this machine’s local run logReach the network, or block or fail the tool call it is logging
mossbear reportAfter each assistant turnUpload the session’s queued action metadata, and pull and reconcile this repo’s subscribed Mossbear-managed artifactsPush a local artifact edit back, close the session, or write to a path whose source of truth is git
mossbear completeWhen the agent session endsUpload what is left, close the session for grading, pull and reconcile subscribed artifacts, and push this checkout’s locally-edited Mossbear-managed artifacts backPush while another session in this checkout is still active, or write to a path whose source of truth is git
mossbear refreshBefore the first turnPull and reconcile this repo’s subscribed Mossbear-managed artifactsUpload actions, push a local edit back, or write to a path whose source of truth is git

All three of report, complete and refresh pull and reconcile subscribed artifacts today — refresh is not the only one that delivers, it is the one installed to do it before the first turn rather than after one. Only complete ever pushes a local edit back.

None of the four ever writes delivered content into a path whose source of truth is git: the planner that resolves where a write may land refuses one by name rather than silently narrowing what it delivers.

Success is silent. None of the four prints anything when a run did exactly what its row above says and nothing else happened — there is no --quiet flag, because quiet is now the default. --verbose opts back into the progress lines a successful run otherwise keeps to itself: what was extracted, uploaded, and pulled.

What still prints, --verbose or not: a file written, refused, or left in conflict; a warning; an error; and, at session start only, one line saying how many notes people have left about this repo’s context and which command reads them. That last line is capped per reader per repo, so its absence means “nothing new to interrupt you about” rather than “no notes”; mossbear notes is never capped and is what answers the question. Reading a hook’s stdout, empty means “ran fine, nothing to relay” — anything else is the run’s own account of something that was not routine, and is worth relaying to whoever is at the keyboard.

Some generated hook entries also wrap their command in || true (or that harness’s equivalent) — a second, unrelated reason one can produce no visible failure. That swallows a network error so it never blocks the agent’s turn; the action stays queued and goes out on the next run that reaches the dashboard. Not every entry is wrapped this way: on Claude Code, only the session-start entry is, so a failed report or complete there does surface as a hook error rather than swallow one. Shell-level silence and --verbose’s absence are not the same guarantee, and neither is universal across harnesses — only --verbose’s absence on a run that actually completed means the run landed.

A single mossbear sync, told apart only by flags, used to be all four rows above. A settings file could not show which flag a given entry meant, so the safe reading had to be the default everywhere and every interesting behaviour had to be asked for. Reading a hook entry now means reading the verb.