Engine Overview
Broski has two task modes:
- Graph mode: staged, cached, deterministic, output-validated.
- Interactive mode: direct workspace execution with inherited TTY.
Determinism Contract
Graph tasks must declare inputs and outputs. The runner fingerprints relevant execution state, including:
- command
- resolved variables and parameters
- explicit environment keys
- input file content hashes
- mode/isolation metadata
What is validated before execution
- dependency graph must be resolvable (cycles rejected via topological sort)
- task names cannot collide with reserved CLI command names
- required tools (
@requires) are checked once per unique tool name across the entire resolved graph, before any task in the run starts - graph-mode tasks rely on declared contracts (
@in,@out) for deterministic reuse
Sandbox internals (@isolation strict)
Strict isolation is implemented via bwrap and is Linux-only. The
sandbox: unshares the network namespace, read-only binds /usr, /bin, /lib (and /lib64 if present), gives
an empty writable /etc plus a read-only-bound /etc/resolv.conf, tmpfs-masks $HOME, mounts /proc, /dev,
and a fresh /tmp, and binds only the task's own stage directory read-write. --force-isolation requires
bwrap on PATH and fails the run up front if it's missing; it's also rejected for interactive tasks, since
those run directly in the workspace by design. best_effort and off never invoke bwrap — they differ only
in environment handling: best_effort (like strict) clears the environment to a minimal safe set before
applying @env/@secret_env; off inherits the full parent environment unchanged.
Streaming execution: ProgressEvent, cancellation, and history
Introduced in v0.7.0, three engine-level APIs sit underneath the CLI, TUI, and any future integration:
ProgressEvent— a structured stream (RunStarted,TaskQueued,TaskStarted,TaskPhase,LogLine,TaskFinished,RunFinished) exposed viaRunOptions::event_sink: Option<Sender<ProgressEvent>>. Opt-in only — the classic spinner-based CLI output is byte-identical when no sink is provided.capture_output: booladditionally enables line-by-line stdout/stderr streaming asLogLineevents. This is what the TUI dashboard is built on, and it's a stable integration point for CI dashboards or IDE plugins that want structured execution data instead of parsing terminal output.CancellationToken— cooperative, two-level cancellation.CancelLevel::Softflips an atomic flag; the executor stops starting new tasks but lets in-flight ones finish.CancelLevel::Hardadditionally sendsSIGTERMto every registered child PID (Unix only). A task whose process exits non-zero because it was hard-cancelled is treated as a graceful stop, not a failure. This backs the TUI's two-stage Ctrl-C.- Versioned execution records —
ExecutionRecordgainedduration_ms, andArtifactStoregainedfetch_history(task, limit)andstats()(object count + total bytes). These backbroski historyand the TUI's cache/session stats cards.
Interactive-task concurrency
Non-interactive (graph-mode) runs take a workspace-level lock (.broski/runtime/active.lock) for the duration
of the run, so two overlapping broski run invocations against the same workspace don't race. Runs whose
entire resolved graph is interactive-only skip this lock entirely — this is deliberate, so you can run two
different interactive tasks (e.g. a dev server in one terminal, a REPL in another) against the same workspace
at once without one blocking the other.
Operational trade-offs
- interactive mode gives terminal-first UX, but bypasses graph caching entirely — no cache read, no cache write
- graph mode gives deterministic reuse, but requires explicit contracts
- strict isolation may require host capabilities not available in every local environment (Linux +
bwrap)
Use:
- Cache Explainability for rerun diagnostics
- TUI Dashboard for the live event-stream-backed dashboard
- Troubleshooting for common failure modes