Skip to main content

What gets written

diaries/agents/<agent-id>/<YYYY-MM-DD>-<slug>.md
Every entry is a hub document with source_type: diary, so it is searchable through kb_search, fetchable through kb_fetch, and visible in the Atlas just like any curated note. Body shape:
# <task title>

## What was done
Route: paid_model via anthropic/claude-sonnet-4-7. Tokens: 5132. Latency: 8421ms.
Applied 3 file operation(s).
Build verified in 1 attempt(s).

## Error encountered            (only on recovered runs)
2 escalation(s); build repaired in 3 attempt(s)

Last build stderr tail:
error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

## How it was recovered          (only on recovered runs)
Orchestrator engaged automatic recovery; the final attempt exited cleanly.

## What was learned
<3-5 sentence model-generated reflection — the durable signal>

## Related
- kh-...
- kh-...
Tags always include outcome:completed or outcome:recovered, and task:<task-id> so future queries can filter on the trigger or join back to the workflow.

When the orchestrator writes one

Two trigger points inside worker/worker/orchestration/thread_runner.py:
  1. Task completion — the success-exit path of PromptThreadRunner.run. One entry per successful run.
  2. Recovered-from-error — the same path, but flagged as recovered when any of these held during the run:
    • escalation_count > 0 (the model escalated)
    • build_verify_attempts > 1 (the build was repaired in-loop)
    • the silent-no-op recovery (_repair_no_files) was triggered
Failed runs do not write a diary. If build_verify_passed is false, the orchestrator treats the run as a hard gate against commit_and_push, and recording it as “completed” would mislead future retrieval. The diary path is also skipped if the task raises before the success path is reached.

Model-driven reflection

The What was learned section is filled by a short LLM call (free model, event_role="diary.reflection"). The prompt asks for 3–5 sentences of insight — not a summary of what was done, which is already in the structural section above. The response is truncated at 800 characters; empty output or a model failure falls back to a deterministic stub so a diary always lands. Why a separate call: a structural summary captures what happened. A model reflection captures what is non-obvious about it. The reflection is the part that appreciates over time as retrieval surfaces it on similar future tasks.

Configuration

VariableDefaultNotes
KB_DIARIES_ENABLEDunsetWhen unset or falsy, the hook short-circuits before any work — no model call, no write. Flip on per environment after the runtime wiring is verified.
MOTHER_AI_URLRequired when diaries are enabled; the writer POSTs to /v1/kb/edit.
MOTHER_AI_API_KEYoptionalPassed as x-api-key if set.

Auto-merge contract

Diary entries land on the base branch immediately because diaries/ is on the KB_AUTO_MERGE_PREFIXES allowlist in Mother AI (mother-ai/src/routes.rs). The worker’s reindex consumer re-embeds the entry within a minute, so retrieval reflects it on the next query. No PR review, no manual merge.

Why this matters

  • An agent that recovered from a tricky build error today writes that down. The next agent looking at the same error pattern finds it through kb_search.
  • A pair of agents working on different parts of one workflow can read each other’s reflections through the same retrieval contract — no special inter-agent channel.
  • Operators looking at “what happened on workflow X” get a chronological trail of agent-authored notes, not just a Postgres audit log.

Invariants

  • A broken diary write never fails a task. All errors are caught in the hook and logged; the surrounding job completes normally.
  • Outcome is structural, not subjective. The recovered flag fires on observable signals (escalation, build repair, no-op repair); a model can’t “decide” it had a hard time.
  • Reflection is opt-in per environment. The same code path runs disabled by default so wiring landings can soak in dev before turning on writes.