Skip to main content

Memory files

How to add, correct, find and verify the repository's frictions, todos and decisions. For what they are, see the concept overview.

Where the files live​

ScopePath
Root (cross-cutting, ci, docs, skills)<repo>/.github/memory/<kind>.md
Package<repo>/<projectFolder>/.github/memory/<kind>.md

<kind> is friction, todo or decision. A package file starts with the entries that belong to that package; anything cross-cutting belongs in the root file, and memory check warns when a root file holds a package entry.

The shape of an entry​

### F-014 — A nested `tap` run ignores `--reporter=json`

> _2026-09-15 · ci · resolved_

The nested tap inherited `TAP_CHILD_ID`, concluded it was a child job and reported zero tests. Unset
the `TAP_*` variables before spawning the inner run.
  • F-/T-/D- plus a number, allocated workspace-wide and never reused.
  • The title is one line, at most 80 characters, and reads on its own.
  • The line under it — a blockquote, so markdownlint does not read it as a heading — carries the date, the area and the status, and is what list --area and list --status filter on.
  • The body is wrapped at 100 columns by the CLI; paragraphs are separated by a blank line. A fenced code block is passed through untouched and needs a language (```bash).

The generated index sits between <!-- memory:index --> and <!-- /memory:index -->. It is derived: the CLI writes it, and check fails when it drifts from the entries.

Adding and correcting entries​

blong-dev memory add friction --area core/blong-gogo --title "…" --body "…"
blong-dev memory add todo --area cross-cutting --title "…" --body-file body.md
blong-dev memory edit F-014 --body-file body.md # correct one (never hand-edit)
blong-dev memory close F-014 --note "fixed in b1f3c9a" # resolved, entry kept
blong-dev memory close D-018 --by D-042 --reason "the union is no longer debounced"
blong-dev memory retire F-014 --reason "never a real defect" # removed, id left in the ledger
blong-dev memory reopen F-014 # it came back
blong-dev memory move F-014 --area core/semantic-log # it belongs elsewhere
blong-dev memory prune F-101,F-102 --reason "duplicated by F-090"

add refuses a title longer than 80 characters and refuses an area that is neither a package folder nor a reserved label. For a long body use --body-file; --date and --status override the defaults (today, and the first status of the kind).

close keeps the entry, whatever the kind: the reasoning outlives the status, and the id is never reused. A friction becomes resolved, a todo done, a decision superseded by the successor its --by/--reason name, and all three stay readable where they are. An entry that should not be in the files at all — a duplicate, a mis-filing, work that was cancelled — is retired instead: the entry goes and only its id stays, as a ## Retired line. retire takes --reason for the fate the line records, and --purge to drop the line too, which is the only thing that frees a number.

Reading them​

blong-dev memory list --kind friction --area core/blong-browser --status open
blong-dev memory list --search "connection pool" --json # agents use --json
blong-dev memory show F-014 --json
blong-dev memory audit # dangling references, duplicate titles, stale entries

Start with the index block at the top of a file: it lists every entry once, grouped by status. Only read further when an entry looks relevant — that is the whole point of the index.

list --search matches the text of an entry. It is always available, it is exact, and it needs nothing running — when you know a word the entry uses, it is the shortest path. When you know only the meaning, use the semantic search below.

Searching by meaning​

search asks a local Hindsight server which entries are close to a question, so a paraphrase finds the entry that uses different words.

blong-dev memory search "why do agents keep editing memory files by hand"
blong-dev memory search "coverage thresholds" --kind friction --area core/blong-browser --limit 5
blong-dev memory search "the union is no longer debounced" --json
blong-dev memory search "how do the notes work" --source docs
blong-dev memory search "handler naming" --source entry,docs # both streams in one query

--source selects the stream: entry (the default), docs or skill, comma-joined for several. A search defaults to the entries, so adding the documentation to the bank never changed what an existing query returns. --kind, --area and --status describe entries only, so asking for them beside a page source is refused rather than answered with nothing.

Each hit prints a rule, the entry's id, kind, status and similarity, the file it lives in, and the entry text as it stands in that file. The number is the cosine similarity of the embedding. The server also reports a ranking score, but it is not comparable between queries — a relevant match can rank first at 0.0006 — so it is carried in --json rather than shown.

Treat an empty result as "nothing close", not as "not written down", and fall back to list --search — the two read the same tree, one by text and one by meaning.

What the index holds, and when​

Every entry is one document, keyed by its id, so re-ingesting an edited entry replaces it rather than duplicating it. The dimensions a search can filter on — kind, area, status, id, file path — are written as tags, and mirrored into the document's metadata for the server's own UI.

The bank holds three streams, told apart by a tag rather than by a second bank, so one query can span them:

StreamWhat it isIdTags
entryone document per memory entrythe entry id (F-316)memory, kind:, area:, status:
docsone document per published page in docs/blong/docs/<tier>/doc-<tier>-<name>kind:documentation, tier:, path:
skillone document per .github/skills/*/SKILL.mdskill-<name>kind:agent-skill, scope:, path:

A page or a skill is stable state rather than an event, so its id comes from its path: an edit replaces the document instead of adding a second fragment, which is what makes re-ingesting a whole tier cheap and safe. --sources docs,skill ingests them; the default is entry, so nothing about an existing query changes.

An agent that wants Hindsight's own documentation as a skill can pull it in beside the local ones (Blong discovers skills from .github/skills/*/SKILL.md and has no installer of its own):

npx skills add https://github.com/vectorize-io/hindsight --skill hindsight-docs

The index is derived: the markdown is the source of truth, and the bank can be dropped and rebuilt at any time.

CommandWhat it does to the index
add, edit, close, reopen, moveQueues the entries the command touched (one call, not one per entry).
pruneDeletes the pruned entries' documents, so a search cannot find what the tree no longer holds.
index --semanticIngests every entry of the selected files — the first fill, and the repair.
index --semantic --dry-runReports how many entries would be ingested, without touching the server.
index --semantic --statsAlso compares the bank with the tree and names what drifted.
index --semantic --pruneDeletes the bank documents the tree no longer holds.

A write never fails because of the index: if the server is unreachable, the command prints one warning on stderr and the entry is written anyway. The backfill is the repair for whatever was skipped, and re-running it is safe.

blong-dev memory index --semantic # every file
blong-dev memory index --semantic --files .github/memory/friction.md
blong-dev memory index --semantic --dry-run # count only
blong-dev memory index --semantic --sources docs,skill # the docs site and the skills
blong-dev memory index --semantic --stats # what the bank holds, and what drifted
blong-dev memory index --semantic --prune # drop what the tree no longer holds

Pointing the CLI at a server​

SettingDefaultMeaning
HINDSIGHT_API_URLhttp://localhost:8888The server; .blong_devrc may hold it as hindsight.url.
HINDSIGHT_BANKblongWhich bank to read and write; .blong_devrc hindsight.bank.
HINDSIGHT_DISABLEDunset1, true, on or yes turns the feature off, write hook included.

The server is deployed by plans/memory-index/hindsight.sh, which carries the two settings that cannot be changed later without wiping the bank: the embedding model, and therefore the vector width, and the bank's retain_extraction_mode: chunks — entries are stored verbatim and ingestion calls no LLM at all.

The embedding model is the built-in BAAI/bge-small-en-v1.5 on purpose. Measured over the whole tree, it and two Ollama models three times its width rank every benchmark query identically while the cross-encoder reranker runs, and differ by noise without it — so a bigger model buys nothing observable against a LAN dependency. EMBED_MODEL on the script still selects an Ollama model for anyone re-measuring; the numbers are in plans/memory-index/embedding-model-comparison.md.

One bank means one embedding model for everything in it: the entries, the documentation pages and the skills are chunked and embedded by the same settings, so a query compares them on equal terms. The width is fixed by the first write, which is why the model is chosen once, at the script.

Writing a batch (migration, or an agent's work)​

A batch is JSON, authored outside the CLI and written by it:

{
"kind": "friction",
"entries": [
{
"title": "A stale line range spliced items into the index block",
"body": "Sections are line ranges; a refresh moves them. Re-read the structure after any refresh.",
"area": "tools/blong-dev",
"status": "resolved",
"date": "2026-09-15"
}
]
}
blong-dev memory import batch.json # dry run: what would be written, and what is refused
blong-dev memory import batch.json --apply # write it

Import validates the whole batch before writing anything: a bad title, an unknown area, a status the kind does not have, an entry that is already in the tree (same title and body) or one that repeats earlier in the batch are all reported and nothing is written. --force overrides the duplicate check, and is almost never what you want — importing a batch twice duplicates every entry it holds.

The user's own list​

The ## Manual section of the root todo is the user's: it is never indexed, never rewritten, and never touched by an agent.

blong-dev memory manual list
blong-dev memory manual add "the thing I keep meaning to do"
blong-dev memory manual done 3

Before you finish​

blong-dev memory check # format, ids, areas, index, statuses, markdown rules
blong-dev memory check --files a.md,b.md
blong-dev memory format # re-wrap and re-render everything the CLI owns
blong-dev memory index # regenerate the index blocks only

check is the gate: common/git-hooks/pre-commit runs blong-dev lint-staged, which detects the staged files under .github/memory/ and checks them, so a malformed entry fails your commit rather than a review comment. (The CI workflow delegates to a reusable Rush workflow and does not run this itself, so the hook is the gate a contributor actually meets.) It also reports the markdown a hand-written body would break — a section heading in the middle of a body, a line over 100 columns, <placeholder> HTML, emphasis on a line of its own, a list or fence without a blank line around it. blong-dev lint --files … covers the same ground for any markdown file plus cspell, and is the command to run when you have touched a .md file outside the memory tree.

Pointing at a note from the code​

A comment carries a summary, not the reasoning, and cites the entry that holds it with @@:

// gc keeps one directory per version, so a rebuilt tree is never rewritten under a reader (@@D-468 @@D-469).

Parentheses are optional. Never cite by line number (decision.md L1153 rots). Cite the active entry: when a decision is superseded, retarget the comment to the successor and close the old one.

blong-dev memory refs --dangling # the linter: every reference in the tree resolves
blong-dev memory refs D-476 # what cites this entry
blong-dev memory refs --summary # references per package, the conversion ledger
blong-dev memory refs --sites # the audit view: each citation beside the title it resolved to
blong-dev memory refs --changed # sites citing an entry the working tree changed (--since <ref>)
blong-dev memory refs --fix-anchors # rewrite an anchor whose heading only moved
blong-dev memory refs --fix-superseded # follow the successor a superseded decision names
blong-dev memory comply # sites citing an entry this change edited, retired or superseded
blong-dev memory check --refs # the memory files, plus the references into and out of them
blong-dev memory export --out .github/memory/references.json # the index the editor reads

refs --dangling exits non-zero on an error; a retired id is a warning. comply compares the memory files with a git ref (--since, default HEAD) and names the code to re-read.

A citation of a superseded decision is reported in two ways: an error when the entry names the successor its reasoning moved to (the paragraph close --by writes), because refs --fix-superseded follows it in one command, and a warning when it names none, because only a reader can settle that. A resolved friction or a done todo stays the citation, because it is the record of the workaround the code embodies — which is why the linter counts the two apart: N retired, M superseded.

Resolution is not correctness: the linter proves an id exists, not that it holds the reasoning. A sweep of realm/blong-kustomize found a citation that resolved cleanly and pointed at the wrong entry, so an audit reads the title --sites prints beside each citation — a word-overlap heuristic flagged 243 of those 388 citations, too many to be a check. --fix-anchors rewrites a token whose heading moved and whose replacement is unambiguous, and reports rather than guesses otherwise.

Retiring an id​

A retired entry, a pruned one or a renumbered one leaves a line in ## Retired, and its number is never reused, so a reference that outlived the entry stays diagnosable. retire removes one entry (--purge deletes it outright), prune refuses an id any file cites (--force retires it anyway), and renumber <id> --to <new-id> fixes a collision:

blong-dev memory retire T-123 --reason "superseded by the new flow" # the id stays in the ledger
blong-dev memory prune F-101 --reason "duplicated by F-090"
blong-dev memory renumber D-001 --to D-050 # the old id stays in the ledger, pointing at the new