Refactor from per-session sqlite files to one shared shard at
~/.arborist/sessions.db. Three things that didn't work before now do:
1. Queries are first-class members of the tree.
nodes_fts (FTS5 over question + answer_text + cited_titles) lets
/find <query> walk every prior turn across every session. Cached
answers and threads become findable, surface in the REPL as
`[<bates>] <audit> <question>` lines.
2. Forking from history works the same as forking from a sibling.
parent_bates can cross sids. After /find returns a hit from
last week's session, /cd <bates> + ask = your next question
lands as a child under that historical turn. The cross-session
parent's subtree_hash ripples up its session_root.
3. One global audit chain instead of per-file.
audit_events.event_hash = sha256(prev || canonical body), one
chain over every state change in the shard. `make
session-chain-check` is now a single pass; tampering anywhere
in the operator's history breaks the chain.
Wire:
- arborist/qa/session.py — drop file-per-session SessionStore class;
Session becomes a viewport on SessionStore. cited_titles_json +
n_cited_sources materialized at insert time so FTS5 doesn't need
a join into providence_cache.
- arborist/cli.py — `session` subcommand swaps --gc for --find;
REPL adds /find. Ancestor-keyword extraction now reads
nodes.cited_titles_json directly (no qa.db roundtrip).
- Makefile — `make session-find Q="..." [LIMIT=N JSON=1]`; `make
session-gc` retired (no per-session files to GC).
- docs/sessions.md — rewritten for the single-shard shape.
- tests/test_session.py — 17 tests: create, add, fork (incl.
cross-session), find (FTS5 + by_cache_key), path_to_root crossing
sessions, audit chain (intact + tampered), cited-title extraction,
subtree_hash ripple across sessions.
Migration: pre-existing per-session dbs at ~/.arborist/sessions/*.db
become orphaned. None lost data — test sessions only. Operator can
rm -rf ~/.arborist/sessions/ (or rename to sessions-old/) at leisure.
126 session+providence+verify+inspect tests pass.