tankslab.com/recall/source
bin/digest.py
123 lines, exactly as they ship in the download.
#!/usr/bin/env python3
"""
digest.py - answer "what happened with X" instead of "which rows mention X".
THE GAP THIS FILLS
------------------
The predecessor shipped a "corpora / knowledge base" feature: build a corpus, prime it,
query it. In practice the useful shape of that is narrower and cheaper - you have a
subject (a host, a file, a subsystem, an incident) and you want its STORY: when it
started, what changed, what broke, where it stands. A search gives you 25 rows and
leaves the assembling to you.
This assembles it. No model is involved: it is grouping and ordering over rows we
already have, which means it cannot hallucinate a history that did not happen - the
failure mode that makes a "knowledge base" worse than no knowledge base.
recall-digest "<subject>" the story of a subject
recall-digest --file <path> everything that touched a file
recall-digest --day 2026-08-19 one day, grouped
"""
from __future__ import annotations
import argparse
import json
import sys
from collections import Counter, defaultdict
from datetime import datetime, timezone
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
import store # noqa: E402
from recall import _fts_escape, TYPE_ICON, _local # noqa: E402
def _rows_for_subject(con, subject: str, limit: int):
q = _fts_escape(subject)
return con.execute("""
SELECT o.id,o.type,o.title,o.subtitle,o.body,o.created_at,o.files_modified,
o.session_uid, bm25(observations_fts) AS rank
FROM observations_fts JOIN observations o ON o.id=observations_fts.rowid
WHERE observations_fts MATCH ? ORDER BY o.created_at_epoch LIMIT ?""",
(q, limit)).fetchall()
def _rows_for_file(con, path: str, limit: int):
like = f"%{path}%"
return con.execute("""
SELECT id,type,title,subtitle,body,created_at,files_modified,session_uid
FROM observations
WHERE files_modified LIKE ? OR files_read LIKE ?
ORDER BY created_at_epoch LIMIT ?""", (like, like, limit)).fetchall()
def _rows_for_day(con, day: str, limit: int):
return con.execute("""
SELECT id,type,title,subtitle,body,created_at,files_modified,session_uid
FROM observations WHERE created_at LIKE ?
ORDER BY created_at_epoch LIMIT ?""", (day + "%", limit)).fetchall()
def main() -> int:
ap = argparse.ArgumentParser(prog="recall-digest")
ap.add_argument("subject", nargs="?")
ap.add_argument("--file")
ap.add_argument("--day")
ap.add_argument("--limit", type=int, default=400)
a = ap.parse_args()
con = store.connect(readonly=True)
if a.file:
rows, label = _rows_for_file(con, a.file, a.limit), f"file {a.file}"
elif a.day:
rows, label = _rows_for_day(con, a.day, a.limit), f"day {a.day}"
elif a.subject:
rows, label = _rows_for_subject(con, a.subject, a.limit), f"subject {a.subject!r}"
else:
ap.error("give a subject, --file, or --day")
if not rows:
print(f"nothing on record for {label}")
return 0
first, last = _local(rows[0]["created_at"]), _local(rows[-1]["created_at"])
kinds = Counter(r["type"] for r in rows)
files = Counter()
for r in rows:
try:
for f in json.loads(r["files_modified"] or "[]"):
files[f] += 1
except Exception:
pass
sessions = len({r["session_uid"] for r in rows if r["session_uid"]})
print(f"=== {label}")
print(f" {len(rows)} entries across {sessions} session(s)")
print(f" {first.strftime('%Y-%m-%d %H:%M')} -> {last.strftime('%Y-%m-%d %H:%M')}")
print(f" " + " ".join(f"{TYPE_ICON.get(k,'-')}{k}={v}" for k, v in kinds.most_common()))
if files:
print(f"\n files most touched:")
for f, n in files.most_common(8):
print(f" {n:>3}x {f}")
# The story, thinned. Everything is too much; the turning points are the value.
notable = [r for r in rows if r["type"] in
("bugfix", "security_alert", "security_note", "decision", "feature")]
if len(notable) < 6:
notable = rows
print(f"\n timeline ({len(notable)} shown):")
day = None
for r in notable[:60]:
lt = _local(r["created_at"])
d = lt.strftime("%Y-%m-%d")
if d != day:
day = d
print(f"\n -- {d}")
ic = TYPE_ICON.get(r["type"], "-")
print(f" {r['id']:>7} {lt.strftime('%H:%M')} {ic} {(r['title'] or '')[:88]}")
print(f"\n expand any of these: recall show <id>")
return 0
if __name__ == "__main__":
sys.exit(main())