From d4a691d1979d40ccccbfeaa2df10e47858aef141 Mon Sep 17 00:00:00 2001 From: doronp <3586743+doronp@users.noreply.github.com> Date: Tue, 29 Sep 2026 13:09:17 +0300 Subject: [PATCH 1/4] feat: add a recall skill to the Claude Code plugin The plugin moved a capture to the compaction boundary and gave the agent no way to use what was captured. skills/recall/SKILL.md is the in-session half: /gitmemory:recall, which Claude also loads on its own when the user refers to a decision, a rule or a conversation from before a compaction or an earlier session. It is the plugin form of the CLAUDE.md line in docs/USAGE.md, "Put it back in front of the agent", and needs no edit to the user's project. The description leads with when to use it, because a crowded skill listing truncates descriptions and a new skill is the first to lose its tail. The procedure is the CLI's, and every command, message and output shape in it was run against a throwaway store: recall and its -k, the no-index error and its exit 2, index as the rebuild, not a store and 0 generation(s) as the signs that nothing was captured, the 160-character text column, and the byte offset as the start of the turn's JSON line in a raw segment, which can run on into the next segment. The store the skill searches also captures the sessions that used it, so it holds the skill's own body, earlier recall commands and their output, and earlier answers. The skill names those echoes and skips them, and its example line carries a placeholder, not a sentence a search could find. It tells a subagent's transcript, which the watcher stores under its own name, from another session by the source_path in the manifest. It reads and rebuilds the index; it installs, configures and captures nothing, and it says that what it reads goes to the model provider with the rest of the conversation, past no redaction gate. plugin.json and marketplace.json stop describing the plugin as the hook alone. The version stays at 0.1.0, which the plugin test pins to pyproject.toml, so installed copies pick the skill up at the next release. Signed-off-by: doronp <3586743+doronp@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 4 +- skills/recall/SKILL.md | 115 ++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 skills/recall/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index f79a1b7..5b31bc2 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -3,7 +3,7 @@ "owner": { "name": "doronp" }, - "description": "The gitmemory plugin for Claude Code: its compaction-boundary hook shim.", + "description": "The gitmemory plugin for Claude Code: its compaction-boundary hook shim, and a skill that searches and quotes the sessions gitmemory has captured.", "plugins": [ { "name": "gitmemory", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 399cb8b..973fb0c 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,12 +1,12 @@ { "name": "gitmemory", "version": "0.1.0", - "description": "Registers gitmemory's hook shim on PreCompact and SessionEnd, so a session is captured at the compaction boundary instead of at the watcher's next sweep. The hook is only a doorbell: nothing is captured unless the gitmemory watcher is installed (uv tool install git+https://github.com/doronp/gitmemory) and running, and without the hook gitmemory is still correct.", + "description": "Registers gitmemory's hook shim on PreCompact and SessionEnd, so a session is captured at the compaction boundary instead of at the watcher's next sweep, and adds a recall skill that has the agent search the captured sessions with gitmemory recall and quote what was said, with the address of each line. The hook is only a doorbell, and the skill writes nothing but the search index: nothing is captured unless the gitmemory watcher is installed (uv tool install git+https://github.com/doronp/gitmemory) and running, and without the hook gitmemory is still correct.", "author": { "name": "doronp" }, "homepage": "https://github.com/doronp/gitmemory", "repository": "https://github.com/doronp/gitmemory", "license": "Apache-2.0", - "keywords": ["agent-memory", "claude-code", "hooks", "compaction", "transcripts", "git"] + "keywords": ["agent-memory", "claude-code", "hooks", "skills", "compaction", "transcripts", "recall", "git"] } diff --git a/skills/recall/SKILL.md b/skills/recall/SKILL.md new file mode 100644 index 0000000..d1ba6ee --- /dev/null +++ b/skills/recall/SKILL.md @@ -0,0 +1,115 @@ +--- +name: recall +description: Use when the user refers to a decision, a rule or a conversation from before a compaction or from an earlier session ("what did we decide about X", "I told you not to ...", "like we discussed", "as I said yesterday"), when you are about to ask them to repeat something they have already told you, or when they ask you to search past sessions. Runs gitmemory recall over the sessions gitmemory has captured and quotes what was actually said, with an address the user can check. +argument-hint: "[topic]" +--- + +# Recall from gitmemory + +gitmemory keeps the transcripts it watches in a local git repository, byte for +byte, and `gitmemory index` builds a search index from that record. This skill +searches it with the `gitmemory` CLI and quotes the words as they were written. +It reads the store and rebuilds the index when it has to; it captures and +configures nothing. It sends nothing anywhere itself, but what it reads enters +this conversation and goes to the model provider with the rest of it, past no +redaction gate: that gate stands at `gitmemory push`. + +The store is `$GITMEMORY_HOME`, or `~/.gitmemory` when that is unset; the CLI +reads it, so leave it as this session has it. If the user keeps the store +somewhere else, pass their path before the subcommand, as in +`gitmemory --home recall ""`, and the same for `index`. `` +below means that directory: the path the user gave, or what +`echo "${GITMEMORY_HOME:-$HOME/.gitmemory}"` prints. Do not type `~/.gitmemory` +in its place from memory. + +## Search + +The topic is the skill's argument if it was given one, otherwise the thing the +user is referring to, in the words they would have used. `recall` ranks by +BM25: it matches words, not meaning. + +```sh +gitmemory recall "" # 10 lines by default; -k N for more or fewer +``` + +One line per turn, best first: + +``` + -7.512 claude-code/0f3a9c2e-1b2d-4c5e-8f90-3a53cc4a5233-76edb6eef42266c0/g00@346 assistant/text +``` + +| Field | Meaning | +|---|---| +| `-7.512` | BM25 score. Lower is better; `-0.000` means the words that matched are common in the store | +| `claude-code//g00` | Agent, session and generation | +| `@346` | Byte offset of the turn (its JSONL line) in that generation's raw bytes | +| `assistant/text` | Role and block kind | +| the rest | The block's first 160 characters, whitespace collapsed | + +`no matches` on stderr, exit 0, is an answer. Try once more with other words +the user might have used, then tell them nothing was found. `query truncated to +64 terms; N dropped` on stderr means the query was too long: shorten it. + +## Echoes + +Sessions that used this skill were captured too, and their records of earlier +recalls match the same words. Skip these hits; they are not sources: + +- user/text starting `Base directory for this skill` (this file, as loaded); +- `/gitmemory:recall` and its arguments, however the transcript wraps them; +- a `gitmemory recall ...` tool call and its result, lines that start with a + score, `N generation(s)` or `no matches`; +- an earlier answer to the same question, and the lines it quoted. + +If they crowd out the rest, search again with `-k 30`. Quote the original turn. + +## When it fails + +- ``no index at ; run `gitmemory index` first``, exit 2: run + `gitmemory index`, then search again. `index` rebuilds the index from the + store in full. It is safe to rerun, and the index is never the source of + truth. +- The watcher captures; it does not index. If a search misses something the + user says was said recently, the index may be older than the capture: run + `gitmemory index` and search once more. +- The shell cannot find `gitmemory`, or `index` prints `not a store: `, + or it reports `0 generation(s)`: nothing has been captured there. Tell the + user how it gets set up, and stop. Do none of it yourself: + 1. `uv tool install git+https://github.com/doronp/gitmemory` + 2. a `[[watch]]` table in `config.toml` in the store (`$GITMEMORY_HOME`, by + default `~/.gitmemory`) naming the agent and where its transcripts live; + for Claude Code, `agent = "claude-code"` and + `roots = ["~/.claude/projects"]` + 3. `gitmemory watch`, left running. + + The [Quickstart](https://github.com/doronp/gitmemory#quickstart) has the + rest. + +## Quoting + +- Quote the text field verbatim, with its address copied whole, never + shortened. A paraphrase is not a quote: if you summarise, say that you are, + and keep the address beside it. +- The text stops at 160 characters. If the question needs the rest, the turn + is in `/raw///g/`. Segment file names are byte + ranges, start included, end excluded. In the one whose range holds the + offset, the turn's JSON line starts `offset - start` bytes in, and the block + is inside it. If the file ends before the line does, the line goes on at the + start of the next segment, whose range starts where this one ends. +- This session is `${CLAUDE_SESSION_ID}`. A hit's manifest, + `/sessions///g.json`, has its transcript's + `source_path`: `/.jsonl`, or for a subagent (a session + named `agent--...`) `//subagents/.../agent-.jsonl`. + Both belong to ``. `` is the working directory, with `/` + and other punctuation turned into `-`. Say so when a hit is from another + agent, another session or another project. +- The store holds whatever was typed or pasted into a watched session, tool + output included. Quote what the question needs and no more. + +## Read-only + +Run `gitmemory recall` and `gitmemory index`, and read files under ``. +`index` writes only the derived index under `/index/`. Do not run +`gitmemory capture`, `gitmemory watch`, `gitmemory derive`, `gitmemory push` +or `gitmemory dashboard`, and never `gitmemory dashboard --expose`. Do not +edit `config.toml` or anything else in the store. From 96659f0d3e8a46613c21557781f8762d6d66e04b Mon Sep 17 00:00:00 2001 From: doronp <3586743+doronp@users.noreply.github.com> Date: Tue, 29 Sep 2026 13:10:07 +0300 Subject: [PATCH 2/4] docs: say the plugin ships the recall skill, not only the hook hook/README.md said the plugin "installs the hook and nothing else", and the README, USAGE.md and the changelog described it the same way. Each now names /gitmemory:recall and what it does not do: it captures nothing, so the watcher is still the whole of capture, and it writes nothing but the index. USAGE.md's "Put it back in front of the agent" gains the skill as the Claude Code alternative to the CLAUDE.md line. hook/README.md adds that the skill reads GITMEMORY_HOME from Claude Code's environment, as the hook does, and how to keep the skill while dropping a settings.json hook, since both at once fire the shim twice. The changelog records the skill under Unreleased; the 0.1.0 section is dated and stays as it was. Two things a user of the skill needs and would not guess. SECURITY.md and hook/README.md say that what the skill reads goes to the model provider past no redaction gate, since the design answer there was that raw/ stays on the machine and the gate stands at push. And the version is still 0.1.0, so a copy installed before the skill keeps its cache: measured with a throwaway CLAUDE_CONFIG_DIR, `claude plugin update` answers "already at the latest version (0.1.0)" and leaves no skills/, and uninstall then install brings it. hook/README.md and the changelog say so. Signed-off-by: doronp <3586743+doronp@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 10 ++++++++++ README.md | 6 ++++-- SECURITY.md | 10 +++++++++- docs/USAGE.md | 9 +++++++++ hook/README.md | 29 +++++++++++++++++++++-------- 5 files changed, 53 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7371e67..183f521 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,16 @@ tell you. `module:function`. Also settable with `GITMEMORY_POLICY`. Nothing is deleted. See [USAGE.md](docs/USAGE.md#when-memories-disagree). - `recall` prints each hit's date, and `Hit` carries the turn's `ts`. +- **Recall skill.** The Claude Code plugin ships a skill, `/gitmemory:recall`, + that has the agent run `gitmemory recall` and quote what it returns, each + line with its address, when you refer to a decision or a rule from before a + compaction or from an earlier session. It runs `gitmemory index` when the + index is missing or stale, and writes nothing else. See + [docs/USAGE.md](docs/USAGE.md#put-it-back-in-front-of-the-agent). What it + reads goes to the model provider, past no redaction gate. The plugin's + version is still 0.1.0, so a copy installed from 0.1.0 does not get the skill + from `claude plugin update`; reinstall it, or wait for the next release + ([hook/README.md](hook/README.md#as-a-claude-code-plugin)). ### Changed diff --git a/README.md b/README.md index d21f3a9..775c316 100644 --- a/README.md +++ b/README.md @@ -84,8 +84,10 @@ That is the whole install. The optional [hook shim](hook/README.md) makes a capture happen exactly at the compaction boundary instead of at the next sweep; **without it the system is still correct.** Claude Code users can install it as a plugin, `/plugin marketplace add doronp/gitmemory` then `/plugin install -gitmemory@gitmemory`; the watcher still does the capturing. For watcher flags and -troubleshooting, see [docs/watching.md](docs/watching.md). +gitmemory@gitmemory`, which also adds `/gitmemory:recall`, a skill that has the +agent search the store and quote what it finds; the watcher still does the +capturing. For watcher flags and troubleshooting, see +[docs/watching.md](docs/watching.md). **What to do with it next** — reading `recall` output, handing it back to an agent after compaction, derived key ideas, the dashboard — is in diff --git a/SECURITY.md b/SECURITY.md index 9887b4c..8cd4d1d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -38,7 +38,8 @@ maintained back-branches. In scope: anything in this repository that runs on a user's machine. That means capture, the store and its contiguity proof, `verify`, the redaction gate -behind `push`, the index, `derive`, the dashboard, the watcher and the hook shim. +behind `push`, the index, `derive`, the dashboard, the watcher, the hook shim and the plugin's +recall skill. Worth reporting, for example: @@ -86,3 +87,10 @@ The design answer is that `raw/` stays on your machine and the gate stands at There is no override flag. A gate you can wave through on a deadline is a gate that gets waved through on a deadline. + +The gate stands at `push` and nowhere else. The Claude Code plugin's recall +skill reads the store into the agent's context, so each line it quotes goes to +the model provider with the rest of the conversation, unscanned. That includes +text another watched agent never sent to that provider, and a credential `push` +would refuse to ship. The skill quotes what the question needs and no more, +which is a rule it follows, not a gate. diff --git a/docs/USAGE.md b/docs/USAGE.md index d70a8e0..be113d2 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -164,6 +164,15 @@ agree about uploads?" from a question for you into a search for the agent. You can also paste the lines in yourself. Either way the agent sees the words as they were written, with an address you can check. +In Claude Code, the plugin does this without the `CLAUDE.md` line. Installed as +in the [hook's README](../hook/README.md#as-a-claude-code-plugin), it adds a +skill, `/gitmemory:recall`, that Claude loads on its own when you refer to a +decision or a rule from before a compaction or from an earlier session, or that +you run with a topic: `/gitmemory:recall retry loop`. It runs `recall`, and +`index` when there is no index yet or the one there is older than what you are +asking about. It quotes each line with its address and says when a hit comes +from another session. It writes nothing but the index. + ## Derive key ideas and decisions `derive` builds, per generation, a ranked list of key sentences and a timeline diff --git a/hook/README.md b/hook/README.md index a59db86..4d63361 100644 --- a/hook/README.md +++ b/hook/README.md @@ -89,13 +89,25 @@ hooks as the `settings.json` example below, `PreCompact` and `SessionEnd` and no `Stop`, and points them at the copy of this shim that Claude Code keeps for the plugin. There is no path to fill in and no file to `chmod`. -The plugin installs the hook and nothing else. **Nothing is captured until the -watcher is installed and running** (`uv tool install -git+https://github.com/doronp/gitmemory`, a watch root in `config.toml`, then -`gitmemory watch`, as in the [Quickstart](../README.md#quickstart)), and -gitmemory is correct without the plugin, as it is without the hook. The hook -runs with Claude Code's environment, so a `GITMEMORY_HOME` you set for the -watcher has to be set there too, and be absolute, as above. +The plugin installs the hook and one skill, `/gitmemory:recall`, which has the +agent run `gitmemory recall` and quote what it returns +([USAGE.md](../docs/USAGE.md#put-it-back-in-front-of-the-agent)). Neither +captures anything. **Nothing is captured until the watcher is installed and +running** (`uv tool install git+https://github.com/doronp/gitmemory`, a watch +root in `config.toml`, then `gitmemory watch`, as in the +[Quickstart](../README.md#quickstart)), and gitmemory is correct without the +plugin, as it is without the hook. The hook and the skill run with Claude +Code's environment, so a `GITMEMORY_HOME` you set for the watcher has to be set +there too, and be absolute, as above. What the skill reads from the store goes +to the model provider with the rest of the conversation, and no redaction gate +stands in that path ([SECURITY.md](../SECURITY.md#if-a-credential-lands-in-the-store)). + +An install made before the skill was added does not have it. Claude Code keeps +a plugin's cached copy until its version changes, and the skill was added +without a version bump, so `claude plugin update gitmemory@gitmemory` reports +that it is already at 0.1.0. The next release, which bumps the version, brings +the skill; until then, `claude plugin uninstall gitmemory@gitmemory` and then +`claude plugin install gitmemory@gitmemory` fetch the current copy. This keeps the rule at the top of [Install](#install): gitmemory never edits another program's configuration. You opt in with `/plugin`, and Claude Code @@ -109,7 +121,8 @@ deduplicate a plugin's hook against one in your settings, so with both installed the shim fires twice per event and writes two records for the same transcript. That is harmless, since a record only asks the watcher to look and the capture tiles either way, but it starts a second shim process for every -event. +event. To have the skill as well, keep the plugin and take the two entries out +of `settings.json`. ### By hand, in `settings.json` From 777cab2621f915d2a0f796954c7df89f53485de3 Mon Sep 17 00:00:00 2001 From: doronp <3586743+doronp@users.noreply.github.com> Date: Tue, 29 Sep 2026 13:13:52 +0300 Subject: [PATCH 3/4] tests: hold the recall skill to the CLI it tells the agent to run A renamed subcommand or flag in skills/recall/SKILL.md is not a stale sentence: it is an error the agent meets mid-answer. The new test in tests/test_docs.py asks argparse for the --help of every gitmemory command in the skill's code, the forbidden ones included, and looks for each flag it names in that help; it pins the query-truncation limit the skill quotes to index.MAX_TERMS, the skill's name to recall, the plugin's skills to that one directory with no manifest path adding another, and /gitmemory:recall to the three documents that tell you to type it. The messages the skill tells the agent to branch on are held to the CLI by running it on a path that is no store and on an empty one: the no-index error and its exit 2, not a store, 0 generation(s), and no matches on stderr with exit 0. Mutations of the skill were each run against it and each failed it: -k renamed, index renamed, --home renamed, --expose renamed, the term limit changed, the name changed, a forbidden command dropped, each of the four messages reworded, exit 2 changed, and a skills path added to plugin.json. The suite grows by one, so the counts the README and REPRODUCE.md state move with it: 1,027 on a fresh checkout, 1352 with the corpora cloned, and 1,027 + 328 is 1,352, not 1,355. All three were collected. Signed-off-by: doronp <3586743+doronp@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 --- docs/REPRODUCE.md | 6 ++-- tests/test_docs.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/docs/REPRODUCE.md b/docs/REPRODUCE.md index b76c419..98d17a4 100644 --- a/docs/REPRODUCE.md +++ b/docs/REPRODUCE.md @@ -484,11 +484,11 @@ uv run pytest -q --collect-only -p no:cacheprovider | tail -1 uv run pytest -q --collect-only -p no:cacheprovider | grep -c '\.jsonl\]$' ``` -**Expected:** `1026/1027 tests collected (1 deselected)`, then -`1351/1352 tests collected (1 deselected)`, then `328`. The deselected test is +**Expected:** `1027/1028 tests collected (1 deselected)`, then +`1352/1353 tests collected (1 deselected)`, then `328`. The deselected test is the LongMemEval corpus test, which is opt-in: `uv run pytest -q -m corpus` after `bench/fetch_longmemeval.sh`. The README's -paragraph on why 1,026 + 328 is 1,351 and not 1,354 is the arithmetic of these +paragraph on why 1,027 + 328 is 1,352 and not 1,355 is the arithmetic of these three numbers. All three were run while writing this guide. `tests/test_docs.py` holds the README to the first and third numbers, so a diff --git a/tests/test_docs.py b/tests/test_docs.py index a6ba304..c2e9c26 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -10,6 +10,8 @@ from __future__ import annotations +import glob +import json import os import re import subprocess @@ -437,3 +439,79 @@ def flat(text: str) -> str: verbatim = sorted(q for q in quoted if q in corpus) claimed = int(re.search(r"quotes \*\*(\d+)\*\* runs of\n40 characters", doc).group(1)) assert len(verbatim) == claimed, verbatim + + +def test_the_plugin_recall_skill_runs_only_commands_the_cli_has(tmp_path, capsys): + """`skills/recall/SKILL.md` is instructions an agent follows word for word. + + A subcommand or a flag that the CLI has since renamed is not a stale + sentence there: it is an error the agent meets mid-answer, with the user + waiting, and it reads as gitmemory being broken. So every `gitmemory ...` + in the skill's code is asked for its own `--help`, which argparse answers + with exit 0 only for a subcommand that exists, and every flag it names is + looked for in that help: `--home` before the subcommand in the top-level + help, the rest in the subcommand's. The commands it forbids are checked the + same way, because a prohibition on a command that no longer exists guards + nothing. The messages it tells the agent to branch on are the ones the CLI + prints, got by running it on a path that is no store and on an empty one. + `skills/` holds this one skill and neither manifest adds a skills path; its + name is the one the three documents that mention it tell you to type. + """ + from gitmemory import __main__ as cli + from gitmemory import index + + def usage(*argv: str) -> str: + with pytest.raises(SystemExit) as done: + cli.main([*argv, "--help"]) + assert done.value.code == 0, argv + return capsys.readouterr().out + + skills = sorted(glob.glob("skills/*/SKILL.md", root_dir=ROOT)) + assert skills == ["skills/recall/SKILL.md"], skills + skill = _read(skills[0]) + front = skill.split("---\n")[1] + assert re.search(r"^name: recall$", front, re.M), front + manifest = json.loads(_read(".claude-plugin/plugin.json")) + entry = json.loads(_read(".claude-plugin/marketplace.json"))["plugins"] + assert "skills" not in manifest and all("skills" not in e for e in entry) + plugin = manifest["name"] + for doc in ("README.md", "hook/README.md", "docs/USAGE.md"): + assert f"/{plugin}:recall" in _read(doc), doc + + fence = r"^```[^\n]*\n(.*?)^```" + code = re.findall(fence, skill, re.M | re.S) + prose = re.sub(fence, "", skill, flags=re.M | re.S) + code += [a or b for a, b in re.findall(r"``(.+?)``|`([^`\n]+)`", prose)] + named = set() + for m in re.finditer(r"(? tuple[int, str, str]: + code = cli.main(["--home", home, *argv]) + out, err = capsys.readouterr() + return code, out, err.replace(home, "") + + absent, empty = str(tmp_path / "absent"), str(tmp_path / "empty") + os.makedirs(os.path.join(empty, "sessions")) + code, _, err = run(absent, "recall", "x") + assert code == 2 and "exit 2" in flat, err + said = re.sub(r"\S*;", ";", err.strip()) + assert f"``{said}``" in flat, said + code, _, err = run(absent, "index") + assert code == 2 and f"`{err.strip()}`" in flat, err + code, out, _ = run(empty, "index") + assert code == 0 and out.startswith("0 generation(s)") and "`0 generation(s)`" in flat + code, _, err = run(empty, "recall", "x") + assert code == 0 and err == "no matches\n" and "`no matches` on stderr, exit 0" in flat From b03ca6f977ac24e9332dd81e81aa55644d52533c Mon Sep 17 00:00:00 2001 From: doronp <3586743+doronp@users.noreply.github.com> Date: Fri, 2 Oct 2026 08:58:42 +0300 Subject: [PATCH 4/4] docs: teach the recall skill the dated, user-first order Co-Authored-By: Claude Opus 5.5 --- README.md | 6 +++--- skills/recall/SKILL.md | 11 +++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 775c316..9b8141b 100644 --- a/README.md +++ b/README.md @@ -185,13 +185,13 @@ real text, and that is a measurement rather than a suspicion. | What | How it was measured | Result | |---|---|---| | Hook cost in the agent's critical path | Timed against spawning `true` the same way, three runs of 400 | p50 **7.4 – 7.5 ms**, p99 **10.2 – 11.5 ms** | -| The suite | On a fresh checkout, no downloads | **1,036 tests**, and **328 conformance cases** against three third-party corpora, one gated on the LongMemEval download and one on `pip install -e '.[serve]'` | +| The suite | On a fresh checkout, no downloads | **1,037 tests**, and **328 conformance cases** against three third-party corpora, one gated on the LongMemEval download and one on `pip install -e '.[serve]'` | | Whether the tests hold anything | Every fix mutated to remove its behaviour; the named test must fail | **606** negative controls | The two suite counts do not add up, and should not. Switching the corpora on -collects 1361, not 1364. Three conformance cases fill parametrisations that +collects 1362, not 1365. Three conformance cases fill parametrisations that collect as one empty placeholder each while the corpora are absent, so they -replace three of the 1036 rather than joining them. Every figure on this board +replace three of the 1037 rather than joining them. Every figure on this board is pinned by a test, which is how it stays true. **What is not measured**, and is shown as a row on the dashboard rather than diff --git a/skills/recall/SKILL.md b/skills/recall/SKILL.md index d1ba6ee..f35d1f7 100644 --- a/skills/recall/SKILL.md +++ b/skills/recall/SKILL.md @@ -32,15 +32,17 @@ BM25: it matches words, not meaning. gitmemory recall "" # 10 lines by default; -k N for more or fewer ``` -One line per turn, best first: +One line per turn: what the user said first, newest first, then everything +else newest first: ``` - -7.512 claude-code/0f3a9c2e-1b2d-4c5e-8f90-3a53cc4a5233-76edb6eef42266c0/g00@346 assistant/text + -7.512 2026-09-28T10:03Z claude-code/0f3a9c2e-1b2d-4c5e-8f90-3a53cc4a5233-76edb6eef42266c0/g00@346 assistant/text ``` | Field | Meaning | |---|---| | `-7.512` | BM25 score. Lower is better; `-0.000` means the words that matched are common in the store | +| `2026-09-28T10:03Z` | When the turn was written, in UTC; `undated` if the transcript did not say | | `claude-code//g00` | Agent, session and generation | | `@346` | Byte offset of the turn (its JSONL line) in that generation's raw bytes | | `assistant/text` | Role and block kind | @@ -50,6 +52,11 @@ One line per turn, best first: the user might have used, then tell them nothing was found. `query truncated to 64 terms; N dropped` on stderr means the query was too long: shorten it. +If two lines disagree, the later date is usually the current decision: say so, +and quote both. An agent's newer proposal does not overrule the user's older +rule; a newer user line does. If the user has set `GITMEMORY_POLICY` or asks +for another order, leave it; `--policy` is theirs to choose. + ## Echoes Sessions that used this skill were captured too, and their records of earlier