···6767mill run <program.ts> [--json] [--sync] [--driver <name>] [--executor <name>] [--confirm=false]
6868mill status <runId> [--json]
6969mill wait <runId> --timeout <seconds> [--json]
7070-mill watch <runId> [--json] [--raw]
7070+mill watch [--run <runId>] [--channel events|io|all] [--source driver|program] [--spawn <spawnId>] [--json]
7171mill ls [--json] [--status <status>]
7272-mill inspect <runId>[.<spawnId>] [--json] [--session]
7372mill cancel <runId> [--json]
7474-mill init
7373+mill init [--global]
7574```
76757776Discovery (for humans and agents):
···102101 -> driver (generic)
103102 -> agent process / remote endpoint
104103105105-engine events -> watch/inspect/tui/automation
104104+engine events -> watch/tui/automation
106105```
107106108107All layers are orthogonal:
···530529- readers decode with `Schema.decodeUnknown*`
531530- unknown schema versions are surfaced as typed decode errors
532531533533-Tier 1 is written to `events.ndjson` and is the source for `watch`, `inspect`, and extensions.
532532+Tier 1 is written to `events.ndjson` and is the source for `watch` (events channel), `status`/`wait` terminal checks, and extensions.
534533535534### Tier 1 lifecycle invariants
536535···549548Terminal states have no outgoing transitions.
550549`mill wait` resolves on first observed terminal event and treats additional terminal events as invariant violations.
551550552552-### Tier 2 (raw passthrough, ephemeral)
551551+### Tier 2 (io passthrough, ephemeral)
553552554554-- full raw bytes/text from driver process or remote stream
555555-- available live via `watch --raw`
553553+- line-oriented IO from driver/program streams
554554+- available live via `watch --channel io` (or merged via `watch --channel all`)
556555- not persisted by engine
557556558557---
···687686688687## 15) Observers
689688690690-Observers consume tier-1 stream (and optionally tier-2 live raw stream):
689689+Observers consume tier-1 stream (and optionally tier-2 live io stream):
691690692692-- `mill watch`
693693-- `mill inspect`
691691+- `mill watch --channel events`
692692+- `mill watch --channel io|all`
694693- future TUI/web UI
695694- automation reading NDJSON
696695···698697699698---
700699701701-## 16) `inspect --session`
700700+## 16) Session ownership + pointers
702701703703-`mill inspect <runId>.<spawnId> --session` resolves the spawn `sessionRef` via the originating driver and opens or prints a pointer to full native session history.
702702+Spawn `sessionRef` values are emitted in `spawn:complete` events and summarized in `result.json`.
704703705704Engine never normalizes full transcript ownership.
706705···9139123. Generic process driver + one codec (pi or claude)
9149134. Engine submit/status/wait/watch/cancel
9159145. Worker process + detached `run`
916916-6. `inspect` and `--session` bridge
915915+6. `watch` channel finalization + cancellation bridge
9179167. Extension hooks
9189178. Guardrail toolchain + rules/tests
919918
···308308- readers decode with `Schema.decodeUnknown*`
309309- unknown schema versions are surfaced as typed decode errors
310310311311-Tier 1 is written to `events.ndjson` and is the source for `watch`, `inspect`, and extensions.
311311+Tier 1 is written to `events.ndjson` and is the source for `watch` (events channel), `status`/`wait` terminal checks, and extensions.
312312313313### Tier 1 lifecycle invariants
314314···327327Terminal states have no outgoing transitions.
328328`mill wait` resolves on first observed terminal event and treats additional terminal events as invariant violations.
329329330330-### Tier 2 (raw passthrough, ephemeral)
330330+### Tier 2 (io passthrough, ephemeral)
331331332332-- full raw bytes/text from driver process or remote stream
333333-- available live via `watch --raw`
332332+- line-oriented IO from driver/program streams
333333+- available live via `watch --channel io` (or merged via `watch --channel all`)
334334- not persisted by engine
335335336336## 10) Driver architecture
···460460461461## 15) Observers
462462463463-Observers consume tier-1 stream (and optionally tier-2 live raw stream):
463463+Observers consume tier-1 stream (and optionally tier-2 live io stream):
464464465465-- `mill watch`
466466-- `mill inspect`
465465+- `mill watch --channel events`
466466+- `mill watch --channel io|all`
467467- future TUI/web UI
468468- automation reading NDJSON
469469470470Observers are read-only; they do not mutate engine state.
471471472472-## 16) `inspect --session`
472472+## 16) Session ownership + pointers
473473474474-`mill inspect <runId>.<spawnId> --session` resolves the spawn `sessionRef` via the originating driver and opens or prints a pointer to full native session history.
474474+Spawn `sessionRef` values are emitted in `spawn:complete` events and summarized in `result.json`.
475475476476Engine never normalizes full transcript ownership.
477477
+14-17
docs/exec-plans/active/vertical-slices.md
···284284285285---
286286287287-## S7 — Final Hardening: inspect/session/cancel/watch Semantics (Dedicated Final Slice)
287287+## S7 — Final Hardening: watch/cancel/list Semantics (Dedicated Final Slice)
288288289289**Goal**
290290Complete observer/control semantics for robust long-running orchestration operations.
291291292292**Package span**
293293294294-- core: `watch`, `inspect`, `cancel`, session-ref resolution, interruption-safe cancellation
295295-- cli: `watch`, `inspect [--session]`, `cancel`, `ls`
296296-- driver-pi (and optionally others): `sessionRef` opener/locator bridge for `inspect --session`
294294+- core: `watch` channels (`events` / `io` / `all`), `cancel`, `list`, interruption-safe cancellation
295295+- cli: `watch`, `cancel`, `ls`
296296+- pi-mill runtime: completion replay via `watch --channel events`
297297298298**Acceptance criteria**
299299300300-1. **Test intent:** integration + e2e command matrix across inspect/session/cancel/watch with concurrent runs.
301301-2. `watch --json` emits valid JSONL tier-1 events; `watch --raw` streams tier-2 raw passthrough without persistence.
302302-3. `inspect <runId>[.<spawnId>] --json` returns decoded persisted data; `--session` resolves driver-owned session pointer.
300300+1. **Test intent:** integration + e2e command matrix across watch/cancel/list with concurrent runs.
301301+2. `watch --json` emits valid JSONL envelopes, with `kind: "event"` for tier-1 events and `kind: "io"` for tier-2 stream lines.
302302+3. `watch --channel io` streams passthrough IO without persisting those lines to `events.ndjson`.
3033034. `cancel <runId>` is interruption-safe, idempotent, and no-op for already terminal runs; emits at most one `run:cancelled` terminal event.
3043045. `ls`/`status` remain consistent with terminal invariants and persisted snapshots after cancellations/completions.
305305306306**Deliverables**
307307308308-- Core observer stream and inspect/cancel implementations tied to event log + in-memory fanout.
309309-- CLI command handlers for `watch`, `inspect`, `cancel`, `ls` with strict JSON stdout contract.
310310-- Driver session bridge interface + driver-pi implementation for session lookup/open.
308308+- Core observer stream implementations for event and IO channels tied to event log + in-memory fanout.
309309+- CLI command handlers for `watch`, `cancel`, and `ls` with strict JSON stdout contract.
310310+- pi-mill completion decoding from `watch --channel events` output.
311311312312**Test commands**
313313314314- `bun test packages/core/src/internal`
315315- `bun test packages/core/src/runtime`
316316- `bun test packages/cli/src`
317317-- `bun test packages/driver-pi/src`
318317- `bun test`
319318320320-**Status (2026-02-23)**
319319+**Status (2026-02-26)**
321320322321- ✅ Added core observer fanout hub (`packages/core/src/internal/observer-hub.effect.ts`) and wired engine tier-1/tier-2 publishing to persisted append + in-memory live subscribers.
323323-- ✅ Extended `MillEngine` with `watch`, `watchRaw`, `inspect`, `cancel`, and `list` semantics.
322322+- ✅ Extended `MillEngine` with `watch`, `watchIo`, `cancel`, and `list` semantics.
324323- ✅ Hardened append path synchronization against concurrent terminal transitions by rehydrating lifecycle guard state from persisted events before each append.
325325-- ✅ Implemented `inspect` run/spawn decoded views and session bridge plumbing in `packages/core/src/public/run.api.ts`.
326326-- ✅ Added driver session bridge contract (`resolveSession`) and implemented pointer resolution for `@mill/driver-pi`.
327327-- ✅ Added CLI handlers for `watch`, `inspect`, `cancel`, and `ls` with JSON stdout contracts.
324324+- ✅ Added CLI `watch` channel/source/spawn filters and removed the separate `inspect` command surface.
328325- ✅ Fixed async submit detachment stdio to `ignore` so `run --json` remains non-blocking even under captured stdout in e2e contexts.
329329-- ✅ Added integration/e2e coverage for command matrix behavior across watch/inspect/session/cancel/ls, including concurrent run cancellation invariants.
326326+- ✅ Added integration/e2e coverage for command matrix behavior across watch/cancel/ls, including concurrent run cancellation invariants.
···47474848## 5) Fast triage checklist for "run stuck in running"
49495050-1. `mill inspect <runId> --json`
5050+1. `mill watch --run <runId> --channel events --json`
5151 - if you only see `spawn:start` and no `spawn:complete`, the child driver call is still in-flight.
52522. Check process liveness using `worker.pid` + OS process list.
53533. `mill cancel <runId> --json`