feat(webapp): a bounded summary and a complete event log (#568) #587

Merged
claude-bot merged 1 commit from feat/568-summary-and-event-log into main 2026-09-10 06:18:55 +00:00
Collaborator

Closes #568, to the design decided on that issue: both a short summary and a complete log, rather than choosing between them. Stacked behind #585; will be rebased before merge.

Why. The compose prompt asks for 3 to 6 paragraphs and for every event to be represented. Those agree only while a session yields a few dozen events. Gemma 4 26B verified 59 on a real session; compose dropped 12 and the retry recovered 2. Hosted models produced 84 to 93 and 10,000-character recaps.

Selection. select_beats_for_compose puts reveals, decisions, combat and travel ahead of social and other — two tiers, not six, because ranking within the first tier would prefer every reveal to every combat and misreport a session that was mostly a fight — then takes 30 and re-sorts chronologically so #566's promise holds. At or below the cap it returns the input list unchanged, so a normal session's summary is byte-identical to today; two tests pin that, including the inclusive boundary.

The threading matters more than the function. recover_dropped_events now gets the selected list, or the retry would re-request the events the cap deliberately omitted and undo it. The unsupported-sentence critic keeps the full list, because a name from a non-selected beat is a real person and the narrower set would cry wolf. Both are commented at the call sites.

The log is computed on read from the persisted beats, not stored: a frozen copy could only drift, and would drift exactly where it matters, when a transcript edit or an erasure changes what the beats mean. Doing it through render_summary_anchors closed a real duplication — to_response had its own inline anchor derivation, which is how a GM ends up sent to one timestamp by an anchor and another by the row beside it.

Visibility. sessions.summary is member-readable today (unlike transcript and beat_notes, which are nulled for non-GMs, and unlike the run detail, which is GM-only because it surfaces unverified beats). The log follows the summary exactly: member-readable, not gated on approval, never an unverified beat, 403 for a non-member. A session with no run returns an empty list rather than a 404, so "no log" is distinguishable from a failure.

Tests: backend 3,099 passed; frontend 851 passed with eslint and build clean; eval scores unchanged before and after, which is expected because every fixture holds 5 to 8 beats and the selection is therefore the identity function on all of them — the harness is wired through it anyway so a future larger fixture is scored on what the product actually delivers.

🤖 Generated with Claude Code

Closes #568, to the design decided on that issue: both a short summary and a complete log, rather than choosing between them. Stacked behind #585; will be rebased before merge. **Why.** The compose prompt asks for 3 to 6 paragraphs *and* for every event to be represented. Those agree only while a session yields a few dozen events. Gemma 4 26B verified 59 on a real session; compose dropped 12 and the retry recovered 2. Hosted models produced 84 to 93 and 10,000-character recaps. **Selection.** `select_beats_for_compose` puts reveals, decisions, combat and travel ahead of social and other — two tiers, not six, because ranking within the first tier would prefer every reveal to every combat and misreport a session that was mostly a fight — then takes 30 and re-sorts chronologically so #566's promise holds. At or below the cap it returns the input list unchanged, so a normal session's summary is byte-identical to today; two tests pin that, including the inclusive boundary. **The threading matters more than the function.** `recover_dropped_events` now gets the *selected* list, or the retry would re-request the events the cap deliberately omitted and undo it. The unsupported-sentence critic keeps the *full* list, because a name from a non-selected beat is a real person and the narrower set would cry wolf. Both are commented at the call sites. **The log is computed on read** from the persisted beats, not stored: a frozen copy could only drift, and would drift exactly where it matters, when a transcript edit or an erasure changes what the beats mean. Doing it through `render_summary_anchors` closed a real duplication — `to_response` had its own inline anchor derivation, which is how a GM ends up sent to one timestamp by an anchor and another by the row beside it. **Visibility.** `sessions.summary` is member-readable today (unlike `transcript` and `beat_notes`, which are nulled for non-GMs, and unlike the run detail, which is GM-only because it surfaces unverified beats). The log follows the summary exactly: member-readable, not gated on approval, never an unverified beat, 403 for a non-member. A session with no run returns an empty list rather than a 404, so "no log" is distinguishable from a failure. Tests: backend 3,099 passed; frontend 851 passed with eslint and build clean; eval scores unchanged before and after, which is expected because every fixture holds 5 to 8 beats and the selection is therefore the identity function on all of them — the harness is wired through it anyway so a future larger fixture is scored on what the product actually delivers. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
rbrooks force-pushed feat/568-summary-and-event-log from 7dd25d043c
Some checks failed
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 56s
CI / Frontend tests, audit, and build (pull_request) Successful in 2m59s
CI / Bot tests and audit (pull_request) Successful in 1m58s
CI / Backend lint (ruff) (pull_request) Successful in 27s
CI / Bot/backend version sync (pull_request) Successful in 21s
CI / Docker image build (pull_request) Successful in 3m15s
CI / Backend migration, tests, and audit (pull_request) Successful in 9m17s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been cancelled
to 935a19745c
All checks were successful
CI / Bot/backend version sync (pull_request) Successful in 26s
CI / Backend lint (ruff) (pull_request) Successful in 39s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m25s
CI / Bot tests and audit (pull_request) Successful in 2m31s
CI / Frontend tests, audit, and build (pull_request) Successful in 3m16s
CI / Docker image build (pull_request) Successful in 3m14s
CI / Backend migration, tests, and audit (pull_request) Successful in 13m23s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Successful in 20m44s
2026-09-10 05:49:55 +00:00
Compare
claude-bot scheduled this pull request to auto merge when all checks succeed 2026-09-10 05:49:59 +00:00
claude-bot deleted branch feat/568-summary-and-event-log 2026-09-10 06:18:57 +00:00
Sign in to join this conversation.
No description provided.