Configuration
Every setting, its default, and the reasoning behind that default.
rose config with no arguments dumps everything; one argument
reads a dotted key; two set it. Any key can be overridden per-run by an
environment variable: recall.max_pack_tokens →
ROSE_RECALL_MAX_PACK_TOKENS.
Ablations#
recall.strategy exists so the descent policy can be measured rather than asserted:
rose config recall.strategy stepwise # baseline: walk children, ignore deltas rose config recall.strategy delta-jump # replace apex with the holder node rose config recall.strategy delta-patch # default: apex + matched claims only
Every setting#
Generated from rose/config.py when the docs are built, so it cannot drift from the code the way a retyped table does — and did.
| Setting | Default | What it is for |
|---|---|---|
version | 1 | |
agent | claude | default backend: claude | codex | mock |
model | null | None -> backend default |
recall.enabled | true | |
recall.strategy | delta-patch | delta-patch | delta-jump | stepwise |
recall.max_pack_tokens | 1200 | |
recall.max_families | 3 | how many lessons to inject per prompt |
recall.judge_calls | 2 | Model calls the walk may spend descending. The first pass over the top level is not drawn from this — it is sized by how many chunks the apex layer needs, because a chunk skipped there is a lesson that can never be retrieved. |
recall.fanout | 12 | Lessons shown to the model in one question. Wide levels are split across several; this is about answer quality, not budget. |
recall.parallel | 4 | Chunks of one level put to the model at the same time. They are independent questions about disjoint sets, and each costs ~15s of which ~5s is process startup, so asking them in turn makes the top level unaffordable exactly as the store grows: six chunks sequentially exceeds the recall timeout and serves nothing. This is what makes covering every lesson possible rather than aspirational. |
recall.max_depth | 2 | how far down the tree the walk may look |
recall.max_expansions | 3 | |
recall.filter_above | 3 | Apexes below which recall serves everything without asking. There used to be no count here, only a token budget, justified as "judgement is only needed under scarcity". That was wrong about cost: measured over 57 prompts on a store that fit the budget, 15,917 of ~17,800 injected tokens went unused, and judging those same sets kept every lesson that mattered while dropping 55% of the noise. Context that fits is not context that is free. It was right about latency, which is the part that cannot be argued away: a routing call costs ~5s of CLI startup alone and ~34s on the model that routes well, in a hook that blocks the user's prompt. So a gate stays — but a small one, sized so that what it lets through is a few lines rather than a page. Three lessons served blind is cheaper than five seconds of waiting; twenty is not. |
recall.warm_prefix_above_tokens | 2000 | Candidate-list size above which routing seeds a reusable conversation instead of re-sending the list every prompt. Providers serve an identical prefix from cache at roughly a tenth of the price, but only within one conversation — and seeding costs an extra round trip, so below this the trick loses money. It is set for where the design has to work rather than where it is: at 5,000 lessons the list runs to ~225k tokens per prompt and re-sending it is not survivable. |
recall.min_cacheable_tokens | 1200 | Smallest prefix a provider will open a cache entry for. Below it, seeding writes nothing and every fork misses — so when warming is on, candidate chunks are widened until they clear this. Roughly 1024 for the larger models and 2048 for the smallest; 1200 leaves margin without making the questions much wider than they need to be. |
recall.model | null | Model for the routing decision. Null means "same as everything else", and that is the measured right answer despite looking wasteful. Choosing which of two dozen one-line summaries bear on a prompt looks like a classification job a small model should win, and it blocks the user's prompt, so the incentive to downgrade it is strong. rose eval-recall says don't: model precision recall noise tok default 48% 100% 7,146 sonnet 35% 81% 9,781 haiku 35% 75% 8,818 Both small models serve more noise and drop useful lessons. Since serving everything unfiltered has 100% recall by construction, a cheap router is strictly worse than no router at all — it costs latency, loses lessons, and does not even save context. Set this only with an eval run to back it. |
recall.stays_fresh_turns | 8 | Turns for which an already-injected lesson counts as still present and attended to. Inside it, a repeat is skipped; beyond it, the lesson is refreshed with its one-line gist rather than repeated in full. Context compaction resets this, since compaction may have removed the text. |
recall.timeout_s | 20 | Bound on the routing call, kept below the hook's own deadline so a slow judgement degrades to "inject nothing" instead of being killed. |
recall.selector | agentic | How selection is made. agentic: fork the live session and let it search the store. The only option whose cost does not grow with the number of lessons — the index is grepped, never sent — and the only one that sees the task's tool calls and reasoning rather than just its opening sentence. judge: render the apex layer into one question and walk it. Costs ~55 tokens per apex per prompt, which is ~225k at 5,000 lessons. judge is not dead code: it is the baseline every arm of rose eval-recall is compared against, and the automatic fallback whenever there is no session to fork. |
recall.selector_timeout_s | 60 | Longer than timeout_s because a search is several tool calls rather than one question, and each costs a round trip. This is the number that decides whether selection is felt as lag, so it is also the first thing to lower if it is. Raised from 45 after a migrated store timed out: a library imported verbatim holds lessons of several thousand tokens, and one whole-file read of one of those can exhaust the window on its own. The prompt now steers toward grep-with-context for that reason; this is the headroom for when it still needs a look. |
recall.selector_max_tool_calls | 6 | Searches the selector may run before it must answer with what it has. An unbounded search is the failure mode here: there is always another phrasing to try, and the user is waiting the whole time. |
routing.enabled | true | Selection lessons: what the reflector learned about where the knowledge was, as opposed to what the knowledge is. |
routing.max_tokens | 800 | The only part of retrieval that costs tokens on every prompt now that the index is searched rather than sent. It is capped rather than trusted to stay small: the claim that selection lessons stay far fewer than lessons is a bet, and rose status reports the ratio so the bet is visible. When the cap binds, the rules with the worst record of improving a selection are the ones that fall out. |
selection.w_judge | 0.60 | The model decides which dropped detail explains a failure; the other two terms are evidence (observed rescue rate) and measurement (tokens), not proxies for judgement. |
selection.w_prior | 0.28 | |
selection.w_cost | 0.12 | |
selection.explore | posterior | posterior | ucb |
selection.ucb_c | 0.7 | |
compaction.enabled | true | |
compaction.min_successes | 1 | Successful recalls before a compression is attempted. Recall serves about one lesson per prompt, so waiting for a second success can take weeks — and the wait buys little, because the thing that actually protects the lesson is replay against its own episodes, which runs either way. One success is enough of an occasion. |
compaction.max_ratio | 0.75 | Candidate must be <= this fraction of the parent's tokens. Measured against real compressors, a single step on an already-dense lesson lands around 0.7; a stricter gate simply rejects everything and leaves you at 100%. What matters is compounding, not per-step depth — 0.75 per level is ~32% of the original after four levels. |
compaction.threshold | 1.0 | required replay pass-rate |
compaction.regression_k | 5 | episodes replayed per validation |
compaction.max_level | 6 | |
compaction.cooldown_s | 900 | |
learning.enabled | true | |
learning.min_tool_calls | 8 | ignore trivial sessions |
learning.capture_failures | true | |
learning.nudge_enabled | true | Reflection needs an occasion, not a verdict. The agent has the judgement to tell a conceptual mistake from a typo, but in flight its attention is on the task — so the harness schedules the look and the agent decides what it sees. These thresholds only ask "did this turn have enough substance to be worth a thought". |
learning.nudge_mode | background | background: a detached process reflects on the transcript, and the agent is never interrupted. Costs nothing on the main thread. block: interrupt the agent to reflect in its own context. It has the live sense of what surprised it, but spends a turn and pollutes the working context — a real cost in the middle of a large task. fork: reflect inside a fork of the live session — same context, off the main thread. Costs ~0.1x its tokens thanks to prompt cache reads, but 10% of a large context still exceeds a digest. off: no reflection until session end. |
learning.nudge_after_tool_calls | 12 | |
learning.nudge_after_turns | 3 | |
learning.min_surprises | 2 | failed tool calls also make a turn substantial |
learning.nudge_cooldown_s | 900 | |
learning.nudge_backoff_after | 3 | If the agent captures on its own, stop interrupting it. Backs the cooldown off after this many nudges in a row that yielded nothing. |
placement.consult | true | ask a model how new knowledge relates to old |
placement.judge_calls | 2 | levels of tree walk when looking for related lessons |
placement.max_depth | 2 | |
placement.surface_conflicts | true | raise unresolved contradictions at recall |
signals.min_confidence | 0.5 | below this an outcome is recorded as 'unknown' |
limits.agent_timeout_s | 180 | |
limits.max_concurrent | 2 | |
privacy.redact | true | scrub secret-shaped strings before anything is stored |