`arborist session` is an interactive multi-turn Q&A REPL where every
turn (or fork) mints one node in a per-session SQLite-backed tree.
Each node carries a stable Bates id (`<sid>-<6-digit>`) and folds into
a Merkle subtree-hash chain; the root node's subtree_hash is the
session_root.
Tree shape lets:
- **Forks** happen implicitly: `/cd <bates>` to a prior node, ask
again → sibling under that parent. Branch points (≥2 children)
surfaced by `/branches`.
- **Page-refresh caching** stay cheap: a client tracking
(bates → subtree_hash, body) only refetches subtrees whose hash
changed. Sibling subtrees that didn't change are byte-identical
→ cache-equivalent. Same property git pack-protocol and IPFS MFS
use.
- **Audit-chain verification** be per-session and independent: each
session db has its own session_audit_events with event_hash =
sha256(prev_hash || canonical_body). `make session-chain-check`
walks all sessions; 0 breaks each = intact.
Wire:
- arborist/qa/session.py — Session class, Bates minting, Merkle
recompute on O(depth) insert, audit chain, helpers (list, render,
resolve <bates|seq|label>).
- arborist/cli.py — `session` subcommand: REPL + --list / --tree
/ --chain-check / --gc / --json flags. Ancestor-titles → retrieval
keywords (parsed from cited-pointer lines in answer_text) flow down
the branch via policy["retrieval_keywords"].
- arborist/qa/providence_query.py — honor policy["retrieval_keywords"]:
augment FTS5 retrieval query without touching cache_key (mirrors
legacy --retrieval-keywords discipline, #000001).
- Makefile — `make session [SID=...]`, `make session-list`,
`make session-tree SID=...`, `make session-chain-check`,
`make session-gc SESSION_KEEP=N`.
- docs/sessions.md — schema, Merkle conventions (portability for
non-Python consumers), REPL command reference.
- tests/test_session.py — 15 tests: create, resume, add_node, fork
via cd, branches, root determinism, audit chain (intact + tampered),
resolve, list, render, sibling-invariance of subtree_hash.
Storage: ~/.arborist/sessions/<sid>.db (self-contained — no FK into
main store). Answers live in providence_cache keyed by cache_key;
session only carries conversation shape. Cache hits stay live across
sessions. Bounded growth via --gc.
Phase 1 scope: tree + Merkle + Bates + retrieval-keyword flow.
NOT in Phase 1: LLM-side conversation_history (threading prior Q&A
into the LLM prompt + conversation_hash). A bare-pronoun follow-up
("who created him?") gets the right retrieval today but the LLM may
still UNGROUNDED because it sees only the new question as user
message. Folding conversation_history into the prompt + cache_key's
conversation_hash dimension is the natural Phase 2.
197 tests pass.