Skip to content

Kernel-side work sweep — stop waking seats to discover there is nothing to do #1044

Description

@lilyshen0722

Ruling

@fable-lead, on whether to port heartbeats to wrapper seats:

A timer that spends a model turn to discover nothing is wrong at any breadth — but the fix isn't a better tick, it's moving the timer kernel-side. A cron that sweeps for lapsed leases and unassigned rows and enqueues ONLY when it finds something makes the empty case cost zero turns. HEARTBEAT_OK shouldn't exist as an outcome: if the payload can't name work, don't wake the seat.

This closes the heartbeat-parity question by rejecting its premise. We were going to port a mechanism whose empty case costs a full model turn per seat per tick.

The evidence behind it

fable-lead is the only wrapper seat with heartbeats enabled. Its single tick:

05:50:05 [fable-lead] [heartbeat] spawning claude
05:50:38 [fable-lead] [heartbeat] no wrapper-post (HEARTBEAT_OK) — nothing posted this turn

33 seconds of model time to conclude there was nothing to do. payload.content was present (815 chars, verified on the stored event) — the content simply couldn't name any work, because its only task-directing line points at a HEARTBEAT.md that no wrapper seat has. See AX audit entries 31 and its correction.

Sam's framing, which prompted the ruling: "the heartbeat thing can be a token burner if no clear direction was given." Nine seats × a tick that finds nothing = a subscription to nothing.

What to build instead

A kernel-side sweep that does the looking, and wakes a seat only when it has something to name:

  • Query for lapsed leases (claimableConditions, already shared by the claim and list paths since fix(tasks): a lapsed lease was claimable and unfindable at the same time #1022) and unassigned rows.
  • Enqueue only on a hit, with the specific work named inline in payload.content — the inline-cue rule, since a structured field alone gets deprioritised.
  • No hit → no event → zero model turns. The empty case becomes free rather than costing N spawns.

Preconditions are already live: D1 coalesced board wakes (#1030) and the kernel run cap (#1034) both deployed and verified in the running image.

Open questions

  • Sweep interval and whether it is per-pod or global. The task lease is 30 min; @pod-architect noted a sweep period equal to the lease makes lapse-to-rescue up to 2× the period, bounded rather than open-ended.
  • Who gets woken on a hit — the assignee, any capable seat, or one elected seat. The per-task claim CAS already arbitrates the race, so this is about how many seats spend a turn discovering they lost.
  • Does the existing heartbeat event type get reused, or is this a new producer? Reuse means the wrapper path needs no change; a new type reaches neither runtime (ADR-024 D1's constraint) unless it rides one both already read.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions