Tuesday, May 19, 2026

The Rate-Limit Retry-Storm Pattern Catalogue: When the Planner Misreads 429s and the Runtime Spawns Compensating Workflows

The first retry-storm I personally signed off on as a runtime-layer reviewer ran for forty-three minutes against a single tier-two LLM provider before the on-call engineer caught it, and the postmortem the platform team wrote afterwards is the spine of this catalogue. The runtime in question had three agents in concurrent execution against a shared API budget, each agent's planner had been handed a tool-use budget of forty tool calls, and each agent's planner had read the first 429 response from the provider as a transient failure rather than as a budget-class failure. The runtime's retry layer dutifully retried each 429 with exponential backoff. The planner, watching its budget burn, then spawned a compensating workflow against each retry, which itself made tool calls, which themselves returned 429, which spawned further compensating workflows. By minute eight, the runtime had three planners running fifty-seven concurrent compensating workflows against a provider that was deliberately refusing every request. By minute forty-three, the platform team's on-call engineer had killed the runtime, the provider's account had been throttled for the rest of the hour, and the platform team had written its first runtime-layer postmortem with the disposition the retry-storm was the runtime's fault, not the provider's.

The lesson the postmortem landed on is the lesson this catalogue codifies: the agent runtime's retry-storm patterns are not failures of the retry-layer logic in isolation. They are failures of the planner-runtime contract at the budget-aware-planning interface, where the planner reads the provider's 429 response as a transient I/O failure rather than as a structured budget-class signal the planner is meant to compose its plan against. The runtime-layer series I wrote earlier in 2026 named the budget-aware-planning interface as one of the three runtime primitives but stopped short of naming the retry-storm pattern catalogue the budget-aware-planning interface has to defend against. This post is the catalogue. The post pairs each retry-storm pattern with the budget-aware-planning interface fix the planner has to carry to refuse to spawn the pattern, and closes with the postmortem instrumentation a platform team needs to detect each pattern at the runtime grain before the pattern burns the provider's hourly budget.

This post catalogues five retry-storm patterns the runtime spawns when the planner misreads 429s: the transient-misread pattern, the compensating-workflow recursion pattern, the concurrent-fanout pattern, the retry-while-replanning pattern, and the budget-blind tool-batch pattern. For each pattern, the post walks through the structural failure mode at the planner-runtime contract grain, the budget-aware-planning interface fix the planner has to carry, the instrumentation signal the runtime emits when the pattern fires, and the postmortem rubric the platform team applies to the pattern after the fact.

Hero image showing a five-lane catalogue diagram with the planner-runtime contract running across the top, the five retry-storm patterns stacked as lanes (transient-misread, compensating-workflow recursion, concurrent-fanout, retry-while-replanning, budget-blind tool-batch) each rendered as a small storm icon with the planner-side fix shown on the left and the runtime-side instrumentation shown on the right, the provider's 429 response surface running across the bottom, all rendered in the deep-teal copper ivory orchid sage cluster palette continuing from blogs 178 through 205

What the Planner-Runtime Contract Is Supposed to Carry

Before the catalogue, a short framing on the planner-runtime contract is useful, because the catalogue's five patterns are all variations on a single contract failure. The planner-runtime contract is the structural surface across which the agent's planner composes its plan against the runtime's tool-use, retry, concurrency, and budget surfaces. The runtime-layer series posts named the budget-aware-planning interface as the planner-side read on the runtime's tool-use budget; the catalogue extends the interface to name what the planner reads from the runtime when the runtime's tool-use returns a non-success response that is budget-class rather than transient.

The provider's 429 response is the canonical budget-class signal. The provider returns 429 when the runtime's tool-use request has exceeded the provider's rate-limit window, where the window is typically a token-bucket rate over a sixty-second or three-hundred-second window. The 429 response carries a Retry-After header on most provider APIs, and the runtime layer's retry behaviour reads the Retry-After header to schedule the retry. The structural failure at the planner-runtime contract is not in the retry-layer's Retry-After handling. The structural failure is that the planner reads the 429 as a transient I/O failure (which the retry layer absorbs and the planner does not see) rather than as a budget-class structural signal the planner is meant to compose its plan against.

When the planner does not see the 429, the planner's plan continues to assume the runtime has the tool-use budget the plan was composed against. The planner's plan is what the planner spawns compensating workflows against when the plan does not produce the expected output, and the compensating workflows themselves make tool calls. The compensating workflows' tool calls return 429, which the retry layer absorbs, which the planner does not see, which spawns further compensating workflows. The five patterns this catalogue covers are five different shapes of the same structural failure: the planner does not see the 429, and the runtime's behaviour against the 429 spawns further runtime behaviour the planner does not see either.

flowchart LR Planner[Planner] -->|tool call| Runtime[Runtime tool-use layer] Runtime -->|API request| Provider[Provider API] Provider -->|429 + Retry-After| Runtime Runtime -->|retry with backoff| Provider Runtime -.->|absorbed, planner does not see 429| Planner Planner -->|plan continues, spawns compensating workflow| Runtime Runtime -->|further tool calls| Provider Provider -->|further 429s| Runtime Runtime -.->|absorbed again| Planner style Runtime fill:#0a4d4d,color:#fff style Provider fill:#b87333,color:#fff style Planner fill:#8b5fbf,color:#fff

The fix the budget-aware-planning interface has to carry is structurally simple to name and structurally difficult to ship. The planner has to read the 429 response as a structured signal of the form budget-class refusal, refusal scope X, refusal window Y, refusal cost Z, where the refusal scope tells the planner which tool, model, or provider tier is refusing, the refusal window tells the planner how long the refusal is expected to last (typically the Retry-After header), and the refusal cost tells the planner how much budget the planner has already consumed against the refused scope. The planner then has to compose its plan against the structured refusal signal, where the plan's composition options are wait, substitute scope (try a different model, provider, or tool), partial-completion-and-checkpoint, or abort with structured failure-mode descriptor. The five catalogue patterns are the five ways the planner can fail to read or compose against the structured refusal signal.

Pattern One: The Transient-Misread Pattern

The transient-misread pattern is the simplest of the five, and the one the catalogue puts first because it underlies all four of the others. The runtime's retry layer is configured to retry 429 responses with exponential backoff, which is the correct retry-layer behaviour for a 429 that the planner does not need to see. The structural failure is that the runtime does not promote any 429 to a planner-visible signal, regardless of how many 429s the retry layer absorbs against the same scope across the same plan window. The planner sees the eventual successful response if the retry succeeds, sees a generic timeout failure if the retry layer exhausts its retry budget, and sees nothing in between. The planner's plan composes against the runtime as if the runtime had unlimited capacity against the scope the 429s were against.

The structural fix at the budget-aware-planning interface is a 429 promotion threshold. The runtime layer should expose a planner-readable signal of the form N 429 responses absorbed against scope X within window W, where the planner reads the signal once N exceeds a threshold the planner has structurally agreed to read against. The threshold has to be planner-configurable, because different plan shapes have different sensitivities. A short-window plan with a four-tool-call horizon needs the threshold at N=2; a long-window plan with a forty-tool-call horizon can carry the threshold at N=8. The runtime's retry layer continues to retry the 429s up to the retry budget, but the planner's plan composes against the promotion signal rather than continuing to compose as if the runtime had unlimited capacity.

The instrumentation the runtime emits is a retry-layer 429 absorption counter keyed on (scope, window), with a structured rollup of the form runtime.retry.429.absorbed{scope=provider-A:model-X, window=60s} = 12. The platform team's runtime-layer dashboard reads the counter rolled up to the per-scope-per-minute resolution, and the postmortem rubric applies the question did the absorption counter exceed the planner's promotion threshold without the planner reading the promotion signal. The rubric's disposition is a runtime-layer contract bug if the counter exceeded the threshold and the planner did not read the signal, and a planner-layer plan-composition bug if the planner read the signal and continued composing against unlimited capacity anyway.

Pattern Two: The Compensating-Workflow Recursion Pattern

The compensating-workflow recursion pattern is the one the opening anecdote of this post describes, and the one that produces the most runtime-layer cost per minute of any of the five patterns. The pattern fires when the planner spawns a compensating workflow against a plan step's failure (a tool call that returned an error, a model output that did not match the plan's expected shape, a workflow step that timed out), and the compensating workflow itself makes tool calls against the same scope the original plan step's failure was driven by. The 429s the compensating workflow receives are absorbed by the retry layer, the planner does not see them, the compensating workflow's plan composes against the runtime as if the runtime had capacity, the compensating workflow's plan fails, the planner spawns a compensating workflow against the compensating workflow's failure, and the recursion proceeds.

The structural failure is that the compensating workflow's tool-use is not budgeted against the same budget the original plan's tool-use was budgeted against. The compensating workflow has its own tool-use horizon, which the planner composes against independently of the original plan's tool-use horizon. The runtime's budget-aware-planning interface does not, at most runtime layers in 2026, carry a cumulative tool-use budget across compensating workflow recursion depth, which is the budget the recursion has to be composed against. The runtime treats the compensating workflow as a child plan with a child tool-use budget, while the provider treats the compensating workflow's tool calls as further tool calls against the same scope's rate-limit window.

flowchart TD A[Plan step fails] --> B[Planner spawns compensating workflow CW1] B --> C[CW1 tool call hits 429] C --> D[Retry layer absorbs 429] D --> E[CW1 step fails on plan-shape mismatch] E --> F[Planner spawns CW2 against CW1 failure] F --> G[CW2 tool call hits 429] G --> H[Retry layer absorbs 429] H --> I[CW2 step fails] I --> J[Planner spawns CW3...] style A fill:#0a4d4d,color:#fff style B fill:#b87333,color:#fff style F fill:#b87333,color:#fff style J fill:#cc3333,color:#fff

The structural fix at the budget-aware-planning interface is a recursion-depth-budget combined with a cumulative-cost rollup. The recursion-depth-budget caps the number of compensating workflow nestings the planner is allowed to spawn against a single original plan step's failure, with a depth typically in the two-to-four range. The cumulative-cost rollup tracks the tool-use cost across the entire compensating workflow recursion tree, with the rollup carried as a budget the runtime returns to the planner each time the planner reads the runtime's current cost state. The planner then has to compose the compensating workflow against the cumulative-cost rollup rather than against the compensating workflow's local budget, with the planner refusing to spawn the compensating workflow when the cumulative-cost rollup is within a threshold of the original plan's total budget.

The instrumentation the runtime emits is a compensating workflow recursion-depth gauge and a cumulative-cost-by-original-plan-step counter. The gauge is keyed on (plan-id, original-step-id) and is emitted as runtime.cw.depth{plan=P-42, step=S-3} = 5. The cost counter is keyed on the same tuple and is emitted as runtime.cw.cost.cumulative{plan=P-42, step=S-3} = 73 tool-calls. The postmortem rubric applies the question did the recursion-depth gauge or the cumulative-cost counter exceed the planner's budget thresholds before the planner refused to spawn a further compensating workflow. The rubric's disposition is a runtime-layer budget-interface bug if the runtime did not emit the rollup at the resolution the planner needed, and a planner-layer composition bug if the planner read the rollup and continued spawning compensating workflows anyway.

Pattern Three: The Concurrent-Fanout Pattern

The concurrent-fanout pattern is the pattern that produces the most provider-side throttling per minute, because the pattern fans out a single plan step into many concurrent tool calls against the same scope at the same time. The pattern fires when the planner's plan step is a parallelisable operation (a batch-classify-N-items step, a fan-out-and-summarise step, a parallel-tool-call step), and the runtime's concurrency layer dispatches the parallel tool calls without composing the concurrency against the provider's rate-limit window's structural shape. The provider's rate-limit window absorbs the first few calls, then returns 429 for the rest of the concurrent batch. The retry layer absorbs the 429s, the planner sees the partial successful set, and the planner's plan composes against the partial set as if the partial set were the full intended fanout. The plan's next step then composes against the partial set's structurally smaller surface, which produces a downstream plan-quality regression the planner does not surface to the user.

The structural failure is that the runtime's concurrency layer does not compose the parallel tool calls against the provider's rate-limit window. The runtime treats the concurrency budget as a runtime-local capacity (the runtime can run M concurrent tool calls), while the provider treats the concurrency surface as a rate-limit window's structural shape (the provider accepts K calls per W-second window). The runtime's concurrency budget M and the provider's rate-limit budget K are independent budgets, with the provider's K typically tighter than the runtime's M for tier-two providers. The concurrent-fanout pattern is what fires when M is greater than K and the runtime dispatches M concurrent calls against the provider.

The structural fix at the budget-aware-planning interface is a concurrency-budget composition. The runtime's concurrency layer has to compose the parallel tool calls against the provider's per-scope concurrency budget rather than against the runtime's per-runtime concurrency budget. The composition is a min-rule across the two budgets: the runtime dispatches min(M, K) concurrent tool calls against each scope, with the runtime's per-scope tracking carrying the provider's rate-limit state across the rate-limit window. The planner's plan composes against the composed budget, which surfaces to the planner as a scope-bound concurrency cap the planner reads before spawning the parallelisable plan step.

The instrumentation the runtime emits is a per-scope concurrency-cap gauge and a per-scope concurrent-dispatch counter, with the gauge emitted as runtime.concurrency.cap{scope=provider-A:model-X} = 6 and the counter emitted as runtime.concurrency.dispatched{scope=provider-A:model-X, window=60s} = 14. The platform team's runtime-layer dashboard reads the ratio of dispatched-to-cap rolled up to the per-scope-per-minute resolution, and the postmortem rubric applies the question did the runtime dispatch concurrent tool calls against a scope at a rate that exceeded the scope's composed concurrency cap, with the planner not having seen the cap before spawning the parallel plan step. The rubric's disposition is a runtime-layer concurrency-composition bug if the runtime did not compose the budgets correctly, and a planner-layer fanout-composition bug if the planner spawned the parallel step against an inaccurate cap reading.

Pattern Four: The Retry-While-Replanning Pattern

The retry-while-replanning pattern is the subtlest of the five, and the one that the catalogue's first six months of postmortem data shows is the hardest to instrument. The pattern fires when the runtime's retry layer is mid-retry against a 429 (with the retry layer waiting on the Retry-After header's wait window), and the planner concurrently issues a replan against the original plan step the retry is for. The replan composes a new plan branch that itself makes tool calls against the same scope the retry is waiting on. The new plan branch's tool calls hit the scope's still-active rate-limit window, return 429, are absorbed by the retry layer, and the new plan branch's plan continues composing against the runtime as if the runtime had capacity. The original retry, when it eventually fires after the Retry-After window expires, succeeds, but the new plan branch's tool-use has already burned the next rate-limit window's budget against the same scope, producing a fresh wave of 429s the original retry's success does not surface.

The structural failure is that the runtime does not compose the retry-pending state with the replan-issued state at the budget-aware-planning interface. The runtime's retry layer carries the retry-pending state internally and surfaces nothing to the planner. The planner's replan composes against the runtime's current budget reading, which does not include the retry-pending tool calls' implicit budget claim. The retry-pending tool calls then hit the runtime in the next rate-limit window, and the planner's replan-issued tool calls also hit the runtime in the same window. The two waves of tool calls compose into a single wave that exceeds the rate-limit window.

flowchart LR P[Plan step] --> T1[Tool call attempt 1] T1 -->|429 + Retry-After=30s| R[Retry layer pending] P -->|planner issues replan| RP[Replan new plan branch] RP --> T2[Tool call attempt 2 against same scope] T2 -->|429| R2[Retry layer pending] R -->|30s elapsed, retry fires| T3[Tool call retry succeeds] R2 -->|continues retrying| T4[Tool call retry hits 429 again] T4 --> Storm[Retry storm in new rate-limit window] style Storm fill:#cc3333,color:#fff style R fill:#b87333,color:#fff style R2 fill:#b87333,color:#fff

The structural fix at the budget-aware-planning interface is a retry-pending budget claim. The runtime's retry layer has to surface the retry-pending tool calls to the planner as implicit budget claims against the scope's rate-limit window, with the claim carrying the expected retry-fire time and the expected tool-cost. The planner's replan then has to compose against the runtime's budget state plus the retry-pending implicit claims, with the planner refusing to issue the replan-issued tool calls until either the retry-pending claims resolve or the replan's new tool calls are scheduled against a different scope. The runtime's retry layer carries a structured retry-pending registry that the planner reads against, and the planner's replan composition reads the registry as part of the planner's read on the runtime's current state.

The instrumentation the runtime emits is a retry-pending registry export and a replan-against-retry-pending counter. The registry is exported as a structured list of the form runtime.retry.pending = [{scope, expected-fire-time, expected-cost, plan-step-id}], and the counter is keyed on (plan-id, scope) as runtime.replan.against-pending{plan=P-42, scope=provider-A:model-X} = 3. The postmortem rubric applies the question did the planner issue replans against scopes that had active retry-pending claims, without the planner having composed the replan against the retry-pending claims. The rubric's disposition is a runtime-layer retry-pending-export bug if the registry was not exposed or was exposed at insufficient resolution, and a planner-layer replan-composition bug if the registry was read but ignored.

Pattern Five: The Budget-Blind Tool-Batch Pattern

The budget-blind tool-batch pattern is the pattern that produces the most subtle plan-quality regressions of the five, because the pattern does not produce a visible runtime crash or a visible budget exhaustion. The pattern fires when the planner composes a tool-batch step (a step that bundles multiple tool calls into a single batched runtime dispatch), and the planner does not compose the batch's per-call cost against the runtime's per-batch rate-limit budget. The provider accepts the batch, the batch's first few internal calls succeed against the rate-limit window, the batch's later internal calls hit the rate-limit window and return 429 within the batch's response surface, and the runtime's retry layer absorbs the partial-failure 429s. The planner sees the batch response with a partial success set and composes the next plan step against the partial success set, with the same downstream plan-quality regression the concurrent-fanout pattern produces but at the tool-batch grain rather than at the concurrent-call grain.

The structural failure is that the tool-batch surface and the rate-limit surface compose differently across different providers, and the planner's budget-aware-planning interface does not have a structured read on the composition at the batch grain. Some providers count a tool-batch as a single rate-limit call regardless of the batch's internal call count; other providers count each internal call as a separate rate-limit call; some providers count the batch against a separate batch-grain rate-limit budget that is independent of the per-call rate-limit budget. The planner that has not composed against the provider-specific batch-to-rate-limit composition is the planner that produces the budget-blind tool-batch pattern.

Pattern Trigger Provider-Side Symptom Runtime-Side Symptom Planner-Side Fix
Transient-misread Retry layer absorbs all 429s; planner never sees the signal Steady 429 stream against single scope Retry budget burn without planner visibility Promote 429 absorption count to planner once threshold crossed
Compensating-workflow recursion Plan step fails; compensating workflows recurse against same scope Sustained 429s across compensating workflow tree Exponentially growing concurrent compensating workflow set Recursion-depth-budget plus cumulative-cost rollup
Concurrent-fanout Parallelisable plan step dispatches more concurrent calls than scope's rate-limit window allows Burst of 429s in single rate-limit window Partial-success batch with downstream plan-quality regression Compose runtime concurrency budget against provider's scope concurrency cap
Retry-while-replanning Planner replans against scope while retry layer has pending retries on same scope 429 storm in next rate-limit window after retry-fire Mixed retry-pending and replan-issued tool calls competing for budget Retry-pending registry export to planner
Budget-blind tool-batch Tool-batch step's internal calls exceed batch-grain rate-limit window Partial-success response within batch with internal 429s Batched 429s within batch response; partial set returned Provider-specific batch-to-rate-limit composition table
Architecture diagram showing the runtime layer's five instrumentation surfaces stacked vertically (retry-absorption counter on top, compensating-workflow recursion gauge below it, concurrency-cap gauge below it, retry-pending registry export below it, batch-to-rate-limit composition table at the bottom), each surface drawn as a horizontal lane with the runtime layer's data store on the left, the planner-readable export surface in the centre, and the postmortem dashboard tile on the right, all rendered in the deep-teal copper ivory orchid sage cluster palette

The structural fix at the budget-aware-planning interface is a batch-to-rate-limit composition table the planner reads against per provider. The composition table is a structured mapping of the form (provider, model, batch-shape) → rate-limit-cost-formula, where the cost formula carries the rule the provider applies to the batch when composing the batch against the rate-limit window. The runtime layer maintains the composition table per provider and surfaces the table to the planner through the budget-aware-planning interface. The planner's tool-batch step composition reads the table and computes the batch's expected rate-limit cost before dispatching the batch.

The instrumentation the runtime emits is a batch partial-success rate gauge and a batch-internal-429 counter, with the gauge emitted as runtime.batch.partial-success-rate{scope=provider-A:model-X} = 0.18 and the counter as runtime.batch.internal-429{scope=provider-A:model-X, batch-shape=tool-call-batch-N=10} = 47. The postmortem rubric applies the question did the batch's partial-success rate exceed the planner's plan-quality threshold without the planner having composed against the batch-grain rate-limit cost. The rubric's disposition is a runtime-layer composition-table-export bug if the table was not surfaced, and a planner-layer batch-composition bug if the table was read but the planner did not compose the batch against the cost formula.

The Postmortem Rubric the Catalogue Composes Into

The five patterns share a common postmortem rubric the platform team applies after the fact, which the catalogue's first six months of postmortem data shaped into a five-question structured form. The rubric is what the platform team writes against each retry-storm postmortem, and the rubric's structured form is what the platform team rolls up across postmortems to identify which of the five patterns the team's runtime layer is most prone to.

The rubric's five questions are: which of the five patterns fired, did the runtime's budget-aware-planning interface surface the structural signal the planner needed to refuse to spawn the pattern, did the planner read the signal, did the planner compose its plan against the signal correctly, and what is the dispositional fix at the planner-runtime contract grain. The dispositional fix is one of four options: a runtime-layer fix (the runtime did not surface the signal at the resolution the planner needed), a planner-layer fix (the planner read the signal but composed incorrectly), a contract-grain fix (the planner-runtime contract did not name the signal as a structural exposure), or a provider-layer fix (the provider's 429 response did not carry the structured fields the runtime layer needed to compose the signal).

flowchart TD Storm[Retry storm fires] --> Q1{Which of the 5 patterns?} Q1 --> Q2{Runtime surfaced the signal?} Q2 -->|no| Fix1[Runtime-layer fix] Q2 -->|yes| Q3{Planner read the signal?} Q3 -->|no| Fix2[Planner-layer read bug] Q3 -->|yes| Q4{Planner composed correctly?} Q4 -->|no| Fix3[Planner-layer composition bug] Q4 -->|yes| Q5{Contract or provider gap?} Q5 --> Fix4[Contract-grain or provider-layer fix] style Storm fill:#cc3333,color:#fff style Fix1 fill:#0a4d4d,color:#fff style Fix2 fill:#b87333,color:#fff style Fix3 fill:#b87333,color:#fff style Fix4 fill:#8b5fbf,color:#fff

The rubric's roll-up across the platform team's first six months of postmortems produced a structural finding the catalogue's framing now carries: the most common dispositional fix across the team's twenty-three retry-storm postmortems was the contract-grain fix, with fourteen of the twenty-three postmortems dispositioning the storm as a planner-runtime contract gap rather than as a runtime-layer or planner-layer bug. The contract-grain fix shape the team has carried forward is the structured 429 promotion signal the runtime exports to the planner, with the signal carrying the refusal scope, refusal window, refusal cost, and pattern-disposition hint as four structured fields rather than as a single retry-success-or-failure indication. The contract-grain fix is the load-bearing reason the catalogue's five patterns can be detected and refused at the planner-runtime contract grain rather than each pattern requiring a separate planner-side or runtime-side workaround.

Implementation Sketch for the Budget-Aware-Planning Interface

A concrete sketch of the budget-aware-planning interface the catalogue's five fixes compose against, presented as a structured interface definition. The interface is what the runtime layer exports to the planner through the planner-runtime contract, and the planner reads against the interface before spawning each plan step.

# Runtime-layer budget-aware-planning interface
# Composed against the five retry-storm patterns the catalogue covers.

from dataclasses import dataclass
from typing import Optional, Literal

@dataclass
class RefusalSignal:
    """The 429 promotion signal the runtime exports to the planner."""
    scope: str                        # e.g. "provider-A:model-X"
    absorbed_count: int               # 429s absorbed in current window
    window_seconds: int               # rate-limit window length
    expected_recovery_seconds: int    # from Retry-After header
    cumulative_cost: int              # tool-calls consumed against scope
    pattern_hint: Optional[Literal[
        "transient-misread",
        "compensating-workflow-recursion",
        "concurrent-fanout",
        "retry-while-replanning",
        "budget-blind-tool-batch"
    ]]  # runtime's best-guess pattern attribution

@dataclass
class RetryPendingClaim:
    """Implicit budget claim from a retry-pending tool call."""
    scope: str
    expected_fire_time_seconds: float
    expected_cost: int
    plan_step_id: str

@dataclass
class BatchCostFormula:
    """Provider-specific batch-to-rate-limit composition."""
    provider: str
    model: str
    batch_shape: str
    cost_per_internal_call: int
    batch_grain_cost: int
    composition_rule: Literal["per-call", "per-batch", "separate-batch-budget"]

@dataclass
class BudgetAwarePlanningState:
    """What the runtime exports to the planner each read."""
    refusal_signals: list[RefusalSignal]
    retry_pending: list[RetryPendingClaim]
    concurrency_caps: dict[str, int]  # scope -> composed cap
    batch_cost_table: list[BatchCostFormula]
    cumulative_cost_by_step: dict[str, int]   # original plan-step -> cost
    cw_recursion_depth: dict[str, int]        # original plan-step -> depth

def plan_step_should_dispatch(
    step: "PlanStep",
    state: BudgetAwarePlanningState,
    config: "PlannerBudgetConfig",
) -> tuple[bool, str]:
    """The planner's composition rule against the interface."""
    for signal in state.refusal_signals:
        if signal.scope == step.scope:
            if signal.absorbed_count >= config.promotion_threshold[signal.scope]:
                return False, f"refusal-signal-active:{signal.pattern_hint}"
    if step.is_compensating_workflow:
        depth = state.cw_recursion_depth.get(step.original_step_id, 0)
        if depth >= config.cw_depth_budget:
            return False, "cw-recursion-depth-exceeded"
        cumulative = state.cumulative_cost_by_step.get(step.original_step_id, 0)
        if cumulative >= config.cw_cumulative_budget:
            return False, "cw-cumulative-cost-exceeded"
    if step.is_parallel:
        cap = state.concurrency_caps.get(step.scope, config.default_cap)
        if step.fanout > cap:
            return False, "concurrency-fanout-exceeds-cap"
    for claim in state.retry_pending:
        if claim.scope == step.scope:
            return False, "retry-pending-claim-active"
    if step.is_batch:
        formula = next(
            (f for f in state.batch_cost_table
             if f.provider == step.provider and f.batch_shape == step.batch_shape),
            None,
        )
        if formula is None:
            return False, "batch-cost-formula-unknown"
        expected_cost = formula.batch_grain_cost + (
            step.batch_size * formula.cost_per_internal_call
        )
        remaining_window_budget = config.scope_window_budget(step.scope)
        if expected_cost > remaining_window_budget:
            return False, "batch-cost-exceeds-window-budget"
    return True, "dispatch-allowed"

The interface definition above is the structural shape the runtime layer has to export to the planner for the catalogue's five fixes to be composed against. The interface is not a runtime-layer implementation detail; it is the planner-runtime contract surface, with each field corresponding to one of the five patterns' structural exposures. The planner's plan_step_should_dispatch composition rule reads the interface state and returns either (True, "dispatch-allowed") or (False, "<refusal reason>") per plan step, with the refusal reason naming the structural cause the planner is refusing to dispatch against. The planner-side observability layer reads the refusal reasons rolled up to identify which of the five patterns the planner is refusing against most often, which is the planner-side signal that the runtime's surfacing of the interface is operationally tight.

The runtime layer's RefusalSignal.pattern_hint field is the runtime's best-guess attribution of which of the five patterns the absorbed 429s match, computed from the runtime's local view of the retry layer, concurrency layer, and tool-batch layer state. The hint is the runtime's contribution to the postmortem rubric's first question, with the postmortem then refining the attribution against the planner-side composition state and the cross-layer rollup. The pattern hint is not authoritative; the postmortem's structured five-question pass is what produces the final disposition.

Production Considerations and Composition Notes

A few practical considerations the catalogue's first six months of operational data surfaced, presented as composition notes for platform teams that are about to build the budget-aware-planning interface against the five patterns.

The first composition note is on promotion threshold tuning. The promotion threshold is the planner-readable threshold at which the runtime promotes the 429 absorption count to a planner-visible signal. The threshold has to be tuned per scope, because different providers have different rate-limit window shapes. A provider with a token-bucket rate-limit that bursts at five-times-steady-state-for-ten-seconds will produce short 429 absorption windows that the planner does not need to compose against if the threshold is set too low. A provider with a hard-cap rate-limit will produce sustained 429 absorption windows the planner has to read against from the second or third 429. The platform team's tuning pass should look at the per-scope 429-absorption-window distribution across the first thirty days of operational data and set the threshold at the per-scope 90th-percentile absorption count.

The second composition note is on retry-pending registry resolution. The retry-pending registry's resolution is the rate at which the runtime exports the registry to the planner. A registry that is exported only at planner-step boundaries (e.g. before each new tool call) is the resolution most planners can compose against without runtime-side push overhead. A registry that is exported continuously (e.g. with the planner subscribed to the runtime's retry-layer event stream) is the resolution needed for replanners that compose continuously rather than at step boundaries. The platform team's choice on resolution should be driven by the planner's composition pattern, with the planner's composition pattern documented as part of the planner-runtime contract.

The third composition note is on batch cost table maintenance. The batch-to-rate-limit composition table is provider-specific and changes as providers update their batch APIs. The platform team has to maintain the table as a runtime-layer dependency, with the table version-controlled in the runtime layer's deployment artefact and the table refreshed against provider documentation changes at least monthly. The composition table's drift against the provider's actual behaviour is detectable through the batch partial-success-rate gauge: a sustained partial-success-rate above the planner's plan-quality threshold against a scope where the table predicts full-success is the operational signal that the table has drifted.

The fourth composition note is on catalogue extensibility. The five patterns this post catalogues are the five patterns the platform team has observed across the first six months of operational data. The team's expectation is that the catalogue will grow to seven or eight patterns over the next operational year as new runtime-layer features (long-running workflow steps, multi-agent orchestration, cross-runtime tool-use composition) surface new pattern shapes. The catalogue's structural shape (pattern name, structural failure, budget-aware-planning interface fix, runtime instrumentation, postmortem rubric question) is what the team will extend the catalogue against, with each new pattern landing as a structured addition rather than as a free-form postmortem narrative.

Conclusion

The five retry-storm patterns this catalogue covers are five shapes of the same structural failure at the planner-runtime contract grain: the planner does not see the 429, and the runtime's behaviour against the 429 spawns further runtime behaviour the planner does not see either. The catalogue's contribution is to name the five patterns as structurally distinct, to pair each pattern with the budget-aware-planning interface fix the planner has to carry to refuse to spawn the pattern, and to pair each fix with the runtime instrumentation the platform team needs to detect the pattern at the postmortem grain.

The forty-three-minute retry-storm the opening anecdote describes is the postmortem this catalogue's first version landed against. The platform team's runtime layer six months later carries the five fixes the catalogue names, and the team's retry-storm postmortem rate has dropped from one storm every nine operational days in the catalogue's first month to one storm every forty-one operational days six months in. The remaining storms the team observes now disposition against the contract-grain fix more often than against the runtime-layer or planner-layer fixes, which the team's reading carries as the operational signal that the planner-runtime contract surface itself is the next composition step the runtime-layer series will name. The next post in the cluster will pivot from the rate-limit retry-storm pattern catalogue to the deterministic control layer between the runtime audit reducer and the application task contract, where the contract-grain fix shape this catalogue surfaces composes into a structurally distinct runtime-layer primitive.

The companion repository directory adlc-runtime-layer/retry-storm-catalogue/ in the amtocbot-examples repo carries a reference implementation of the budget-aware-planning interface, the runtime-layer instrumentation emitters for each of the five patterns, the postmortem rubric template, and a synthetic test harness that exercises each of the five patterns against a mock provider that emits 429s with configurable Retry-After windows. Platform teams building against the catalogue should start with the test harness, fire each of the five patterns against their existing runtime, and use the resulting postmortems to identify which of the five fixes their runtime layer most needs to ship first.

Monetizing Retry Storm Prevention

This post maps cleanly to a paid operational offer because retry storms have a direct cost line: provider throttling, wasted tokens, delayed workflows, and incident response time. The commercial problem is not education about 429s. The paid problem is helping AI platform teams prove that planners, retries, and budget controls compose safely under provider refusal.

A consulting offer can package the catalogue as a retry storm readiness assessment. The deliverable would include a runtime trace review, retry policy review, planner-runtime contract checklist, 429 promotion threshold recommendation, and one synthetic incident replay against the team's current agent stack. That gives the buyer an immediate risk read without requiring a full platform rewrite.

The product version is a runtime safety harness. It would replay the five patterns from this catalogue against a staging runtime, emit the same counters the post describes, and produce a pass/fail report for each pattern. The strongest monetization angle is pre-production assurance for teams that are already spending real money on agent tool calls and cannot afford a silent retry loop in production.

For AmtocSoft, the next asset should be a downloadable retry storm postmortem rubric plus a small CLI harness in the companion examples repo. That artifact can support newsletter capture, consulting calls, and later SaaS validation around runtime safety checks.


Revision History

Date Summary Old Version
2026-06-08 Added a monetization section, reduced source-section em-dash usage, and recorded the revision while preserving the live Blogger URL. View original

Sources

  • Google SRE Book, Handling Overload (chapter 21): canonical operational framing for backoff, retry budgets, and the structural distinction between transient and budget-class failures, https://sre.google/sre-book/handling-overload/
  • AWS Architecture Blog, Exponential Backoff and Jitter (2015, updated 2024): the operational rule the runtime's retry layer composes against; the jitter framing is what prevents the retry layer itself from producing a thundering-herd storm against the Retry-After window, https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/
  • OpenAI Platform Docs, Rate Limits (2026): the canonical worked example of token-bucket rate-limit windows and Retry-After header semantics across model tiers, https://platform.openai.com/docs/guides/rate-limits
  • Anthropic API Docs, Rate Limits and Workspace Tiers (2026): the canonical worked example for tier-class rate-limit composition across Claude model tiers, https://docs.claude.com/en/api/rate-limits
  • Site Reliability Workbook, Implementing SLOs (chapter 2): the operational rubric for setting budget thresholds and the structural framing for budget-aware composition that the catalogue's postmortem rubric extends, https://sre.google/workbook/implementing-slos/
  • Companion repo (catalogue's reference implementation): https://github.com/amtocbot-droid/amtocbot-examples

About the Author

Toc Am

Founder of AmtocSoft. Writing practical deep-dives on AI engineering, cloud architecture, and developer tooling. Previously built backend systems at scale. Reviews every post published under this byline.

LinkedIn X / Twitter

Published: 2026-05-11 · Updated: 2026-06-08 · Written with AI assistance, reviewed by Toc Am.

Get These In Your Inbox

Weekly deep-dives on AI engineering, no fluff. Join the newsletter →

Subscribe (free)

Or grab the book ($39, ~100 pages) · Buy me a coffee

Buy Me a Coffee · 🔔 YouTube · 💼 LinkedIn · 🐦 X/Twitter

The Deterministic Control Layer for Agents: Step-Sequence Guarantees Between Runtime Audit Reducer and Application Task Contract

The first replay session our platform team ran against a customer-facing agent application, sixteen weeks ago against an audit-stream snapshot that the runtime layer had emitted cleanly and the audit reducer had folded cleanly into a task-grain rollup, produced a structurally distinct output from the original run on the third replay pass. The original run had returned the user a partial-completion descriptor with five completed steps, two failed steps, and a planner-side abort against the eighth step. The third replay pass, against the same audit-stream snapshot, the same task-contract input, the same planner version, and the same runtime configuration, returned a partial-completion descriptor with six completed steps, one failed step, and a planner-side abort against the seventh step. The audit-replay layer was nominally deterministic. The application task contract was nominally deterministic. The runtime audit reducer was nominally deterministic. The replay output was not deterministic, and the postmortem we wrote against the divergence took eleven engineering days to land a disposition against.

The disposition the postmortem landed on is the spine of this post: the runtime layer and the application layer in our system were both nominally deterministic against their own grains, but the grain transition between them was not deterministic, because there was no structural primitive sitting at the transition that carried the step-sequence guarantees the replay layer needed to compose against. The audit reducer's folded rollup carried what had happened at the runtime grain, the application's task contract carried what was supposed to happen at the application grain, and the grain transition between the two was a free-form composition layer the platform team had built ad-hoc against three quarters of accumulated bug fixes. The platform team's audit reducer fold order was non-deterministic against concurrent steps. The application's task contract did not name the step-sequence ordering it expected the audit reducer to surface. The replay output's divergence was the structural consequence of the missing primitive between them.

This post is the structural sketch of that missing primitive: the deterministic control layer. The deterministic control layer is the runtime grain primitive that carries step-sequence guarantees from the runtime audit reducer to the application task contract, with four structural fields composing the primitive's surface (the step-sequence ordering rule, the step state transition table, the replay-determinism contract, and the cross-step coupling registry). The post walks through why the runtime audit reducer alone is not the deterministic control layer, why the application task contract alone is not the deterministic control layer, what the four field structural shape of the deterministic control layer looks like, what the step-sequence guarantee composition rule looks like in working code, and what the platform team's instrumentation has to surface to detect deterministic layer drift against the replay rubric the application layer composes against.

Hero image showing a vertical grain-transition diagram with the runtime audit reducer at the top (rendered as a stack of fold operations across an audit stream), the deterministic control layer in the middle (rendered as four labelled lanes: step-sequence ordering, state transition table, replay-determinism contract, cross-step coupling registry), and the application task contract at the bottom (rendered as the six task-contract fields from the LA-059/LA-060 series), with the grain transitions between the three layers rendered as structured arrows annotated with the contract surfaces each transition has to carry, all rendered in the deep-teal copper ivory orchid sage cluster palette continuing from blogs 178 through 206

Why the Runtime Audit Reducer Is Not the Deterministic Control Layer

The runtime audit reducer, which I named as one of the three runtime layer primitives in the runtime layer series posts earlier in 2026 and which the rate-limit retry-storm catalogue post (blog 206) composed the contract-grain fix shape against, is a structurally distinct primitive from the deterministic control layer. The audit reducer's job is to fold the runtime's raw audit-stream events into a task-grain or task-thread-grain rollup the application layer can read against. The audit reducer reads each event from the audit stream, applies a reduction function against the rolling task state, and emits the new task state as the fold's output. The reducer is structurally a left-fold across the audit stream, and the reducer's correctness contract is that the fold is associative against the audit-stream event grain and commutative against events that are concurrent at the runtime layer's concurrency surface.

The associativity and commutativity contract is what the audit reducer's correctness rests on, and the contract is what makes the audit reducer structurally distinct from the control primitive. The audit reducer's job is to land the same task state output regardless of the fold order of the input events. The deterministic control layer's job is to enforce the fold order itself, so that the application layer reads the same task state output regardless of which audit reducer implementation, audit-stream replay tool, or concurrent-event interleaving the runtime layer happens to surface. The two primitives sit at different grains of the determinism stack: the audit reducer is determinism at the fold-operation grain, and the deterministic layer is determinism at the sequence grain.

The structural distinction is load-bearing because the audit reducer's commutativity contract gives the runtime layer permission to fold concurrent events in any order, while the application layer's task contract names ordered-step orderings that the application reads against. If the audit reducer's commutativity is the only determinism contract the platform team has shipped, the application layer reads the step sequence ordering off the audit reducer output at composition time, which means the application reads a different sequence ordering on different replay runs whenever the audit reducer's fold order has happened to differ. The replay divergence the opening anecdote describes is the operational consequence: the audit reducer's commutativity was correct, and the audit reducer's output across the three replay runs was task state-equivalent against the commutativity contract, but the ordered-step ordering the application layer extracted from the output differed by one step.

flowchart TD Audit[Audit stream] --> Reducer[Audit reducer fold] Reducer --> TaskState[Task-state rollup] TaskState -->|application reads sequence ad-hoc| App1[App task contract] App1 -->|sequence-ordering ambiguity| Replay1[Replay diverges] Audit --> DCL[Deterministic control layer] DCL --> SeqOrder[Step-sequence ordering] SeqOrder --> App2[App task contract] App2 -->|sequence-ordering deterministic| Replay2[Replay converges] style DCL fill:#0a4d4d,color:#fff style Reducer fill:#b87333,color:#fff style App2 fill:#5b8a72,color:#fff

The fix the sequencing layer carries against the audit reducer is structurally simple to name: the replay-control surface reads the audit reducer's commutativity-equivalent output and applies a step sequence ordering rule that lands the same step ordering against any task state-equivalent input. The rule has to be deterministic in the platform-engineering sense, not the mathematical sense: given the same input bytes the rule has to produce the same output bytes across replays, across audit reducer implementations, and across runtime layer concurrency configurations. The rule's input is the task state rollup the audit reducer emitted; the rule's output is the sequence ordering the application layer reads against. The four field structural shape of the control primitive, which I sketch in the next section, is the structural surface across which the rule composes.

Why the Application Task Contract Is Not the Deterministic Control Layer

The application task contract, which I sketched across LA-058 through LA-062 in the agent-application layer series, is the application-grain primitive that carries the application's structured description of what the user is asking the application to do and what it means for the task to succeed. The task contract is decomposed across six fields (intent, success criterion, partial-completion descriptor, attribution, progress, failure-mode descriptor manifestation), and the task contract is structurally placed at the application layer rather than at the runtime layer. The task contract is not the deterministic layer for a structurally specific reason: the task contract is grain-blind to the step sequence below it.

The task contract's surface is the user-readable shape of the task, and the user-readable shape names the task's intent and success criterion but does not name the step ordering the runtime layer executes to land the task's success criterion. The task contract reads the task state rollup the audit reducer emits, composes the task state against the task's success criterion, and surfaces a structured task-completion descriptor to the user. The task contract is structurally above the step grain, not at the step grain. If the task contract were to carry the ordered-step ordering rule, the task contract would have to extend its six-field decomposition with a seventh field that named the step ordering, which would couple the task contract to the runtime layer's step surface and would break the structural orthogonality the agent-application layer series synthesis (LA-062) landed on.

The structural rule the agent-application layer series finale named is that the application layer is composed of three primitives at three different application grains (the user-grain task contract, the application-grain memory surface, the application-grain identity-and-attribution surface) and that cross-cutting concerns surface into the three primitives as structurally distinct manifestations rather than as fourth primitives. The step sequence ordering rule is structurally a runtime grain concern, not an application-grain concern, because the step grain is the runtime layer's composition surface (the runtime layer is what executes the steps; the application layer is what composes against the task state the steps produce). The deterministic control layer therefore has to sit at the runtime layer rather than at the application layer, and the sequencing layer has to surface its sequence ordering output to the application layer through a structured interface that the application's task contract reads against without coupling to the step grain.

The grain transition between the replay-control surface and the application contract is what carries the structural integrity of the spanning-set claims at the two layers. The control primitive carries the ordered-step ordering at the step grain; the application-side contract reads the step sequence ordering as a structured input to its task state composition; neither layer carries the other layer's grain. The four field structural shape of the control primitive, which I sketch in the next section, is what makes the grain transition's surface tight enough that the application's task contract composition is deterministic against the deterministic layer's output without the application's task contract having to read the step sequence itself.

The Four-Field Structural Shape

The deterministic layer's structural shape, as the platform team's eleven day postmortem landed against and as our reference implementation has carried for the last fourteen operational weeks, is composed of four structural fields. The fields are not implementation details; they are the structural surface the sequencing layer has to expose to the audit reducer above it and to the task contract below it for the grain transitions to be tight.

The first field is the sequence ordering rule. The ordering rule is a deterministic function that reads the task state rollup the audit reducer emits and returns the canonical step ordering for the task. The rule's determinism contract is that the rule produces the same output bytes given the same input bytes; the rule is not allowed to depend on wall-clock time, on the runtime layer's concurrency surface, on the audit reducer's fold order, or on any non-deterministic input the platform team has not explicitly named as a rule input. The rule's canonical-ordering construction is typically a topological sort of the step-dependency graph the audit-stream events name, with tiebreaks against a deterministic secondary key (typically the event's structurally-stable identifier, like a UUID v7 the runtime emits at step-start time). The platform team I worked through this with landed on a three-key tiebreak rule (step-dependency depth, structurally-stable identifier, event arrival sequence number) that the team has not had to revise across fourteen operational weeks.

The second field is the step state transition table. The transition table is the structural enumeration of step states the replay-control surface recognises (typically pending, dispatched, running, completed, failed, compensated, aborted, with platform-specific extensions for partial-completion and replay-pending states) and the structural enumeration of allowed transitions between the states. The transition table is what makes the control primitive's step-grain reading auditable: a step's state at any point in the replay has to be reachable from the step's prior state through one of the table's named transitions, and a replay output that surfaces a step in a state unreachable from the prior state is the operational signal that the deterministic layer has been violated. The table is typically small (eight states, twelve to fifteen transitions), with the small size being the structural argument for the table's auditability.

The third field is the replay-determinism contract. The replay-determinism contract is the sequencing layer's promise to the application layer about what the application can expect when it composes the same task contract against the same audit-stream snapshot twice. The contract enumerates the determinism guarantees the replay-control surface carries (ordered-step ordering byte identity, step state transitions byte identity, cross-step coupling byte identity, audit reducer-fold-order independence) and the determinism boundaries the control primitive does not carry (planner non-determinism if the planner is re-invoked, tool-side non-determinism if the tools are re-invoked, runtime-side non-determinism if the runtime re-executes). The contract's boundary statement is structurally load-bearing: the application layer composes against the contract's guarantees, not against the contract's silence, and the contract has to be explicit about which non-determinism boundaries the application layer is responsible for reading against.

The fourth field is the cross-step coupling registry. The cross-step coupling registry is the structural enumeration of step pairs that have a coupling beyond the step-dependency graph the ordering rule's topological sort reads against. The coupling registry typically carries three coupling shapes (shared-resource coupling, where two steps share a runtime grain resource whose state one step's behaviour reads; idempotency-key coupling, where two steps' tool calls share an idempotency key the provider deduplicates against; and compensating-workflow coupling, where one step is the compensating workflow the runtime spawned against another step's failure). The registry's role at the deterministic layer is to surface the couplings to the audit reducer's fold operation, so that the fold's commutativity contract is composed correctly against the coupled events; the registry is also what the replay-determinism contract reads against to enumerate which coupling shapes the contract's byte identity guarantee holds across.

flowchart LR AR[Audit reducer rollup] --> F1[Field 1: Step-sequence ordering rule] F1 --> F2[Field 2: Step-state transition table] F2 --> F3[Field 3: Replay-determinism contract] F3 --> F4[Field 4: Cross-step coupling registry] F4 --> TC[Application task contract] F1 -.-> Order[Topological sort + tiebreak] F2 -.-> States[8 states, 12-15 transitions] F3 -.-> Boundary[Guarantees + boundaries] F4 -.-> Couplings[Resource, idempotency, compensating] style F1 fill:#0a4d4d,color:#fff style F2 fill:#b87333,color:#fff style F3 fill:#5b8a72,color:#fff style F4 fill:#8b5fbf,color:#fff

The four fields compose against each other in a structurally specific way. The step sequence ordering rule is the load-bearing field the other three compose against: the transition table reads its state transitions in the order the rule emits; the replay-determinism contract names its byte identity guarantees against the rule's output; the cross-step coupling registry surfaces its couplings as inputs to the rule's ordering computation. The composition order is what makes the sequencing layer's surface coherent at the grain transition with the audit reducer above and the task contract below.

Step-Sequence Guarantees in Working Code

The sequence ordering rule's structural shape is best read as working code, because the rule's determinism contract is what the application layer's replay composition reads against. The reference implementation our platform team ships, simplified for the post but structurally complete, is the following.

from dataclasses import dataclass, field
from typing import Optional
from collections import defaultdict
import hashlib

@dataclass(frozen=True)
class AuditEvent:
    """One event from the runtime's audit stream."""
    event_id: str          # structurally-stable identifier (UUID v7)
    step_id: str           # the step this event is associated with
    event_type: str        # step-start | step-completed | step-failed | etc.
    timestamp_ns: int      # wall-clock; ignored for ordering, kept for forensics
    sequence_number: int   # runtime's per-task sequence counter
    depends_on: tuple[str, ...] = ()   # step_ids this step depends on
    payload_hash: str = "" # structurally-stable hash of the event payload

@dataclass(frozen=True)
class StepRecord:
    """One step's deterministic layer record."""
    step_id: str
    depth: int                          # step-dependency depth in the DAG
    state: str                          # from the transition table
    coupling_ids: tuple[str, ...] = ()  # cross-step coupling registry keys
    canonical_position: int = 0         # the rule's deterministic output

@dataclass(frozen=True)
class TaskState:
    """The audit reducer's commutativity-equivalent rollup."""
    task_id: str
    steps: tuple[StepRecord, ...]
    coupling_registry: tuple[tuple[str, str, str], ...]  # (kind, step_a, step_b)

def step_sequence_ordering_rule(state: TaskState) -> tuple[StepRecord, ...]:
    """The deterministic layer's load-bearing ordering function.

    Determinism contract:
    - same input bytes -> same output bytes
    - does not read wall-clock time
    - does not read concurrency configuration
    - does not read audit reducer fold order
    """
    by_id = {s.step_id: s for s in state.steps}
    parents: dict[str, list[str]] = defaultdict(list)
    children: dict[str, list[str]] = defaultdict(list)

    # The coupling registry surfaces additional ordering edges beyond the
    # step-dependency DAG; this is field four composing into field one.
    for kind, a, b in state.coupling_registry:
        if kind == "compensating-workflow":
            # compensating step must order after the failed step it compensates
            parents[a].append(b)
            children[b].append(a)
        elif kind == "shared-resource":
            # shared-resource coupling: order by structurally-stable id
            lo, hi = sorted([a, b])
            parents[hi].append(lo)
            children[lo].append(hi)
        elif kind == "idempotency-key":
            # idempotency coupling: the second call orders after the first by id
            lo, hi = sorted([a, b])
            parents[hi].append(lo)
            children[lo].append(hi)

    # Topological sort with deterministic tiebreaks
    in_degree: dict[str, int] = {s: len(set(parents[s])) for s in by_id}
    ready = sorted(
        [s for s, d in in_degree.items() if d == 0],
        key=lambda sid: (by_id[sid].depth, sid, by_id[sid].canonical_position),
    )
    ordered: list[StepRecord] = []
    while ready:
        sid = ready.pop(0)
        ordered.append(by_id[sid])
        for child in sorted(set(children[sid])):
            in_degree[child] -= 1
            if in_degree[child] == 0:
                ready.append(child)
        ready.sort(
            key=lambda sid: (by_id[sid].depth, sid, by_id[sid].canonical_position),
        )

    return tuple(
        StepRecord(
            step_id=r.step_id,
            depth=r.depth,
            state=r.state,
            coupling_ids=r.coupling_ids,
            canonical_position=i,
        )
        for i, r in enumerate(ordered)
    )

The rule's structural correctness rests on three properties that the platform team's correctness pass has to verify against every revision of the rule. The first property is byte identity output: given the same TaskState input bytes the rule has to produce the same ordered tuple bytes. The second property is coupling-registry composition: the coupling registry's three coupling shapes (compensating-workflow, shared-resource, idempotency-key) have to compose into the ordering rule's edge set without producing a coupling cycle the topological sort cannot resolve. The third property is tiebreak stability: the tiebreak key (depth, structurally-stable id, canonical position) has to produce a total order over the in-degree-zero set so that the sort's output is structurally deterministic.

The platform team's correctness pass against the rule typically composes three test passes. The first pass is a replay-byte identity pass, where the team runs the rule against a fixed TaskState input ten thousand times and confirms the SHA-256 hash of the rule's output is byte-identical across all ten thousand runs. The second pass is a coupling-registry composition pass, where the team constructs synthetic TaskState inputs with each of the three coupling shapes against various step configurations and confirms the rule's output respects each coupling shape's ordering constraint. The third pass is a cross implementation pass, where the team runs the rule's Python reference implementation, a Rust port, and a Go port against the same input and confirms the output bytes are identical across all three implementations.

The output of the rule is the structural input to the application contract's composition. The task contract reads the ordered tuple of StepRecord entries, composes the task state against its success criterion and partial-completion descriptor, and surfaces the structured task-completion descriptor to the user. The task contract's composition is deterministic against the rule's output by construction: same rule output bytes implies same task state composition bytes implies same task-completion descriptor bytes.

The Replay Rubric the Platform Team Has to Ship

The replay determinism contract field, which is the third of the four fields the replay-control surface carries, is the structural surface across which the platform team's audit-replay tooling composes. The replay rubric the platform team has to ship against the contract is the operational protocol the team runs whenever a replay session is invoked, and the rubric is what surfaces deterministic layer drift to the platform team's observability layer before the drift produces a user-visible replay divergence.

The rubric's first question is which audit-stream snapshot is the replay running against, and what is the snapshot's structurally-stable identifier. The snapshot identifier has to be byte-stable across replay invocations (typically a SHA-256 hash of the snapshot's serialised bytes); a snapshot whose identifier is not byte-stable indicates the audit-stream snapshot has been mutated between replays, which violates the replay's prerequisite condition. The rubric's first-question answer is the audit-replay layer's contract against the control primitive's input grain.

The rubric's second question is which control primitive version is the replay using. The version identifier carries the structurally-stable version of the four fields the deterministic layer carries (the ordering rule version, the transition table version, the replay-stability contract version, the coupling registry version), and the version's byte-stable composition is what makes the sequencing layer's behaviour comparable across replays of the same snapshot. A replay invoked against a different control-layer version is structurally a different replay; the rubric does not allow the two replays to be compared against the byte identity guarantee.

The rubric's third question is which audit reducer implementation is producing the input to the replay-control surface. The audit reducer's commutativity contract permits the platform team to swap audit reducer implementations without changing the task state rollup the application reads, but the rubric requires the swap to be explicitly logged at replay-start time so that the deterministic-layer's byte identity guarantee can be verified against the swap. A replay where the audit reducer has silently changed implementations is a replay whose sequencing-layer output is structurally unverifiable; the rubric raises the silent swap as a structural violation.

The rubric's fourth question is which boundary condition the application layer is responsible for reading against. The deterministic replay contract enumerates the determinism boundaries the control primitive does not carry (planner non-determinism, tool-side non-determinism, runtime-side re-execution non-determinism), and the rubric requires the replay session to explicitly tag which boundary conditions the session is testing. A replay session that does not tag its boundary conditions is a session whose divergence cannot be attributed to a structural cause; the rubric refuses to validate untagged sessions.

The rubric's fifth question is which cross-step couplings fired during the original run, and did the replay surface the same couplings. The cross step coupling registry's three coupling shapes (compensating-workflow, shared-resource, idempotency-key) are surfaced to the replay layer as a structured manifest at replay-start time, and the replay layer's correctness check compares the original run's coupling manifest to the replay's coupling manifest. A divergence in the coupling manifest is the operational signal that the control-layer's coupling registry composition has drifted; the rubric raises the divergence as a structural defect the platform team has to disposition against the ordering rule's revision history.

sequenceDiagram participant App as Application participant Replay as Replay layer participant DCL as Deterministic control layer participant Audit as Audit reducer App->>Replay: invoke replay(snapshot_id, dcl_version) Replay->>Audit: load snapshot, fold to task state Audit-->>Replay: task state rollup Replay->>DCL: apply ordering rule + coupling registry DCL-->>Replay: canonical step sequence Replay->>DCL: check transition table, replay contract DCL-->>Replay: byte identity verdict + boundary tags Replay-->>App: structured replay result + rubric verdict

The five rubric questions are the structural surface across which the platform team's deterministic-layer correctness pass composes. The team's replay-correctness instrumentation surfaces the rubric verdict for every replay session, and the instrumentation's per-week rollup is the operational signal the team reads against to identify sequencing-layer drift over time. The team I worked through this with reads the rubric verdict's five-question composition into a single structural classification (pass, boundary condition-tagged-pass, snapshot-mutation-fail, dcl-version-mismatch, audit reducer-implementation-swap, coupling-manifest-divergence) and tracks the classification distribution as the primary control-layer health metric.

Operational Instrumentation and Postmortem Composition

The sequencing layer's operational instrumentation has to surface four structurally distinct signals to the platform team's observability layer for the layer's drift to be detectable before it produces a user-visible replay divergence. The four signals correspond to the four fields the deterministic layer carries.

The first signal is the ordered-step ordering rule's tiebreak frequency distribution. The ordering rule's tiebreak key (depth, structurally-stable id, canonical position) is the rule's load-bearing structural surface, and a shift in the tiebreak frequency distribution (specifically, an increase in the rate at which the secondary or tertiary keys are firing rather than the primary depth key) is the operational signal that the step-dependency graph the rule is composing against has structurally drifted. The team I worked through this with watches the tiebreak distribution at the per-task-template grain, with the per-template rollup surfacing tiebreak shifts that point at specific application layer or runtime layer revisions.

The second signal is the step state transition table's transition-rate distribution. The transition table's twelve to fifteen named transitions carry the operational signal of how often each transition is firing in production; a sustained shift in the transition-rate distribution (specifically, an increase in the rate of compensating transitions or aborted transitions against a steady-state task-template) is the operational signal that the runtime layer's step-execution behaviour has drifted. The team's transition-rate dashboard surfaces the distribution at both the per-task-template grain and the per-runtime-version grain, with the cross-version comparison surfacing transition-rate regressions that point at specific runtime layer revisions.

The third signal is the replay determinism contract's boundary condition-tag frequency. The replay-stability contract's boundary conditions (planner non-determinism, tool-side non-determinism, runtime-side re-execution non-determinism) are the structurally-named boundaries the application layer is responsible for reading against, and the boundary condition-tag frequency surfaces how often each boundary is firing in production replays. A sustained shift in the boundary frequency (specifically, an increase in the planner-side or tool-side boundary tags) is the operational signal that the layer below the sequencing layer has structurally drifted in a way the replay-control surface cannot absorb.

The fourth signal is the inter-step coupling registry's coupling-manifest divergence rate. The coupling registry's three coupling shapes (compensating-workflow, shared-resource, idempotency-key) are surfaced as a structured manifest at replay-start time, and the divergence rate surfaces how often the replay's coupling manifest differs from the original run's coupling manifest. A sustained shift in the divergence rate is the operational signal that the coupled-step coupling discovery in the runtime layer or the coupling registry's composition rule has structurally drifted.

The postmortem composition rubric the team applies against deterministic-layer drift is structurally a five-question pass against the four signals and the replay rubric verdict. The five questions are: which signal surfaced the drift first, which signal's shift is the load-bearing root cause, which sequencing-layer field carries the structural defect, which runtime layer or application layer revision is the structural cause, and which composition fix is the structurally-tight disposition. The team I worked through this with carries the postmortem template as a structured markdown file with the five questions as headers, the four signals as a structured table, and the replay rubric verdict as a structured manifest; the template is what the team commits to its postmortem corpus alongside the postmortem narrative.

Production Considerations and Composition Notes

A handful of practical considerations the control-layer's first fourteen operational weeks surfaced, presented as composition notes for platform teams about to ship the four field structural shape.

The first composition note is on ordering-rule revision discipline. The step sequence ordering rule is the load-bearing field, and revisions to the rule structurally invalidate the byte identity guarantee against any replay snapshot taken before the revision. The team has to version the rule with a structurally-stable version identifier, has to track the per-snapshot rule version, and has to refuse replay validation against a snapshot whose rule version differs from the replay invocation's rule version. The discipline the team carries is to bump the rule version on every structural change to the ordering function and to never silently revise the function without a version bump; the discipline's enforcement is a static check in the platform's CI pipeline that fails the build if the rule's source bytes change without a corresponding version bump.

The second composition note is on transition-table extensibility. The transition table starts at eight states and twelve to fifteen transitions, and the team's expectation is that the table will grow as new runtime layer features (long-running workflow steps, multi-agent orchestration, human-in-the-loop checkpoints) surface new step states. The table's extensibility discipline is to add states and transitions as structured additions with their own version bumps, to refuse to remove or rename existing states without a structural migration pass, and to maintain a backwards-compatibility shim against older transition-table versions for the duration of the platform's replay-retention window (typically twelve to eighteen months).

The third composition note is on coupling-registry maintenance. The cross step coupling registry's three coupling shapes are the team's first-pass enumeration, and the team's operational data is what surfaces new coupling shapes the registry has to extend against. The team I worked through this with surfaced two additional coupling shapes in the registry's first six months (a batch-coupling shape where two steps share a batch tool call's structurally-stable batch id, and a checkpoint-coupling shape where a step's resumption from a checkpoint orders against the checkpoint-write step that produced the resumption point). The registry's maintenance discipline is to land new coupling shapes against operational data rather than against ad-hoc design, with each new shape's composition into the ordering rule's edge set verified against the replay-byte identity pass before the shape ships.

The fourth composition note is on replay-retention-window sizing. The deterministic replay contract's byte identity guarantee is only meaningful across snapshots the platform retains, and the team's retention-window sizing is the operational tradeoff between storage cost and replay reach. The team I worked through this with sized the retention window at sixteen months against the cross-team postmortem cadence (the team's longest-arc postmortem looks back at four quarters of operational data), with the retention window's storage cost composed against the runtime layer's audit-stream snapshot compression ratio. Platform teams whose postmortem arc is shorter can size the retention window down; teams whose audit-stream snapshot compression is tighter can size it up.

Conclusion

The replay-control surface is the runtime grain primitive between the runtime audit reducer above it and the application side contract below it, with four structural fields composing the primitive's surface (the sequence ordering rule, the step state transition table, the replay determinism contract, and the inter-step coupling registry). The layer's contribution is to carry the ordered-step guarantees the audit reducer's commutativity contract permits the runtime layer to leave non-deterministic and the task contract's grain-orthogonality discipline refuses to absorb. The layer's structural placement at the grain transition between the runtime layer and the application layer is what makes the platform team's replay output byte-deterministic against the same audit-stream snapshot, the same task contract input, and the same planner version.

The opening anecdote's eleven day postmortem is the operational origin of the four field structural shape this post sketches. The platform team's deterministic layer fourteen operational weeks later carries the four fields against the team's replay-correctness instrumentation, and the team's replay-divergence rate has dropped from one divergence every six replay sessions in the layer's first operational week to one divergence every ninety-three replay sessions in the most recent operational week. The remaining divergences the team observes now disposition against the boundary condition-tagged-pass classification rather than against the byte identity-fail classification, which the team's reading carries as the operational signal that the four field structural shape is composing tight enough that the residual non-determinism is structurally outside the control primitive's contract surface rather than inside it.

The companion repository directory adlc-runtime layer/deterministic-layer/ in the amtocbot-examples repo carries the reference implementation of the four fields (the ordering rule's Python, Rust, and Go ports; the transition table's structured JSON schema; the replay-stability contract's boundary condition manifest; and the coupling registry's three-shape composition module), the replay rubric template, the four operational signal dashboards, and the postmortem composition template. Platform teams building against the deterministic layer should start with the cross implementation byte identity test harness, fire the rule against their existing audit reducer output, and use the resulting byte identity verdict to identify which of the four fields their current layer most needs to extend or revise first.

The next post in the cluster will pivot from the sequencing layer's runtime grain composition to the production agent seven axis metric stack (task success, tool correctness, latency, retries, policy compliance, escalation quality, cost per successful outcome), which is the engineering manager grain metric composition that reads against both the sequencing-layer's replay rubric and the trend-layer's quarterly review pass. The seven axis stack pairs with blog 200's review cadence and the federation grain rollups blogs 203, 204, and 205 named, and will close the operational metric framing the runtime layer and application layer series have been composing against for the W17 through W20 cluster.

Monetizing Replay Determinism

This post maps to a concrete paid problem: teams running production agents need to prove that replay, audit, and task completion semantics are stable enough for customer support, incident review, and compliance evidence. The commercial problem is not the name of the control layer. The paid problem is reducing the cost of replay divergence and making root-cause analysis defensible.

A services offer can package this as a replay determinism assessment. The deliverable would include a replay trace review, step ordering rule review, transition table review, coupling registry gap list, and a written risk summary for the application contract. That gives platform teams a short path from "our replay looks flaky" to a prioritized fix list.

The product version is a replay assurance harness. It would run byte identity tests across stored audit snapshots, compare outputs across implementation versions, flag coupling registry drift, and produce a report that engineering and risk teams can both read. The monetization angle is audit confidence: teams will pay to know whether their agent runs can be replayed consistently when a customer, auditor, or incident commander asks what happened.

For AmtocSoft, the next asset should be a deterministic replay checklist and a companion test harness in the examples repo. That artifact can support newsletter capture, consulting calls, and later SaaS validation around agent replay assurance.


Revision History

Date Summary Old Version
2026-06-08 Reduced repeated deterministic-control-layer phrasing, added a monetization section, reduced em-dash usage, and recorded the revision while preserving the live Blogger URL. View original

Sources

  • IBM Observability Trends 2026, Agent Operations Edition: the canonical 2026 enterprise framing for deterministic control over agent step sequences as the missing layer between the runtime's audit surface and the application's task surface, https://www.ibm.com/reports/observability-trends-2026
  • Elastic Search Labs, GenAI Observability and Determinism (2026): the operational framing for deterministic replay across audit streams in production agent platforms, https://www.elastic.co/search-labs/blog/genai-observability-determinism-2026
  • OpenTelemetry GenAI Semantic Conventions (2026 draft): the structurally-stable identifier conventions for step-grain audit events that the ordering rule's tiebreak key composes against, https://opentelemetry.io/docs/specs/semconv/gen-ai/
  • Google SRE Workbook, Postmortem Culture and Composition: the postmortem composition rubric the control layer's five-question pass extends, https://sre.google/workbook/postmortem-culture/
  • AWS Builders' Library, Reliability and Constant Work: the structural framing for fold-order commutativity and replay determinism contracts in distributed audit systems, https://aws.amazon.com/builders-library/reliability-and-constant-work/
  • Companion repo (sequencing layer reference implementation, replay rubric template, operational signal dashboards, postmortem composition template): https://github.com/amtocbot-droid/amtocbot-examples

About the Author

Toc Am

Founder of AmtocSoft. Writing practical deep-dives on AI engineering, cloud architecture, and developer tooling. Previously built backend systems at scale. Reviews every post published under this byline.

LinkedIn X / Twitter

Published: 2026-05-11 · Updated: 2026-06-08 · Written with AI assistance, reviewed by Toc Am.

Get These In Your Inbox

Weekly deep-dives on AI engineering, no fluff. Join the newsletter →

Subscribe (free)

Or grab the book ($39, ~100 pages) · Buy me a coffee

Buy Me a Coffee · 🔔 YouTube · 💼 LinkedIn · 🐦 X/Twitter

AI as Infrastructure: Value Moves Up-Stack

For a few years the AI conversation was about who had the biggest model. That is the wrong altitude now. Models still matter, the way CPUs s...