Skip to content

Commit 874beaf

Browse files
authored
feat: tell it when it's wrong, and see what it knows (#40)
Two things a person needs from a memory that learns on its own: a way to see what it holds, and a way to correct it. Neither existed — the first time memory kept a stale preference or a dropped project, the only recourse was editing SurrealDB by hand. **what-do-you-know** renders what memory actually holds, grouped by type, with confidence — and surfaces the self-reinforcement tally where it is non-zero, which is the "this may be me talking to myself" signal from the provenance work. --about <topic> answers the same question on one subject. **graph correct** records corrections as evidence rather than deletion. A person saying "that's wrong" is the highest-authority signal the system can receive, so it enters as contradiction at User provenance and the posterior moves accordingly; before/after confidence and evidence weight are both shown. --forget still deletes, but prints what will go and requires confirmation, gated daemon-side so no client can skip it. Subtle correctness point, caught during implementation: contradiction must NOT touch last_reinforced. Reusing the reinforce path would have reset the decay anchor, so telling the system a memory was wrong would have made it MORE visible — an edge stored at 0.6 and decayed to 0.3 would return at 0.545. crud::contradict_relationship writes counts and mean while leaving the anchor alone, so corrections are monotone. The extraction path has the same latent wart; noted for a follow-up. Ambiguity is refused rather than guessed: an entity with several live edges lists them with confidence and exits without changing anything, and an unknown name suggests the closest stored ones. Damaging the wrong memory is worse than doing nothing. MCP gains recall_overview (read-only) — an agent asking "what do I know" is the same question. No correction tool, deliberately: a model calling it would be recording its own judgement at the user's authority, which is the loudest version of the failure provenance weighting exists to prevent. Also: config show now prints [capture], [serve] and [extraction], which it silently omitted. 728 tests, clippy -D warnings clean.
1 parent 9d5cf23 commit 874beaf

21 files changed

Lines changed: 3716 additions & 18 deletions

‎README.md‎

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,21 @@ On a LongMemEval subset it went from answering **0 of 9** questions correctly to
4141
When you *do* want to poke at it:
4242

4343
```bash
44+
recall-echo what-do-you-know # everything it thinks it knows, and how sure it is
45+
recall-echo what-do-you-know --about D # …on one subject
4446
recall-echo status # is it healthy, what has it got
4547
recall-echo search "deployment" # grep your conversation history
4648
recall-echo graph query "auth flow" # semantic search + related entities
4749
recall-echo graph traverse "recall-echo" # what's connected to what, with confidence
4850
```
4951

52+
And when it has something wrong — which it will — you tell it so:
53+
54+
```bash
55+
recall-echo graph correct "D" "USES" "Vim" --wrong # that claim is mistaken
56+
recall-echo graph correct "Vim" --forget # that memory should not exist
57+
```
58+
5059
And from inside your agent, once the MCP server is registered, it asks for itself:
5160

5261
> **You:** why did we drop the websocket approach?
@@ -128,6 +137,11 @@ recall-echo graph search <query> # Semantic search across entitie
128137
recall-echo graph query <query> # Hybrid: semantic + graph expansion + episodes
129138
recall-echo graph traverse <entity> # Graph traversal from entity (shows confidence)
130139

140+
# Reading and correcting it
141+
recall-echo what-do-you-know # What memory holds, and how sure it is
142+
recall-echo graph correct <from> <REL> <to> --wrong # That claim is mistaken
143+
recall-echo graph correct <entity> --forget # Remove it outright (asks first)
144+
131145
# Data management
132146
recall-echo graph add-entity --name <n> --type <t> --abstract <a> # Add entity manually
133147
recall-echo graph relate <from> --rel <type> --target <to> # Create relationship
@@ -302,6 +316,20 @@ Knowledge graph operations. See the Architecture section above for the full comm
302316
- `graph ingest-all` — Scan conversations/ and ingest all un-ingested archives.
303317
- `graph extract` — LLM-powered entity extraction. Supports `--log <N>` (single archive), `--all` (all un-extracted), `--dry-run`, `--model`, `--provider` (any name from [LLM providers](#llm-providers)), `--delay-ms`. The daemon runs this pass on its own once the machine is quiet (see [Background extraction](#background-extraction)); this command is how you run it *now*, or in `server` mode, or after changing the model.
304318

319+
**Correction:**
320+
321+
- `graph correct <entity> --wrong` — Record that what memory claims about an entity is mistaken. Claims live on relationships, so this contradicts the entity's relationship — and if it has several, it lists them and asks which rather than guessing. `--all-edges` contradicts every one of them deliberately.
322+
- `graph correct <from> <REL> <to> --wrong` — Contradict one specific claim. Direction does not have to match how the graph stored it.
323+
- `graph correct <name> --forget` — Remove an entity and its relationships, or with a `<from> <REL> <to>` triple, one relationship. Prints what would go and asks before doing it; `--yes` skips the prompt, and a non-interactive stdin is never taken as consent.
324+
325+
A correction is *evidence*, not an override. `--wrong` records one contradicting
326+
observation at your authority ([`weight_user`](#configuration), 0.8 by default) and
327+
lets the Beta posterior move: a claim resting on twenty independent
328+
observations survives one correction with reduced confidence, a claim resting
329+
on one collapses. Say it twice and it counts twice. `--forget` is the escape
330+
hatch for memory that should not exist at all — it leaves no trace and cannot
331+
be re-weighed, so prefer `--wrong`.
332+
305333
**Daemon:**
306334

307335
- `graph daemon status` — Socket path, pid, version and uptime of the daemon serving this graph.
@@ -315,6 +343,27 @@ Knowledge graph operations. See the Architecture section above for the full comm
315343
- `graph pipeline stale` — List stale pipeline entities. Supports `--days` (threshold, default 7).
316344
- `graph vigil-sync` — Sync vigil-pulse metacognitive signals and caliber outcomes into the graph as Measurement and Outcome entities. Supports `--signals-path` and `--outcomes-path`.
317345

346+
### `recall-echo what-do-you-know`
347+
348+
What memory actually holds, written for a person rather than a program.
349+
350+
```bash
351+
recall-echo what-do-you-know # everything, grouped by entity type
352+
recall-echo what-do-you-know --about "rust" # one subject, with its relationships
353+
recall-echo what-do-you-know --limit 5 # entities listed per type (default 3)
354+
```
355+
356+
Without `--about` it lists the strongest entities of each type, then how firmly
357+
the relationships between them are held, then the two things a confidence
358+
number cannot tell you on its own: which relationships it is least sure of, and
359+
which ones it believes largely because it kept repeating them (`self×N` — the
360+
coherence tally, deliberately kept out of confidence). With `--about` it runs
361+
the ordinary hybrid query and renders each hit with the claims it takes part in
362+
and how sure it is of each.
363+
364+
Everything it prints ends in the same place: if a line is wrong, `graph correct`
365+
is how you say so.
366+
318367
### `recall-echo serve`
319368

320369
Runs the graph daemon for a memory directory. You never need to run this by
@@ -367,14 +416,15 @@ Or, equivalently, in a project's `.mcp.json`:
367416
`--entity-root` defaults to the current directory, so it can be omitted when
368417
the client is launched from the entity root.
369418

370-
**Tools.** All five are read-only; none can write to the graph.
419+
**Tools.** All six are read-only; none can write to the graph.
371420

372421
| Tool | Answers |
373422
| --- | --- |
374423
| `recall_query` | The default lookup: semantic search + one hop of graph expansion + the conversation fragments behind it |
375424
| `recall_search` | Semantic entity search alone — names, types, abstracts, retrieval scores |
376425
| `recall_episodes` | The raw conversation fragments, for what was actually said |
377426
| `recall_traverse` | Relationships out of one named entity, as a tree with edge confidence |
427+
| `recall_overview` | What memory holds without being asked for anything in particular — the content, and where it is unsure |
378428
| `recall_status` | Entity, relationship and episode counts — tells an empty memory from a failed lookup |
379429

380430
Every tool runs through the same graph daemon as the CLI, so it inherits the
@@ -387,6 +437,11 @@ entities and edges directly would route around exactly that mechanism. Memory
387437
is written on the ingest path, where every episode is stamped with its
388438
authorship.
389439

440+
**So is correction.** `graph correct` enters a contradiction at *user*
441+
authority, which is only true while a human is the one typing it — a model
442+
calling a correction tool would be recording its own judgement as yours.
443+
Corrections stay on the CLI.
444+
390445
## Archive Format
391446

392447
Conversation archives use YAML frontmatter with markdown content:

‎src/config_cli.rs‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ pub fn show(memory_dir: &Path) -> Result<(), RecallError> {
6262
);
6363
}
6464

65+
show_capture_section(&cfg.capture);
66+
show_extraction_section(&cfg.extraction);
67+
show_serve_section(&cfg.serve);
68+
6569
// Pipeline
6670
if let Some(ref pipeline) = cfg.pipeline {
6771
eprintln!("\n{BOLD}[pipeline]{RESET}");
@@ -78,6 +82,61 @@ pub fn show(memory_dir: &Path) -> Result<(), RecallError> {
7882
Ok(())
7983
}
8084

85+
/// Which agent CLIs the daemon sweeps transcripts from.
86+
///
87+
/// Shown even at its defaults: "sessions are being imported from every CLI on
88+
/// this machine" is a thing a user debugging their setup needs to know, and it
89+
/// is invisible in a config file that never mentions it.
90+
fn show_capture_section(capture: &config::CaptureSection) {
91+
eprintln!("\n{BOLD}[capture]{RESET}");
92+
eprintln!(" enabled = {}", capture.enabled);
93+
let sources = match &capture.sources {
94+
Some(sources) if !sources.is_empty() => sources
95+
.iter()
96+
.map(ToString::to_string)
97+
.collect::<Vec<_>>()
98+
.join(", "),
99+
_ => "(every CLI with sessions on this machine)".to_string(),
100+
};
101+
eprintln!(" sources = {sources}");
102+
eprintln!(
103+
" settle_secs = {} {DIM}(a transcript must be this quiet to count as finished){RESET}",
104+
capture.settle_secs
105+
);
106+
}
107+
108+
/// Background entity extraction inside the graph daemon.
109+
fn show_extraction_section(extraction: &config::ExtractionSection) {
110+
eprintln!("\n{BOLD}[extraction]{RESET}");
111+
eprintln!(" background_enabled = {}", extraction.background_enabled);
112+
eprintln!(
113+
" idle_after_secs = {} {DIM}(quiet period before a batch starts){RESET}",
114+
extraction.idle_after_secs
115+
);
116+
eprintln!(
117+
" batch_size = {} {DIM}(archives per batch){RESET}",
118+
extraction.batch_size
119+
);
120+
}
121+
122+
/// Where the graph daemon listens and how long it lives.
123+
fn show_serve_section(serve: &config::ServeSection) {
124+
eprintln!("\n{BOLD}[serve]{RESET}");
125+
let socket = serve
126+
.socket_path
127+
.as_deref()
128+
.filter(|path| !path.trim().is_empty());
129+
match socket {
130+
Some(path) => eprintln!(" socket_path = {path}"),
131+
None => eprintln!(" socket_path = {DIM}(derived from the memory directory){RESET}"),
132+
}
133+
let idle = match serve.idle_timeout_secs {
134+
0 => "0 (never idle-shuts-down)".to_string(),
135+
secs => format!("{secs}"),
136+
};
137+
eprintln!(" idle_timeout_secs = {idle}");
138+
}
139+
81140
/// Show the resolved agent-CLI call, so a misconfigured vendor is visible
82141
/// before it is spawned rather than after it fails.
83142
fn show_cli_section(llm: &config::LlmSection) {

0 commit comments

Comments
 (0)