---
name: mention-scan
description: Engine substrate — scans narrative content (journal, EOD reports, decisions, goal pursuit logs, channel messages, etc.) for references to known entities and emits `references` triples that
  surface as backlinks on entity wiki pages.
title: Mention Scanner
category: _meta
status: active
author: system
metadata:
  priority: 0
---
# Mention scanner

This is **engine substrate**, not an agent skill. It is never injected into an agent prompt. It exists so the strategy of "what counts as a mention" lives in YAML, never in Python.

## What it does

Whenever a narrative content artifact is written or updated (a journal entry, an EOD report, a daily memory file, a goal pursuit log entry, a decision rationale, a chat message, a channel message), the scanner walks the text, finds references to known entities in the registry, and emits `(source_path, references, entity_slug)` triples with snippet context.

The triples surface in the wiki UI as a `Mentioned in` panel on every entity page — the user sees every place an entity is referenced across narrative content, not just where it has explicit `[[wikilinks]]`.

## Why `references` and NOT `mentions`

The ontology's `mentions` predicate is reserved for the undirected wikilink co-occurrence graph, which the graph UI hides by default as "wikilink noise." Adding a directed SPO edge with the same name would silently disappear in the visualisation. `references` is a new directed predicate — narrative source → entity — that renders as a distinct edge tier.

## Scan modes

The scanner runs three independent passes; results are deduplicated by (source_path, entity_slug):

1. **Wikilink scan** (cheapest, highest precision) — finds `[[Display Name]]` patterns and resolves through the registry. Already explicit, always counted.
2. **Title-Case scan** — `extract_entity_candidates` walks Title-Case runs and resolves through the registry. Catches "Sarah Chen joined the meeting" without requiring brackets.
3. **Alias-substring scan** — for high-confidence multi-word aliases the user has explicitly registered, scan the text for the literal alias substring (case-insensitive, word-boundary).

Single-word names are intentionally NOT matched in Title-Case mode — too many false positives ("Today" is not an entity). Single-word entities only match if explicitly bracketed or if they appear as a registered alias.

## Configuration

All knobs live in [config/scan.yaml](config/scan.yaml). Edit and restart to retune; no Python changes.

- `enabled` — master kill switch (default `true`)
- `snippet_chars_before` / `snippet_chars_after` — context window around each match
- `min_match_length` — reject mentions shorter than this many characters (default 2)
- `max_mentions_per_source` — cap per source to avoid runaway in pathological inputs (default 200)
- `ignore_segments` — file-path substrings whose content should NEVER be scanned (e.g. `.history/`, `.archives/`, `.quarantine/`)
- `context_types` — recognized labels (`journal_user`, `journal_agent`, `eod_report`, `daily_memory`, `goal_pursuit_log`, `decision`, `chat:<channel>`, `email`, etc.). Free-form — the scanner doesn't care, the UI groups by them.

## Triple shape

Each detected mention is recorded as:

```
(source_path, references, entity_slug)
  authority         = "system"
  epistemic_status  = "believed"
  confidence        = 0.7
  knowledge_type    = "episodic"
  decay_condition   = "when:unaccessed:90d"
  situational_context = {
    snippet:       "<text around the match>",
    context_type:  "<label>",
    matched_text:  "<exact span that matched>",
    offset:        <int>,
    when:          "<ISO8601>",
  }
```

Source `path` is the vault-relative path of the document (or a synthetic `chat:<channel>:<conv_id>:<msg_id>` for chat messages). Subject/object are resolved through the registry like any other triple — so the same dedup, supersession, and decay rules apply.

## What it does NOT do

- **Does not modify the source text.** Pure read.
- **Does not write user files.** Triples land in the graph (`memory/graph/triples.json`); source files are untouched.
- **Does not extract triples from prose.** That's the LLM triple-extractor's job. This is the deterministic complement: every Title-Case name in the prose becomes a graph edge, regardless of whether the LLM noticed it.
- **Does not run on KB sources.** Wiki articles, library files, source summaries already enter the graph through the ingest pipeline. Mention scanning is only for ad-hoc narrative content the user/agent produces in journal/memory/decision/chat surfaces.