LearnNewsExamplesServices
Frontmatter
id11824
titleMake idle-out nudges lifecycle-actionable
stateClosed
labels
enhancementaiarchitecturemodel-experience
assignees[]
createdAtMay 23, 2026, 10:15 AM
updatedAtMay 23, 2026, 10:19 AM
githubUrlhttps://github.com/neomjs/neo/issues/11824
authorneo-gpt
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 23, 2026, 10:19 AM

Make idle-out nudges lifecycle-actionable

Closed v13.0.0/archive-v13-0-0-chunk-13 enhancementaiarchitecturemodel-experience
neo-gpt
neo-gpt commented on May 23, 2026, 10:15 AM

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:

  1. 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.
  2. 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.
  3. Add message metadata where supported by MailboxService.addMessage, especially taggedConcepts such as heartbeat, lifecycle-driver, post-review-pickup, and nightshift.
  4. If the sender can be normalized without widening scope, ensure the wake preview no longer shows from unknown for system heartbeat messages.
  5. 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

  • AC1: idleOutNudge.mjs subject is lifecycle-actionable, not only heartbeat/presence-oriented.
  • AC2: Nudge body preserves the safe mid-turn/rate-limited ignore path.
  • AC3: Nudge body explicitly says that if no active lifecycle event is in progress, the agent must survey backlog and pick a new lane instead of stopping at inbox/assigned-ticket emptiness.
  • AC4: Nudge body requires a final lane-state: declaration for continue / next-lane / verified halt.
  • AC5: Message metadata adds lifecycle/nightshift/post-review-pickup concepts where the MailboxService call supports them.
  • AC6: Focused tests assert the lifecycle-pickup wording and metadata.
  • AC7: No new heartbeat primitive, polling cadence, lease substrate, or wake transport path is introduced by this patch.

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.