Expand description
RunObserver implementations for the two normative
presentation modes of EXEC-016.
Two distinct observer types live here. They share the
EXEC-016 vocabulary (mode-specific surfacing of a task’s
captured stdout and stderr) but compose differently with
the scheduler:
-
LiveOutputObserversplits incoming byte chunks on\nand writes each complete line to the configuredstdout/stderrsink prefixed with[project:task]. Multiple in-flight tasks interleave at line granularity; never within a line. Trailing partial bytes (a write with no terminating newline) are buffered until the next\nor until the task terminates, at which point they are flushed with an appended\nso the line-atomicity invariant is preserved. -
BufferedOutputObserveraccumulates incoming byte chunks per task into in-memory buffers and writes each task’s fullstdoutfollowed by its fullstderras two contiguous blocks when the task terminates. No tag prefix; bytes are verbatim. Output from different tasks never interleaves.
Both observers are generic over the sink types O: Write + Send (stdout) and E: Write + Send (stderr). Production code
passes std::io::Stdout / std::io::Stderr (or their
lock guards). Tests pass Vec<u8> (which implements
Write) and call LiveOutputObserver::into_inner /
BufferedOutputObserver::into_inner after the run to extract
the captured bytes.
Interior mutability is a single std::sync::Mutex per
observer guarding the sinks together with the per-task
accumulator map. The kernel write syscall dominates lock-hold
time; per-stream contention savings are theoretical. Holding a
single mutex across the line-splitting and sink write is what
enforces EXEC-016’s atomic-per-line-writes contract in live
mode.
Both observers implement the cache-hit contract of EXEC-017
implicitly: crate::run_task::restore_from_hit feeds cached
stdout / stderr bytes through the same
RunObserver::on_stdout and RunObserver::on_stderr calls
a fresh run uses. The observer cannot distinguish a hit from a
fresh run; both surface byte-identically.
Cache contents themselves are untouched by either observer.
EXEC-016’s “the captured bytes that enter the cache per
CACHE-012 are the bytes the process wrote, EXACTLY, with no
prefix and no transformation” is satisfied structurally:
crate::run_task::run_fresh hashes and stores the raw byte
buffers; the observer reads its own copy of the same bytes for
presentation.
Structs§
- Buffered
Output Observer - Buffered-mode
RunObserverperEXEC-016. - Live
Output Observer - Live-mode
RunObserverperEXEC-016.