flower
/
All briefs
complete draft note flower
epic · Notes for the /roster view: 1) Let's add a 'Go to r...

Roster: ensure daemons — 'Water the garden' / 'And there was light'

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.

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. status change 2d ago
    agent · flower-orchestrator
  4. note added 2d ago

    Build complete on branch flower/roster-ensure-daemons — commit e66f683 (Brief: #72 trailer). NOT merged, NOT pushed (orchestrator merges). Full suite: php artisan test → 598 passed, 1 skipped (pre-existing), 0 failures, 4072 assertions. Pint clean. HOW I USED THE EXISTING INFRA: - SpawnDaemonBridge: EnsureDaemons calls bridge->spawn(role, project, actorRef, confirmed: true) per MISSING role. I did NOT reimplement spawn — the bridge already does the whole fresh-with-packet flow: plan() → SpawnPacketService::render(hold: true) → safety pre-flight → roster->registerExpected() (creates the Expected daemon row) → solo->spawnAgent(packet). So EnsureDaemons is a thin reconcile loop on top. - SpawnPacketService: used INDIRECTLY via the bridge (bridge.plan calls spawnPackets->render with hold:true). FRESH-WITH-PACKET SEED SHAPE (assumption, verified from SpawnPacketService::packet + the bridge test): the agentArgs[0] handed to Solo is a text "Daemon spawn packet: <role> for <project>" block — Target (project/root_path/role/charter template), Solo spawn instructions (spawn_agent, paste charter, readiness/whoami), a HOLD kickoff line ("run daemon_checkin with role=…, project=…, then remain on HOLD; do not arm a loop/timer until the operator enables it"), a replace-handoff pointer line, then the filled charter body. This is the recall_resume/HANDOFF-style seed the brief locked — NOT a dead-PTY resume. I did not change the packet shape. - DaemonRegisterExpected: it registers ONE expected daemon per call and does NOT model a per-scope expected SET, so the expected SETS live in config (config/flower.php ensure.*, LOCKED: default {orchestrator, refine}; platform/flower {orchestrator, ops, refine}). The expected-daemon ROW is still created through registerExpected — inside bridge->spawn — when we actually spawn. So the register-expected mechanism is used, just not as the set-of-roles source. MAPPING / RECONCILE RULE: - "present" (skip) = a daemon of that role in the scope whose liveness ∈ {alive, expected} (expected = already spawning, so re-running Water is a no-op). Missing = no daemon or only stale/dead → spawn fresh. - expectedRolesFor(project): platform_roles if project.slug === ensure.platform_project (flower), else default_roles. FILES (8, +791/-140): - config/flower.php — ensure.{default_roles, platform_project, platform_roles}. - app/Services/Daemons/EnsureDaemons.php (new) — ensure()/expectedRolesFor(); returns {project, expected, spawned, skipped, errors}; per-role try/catch (DaemonSpawnSafetyException/SoloCliException/InvalidArgumentException) so one blocked role doesn't abort the rest. - app/Livewire/Roster/Index.php — waterTheGarden(projectId), andThereWasLight() (resolves flower via config), removeDaemon(id); ensureResult/ensureNotice banner state; render() partitions flower (platform) out of the grid, reusing the projects query (no extra query — /roster audit stays bounded at 5). - resources/views/livewire/roster/index.blade.php — status banner + platform band ("And there was light", expected-roles line, empty-state seeds copy) + per-card "Water the garden" button + grid empty-state gated on both platform & grid empty; grid row now @includes the shared partial. - resources/views/livewire/roster/partials/daemon-row.blade.php (new) — extracted daemon row shared by grid + platform band; the old disabled "Retire" stub is now a live "Remove dead daemon" button for dead/stale rows (wire:confirm), disabled stub otherwise. - tests: EnsureDaemonsTest (spawn missing + skip live, platform set incl ops, idempotent over expected-status, per-role errors captured, config-scoped expected sets); EnsureDaemonsRosterTest (flower renders top-level platform band not a project card, Water/And-there-was-light invoke ensure through the fake bridge, missing-platform notice, remove-dead deletes the row + affordance renders for dead rows); EnsureFakeSpawnBridge (records spawn calls / optional per-role throw — the SpawnDaemonBridge test double; NO live spawn or solo-cli). REMOVE-DEAD SCOPE (assumption to confirm): SoloClient has NO process-close method and the guardrails forbid live process control here, so removeDaemon deletes the daemon_agents ROW only; any attached live Solo process is left for the operator to close. A live daemon simply reappears on its next check-in. If you want in-app process termination, that needs a new SoloClient close/stop method (deferred). DEFERRED / for the orchestrator on MAIN: the LIVE "Water the garden" / "And there was light" execution (real spawns) is yours on MAIN — this worktree only builds + tests with a mocked bridge. Note #72 is marked blocking #76 (daemon self-reset / rolling replacement) — the remove-dead + ensure primitives here are the foundation for that.

    agent · agent:flower-72-worker(proc-992)
  5. participant joined 2d ago
    system · agent:flower-72-worker(proc-992)
  6. link added 2d ago
    agent · flower-orchestrator
  7. status change 2d ago
    agent · flower-orchestrator
  8. parent set 3d ago

    Grouped under epic #66.

    agent · flower-orchestrator
  9. note added 3d ago

    Child of #66, maps notes #3/#4. Reconcile actual→expected daemon roster and spawn missing. Expected sets (LOCKED): regular project = {orchestrator, refine}; flower = {orchestrator, ops, refine} (3). "Water the garden" (per-project button) ensures that project's set; "And there was light" (top-level button) ensures flower's platform set incl ops. Spawn missing daemons FRESH, seeded with a recall_resume/HANDOFF packet (LOCKED — not a dead-PTY resume) via SpawnDaemonBridge (#62); idempotent (no-op if already live). Add a "remove dead/replaced daemon" affordance (LOCKED, from Q2) to clear stale/superseded daemon rows. Layout (LOCKED): DROP flower's project card from the per-project grid; render flower's daemons in a dedicated top-level "platform" band with "And there was light" there. Build on daemon_register_expected if it already models per-scope expected roles. Deps: spawn infra (#62, done). M/L.

    agent · flower-orchestrator
  10. participant joined 3d ago
    system · flower-orchestrator

epic · dependencies

Relationships

depends on

No dependencies — dispatchable once planned.

agents · waves

Participants

  • flower-orchestrator participant · active
  • agent:flower-72-worker(proc-992) participant · active
  • system:commit-trailer participant · active

trace · graph

Links

  • Commit #1602 execution
  • Scratchpad #346 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.