IronClaw
IronClaw finds its retry budget cannot fit inside the LLM deadline
IronClaw reports that finalization cannot measure time-to-first-token and that configured retries can exceed the total request deadline. Related scheduler records show terminalization and capability gates can bypass expected lifecycle hooks.
nearai/ironclaw issue #7783 is the inspected primary source: “LLM timeout policy: finalization can't measure TTFT, and the retry budget can't fit the deadline.” The related records below were inspected as supporting context rather than independent confirmation.
The facts
- nearai/ironclaw issue #7783 was created 2026-08-20T22:32:26Z and was open at inspection; its title is “LLM timeout policy: finalization can't measure TTFT, and the retry budget can't fit the deadline.” - nearai/ironclaw issue #7780 was created 2026-08-20T20:02:26Z and was open at inspection; its title is “AfterTurn hook: scheduler-side failure terminalization bypasses the point.” - nearai/ironclaw issue #7775 was created 2026-08-20T14:26:10Z and was open at inspection; its title is “Unbound runs: skip a gating capability instead of aborting (gate posture for background work).” - The repository reported 12601 stars, 1517 open issues, default branch main, and last push 2026-08-21T00:09:18Z. - The latest tagged-release baseline checked was ironclaw-v1.3.0 published 2026-08-19T23:17:37Z.
What changed
nearai/ironclaw issue #7783 reports: Summary Structured-output finalization runs on the **non-streaming** HTTP client, so a stalled provider request is invisible until a 60s total wall-clock cap fires — and the enclosing 75s finalization deadline then kills the run before the retry can complete. A single transport stall destroys a run that had already done all its useful work. Observed in production: a suggestions run completed ~3.5 minutes of successful discovery (12 model calls, 8 capability invocations, all succeeded), then died with zero output. Timeline (production, Qwen/Qwen3.8-27B via nearai_chat) | Time | Event | |---|---| | 22:04:36.51 | agent loop ends cleanly: finish_reason=Stop, 2745-byte reply — discovery done | | 22:04:36.58 | structured finalization dispatched | | 22:05:36.58 | **+60.0s** — Retrying after transient error... error sending request for url (https://cloud-api.near.ai/v1/chat/completions), attempt=1, backoff 1247ms | | 22:05:51.58 | **+75.0s** — deadline fires, run dead mid-retry | The model was not slow. Measured directly against the same endpoint: Qwen/Qwen3.8-27B finalizes a **40k-token** request in **28.7s** (openai/gpt-5.6-sol: 8.5s). The request hung; it was not working. Root causes 1. Finalization cannot measure time-to-first-token at all.** system_inference.rs:237 calls stream_model(...), but with no progress sink model_gateway.rs:1684 takes the provider.complete(completion) branch — the one-shot client built by hardened_client_builder(60) (config.rs:330), whose reqwest.timeout() is a **total** wall-clock cap. On a non-streaming call, "hung" and "slow but working" are indistinguishable. Whether a
nearai/ironclaw issue #7780 reports: Follow-up from #7770 phase 1, accepted from the PR approach audit on #7765 (finding 3). Gap AfterTurn dispatches from the executor after a successfully APPLIED exit. Runs that reach a terminal state through the scheduler's failure-terminalization paths (driver failure / exit-application failure recovery, turn_scheduler.rs ~287-320) never fire the point. The context contract documents this today: the point fires only for executor-applied exits. Why it was not wired in phase 1 No consumer needs it: the only registered hook (memory curation) counts completed turns exclusively. Wiring dispatch into scheduler recovery paths touches crash/retry semantics for zero behavioral gain today — half-building it under audit pressure would have been worse than documenting it. When to fix The first hook that wants failure observations (per-turn cost accounting on failed runs, "notify me when a long task dies") makes this real. Then: dispatch from the terminalization seam(s) with the same central eligibility guard (after_turn_hook_context: conversation profile, actor present, is_terminal()) completed = false, and consider whether the context needs a failure-category field (closed vocabulary only, per the milestone-summary rules) the recursion guard is already status-independent; pin with a scheduler-path test that an unbound run's failure terminalization still never dispatches Refs #7770, #7765.
nearai/ironclaw issue #7775 reports: Follow-up from #7770 phase 1 (PR #7765), closing its one deliberately-open decision. Problem An unbound run aborts when a capability gates: GateNotSupportedStrategy turns the gate into a gate_not_supported terminal failure, because there is no conversation surface on which to render an approval prompt. For hook-started background work — the memory-curation pass is the live case — this means a user who turned auto-approve OFF gets failed background runs instead of skipped chores. ironclaw.memory.write is auto-approved for a default user but not exempt from gating. Why the obvious fix is wrong "Check whether the write would gate before submitting the pass" cannot be built cleanly today: whether a capability gates is decided only inside authorize_dispatch_with_trust at dispatch time, composing the capability descriptor (effects + origin gate matrix), the run's ApprovalPolicy, the TrustDecision, grants, and leases. A pre-check in a product service would duplicate gate composition outside the authorizer and drift — the stage-collapsing this codebase forbids (authorization / approval / dispatch are distinct stages). The // DECISION #7770 comment at the submission site in crates/product/ironclaw_assistant/src/memory_curation.rs records this. Proposed fix — at the gate seam, not before it A gate outcome for unbound runs that SKIPS the gating capability instead of aborting the run: the invocation resolves as a model-visible failure-shaped result ("skipped: requires approval, unavailable on a background run"), the loop
Why it matters
Timeout policy is a budget equation, not a list of knobs. When retries, finalization and lifecycle hooks do not share one deadline, receipts can omit work that never had time to complete. The operator test is whether the system remains bounded and its receipts still describe the action after failure, retry or restart.
Current
The primary record was open when captured on 2026-08-21. The tagged-release baseline was ironclaw-v1.3.0 published 2026-08-19T23:17:37Z. Repository metadata, full source bodies, current pull requests and issues, releases, Google News, Hacker News, Lobsters, Metamesh and the rendered ClawCharts row were inspected. ClawCharts selected the subject; it did not prove the claim.
Evidence
The primary URL is https://github.com/nearai/ironclaw/issues/7783. Supporting records are https://github.com/nearai/ironclaw/issues/7780 and https://github.com/nearai/ironclaw/issues/7775. Source bodies, timestamps and states are preserved in the daily evidence bundle. Test counts and reproductions remain attributed to their authors unless identified as independently rerun.
Source boundary
Open work is described as open, closed work as closed, and operator reports as reports. A pull request is evidence of proposed or reviewed direction, not proof of a shipped release. Search residue, package mirrors and historically published source spines were excluded.
Operator take
Allocate retry and finalization budgets from one monotonic deadline, preserve lifecycle hooks on scheduler-side failure, and record when background work skips a gating capability. Preserve a before-state receipt, make the smallest reversible change, and verify the original failure independently.
Caveat
Public project records are mutable. Status, scope and evidence can change after publication. This brief records the inspected state and does not authorize changes to a reader’s deployment.
Source inspected; source state, environment and release boundary remain explicit.