CLI reference¶
hansard replay <session_dir> # ordered transcript, attribution inline
hansard verify <session_dir> # integrity check: seq gaps, missing footers, unresolved refs
hansard inspect <session_dir> # one-screen summary + attribution method breakdown
hansard prune <dir> --older-than 30d [--yes]
replay, verify, and inspect are read-only — none of them ever writes
to, modifies, or deletes a session directory. prune is the one
destructive command in the tool.
replay, verify, and inspect all accept --json (machine-readable
output), --no-color (disable ANSI colour), and --ascii (force ASCII
box/glyph characters instead of Unicode). prune accepts --json and
--no-color too, but not --ascii: its output is plain text with no
Unicode box-drawing or glyph characters to begin with, so there is nothing
for the flag to do — adding it would be surface area with no behaviour
behind it. All output below is real, captured against the session produced
by the Quickstart example.
hansard replay¶
$ hansard replay ./sessions/s_01KZVVGRNR3D5V708Z4NPGG11H
session s_01KZVVGRNR3D5V708Z4NPGG11H | corrected view | 1 person | 1 writer | less than a second
20:44:44.090 priya restart the payments worker
20:44:44.090 - turn begins
agent saw: priya's message
20:44:44.090 * restart_worker(service="payments")
`- caused by priya | recorded by the agent | 1.0
20:44:44.090 * output: Restarted the payments worker.
`- caused by priya | recorded by the agent | 1.0
20:44:44.090 - turn ends (ok)
--user USER_ID filters to only events involving one person. --raw
skips apply_corrections and shows the log exactly as originally written
— see The log format for why that distinction matters.
--html renders a self-contained HTML file instead — one page, no external
requests, with collapsible turns and a user filter — for sharing a
transcript with someone who wasn't there.
hansard verify¶
$ hansard verify ./sessions/s_01KZVVGRNR3D5V708Z4NPGG11H
hansard verify: sessions\s_01KZVVGRNR3D5V708Z4NPGG11H: clean (1 segment checked, no seq gaps or duplicates, 1/1 writer.end footers present, 3/3 references resolved)
A clean run states what was checked, not just that nothing went wrong —
printing nothing would be indistinguishable from a no-op. verify also
catches what read_session alone would abort on: it checks every segment
independently, so one corrupt segment degrades the report instead of
blanking it, and it re-derives seq gap/duplicate detection itself, since
nothing beneath it does that check.
hansard inspect¶
$ hansard inspect ./sessions/s_01KZVVGRNR3D5V708Z4NPGG11H
session s_01KZVVGRNR3D5V708Z4NPGG11H
view: corrected
1 person | 1 writer | 7 events | less than a second
participants: priya
event counts:
agent.action: 1
agent.output: 1
message: 1
turn.end: 1
turn.start: 1
writer.end: 1
writer.start: 1
attribution profile:
2 attributed actions, average confidence 1.00
recorded by the agent (explicit): 2
The attribution profile doubles as a diagnostic for the integrator: a
session where most attributed actions land on temporal (the engine had
to guess from timing) rather than explicit/turn_context (the agent
recorded a real cause) means the host application isn't passing
caused_by/context_message_ids — inspect says so directly, as a
printed nudge, once there's a large enough sample to generalize from.
With --json:
$ hansard inspect ./sessions/s_01KZVVGRNR3D5V708Z4NPGG11H --json
{
"hansard_inspect_version": 1,
"session_dir": "sessions\\s_01KZVVGRNR3D5V708Z4NPGG11H",
"sid": "s_01KZVVGRNR3D5V708Z4NPGG11H",
"view": "corrected",
"participants": ["priya"],
"writers": ["w_01KZVVGRNRKM87Z6X6TXYQR7EX"],
"event_counts": {
"writer.start": 1,
"message": 1,
"turn.start": 1,
"agent.action": 1,
"agent.output": 1,
"turn.end": 1,
"writer.end": 1
},
"total_events": 7,
"duration_seconds": 0.000577,
"attributable_count": 2,
"method_counts": { "explicit": 2 },
"average_confidence": 1.0,
"nudge": null
}
hansard prune¶
The one destructive command — dry-run by default. It prints what it
would delete without touching anything; --yes performs the deletion. Exit
code is 0 if every session could be classified, 1 if any session was
unreadable and therefore left out (dry-run or not). See Redaction and
retention for the full reasoning.
$ hansard prune ./sessions --older-than 30d
hansard prune: DRY RUN -- nothing has been deleted. Pass --yes to actually delete.
hansard prune: sessions (older than 30d)
nothing eligible for deletion
kept, not old enough (1):
- s_01KZVVGRNR3D5V708Z4NPGG11H last activity 2026-08-12T20:44:44.090538Z (2.4m old)
hansard prune: DRY RUN -- nothing has been deleted. Pass --yes to actually delete.
Lowering the threshold below the session's actual age flips the same session into the deletion list — still a dry run, still nothing touched:
$ hansard prune ./sessions --older-than 1m
hansard prune: DRY RUN -- nothing has been deleted. Pass --yes to actually delete.
hansard prune: sessions (older than 1m)
would delete (1):
- s_01KZVVGRNR3D5V708Z4NPGG11H last activity 2026-08-12T20:44:44.090538Z (2.2m old) [would delete]
hansard prune: DRY RUN -- nothing has been deleted. Pass --yes to actually delete.
Age is computed from the session's own last recorded event timestamp,
never filesystem mtime — a restored backup has fresh mtimes and stale
contents, and mtime-based pruning would silently keep the wrong sessions.
--older-than accepts an integer followed by d/h/m (days/hours/
minutes) — 30d, 24h, 90m — and rejects anything else, including a
bare 0, so a typo can never silently become "delete everything."