Skip to main content

haz_exec/
lib.rs

1//! Async task scheduler and process executor for `haz`.
2//!
3//! The crate currently exposes the following public modules:
4//!
5//! - [`process`]: the [`process::ProcessSpawner`] /
6//!   [`process::Process`] trait pair, the
7//!   [`std_impl::StdProcessSpawner`] production backend over
8//!   [`tokio::process::Command`], and the value types every
9//!   implementation produces. A scriptable mock implementation lives
10//!   alongside but is gated to test builds only and is not part of
11//!   the crate's public surface.
12//! - [`cache_key`]: the [`cache_key::build_cache_key`] derivation,
13//!   gluing the workspace state, the validated dependency graph,
14//!   the host-env snapshot, and the per-predecessor stream hashes
15//!   into a [`haz_cache::CacheKey`].
16//! - [`run_task`]: the single-task lifecycle ([`run_task::run_task`],
17//!   [`run_task::cache_lookup_phase`], [`run_task::restore_from_hit`],
18//!   [`run_task::run_fresh`]) and the [`run_task::RunObserver`]
19//!   port. The [`run_task::RunOutcome`] sum type carries
20//!   [`run_task::CompletedRecord`] (a task that reached an
21//!   `EXEC-009` classification through the lookup-then-spawn
22//!   pipeline), [`run_task::SkipRecord`] (a task the cascade
23//!   marked do-not-schedule per `EXEC-010` / `EXEC-011`), and
24//!   [`run_task::CancelledRecord`] (a task the cancellation
25//!   flow caught per `EXEC-012..015`, in three structural
26//!   shapes: signalled in flight, drained from the ready set,
27//!   or cascade-cancelled from an upstream cancellation).
28//! - [`run_graph`]: the workspace-wide [`run_graph::run_graph`]
29//!   scheduler. It walks a validated [`haz_dag::graph::TaskGraph`]
30//!   in canonical order, admits ready tasks subject to the
31//!   global and per-tag concurrency caps (`EXEC-004` /
32//!   `EXEC-005`), evaluates mutex compatibility post-lookup
33//!   against a live hold set (`EXEC-006` condition 3 /
34//!   `EXEC-007` / `MUTEX-001..007`), threads predecessor stream
35//!   hashes into every downstream [`run_task::run_task`] call,
36//!   and cascade-skips hard descendants of a failed task while
37//!   letting unrelated subgraphs continue (`EXEC-010`). Per
38//!   `EXEC-011` the scheduler emits a
39//!   [`run_task::RunOutcome::Skipped`] for every cascade-skipped
40//!   descendant and fires
41//!   [`run_task::RunObserver::on_task_skipped`] at cascade time.
42//!
43//! Cancellation (`EXEC-009` cancelled state,
44//! `EXEC-012..015`) is wired end-to-end: the spawn-step future
45//! observes [`run_task::RunContext::cancel`] and runs the
46//! per-future SIGTERM / grace / SIGKILL dance against the child's
47//! process group on Unix; the scheduler polls the same token,
48//! stops admitting new tasks, drains [`run_task::RunOutcome`]s
49//! into the `RunCancelled` shape for every task still in the
50//! ready set, and reclassifies late-arriving lookup-step results
51//! as `RunCancelled`. Cancellation cascades along hard edges via
52//! the same `complete_failed` machinery as the failure cascade,
53//! emitting [`run_task::CancelledRecord::UpstreamCancelled`]
54//! per descendant.
55//!
56//! Output presentation (`EXEC-016` / `EXEC-017`) is delivered as
57//! two distinct [`run_task::RunObserver`] implementations under
58//! the [`output`] module:
59//! [`output::LiveOutputObserver`] tag-prefixes each emitted line
60//! with `[project:task] ` and writes lines atomically under a
61//! single per-observer mutex, so multiple in-flight tasks
62//! interleave at line granularity only;
63//! [`output::BufferedOutputObserver`] accumulates each task's
64//! bytes and writes them as two contiguous blocks (`stdout` then
65//! `stderr`) on task completion. Cache-hit emission travels the
66//! same observer methods as a fresh run, satisfying `EXEC-017`
67//! structurally.
68//!
69//! Runtime DAG validation (`EXEC-019`, `EXEC-020`) is wired:
70//! [`run_task::CompletedRecord`] carries each succeeded task's
71//! [`run_task::CompletedRecord::materialised_outputs`] (sourced
72//! from the output-resolution pass for fresh runs and
73//! [`haz_cache::Manifest::outputs`] for cache hits); the
74//! [`run_graph::run_graph`] scheduler maintains a per-run claim
75//! map for `EXEC-020` and a kind-erased augmented edge set for
76//! `EXEC-019`. Detected violations are appended to
77//! [`run_graph::RunGraphOutcome::invariant_violations`] as
78//! [`run_graph::RuntimeInvariantViolation`] entries
79//! ([`run_graph::RuntimeInvariantViolation::OutputOverlap`] and
80//! [`run_graph::RuntimeInvariantViolation::RuntimeCycle`]
81//! variants). `EXEC-019` additionally trips an
82//! internal child token (a child of [`run_task::RunContext::cancel`])
83//! so in-flight non-cycle tasks receive SIGTERM via the
84//! `EXEC-014` grace-and-escalate flow; the user-supplied parent
85//! token stays
86//! uncancelled. Pending cycle members in the ready set surface
87//! as [`run_task::RunOutcome::Skipped`] with
88//! [`run_task::SkipCause::RuntimeCycle`]. `EXEC-020` only stops
89//! admission and lets in-flight tasks complete naturally per the
90//! spec's silence on cancellation for output overlaps.
91//!
92//! Exit-code mapping (`EXEC-021`) is delivered by the
93//! [`exit_code`] module: [`exit_code::exit_code_for`] takes a
94//! finished [`run_graph::RunGraphOutcome`] plus an optional
95//! [`exit_code::CancellationSignal`] (recorded by the binary's
96//! OS signal handler) and returns the numeric exit status the
97//! `haz run` process MUST report. The helper computes the
98//! number; the binary that consumes haz-exec performs the
99//! actual `process::exit` call (per the workspace-wide "pure
100//! library" decision: signal handlers and stdio writes live at
101//! the binary boundary, not inside any haz-exec module).
102
103#![deny(missing_docs)]
104
105pub mod cache_key;
106pub mod exit_code;
107pub(crate) mod hold_set;
108#[cfg(any(test, feature = "test-util"))]
109pub mod mock_impl;
110pub mod output;
111pub(crate) mod pattern_walk;
112pub mod presenter;
113pub mod process;
114pub mod run_graph;
115pub mod run_task;
116pub mod std_impl;