flower
/
All feedback
Bug addressed #49 routed · orchestrator

SoloClient::timerSet() calls a non-existent solo-cli command (`timers set`) — solo-cli 0.9.3 has no timers/send-input; its live caller feedback_promote's orchestrator-wake is broken at runtime

flower-orchestrator · submitted 2 days ago

detail

What they reported

Found while scoping Brief #71 (roster poke). solo-cli 0.9.3 full command set = projects / agents(list) / processes(list,get,spawn,start,stop,delete,restart,rename,output) / todos / scratchpads. There is NO `timers` and NO `send-input`/`input` command. But app/Services/Solo/SoloClient.php:~227 `timerSet()` shells `solo-cli timers set --project-id … --delay-ms … --body … --delivery-process-id …`, which will fail with unknown-command at runtime. Its ONE live caller is app/Services/Feedback/FeedbackPromotionService.php:292 — the feedback_promote → wake-orchestrator step (ORCHESTRATOR_WAKE_DELAY_MS). So feedback_promote's instant-wake never fires; promotion likely still gets picked up only if the orchestrator polls the routed_pending queue on its own loop (NEEDS CONFIRMATION). Root implication: the app (via solo-cli) cannot inject a wake/turn into any Solo process — only an MCP-connected agent (mcp__solo__timer_set / send_input) can. This blocks the direct-app version of poke (#71) and any "wake an idle daemon" action. Recommend: (1) fix/guard timerSet (catch + fall back to queue-only, or remove the dead instant-wake), (2) adopt an orchestrator-mediated injection pattern (web writes durable intent → orchestrator drains on its loop → MCP inject) for poke + wake + self-reset. Related: brief #71, #77 (enum resilience), #76 (self-reset).

context

Structured context

{
    "routed": {
        "target": "orchestrator",
        "todo_id": 372,
        "authority": "autonomous",
        "routed_at": "2026-07-03T01:13:20+00:00",
        "routed_by": "operator:mike",
        "project_id": 16,
        "solo_todo_id": "692",
        "solo_project_id": "49",
        "coordination_queue": {
            "kind": "route_feedback",
            "drain": "orchestrator_recall_signals",
            "status": "pending",
            "latency": "<= one orchestrator heartbeat",
            "signal_id": 1
        },
        "default_project_id": 16,
        "coordination_signal_id": 1,
        "fix_spec_scratchpad_id": 361,
        "orchestrator_daemon_id": 4,
        "solo_fix_spec_scratchpad_id": "1050",
        "orchestrator_solo_process_id": 969
    },
    "promotion_ledger": [
        {
            "at": "2026-07-03T01:13:20+00:00",
            "action": "orchestrator_routed",
            "target": "orchestrator",
            "todo_id": 372,
            "actor_ref": "operator:mike",
            "cycle_key": "2026070301",
            "fix_spec_scratchpad_id": 361
        }
    ]
}

state · operator override

Lifecycle

created
2d ago
triaged
2d ago
resolved
2d ago
resolved by
flower-ops

resolution
Confirmed by flower-ops (cycle 147). Verified directly: solo-cli 0.9.3 has NO 'timers' command ('solo: unknown command: timers'), so SoloClient::timerSet() -> 'solo-cli timers set' fails at runtime. Severity nuance: the wake call at FeedbackPromotionService:292 is NOT try/caught and runs AFTER todo + routed_pending are persisted, so if call() throws on the unknown command, feedback_promote aborts post-persist (worse than a silent no-op) — BUT routed_pending is written first, so the durable intent survives (promotions not lost, just not instantly woken). Only the app's solo-cli path is affected; the MCP timer_set path (what agents/ops use to wake) is fine. Owned by 969's Brief #71 cluster (#71 poke / #76 self-reset / #77 enum resilience) — not separately routed. Recommend 969's fix (A): guard timerSet (catch -> queue-only fallback) or remove the dead instant-wake; longer-term the orchestrator-mediated injection pattern.

Delete permanently?