HeddleAdmissionControl

class HeddleAdmissionControl(heddle: FairShareExecution, costPerTask: Long = 1, costOf: (TaskDescriptor) -> Long = { costPerTask }, laneToLeaf: (Lane) -> GroupId? = { lane -> defaultLeafOf(lane) }) : AdmissionControl

Binds warp's opaque Lane tags to a weighted fair-share tree and gates warp's free execution path on entitlement — the whole point of the :kuilt-warp-heddle satellite.

Warp answers where a task runs (the consistent-hash ring) and whose turn (the free claim path). It has no notion of how much any lane may take. This adapter supplies that missing dimension without warp core learning a single fair-share type: it implements warp's opaque AdmissionControl, and warp calls it just before running each task. The adapter maps the task's Lane to a fair-share leaf and reserves one task's worth of that leaf's entitlement; when the task finishes warp calls AdmissionTicket.settle, which completes the reservation and charges the ledger exactly once.

The behaviour that falls out, per warp's contract:

  • Untagged is free. A task on the Lane.ROOT lane (the default) is admitted immediately with a no-op ticket — no reservation, no ledger touch — so an untagged workload is bit-for-bit today's warp.

  • Exhaustion defers, never drops. When a lane's leaf has no spare entitlement, FairShareExecution.reserve returns null; this adapter returns null too, so warp defers the task (leaves it pending) and re-attempts it on a later claim cycle — work resumes when entitlement flows in.

  • Zero consensus on the hot path. reserve / complete are local reads and writes of already-converged holdings; admitting a task adds no consensus round. The heddle's own ledger replication is coordination-free cloth (a Quilter), not consensus.

Entitlement itself flows the ordinary heddle way — a consumer advertises demand and calls HeddleNode.schedule to delegate holdings down the tree by weight. Two lanes weighted 3:1 therefore complete tasks in a 3:1 ratio: each lane runs exactly as many tasks as it was delegated entitlement for.

Parameters

heddle

the fair-share data plane whose holdings this adapter reserves against — the FairShareExecution surface shared by both front doors, so this accepts either a us.tractat.kuilt.heddle.heddleStatic node (no consensus) or an H5 us.tractat.kuilt.heddle.heddleGoverned node (governed) interchangeably. One asymmetry: a governed node must have enrolled itself before it will author any entitlement — until enroll(self) applies, its write gate is closed, so reserve returns null and schedule delegates nothing and this adapter admits no gated task (#1693, design §13.2).

costPerTask

service units reserved and charged per task. Defaults to 1 — the §14.4 "one unit per task" costing; a caller with variable-cost work supplies a per-descriptor cost via costOf.

costOf

per-task cost function; defaults to a flat costPerTask for every descriptor.

laneToLeaf

maps a task's Lane to the fair-share leaf GroupId to charge, or null to admit the task un-gated. The default treats Lane.ROOT as un-gated and every other tag as the identically-named leaf (Lane("acme/batch") → GroupId("acme/batch")).

See also

Samples

// 1. Build the adapter — warp's opaque AdmissionControl, backed by the fair-share ledger.
val admission = HeddleAdmissionControl(heddle)
// Pass it to a node:  WarpNode(selfId, seam, roster, scope, clock = …, registry = …,
//                              admissionControl = admission, epoch = <per-boot counter>)

// 2. Tag a task into a lane on the producer side.
val interactive: TaskDescriptor =
    TaskDescriptor(op = OpId("score"), args = "doc-1".encodeToByteArray())
        .inLane("acme/interactive")
check(interactive.lane == Lane("acme/interactive"))

// 3. An untagged task rides the default root lane and is admitted un-gated.
val untagged = TaskDescriptor(op = OpId("score"), args = ByteArray(0))
check(untagged.lane == Lane.ROOT)
check(admission.admit(untagged) === AdmissionTicket.NOOP)

Constructors

Link copied to clipboard
constructor(heddle: FairShareExecution, costPerTask: Long = 1, costOf: (TaskDescriptor) -> Long = { costPerTask }, laneToLeaf: (Lane) -> GroupId? = { lane -> defaultLeafOf(lane) })

Types

Link copied to clipboard
object Companion

Functions

Link copied to clipboard
open override fun admit(descriptor: TaskDescriptor): AdmissionTicket?

Reserve this task's lane entitlement, or defer it.