# Limitations

What aelfrice doesn't do yet. Tracked openly.

## The big one: feedback doesn't drive ranking (lifted at v1.3.0, partially)

Through v1.2.x: `apply_feedback` updates `(α, β)` and writes an audit row. The posterior mean is exposed via `aelf stats` and the MCP. But L1 retrieval orders hits by `bm25(beliefs_fts)` alone, not by posterior. Marking a belief `harmful` weakens its math, but the next retrieval that matches its keywords still surfaces it.

The benchmark harness shipped at v1.0 as the measurement instrument; it was not yet a proof of the feedback claim. The v1.3 partial Bayesian re-rank, v1.6 deferred-feedback sweeper, and v1.7 BM25F default-on flip (see below) progressively closed that gap.

**At v1.3.0:** ranking begins to consume the posterior. L1 score becomes `log(bm25) + 0.5 * log(posterior_mean)`; locked beliefs (L0) bypass scoring as before; the cache invalidates correctly through the existing store-mutation hook. **At v1.6.0:** the MRR-uplift + ECE-calibration eval harness ships at `benchmarks/posterior_ranking.py` and the heat-kernel composition wiring lands as a log-additive term in the ranking score (#151, #306, #310). Both ship default-OFF; the default-flip is gated on the harness clearing the MRR-uplift / ECE thresholds against the v2.0 corpus and lands at v1.7.0 (#154). See [`docs/bayesian_ranking.md`](bayesian_ranking.md) for the v1.3 contract.

## No semantic similarity

Retrieval is BM25 keyword search over FTS5 (porter unicode61 stemming) — BM25F with anchor-text augmentation since v1.7.0 default-on. "deploy" will not surface "publish to prod" without tokenizable substring overlap.

This is a deliberate scope choice, not a roadmap item. Adding embeddings would break determinism end-to-end — see [PHILOSOPHY § Determinism is the property](PHILOSOPHY.md#determinism-is-the-property). The principled response to fuzzy semantic recall queries is to pair aelfrice with a separate tool, not blend embeddings into the retrieval path.

## Onboarding scope

The CLI scanner walks three sources: prose files (`*.md`, `*.rst`, `*.txt`, `*.adoc`), `git log`, and Python AST. Not yet wired: JavaScript / TypeScript / Rust / Go ASTs.

The research line shipped a wider set of extractors — citation-ref scanning across markdown bodies, test↔implementation linkage from filename and import patterns, and a directive detector that captured imperative user statements as TODO beliefs. These remain scope-deferred; the directive-detection path in particular has architectural implications (it would land alongside the violation-detection tier of [PHILOSOPHY § What we can and can't guarantee](PHILOSOPHY.md#what-we-can-and-cant-guarantee)).

Classification on the CLI path defaults to regex-based priors. Higher-quality classification is available via three paths:
- **`/aelf:onboard <path>`** (v1.5.x, default-on) — host-driven flow that orchestrates the four-class classifier through the host model's Task tool. No API key required. Soft-falls to the regex classifier when the host's Task tool is unavailable. See [llm_classifier.md](llm_classifier.md).
- **MCP `aelf:onboard`** polymorphic flow, which routes through the host LLM.
- **`aelf onboard --llm-classify`** (v1.3+, default-off) — routes through Claude Haiku directly. Requires `ANTHROPIC_API_KEY`. Four consent gates enforce the privacy boundary. See [llm_classifier.md](llm_classifier.md).

## BFS multi-hop temporal coherence

The v1.3.0 BFS multi-hop expansion (see [bfs_multihop.md](bfs_multihop.md)) resolves each hop to the globally latest serial of its target belief independently. For audit-shaped queries ("what did the agent believe at the time it made this decision?") this can surface a chain whose intermediate hops postdate the seed's session — a fidelity loss against a corpus where supersessions accumulated after the seed was written.

The default retrieval mode (recall, not audit) is correctly served by latest-serial-per-hop. The temporal-coherence fix is targeted at v2.0.0 alongside the published-numbers reproducibility harness; the spec § Open question: temporal coherence documents why deferring is the right call for v1.3.

## Sharp edges

- **Locks are durable.** Fresh lock = `(α, β) = (9.0, 0.5)`. Five independent contradicting feedback events are required to auto-demote. If you lock a wrong rule and correct it only once or twice, the lock keeps winning. `aelf demote <id>` drops it immediately.
- **`CONTRADICTS` edges drive demotion pressure.** The regex classifier rarely produces them, so demotion pressure accumulates only from manual feedback unless you wire commit-ingest or transcript-ingest. Note that this means the README-level "catches contradictions and flags them" claim is shipped *partially* at v1.x: contradiction **resolution** (`aelf resolve`, the tie-breaker that picks a winner given a CONTRADICTS edge) is in v1.0; contradiction **detection** (the post-insert semantic-divergence detector that creates those edges automatically) is deferred. Until detection lands, contradiction-flagging surfaces only on edges produced by the triple extractor's six explicit relation-family regexes.
- **Natural-language sentiment is not captured automatically.** Saying "ok good" or "no that's wrong" in chat does **not** strengthen or weaken the beliefs the agent just used. Explicit `aelf feedback <id> used|harmful` (CLI) or the equivalent MCP `aelf:feedback` call remains the high-confidence channel. Since v1.6 (#191), the deferred-feedback sweeper does emit *implicit* posterior signal — it observes which retrieved beliefs the host's subsequent turn references (string-overlap of retrieved beliefs against the next prompt + assistant response) and writes `helped` / `noise` events. That is an exposure-based signal, not a sentiment-from-prose signal: the user's affect words are still not parsed.
- **Confidence drops below 0.5 do not auto-flag.** A belief whose posterior drifts under the prior is not surfaced as a warning. The only automatic state change driven by negative evidence is locked-belief demotion-pressure (≥5 contradictions → auto-demote). To find drifting beliefs, query `aelf stats` directly.
- **Jeffreys prior reads as 0.5.** A belief with no feedback reports posterior mean exactly `0.5`. That means "no evidence yet," not "coin-flip true."
- **`aelf onboard` is non-incremental on duplicates.** Re-runs are idempotent; existing beliefs are not re-scored or refreshed. Since v1.7.0 (#264, closed), every ingest entry point writes to `ingest_log` and materialises `beliefs` via the derivation worker, so `aelf doctor --derive-pending` runs the worker once over any unstamped log rows — useful when a prior batch crashed mid-stamp. The view-flip that makes `beliefs` / `edges` a materialized projection of the log (#265, still open) is the v2.x piece that would enable `aelf rebuild --rule-set <hash>` re-derivation without a re-onboard.
- **Near-duplicates from different ingest paths persist.** `INSERT OR IGNORE` on `(source, sentence)` content_hash dedupes exact matches but not paraphrases (e.g. "don't push to main" locked + "never push directly to main" scanned both surface in retrieval). The v1.7.0 dedup audit pass + `aelf doctor dedup` CLI (#197 R1, see CHANGELOG) lists Jaccard + Levenshtein-confirmed duplicate clusters read-only; the write-path SUPERSEDES hook (#197 full module) is bench-gated and still deferred.
- **No bulk operations.** No batch lock, no `delete <pattern>`, no merge.
- **No edit.** A wrong belief is corrected by inserting a new one with a `SUPERSEDES` edge; the original stays.
- **No graph viz.** Inspect with `sqlite3 "$(python -c 'from aelfrice.cli import db_path; print(db_path())')"`.
- **JSONL batch ingest has no PII scrubber.** `aelf ingest-transcript --batch ~/.claude/projects/` will pull whatever you typed in chat into the local belief graph. Review before backfilling.
- **Hook framing relies on the model honoring the trust boundary.** The `UserPromptSubmit` hook ships three structural defenses (framing tag, render-time tag-substring escape, per-turn audit log — see [hook_hardening.md](hook_hardening.md) and [PHILOSOPHY § Trust boundary at the hook surface](PHILOSOPHY.md#trust-boundary-at-the-hook-surface)). What it cannot do is force the model to verify session artifacts named in user-turn text adjacent to the block, or guarantee the model treats `<belief>`-wrapped content as data rather than instruction. A model that ignores the framing is a model-layer failure, not a hook-layer one. The `hook_audit.jsonl` log is the recovery surface — it records what was injected so a human can review after the fact.

## Out of scope

These are scope choices that follow from aelfrice's commitments. They are *not* roadmap items.

### Sharing, sync, or federation

aelfrice ships no mechanism for exporting, syncing, or distributing memory contents between users, machines, or projects. The brain graph stays on the machine it was written on.

This is a privacy and audit choice. A graph derived from real session activity contains filesystem paths, hostnames, internal URLs, names from git config, project architecture details, and content the agent inferred from chat. None of that is suitable for cross-machine distribution by default. Per-belief allowlists are not reliable enough to make automated export safe.

To bootstrap a new clone or collaborator: run `aelf onboard .`. The graph is re-extracted from publicly-visible repo content. To share rules: lock them in CLAUDE.md, CONTRIBUTING.md, or other repo-tracked prose, and the onboard scanner picks them up.

### Multi-session aggregation

aelfrice is not optimised for "how many times did the user mention X across last quarter?" queries. That is the LLM-with-RAG-and-summary-buffer task, not a behavioural-directive recall task. On benchmarks like LongMemEval multi-session, embedding systems will outperform aelfrice for that query category.

The principled response is to add aggregative-query routing at the structural-analysis layer (SQL aggregations over `feedback_history`, scoped graph walks, time-bucketed COUNT queries) — not to add embeddings.

### Multi-project query

One DB at a time. Beliefs from project A do not surface in project B (different `.git/` directories). Use `AELFRICE_DB` to scope per-project explicitly.

## Compatibility

- Python 3.12 or 3.13.
- macOS and Linux are routinely tested. Windows should work but is not exercised on every release.
- `uv` recommended; `pip` works.

## Reporting

File an issue tagged `limitations` for additions or corrections.
