flower
/
All briefs
complete draft note flower

Fix spawn-packet + conventions to use the self-resolving check-in COMMAND, not bare daemon_checkin (null ids)

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.

#17 done fresh flower · flower/89-null-ids
agent: claude
You are being dispatched from flower Brief #89: Fix spawn-packet + conventions to use the self-resolving check-in COMMAND, not bare daemon_checkin (null ids)

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

Target:
- project: flower (/Users/mikeferrara/Documents/code/flower)
- branch: flower/89-null-ids
- worktree: not specified
- kind: fresh

Current brief spec:
## Problem
Packet-spawned daemons check in with `solo_process_id=null` + `session_id=null` (→ null context, no roster correlation). Root cause is that the spawn kickoff and the conventions steer agents to the bare `daemon_checkin` MCP tool, which cannot self-resolve identity.

**Why:** The `flower:daemon-checkin` COMMAND works because it runs `php artisan` INSIDE the agent's Solo PTY, so it reads `SOLO_PROCESS_ID` + `CLAUDE_CODE_SESSION_ID` from the agent's env. The `daemon_checkin` MCP TOOL is handled by the flower MCP **server** process (a separate long-running process), which does NOT have the calling agent's env — so it has no ids to resolve unless the caller passes them.

## Confirmed source sites
1. `app/Services/Daemons/SpawnPacketService.php:130-132` — kickoff step 4 (both `$hold` and non-hold variants) says: "have the agent run daemon_checkin with role=…, project=…, and its actor_ref". This is the bare tool.
2. `app/Support/AgentConventions.php:35` — heartbeat line: "…by running the check-in command (`/flower-checkin …` / `php artisan flower:daemon-checkin`) **or** daemon_checkin with role, project, current_cadence, and actor_ref. Identity (solo_process_id, session_id) resolves from your environment…". The env-resolution clause reads as if it applies to the bare tool too. It does not.

## Fix (required)
1. **SpawnPacketService kickoff (lines 131-132):** replace the bare-tool instruction with the COMMAND, e.g.:
   - hold: `4. Kickoff: have the agent run \`php artisan flower:daemon-checkin --role={role} --actor-ref={actor_ref} --cadence={cadence}\` (the COMMAND self-resolves solo_process_id + session_id from its env — do NOT call the bare daemon_checkin MCP tool, which cannot), then remain on HOLD. Do not arm a recurring loop/timer until the operator explicitly enables it.`
   - non-hold: same command, then "start its recurring loop/timer from the charter."
   - Use the role/project/actor_ref/cadence already in scope. Pick a sensible default cadence (e.g. the charter's initial cadence, or `fast`).
2. **AgentConventions.php:35 (heartbeat line):** disambiguate. Make it explicit that ONLY the check-in command self-resolves ids from env; if an agent calls the `daemon_checkin` MCP tool directly it MUST pass `solo_process_id` + `session_id` itself. Prefer the command. Keep it one/two lines, consistent with the surrounding `daemonLines()` voice.

## Fix (stretch / needs-design — do NOT block on this)
3. Optionally make the `daemon_checkin` MCP tool self-heal when ids are omitted. NOTE: the MCP server does not see the caller's env, so this is NOT a simple `env()` read — it requires the MCP transport/session to carry caller identity (e.g. a per-connection Solo-process binding). Capture as a follow-up; the 1+2 fix resolves the live bug.

## Tests
- SpawnPacketService test: assert the rendered packet's kickoff step contains `php artisan flower:daemon-checkin` (command) and does NOT instruct the bare `daemon_checkin` tool for identity. Cover both hold + non-hold.
- AgentConventionsTest: assert the heartbeat line makes the command the identity-resolving path (e.g. no longer implies the bare tool self-resolves).

## Constraints
- Isolated worktree + sqlite tests; keep `php artisan test` green; run `./vendor/bin/pint`.
- Small, correctness-affecting, docs/string-level. Every packet-spawned daemon hits this until fixed.
- Add commit trailer `Brief: #89`.

Recent/key trace events:
[1] participant_joined flower-orchestrator: (no body)
[2] note_added flower-orchestrator: Real bug hit by the freshly-spawned flower-refine daemon (2026-07-03): its daemon_checkin came through with solo_process_id=null + session_id=null (and thus null context, no correlation). Root cause: the spawn packet's kickoff (SpawnPacketService) step 4 tells the agent to "run daemon_checkin with role, project, and actor_ref" — i.e. the bare `daemon_checkin` MCP TOOL, which does NOT auto-resolve identity from env. Only the check-in COMMAND (`flower:daemon-checkin` / `/flower-checkin`, Brief #67) reads SOLO_PROCESS_ID + CLAUDE_CODE_SESSION_ID from env. Compounding it, the AgentConventions heartbeat line is ambiguous: "…by running the check-in command (…) or daemon_checkin with role, project, current_cadence, and actor_ref. Identity (solo_process_id, session_id) resolves from your environment…" — which reads as if the bare daemon_checkin tool also self-resolves. It does not. Fix: (1) SpawnPacketService kickoff → instruct the daemon to run the COMMAND (`php artisan flower:daemon-checkin --role=<role> --actor-ref=<actor> --cadence=<cadence>`), not the bare MCP tool. (2) AgentConventions daemon block → make crystal clear that ONLY the command self-resolves ids from env; if you must call the daemon_checkin MCP tool directly you MUST pass solo_process_id + session_id yourself (or better, always use the command). (3) Optional hardening: have the daemon_checkin MCP tool itself fall back to env resolution when ids are omitted, so both paths behave the same. Small, correctness-affecting; every packet-spawned daemon hits this until fixed.
[3] plan_proposed flower-orchestrator: ## Problem
Packet-spawned daemons check in with `solo_process_id=null` + `session_id=null` (→ null context, no roster correlation). Root cause is that the spawn kickoff and the conventions steer agents to the bare `daemon_checkin` MCP tool, which cannot self-resolve identity.

**Why:** The `flower:daemon-checkin` COMMAND works because it runs `php artisan` INSIDE the agent's Solo PTY, so it reads `SOLO_PROCESS_ID` + `CLAUDE_CODE_SESSION_ID` from the agent's env. The `daemon_checkin` MCP TOOL is handled by the flower MCP **server** process (a separate long-running process), which does NOT have the calling agent's env — so it has no ids to resolve unless the caller passes them.

## Confirmed source sites
1. `app/Services/Daemons/SpawnPacketService.php:130-132` — kickoff step 4 (both `$hold` and non-hold variants) says: "have the agent run daemon_checkin with role=…, project=…, and its actor_ref". This is the bare tool.
2. `app/Support/AgentConventions.php:35` — heartbeat line: "…by running the check-in command (`/flower-checkin …` / `php artisan flower:daemon-checkin`) **or** daemon_checkin with role, project, current_cadence, and actor_ref. Identity (solo_process_id, session_id) resolves from your environment…". The env-resolution clause reads as if it applies to the bare tool too. It does not.

## Fix (required)
1. **SpawnPacketService kickoff (lines 131-132):** replace the bare-tool instruction with the COMMAND, e.g.:
   - hold: `4. Kickoff: have the agent run \`php artisan flower:daemon-checkin --role={role} --actor-ref={actor_ref} --cadence={cadence}\` (the COMMAND self-resolves solo_process_id + session_id from its env — do NOT call the bare daemon_checkin MCP tool, which cannot), then remain on HOLD. Do not arm a recurring loop/timer until the operator explicitly enables it.`
   - non-hold: same command, then "start its recurring loop/timer from the charter."
   - Use the role/project/actor_ref/cadence already in scope. Pick a sensible default cadence (e.g. the charter's initial cadence, or `fast`).
2. **AgentConventions.php:35 (heartbeat line):** disambiguate. Make it explicit that ONLY the check-in command self-resolves ids from env; if an agent calls the `daemon_checkin` MCP tool directly it MUST pass `solo_process_id` + `session_id` itself. Prefer the command. Keep it one/two lines, consistent with the surrounding `daemonLines()` voice.

## Fix (stretch / needs-design — do NOT block on this)
3. Optionally make the `daemon_checkin` MCP tool self-heal when ids are omitted. NOTE: the MCP server does not see the caller's env, so this is NOT a simple `env()` read — it requires the MCP transport/session to carry caller identity (e.g. a per-connection Solo-process binding). Capture as a follow-up; the 1+2 fix resolves the live bug.

## Tests
- SpawnPacketService test: assert the rendered packet's kickoff step contains `php artisan flower:daemon-checkin` (command) and does NOT instruct the bare `daemon_checkin` tool for identity. Cover both hold + non-hold.
- AgentConventionsTest: assert the heartbeat line makes the command the identity-resolving path (e.g. no longer implies the bare tool self-resolves).

## Constraints
- Isolated worktree + sqlite tests; keep `php artisan test` green; run `./vendor/bin/pint`.
- Small, correctness-affecting, docs/string-level. Every packet-spawned daemon hits this until fixed.
- Add commit trailer `Brief: #89`.
[4] status_change flower-orchestrator: (no body)

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; as your final dispatched-worker step, call brief_dispatch_complete with dispatch_request_id (or brief_id) and actor_ref.
- Codex workers should verify mutating Flower tools with tool_search query `brief_append brief_dispatch_complete flower_feedback` (limit 20) when tool availability is in doubt; report raw SEE/LOAD vs NOT visible instead of silently using local fallbacks.
- Add a git commit trailer `Brief: #89` 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 2d ago
    agent · system:commit-trailer
  2. participant joined 2d ago
    system · system:commit-trailer
  3. note added 2d ago

    MERGED to master on MAIN by orchestrator daemon 10 (proc 996). Worker 997 (flower-89-worker, flower-wt1) built commit 11af1aa on flower/89-null-ids (merge-base = fce0ee9, current master incl. #86 — clean single-commit merge, no conflicts). Merged --no-ff as 449a2e8. Fixes 1+2 shipped: SpawnPacketService kickoff (hold + non-hold) emits the self-resolving COMMAND (php artisan flower:daemon-checkin --role/--actor-ref/--cadence, actor_ref via DaemonActorRef::canonical, cadence=fast) with an explicit "not the bare MCP tool" note; AgentConventions heartbeat line disambiguated (only the command self-resolves ids). Stretch item 3 correctly out of scope. Tests green on MAIN (SpawnPacketServiceTest + SpawnDaemonBridgeTest + AgentConventionsTest); worker ran full suite 632 pass/1 skip, pint clean. No migration/config/job change → no config:clear or horizon:terminate. Worker 997 closed.

    agent · flower-orchestrator
  4. status change 2d ago
    agent · flower-89-worker
  5. dispatched 2d ago

    Dispatch request #17 marked done.

    agent · flower-89-worker
  6. note added 2d ago

    Implemented required fixes 1+2 (stretch item 3 left out of scope) on branch flower/89-null-ids. - app/Services/Daemons/SpawnPacketService.php: kickoff step 4 (both hold + non-hold) now instructs the self-resolving COMMAND `php artisan flower:daemon-checkin --role=<role> --actor-ref=<actor_ref> --cadence=fast`, with an explicit note NOT to use the bare daemon_checkin MCP tool for identity (the MCP server has no access to the agent's env → null ids). actor_ref is derived via DaemonActorRef::canonical(role, project->slug) so it renders the canonical `flower-<role>` / `<project-slug>-<role>` ref; cadence defaults to `fast` (matches the command's own default). - app/Support/AgentConventions.php: heartbeat line disambiguated — ONLY the check-in COMMAND self-resolves solo_process_id + session_id from the Solo PTY env; a direct daemon_checkin MCP call MUST pass those ids itself or it checks in with null ids. Kept daemonLines() voice. Tests: added test_kickoff_uses_self_resolving_checkin_command_not_bare_tool (hold + non-hold) and test_kickoff_command_uses_project_scoped_actor_ref in SpawnPacketServiceTest; updated the existing packet assertion; added test_heartbeat_line_makes_the_command_the_identity_resolving_path in AgentConventionsTest; updated the SpawnDaemonBridge hold-packet assertion. `php artisan test` green (632 passed, 1 pre-existing skip, sqlite in worktree); pint clean on changed files. Commit: 11af1aafd9e699ae8eabebb2322f44e80f0fc9b4 (trailer `Brief: #89`). Not merged/pushed — orchestrator owns MAIN merge.

    agent · flower-89-worker
  7. participant joined 2d ago
    system · flower-89-worker
  8. dispatched 2d ago

    Dispatch request #17 queued for flower.

    agent · flower-orchestrator
  9. status change 2d ago
    agent · flower-orchestrator
  10. status change 2d ago
    agent · flower-orchestrator
  11. plan proposed 2d ago

    ## Problem Packet-spawned daemons check in with `solo_process_id=null` + `session_id=null` (→ null context, no roster correlation). Root cause is that the spawn kickoff and the conventions steer agents to the bare `daemon_checkin` MCP tool, which cannot self-resolve identity. **Why:** The `flower:daemon-checkin` COMMAND works because it runs `php artisan` INSIDE the agent's Solo PTY, so it reads `SOLO_PROCESS_ID` + `CLAUDE_CODE_SESSION_ID` from the agent's env. The `daemon_checkin` MCP TOOL is handled by the flower MCP **server** process (a separate long-running process), which does NOT have the calling agent's env — so it has no ids to resolve unless the caller passes them. ## Confirmed source sites 1. `app/Services/Daemons/SpawnPacketService.php:130-132` — kickoff step 4 (both `$hold` and non-hold variants) says: "have the agent run daemon_checkin with role=…, project=…, and its actor_ref". This is the bare tool. 2. `app/Support/AgentConventions.php:35` — heartbeat line: "…by running the check-in command (`/flower-checkin …` / `php artisan flower:daemon-checkin`) **or** daemon_checkin with role, project, current_cadence, and actor_ref. Identity (solo_process_id, session_id) resolves from your environment…". The env-resolution clause reads as if it applies to the bare tool too. It does not. ## Fix (required) 1. **SpawnPacketService kickoff (lines 131-132):** replace the bare-tool instruction with the COMMAND, e.g.: - hold: `4. Kickoff: have the agent run \`php artisan flower:daemon-checkin --role={role} --actor-ref={actor_ref} --cadence={cadence}\` (the COMMAND self-resolves solo_process_id + session_id from its env — do NOT call the bare daemon_checkin MCP tool, which cannot), then remain on HOLD. Do not arm a recurring loop/timer until the operator explicitly enables it.` - non-hold: same command, then "start its recurring loop/timer from the charter." - Use the role/project/actor_ref/cadence already in scope. Pick a sensible default cadence (e.g. the charter's initial cadence, or `fast`). 2. **AgentConventions.php:35 (heartbeat line):** disambiguate. Make it explicit that ONLY the check-in command self-resolves ids from env; if an agent calls the `daemon_checkin` MCP tool directly it MUST pass `solo_process_id` + `session_id` itself. Prefer the command. Keep it one/two lines, consistent with the surrounding `daemonLines()` voice. ## Fix (stretch / needs-design — do NOT block on this) 3. Optionally make the `daemon_checkin` MCP tool self-heal when ids are omitted. NOTE: the MCP server does not see the caller's env, so this is NOT a simple `env()` read — it requires the MCP transport/session to carry caller identity (e.g. a per-connection Solo-process binding). Capture as a follow-up; the 1+2 fix resolves the live bug. ## Tests - SpawnPacketService test: assert the rendered packet's kickoff step contains `php artisan flower:daemon-checkin` (command) and does NOT instruct the bare `daemon_checkin` tool for identity. Cover both hold + non-hold. - AgentConventionsTest: assert the heartbeat line makes the command the identity-resolving path (e.g. no longer implies the bare tool self-resolves). ## Constraints - Isolated worktree + sqlite tests; keep `php artisan test` green; run `./vendor/bin/pint`. - Small, correctness-affecting, docs/string-level. Every packet-spawned daemon hits this until fixed. - Add commit trailer `Brief: #89`.

    agent · flower-orchestrator
  12. note added 2d ago

    Real bug hit by the freshly-spawned flower-refine daemon (2026-07-03): its daemon_checkin came through with solo_process_id=null + session_id=null (and thus null context, no correlation). Root cause: the spawn packet's kickoff (SpawnPacketService) step 4 tells the agent to "run daemon_checkin with role, project, and actor_ref" — i.e. the bare `daemon_checkin` MCP TOOL, which does NOT auto-resolve identity from env. Only the check-in COMMAND (`flower:daemon-checkin` / `/flower-checkin`, Brief #67) reads SOLO_PROCESS_ID + CLAUDE_CODE_SESSION_ID from env. Compounding it, the AgentConventions heartbeat line is ambiguous: "…by running the check-in command (…) or daemon_checkin with role, project, current_cadence, and actor_ref. Identity (solo_process_id, session_id) resolves from your environment…" — which reads as if the bare daemon_checkin tool also self-resolves. It does not. Fix: (1) SpawnPacketService kickoff → instruct the daemon to run the COMMAND (`php artisan flower:daemon-checkin --role=<role> --actor-ref=<actor> --cadence=<cadence>`), not the bare MCP tool. (2) AgentConventions daemon block → make crystal clear that ONLY the command self-resolves ids from env; if you must call the daemon_checkin MCP tool directly you MUST pass solo_process_id + session_id yourself (or better, always use the command). (3) Optional hardening: have the daemon_checkin MCP tool itself fall back to env resolution when ids are omitted, so both paths behave the same. Small, correctness-affecting; every packet-spawned daemon hits this until fixed.

    agent · flower-orchestrator
  13. participant joined 2d ago
    system · flower-orchestrator

epic · dependencies

Relationships

epic parent

depends on

No dependencies — dispatchable once planned.

agents · waves

Participants

  • flower-orchestrator participant · active
  • flower-89-worker participant · active
  • system:commit-trailer participant · active

trace · graph

Links

  • Commit #1624 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.