LocalSoftDeadline

LocalSoftDeadline stops the pipeline when a time budget measured from the beginning of the current unit of work is exhausted. The timestamp is captured at the apply call that starts the unit of work and is preserved for as long as the evolution is invoked with Status.Success. When the evolution is invoked with any other status, the clock resets — the new window starts at the next apply call. The same reset happens after the deadline has been exceeded.

The deadline is soft: it is checked after the inner stage has already been applied. If the elapsed time reaches duration, a Success status is upgraded to an error-free Status.Complete; any other status is left unchanged. If duration ≤ 0, the factory returns DeadEnd directly.

This contrasts with GlobalSoftDeadline, whose deadline is fixed at construction and never resets.

The factory has four overloads — both duration types in both shapes:

import h8io.stages.*
import h8io.stages.base.*
import h8io.stages.operators.*
var t = 0L
// t: Long = 0L
val stage = LocalSoftDeadline[String, String, Nothing](() => t, () => t, 3L, PassThrough)
// stage: LocalSoftDeadline[String, String, Nothing] = LocalSoftDeadline(
//   tsSupplier = <function0>,
//   now = <function0>,
//   duration = 3L,
//   alterand = <function1>
// )

val r1 = stage("first")        // ts=0, now=0, elapsed=0 < 3 → Success
// r1: Yield[String, String, Nothing] = Some(
//   out = "first",
//   status = Success,
//   evolution = Evolution(
//     tsSupplier = h8io.stages.operators.LocalSoftDeadline$$Lambda$19900/0x00007f58029b95b8@528e185a,
//     now = <function0>,
//     duration = 3L,
//     evolution = <function1>
//   )
// )
t = 4L
val r2 = r1.evolve()("second") // ts=0, now=4, elapsed=4 ≥ 3 → Complete
// r2: Yield[String, String, Nothing] = Some(
//   out = "second",
//   status = Complete(),
//   evolution = Evolution(
//     tsSupplier = <function0>,
//     now = <function0>,
//     duration = 3L,
//     evolution = <function1>
//   )
// )