ADR-189: Session introspection implementation plan¶
Written during the ADR-153/154 sequencing work, before ADR-156 shipped (pre-156). The body below still treats a semantic fire as raw cosine
fire_score ≥ embed_threshold(§ "No fabricated semantic 'why.'") — the retired model. ADR-156 superseded that: a fire is nowg(s) ≥ τ_sin probability space, whereg(s) = σ(a·s + b)is a per-model logistic fit at corpus generation andτ_s = 0.5is a global threshold — there is noembed_thresholdfrontmatter field and no per-way threshold. Preserved for the reasoning that led here; for the shipped model see ADR-156 and../hooks-and-ways/engine-reference.md.Cites: ADR-153 (introspection substrate), ADR-154 (three front-ends), ADR-201 (shared finding evidence), ADR-134 (near-miss telemetry) Motivates: the
ways introspect <replay|live|dump>surface (ways rethinkfixes + a live monitor + non-interactive dump) and the why-fired drill-down
What this note is¶
The two ADRs decide what and why; this note sequences the how into independently-shippable increments, so the work can land incrementally without a big-bang PR. Grounded in the 2026-07-02 research pass (three probes: rethink internals, correlation data model, TUI trade study).
Guardrails (carry into every increment)¶
- Join honesty (ADR-153). The fired-way ↔ turn link is keyed only after
enrichment (increment 3); before that it is a
(session, token_position, ts≈)heuristic bucket. The model must label each edge's confidence; never render a proximity guess as a foreign key. - No fabricated semantic "why." One embedding per way — there is no matched
vocabulary term to recover. Present semantic fires as way-level cosine
(
fire_score≥embed_threshold), never a highlighted term. - Lean binary. Zero new deps is the target (crossterm + micro-compositor, poll
- stat-gate). Adopting ratatui is the documented escape hatch only if the inspector grows into a true multi-pane/mouse/resizable tool — decide before, not during.
- Transcripts are read-only (Claude-Code-owned) and may be truncated to a
spilled
tool-results/…-additionalContext.txt; tolerate absence and truncation.
Increments¶
1 — Correctness fixes (fast, independently shippable) — ADR-153 §1, ADR-154 §4¶
The bugs that make rethink wrong today. Land first; delivers value alone.
- Events-log single-writer. Add
ways events-log-path; rewirehooks/.../clear-markers.shandinject-subagent.shto resolve it from the binary (precedent:ways response-topics-path). Optional transitional union-read inresolve_events. This is the fix for "rethink doesn't see post-1.0.0 sessions." - Project scoping.
rethink/rethink_dump: default to current project, add--all, fail-loud (or cwd-fallback) when detection isNone, compare on slug/normalized path not substring. Insertion points:rethink.rs:123-134+665-668;rethink_dump.rs:94-102+277-281. ways rethink --list --json— machine-listable session enumeration. (Becomesways introspect list --jsonat increment 4; the alias preserves this form.)- Key files:
session.rs(log_event / new path cmd), the two shell hooks,cmd/rethink.rs,cmd/rethink_dump.rs,ways-core/src/paths.rs.
2 — The SessionIntrospection model — ADR-153 §2¶
- New
ways-coremodule (orcmd/introspect/model.rs): pure serde structsSession → Turn → FiredWay{criteria, match?}with confidence-labelled joins. - Generalize
rethink::reconstruct_framesinto it (keep the existing frame data; addMatchCriteriafrom frontmatter fire-bearing fields, read viacmd/scan/candidates.rs's raw extraction). - No UI yet — model + unit tests over sample events + a fixture transcript.
3 — Precise "why" — ADR-153 §3 (revised: split by source)¶
Investigation (2026-07-03) corrected §3: no message uuid is available at fire time
(UserPromptSubmit carries none), but the transcript records each hook injection
with a parentUuid chain back to its user message. So the two halves are sourced
differently and land as two independently-shippable sub-increments:
- 3a —
transcript_uuid, post-hoc in the ways-core model (no hot path). Read the transcript, indexUserPromptSubmitattachments (theirparentUuidwalks to the triggeringusermessage), match each turn to its attachment by session + fire-timestamp, follow to the user-message uuid, and flip that turn's joinHeuristic→Keyed. Absent/unmatched transcript → staysHeuristic. Reuse the transcript-reading pattern fromcontext.rs/rethink::build_token_timeline. - 3b —
matched_span, fire-time enrichment (hot path). The injected content is way-anonymous, so what text matched a way must be captured when it fires. Addmatched_spanfor the keyword/command/file channels at the fire sites (cmd/show/mod.rsway_scoredlog block ~155-186, viascan/scoring.rs:179;cmd/scan/state.rs). Plumb the regex/glob match throughPromptMatch::Fired→scoring.rs→way_scored. Cheap, line-atomic. Semantic staysfire_scoreonly. - Model reads each field when present, falls back to the heuristic grain when absent.
4 — ways introspect surface + non-interactive dump — ADR-154 §1, §4 (agent-facing MVP)¶
- Introduce the
ways introspect <replay|live|dump|list>command; wireways rethinkas a deprecated alias →introspect replay(stderr notice, still works). - Re-point
rethink_dumpat the increment-2 model asways introspect dump. This is the autonomous-investigation surface — ship it before the TUIs.
5 — Micro-compositor + why-fired drill-down — ADR-154 §2¶
- ~200–400-line compositor over
render.rs's ANSI-Stringpanels: panel (Vec<String>+width), side-by-side placer, scroll-window, tab-bar. - Drill-down tab in
rethink: fired-ways list → enter → way body +MatchCriteria - matched clip (precise post-enrichment, heuristic-labelled before).
6 — ways introspect live monitor — ADR-154 §3¶
- New mode; reuse
rethink::tui_loop'spoll(Duration)skeleton with the refresh interval as timeout; re-read tail of events-log + transcript on tick; mtime/size stat-gate to skip unchanged re-parse. Same compositor + model.
Resolved decisions (settled 2026-07-02)¶
- Command naming — DECIDED: one unified
ways introspect <replay|live|dump>surface (ADR-111 single-surface spirit), not theways rethink/ways thinkverb pair (rejected as too cute).ways rethinkis kept as a deprecated alias →introspect replay(andrethink --json→introspect dump) so existing muscle memory keeps working. TheintrospectCLI surface + alias is a thin step introduced at increment 4 (the first new command); increments 1–3 fix and factor the underlying code thatintrospect replaywill call. - Shared substrate with ADR-201 — DECIDED: the
SessionIntrospectionmodel lives inways-core(notways-cli) and is the finding pipeline's evidence source, so introspection and findings read one correlation, not two drifting re-derivations. - ratatui escape-hatch trigger — DECIDED: the concrete criterion is the drill-down needing text selection or resizable / mouse-driven panes. Short of that, the micro-compositor stays. It's a criterion now, not a vibe.