# `Foresight.Reflect.MentalModels`
[🔗](https://github.com/fosferon/foresight/blob/v0.3.2/lib/foresight/reflect/mental_models.ex#L1)

Mental models for reflect, at parity with Hindsight: ranked by embedding
similarity to the query, capped, and reported with a per-model staleness verdict.

Mental models are documents (`metadata.document_type = "mental_model"`). Their
embeddings live in `mental_model_embeddings` and are kept current lazily: before a
search the models whose content, dimension or model no longer match their stored
row are re-embedded under the bank's embedding profile, so curated models that
predate this table get indexed on first use and a profile change re-indexes them.

A model is stale when a memory in its scope was written after
`last_refreshed_at`. The scope is the model's tags under `trigger.tags_match`
(`any` | `all` | `any_strict` | `all_strict`, the recall semantics; the
non-strict modes also admit untagged memories) and `trigger.fact_types` when set.
An untagged model is global and any new memory makes it stale. A model never
refreshed is stale by definition.

# `staleness`

```elixir
@type staleness() :: {boolean(), String.t() | nil}
```

# `ensure_embeddings`

```elixir
@spec ensure_embeddings(module(), String.t(), [struct()], map(), module(), keyword()) ::
  :ok
```

Embed (or re-embed) every model whose stored embedding is missing or outdated.

# `entry`

```elixir
@spec entry(struct(), float() | nil, staleness()) :: map()
```

The tool/result entry for one ranked model.

# `rank`

```elixir
@spec rank(
  module(),
  String.t(),
  [struct()],
  String.t(),
  pos_integer(),
  map() | nil,
  module() | nil,
  keyword()
) :: [{struct(), float() | nil}]
```

Order `documents` by cosine similarity of their stored embedding to `query`, most
similar first, models without a comparable embedding last. Falls back to lexical
ranking when the query is blank, the profile is unknown or the query cannot be
embedded. Returns `{document, relevance | nil}` pairs, at most `limit`.

# `search`

```elixir
@spec search(module(), String.t(), [struct()], String.t(), pos_integer(), keyword()) ::
  [map()]
```

Rank `documents` for `query`, embed the stragglers first, and return at most
`limit` tool entries (Hindsight's `tool_search_mental_models` shape plus the
`title`/`text` keys older callers read). Without a usable profile or query the
lexical ranking stands in, with `relevance` absent.

# `stale?`

```elixir
@spec stale?(module(), String.t(), struct(), keyword()) :: staleness()
```

Whether new in-scope memories arrived since the model's last refresh, with the reason.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
