Skip to content

refactor(hm-pipeline-ir): timeout_seconds uses Option<u32> where 0 is a meaningless / footgun value#125

Merged
markovejnovic merged 2 commits into
mainfrom
cq/hm-pipeline-ir-timeout-seconds-uses-option-u32-where-0--21
Jun 10, 2026
Merged

refactor(hm-pipeline-ir): timeout_seconds uses Option<u32> where 0 is a meaningless / footgun value#125
markovejnovic merged 2 commits into
mainfrom
cq/hm-pipeline-ir-timeout-seconds-uses-option-u32-where-0--21

Conversation

@markovejnovic

Copy link
Copy Markdown
Contributor

The smell

CommandStep::timeout_seconds (graph.rs:32) and PipelineGraph::timeout_seconds (graph.rs:92) were both Option<u32>. A literal 0 is fully representable but semantically nonsense — a 0-second budget means "kill the step/build immediately". Consumers got no guarantee the value was positive; scheduler.rs:447 had to hand-roll a Some(secs) if secs > 0 runtime guard, and nothing caught an absurd 0 budget at the wire boundary.

The change

Both fields are now Option<NonZeroU32>. serde deserializes NonZeroU32 directly and rejects 0 at parse time, so Some(0) is unrepresentable. The PipelineGraph::timeout_seconds() accessor now returns Option<NonZeroU32>, positive by construction — no runtime > 0 guard needed downstream.

Added a rejects_zero_pipeline_timeout_seconds test confirming "timeout_seconds": 0 is rejected at deserialization. The JsonSchema snapshot updated to reflect the new invariant (minimum: 1.0 instead of 0.0) — note this minimum tightening is the schema correctly advertising the >= 1 constraint to authors.

Type-safety pattern

This applies the "NonZero for counts that must never be zero" pattern from fd / rust-analyzer: push the >= 1 invariant into the type so divide/timeout-arming code is panic-free by construction, and an absurd 0-second budget is caught at the wire boundary rather than deep in the scheduler.

CI / review notes

  • Only cargo check -p hm-pipeline-ir (plus that crate's tests) was run locally. Full CI runs on this PR.
  • This changes the public API of hm-pipeline-ir. Downstream hm-exec reads input.step.timeout_seconds directly (scheduler.rs:421) and applies a secs > 0 guard (scheduler.rs:447); those call sites will need a follow-up adjustment (.get() / drop the now-redundant guard) which CI on the consuming crates will surface. Marked draft for that reason — needs review of the downstream rollout.

@markovejnovic markovejnovic marked this pull request as ready for review June 10, 2026 20:11
@markovejnovic markovejnovic merged commit 301c20b into main Jun 10, 2026
17 checks passed
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