Docs/02 Reflection

02 Reflection

Noticing that a session contained something worth keeping.

Runs on Stop, per turn. Its job is to schedule attention, not to judge.

The occasion is structural — "did this turn have enough substance to be worth a thought" — measured by nudge_after_tool_calls (12), nudge_after_turns (3), or min_surprises (2 failed tool calls), subject to nudge_cooldown_s.

The occasion is deliberately not "something failed." An earlier version triggered on failed tool calls, which sounds structural but is a mechanical proxy for a semantic question, and it biases hard toward the cheapest mistakes. Conceptual errors — believing a system works one way when it does not — exit zero and pass their tests. During ROSE's own development the worst mistake ran green the whole way.

What it looks for#

Not "were you wrong about anything?" — that is a judgement the reflector has to reach for, and it reliably answered no on sessions full of corrections. The question is where did the user have to steer you, which is evidence sitting in the transcript. It is the same split the rest of ROSE applies: count the observed outcome, judge only what needs judging.

A correction, a rejection, a "no, like this", a restated requirement, a complaint about quality, a preference you did not know they held. Each is a round of the user's time a better-informed agent would not have cost them. For each, the reflector asks what it would have needed to know at the start, and the answer is usually one of:

#KindWhy it leads
1A standard or preference this user holds The three most often missed, and the three that remove the most future steering. They are not mistaken beliefs, so a taxonomy asking only about wrongness cannot see them at all.
2A method they expect applied
3A quality bar
4A false belief about a tool, language or platform Counts even though it surfaced as a bug. The test is whether a belief was wrong, not whether it arrived with an error message.
5A false belief about this project
6A modelling errorYou represented something in a way that is not true of it.

Then it filters on usefulness rather than worthiness:

  • would knowing this at the start have removed at least one round of steering?
  • does it apply to some task other than this exact file?
  • is it still true tomorrow?

Three things it does instead of capturing#

  • Already served, still ignored. If the user had to say something that was already in a lesson the agent was shown, that is not a gap in what is known — it is a lesson that is not landing. The reflector names the node id and captures nothing.
  • A capture it should have made. If the conversation shows the agent running rose add itself, or the user asking why something was not learned, an earlier pass missed it. The reflector works out what about these criteria let it through and captures that, in the reflection family.
  • A defect in ROSE. If what it found is a bug in the harness rather than a lesson for the store, it runs rose report and asks whether you want it filed. It never files anything itself.
Calibration is against the steering, not a general prior. A session where the user never redirected the agent almost certainly teaches nothing. A session where they redirected it repeatedly almost certainly does, and returning nothing from one of those is the check failing.

The backoff, and why it is versioned#

Nudges that keep producing nothing lengthen the cooldown, up to a cap of one hour. But "found nothing" and "there was nothing to find" are different states, and a backoff that cannot tell them apart throttles hardest exactly when the reflector is broken.

That happened here. Bad criteria produced six barren nudges, the cooldown grew from 15 minutes to four hours, and rewriting the criteria did not release it — so the fix could not run to prove itself. The criteria are now fingerprinted and every nudge records the fingerprint it was issued under; a streak only counts events sharing the current one, so changing a prompt resets it by construction.

Where it runs — learning.nudge_mode#

ModeReflector seesCostInterrupts you
background (default)a transcript digest, ~3k tokensone small callno
forkthe whole session, inherited~0.1× its tokensno
blockthe agent's own live contextnone extrayes, one turn
offnone until session endno

fork spawns claude --resume <id> --fork-session detached. --fork-session allocates a new id, so the live session is never written to. It is affordable because prompt-cache reads bill at 0.1× and the cache keys on prefix content, not session identity — the fork hits what the live session just wrote.

If nudges keep producing nothing, the cooldown backs off automatically (nudge_backoff_after).