Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ build/
*.db-shm
!tests/e2e/fixtures/*.db
telemetry/
.agents/
29 changes: 20 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
> Your AI stops forgetting.
> Set up once. Stays out of the way.
>
> _Local SQLite. Fully auditable. No GPU, no network._
> _No cloud. No account. No telemetry._

[![PyPI](https://img.shields.io/pypi/v/aelfrice.svg)](https://pypi.org/project/aelfrice/)
[![Python](https://img.shields.io/pypi/pyversions/aelfrice.svg)](https://pypi.org/project/aelfrice/)
Expand All @@ -18,23 +18,34 @@

You correct your agent. *"Got it,"* it says. Next session, same mistake.

aelfrice is a memory substrate that runs in the background of your coding agent. Write a rule once and every relevant prompt thereafter ships with that rule already attached — *before* the model sees your message. No `CLAUDE.md` chain to maintain, no cross-references for the agent to skip; the matched beliefs are in the prompt, not in a file the agent is supposed to consult.
aelfrice runs in the background and stops the amnesia. Write a rule once and every relevant prompt thereafter ships with that rule already attached — *before* the model sees your message. No `CLAUDE.md` chain to maintain, no cross-references for the agent to skip; the matched beliefs are in the prompt, not in a file the agent is supposed to consult.

## Install

```bash
uv tool install aelfrice # requires uv — https://docs.astral.sh/uv/
aelf setup # wire the UserPromptSubmit hook into your agent
aelf onboard . # one-shot project scan: filesystem, git log, AST
aelf onboard . # one-shot project scan: filesystem, git log, code structure
aelf lock "never push directly to main; use scripts/publish.sh"
```

That's it. The next prompt that mentions "push" already has the rule. From here on out aelfrice is invisible — no command to remember to run, no file to keep updated.

---

## What it does for you

- **Stops the AI forgetting your rules.** Lock a rule once with `aelf lock "..."` — it comes back attached to every prompt after that, in every new session. You don't have to remind the AI; aelfrice does.
- **The AI can't skip it.** What aelfrice remembers is in the prompt *before* the model sees it — not stored in a file the AI is supposed to check on its own. The reminding happens for the AI, not by you.
- **Stays on your computer.** One file on your machine. No cloud account. No telemetry. If you stop trusting aelfrice, `aelf uninstall` removes it cleanly in one command.
- **Set up once. Forget it's there.** Three commands at install time. After that, you reach for `aelf` rarely. The background hooks do the rest.

---

## How it works

In plain English: four searches run in parallel over your stored rules, the best matches get prepended to your prompt, and the model reads the lot as one message. Below is the wiring for readers who want the receipts.

When you submit a prompt, aelfrice's `UserPromptSubmit` hook fires before the model sees your message. It runs four retrieval lanes in parallel, merges the result, and prepends the matched beliefs as an `<aelfrice-memory>` block:

```text
Expand All @@ -58,7 +69,7 @@ The result is prepended to your prompt verbatim:
push the release
```

**Lock count is the operator's baseline-context budget knob.** If you lock 200 things, every session opens with all 200, by design. Everything non-locked is BM25-ranked and budget-trimmed. The first prompt of a new session carries one extra block — a `<session-start>` sub-block listing all locks plus load-bearing unlocked beliefs (corroboration ≥ 2, or posterior mean ≥ ⅔ with α+β ≥ 4); subsequent prompts in the same session skip it. Detection is session-scoped via a sentinel at `<git-common-dir>/aelfrice/session_first_prompt.json`.
**Lock count is the operator's baseline-context budget knob.** If you lock 200 things, every session opens with all 200, by design. Everything non-locked is BM25-ranked and budget-trimmed. The first prompt of a new session carries one extra block — a `<session-start>` sub-block listing all locks plus load-bearing unlocked beliefs (corroboration ≥ 2, or posterior mean ≥ ⅔ with α+β ≥ 4); subsequent prompts in the same session skip it. One extra block per new session, not per prompt.

---

Expand Down Expand Up @@ -97,7 +108,7 @@ Every belief carries a `(α, β)` Beta-Bernoulli posterior: `α / (α+β)` is th

| You run | It stores |
|---|---|
| `aelf lock "never commit .env files"` | Permanent rule. Initial prior `(9.0, 0.5)`. Returned on every retrieval. |
| `aelf lock "never commit .env files"` | Permanent rule. Returned on every retrieval. |
| `aelf onboard .` | Walks the project — git log, prose headings, code structure — and ingests structural facts as `agent_inferred` beliefs. |
| `aelf feedback <id> used` | `α += 1`. Strengthens the belief's posterior. |
| `aelf feedback <id> harmful` | `β += 1`. Weakens it. Five independent harmfuls through `CONTRADICTS` edges to a lock auto-demote it. |
Expand Down Expand Up @@ -135,10 +146,10 @@ By that bar, "a vector store with a similarity query" is not a memory system —

| Lin's pillar | What it means | aelfrice mechanism (v3.0) |
|---|---|---|
| **Provenance / audit trail** | Every row traces back to the action that wrote it: who, when, via what ingress channel. | `origin` column on every belief — eight tier values including `speculative` for `/aelf:wonder` phantoms ([`src/aelfrice/models.py`](src/aelfrice/models.py)). v3.0 adds a `scope` column (`project` / `global` / `shared:<name>`, [#688](https://github.com/robotrocketscience/aelfrice/issues/688)) tagging federation visibility. Append-only [`ingest_log`](src/aelfrice/store.py) records every raw input with source kind, source path, and session id — tear the DB down and rebuild it from this log alone. `content_hash` binds each row to its content. `belief_versions` / `edge_versions` sidecar tables carry per-scope version vectors. Open the file in any SQLite browser; nothing is hidden. |
| **Write gates / confirmation** | Persistence is not unconditional. Some writes need explicit approval; external-origin claims cannot be laundered into ground truth. | Two-tier lock state: `lock_level ∈ {none, user}` with a `locked_at` timestamp and a `demotion_pressure` counter that blocks silent removal. `aelf lock` is the only path to L0 ground truth. `aelf confirm` bumps a Beta-Bernoulli posterior but cannot flip `origin`. Phantom promotion has two explicit surfaces (v3.0, [#550](https://github.com/robotrocketscience/aelfrice/issues/550)): `aelf promote <id>` for the explicit path, and `aelf lock <text>` with Jaccard ≥ 0.9 substring match for the implicit auto-promote — both write `promotion:user_validated` / `promotion:phantom_lock_match` audit rows. The `(α, β)` posterior means feedback *accumulates* rather than overwrites; one harmful click does not erase a belief, it nudges the mean. |
| **Conflict handling** | Competing claims about the same thing are surfaced, not silently overwritten. | First-class edge types `CONTRADICTS`, `SUPERSEDES`, `RESOLVES` in [`src/aelfrice/models.py`](src/aelfrice/models.py) — disagreement is a graph relation, not a vanished row. v3.0's `/aelf:reason` emits a typed `VERDICT` (`SUFFICIENT` / `INCOMPLETE` / `CONTRADICTED` / `IMPASSE`) plus typed `IMPASSES` (`TIE` / `GAP` / `CONSTRAINT_FAILURE` / `NO_CHANGE`) so a downstream agent can act on the disagreement instead of guessing ([#645](https://github.com/robotrocketscience/aelfrice/issues/645) verdict/impasse classifiers, [#658](https://github.com/robotrocketscience/aelfrice/issues/658) `ConsequencePath` fork-on-`CONTRADICTS`). Per-scope version vectors preserve causal ordering across worktrees and federation peers. |
| **Reversibility (inspect / edit / delete)** | Mutations remain auditable and partially undoable. The user is the boss of their memories. | `aelf delete <id>` writes an audit row *before* the cascade. `aelf unlock` writes a `lock:unlock` audit row; `aelf promote --to-scope` writes a `scope:<old>-><new>` row; `aelf feedback` lands rows in `feedback_history`. Read-only federation (v3.0, [#650](https://github.com/robotrocketscience/aelfrice/issues/650) / [#655](https://github.com/robotrocketscience/aelfrice/issues/655)) lets a project surface peer beliefs via `knowledge_deps.json` without taking ownership — every mutation against a foreign belief id raises `ForeignBeliefError` at the API surface, so the boundary holds end-to-end. At the top level: `aelf uninstall --archive backup.aenc` encrypts and removes the live DB, `--purge` wipes, `--keep-db` leaves data untouched. No vendor lock-in by construction. |
| **Provenance / audit trail** | Every row traces back to the action that wrote it: who, when, via what ingress channel. | `origin` column on every belief — eight tier values including `speculative` for `/aelf:wonder` phantoms ([`src/aelfrice/models.py`](src/aelfrice/models.py)). v3.0 adds a `scope` column (`project` / `global` / `shared:<name>`, [#688](https://github.com/robotrocketscience/aelfrice/issues/688)) tagging federation visibility. Append-only [`ingest_log`](src/aelfrice/store.py) records every raw input — tear the DB down and rebuild from this log alone. Open the file in any SQLite browser; nothing hidden. |
| **Write gates / confirmation** | Persistence is not unconditional. Some writes need explicit approval; external-origin claims cannot be laundered into ground truth. | `aelf lock` is the only path to user-asserted ground truth. `aelf confirm` bumps the `(α, β)` posterior but cannot flip `origin`. Phantom promotion has two explicit surfaces (v3.0, [#550](https://github.com/robotrocketscience/aelfrice/issues/550)): `aelf promote <id>` for the explicit path, and `aelf lock <text>` with Jaccard ≥ 0.9 substring match for the implicit auto-promote — both write audit rows. Feedback accumulates rather than overwrites: one harmful click nudges the mean, it doesn't erase a belief. |
| **Conflict handling** | Competing claims about the same thing are surfaced, not silently overwritten. | First-class edge types `CONTRADICTS`, `SUPERSEDES`, `RESOLVES` — disagreement is a graph relation, not a vanished row. v3.0's `/aelf:reason` emits a typed `VERDICT` (`SUFFICIENT` / `INCOMPLETE` / `CONTRADICTED` / `IMPASSE`) plus typed `IMPASSES` (`TIE` / `GAP` / `CONSTRAINT_FAILURE` / `NO_CHANGE`) so a downstream agent can act on the disagreement ([#645](https://github.com/robotrocketscience/aelfrice/issues/645) classifiers, [#658](https://github.com/robotrocketscience/aelfrice/issues/658) `ConsequencePath` fork-on-`CONTRADICTS`). Per-scope version vectors preserve causal ordering across worktrees and federation peers. |
| **Reversibility (inspect / edit / delete)** | Mutations remain auditable and partially undoable. The user is the boss of their memories. | `aelf delete`, `aelf unlock`, `aelf promote --to-scope`, and `aelf feedback` all write audit rows; the `ingest_log` is append-only and replay-capable. Read-only federation (v3.0, [#650](https://github.com/robotrocketscience/aelfrice/issues/650) / [#655](https://github.com/robotrocketscience/aelfrice/issues/655)) lets a project surface peer beliefs via `knowledge_deps.json` without taking ownership — foreign-id mutations raise `ForeignBeliefError` at the API surface. Top level: `aelf uninstall --archive backup.aenc` encrypts and removes; `--purge` wipes; `--keep-db` leaves data untouched. No vendor lock-in. |

---

Expand Down
Loading