Pure critical-path estimates for Delegate launch admission.
Subagent estimates use ceil(task_count / max_threads) scheduling waves
multiplied by the resolved uniform child budget. Workflow estimates sum the
largest effective step budget in each planned dependency wave. Omitted step
budgets use the runtime Subagent default, while both omitted and explicit budgets
are capped by the normalized workflow timeout for the current estimate and
wave_budgets_ms. suggested_timeout_ms is the least sufficient fixed point after
that cap moves: it sums each wave's uncapped declared/default maximum, and can
therefore exceed the current estimate only when the current workflow timeout binds.
The estimate details carry the resolved caller horizon, whether that wait horizon was
explicitly supplied rather than defaulted from the delegate timeout, the normalized
declared workflow timeout, and whether that workflow timeout was explicitly declared
before default injection, separately from their effective minimum. Rejection recovery
chooses the wait-horizon knob when it was explicit and the delegate-timeout knob when
it was derived. An explicit workflow timeout is combined with that caller knob only
when it also binds. An omitted workflow timeout instead remains a delegate-derived
ceiling: with an explicit wait horizon, recovery independently widens the wait and/or
delegate timeout that binds; with a derived wait horizon, widening the delegate timeout
alone moves both values. Maps from older callers that lack binding-source evidence
retain the prior classification. Relaunching once at the suggestion is sufficient.
Separately from admission, capped_step_budgets/4 names every explicitly declared step
budget an explicitly declared workflow timeout caps, so the dry-run can predict the
runtime closed_by_workflow_timeout cancellation instead of leaving it implicit in the
gap between estimated_critical_path_ms and suggested_timeout_ms. That warning is
purely advisory: it never moves would_reject, an admission verdict, or an exit code.
Workflow wave maxima deliberately form a conservative batch-admission sum; a
work-conserving scheduler can realize less wall time by starting newly unblocked work
before a whole batch finishes.
Admission still fails closed on that sum. Retries, provider jitter, and orchestration
overhead are intentionally excluded.
Summary
Functions
Human-readable capped-step-budget lines, one per offending step.
Build the additive, machine-readable capped-step-budget warning, or nil when empty.
Name every explicitly declared step budget the declared workflow timeout caps.
Estimate uniform-budget Subagent scheduling waves.
Estimate a Workflow using the runtime default for omitted step budgets.
Estimate a Workflow; current arithmetic is capped, while suggestion is its fixed point.
Return the four stable horizon values shared by advisory, rejection, and override.
Build the canonical fail-closed horizon error from an enriched estimate.
Types
@type estimate() :: %{ :estimated_critical_path_ms => pos_integer(), :waves => pos_integer(), :suggested_timeout_ms => pos_integer(), optional(:json_pointer) => String.t(), optional(:path) => [String.t() | non_neg_integer()], optional(:step_index) => non_neg_integer(), optional(:per_wave_budget_ms) => pos_integer(), optional(:wave_budgets_ms) => [pos_integer()] }
Functions
Human-readable capped-step-budget lines, one per offending step.
Build the additive, machine-readable capped-step-budget warning, or nil when empty.
The warning is advisory only: it never changes would_reject, the admission verdict,
or the exit code of an accepted plan. Its recovery guidance covers both directions
(raise the workflow timeout, or lower the offending step budgets) and reuses the exact
runtime remedy token the workflow-timeout fold publishes so dry-run and fold vocabulary
stay greppable together.
@spec capped_step_budgets([map()], pos_integer() | nil, boolean(), [String.t()]) :: [ map() ]
Name every explicitly declared step budget the declared workflow timeout caps.
Returns [] unless the workflow timeout was explicitly declared in the spec: a
defaulted/derived workflow timeout is a delegate-derived ceiling already covered by
the horizon guard, not an operator-declared conflict. Steps that omitted timeout_ms
declare no conflicting intent and are skipped even though the Subagent default they
fall back to may exceed the workflow timeout. Steps whose declared budget is less than
or equal to the declared workflow timeout are not capped.
steps_path is the 0-based location prefix of the step list as the caller submitted
it, so machine callers can patch the pointer they actually sent: ["steps"] for a
flat spec and ["workflow", "steps"] for the nested shell form, which the runner
flattens before estimation.
@spec estimate_subagents(pos_integer(), pos_integer(), pos_integer()) :: {:ok, estimate()} | {:error, map()}
Estimate uniform-budget Subagent scheduling waves.
@spec estimate_workflow([map()], [[non_neg_integer()]], pos_integer()) :: {:ok, estimate()} | {:error, map()}
Estimate a Workflow using the runtime default for omitted step budgets.
@spec estimate_workflow([map()], [[non_neg_integer()]], pos_integer(), pos_integer()) :: {:ok, estimate()} | {:error, map()}
Estimate a Workflow; current arithmetic is capped, while suggestion is its fixed point.
Return the four stable horizon values shared by advisory, rejection, and override.
Build the canonical fail-closed horizon error from an enriched estimate.