Hive Hive
Sign in

fix(server): apply shard min/max defaults when caller passes nil

GitHub issue · Closed

Metadata
Source
tuist/tuist #10941
Updated
Jun 24, 2026
Domains
Compute
Details

Summary

--shard-max N (without --shard-min or --shard-max-duration) silently fell back to one shard per test module instead of capping at N. Users with hundreds of modules got hundreds of shards. --shard-total N was unaffected because it short-circuits before touching min/max.

Root cause

In Tuist.Shards.BinPacker.determine_shard_count/2:

min_shards = Keyword.get(opts, :min, @default_min_shards)
max_shards = Keyword.get(opts, :max, @default_max_shards)

Keyword.get/3 (and Map.get/3) only applies the default when the key is missing, not when its value is nil.

The shards controller (lib/tuist_web/controllers/api/shards_controller.ex) builds the params map with Map.get(body_params, :shard_min) etc., so any CLI flag the user did not pass arrives as a present-with-nil key. By the time Shards.create_shard_plan/2 calls the bin-packer, both min: and max: may be nil, and the documented defaults never apply.

The fall-through true -> branch then evaluates:

max_shards |> max(min_shards) |> min(unit_count)
# 2 |> max(nil) |> min(144)

In Elixir’s term ordering, atoms sort greater than numbers, so max(2, nil) == nil and min(nil, 144) == 144. The user’s --shard-max 2 request quietly produced 144 shards.

Fix

Replace Keyword.get(opts, :min, @default_min_shards) with Keyword.get(opts, :min) || @default_min_shards (same for :max) so nil and missing collapse to the same default. This makes the function match its own documentation and removes the footgun for any caller that forwards optional fields through a map.

Considered fixing only at the controller boundary (filter out nil values, or use || there), but the algorithmic API itself is the more durable place to be tolerant — there are several call sites and any future ones would re-hit the same trap.

Validation

  • New regression test in test/tuist/shards/bin_packer_test.exs exercises the exact failure mode:
    • min: nil, max: 2 over 144 units → 2 shards (was 144)
    • min: nil, max: nil over 144 units → 10 shards (the documented default)
  • Existing BinPacker test suite still passes (19 tests, 0 failures).

Test plan

  • mix test test/tuist/shards/bin_packer_test.exs passes
  • Manually verify tuist test --shard-max 2 (no other shard flags) produces a 2-shard plan

🤖 Generated with Claude Code

Flights

Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.

New Flights are paused Configure model inference, GitHub, and a sandbox provider to start another Flight. Existing results remain available below.
No Flights yet

Start a Flight and preserve its objective, outcome, and session here.

Comments

No GitHub comments yet.