Repository navigation
Ask before closing while recording or streaming, and finish the files first (T1.8) - #467
Merged
Merged
Conversation
… first (T1.8, #461) Closing the app kill-treed the media core with no stop sent, so the Program MP4's moov atom, every ISO writer and any RTMP/SRT egress were cut off mid-write: an operator who closed the app after a show lost the recording. - CloseGuardPolicy (pure, tested): asks when recording or streaming is live per the shell flags OR the core lifecycle; stopping/finalizing still count; absent lifecycle trusts the shell flag; virtual camera alone never asks. - MainWindow: "Stop outputs and close?" ContentDialog (Stop and close / Keep running, Keep running default). A second close while it is open or while outputs finish is ignored. Any exception logs and falls back to the existing shutdown path. - OutputShutdownCoordinator: Stop and close sends the existing transport stops, then waits for terminal lifecycle evidence from core snapshots, re-sending a stop that did not land, bounded at 15 s (loud log on timeout), before the unchanged ShutdownAsync. - App-exit core stop only: close stdin (the core exits on EOF), keep draining its stdout, give it 2 s (ShutdownBudget) to exit on its own, then the old kill-tree. Leave-meeting / respawn / failure fallback keep the immediate kill. - CLAUDE.md (Engine teardown order) and docs/BACKLOG.md T1.8 updated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SAB8v62BEV8ihmaeGhhtY9
…g failure Review findings (t1-8-review.md) and binding rulings: - Evidence is tied to THIS stop: the coordinator reads the bridge's LastSnapshot (no competing syncs) and accepts only a snapshot whose RawReceivedUtc is later than the last moment the stop was pending (intent set or a Record/Stream command in flight). CloseGuardPolicy captures an OutputStopBaseline at the close request (live recording session id, live senders, core generation); only their terminal states count. Absent nodes, stale pre-stop completions, other sessions and old failures never read as finished. A changed restart count ends the wait as CoreRestarted, a dead core as CoreUnavailable, both logged loudly. Stops are re-sent while intent is still set. - CloseGuardFlow (tested) owns the close flow. A dialog failure while outputs are live takes the Stop-and-close path, never the old kill path. The window is restored/activated before the dialog, which opens after the Closing callback returns; ignored closes bring it forward. - Starts are refused while Stop and close finishes the outputs. - Legacy recording.active with no lifecycle now asks. - Supervisor: a plain Stop during an app-exit grace kills the retiring core; descendants are snapshotted before stdin closes and survivors of a clean exit are killed; stderr is drained (<=250 ms) after a clean exit. Post-core-stop disposal time is logged. - MediaCoreAppExitStopTests: structural assertions only (exit-handler trace), generous graces, hang guards; plus sweep and retiring-kill tests. - CLAUDE.md (Engine teardown order) and docs/BACKLOG.md T1.8 updated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SAB8v62BEV8ihmaeGhhtY9
…line at send Scoped re-review (t1-8-rereview.md): - N1: record+stream always timed out at 15 s. A recording stop sent while Streaming was still desired carried start-program-output, which restarts the encoder and erases the recording lifecycle. SendStops now stops streams first, then recording. Tests pin the order (first send and re-send) and model the record+stream case end to end; both fail with the order swapped. The core defect is left for a separate backlog item. - N2: the core-generation baseline is re-armed when the stop is sent; a core respawned while the dialog was open is logged, and the new core's outputs are still stopped and waited for. - N3: a baseline sender counts as finished only when its adapter reads stopped/failed (not a core idle or a re-served older terminal state). - N4: a retiring core killed by a concurrent Stop() is reported Killed, and the clean-exit drain/sweep are skipped. - N5: interrupted is no longer terminal (matches the core's isTerminal); a stalled recording is the live baseline and its later failure is reported. - CLAUDE.md and BACKLOG updated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SAB8v62BEV8ihmaeGhhtY9
Owner
Author
Live checklist: all 8 checks passed (2026-09-10, 21:26-21:40)Setup
Observed, not blocking
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #461 (T1.8). Owner decision: ask first.
Before: closing the app killed the core's whole process tree. No stop-recording was sent, so the Program MP4, every ISO and any RTMP/SRT stream were cut off mid-write. An operator who closed the app after a show lost the recording.
Now
completed, a respawned core'sidle, or a missing node never counts. A dead or restarted core ends the wait immediately, logged loudly as the recording being interrupted with nothing more to save.JsonRpcServer.cppEOF → loop break → joins →mainreturns). So app exit now closes stdin, waits 2 s, and only then kill-trees. The supervisor keeps draining the retiring core's stdout, or a full pipe would wedge its clean exit. Leftover child processes are swept after the exit. Leave-meeting, respawn and failure paths still stop immediately. The whole close fits inside the 5 s / 6 s shutdown budget, and a test pins that.CloseGuardPolicy(pure),CloseGuardFlow, andOutputShutdownCoordinator. StudioViewModel and MainWindow only forward.Review. One task review (4 Important, 9 Minor) and two fix rounds. Scoped re-review 1 found N1: Record and Stream together always hit the 15 s timeout. Scoped re-review 2 approved it with no findings.
Tests. WinUI 1409/1409. MediaCore 2171/2171, 3 back-to-back runs. The app-exit tests are structural, with no wall-clock assertions, and passed 5/5 with every core pinned busy.
Known gaps (tracked)
stop_raw_media. That needs a core change.Required before merge: live checklist (owner, test build stopped, quiet machine)
launch.logshowsmedia core exited on its ownor…killed. Note which, and the time.outputs finished after …ms. The Program MP4 and each ISO play, and ffprobe reads a duration.outputs finished after …ms, not the 15 s timeout.CoreVideoPro.WinUI.exe.*.dmpfiles and zero Application Error 1000 events across all runs.🤖 Generated with Claude Code
https://claude.ai/code/session_01SAB8v62BEV8ihmaeGhhtY9