Skip to content

fix(history): make Phase 4 finalization interrupt-safe (#137)#139

Merged
Sootopolis merged 1 commit into
mainfrom
wip
Jun 27, 2026
Merged

fix(history): make Phase 4 finalization interrupt-safe (#137)#139
Sootopolis merged 1 commit into
mainfrom
wip

Conversation

@Sootopolis

Copy link
Copy Markdown
Owner

What

HistoryApp.discoverClub's onInterrupt guard covered only Phases 2–3; Phase 4 (HistoryRun.complete + logSummary) ran outside it. ZIO's .onInterrupt deregisters once the wave block completes, so an interrupt landing in the Phase-4 window ran neither finalizer, leaving history_run stranded in its non-terminal Phase-1 state.

Fix

  • Widen the interrupt guard to span Phases 2–4 as one effect.
  • Gate the onInterrupt finalizer on a new finalizedRef.
  • Make the success-path finalize (complete + logSummary + finalizedRef.set(true)) commit atomically (.uninterruptible).

Result — exactly one finalize on every exit, run always terminal:

  • success → atomic full-stats finalize, flag flipped; a late interrupt finds the flag set and skips.
  • interrupt anywhere earlier → flag false → partial-stats finalizeInterrupted.
  • never both (the old code's risk was neither, not double).

Fixes

Closes #137 (surfaced during review of #138).

Testing

  • Full sbt test green via pre-push (960 passed).
  • testOnly history + TestJobRunner + stats + membership (143) — green, incl. TestHistoryAppNetworkOutage abort path.
  • No bespoke interrupt test: deterministically hitting the Phase-4 microsecond window would be flaky (against repo's deterministic-test preference); the fix is structural and compiler-checked, existing interrupt/abort coverage unbroken.

🤖 Generated with Claude Code

discoverClub's onInterrupt guard covered only Phases 2-3; Phase 4
(HistoryRun.complete + logSummary) ran outside it. An interrupt landing in that
window ran neither finalizer — the wave block's onInterrupt had already
deregistered — leaving history_run stranded in its non-terminal Phase-1 state.

Widen the guard to span Phases 2-4 and gate the onInterrupt finalizer on a
finalizedRef. The success-path finalize (complete + logSummary + flag) commits
atomically (uninterruptible) and flips the flag, so a late interrupt skips the
partial-stats finalize; an interrupt anywhere earlier finds the flag false and
completes the run with partial stats. Exactly one finalize on every exit, and
the run always reaches a terminal state.

Closes #137

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Sootopolis
Sootopolis merged commit 6155bf6 into main Jun 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HistoryApp Phase 4 finalization is not interrupt-safe — interrupt after the wave block strands a non-terminal history_run

1 participant