> ## Documentation Index
> Fetch the complete documentation index at: https://comis-feature-matrix-channel.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Memory configuration

> Every Comis memory capability and the exact config option that enables it — recall, the recall-time signals, the one learning-reflection engine, and the maintenance jobs

**What this page is.** The complete reference for Comis's agent-memory capabilities and the
exact config option for each. **The memory features are ON by default (opt-out)** — a fresh
install runs them and you edit config to turn them off. The trust boundary
(`rag.scoring.trustAlpha`, `rag.includeTrustLevels`) is **frozen** — not a tunable capability.

<Warning>
  The LLM **build/ask** features (the session review job, the one learning-reflection cron,
  and the `memory_ask` grounded-Q\&A tool) spend **your own** LLM/API budget. They are on by
  default, and the daemon prints a first-run notice listing what's active.
  **One line turns all of them off:** `memory.enabled: false`.
</Warning>

<Note>
  The master kill-switch is `memory.enabled`, and the three recall model knobs nest under
  `memory.recall.*`. The whole per-agent learning layer is **one** `agents.<id>.learning`
  block (one `learning.enabled` flag + `learning.reflect.*` + `learning.forget.*`).
</Note>

**Where the config lives.** Capabilities are configured **per agent** under
`agents.<agentId>.` in `~/.comis/config.yaml`. The shared **memory engine** (store +
embeddings + reranker + the cost kill switch) is the top-level `memory:` block. The config
below is the **effective default** — set any `enabled: false` to opt a feature out.

<Note>
  On by default is necessary but not always sufficient — several capabilities also need
  *built derived state* (a populated graph, scored usefulness) before they change recall. See
  [Dependencies & gotchas](#dependencies--gotchas) below.
</Note>

## Default config (opt-out)

This is the **effective default** a fresh install runs. To opt out, set the relevant
`enabled: false` (or flip the master `memory.enabled: false` to silence all LLM-cost features
at once).

```yaml theme={}
# ─────────────────────────────────────────────────────────────────────────
# 1. MEMORY ENGINE (top-level) — the substrate every capability runs on
# ─────────────────────────────────────────────────────────────────────────
memory:
  enabled: true                     # MASTER KILL SWITCH — set false to disable ALL LLM cost features at once
  dbPath: memory.db
  walMode: true
  recall:                           # the $0 on-device recall substrate — NOT gated by `memory.enabled`
    embeddingModel: text-embedding-3-small   # hosted (1536d). Point at a local GGUF for on-device.
    embeddingDimensions: 1536
    rerankerModel: "hf:gpustack/bge-reranker-v2-m3-GGUF:bge-reranker-v2-m3-Q8_0.gguf"  # local by default
  rerankerModelsDir: models
  rerankerGpu: auto                 # auto | metal | cuda | vulkan | false
  rerankerThreads: 4
  compaction: { enabled: true, threshold: 1000, targetSize: 500 }
  retention: { maxAgeDays: 0 }      # 0 = keep forever

agents:
  my-agent:
    # ───────────────────────────────────────────────────────────────────
    # 2. RECALL + RECALL-TIME CAPABILITIES  (agents.<id>.rag)
    # ───────────────────────────────────────────────────────────────────
    rag:
      enabled: true                 # recall is ON by default
      maxResults: 5
      minScore: 0.1
      includeTrustLevels: [system, learned]   # trust filter — FROZEN (see Trust note)
      rerank:
        enabled: true               # default false; cross-encoder rerank (local bge)
        maxCandidates: 40
        minResults: 1
        timeoutMs: 800
      scoring:                      # ranking weights, each 0..1
        recencyAlpha: 0.2
        temporalAlpha: 0.2
        proofAlpha: 0.1
        trustAlpha: 0.1             # FROZEN — leave at the shipped value
        usefulnessAlpha: 0.1        # magnitude of the recall-utility (FEED) loop
        forgetAlpha: 0.1            # magnitude of the recall-score decay (FORGET)
      lanes:
        fts: { weight: 1.0 }
        vector: { weight: 1.5 }
        temporal: { enabled: true, weight: 1.0, windowDays: 7 }            # default off
        causal:   { enabled: true, weight: 1.0 }                           # default off
        graphSpread: { enabled: true, weight: 1.0, maxDepth: 2, fanOut: 8 } # KG lane — default off
      entityLane: { enabled: true, seedCount: 5, perEntityCap: 200, weight: 1.0 }  # default off
      mmr: { enabled: true, lambda: 0.7 }       # MMR diversity re-rank — default off
      feedback: { enabled: true }               # FEED recall-utility loop (uses scoring.usefulnessAlpha)
      forget: { enabled: true }                 # recall-score decay gate (uses scoring.forgetAlpha)
      queryUnderstanding:                       # LEARN-IQ (LLM-free)
        intentReweight: true
        synonyms: true
        temporalParse: true

    # ───────────────────────────────────────────────────────────────────
    # 3. OUTCOME SIGNAL + the ask tool
    # ───────────────────────────────────────────────────────────────────
    learningOutcome:                 # the Verified Learning outcome signal (gates reflection + forgetting)
      enabled: false                 # default OFF (opt-in)
      judge: { enabled: true }       # cost-gated LLM fallback for a conversational turn

    dialectic:                       # memory_ask grounded-Q&A tool (the one query-time LLM surface)
      enabled: true
      maxOutputTokens: 1024
      maxRecall: 10

    # ───────────────────────────────────────────────────────────────────
    # 4. MAINTENANCE / LIFECYCLE JOBS (cron-driven)
    # ───────────────────────────────────────────────────────────────────
    memoryReview:                    # session → memory review + dedup (accumulate tier)
      enabled: true
      schedule: "0 2 * * *"
      minMessages: 5
      maxSessionsPerRun: 50            # best-out-of-box: broader consolidation coverage per run
      maxReviewTokens: 16384           # best-out-of-box: richer consolidation per session

    memoryLifecycle:                 # the FORGET sweep cron — TRIMMED to enable + schedule
      enabled: true
      schedule: "0 9 * * *"          # the __LIFECYCLE__ sweep slot (keyless; thresholds live in learning.forget)

    # ───────────────────────────────────────────────────────────────────
    # 5. LEARNING — the one outcome-gated reflection engine (~10 keys)
    # ───────────────────────────────────────────────────────────────────
    learning:
      enabled: true                  # ONE master gate for the WHOLE learning layer (opt-out; force-disabled by memory.enabled: false)
      reflect:                       # the one __REFLECT__ cron (skill + profile + topic in one pass)
        schedule: "0 */3 * * *"      # best-out-of-box: every 3 hours (near-real-time learning)
        minConfidence: 0.6           # reflection-side confidence floor [0..1]
        promoteAtProofCount: 3       # verified-success count at which a doc promotes candidate → active
        maxDocsPerRun: 100           # finite DoS bound, set high so it does not throttle learning
        maxProcedureDocsSurfaced: 10 # per-agent cap on orchestrate-derived procedure docs in <available_skills> (the scaling guard)
        corroboration:               # HOW a topic must corroborate before it can seed a learned doc
          mode: single_owner         # default: learn from one explicitly-trusted owner's repetition. "distinct_sessions" = the stricter ≥2-distinct-(session,sender) gate (see below)
          minObservations: 2         # single_owner ONLY: min distinct successful repetitions by the owner (≥2; a single success never corroborates)
      forget:                        # the wrongness-based soft-eviction policy (the __LIFECYCLE__ sweep reads these)
        maxDormantDays: 365          # best-out-of-box: remember ~a year (forget pure-disuse less)
        failureEvictionFloor: 3      # corroborated failure_count at/above which a non-exempt memory is soft-evicted
        highProofFloor: 5            # high-proof exemption — a memory at/above this proof count is never failure-evicted
```

## Capability → config map

| Capability                | Enable knob (`agents.<id>.`)                                            | What it does                                                                                                                                                                                                                    |
| ------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Recall (base)             | `rag.enabled` *(default true)*                                          | Hybrid FTS + vector recall, fused + scored                                                                                                                                                                                      |
| Rerank                    | `rag.rerank.enabled`                                                    | Local cross-encoder rerank of the top candidates                                                                                                                                                                                |
| Temporal lane             | `rag.lanes.temporal.enabled`                                            | Recency-window recall lane                                                                                                                                                                                                      |
| Causal lane               | `rag.lanes.causal.enabled`                                              | Cause/effect-linked recall lane                                                                                                                                                                                                 |
| **KG** graph-spread       | `rag.lanes.graphSpread.enabled`                                         | Walks the knowledge graph from top hits (LLM-free)                                                                                                                                                                              |
| Entity lane               | `rag.entityLane.enabled`                                                | Entity-seeded recall expansion                                                                                                                                                                                                  |
| MMR diversity             | `rag.mmr.enabled`                                                       | Maximal-marginal-relevance diversification                                                                                                                                                                                      |
| **FEED** loop             | `rag.feedback.enabled`                                                  | Boosts memories that proved useful (`scoring.usefulnessAlpha`)                                                                                                                                                                  |
| **LEARN-IQ**              | `rag.queryUnderstanding.intentReweight` (+ `synonyms`, `temporalParse`) | LLM-free query understanding / lane reweighting                                                                                                                                                                                 |
| **FORGET** (recall decay) | `rag.forget.enabled`                                                    | Per-type score decay demotes stale memories at recall (`scoring.forgetAlpha`)                                                                                                                                                   |
| Outcome signal            | `learningOutcome.enabled`                                               | Records each finished turn's real task-outcome (the signal reflection + forgetting gate on)                                                                                                                                     |
| **DIALECTIC**             | `dialectic.enabled`                                                     | The `memory_ask` grounded-Q\&A tool (query-time LLM)                                                                                                                                                                            |
| Review                    | `memoryReview.enabled`                                                  | Turns sessions into reviewed memories (accumulate tier)                                                                                                                                                                         |
| Lifecycle sweep           | `memoryLifecycle.enabled`                                               | The keyless `__LIFECYCLE__` soft-eviction sweep (thresholds in `learning.forget.*`)                                                                                                                                             |
| **LEARN** (reflection)    | `learning.enabled`                                                      | The one outcome-gated reflection engine — maintains the learned **skill** docs, the per-user **profile** doc, and higher-order **topic** observations in one pass; plus the wrongness-based soft eviction (`learning.forget.*`) |

## The one learning-reflection engine

Comis's learning layer is **one outcome-gated reflection engine**. A single `__REFLECT__`
cron maintains named **Mental Model** docs
(`kind: skill | profile | topic`) via byte-stable delta-ops, gated on a **real task outcome**
(the `learningOutcome` signal) rather than a text-overlap proxy. The whole layer is governed by
**one flag** — `agents.<id>.learning.enabled` — under the master `memory.enabled` switch.

```yaml theme={}
agents:
  my-agent:
    learning:
      enabled: true            # the SINGLE gate for the whole learning layer
      reflect:
        schedule: "0 3 * * *"  # the one reflection cron
        minConfidence: 0.6     # the reflection-side confidence floor
        promoteAtProofCount: 3 # candidate → active after this many attributed successes
        maxDocsPerRun: 25      # per-run cost bound
        maxProcedureDocsSurfaced: 10 # per-agent surface cap on procedure docs (default 10)
        corroboration:
          mode: distinct_sessions # OVERRIDE the single_owner default with the stricter ≥2-distinct-sessions gate (multi-user box)
          minObservations: 2   # (single_owner only) the owner must succeed at the same task ≥2 times before it seeds a skill
      forget:
        maxDormantDays: 90
        failureEvictionFloor: 3
        highProofFloor: 5
```

* **Reflection (`learning.reflect.*`)** — the `__REFLECT__` cron clusters trusted-origin
  **successful** trajectories and distils them into Mental Model docs at `trust=learned`. A doc
  seeds only once its topic **corroborates** (`learning.reflect.corroboration.mode`, below). A learned
  doc is **advisory** (no executable column — the agent reads it and acts through its own
  already-permissioned tools); it can **never** exceed `learned` trust (a DB `CHECK` makes
  `system` structurally impossible). An admitted `candidate` **promotes to `active`** once its
  `proof_count` crosses `promoteAtProofCount` from attributed successful reuse; an active doc
  **demotes** (`active` → `stale` → `archived`) on corroborated, sustained failing reuse. The
  per-user **profile** doc (`kind:"profile"`) and higher-order **topic** observations
  (`kind:"topic"`) are maintained in the **same pass** — a profile **correction supersedes**
  the prior doc with a bi-temporal **history-append** (the prior body is preserved in the doc's
  `history`, never hard-deleted), and external-trust sources are excluded by the engine's
  two-axis anti-poison SELECT. The same pass also distils **advisory procedure docs** from
  **verifiably-successful `orchestrate` runs** — a `kind:"skill"` doc that additionally records
  the deterministic **tool footprint** of the run (the audited tool names). It is outcome-gated
  and **demote-on-failure** like every learned doc, and it is **advisory**: the model reads the
  guidance and **re-authors** the script through its own already-permissioned tools — the doc is
  never a stored or replayed template, and there is **no new execution path**.
* **Procedure-doc surface budget (`learning.reflect.maxProcedureDocsSurfaced`, default `10`)** —
  a per-agent cap on how many procedure docs surface into one prompt's `<available_skills>`. With
  no ranked top-K at surface time, a burst of procedure docs would otherwise bloat every prompt;
  the budget caps that subset only — when it is exceeded the **most-corroborated** procedure docs
  (highest proof count) keep their slot, surfaced in a stable listing order. User-intent **skill**
  docs and **topic** docs are **unaffected** — they keep a separate, uncapped path.
* **Forgetting (`learning.forget.*`)** — couples a memory's decay to its outcome-attributed
  `failure_count`, so **wrong fades faster than merely old**, and **soft-evicts** a sufficiently
  weak or stale memory: it is marked `evicted_at` (excluded from recall, still resolvable via
  `asOf`/inspect, **reversible**, never hard-deleted). A memory implicated in
  `failureEvictionFloor` (default 3) or more **corroborated** failures is soft-evicted regardless
  of its decayed strength, while a memory at/above `highProofFloor` (default 5) is **exempt**.
  A **corroboration gate** (≥2 independent failures, or one deterministic source, on every
  `failure_count` increment) plus exemptions for pinned / `system` / high-`proof_count` memories
  make induced eviction impractical: a poisoner cannot evict a well-corroborated, pinned, or
  system memory no matter how many failures it induces. The sweep runs on the keyless
  `memoryLifecycle` cron (`__LIFECYCLE__`).

Recall **ranking** itself is the **fixed `rag.scoring`** fusion (deterministic RRF + the
cross-encoder reranker) — there is no learned recall weight.

### Corroboration: single-owner (default) vs distinct-sessions (`learning.reflect.corroboration`)

Before a topic can seed a learned doc it must **corroborate**. The mode governs what counts as
corroboration:

* **`mode: single_owner`** (**default**) — **repetition-as-corroboration**, tuned for the primary
  Comis use case: a **single-owner deployment** (one trusted DM). One stable session means the
  distinct-sessions gate below is structurally unreachable (cardinality is always 1), so it would
  never learn anything. In `single_owner` mode the engine counts an **explicitly-trusted owner's
  repeated successes**: when the owner succeeds at the same task **≥`minObservations`** times
  (default `2`, never one-shot), the topic corroborates. The distinct-sessions gate still applies
  as a superset, so a box with **two or more** explicitly-trusted senders is handled the
  multi-user way automatically.
* **`mode: distinct_sessions`** — the classic **anti-domination** gate: a topic needs **≥2 distinct
  `(session, sender)`** successful observations. N near-identical successes from one sender in one
  session count **once**. Set this explicitly on a multi-user box that wants to **require**
  independent sessions even from its single trusted owner (the stricter posture).

<Note>
  **`single_owner` is safe by construction as a default**, even on a multi-user box. Repetition is
  counted **only** among senders the operator **explicitly named** in
  [`elevatedReply.senderTrustMap`](/reference/config-yaml) (resolving to a non-external tier) — a
  sender trusted merely by a promiscuous `defaultTrustLevel`, or an unknown sender, can ride past
  the success filter yet **never** self-corroborate by repetition. It also requires a **single**
  distinct owner, so a box with **≥2** explicitly-trusted senders falls back to the
  distinct-sessions gate. The only behavior change versus `distinct_sessions` is for a box with
  **exactly one** explicitly-trusted owner: it learns from that owner's repetition (one session)
  instead of requiring two sessions. Choose `distinct_sessions` if you want to forbid even that.
</Note>

The `reflect:funnel` telemetry carries a `singleOwnerCorroborated` count (how many topics
corroborated via repetition this run), so [`comis explain`](/reference/cli#comis-explain) and
[`comis fleet`](/reference/cli#comis-fleet) show the mode is active — an `admitted > 0` run with
`maxClusterCardinality: 1` reads as single-owner learning, not a contradiction.

See [`learning` in config-yaml](/reference/config-yaml#learning-agents-learning) for every key
and default, the [`mental_models`](/operations/data-directory#learned-skills-verified-learning)
and [`memory_usefulness` / `evicted_at`](/operations/data-directory#ranking--forgetting-verified-learning)
storage, and the **counts-only** trajectory telemetry (`reflect:admitted` / `reflect:funnel`
for the reflection funnel; `learning:memory_demoted` / `learning:memory_evicted` for the
forget sweep) surfaced through [`comis explain`](/reference/cli#comis-explain) and
[`comis fleet`](/reference/cli#comis-fleet).

## Strict config schema

The config schema is **strict** (`z.strictObject`): a `config.yaml` that carries an
unrecognized key is **rejected at boot** with a parse error naming the exact key, so a typo
or an unsupported block is pointed straight at the line to fix. A **bare config**
(`memory:`/`agents:` with no learning keys) loads fully-defaulted.

## Dependencies & gotchas

1. **Some capabilities need built derived state, not just the flag:**
   * **KG** (`rag.lanes.graphSpread`) is a no-op until the knowledge graph is populated with
     entities/edges.
   * **FORGET** decay (`rag.forget`) shows up at recall; **eviction** is the `memoryLifecycle`
     sweep — live as **soft, reversible** eviction (`evicted_at`) when `learning.enabled` is on
     (it reads `learning.forget.*`), exempting pinned/system/high-proof memories. See
     [the one learning-reflection engine](#the-one-learning-reflection-engine).
   * **Reflection** needs a stream of trusted-origin **successful** outcomes to learn from — it
     gates on the `learningOutcome` signal, and abstains (a benign skip, never a failure) when a
     run finds no corroborated success.
2. **`dialectic` (`memory_ask`) spends tokens per ask** — it is the only query-time LLM
   surface in the memory stack. Everything else in recall is LLM-free.
3. **Trust is frozen, not a tunable capability.** `rag.scoring.trustAlpha` and
   `rag.includeTrustLevels` are the trust hard-boundary; leave them at the shipped values.
4. **On by default — watch your spend.** The LLM build/ask features are opt-out so operators
   get the full memory stack from day one; they spend your own budget. Your controls are the
   first-run notice and the master switch `memory.enabled: false` (or per-feature
   `enabled: false`). Measure the effect in your own domain — Comis's honest, reproducible
   methodology and the latest costed results are on the
   [Memory benchmarks](/agents/memory-benchmarks) page.

See also: [Memory](/agents/memory) · [Search](/agents/search) · [Embeddings](/agents/embeddings) · [RAG](/agents/rag).
