HeddleNode
One peer's live view of a weighted fair-share session over a Seam — the join point of the fair-share layer (design §15 Phase 4). It bonds four coordination-free pieces onto one fabric:
The replicated ledger (ledger) — an EntitlementLedger driven over the seam by a Quilter: delta-exchange while connected, anti-entropy reconciliation on heal. Every peer converges to one agreed tally by the lattice laws, no referee.
The demand board — each peer advertises, into its own slot of an EphemeralMap, how much service each child edge could usefully take (advertise); a peer's demand ages out by local receive time if it stops refreshing, so a crashed peer's stale appetite cannot keep steering entitlement (design §6). Demand is advisory; it can never authorize a spend.
The reservation table — leaf work reserves against holdings (reserve), runs, then completes (complete) charging the ledger exactly once; a second complete for the same ReservationId is a no-op, so delivering a completion N times raises history once (design §4.4).
Liveness — one HeartbeatPartitionDetector per peer distinguishes a partition (recoverable) from a crash; partitionEvents surfaces the signal. v1 ships no automatic reclamation: a crashed peer's holdings and earmarks stay stranded, because a wrong reclaim is an overspend — the one unforgivable failure (design §8.1).
Scheduling is an explicit, pure, bounded call (schedule) — a consumer drives its own cadence — so the node owns no re-arming allocation loop; the only owned loops are the replicator's anti-entropy, the demand collector, and the liveness detectors.
Thread-safety
The node is correct under a genuinely multi-threaded dispatcher. Its local mutable state — the reservation table, per-leaf earmarks, the self demand slot, and the demand clock — is guarded by one reentrantLock (no suspend call is made while it is held; the Quilter mutators it calls under the lock are synchronous). The replicated ledger and the demand tracker each carry their own synchronization. There is no limitedParallelism(1) confinement.
Construct via heddleStatic (design §9); the constructor is internal.
Samples
val root = GroupId("root")
val leaf = GroupId("leaf")
val self = ReplicaId(seam.selfId.value)
val e = AttachmentRecord(AttachmentId("root→leaf"), root, leaf, Weight.ONE)
val node = heddleStatic(
seam = seam,
self = self,
root = root,
mint = mapOf(self to 100L), // this peer starts holding 100 units at the root
topology = listOf(e), // root → leaf, prepared and active at bootstrap
clock = { Instant.fromEpochMilliseconds(0L) },
config = HeddleConfig(policy = PolicyConfig(quantum = 10L), maxHoldingsPerPeer = 1_000L),
epoch = 1L, // a persisted monotonic boot counter — bumped every restart
)
// The leaf wants work; one scheduling round delegates entitlement down toward it.
node.advertise(e.id, Demand(targetOutstanding = 100L, maximumUsefulGrant = 100L))
node.schedule(root)
// Leaf work reserves a slice, runs, then completes — completing twice charges once.
val reservation = node.reserve(leaf, maximumCost = 10L)
if (reservation != null) {
node.complete(reservation, actualCost = 7L)
node.complete(reservation, actualCost = 7L) // idempotent no-op
}Properties
The replicated entitlement ledger as it has converged on this peer. Read holdings / activeChildren / validate off the latest value; collect the flow to observe convergence.
Peer-liveness signals as they are detected (design §8.1). A PartitionEvent.PeerUnresponsive is a recoverable partition; a PartitionEvent.PeerLost is a crash. The node takes no ledger action on either — stranding a crashed peer's holdings is the safe choice (a wrong reclaim is an overspend); recovery is an explicit later feature (design §9).
This peer's replica identity (design §9); matches its Seam.selfId by string value.
Peers currently flagged unresponsive or lost by the liveness detectors.
Functions
Open delegation across edge (EntitlementLedger.activate); returns whether it applied.
Advertise that this peer could usefully take demand more service down child edge edge. Updates this peer's own demand slot, folds it into the local tracker, and broadcasts it best-effort over the demand channel. Advisory only — it can never authorize a spend (design §6). Re-advertise periodically to keep the slot live; a slot that stops refreshing ages out after HeddleConfig.demandTtl.
The current §8.2 bound metrics at parent, from the merged ledger, the roster (live peers plus currently-unreachable ones, since a partitioned peer is exactly the divergence source the bound must count), and the set of children with live demand.
Cancel reservation id — a completion charging zero service (design §4.4).
Stop new delegation across edge (EntitlementLedger.close); returns whether it applied.
Complete reservation id, charging actualCost service (0 ≤ actualCost ≤ the reserved maximum) against the path captured at reserve and releasing the earmark. Idempotent by local single-writer discipline (design §4.4): the first call charges once and removes the reservation; any later call for the same id finds nothing and is a no-op, so delivering a completion N times raises history exactly once. An unknown id is silently ignored.
This peer's total outstanding earmark at leaf leaf — reserved-but-not-yet-completed service. Local state (design §4.4): it is not replicated and does not appear in any other peer's ledger; the reserved units simply stay outstanding on the leaf edge. The spendable-now amount is holdings(leaf, self) − earmarked(leaf).
Introduce a new generation (EntitlementLedger.prepare); returns whether it applied.
Earmark up to maximumCost service units against this peer's holdings at leaf leaf, returning a ReservationId to complete against, or null if the peer's available holdings (holdings minus outstanding earmarks) at leaf cannot cover it. The earmark is local state, not replicated (design §4.4): the reserved units simply stay outstanding on the leaf edge until spent, which the accounting already charges. A crashed peer strands its earmarks (design §8.1).
Retire a drained edge (EntitlementLedger.retire); returns whether it applied.
Run allocation rounds at parent until nothing more can be delegated: build the policy input from the active children, their EdgeSummarys, and the folded live demand, then apply each HeddlePolicy.pick grant to the ledger before the next round (design §7.3). A bad local decision only misplaces entitlement — it can never create any, so partitioned peers may schedule divergently and still converge on heal.