reflect is configured in two ways: a layered TOML file (reflect.toml) and environment variables. Most day-to-day tuning is environment variables, because the hooks and the drain run as separate processes that the harness launches. Most reflect.toml keys are either read by the ingest and discovery scripts or are reserved. Both are listed in full below, with the status of each key checked against the code that consumes it.
Used by ingest, discovery, the DB layer, the mode loader, the signal detector, the cascade and the lifecycle-event emitter. Layers are deep-merged, later wins:
#
Layer
Path
1
Built-in defaults
hard-coded in _BUILTIN_DEFAULTS
2
Plugin default
reflect.toml next to the plugin (plugin/reflect.toml)
A missing or malformed file is skipped silently (an empty table). Values from the environment are cast to the key’s type: lists split on commas, booleans are false for 0, false, no, off or empty, ints and floats are parsed (a malformed number raises). The result is cached per process.
The user override path is fixed at ~/.reflect/reflect.toml; it does not follow $REFLECT_STATE_DIR.
Used only by reflect issues run, which reads the [issues] table. First existing file wins; there is no merging.
#
Path
1
$REFLECT_CONFIG
2
$REFLECT_STATE_DIR/reflect.toml
3
~/.reflect/reflect.toml
4
bundled plugin/reflect.toml, found by walking up from the module (editable checkouts only)
A missing or malformed file yields {}, so the CLI always has defaults. For reflect issues run, an explicit flag beats [issues], which beats the built-in default.
mode_loader.py resolves the active mode in this order: REFLECT_MODE env, then <project>/.reflect/config.json ({"mode": "<id>"}, written by mode_loader.py set <id>), then the mode key of the merged TOML cascade, then engineering. The project root is $CLAUDE_PROJECT_DIR, else the nearest .git ancestor, else cwd.
python3plugin/scripts/reflect_config.py# merged plugin-side config as JSON
Run it from the project directory so ./.reflect.toml is included. For an installed plugin the script sits under the plugin root (${CLAUDE_PLUGIN_ROOT}/scripts/reflect_config.py).
Status column: live means code reads the key; reserved means it is parsed and merged but no code in this repo reads it, so changing it has no effect (use the env var named in the Effect column where one exists); env-only means it exists only as a built-in default plus an env overlay.
Providers memory_discovery.py scans for ingest. Only claude, codex, copilot, gemini have a provider class; hermes is silently skipped. A provider is also skipped when its home directory does not exist. Env REFLECT_PROVIDERS (comma-separated).
staleness_days
int
30
reserved
Intended stale-source threshold. Nothing reads it. Env REFLECT_STALENESS_DAYS feeds the same unread key.
All four keys are declarative only: recall.py runs as a standalone script and reads env vars, not this table. The REFLECT_RECALL_CE_* overlay variables write into the config dict and nothing else, so they do not change recall behavior.
Also declarative; the live control is the env var. Each boost is bounded: a hit’s score is multiplied by 1 + alpha * (norm - 0.5), so 0.2 is at most plus or minus 10 percent. 0 disables.
Key
Type
Default
Status
Real control
project_affinity_alpha
float
0.2
reserved
RECALL_PROJECT_ALPHA (default 0.2). Same-project hits gain up to 10 percent; cross-project hits are unchanged.
domain_affinity_alpha
float
0.2
reserved
RECALL_DOMAIN_ALPHA (default 0.2). Applies when a --domain-hint matches the learning’s domain.
authority_alpha
float
0.1
reserved
RECALL_AUTHORITY_ALPHA (default 0.1). law and promoted notes rank above advisory; archived takes the floor.
[recall.arm.<name>] (built-in default, absent from the shipped file)
Calibrated per-arm out-of-domain floors (reflect calibrate-thresholds). They exist only in _BUILTIN_DEFAULTS and the env overlay. recall.py ignores them: its runtime default for every arm is 0 (off), and it only changes when you export the matching RECALL_ARM_<NAME>_MIN_SCORE.
Per-ingest semantic dedup. A revise CREATE whose embedding cosine to an existing learning is at or above this value is held for a focused merge-or-keep verdict instead of landing as a near-duplicate. >= 1.0 disables the probe. Env REFLECT_DEDUP_THRESHOLD wins over the file; an unparseable value falls back to 0.97.
Defaults for reflect issues run. Flags override. Empty string disables label and title_prefix.
[events.on]
plugin-side
<event> = "<shell command>" for learning.created, learning.updated, skill.refreshed, consolidation.completed
Runs the command (through the shell) after the event is appended to events.jsonl. Receives REFLECT_EVENT and REFLECT_EVENT_PAYLOAD (JSON) in its environment. Env REFLECT_EVENTS_ON_<EVENT> (dots become underscores, upper case) wins over the file. A failing hook never breaks the emitter.
Variables are read at call time unless noted. Booleans written “truthy” accept 1, true, yes, on (case-insensitive). Booleans written “0 disables” are on unless the value is exactly 0.
Explicit reflect.toml path for the engine-side loader (first in its search order). Not read by the plugin-side loader. See Search order.
REFLECT_STATE_DIR
~/.reflect
Root for the queue, armed files, ledgers, logs, errors, cost log, drain lock, models cache and fleet ledger. Read by every hook, the drain, the engine and recall.
GLOBAL_LEARNINGS_PATH
~/.learnings
Knowledge-base root; learnings live under documents/. Setting it pins recall to that KB and disables per-project shard selection. Also read by the CLI, the corpus filter and the maintenance watchdog.
REFLECT_LEARNINGS_DIR
~/.learnings/documents
Directory hooks write mini-learnings and TodoWrite learnings into. Note this is the documents/ directory itself, not the KB root.
RECALL_LEARNINGS_ROOT
~/.learnings
Root that per-project shards (shards/<project>/, shards/<project>/branches/<branch>/) live under.
REFLECT_METRICS_PATH
~/.learnings/metrics.jsonl
Recall follow-up metrics, reflect_cost.py, and Hermes shadow telemetry. The engine’s own reflect metrics writer uses a fixed path and does not honor this.
REFLECT_SKILLS_DIR
~/.claude/skills
Directory indexed for the skills tier of SessionStart recall.
REFLECT_MODES_DIR
<plugin>/references/modes
Directory of mode JSON files.
REFLECT_POLICY_FILE
unset
Extra policy-rules JSONL, read before policy-rules.jsonl and permission-policy.jsonl in the state dir. See Policy rules file.
CLAUDE_PROJECT_DIR
unset (set by Claude Code)
Project root for the hooks, mode loader and artifact paths. Falls back to the stdin cwd, then the process cwd.
1 makes the drain, the idle sweep and the maintenance watchdog exit immediately. It does not disable recall hooks or the Stop, SessionEnd, SubagentStop and PreCompact queue producers.
REFLECT_AUTO_REFLECT
on
0, false, no or off makes precompact_reflect.py a no-op. Read by no other hook.
REFLECT_POSTCOMPACT_RESET_DEDUPE
unset
Exactly 1: PostCompact deletes the session’s session-injected dedupe file.
REFLECT_SLOTS
off (truthy)
Memory slots. Injects the slots block ahead of recall at SessionStart, and runs the deterministic slot-reflect pass at Stop.
REFLECT_TIERED_INJECT
off (truthy)
Enables the skills tier (a strong skill hit replaces raw recall) and the one-line CONVENTIONS pointer at SessionStart.
REFLECT_SKILL_TIER_MIN_SCORE
2.0
Minimum skills-index score that counts as a strong hit (name or tag hit scores 2.0, summary hit 1.0).
REFLECT_CONVENTIONS_SYMLINK
off (truthy)
Also create a CONVENTIONS.md symlink in the project root. Writes into your repo, so off by default.
REFLECT_RECALL_TIMEOUT
30
Seconds allowed for the recall subprocess in the SessionStart and UserPromptSubmit hooks. Invalid values fall back to 30.
REFLECT_RECALL_MIN_OVERLAP
0.2 in the SessionStart hook, 0.0 in recall.py
Out-of-domain gate: minimum query-term coverage of the top hit. A non-numeric value crashes the SessionStart hook at import.
REFLECT_RECALL_MAX_TOKENS
0
Token budget for the SessionStart block (0 means max-chars only). Must be an integer.
REFLECT_SUBAGENT_RECALL_TIMEOUT
5
Seconds for the SubagentStart recall. Must be numeric.
REFLECT_SUBAGENT_RECALL_LIMIT
3
Learnings injected at SubagentStart.
REFLECT_SUBAGENT_RECALL_MAX_CHARS
1500
Character budget at SubagentStart.
REFLECT_SUBAGENT_CONTEXT
unset
If set (even to empty), used verbatim instead of running recall at SubagentStart. For tests and fixed context.
REFLECT_HARNESS
unset
copilot selects Copilot’s {"additionalContext": ...} envelope. The Copilot adapter prefixes it on every hook command; the Hermes shim sets hermes for its child process. Recorded on queue entries written by the shared helper (unknown when unset).
Read by plugin/skills/recall/scripts/recall.py. These tune ranking and are safe to leave alone. See Recall pipeline and Retrieval features.
Variable
Default
Effect
REFLECT_RECALL_LIMIT
10
Default result count for recall.py (the hooks pass 3).
REFLECT_RECALL_MAX_CHARS
2000
Default character budget for recall.py (the hooks pass 1500).
REFLECT_RECALL_DEBUG
unset
Any non-empty value prints cache and debug warnings to stderr.
REFLECT_RECALL_HYDE
unset
Exactly 1: expand the query with a hypothetical answer sentence from claude -p (uses REFLECT_DRAIN_MODEL). Falls back to the raw query on any failure.
RECALL_GRAPH_ARM
on
0 disables the graph-expansion arm.
RECALL_CROSS_ENCODER
on
0 disables cross-encoder rerank (falls back to the formula).
RECALL_CE_TIMEOUT
60
Seconds for the rerank call.
RECALL_MMR
on
0 disables MMR diversity selection.
RECALL_MMR_LAMBDA
0.7
1.0 is pure relevance, 0.0 pure diversity.
RECALL_EMBED_TIMEOUT
60
Seconds for the embedding call used by MMR.
RECALL_TEMPORAL
on
0 disables query date-phrase extraction.
RECALL_TEMPORAL_ARM
on
0 disables the temporal retrieval arm.
REFLECT_RECALL_INCLUDE_SUPERSEDED
unset
1 keeps superseded and archived notes (frontmatter superseded_by or status, archived/ and .forgotten/ ids, ledger is_latest = 0) in recall results, for debugging.
RECALL_BITEMPORAL_EDGES
on
0 disables the supersession filter on graph edges for dated queries.
RECALL_FUZZY_CACHE
on
0 disables the fuzzy (Jaccard) cache tier.
RECALL_FUZZY_THRESHOLD
0.85
Minimum token-set similarity for a fuzzy cache hit (0 to 1).
RECALL_GAP_LOG
on
0 stops logging zero-result queries as knowledge gaps.
RECALL_FOLLOWUP
on
0 disables follow-up-rate tracking.
RECALL_FOLLOWUP_WINDOW_SECONDS
30
Follow-up detection window (floor 1).
RECALL_ECONOMICS
on
0 hides per-learning and total token-economics numbers.
RECALL_ARM_{VECTOR,BM25,GRAPH,TEMPORAL}_MIN_SCORE
0 (off)
Per-arm query-term-coverage floor before fusion, clamped 0 to 1.
Project and domain affinity boosts; speculative notes take the floor (minus 10 percent).
RECALL_GLOBAL
off (truthy)
Search the pooled ~/.learnings KB instead of the current project shard.
RECALL_ALL_BRANCHES
off (truthy)
Search every branch of the current project instead of the current branch sub-shard.
RECALL_BRANCH
current git branch
Override the branch used to pick the sub-shard. main, master and detached HEAD map to the project-level shard. The SessionStart hook sets it for its child processes.
Shard precedence, highest first: an explicit GLOBAL_LEARNINGS_PATH, then --global or RECALL_GLOBAL, then the current project shard, then the pooled KB.
Read by plugin/hooks/reflect-drain-bg.sh and a few helper scripts. See Drain for the pipeline these cap.
Variable
Default
Effect
REFLECT_DRAIN_MAX
3
Max queue entries per drain run.
REFLECT_DRAIN_DAILY_MAX
20
Max entries per UTC day.
REFLECT_DRAIN_MAX_RETRIES
3
Per-entry retries before the transcript is poisoned (archived, never retried).
REFLECT_DRAIN_TIMEOUT
300
Per-entry claude -p wall-clock cap, seconds.
REFLECT_DRAIN_TIMEOUT_RETRIES
1
Timeout or no-output retries before quarantine.
REFLECT_DRAIN_MAX_TURNS
16
Turn budget per claude -p run.
REFLECT_DRAIN_TOKEN_MAX
2000000
A completed run reporting more total tokens than this is poisoned so it cannot be retried.
REFLECT_DRAIN_MODEL
sonnet
--model alias for the writer.
REFLECT_DRAIN_WRITER
extract
extract: one tool-free call that emits JSON actions, executed deterministically (default since 5.2.5). agentic: legacy multi-turn /reflect loop. Entries with no slice fall back to agentic.
REFLECT_DRAIN_CASCADE
1
Gate and slice each transcript before spending. Any value other than 1 disables it.
REFLECT_DRAIN_MAX_INPUT_CHARS
60000
Hard cap on writer input when no cascade slice bounded it; larger inputs are cut to a head-plus-tail window.
REFLECT_DRAIN_DEBOUNCE_SEC
600
Minimum seconds between drain runs, collapsing a burst of session starts. The launchd plist sets it to 0.
REFLECT_DRAIN_CWD
$HOME
Working directory for claude -p (neutral, not the triggering repo).
REFLECT_DRAIN_CLAUDE_BIN
claude
Path to the claude binary.
REFLECT_DRAIN_REFLECT_BIN
reflect on PATH, else ~/.local/bin/reflect
Path to the reflect binary for the writer’s reflect add calls.
REFLECT_DRAIN_INVALID_THRESHOLD
3
Consecutive non-valid writer outputs before the writer-drift breaker poisons the transcript.
REFLECT_DRAIN_MAINTAIN_EVERY
10
Run the graph-maintenance sweep (orphan and stale prune, relink) once per N reindexing drains. 0 disables.
REFLECT_DRAIN_SKIP_REINDEX
0
1 skips the incremental reindex after a drain.
REFLECT_DRAIN_LOG_MAX_BYTES
10485760
drain.log rotation threshold.
REFLECT_DRAIN_DRY_RUN
0
1 logs what would run and never calls claude -p. Side-effect free: the queue, daily cap, ledgers and dedup hashes are left untouched. Only drain.log is written.
REFLECT_DRAIN_NO_DELEGATE
0
Internal. 1 stops a Codex-installed copy of the drain from delegating to the newest Claude plugin-cache copy.
REFLECT_QUOTA_GATE
1
Any value other than 1 skips the subscription-quota gate. When the gate is on, the queue is deferred (quota_near_limit) near a limit and replays later.
REFLECT_QUOTA_TTL_SEC
3600
Freshness window for a quota snapshot; a stale snapshot opens the gate.
REFLECT_QUOTA_UTIL_THRESHOLD
per-window
Single ceiling (greater than 0, up to 1) applied to every window. Defaults: five-hour 0.95, seven-day 0.93, seven-day Opus 0.93, seven-day Sonnet 0.92, overage 0.95.
REFLECT_QUIET_INSTALL_WARNING
0
1 suppresses the missing-reflect-CLI notice.
REFLECT_SYNTHESIS_AUTO_THRESHOLD
30
New learnings since the last pass that trigger the synthesis job early.
REFLECT_DEDUP_THRESHOLD
0.97
See [cascade].dedup_threshold.
ANTHROPIC_API_KEY
unset
Read by the quota gate only: when set, the gate treats you as API-billed and never defers.
Embedding model for indexing and reflect embed. Changing it needs a fresh reindex, because similarity must stay in one vector space.
REFLECT_CE_MODEL
cross-encoder/ms-marco-MiniLM-L-6-v2
Cross-encoder rerank model (about 90 MB, downloaded on first use).
REFLECT_NO_DAEMON
unset
Exactly 1: skip the model daemon and load models in-process every call.
REFLECT_IDLE_TIMEOUT
1800
Seconds the model daemon idles before exiting (0 means never).
REFLECT_DAEMON_TIMEOUT
120
Client per-request timeout to the daemon, seconds (covers a cold model load).
TMPDIR
/tmp
Where the daemon socket and locks go; falls back to /tmp if the path would exceed the Unix socket limit.
HF_HOME, SENTENCE_TRANSFORMERS_HOME
$REFLECT_STATE_DIR/models
Set via setdefault before loading the cross-encoder; your own value wins.
HF_HUB_DISABLE_TELEMETRY
1 (set if unset)
Set by the cross-encoder loader.
HF_HUB_OFFLINE, TRANSFORMERS_OFFLINE
1 (set if unset)
Set by the two recall hooks so a cached model never hits the network. Export 0 to allow downloads.
REFLECT_PG_DSN
unset
Postgres DSN. With REFLECT_WORKSPACE_ID, moves the graph, vectors and community reports to shared Postgres instead of local files. The generic DATABASE_URL is deliberately not a trigger.
REFLECT_WORKSPACE_ID
unset
Tenant scope for the Postgres backend. Required together with the DSN.
DATABASE_URL
unset
Fallback DSN inside the Postgres nano-graphrag connection helper when no pg_dsn is passed.
CLAUDECODE, CODEX_CLI, GITHUB_COPILOT
unset
Presence selects the harness label (claude, codex, copilot, else other) recorded on metrics lines.
CLAUDE_PLUGIN_ROOT
unset (set by Claude Code)
reflect timeline uses it to find scripts/reflect_timeline.sh.
Reserved reflect.toml keys: discovery.staleness_days, indexers.graphrag.*, policies.* (all three), telemetry.*, providers.hermes.home_dir, every recall.* key. Their overlay env vars also have no effect.
hermes in discovery.enabled_providers is accepted and skipped.
A malformed numeric value in REFLECT_RECALL_MIN_OVERLAP, REFLECT_RECALL_MAX_TOKENS or REFLECT_SUBAGENT_RECALL_TIMEOUT raises at import, before the hook’s silent-fail wrapper. See the caution on the Hooks reference.
REFLECT_DISABLED gates only the drain family. To stop hooks, disable the plugin.