InboxWakePolicy class final

The idle inbox-wake lane policy (gh-1180).

The hosts' idle inbox watchers (CLI, app) start a turn when mail arrives while idle. Two protections share that gate:

  • the agent-chatter anti-storm cap: at most maxStreak consecutive wakes without user input, so two chatty instances cannot ping-pong forever;
  • the user-input guarantee: mail of kind AgentMessageKind.user IS the user talking and must always wake — its delivery resets the streak, so gating it on the counter would deadlock (no run → no reset → no run).

gh-1180 adds the missing third lane: a delivered schedule_message self-reminder ([scheduled] prefix, from == to == the recipient's own mailbox) is a DELIBERATE, agent-chosen cadence — a night watch, a periodic sweep, a delayed follow-up — not chatter. It is EXEMPT from the cap so a self-chain wakes forever, with one guard: wakes arriving faster than scheduledSelfCadenceFloor are a busy-spin disguised as self-scheduling (a zero-delay re-schedule loop) and count against the ordinary cap, bounding the storm exactly like foreign chatter.

Constructors

InboxWakePolicy({int maxStreak = defaultMaxInboxWakeStreak, Duration scheduledSelfCadenceFloor = defaultScheduledSelfCadenceFloor, DateTime clock()?})

Properties

clock → DateTime Function()?
Injectable clock (tests); null uses DateTime.now.
final
hashCode → int
The hash code for this object.
no setterinherited
maxStreak → int
Consecutive chatter wakes allowed without user input.
final
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited
scheduledSelfCadenceFloor ↔ Duration
Minimum spacing between two EXEMPT scheduled-self wakes; anything faster counts against the cap. Mutable so a host test can collapse it (zero: every self wake exempt) or exercise the spin guard.
getter/setter pair
streak ↔ int
Consecutive inbox-triggered wakes without user input.
getter/setter pair

Methods

announceRefusal() → bool
Whether THIS call may announce the current refusal: true exactly once per refusal episode. An episode opens on the first refusal and closes when the gate reopens — a user-input reset (resetStreak) or any allowed cap-consuming (non-exempt) wake. Hosts gate their visible refusal line AND their refusal receipts on this: the gate holds until user input arrives, so per-tick announcements would append ~43k duplicate lines/day to the receipt trail (review: the wake_attempted spam) and spam the terminal.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
resetStreak() → void
A delivered user-kind message (or a typed line) IS the user talking. Ends the refusal episode too (see announceRefusal): the two pieces of episode state live side by side so they cannot drift apart.
toString() → String
A string representation of this object.
inherited
wakeDecisionFor(List<AgentMessage> pending, {bool pluginPending = false}) → InboxWakeDecision
Decides — and books — the wake for one pending batch. Call only when mail is pending and the host is idle; a refused decision leaves the counters untouched, so the next tick retries under the same rules.

Operators

operator ==(Object other) → bool
The equality operator.
inherited

Static Methods

isScheduledSelfMail(AgentMessage message) → bool
Whether message is the delivery of a scheduled self-reminder: the scheduler's [scheduled] prefix addressed from the recipient's own mailbox (from == to). Foreign chatter cannot produce the shape by accident — its fromId is the sender's mailbox, never mine.

Constants

defaultMaxInboxWakeStreak → const int
The anti-storm cap for foreign agent-to-agent chatter (unchanged behavior — the old _maxInboxWakeStreak).
defaultScheduledSelfCadenceFloor → const Duration
Wakes closer together than this are a spin, not a cadence (gh-1180 E5): the exemption must not open a busy-spin.