Skip to content

Workflow monitoring

Workflow monitoring exposes current actionable conditions through two read-only delivery shapes. shipper monitor is a continuous stream; the MCP shipper_monitor tool returns one complete current snapshot per independent call. Both serialize the same versioned core records. Monitoring never recovers a store, settles a dead run, changes an issue, repairs a mirror, or starts a daemon.

Monitoring complements rather than replaces shipper status, shipper_status, issue listing and detail, run history, and store-level inspection. Use those lower-level surfaces for orientation and for the underlying records; use monitoring when a caller needs a current baseline or an ordered stream of changes.

The ten default-visible classes are:

ClassDurable authority
bouncedThe latest unsuperseded, non-reset rejected stage_transition.
failedCurrent failed issue state or a durable failed transition.
blockedCurrent blocked issue state plus its latest live block reason.
awaiting_answersA valid saved Groom question session, including its owner and session identity but no question or answer content.
completedA new completed tombstone, or the current terminal state when that issue is explicitly filtered.
orphanedThe latest relevant durable background-run outcome recorded as orphaned.
undispatchableOne repository condition for an engaged safety stop intersected with all otherwise dispatchable issues.
blockers_satisfiedA still-blocked issue whose nonempty blocker set resolves entirely to completed tombstones.
mirror_pendingThe latest currently repairable pending mirror-state fact when issue mirroring is enabled.
closed_not_plannedA new not-planned tombstone, or the current terminal state when that issue is explicitly filtered.

progress is an eleventh, opt-in class. --include-progress or include_progress: true adds the current stage to a fresh baseline. Continuous CLI streams and named watches also include later accepted forward stage movement. Monitoring does not treat prior success, elapsed time, a quiet process, transcript content, or a zero exit by itself as progress.

In store-only mode, mirror_pending is omitted completely. In mirror-on mode it reports only currently repairable pending debt and omits clear or currently non-repairable debt. If later state makes that debt repairable, the condition re-enters. Store-only repositories emit no mirror condition.

A new capture first freezes the durable source heads, resolves any issue filter, and emits all and only active default-visible conditions with baseline: true. A positive completed or not-planned tombstone is the terminal authority: a fresh repository baseline omits that issue’s old completion or not-planned record and every retained pre-closure workflow condition. Absence from active snapshots is unknown, not inferred closure; an issue missing from both snapshots and tombstones is an authoritative inconsistency. An explicitly filtered terminal issue still returns its current completed or closed_not_planned baseline and may independently include current repairable mirror debt, with no retained pre-closure workflow condition presented as active. A failed filter never broadens to the repository. A running CLI or named-watch attachment continues to emit the durable terminal occurrence and mark every previously observed standing workflow condition resolved.

An engaged safety stop that affects no otherwise dispatchable issue appears once in batch.context.suppression with impacting: false. It is context, not an event, and does not change the classified outcome.

Durable edge records, including workflow transitions and new tombstones, have lifecycle occurred. Standing conditions in CLI streams and named watches use attachment-relative lifecycle:

  • entered when that attachment first observes a new condition;
  • updated only when material durable facts change; and
  • resolved when a condition previously observed by that attachment is no longer active.

Unchanged standing conditions do not repeat. Observation time and age alone are not material. A condition that begins and ends wholly between two stream or watch polls is intentionally absent: monitoring does not persist shadow history to manufacture an edge it never observed. Eligibility has no age cutoff: elapsed time neither expires a genuinely current condition nor keeps a resolved condition active. A separate CLI or watch attachment gets a fresh baseline rather than inheriting another attachment’s lifecycle. Each unnamed MCP call is likewise a fresh snapshot with no inherited lifecycle. Lower-level shipper status, shipper_status, issue listing and detail, run history, and store-level inspection continue to expose retained historical facts without treating them as active fresh-baseline conditions.

Ordering, deduplication, and compatibility

Section titled “Ordering, deduplication, and compatibility”

CLI and named-watch continuation verifies the retained byte prefix of every workflow log before decoding its complete suffix. Records are ordered deterministically while preserving order within each source file. Shipper coalesces facts only through persisted identities such as eventId, mirrorGroupId, sessionId, or explicit persisted linkage; timestamps and similar prose are never deduplication keys. This is why a successful process exit linked to a rejected backward transition remains one bounced result.

Every required issue-state, workflow-log, background-run, and question-session source is captured twice. If bytes continue changing or an operation manifest remains in flight after three attempts, the observation returns a complete integrity failure without records and, for a continuing CLI or watch attachment, without advancing the prior checkpoint. A malformed record, unsupported future schema, changed/truncated retained prefix, or unknown strict event is likewise fail-complete. An unsupported future schema names updating the installed Shipper as its remedy.

Material authoritative contradictions, such as active and terminal identity at once, a latest installed transition that disagrees with issue state without a later state edge, or a retained current-condition claim whose local ID is absent from both snapshots and tombstones, return one empty consistency-failure batch containing both facts. Monitoring never guesses an issue event, never infers closure from snapshot absence, and never returns partial records around such a conflict.

Issue filters use the standard local-only grammar: case-insensitive S#<id>, gh:<number>, #<number>, a bare local ID with mapped-GitHub fallback, or a same-repository GitHub issue URL. Active records and tombstones are both eligible. Unknown, malformed, pull-request, or foreign-repository references fail as scoped errors before any baseline record is emitted.

Each workflow record carries ordered source facts, a recovery assessment, liveness when applicable, and either a catalog-backed recommended action or an evidence-based reason that no action can be recommended. Saved Groom sessions expose only identity, owner or explicit unowned state, session ID, answered-round count, pending-batch state, and timestamps. Question text, answers, notes, transcripts, and pull-request overlap payloads are excluded.

Recommended actions are emitted only when all required issue, session, blocker, reset-target, or repairability context is present. Shipped actions cover terminal Groom answers, MCP answer/resume Groom, unblock, safety-stop resume, mirror repair, recover, and targeted reset. Monitoring does not invent generic command prose when that evidence is missing.

Recovery is one of:

  • resume when a current saved session, preserved local change, or exact verified stage continuation proves completed work can be reused;
  • restart when a durable failure has no continuation and a valid reset target exists;
  • none for completed or otherwise self-resolving outcomes; or
  • unknown when the evidence cannot support another conclusion.

Liveness is independent from workflow outcome. A fresh positive PID probe means running; ESRCH or a matching durable terminal record means not_running; stale, missing, indeterminate, or conflicting evidence means unknown; and no in-flight attempt means not_applicable. Every liveness fact records its signal, observation and effective times, age, freshness, and conclusion. A standalone liveness record is emitted only after the same CLI or named-watch attachment observes a supported material change.

Quiet output, low CPU, elapsed time, caller timeout, idle supervision, quiescence, a prior successful stage, exit status alone, and transcript content never prove process death or resumability.

Use MCP shipper_get_issue_runtime to poll one complete, read-only snapshot for a resolvable managed issue in the active repository. The inspection reads local state only, never consults GitHub, and is separate from ordinary shipper_get_issue detail. It supports open and closed records and preserves field-level uncertainty instead of failing the whole read when one observation is unavailable.

Each snapshot has exactly one assessment:

  • healthy_active means an alive, non-stopped worker has affirmative current activity;
  • intentional_wait covers verification-capacity queueing, protected GitHub retry, checks, review, operator pause, and awaiting-answer states;
  • stopped covers a dead issue-lock holder, zombie or PID-reused holder, or a live kernel-stopped holder, with liveness and OS state kept separate so recovery conditions remain distinguishable;
  • suspected_hang is a cautious compound observation, never a definitive hung-worker claim;
  • indeterminate means required evidence is unavailable or conflicting; and
  • not_applicable is limited to closed issues and ownerless open issues without a recognized wait.

The primary phase is exactly one of agent_turn, verification, verification_queue_wait, github_retry_wait, checks_wait, review_wait, awaiting_operator, finalizing, unknown, and none. Current-run verification and specific wait evidence take precedence over finalization and generic agent activity. Different simultaneous specific phases produce unknown and identify every conflicting source. Completed, expired, malformed, and prior-run records cannot select a phase. A dead holder uses none; a live stopped worker retains a specific phase only while its authoritative current-run source remains active.

The suspected-hang threshold is the domain constant 600_000 ms and is independent of agentTimeoutMinutes. Suspicion requires every condition at capture time: the current lock holder is alive and not OS-stopped; no current verification window, protected GitHub retry wait, or checks wait applies; the worker shows no runnable or CPU activity; no active descendant exists at any depth; and the active model agent’s own transcript has not changed for at least ten minutes. Missing or conflicting evidence yields indeterminate. Descendant sampling follows the full process tree; live Git work and correlated verification-hook work count as activity, while an idle model child does not count merely because it exists.

Worker identity always means the issue-lock-holding ship or stage worker. For daemon dispatch this is the forked worker, not the daemon. It is never the MCP server, supervisor, or model-agent child. Spawn time, semantic Shipper version, and immutable build identity are reported independently, so a source-tree worker can have known spawn/version values and unknown build identity. Worker liveness, OS process state, descendant activity, and transcript freshness likewise retain separate unknown or not-applicable reasons. On macOS T/t is an actual kernel stopped state. App Nap, ordinary sleep, low CPU, reduced priority, or elapsed time is not classified as OS-stopped.

This capability is current-only: snapshots are never retained as liveness or phase history. Once ownership ends, the response reports no current worker, explains why, and names shipper_get_session_logs for post-mortem session history.

On macOS, Shipper automatically prevents idle system sleep while qualifying work can make progress without a new operator setup step. The same default applies on battery and external power, across Terminal, Desktop, MCP, and daemon dispatch.

The qualifying set is New, assisted setup (setup --assist), Groom, direct stages, Next, Ship/Auto-ship, recovery, Unblock, and Merge. Deterministic bare setup, deprecated init, read-only inspection, configuration-only and administrative commands, queued work that has not begun, and an idle resident daemon do not activate protection.

Protection starts with progress-capable preparation, including Desktop New Issue validation before an agent starts. It remains through automated waits that can resume without input: agent responses, retries, quota recovery, pending checks, review/rework transitions, drain-to-checkpoint work, failure reporting, and cleanup. It ends only after that work settles or becomes non-progress-capable.

Observable headless or orchestrated operator-answer waits, explicit operator pauses, durable halts, recovery-only quiescence, and durable resumable sessions without a progress-capable worker do not hold protection. When an answer or resume makes work progress-capable again, Shipper makes a fresh best-effort protection attempt before the resumed active period continues. Attended New, Groom, and assisted-setup sessions keep their current continuous native question experience, and protection stays active for the lifetime of that live session, including while the native agent may be waiting for an operator answer.

Concurrent qualifying runs keep the Mac protected until the last progress-capable run settles or enters a non-qualifying state. Successful acquire, retain, restore, and release add no routine operator-facing output. Failure is best-effort: otherwise healthy work continues, restoration retries while work still qualifies, and one continuous machine-wide loss episode produces at most one warning. That warning is discoverable through affected workflows’ normal status or history surfaces and persistent logs. It states that idle sleep may interrupt active work and advises keeping the Mac awake or otherwise preventing idle sleep until protection returns or the work finishes. After restoration or a period with no qualifying work, a later distinct episode may warn again.

The product promise is limited to idle system sleep on macOS. Shipper does not prevent or promise to prevent display sleep, screen lock, explicit Sleep, lid-close sleep, shutdown, restart, or safety-driven power events. Non-macOS platforms keep their existing behavior and output, with no equivalent sleep-prevention requirement.

Each batch is classified as clean, workflow_regression, human_action_required, or monitor_integrity_failure. Bounce, failure, and orphaning are regressions. Blocking, awaiting answers, impacting suppression, satisfied blockers, incompatible terminal targets, and repairable operator-action mirror debt require human action. Non-impacting suppression remains context only.

The CLI supports three stop policies:

  • never observes continuously;
  • stage_end stops when a filtered issue cleanly moves forward from its attachment stage or reaches merge-attributable completion, and also stops for any settled adverse condition; and
  • merge continues through recoverable nonterminal conditions but stops on regression, closed_not_planned, or completion not attributable to pr_association.source: merge_completed.

stage_end and merge require one issue filter. A target can remain reached while mirror_pending is present: operator-action repairability may change the outcome family without undoing the reached stage or merge target. Currently non-repairable or clear mirror debt is omitted rather than retained as a non-failing condition.

CLI exit codes are stable: 0 for target reached or operator interrupt, 2 for workflow regression, 3 for required human action, and 4 for monitor integrity or continuation failure. Commander argument and usage errors retain exit 1. JSONL writes one versioned attachment line, each canonical event unchanged, and one versioned result line; empty polls write nothing.

Every shipper_monitor call makes a new stable capture and returns the entire applicable current baseline in one response. Omit issue for repository scope or provide one valid issue reference for a complete single-issue snapshot. Set include_progress: true to include current ordinary progress; omitting it excludes ordinary progress without changing any other record class or meaning.

Unnamed MCP calls are independent. The tool retains no cursor, provides no pagination or replay, never waits, and does not derive a later response from an earlier call. Use shipper monitor --jsonl for continuous, replay-oriented, or waiting workflows.

Invalid issue or repository-path inputs are correctable caller errors that identify the offending input. A genuine monitoring-integrity failure remains fail-complete: it returns an error batch with no partial baseline records.