Reproduction
On main at 992db43f45ec856d5f0d8cb2936cdb4501b8fb9b, run_and_observe_episodes() creates a VideoSubject, but its ExitStack only disposes observable subscriptions. It does not dispose the recorder itself.
When a policy or environment raises after the first video frame, there is no final timestep to release the OpenCV writer. The incomplete recording remains open while the recorder or exception traceback is retained. VideoSubject already provides the needed writer cleanup in its dispose() method.
Expected behavior
Register recorder disposal with the existing ExitStack, before registering subscriptions so that they are detached first. Finalize writers when evaluation exits, preserving returns, video paths and the original error.
The regression tests use the actual evaluation loop, population workers and real OpenCV files, retaining references so cleanup cannot depend on garbage collection. This is independent of the writer type-narrowing PR #399 and does not require editing VideoSubject.
Reproduction
On main at
992db43f45ec856d5f0d8cb2936cdb4501b8fb9b,run_and_observe_episodes()creates a VideoSubject, but its ExitStack only disposes observable subscriptions. It does not dispose the recorder itself.When a policy or environment raises after the first video frame, there is no final timestep to release the OpenCV writer. The incomplete recording remains open while the recorder or exception traceback is retained. VideoSubject already provides the needed writer cleanup in its
dispose()method.Expected behavior
Register recorder disposal with the existing ExitStack, before registering subscriptions so that they are detached first. Finalize writers when evaluation exits, preserving returns, video paths and the original error.
The regression tests use the actual evaluation loop, population workers and real OpenCV files, retaining references so cleanup cannot depend on garbage collection. This is independent of the writer type-narrowing PR #399 and does not require editing VideoSubject.