@@ -35,6 +35,11 @@ As of April 9, 2026:
3535- only the ` nobify ` product needs portability; test infrastructure does not
3636- inter-wave breakage remains acceptable if it shortens time-to-daemon
3737
38+ The core execution-owner rewrite is now landed. The main remaining pain is
39+ operational rather than structural: daemon state, recovery, lifecycle, and
40+ watch session ownership now need explicit hardening ahead of any further
41+ throughput or scheduler expansion.
42+
3843The target end state of this roadmap is:
3944
4045` ./build/nob ` front door -> daemon client/supervisor -> reactor ` nob_testd ` -> shared runner core -> suites
@@ -114,6 +119,7 @@ The target CLI contract is:
114119- ` ./build/nob test watch auto `
115120- ` ./build/nob test daemon start `
116121- ` ./build/nob test daemon stop `
122+ - ` ./build/nob test daemon restart `
117123- ` ./build/nob test daemon status `
118124
119125## 4. Why This Is A Separate Program
@@ -176,6 +182,22 @@ as a contract that must survive intact.
176182- remove or hollow out ` build/nob_test `
177183- align docs and scripts around the new mental model
178184
185+ ### 5.7 Operational Ergonomics, Recovery, And Introspection
186+
187+ - make daemon lifecycle and active-work state visible and explainable through
188+ CLI and protocol surfaces
189+ - replace opaque ` busy ` /recovery behavior with structured control-plane
190+ outcomes and explicit admission policy
191+ - remove manual stop/clean/status choreography from the common local workflow
192+
193+ ### 5.8 Detached Watch Sessions And Session Ownership
194+
195+ - move long-lived watch ownership fully into the daemon instead of the active
196+ client connection
197+ - let watch sessions survive transient client disconnects by default
198+ - surface detached watch state and lifecycle through ` status ` and daemon
199+ control paths
200+
179201## 6. Wave Plan
180202
181203The wave descriptions below intentionally preserve the state and assumptions
@@ -363,6 +385,64 @@ Exit criteria:
363385- there is one clear public entrypoint and one clear execution owner
364386- stale mental models around the old runner are gone
365387
388+ ### T7 Operational Hardening And Introspection
389+
390+ Goal:
391+ - make the daemon predictable to operate during normal day-to-day development
392+
393+ Deliverables:
394+ - enriched ` daemon status ` output covering effective state, active work,
395+ module/profile, run duration, attached client state, preserved logs or
396+ workspace paths, pending cancel/drain state, and cache hit/miss reasons
397+ - explicit admission policy:
398+ - foreground run versus another foreground run rejects with a structured
399+ error instead of only free-form text
400+ - watch reruns use documented replace-running behavior
401+ - the daemon reports which policy was applied for the active request
402+ - ` daemon stop ` becomes drain-aware instead of failing solely because the
403+ daemon is currently ` busy `
404+ - ` daemon stop --force ` becomes the official immediate-cancel path with
405+ timeout-aware escalation
406+ - ` ./build/nob test daemon restart ` becomes an official lifecycle path
407+ - ` ./build/nob test clean ` stops requiring manual pre-stop coordination:
408+ - default ` clean ` coordinates daemon drain/stop first
409+ - ` clean --force ` performs immediate daemon teardown before cleanup
410+ - the binary protocol gains stable error codes plus optional metadata for the
411+ active request so text messages stop being the only control-plane surface
412+ - stale socket/PID recovery and kill-escalation outcomes become visible through
413+ ` status ` and structured errors instead of remaining purely internal behavior
414+
415+ Non-goals:
416+ - no general parallel scheduler yet
417+ - no detached watch sessions yet
418+ - no portability work
419+
420+ Exit criteria:
421+ - ` busy ` is explainable and recoverable from the CLI
422+ - ` status ` shows what the daemon is actually doing
423+ - ` stop ` and ` clean ` work from both idle and busy states
424+ - watch replace/cancel behavior is explicit instead of implicit
425+
426+ ### T8 Detached Watch Sessions
427+
428+ Goal:
429+ - decouple watch session lifetime from the client connection that started it
430+
431+ Deliverables:
432+ - watch sessions become daemon-owned state instead of attached-client state
433+ - terminal or client disconnect no longer stops watch by default
434+ - ` status ` and daemon lifecycle surfaces report and control detached watch
435+ sessions explicitly
436+
437+ Non-goals:
438+ - no multi-user sharing model
439+ - no rich attach UX in this wave
440+
441+ Exit criteria:
442+ - watch survives client disconnect
443+ - detached watch ownership and state are visible and controllable through the
444+ daemon surface
445+
366446## 7. Required Interfaces And Behavior
367447
368448The implementation target for later waves is:
@@ -374,17 +454,27 @@ The implementation target for later waves is:
374454 embedding them in CLI-only flow
375455- daemon-facing control flow uses typed request/result structures and stable
376456 ids, even if the CLI remains text-oriented
457+ - the control plane eventually exposes structured daemon states including
458+ ` idle ` , ` busy ` , ` watching ` , ` draining ` , and ` stopping `
459+ - the control plane eventually exposes stable error codes and optional active
460+ request metadata rather than relying on free-form text alone
461+ - daemon lifecycle eventually includes ` start ` , ` stop ` , ` stop --force ` ,
462+ ` restart ` , and ` status `
377463- the daemon is a single-threaded Linux reactor that owns:
378464 - local socket accept/read/write
379465 - child-process supervision
380466 - timer-based debounce and timeout handling
381467 - watch event intake and overflow recovery
468+ - ` clean ` eventually coordinates with daemon lifecycle instead of requiring
469+ users to manually stop the daemon first
382470- logs, preserved failure workspaces, and ` Temp_tests ` layout remain
383471 conceptually intact unless a later wave explicitly replaces them
384472- old preflight checks stay logically owned by runner-core, but local daemon
385473 runs may cache or skip them under the fast profile
386474- module registry entries carry the watch roots and routing metadata needed by
387475 ` watch auto `
476+ - detached watch session ownership eventually belongs to daemon state rather
477+ than to the lifetime of a single foreground client
388478- sanitizer, coverage, and explicit heavy suites remain supported, but the
389479 fast loop is optimized around normal local development rather than around
390480 those profiles
@@ -397,14 +487,21 @@ The daemon program is only complete when it has explicit proof for:
397487- transitional self-rebuild for ` src_v2/build/nob_test.c `
398488- ` ./build/nob test <module> ` end-to-end
399489- daemon auto-start on first client request
400- - ` daemon start|stop|status `
490+ - ` daemon start|stop|restart|status `
491+ - enriched ` daemon status ` for an active run, including active-work metadata
401492- stale socket and stale PID recovery
402493- client/daemon compatibility for the versioned local protocol
403494- peer validation on the local UNIX socket
495+ - structured ` busy ` and recovery errors instead of text-only rejection
496+ - ` daemon stop ` while work is active
497+ - ` daemon stop --force ` against a stuck worker
498+ - ` ./build/nob test clean ` from both idle and busy daemon states
404499- watch rerun on save
405500- ` watch auto ` routing for representative file changes
406501- last-write-wins cancellation under save storms
502+ - explicit watch replace-running reporting under save storms
407503- watch recovery after rename churn or inotify overflow
504+ - detached watch persistence across client disconnect in the T8 wave
408505- fast profile behavior not regressing strict or sanitizer profiles
409506- preserved failure workspaces and captured logs still working through daemon
410507 execution
0 commit comments