pub struct SpawnOpts {Show 15 fields
pub cwd: PathBuf,
pub env: Vec<(String, String)>,
pub label: Option<String>,
pub initiator: Initiator,
pub pty_cols: u16,
pub pty_rows: u16,
pub stdin_enabled: bool,
pub pin: bool,
pub beholder_select: BeholderSelect,
pub verbatim_output: bool,
pub log_fd_enabled: bool,
pub origin: Option<String>,
pub argv: Option<Vec<String>>,
pub pipe: bool,
pub pipefail: bool,
}Expand description
Options for TaskDriver::spawn_run.
Fields§
§cwd: PathBuf§env: Vec<(String, String)>Env vars set on the child process (merged on top of the current env).
label: Option<String>§initiator: Initiator§pty_cols: u16PTY column count. Defaults to 80.
pty_rows: u16PTY row count. Defaults to 24.
stdin_enabled: boolEnable stdin relay via TaskDriver::send_stdin.
pin: boolPin the run so the GC sweep does not drop its output during warm rolloff.
beholder_select: BeholderSelectBeholder attachment policy. Defaults to BeholderSelect::Auto.
verbatim_output: booltrue when this run’s output is read as text by somebody downstream —
a human watching a terminal tile, or a client that promised its caller
byte-identical passthrough. Causes Rewriter beholders to decline in
Auto mode, since a rewrite changes what that reader gets.
R739-B10 renamed this from tty_attached: a PTY was only ever a proxy
for “someone is reading this”, and the proxy broke the moment
yah build run moved onto pipes (R739-F6).
log_fd_enabled: boolCreate a side-channel FIFO and export YAH_TASK_RUN / YAH_LOG_PIPE
so Tier-2 shim libraries (yah-log-rust, @yah/log) can emit structured
events. Has no effect on non-Unix platforms. Defaults to true.
origin: Option<String>Provenance tag stored on the run’s TaskRunMeta.origin (e.g.
Some("terminal") for an interactive shell). None is an ordinary job.
argv: Option<Vec<String>>Exec this argv directly instead of wrapping cmd in sh -c.
The default sh -c <cmd> is right for a job — the caller wrote a
command line and expects a shell to parse it. It is wrong for an
interactive shell: sh -c "zsh -l" leaves an inert sh as the PTY’s
foreground process group leader, so job control misbehaves, signals go
to the wrong process, and anything that reads the foreground pid (a
live-cwd probe, say) sees sh instead of the shell the operator is
typing into. Handing the exact argv here makes the shell itself the
child, which is also the only way to pass -l as a real argv element
so .zprofile / .profile actually run.
cmd is still what gets recorded on TaskRunMeta.command, so the run
reads the way the caller asked for it. Beholder argv rewriting is
bypassed when this is set: the caller has already decided the exact
process to exec, and a recorded rewrite=… that didn’t happen would be
a lie in the run metadata.
pipe: boolR739-F6 — spawn on pipes instead of a PTY. Defaults to false,
which is the PTY behaviour every existing caller already has.
A PTY is right for an interactive terminal tile: the child gets a
controlling terminal, job control works, and isatty says yes, which is
what a human sitting in front of it expects. It is wrong for emulating
a non-interactive shell invocation, where three PTY properties show up
as divergence from running the same command directly (all three measured
in R739-F4 against cargo check):
isatty(1)is true, so tools colorize — plainerror: …arrives as\x1b[1m\x1b[91merror\x1b[0m: …, which also defeats| rg "^error".- The line discipline’s
ONLCRrewrites every\nthe child wrote into\r\n. - The terminal merges stderr into stdout, so stream separation is gone by the time anything reads the capture.
In pipe mode the child gets pipe(2) for stdout and stderr, chunks are
stored under their true Stream, and TERM is left alone rather than
forced to xterm-256color. TaskDriver::resize_run and
TaskDriver::foreground_pid have no PTY to answer for and report
NotFound / None.
pipefail: boolR901-B2 — run the sh -c line with pipefail, so a pipeline reports
the leftmost failing stage instead of its last one. Defaults to
false, i.e. POSIX behaviour, which is what every existing caller has.
Without it a pipeline’s status is the last stage’s and nothing else:
cargo check 2>&1 | tail -40 exits 0 on a build with 101 errors,
because tail succeeded. That is not a wrapper lying — the wrapper is
faithful, and the shell is answering the question it was actually
asked — but it is indistinguishable from a green build to everything
downstream, including the harness task notification an agent reads to
decide whether it is done. On 2026-09-13 two sessions read that 0 as a
pass and left cargo check -p yah red camp-wide for ~50 minutes.
Only meaningful when SpawnOpts::argv is None; an explicit argv is
not a shell line and has no pipeline to take a status from.
§Known cost, accepted deliberately
pipefail also surfaces a producer killed by SIGPIPE, so
cargo check 2>&1 | head -40 can now report failure once head closes
the pipe early on a build that was fine. That is a false RED, and it is
the right trade against the false GREEN above: a red is investigated,
a green ends the turn. Prefer | tail over | head on a build line.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for SpawnOpts
impl RefUnwindSafe for SpawnOpts
impl Send for SpawnOpts
impl Sync for SpawnOpts
impl Unpin for SpawnOpts
impl UnsafeUnpin for SpawnOpts
impl UnwindSafe for SpawnOpts
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<T> ErasedDestructor for Twhere
T: 'static,
impl<T> Fruit for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more