flower
/
All briefs
complete draft note flower

Reliable daemon heartbeat + expected-daemon reconcile seam (Brief #11 foundation)

canonical · plan

Spec

markdown

hand-off · dispatch

Dispatch

Auto-dispatch

when it reaches planned

Design-loop

design pass before build

This brief is complete — dispatch is closed.

#6 done fresh flower · flower/daemon-heartbeat-seam
You are being dispatched from flower Brief #26: Reliable daemon heartbeat + expected-daemon reconcile seam (Brief #11 foundation)

Recall pointer:
- Use recall_brief with id 26 for the full folder if you need provenance.

Target:
- project: flower (/Users/mikeferrara/Documents/code/flower)
- branch: flower/daemon-heartbeat-seam
- worktree: not specified
- kind: fresh

Current brief spec:
## Goal
Make daemon check-in **reliable/foolproof** and add the **"expected daemon" seam** the /roster
spawn+monitor UI (Brief #22) needs. Backend/infra foundation for Brief #11; unblocks #22 and is a
prerequisite for turning standing daemons loose safely.

## Context
Daemons (even flower-orchestrator/ops) read stale on /roster because check-in depends on the agent
REMEMBERING to call `daemon_checkin` each loop. And flower-design deferred "register expected
daemon" during spawn-flow-ui because `daemon_agents` has no `expected` state / reconcile seam.

## Scope
1. **Expected-daemon state + reconcile (the core seam).** `daemon_agents` keys on
   (role, project_id, solo_process_id). Add an `expected` status (e.g. a status enum +
   nullable/placeholder solo_process_id) representing an intended-but-not-yet-live daemon. Make
   `daemon_checkin` **RECONCILE**: when a real check-in arrives matching an expected (role, project)
   with no/placeholder process id, UPGRADE that row to live rather than creating a phantom
   duplicate. No orphan phantom rows. Provide a way to register an expected daemon — a
   `DaemonRosterService` method + an MCP tool (`daemon_register_expected`, or extend
   `daemon_checkin` with an `expected` flag), matching the existing daemon-tool shape. This is what
   /roster's spawn UI (Brief #22) calls to show "spawning…" before the daemon checks in.
2. **Session-link liveness backstop (extend 28ddf12).** Roster liveness already falls back to
   linked-session activity (`effectiveLastActivity`). Extend so `context_size` / `needs_compaction`
   degrade GRACEFULLY when only session-link data exists (no explicit check-in) — surface "unknown"
   cleanly rather than alarming/false states.
3. **Reliable heartbeat convention (flag, don't necessarily build).** The durable fix for "agent
   forgets to check in" is to make `daemon_checkin` the FIRST action of every charter-loop iteration
   (+ optionally a repeating Solo timer the daemon arms once). The charter wording lives in
   `SpawnPacketService`/`AgentConventions` and is ORCHESTRATOR-owned — so just **flag a recommended
   wording in brief_append**; focus your build on (1) + (2).

## Acceptance
- An "expected" daemon can be registered and shows on the roster as expected/spawning; a subsequent
  real `daemon_checkin` reconciles it to live (no phantom/duplicate row). Regression test.
- Roster liveness + health fields degrade gracefully with only session-link data. Test.
- Keep `php artisan test` green + pint clean. `Brief: #26` trailer on commits; `brief_append` when
  done (and `brief_dispatch_complete` if that tool is in your MCP set).

## Provenance
Extracted from operator brief #25 (item 1) + flower-design spawn-flow-ui deferral; folded into
Brief #11. Unblocks Brief #22 (/roster spawn+monitor).

Recent/key trace events:
[1] participant_joined flower-orchestrator: (no body)
[2] note_added flower-orchestrator: Extracted from operator brief #25 (item 1) + the flower-design spawn-flow-ui "expected daemon" deferral. Backend/infra foundation for Brief #11; unblocks the /roster spawn+monitor UI (Brief #22).
[3] plan_proposed flower-orchestrator: ## Goal
Make daemon check-in **reliable/foolproof** and add the **"expected daemon" seam** the /roster
spawn+monitor UI (Brief #22) needs. Backend/infra foundation for Brief #11; unblocks #22 and is a
prerequisite for turning standing daemons loose safely.

## Context
Daemons (even flower-orchestrator/ops) read stale on /roster because check-in depends on the agent
REMEMBERING to call `daemon_checkin` each loop. And flower-design deferred "register expected
daemon" during spawn-flow-ui because `daemon_agents` has no `expected` state / reconcile seam.

## Scope
1. **Expected-daemon state + reconcile (the core seam).** `daemon_agents` keys on
   (role, project_id, solo_process_id). Add an `expected` status (e.g. a status enum +
   nullable/placeholder solo_process_id) representing an intended-but-not-yet-live daemon. Make
   `daemon_checkin` **RECONCILE**: when a real check-in arrives matching an expected (role, project)
   with no/placeholder process id, UPGRADE that row to live rather than creating a phantom
   duplicate. No orphan phantom rows. Provide a way to register an expected daemon — a
   `DaemonRosterService` method + an MCP tool (`daemon_register_expected`, or extend
   `daemon_checkin` with an `expected` flag), matching the existing daemon-tool shape. This is what
   /roster's spawn UI (Brief #22) calls to show "spawning…" before the daemon checks in.
2. **Session-link liveness backstop (extend 28ddf12).** Roster liveness already falls back to
   linked-session activity (`effectiveLastActivity`). Extend so `context_size` / `needs_compaction`
   degrade GRACEFULLY when only session-link data exists (no explicit check-in) — surface "unknown"
   cleanly rather than alarming/false states.
3. **Reliable heartbeat convention (flag, don't necessarily build).** The durable fix for "agent
   forgets to check in" is to make `daemon_checkin` the FIRST action of every charter-loop iteration
   (+ optionally a repeating Solo timer the daemon arms once). The charter wording lives in
   `SpawnPacketService`/`AgentConventions` and is ORCHESTRATOR-owned — so just **flag a recommended
   wording in brief_append**; focus your build on (1) + (2).

## Acceptance
- An "expected" daemon can be registered and shows on the roster as expected/spawning; a subsequent
  real `daemon_checkin` reconciles it to live (no phantom/duplicate row). Regression test.
- Roster liveness + health fields degrade gracefully with only session-link data. Test.
- Keep `php artisan test` green + pint clean. `Brief: #26` trailer on commits; `brief_append` when
  done (and `brief_dispatch_complete` if that tool is in your MCP set).

## Provenance
Extracted from operator brief #25 (item 1) + flower-design spawn-flow-ui deferral; folded into
Brief #11. Unblocks Brief #22 (/roster spawn+monitor).

Recommended linked context:
{
    "todos": [],
    "scratchpads": []
}

Execution notes:
- Treat the brief as the source of truth.
- Keep work scoped to this dispatch request.
- Use brief_append / brief_update_status when reporting material progress.
- Add a git commit trailer `Brief: #26` to every commit for this brief so flower can exact-link commits back to the brief.

provenance · append-only

Trace

live
or paste a screenshot uploading…
  1. link added 4d ago
    agent · system:commit-trailer
  2. participant joined 4d ago
    system · system:commit-trailer
  3. status change 4d ago
    agent · flower-orchestrator
  4. dispatched 4d ago

    Dispatch request #6 marked done.

    agent · flower-orchestrator
  5. comment 4d ago

    Implemented Brief #26 on branch flower/daemon-heartbeat-seam. Backend changes: - Added DaemonStatus::Expected and daemon_register_expected MCP surface. - DaemonRosterService::registerExpected creates/reuses a role+project expected placeholder with no solo_process_id. - daemon_checkin now reconciles a matching expected role+project placeholder into the live daemon row instead of creating a phantom duplicate. - recall_roster/daemonPayload reports expected liveness for expected rows. - Session-link-only liveness now reports health_source=session_link and degrades context_size/needs_compaction to "unknown" rather than returning a false compaction signal. - /roster can render expected daemons as "spawning". Recommended charter wording to wire later in the orchestrator-owned charter/conventions layer: "At the start of every loop iteration, before reading queues or doing work, call daemon_checkin with your role, project, solo_process_id, session_id when known, context_size/usage when available, and actor_ref. Treat this as the heartbeat that keeps /roster accurate; if context_size is unavailable, still check in and let flower mark compaction state unknown." Verification: - Focused daemon/roster/query-budget slice passed: 19 tests, 250 assertions. - Full suite passed: 380 tests, 370 passed, 10 skipped, 2724 assertions. - Pint passed on touched files. brief_dispatch_complete was requested if present, but it is not exposed in my current MCP tool set.

    agent · agent:flower-foundation-981
  6. participant joined 4d ago
    system · agent:flower-foundation-981
  7. dispatched 4d ago

    Dispatch request #6 queued for flower.

    agent · flower-orchestrator
  8. status change 4d ago
    agent · flower-orchestrator
  9. plan proposed 4d ago

    ## Goal Make daemon check-in **reliable/foolproof** and add the **"expected daemon" seam** the /roster spawn+monitor UI (Brief #22) needs. Backend/infra foundation for Brief #11; unblocks #22 and is a prerequisite for turning standing daemons loose safely. ## Context Daemons (even flower-orchestrator/ops) read stale on /roster because check-in depends on the agent REMEMBERING to call `daemon_checkin` each loop. And flower-design deferred "register expected daemon" during spawn-flow-ui because `daemon_agents` has no `expected` state / reconcile seam. ## Scope 1. **Expected-daemon state + reconcile (the core seam).** `daemon_agents` keys on (role, project_id, solo_process_id). Add an `expected` status (e.g. a status enum + nullable/placeholder solo_process_id) representing an intended-but-not-yet-live daemon. Make `daemon_checkin` **RECONCILE**: when a real check-in arrives matching an expected (role, project) with no/placeholder process id, UPGRADE that row to live rather than creating a phantom duplicate. No orphan phantom rows. Provide a way to register an expected daemon — a `DaemonRosterService` method + an MCP tool (`daemon_register_expected`, or extend `daemon_checkin` with an `expected` flag), matching the existing daemon-tool shape. This is what /roster's spawn UI (Brief #22) calls to show "spawning…" before the daemon checks in. 2. **Session-link liveness backstop (extend 28ddf12).** Roster liveness already falls back to linked-session activity (`effectiveLastActivity`). Extend so `context_size` / `needs_compaction` degrade GRACEFULLY when only session-link data exists (no explicit check-in) — surface "unknown" cleanly rather than alarming/false states. 3. **Reliable heartbeat convention (flag, don't necessarily build).** The durable fix for "agent forgets to check in" is to make `daemon_checkin` the FIRST action of every charter-loop iteration (+ optionally a repeating Solo timer the daemon arms once). The charter wording lives in `SpawnPacketService`/`AgentConventions` and is ORCHESTRATOR-owned — so just **flag a recommended wording in brief_append**; focus your build on (1) + (2). ## Acceptance - An "expected" daemon can be registered and shows on the roster as expected/spawning; a subsequent real `daemon_checkin` reconciles it to live (no phantom/duplicate row). Regression test. - Roster liveness + health fields degrade gracefully with only session-link data. Test. - Keep `php artisan test` green + pint clean. `Brief: #26` trailer on commits; `brief_append` when done (and `brief_dispatch_complete` if that tool is in your MCP set). ## Provenance Extracted from operator brief #25 (item 1) + flower-design spawn-flow-ui deferral; folded into Brief #11. Unblocks Brief #22 (/roster spawn+monitor).

    agent · flower-orchestrator
  10. note added 4d ago

    Extracted from operator brief #25 (item 1) + the flower-design spawn-flow-ui "expected daemon" deferral. Backend/infra foundation for Brief #11; unblocks the /roster spawn+monitor UI (Brief #22).

    agent · flower-orchestrator
  11. participant joined 4d ago
    system · flower-orchestrator

epic · dependencies

Relationships

epic parent

depends on

No dependencies — dispatchable once planned.

agents · waves

Participants

  • flower-orchestrator participant · active
  • agent:flower-foundation-981 participant · active
  • system:commit-trailer participant · active

trace · graph

Links

  • Commit #1170 execution

scope

Projects

  • flower · primary

dogfood · read-only

Agent’s-eye view

The literal recall_brief payload an agent gets — same service path as the MCP tool.