You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A disconnected worker's expired lease locks its requester out of the fleet for up to 24 h #984
A gateway lease on a worker that disconnects (the Mac sleeps or drops off the network) stays in the gateway's lease index for as long as the worker's view is kept: 24 h after last seen (src/gateway/worker-registry.ts:442-457, :566-577). The requester limit counts it regardless of connection state (src/gateway/lease-index.ts:152-165, src/gateway/fleet-coordinator.ts:311-318). Release answers WORKER_UNREACHABLE (fleet-coordinator.ts:1365-1369).
So once the lease's TTL has passed, the agent is locked out of the whole fleet with REQUESTER_ALREADY_LEASED until the worker returns or 24 h pass. lease.list also keeps showing the lease past its deadline.
What you expected
A lease past its last reported ttlDeadline on a disconnected worker no longer counts against the requester limit. No renew can have reached it through this gateway. The agent can lease another device.
How to reproduce
Gateway with two workers; an agent leases a device on worker A.
Disconnect worker A (sleep the Mac).
Wait past the lease's TTL, then request a new device: REQUESTER_ALREADY_LEASED, even though worker B has capacity.
Environment
main at 7a2f0b3. Found by reading code in the #412 review, not by a user report.
Logs and evidence
From the #412 review, finding R2. Proposed fix there: skip such an entry for the requester limit, and keep it for routing.
Confirmed. A defect: ADR 0005 §6 already treats a disconnected worker's lease past its deadline as expired, and the requester limit ignores that.
src/gateway/fleet-coordinator.test.ts, "grants a requester a new lease once its only lease, on a disconnected worker, is past its last reported deadline, and keeps that lease routable to its worker (#984)", on bug/984-repro. One ms before the deadline the request is refused (control). One ms after it, the request still fails:
pnpm exec vitest run --project unit src/gateway/fleet-coordinator.test.ts -t "#984"
AssertionError: expected RequesterAlreadyLeasedError: Requester ag… to match object { lease: { id: 'wrk_b.lse_1', …(1) } }
+ "existingLeaseId": "wrk_a.lse_old",
❯ src/gateway/fleet-coordinator.test.ts:1872:27
Root cause
src/gateway/fleet-coordinator.ts:312 passes every index entry (leaseIndex.held, lease-index.ts:152) to requesterLimitDecision. The entry (lease-index.ts:26) has no deadline and no connection state, so nothing at admission can see that the lease is past its deadline on a worker that cannot be reached. The only place that checks this is retention (worker-registry.ts:574), and it drops the entry only after 24 h.
Simplest fix
In #refuseIfAlreadyLeased, leave out a held lease whose view is disconnected and whose reported ttlDeadline <= now. Use the same comparison as pruneExpired. Keep the index entry, so ADR §28 and the gateway rule "never decides a lease is gone" still hold. A throwaway version of this made the whole file pass (247/247). Update line 65 of src/gateway/AGENTS.md.
Alternatives rejected
Drop the entry at the deadline: the lease can no longer be routed, which breaks ADR §28.
Store ttlDeadline in FleetLeaseEntry: a second copy of view state that every renew must keep in sync.
Shorter retention: does not fix it, it only shortens the lockout.
Risk
When the worker comes back, the requester briefly holds two leases, until the worker's lease.expired arrives.
The worker's clock and the gateway's clock may disagree. Retention already accepts this.
A view that disconnected while starting has no leases list, so its lease still counts.
Maintainer accepted the triage report's proposed fix: for the requester limit, skip a lease whose worker is disconnected and whose last reported deadline has passed. Keep the entry in the index for routing (ADR 0005 §28). The review should weigh the three risks the report lists.
Activity
V3RON commentedon Oct 25, 2026
Reproduction
Confirmed. A defect: ADR 0005 §6 already treats a disconnected worker's lease past its deadline as expired, and the requester limit ignores that.
src/gateway/fleet-coordinator.test.ts, "grants a requester a new lease once its only lease, on a disconnected worker, is past its last reported deadline, and keeps that lease routable to its worker (#984)", onbug/984-repro. One ms before the deadline the request is refused (control). One ms after it, the request still fails:Root cause
src/gateway/fleet-coordinator.ts:312passes every index entry (leaseIndex.held,lease-index.ts:152) torequesterLimitDecision. The entry (lease-index.ts:26) has no deadline and no connection state, so nothing at admission can see that the lease is past its deadline on a worker that cannot be reached. The only place that checks this is retention (worker-registry.ts:574), and it drops the entry only after 24 h.Simplest fix
In
#refuseIfAlreadyLeased, leave out a held lease whose view isdisconnectedand whose reportedttlDeadline <= now. Use the same comparison aspruneExpired. Keep the index entry, so ADR §28 and the gateway rule "never decides a lease is gone" still hold. A throwaway version of this made the whole file pass (247/247). Update line 65 ofsrc/gateway/AGENTS.md.Alternatives rejected
ttlDeadlineinFleetLeaseEntry: a second copy of view state that every renew must keep in sync.Risk
lease.expiredarrives.startinghas noleaseslist, so its lease still counts.Written by an agent.
V3RON commentedon Oct 25, 2026
Decision
Maintainer accepted the triage report's proposed fix: for the requester limit, skip a lease whose worker is disconnected and whose last reported deadline has passed. Keep the entry in the index for routing (ADR 0005 §28). The review should weigh the three risks the report lists.
Written by an agent.
docs(gateway): the requester limit skips an expired lease on a discon…
test: straddle the #984 deadline, cover a connected worker, fail loudly
docs(gateway): an expired lease on a disconnected worker does not cou…
test: a lease its disconnected worker's last view does not list still…
fix(gateway): an expired lease on a disconnected worker no longer blo…
V3RON commentedon Oct 25, 2026
Delivery run
Merged: #992 (#984)
Written by an agent.