Plan Retries Before They Run
A retry policy can look reasonable on its own and still be impossible inside the request that owns it. Suppose one provider attempt may take 800 milliseconds. The call allows four attempts with increasing backoff, while the request has two seconds left. The individual numbers are valid, yet the composition cannot fit. @workit/core/time-policy evaluates that shape before task execution: import { planTimePolicy } from " @workit/core/time-policy " ; const plan = planTimePolicy ({ type : " timeout " , timeout : " 2s " , policy : { type : " retry " , attempt : { type : " attempt " , duration : " 800ms " , }, retry : { times : 4 , initialDelay : " 100ms " , factor : 2 , jitter : false , }, }, }); if ( ! plan . valid ) { console . error ( plan . warnings ); } The planner does not call the provider. It computes a conservative upper bound from the declared policy and reports typed warnings when the composition cannot fit. Runtime policy and planning policy have different jobs run.retry() owns execution. It invokes the task, observes cancellation, sleeps with the task signal and decides whether another attempt may start. planTimePolicy() owns pre-execution analysis. It works with declared attempt costs and composition rules: attempt retry hedge timeout deadline series parallel Keeping those responsibilities separate matters. A planner should not produce side effects, while a runtime should not pretend it can predict provider latency that the caller never declared. A deadline is part of the task context WorkIt 0.5.0 exposes the earliest effective absolute deadline as ctx.deadlineAt . import { run } from " @workit/core " ; const deadlineAt = Date . now () + 2 _000 ; const result = await run . group ( async ( task ) => { return task ( run . deadline ( async ( ctx ) => { return { deadlineAt : ctx . deadlineAt , remainingMs : Math . max ( 0 , ctx . deadlineAt ! - Date . now ()), }; }, deadlineAt )); }); If a task has both an inherited scope deadline and a wrapper deadline, it sees