fix(backend): no empty "What's next?", and collapse exact-duplicate beats (#595, #588) #598

Merged
claude-bot merged 1 commit from fix/595-588-hook-and-dupes into main 2026-09-11 05:33:34 +00:00
Contributor

Two deterministic fixes from the Gemma 4 replay comparison (run a0784433, 2026-09-11). They're measured together on one dev rerun, before #596, which changes model output and is measured on its own.

#595: the empty "What's next?" hook

Gemma printed the prompts' quoted 'What's next?' hook as a heading and stopped, on both runs.

  • One instruction. The three prompts that ask for a hook (compose, single-shot, chunked-reduce) now share beat_service.HOOK_INSTRUCTION, which asks for a final paragraph that begins "What's next?" and says what the party faces or has decided.
  • A guard. audio_service.drop_empty_hook removes a last line that is only the phrase: markup and punctuation are allowed, and straight, curly or missing apostrophes all count. It runs on the beats path after the compose step and before note_unsummarised_windows, and at the end of the prose fallback.
    • A hook with content after it is untouched, and so is "what's next" mid-text.
    • An ordering test fails if the note were inserted first.
  • summarisation_runs.hook_dropped records when the guard fired, so an empty hook is visible rather than silently patched.

#588: exact duplicates only

  • The rule: beats with the same t_start and the same normalised summary (casefolded, whitespace collapsed, punctuation stripped at the edges) collapse.
  • A fallback, not a replacement. The rule is tried only when the existing shared-evidence rule finds nothing, so every earlier merge still happens, into the same surviving beat.
  • Which copy survives: the first, unless a later copy is verified and the first isn't. A verified event is never dropped for an unverified one (the #333 inversion).
  • No fields are merged. The survivor's citations were validated against its own actors.
  • summarisation_runs.beats_collapsed is the total the dedupe step removed. The log splits it by rule. It's NULL only when beats were never extracted.
  • #588 stays open for same-moment rewordings and split moments.

Worth knowing

  • Migration 8d9e0f1a2b3c (down 7c8d9e0f1a2b) adds hook_dropped BOOLEAN NOT NULL DEFAULT false and beats_collapsed INTEGER, not backfilled.
  • An extraction pass that re-finds an event word for word no longer counts as finding something new, so the pass loop can stop earlier. The dev rerun will show whether extraction_passes changes against run 3's 4.
  • beats_collapsed numbers will look large, because most of the total is later passes re-finding earlier events under the old rule.

Checked

  • Full backend suite: 3190 passed, 13 skipped. That includes 53 new tests: guard cases, the ordering interaction, both paths, persistence, the migration, and collapse and non-collapse cases.
  • Prompt tests at the request level assert the reworded instruction in the real llama.cpp request body, for all three calls.
  • Lint: ruff 0.4.4 check and format --check are clean.

🤖 Generated with Claude Code

Two deterministic fixes from the Gemma 4 replay comparison (run `a0784433`, 2026-09-11). They're measured together on one dev rerun, **before** #596, which changes model output and is measured on its own. ## #595: the empty "What's next?" hook Gemma printed the prompts' quoted `'What's next?' hook` as a heading and stopped, on both runs. - **One instruction.** The three prompts that ask for a hook (compose, single-shot, chunked-reduce) now share `beat_service.HOOK_INSTRUCTION`, which asks for a final paragraph that begins "What's next?" and says what the party faces or has decided. - **A guard.** `audio_service.drop_empty_hook` removes a last line that is only the phrase: markup and punctuation are allowed, and straight, curly or missing apostrophes all count. It runs on the beats path after the compose step and **before** `note_unsummarised_windows`, and at the end of the prose fallback. - A hook with content after it is untouched, and so is "what's next" mid-text. - An ordering test fails if the note were inserted first. - **`summarisation_runs.hook_dropped`** records when the guard fired, so an empty hook is visible rather than silently patched. ## #588: exact duplicates only - **The rule:** beats with the same `t_start` and the same normalised summary (casefolded, whitespace collapsed, punctuation stripped at the edges) collapse. - **A fallback, not a replacement.** The rule is tried only when the existing shared-evidence rule finds nothing, so every earlier merge still happens, into the same surviving beat. - **Which copy survives:** the first, unless a later copy is verified and the first isn't. A verified event is never dropped for an unverified one (the #333 inversion). - **No fields are merged.** The survivor's citations were validated against its own actors. - **`summarisation_runs.beats_collapsed`** is the total the dedupe step removed. The log splits it by rule. It's NULL only when beats were never extracted. - **#588 stays open** for same-moment rewordings and split moments. ## Worth knowing - **Migration `8d9e0f1a2b3c`** (down `7c8d9e0f1a2b`) adds `hook_dropped BOOLEAN NOT NULL DEFAULT false` and `beats_collapsed INTEGER`, not backfilled. - **An extraction pass that re-finds an event word for word no longer counts as finding something new**, so the pass loop can stop earlier. The dev rerun will show whether `extraction_passes` changes against run 3's 4. - **`beats_collapsed` numbers will look large,** because most of the total is later passes re-finding earlier events under the old rule. ## Checked - **Full backend suite:** 3190 passed, 13 skipped. That includes 53 new tests: guard cases, the ordering interaction, both paths, persistence, the migration, and collapse and non-collapse cases. - **Prompt tests at the request level** assert the reworded instruction in the real llama.cpp request body, for all three calls. - **Lint:** ruff 0.4.4 `check` and `format --check` are clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(backend): drop an empty "What's next?" hook, collapse exact duplicate beats (#595, #588)
All checks were successful
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 59s
CI / Backend lint (ruff) (pull_request) Successful in 27s
CI / Bot/backend version sync (pull_request) Successful in 22s
CI / Bot tests and audit (pull_request) Successful in 1m56s
CI / Docker image build (pull_request) Successful in 15s
CI / Frontend tests, audit, and build (pull_request) Successful in 2m31s
CI / Backend migration, tests, and audit (pull_request) Successful in 7m52s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Successful in 19m28s
0c4862d3c5
#595: every summary prompt asked for "a short 'What's next?' hook" and Gemma 4
26B printed the quoted phrase as a heading and stopped. The compose, single-shot
prose and reduce prompts now share beat_service.HOOK_INSTRUCTION, which asks for
a closing paragraph that begins "What's next?" and says what the party faces or
has decided. audio_service.drop_empty_hook strips a bare hook on the last
non-empty line (straight, curly or missing apostrophe; markup allowed) and
nothing else; it runs on the beats path before note_unsummarised_windows, which
positions its note relative to the hook, and on both prose paths. Recorded as
SummarisationRecord/summarisation_runs.hook_dropped.

#588 (exact half only): dedupe_beats gains a fallback rule collapsing beats with
the same start second and the same normalised summary (casefolded, whitespace
collapsed, edge punctuation stripped). First occurrence kept unless only the
later copy is verified; no fields merged. It is tried only when the older
same-event rule matches nothing, so existing merges are unchanged. The total
removed across passes is recorded as beats_collapsed (NULL when beats were never
extracted); the per-rule split is logged. Rewordings and split moments are left
for #588.

Migration 8d9e0f1a2b3c adds both columns (hook_dropped BOOLEAN NOT NULL DEFAULT
false, beats_collapsed INTEGER NULL), wired through the model, API schema,
run-to-API mapping and _persist_summarisation_run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
claude-bot scheduled this pull request to auto merge when all checks succeed 2026-09-11 05:12:51 +00:00
claude-bot deleted branch fix/595-588-hook-and-dupes 2026-09-11 05:33:35 +00:00
Sign in to join this conversation.
No description provided.