Summary
Today, tms's events table (tms_review.events) captures dispatched-reviewer events with model, provider, session columns, but does NOT capture the interactive session that drove a workstream. So if we want to A/B compare interactive session models (e.g. Claude Code kimi-k3 vs pi/MiniMax-M3 vs future Claude-Code / Max-sub sessions), we have no fleet-side data — only ad-hoc wrap memos and pi footer text.
This is the missing link: a small event class on the events table fires whenever a tmq/aoe session starts, carrying the interactive session's provider + model. Once landed, all the analytics (plan-gate latency, code-review P0-detection, cost-per-task) become sliceable by interactive session model, not just by dispatched reviewers.
Scope
- Add a new event class:
interactive_session_start (and interactive_session_end for symmetry).
- Schema change: NONE. Existing columns
session, model, provider, event_type, event_timestamp, repo, worktree, payload already cover everything; idempotency UNIQUE on (event_type, aoe_id_prefix, event_timestamp) covers backfill too.
- Hook: at aoe/tmq spawn time (after the worktree is attached, before the agent process boots), emit one
interactive_session_start event with the served model+provider from pi's models.json (or equivalent) and the aoe session id as aoe_id_prefix.
- Migration path: introduce the event class in the producer (tms poll, tmq spawn, or pi CLI
--record-session-start). Backfill: aoe list can reconstruct historical sessions; models.json on each worktree provides the model. For backfill beyond what the producer captures, scrape existing ~/.local/state/tmq/events.jsonl archives (if any were retained) and the agent-config repo for served-model hints.
- Query surface: add a tms CLI subcommand
tms events sessions (or extend tms events transitions) that returns events filtered by event_type='interactive_session_start' and joins on reviewer_runs.model / llm_call_log for the workstream attribution.
Why now
This week's dogfood waves (Jul 19, two wraps) dispatched 14 PRs and recorded 29 reviewer_runs rows, all with panel= attribution. The missing attribution is the interactive session that authored the day. Without it, every "how is model X performing?" question reduces to inference from memos.
Out of scope
- A new "sessions" table — the events table already covers this surface.
- A cost column on the event itself — cost flows through
llm_call_log (worktree attribution) and joins from there.
- Per-user attribution (which human kicked off which session) — would need an aoe event for
start_session that doesn't exist yet.
Acceptance criteria
References
- Precedent: distillery's
media_digest_runs.model = actually-served (PR distillery#442, memory distillery-provenance-served-model.md) — the same pattern: capture the model that actually served, not the model that should have served.
- Existing migration
006-specialist-composition.sql establishes the precedent for additive column-add migrations on tms_review.
bogocat/openagent#51 (weekly fleet-digest) is the downstream consumer; this column lands the sliceable dimension.
- Memory: "Fleet runtimes: pi is primary" + "MiniMax-M3 eval — Opus-class; caveat = runtime reliability" make the case for the data without the metric being available.
- Related:
tms#101 (verdict capture → reviewer_runs, landed 2026-07-19) did this for dispatched reviewers; this issue extends the same pattern to the interactive session.
Summary
Today, tms's events table (
tms_review.events) captures dispatched-reviewer events withmodel,provider,sessioncolumns, but does NOT capture the interactive session that drove a workstream. So if we want to A/B compare interactive session models (e.g. Claude Code kimi-k3 vs pi/MiniMax-M3 vs future Claude-Code / Max-sub sessions), we have no fleet-side data — only ad-hoc wrap memos and pi footer text.This is the missing link: a small event class on the events table fires whenever a tmq/aoe session starts, carrying the interactive session's provider + model. Once landed, all the analytics (plan-gate latency, code-review P0-detection, cost-per-task) become sliceable by interactive session model, not just by dispatched reviewers.
Scope
interactive_session_start(andinteractive_session_endfor symmetry).session,model,provider,event_type,event_timestamp,repo,worktree,payloadalready cover everything; idempotency UNIQUE on (event_type, aoe_id_prefix, event_timestamp) covers backfill too.interactive_session_startevent with the served model+provider from pi'smodels.json(or equivalent) and the aoe session id asaoe_id_prefix.--record-session-start). Backfill:aoe listcan reconstruct historical sessions;models.jsonon each worktree provides the model. For backfill beyond what the producer captures, scrape existing~/.local/state/tmq/events.jsonlarchives (if any were retained) and the agent-config repo for served-model hints.tms events sessions(or extendtms events transitions) that returns events filtered byevent_type='interactive_session_start'and joins onreviewer_runs.model/llm_call_logfor the workstream attribution.Why now
This week's dogfood waves (Jul 19, two wraps) dispatched 14 PRs and recorded 29 reviewer_runs rows, all with
panel=attribution. The missing attribution is the interactive session that authored the day. Without it, every "how is model X performing?" question reduces to inference from memos.Out of scope
llm_call_log(worktree attribution) and joins from there.start_sessionthat doesn't exist yet.Acceptance criteria
interactive_session_startevent withsession,model,provider,worktreepopulatedtms backfill session-eventsor similar) reconstructs the past N days of interactive session starts fromaoe listJSON + the worktree'smodels.jsontms events sessions --model <id> --since <date>returns the sessions for that model; cross-joinsreviewer_runsto attribute panel verdicts to their driving interactive sessionmodels.jsongraceful skipReferences
media_digest_runs.model = actually-served(PR distillery#442, memorydistillery-provenance-served-model.md) — the same pattern: capture the model that actually served, not the model that should have served.006-specialist-composition.sqlestablishes the precedent for additive column-add migrations ontms_review.bogocat/openagent#51(weekly fleet-digest) is the downstream consumer; this column lands the sliceable dimension.tms#101(verdict capture → reviewer_runs, landed 2026-07-19) did this for dispatched reviewers; this issue extends the same pattern to the interactive session.