Skip to content

feat(task): emit_trial fans out to writer + relay; guaranteed teardown - #79

Merged
larsrollik merged 1 commit into
mainfrom
ft/emit-trial-fanout
Aug 18, 2026
Merged

feat(task): emit_trial fans out to writer + relay; guaranteed teardown#79
larsrollik merged 1 commit into
mainfrom
ft/emit-trial-fanout

Conversation

@larsrollik

Copy link
Copy Markdown
Member

PR A of the emit_trial convergence (docs/plans/PLAN_task_isolation_emit_trial.md §7). The framework-guarantee piece: make emit_trial the single per-trial seam and ensure teardown always runs, so no task can end a session with unpersisted data. Prompted by an audit that found the bare periodic_trigger protocol writes no .msw.df.jsonl at all because each task hand-rolls its own write + relay.

What

  • TaskRunner.emit_trial(trial, *, relay=None) — one call now (a) durably writes the scored trial through the injected TrialDataWriter, and (b) if a relay= event is given (the task's flat monitor event, a different shape from the stored trial), dispatches it to the LogAgent relay queue best-effort (contextlib.suppress — a down/full monitor never interrupts the task). Migrated tasks stop hand-rolling put_nowait boilerplate; the write and relay can't drift apart.
  • Guaranteed teardownTaskProcess.__exit__ now calls exit_safely() from a finally, so the trial-data writer is flushed (close() when trial_count > 0) and hardware is torn down even if a post-hook or finalize_acquisition_in_session raises. Previously a raise there skipped teardown, so a session could end with its df unflushed or a device left open.

Compatibility

Fully additive: relay defaults to None (existing emit_trial(dict) callers unchanged), and un-migrated tasks that still write their own file are untouched. No task-boundary change. The finalize error still propagates — teardown is guaranteed to run, but a real finalize failure is not swallowed.

Tests

tests/test_emit_trial.py: relay dispatched only when relay= is passed; the full dict is written while the flat event is relayed; a raising relay queue is swallowed; and teardown (writer close() + clear) runs even when finalize_acquisition_in_session raises. Full msw-core suite green.

Next

PR B (msw-tasks-lab) migrates the trigger family onto this seam and fixes periodic_trigger; PR C migrates the remaining tasks; PR D adds the two-payload canonical record shared with control-plane §T8.

Make `TaskRunner.emit_trial` the single per-trial seam: it durably writes
the scored trial through the injected `TrialDataWriter` and, when a `relay=`
event is passed, dispatches that flat monitor event to the LogAgent relay
queue best-effort (a down monitor never interrupts the task). This lets a
task persist + relay in one call instead of hand-rolling both.

Also run `TaskProcess.exit_safely()` from a `finally` in `__exit__`, so the
trial-data writer is flushed and hardware torn down even if a post-hook or
the acquisition finalise raises — a session can no longer end with its df
unflushed or a device left open.

Minor bump (feat).
@larsrollik
larsrollik requested a review from a team as a code owner August 18, 2026 13:50
@larsrollik
larsrollik merged commit 405684e into main Aug 18, 2026
7 checks passed
@larsrollik
larsrollik deleted the ft/emit-trial-fanout branch August 18, 2026 13:53
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.

1 participant