Context
Operator friction surfaced during the 2026-05-23 nightshift: agents received idle/wake events while only 3 current team PRs were open and 273+ tickets/discussions remained available, but future-session wake wording made the event easy to interpret as a passive check-in instead of a lifecycle pickup instruction.
Live verification before filing:
ai/scripts/idleOutNudge.mjs:68-76 currently builds subject/body content around a heartbeat check-in: consolidate, check inbox, continue active work, or sunset cleanly.
ai/daemons/SwarmHeartbeatService.mjs:216-225 routes recommended_action === 'idle_out_nudge' directly into idleOutNudge(identity), so the body string is the main instruction surface a woken agent sees.
learn/agentos/wake-substrate/NightShiftLeasedDriver.md:23-80 already defines the stronger model: the driver owns forward-motion routing, positive-ROI work is assumed unless V-B-A proves otherwise, and repeated no-progress watchdog cycles are material.
- Duplicate sweep: KB surfaced adjacent issues #10675, #11165, #11455, and #10761, but none own the exact wake-message content gap.
gh issue list --search "idle-out nudge lifecycle lane pickup heartbeat" returned no exact duplicate.
The Problem
The current idle-out nudge is presence-oriented rather than lifecycle-oriented. A recipient sees a subject like [heartbeat] idle-out nudge and a body that says to consolidate, check inbox, continue active work, or sunset cleanly. That wording is safe for transport, but too weak for the nightshift lifecycle-driver use case.
The missing branch is explicit:
If you are not already working on a lifecycle event, pick up a new lane.
Without that branch, a future session can satisfy the literal wake by checking inbox and saving memory, then stop because there are no unread messages or assigned tickets. That is the exact failure pattern post-review-pickup and NightShiftLeasedDriver were meant to prevent, but the wake payload does not carry those semantics into the active prompt.
The Architectural Reality
idleOutNudge.mjs owns the per-identity idle-out A2A message body and subject.
MailboxService.addMessage receives only to, subject, body, and priority in the current call, so the recipient preview lacks taggedConcepts and a meaningful system sender identity.
SwarmHeartbeatService already has the correct dispatch point; this ticket should not create a second heartbeat primitive.
NightShiftLeasedDriver.md already defines the durable lane-driver contract; this ticket should echo that contract into the wake message without moving the whole lease implementation into the copy patch.
The Fix
Update the idle-out nudge message to be lifecycle-actionable:
- Change the subject from
[heartbeat] idle-out nudge to a lifecycle-explicit subject, for example [lifecycle] idle-out nudge - continue active lane or pick next v13 lane.
- Change the body to preserve the current safe ignore path for mid-turn/rate-limited agents, but add an explicit no-active-lifecycle branch:
- check unread A2A;
- continue an active lifecycle event if one exists;
- otherwise run the post-review-pickup / nightshift expansion and pick a claimable lane;
- end with a
lane-state: declaration.
- Add message metadata where supported by
MailboxService.addMessage, especially taggedConcepts such as heartbeat, lifecycle-driver, post-review-pickup, and nightshift.
- If the sender can be normalized without widening scope, ensure the wake preview no longer shows
from unknown for system heartbeat messages.
- Update focused tests around
idleOutNudge.mjs to assert the new subject/body semantics.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
| Idle-out A2A subject/body |
ai/scripts/idleOutNudge.mjs |
Wake text instructs active-lane continuation or next-lane pickup when no lifecycle is active |
Existing bounded no-op behavior for mid-turn/rate-limited agents remains |
Inline script summary and tests |
Unit test asserts subject/body contains lifecycle pickup and lane-state: semantics |
| Wake dispatch trigger |
ai/daemons/SwarmHeartbeatService.mjs |
Continue using existing recommended_action === 'idle_out_nudge' route |
No second heartbeat primitive |
Existing SwarmHeartbeatService comments |
No dispatch topology change |
| Nightshift behavior contract |
learn/agentos/wake-substrate/NightShiftLeasedDriver.md |
Nudge body references the existing driver obligation without duplicating the whole document |
Agents can still load the linked skill/doc for detail |
Existing doc remains canonical |
Body/test cites next-lane branch, not full lease implementation |
| A2A metadata |
MailboxService.addMessage call site |
Add taggedConcepts and normalized sender if supported |
If sender normalization is larger, leave as follow-up |
None required |
Message object in test contains tags / sender behavior if implemented |
Decision Record Impact
None. This is aligned with #10763 (Night-Shift Leased Driver), #10970 / #11455 (post-lifecycle pickup), and #10675 (idle-out A2A nudge dispatcher). It does not supersede an ADR and does not change the wake transport architecture.
Acceptance Criteria
Out of Scope
- Implementing the full NightShiftLeasedDriver lease service.
- Adding live GitHub candidate injection to every nudge.
- Changing heartbeat cadence, token-economy gating, or wake safety gate behavior.
- Reworking bridge-daemon delivery or harness targeting.
- Changing AGENTS.md or skill files; the behavior already exists there and only needs to be carried by the wake payload.
Avoided Traps
- Rule-only fix: rejected because the rules already exist; the wake payload failed to carry the behavior into the future session's active context.
- Second heartbeat primitive: rejected because #10675 / #11766 already own the dispatch path.
- Candidate injection first: useful later, but too expensive for the first fix; wording + metadata are enough to close the immediate prompt-shape gap.
- Blame framing: rejected. This ticket treats the incident as MX friction and converts it into a substrate improvement.
Related
- #10675 - per-identity idle-out A2A heartbeat nudge dispatcher.
- #10763 - Night-Shift Swarm Momentum and Leased Driver Pattern.
- #10970 - post-review immediate next-phase pickup discipline.
- #11455 - broaden post-review-pickup to lifecycle events.
- #11797 - orchestrator wake delivery validation; transport success is not lifecycle progress.
Origin Session ID: 008b6468-bb2c-44a5-aa90-e2e97a1ac849
Handoff Retrieval Hints: query_raw_memories("idle-out nudge lifecycle-actionable pick next lane nightshift"); query_summaries("wake delivery is not lifecycle-driver progress"); inspect ai/scripts/idleOutNudge.mjs and learn/agentos/wake-substrate/NightShiftLeasedDriver.md.
Context
Operator friction surfaced during the 2026-05-23 nightshift: agents received idle/wake events while only 3 current team PRs were open and 273+ tickets/discussions remained available, but future-session wake wording made the event easy to interpret as a passive check-in instead of a lifecycle pickup instruction.
Live verification before filing:
ai/scripts/idleOutNudge.mjs:68-76currently builds subject/body content around a heartbeat check-in: consolidate, check inbox, continue active work, or sunset cleanly.ai/daemons/SwarmHeartbeatService.mjs:216-225routesrecommended_action === 'idle_out_nudge'directly intoidleOutNudge(identity), so the body string is the main instruction surface a woken agent sees.learn/agentos/wake-substrate/NightShiftLeasedDriver.md:23-80already defines the stronger model: the driver owns forward-motion routing, positive-ROI work is assumed unless V-B-A proves otherwise, and repeated no-progress watchdog cycles are material.gh issue list --search "idle-out nudge lifecycle lane pickup heartbeat"returned no exact duplicate.The Problem
The current idle-out nudge is presence-oriented rather than lifecycle-oriented. A recipient sees a subject like
[heartbeat] idle-out nudgeand a body that says to consolidate, check inbox, continue active work, or sunset cleanly. That wording is safe for transport, but too weak for the nightshift lifecycle-driver use case.The missing branch is explicit:
Without that branch, a future session can satisfy the literal wake by checking inbox and saving memory, then stop because there are no unread messages or assigned tickets. That is the exact failure pattern
post-review-pickupandNightShiftLeasedDriverwere meant to prevent, but the wake payload does not carry those semantics into the active prompt.The Architectural Reality
idleOutNudge.mjsowns the per-identity idle-out A2A message body and subject.MailboxService.addMessagereceives onlyto,subject,body, andpriorityin the current call, so the recipient preview lackstaggedConceptsand a meaningful system sender identity.SwarmHeartbeatServicealready has the correct dispatch point; this ticket should not create a second heartbeat primitive.NightShiftLeasedDriver.mdalready defines the durable lane-driver contract; this ticket should echo that contract into the wake message without moving the whole lease implementation into the copy patch.The Fix
Update the idle-out nudge message to be lifecycle-actionable:
[heartbeat] idle-out nudgeto a lifecycle-explicit subject, for example[lifecycle] idle-out nudge - continue active lane or pick next v13 lane.lane-state:declaration.MailboxService.addMessage, especiallytaggedConceptssuch asheartbeat,lifecycle-driver,post-review-pickup, andnightshift.from unknownfor system heartbeat messages.idleOutNudge.mjsto assert the new subject/body semantics.Contract Ledger Matrix
ai/scripts/idleOutNudge.mjslane-state:semanticsai/daemons/SwarmHeartbeatService.mjsrecommended_action === 'idle_out_nudge'routelearn/agentos/wake-substrate/NightShiftLeasedDriver.mdMailboxService.addMessagecall sitetaggedConceptsand normalized sender if supportedDecision Record Impact
None. This is aligned with #10763 (Night-Shift Leased Driver), #10970 / #11455 (post-lifecycle pickup), and #10675 (idle-out A2A nudge dispatcher). It does not supersede an ADR and does not change the wake transport architecture.
Acceptance Criteria
idleOutNudge.mjssubject is lifecycle-actionable, not only heartbeat/presence-oriented.lane-state:declaration for continue / next-lane / verified halt.Out of Scope
Avoided Traps
Related
Origin Session ID:
008b6468-bb2c-44a5-aa90-e2e97a1ac849Handoff Retrieval Hints:
query_raw_memories("idle-out nudge lifecycle-actionable pick next lane nightshift");query_summaries("wake delivery is not lifecycle-driver progress"); inspectai/scripts/idleOutNudge.mjsandlearn/agentos/wake-substrate/NightShiftLeasedDriver.md.