Skip to content

feat(work-driver): nothing reaches the operator outside the Pi session #388

Description

@randomm

Context

The whole point of /work <issues> is that you fire it and walk away. But when a cycle parks, halts, or stops awaiting a human merge, the only places that say so are the Pi scrollback and a JSON file — both of which require you to already be looking.

grep -rniE 'osascript|terminal-notifier|notify-send|webhook|slack' extension/src/ returns nothing. The GitHub comment + needs-human-attention label exist only on the runHandoff path — not on a queue halt, not on awaiting-human-merge (#380), not on a driver crash.

So the realistic sequence today is: fire /work over eight issues, go to lunch, come back two hours later to find it stopped after twenty minutes on the second group. Everything needed to fix it was known at minute twenty.

What to build

A. One notification hook, configured by the operator. A single command template invoked on terminal states, e.g. PI_ENSEMBLE_NOTIFY_CMD. The driver runs it with the message on stdin (or as $1) and does not care what it is — osascript, terminal-notifier, notify-send, curl to a webhook, say. Choosing the transport is not the harness's job; reaching the human is.

B. Fire on the states that need a human, and only those:

State Message must name
parked (per issue) the issue, the park reason, and the human action
queue halted why continuing was pointless, and the groups not started
awaiting-human-merge (#380) the PR URL
driver crash the state file path

One notification per parked issue, not one per event. A hook that fires on every step is noise, and noise is indistinguishable from no hook at all.

C. Fail open, always. A notification failure must never affect the cycle: no throw, no retry storm, a short timeout, and the failure recorded in the trace only. The hook is an observer, and an observer that can break the thing it observes is worse than none.

D. Say what a human must do that the system cannot. The existing humanActionFor already produces this; the hook should carry it rather than inventing its own wording. "Issue #287 parked" is not actionable; "add acceptance criteria to #287" is.

Acceptance criteria

  • With PI_ENSEMBLE_NOTIFY_CMD unset, behaviour is byte-identical to today (no spawn attempted).
  • A parked group fires exactly one notification whose text contains the issue number and the human action.
  • A hook that exits non-zero, hangs past the timeout, or does not exist leaves the queue outcome unchanged — asserted, not assumed.
  • A hook is never invoked for a cycle that merged cleanly.
  • The command is not built by string-concatenating untrusted issue text into a shell line.
  • Quality gate green per AGENTS.md §1; docs updated (README env table + docs/troubleshooting.md).

Out of scope

A hosted notification service, batching/digest logic, and notifying on non-terminal progress.

This work must ship as its own separate PR, independent of any other open issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions