590	                if crate::deelevate::is_elevated() {
591	                    // No unelevated identity exists (UAC-off admin desktop,
592	                    // SYSTEM): the whole universe is uniformly elevated, so
593	                    // an elevated daemon is consistent there. Loud anyway.
594	                    eprintln!(
595	                        "ELEVATED_DAEMON: no unelevated identity to drop to — daemon inherits \
596	                         the elevated token"
597	                    );
598	                }
599	            }
600	            // A de-elevation TARGET existed but the spawn failed: an
601	            // elevated daemon here is a known-torn state (its pipes deny
602	            // the unelevated clients that target exists for). ABORT — do
603	            // not start a daemon that cannot serve (user-ratified
604	            // 2026-06-06: DEELEVATE_FAIL means spt won't function; refuse,
605	            // don't limp).
606	            Err(e) => {
607	                return Err(io::Error::other(format!(
608	                    "DEELEVATE_FAIL: {e} — refusing to start an ELEVATED daemon (it would deny \
609	                     unelevated clients); run spt from an unelevated shell"
610	                )));
611	            }
612	        }
613	        launch_daemon_job_neutral(&exe, &args)?;
614	        Ok(())
615	    }
616	    #[cfg(unix)]
617	    {
618	        use std::os::unix::process::CommandExt;
619	        let mut cmd = Command::new(exe);
620	        cmd.args(["daemon", "run"])
621	            .stdin(Stdio::null())
622	            .stdout(Stdio::null())
623	            .stderr(Stdio::null());
624	        if let Some(invoker) = crate::deelevate::daemon_target_user() {
625	            // Drop the child to the elected default user (else the sudo
626	            // invoker — M8 decision 8): std clears supplementary groups and
627	            // orders setgid-before-setuid when uid is set.
628	            // [impl->REQ-INSTALL-6]
629	            invoker.stamp_env(&mut cmd);
630	            cmd.uid(invoker.uid).gid(invoker.gid);
631	        }
632	        // setsid: detach into a NEW SESSION, not just a new process group —
633	        // a daemon auto-started inside an ssh/non-interactive session
634	        // otherwise dies with it (sshd HUPs the whole session on
635	        // disconnect; observed live in the 2026-06-06 fleet deploy, where
636	        // the freshly-spawned daemon vanished the moment the ssh command
637	        // returned). `process_group(0)` is NOT enough — the new group
638	        // still belongs to the dying session. Safety: this closure only
639	        // calls the async-signal-safe setsid(2).
640	        unsafe {
641	            cmd.pre_exec(|| {
642	                if libc::setsid() == -1 {
643	                    return Err(std::io::Error::last_os_error());
644	                }
645	                Ok(())
646	            });
647	        }
648	        cmd.spawn()?;
649	        Ok(())
650	    }
651	}
652
653	/// CREATE_BREAKAWAY_FROM_JOB: escape the launching terminal's Job Object.
654	/// Windows Terminal / VS Code put the launched shell AND every descendant in a
655	/// Job with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; closing the tab drops the job's
656	/// last handle → the OS terminates every member. A child created WITH this flag
657	/// leaves the job at birth — but only if the job permits it (BREAKAWAY_OK /
658	/// SILENT_BREAKAWAY_OK) or there is no job; a job that FORBIDS breakaway fails
659	/// `CreateProcessW` with ERROR_ACCESS_DENIED. Used by [`detached_no_inherit`]'s
660	/// fallback rung and, for the daemon, the [`launch_daemon_job_neutral`] ladder.
661	#[cfg(windows)]
662	const CREATE_BREAKAWAY_FROM_JOB: u32 = 0x0100_0000;
663	#[cfg(windows)]
664	const ERROR_ACCESS_DENIED: i32 = 5;
665
666	/// One CreateProcessW attempt: spawn `program args…` detached, inheriting NO
667	/// handles, with `extra_flags` OR'd onto the base detached flags. The single
668	/// FFI primitive shared by [`detached_no_inherit`] and the daemon's job-neutral
669	/// launcher ladder ([`launch_daemon_job_neutral`]).
670	///
671	/// **`bInheritHandles = FALSE`** is the KH 5.6 guard in its complete form: no
672	/// handle (std or stray grandparent pipe) flows into the immortal child, so no
673	/// capturing caller anywhere up the process chain can be wedged by it.
674	/// `DETACHED_PROCESS` ⇒ no console, so the child's std handles are simply absent
675	/// (every such child runs stdio-null by design anyway). `lpApplicationName`
676	/// stays NULL so `program` resolves via PATH like `Command` would. Raw
677	/// `CreateProcessW` declared inline to avoid a winapi dependency for one call.
678	// [impl->REQ-HAZARD-DETACHED-PIPE-INHERIT]
679	#[cfg(windows)]

                if crate::deelevate::is_elevated() {
                    // No unelevated identity exists (UAC-off admin desktop,
                    // SYSTEM): the whole universe is uniformly elevated, so
                    // an elevated daemon is consistent there. Loud anyway.
                    eprintln!(
                        "ELEVATED_DAEMON: no unelevated identity to drop to — daemon inherits \
                         the elevated token"
                    );
                }
            }
            // A de-elevation TARGET existed but the spawn failed: an
            // elevated daemon here is a known-torn state (its pipes deny
            // the unelevated clients that target exists for). ABORT — do
            // not start a daemon that cannot serve (user-ratified
            // 2026-06-06: DEELEVATE_FAIL means spt won't function; refuse,
            // don't limp).
            Err(e) => {
                return Err(io::Error::other(format!(
                    "DEELEVATE_FAIL: {e} — refusing to start an ELEVATED daemon (it would deny \
                     unelevated clients); run spt from an unelevated shell"
                )));
            }
        }
        launch_daemon_job_neutral(&exe, &args)?;
        Ok(())
    }
    #[cfg(unix)]
    {
        use std::os::unix::process::CommandExt;
        let mut cmd = Command::new(exe);
        cmd.args(["daemon", "run"])
            .stdin(Stdio::null())
            .stdout(Stdio::null())
            .stderr(Stdio::null());
        if let Some(invoker) = crate::deelevate::daemon_target_user() {
            // Drop the child to the elected default user (else the sudo
            // invoker — M8 decision 8): std clears supplementary groups and
            // orders setgid-before-setuid when uid is set.
            // [impl->REQ-INSTALL-6]
            invoker.stamp_env(&mut cmd);
            cmd.uid(invoker.uid).gid(invoker.gid);
        }
        // setsid: detach into a NEW SESSION, not just a new process group —
        // a daemon auto-started inside an ssh/non-interactive session
        // otherwise dies with it (sshd HUPs the whole session on
        // disconnect; observed live in the 2026-06-06 fleet deploy, where
        // the freshly-spawned daemon vanished the moment the ssh command
        // returned). `process_group(0)` is NOT enough — the new group
        // still belongs to the dying session. Safety: this closure only
        // calls the async-signal-safe setsid(2).
        unsafe {
            cmd.pre_exec(|| {
                if libc::setsid() == -1 {
                    return Err(std::io::Error::last_os_error());
                }
                Ok(())
            });
        }
        cmd.spawn()?;
        Ok(())
    }
}

/// CREATE_BREAKAWAY_FROM_JOB: escape the launching terminal's Job Object.
/// Windows Terminal / VS Code put the launched shell AND every descendant in a
/// Job with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; closing the tab drops the job's
/// last handle → the OS terminates every member. A child created WITH this flag
/// leaves the job at birth — but only if the job permits it (BREAKAWAY_OK /
/// SILENT_BREAKAWAY_OK) or there is no job; a job that FORBIDS breakaway fails
/// `CreateProcessW` with ERROR_ACCESS_DENIED. Used by [`detached_no_inherit`]'s
/// fallback rung and, for the daemon, the [`launch_daemon_job_neutral`] ladder.
#[cfg(windows)]
const CREATE_BREAKAWAY_FROM_JOB: u32 = 0x0100_0000;
#[cfg(windows)]
const ERROR_ACCESS_DENIED: i32 = 5;

/// One CreateProcessW attempt: spawn `program args…` detached, inheriting NO
/// handles, with `extra_flags` OR'd onto the base detached flags. The single
/// FFI primitive shared by [`detached_no_inherit`] and the daemon's job-neutral
/// launcher ladder ([`launch_daemon_job_neutral`]).
///
/// **`bInheritHandles = FALSE`** is the KH 5.6 guard in its complete form: no
/// handle (std or stray grandparent pipe) flows into the immortal child, so no
/// capturing caller anywhere up the process chain can be wedged by it.
/// `DETACHED_PROCESS` ⇒ no console, so the child's std handles are simply absent
/// (every such child runs stdio-null by design anyway). `lpApplicationName`
/// stays NULL so `program` resolves via PATH like `Command` would. Raw
/// `CreateProcessW` declared inline to avoid a winapi dependency for one call.
// [impl->REQ-HAZARD-DETACHED-PIPE-INHERIT]
#[cfg(windows)]


863	pub(crate) fn launch_daemon_job_neutral(program: &str, args: &[String]) -> io::Result<u32> {
864	    // Test hook (job_escape_e2e): force a single rung so a green that silently
865	    // ran a different rung is impossible. Unset in production → full ladder.
866	    let ladder = match std::env::var("SPT_FORCE_LAUNCH_RUNG").ok().as_deref() {
867	        Some("wmi") => [LaunchRung::Wmi; 4],
868	        Some("schtasks") => [LaunchRung::Schtasks; 4],
869	        Some("breakaway") => [LaunchRung::Breakaway; 4],
870	        Some("injob") => [LaunchRung::InJob; 4],
871	        _ => launch_ladder(),
872	    };
873	    let (rung, pid) = drive_ladder(ladder, |rung| match rung {
874	        LaunchRung::Wmi => spawn_daemon_via_wmi(program, args),
875	        LaunchRung::Schtasks => spawn_daemon_via_schtasks(program, args),
876	        LaunchRung::Breakaway => create_process_detached(program, args, CREATE_BREAKAWAY_FROM_JOB),
877	        LaunchRung::InJob => create_process_detached(program, args, 0),
878	    })?;
879	    let via = match rung {
880	        LaunchRung::Wmi => "WMI",
881	        LaunchRung::Schtasks => "SCHTASKS",
882	        LaunchRung::Breakaway => "BREAKAWAY",
883	        LaunchRung::InJob => "IN_JOB",
884	    };
885	    eprintln!("DAEMON_LAUNCH_VIA_{via}: pid {pid}");
886	    // Test hook (job_escape_e2e): record which rung won so the int can ASSERT
887	    // the WMI path fired (not a silent breakaway). Unset in production.
888	    if let Ok(marker) = std::env::var("SPT_LAUNCH_RUNG_MARKER") {
889	        let _ = std::fs::write(&marker, via);
890	    }
891	    if matches!(rung, LaunchRung::InJob) {
892	        eprintln!(
893	            "DETACH_IN_JOB: daemon launched IN the terminal's Job Object \
894	             (may be reaped if the launching terminal/tab closes)"
895	        );
896	    }
897	    Ok(pid)
898	}
899
900	/// Build the command line a job-neutral creator (WMI / Task Scheduler) runs to
901	/// launch the daemon.
902	///
903	/// **Why a `cmd /c` wrapper, not the bare exe.** A WMI- or scheduler-created
904	/// process does NOT inherit this process's *in-process* environment — it gets
905	/// the user-profile environment block, so transient shell vars (most importantly
906	/// `SPT_HOME`, which selects the daemon's entire universe of sockets/perches)
907	/// are LOST. Verified empirically (a WMI child saw `%SPT_HOME%` unexpanded).
908	/// We therefore forward every `SPT_*` var explicitly via `cmd /c set …`, then
909	/// `start "" /b` so the launching `cmd` returns immediately and the daemon
910	/// continues — job-neutral, since `cmd` is the creator's child (outside any
911	/// terminal Job), not ours.
912	// [impl->REQ-HAZARD-VIEWER-CLOSE-DETACH]
913	#[cfg(windows)]
914	fn wrapped_daemon_command(program: &str, args: &[String]) -> String {
915	    let mut s = String::from("cmd.exe /c ");
916	    for (k, v) in std::env::vars() {
917	        // Forward SPT_* knobs. A value containing a double-quote can't ride the
918	        // `set "K=V"` form safely; skip it (paths/ids never contain one).
919	        if k.starts_with("SPT_") && !v.contains('"') {
920	            s.push_str(&format!("set \"{k}={v}\" & "));
921	        }
922	    }
923	    s.push_str("start \"\" /b ");
924	    quote_windows_arg(&mut s, program);
925	    for a in args {
926	        s.push(' ');
927	        quote_windows_arg(&mut s, a);
928	    }
929	    s
930	}
931
932	/// The powershell script that drives `Win32_Process.Create` for the WMI daemon
933	/// launch. Pure over `ps_literal` so the no-window startup spec is unit-guarded.
934	/// Win32_Process.Create does NOT inherit the launching powershell's
935	/// CREATE_NO_WINDOW — without an explicit `Win32_ProcessStartup` the spawned
936	/// `cmd.exe` wrapper pops a console window (the v0.12.1 regression). The startup
937	/// spec carries `CreateFlags=DETACHED_PROCESS` (0x8 — no console ⇒ no window;
938	/// `CREATE_NO_WINDOW` 0x08000000 is NOT a valid Win32_ProcessStartup flag →
939	/// ReturnValue 21) plus `ShowWindow=SW_HIDE`(0), keeping `spt daemon start` a
940	/// console-less background daemon (v0.7.4 no-persistent-window invariant,
941	/// REQ-INSTALL-10).
942	// [impl->REQ-HAZARD-WMI-DAEMON-WINDOW]
943	#[cfg(windows)]
944	fn wmi_create_script(ps_literal: &str) -> String {
945	    format!(
946	        "$ProgressPreference='SilentlyContinue'; \
947	         $si = New-CimInstance -ClassName Win32_ProcessStartup -ClientOnly \
948	         -Property @{{CreateFlags=[uint32]8; ShowWindow=[uint16]0}}; \
949	         $r = Invoke-CimMethod -ClassName Win32_Process -MethodName Create \
950	         -Arguments @{{CommandLine='{ps_literal}'; ProcessStartupInformation=$si}}; \
951	         Write-Output \"$($r.ReturnValue) $($r.ProcessId)\""
952	    )
953	}
954
955	/// Launch the daemon via WMI `Win32_Process.Create` (the daemon becomes a child
956	/// of WmiPrvSE, outside any terminal job). Uses the ABSOLUTE powershell path
957	/// (never bare `powershell` — KH 5.12: PATH/PATHEXT not trusted for a
958	/// privilege-relevant launch). The script is passed as `-EncodedCommand`
959	/// (base64 of UTF-16LE) so the embedded command line — which carries spaces,
960	/// double-quotes, `&`, and a single-quoted PS literal — survives Rust→CreateProcess
961	/// →powershell arg parsing intact, with zero quoting hazards. Success REQUIRES
962	/// both `ReturnValue == 0` AND a parsed `ProcessId`; anything else falls through
963	/// to the next rung — never a silent "launched".
964	// [impl->REQ-HAZARD-VIEWER-CLOSE-DETACH]
965	#[cfg(windows)]
966	fn spawn_daemon_via_wmi(program: &str, args: &[String]) -> io::Result<u32> {
967	    use base64::engine::general_purpose::STANDARD as B64;
968	    use base64::Engine;
969	    use std::os::windows::process::CommandExt;
970
971	    // The command WMI runs, embedded as a single-quoted PS literal (F-009: embedded
972	    // `'` doubled so a path/value can never split or inject).
973	    let wmi_cmdline = wrapped_daemon_command(program, args);
974	    let ps_literal = wmi_cmdline.replace('\'', "''");
975	    let script = wmi_create_script(&ps_literal);
976	    // -EncodedCommand wants base64 of the UTF-16LE script bytes.
977	    let utf16le: Vec<u8> = script.encode_utf16().flat_map(|u| u.to_le_bytes()).collect();
978	    let encoded = B64.encode(&utf16le);
979
980	    const POWERSHELL_ABS: &str = r"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe";
981	    const CREATE_NO_WINDOW: u32 = 0x0800_0000;
982

pub(crate) fn launch_daemon_job_neutral(program: &str, args: &[String]) -> io::Result<u32> {
    // Test hook (job_escape_e2e): force a single rung so a green that silently
    // ran a different rung is impossible. Unset in production → full ladder.
    let ladder = match std::env::var("SPT_FORCE_LAUNCH_RUNG").ok().as_deref() {
        Some("wmi") => [LaunchRung::Wmi; 4],
        Some("schtasks") => [LaunchRung::Schtasks; 4],
        Some("breakaway") => [LaunchRung::Breakaway; 4],
        Some("injob") => [LaunchRung::InJob; 4],
        _ => launch_ladder(),
    };
    let (rung, pid) = drive_ladder(ladder, |rung| match rung {
        LaunchRung::Wmi => spawn_daemon_via_wmi(program, args),
        LaunchRung::Schtasks => spawn_daemon_via_schtasks(program, args),
        LaunchRung::Breakaway => create_process_detached(program, args, CREATE_BREAKAWAY_FROM_JOB),
        LaunchRung::InJob => create_process_detached(program, args, 0),
    })?;
    let via = match rung {
        LaunchRung::Wmi => "WMI",
        LaunchRung::Schtasks => "SCHTASKS",
        LaunchRung::Breakaway => "BREAKAWAY",
        LaunchRung::InJob => "IN_JOB",
    };
    eprintln!("DAEMON_LAUNCH_VIA_{via}: pid {pid}");
    // Test hook (job_escape_e2e): record which rung won so the int can ASSERT
    // the WMI path fired (not a silent breakaway). Unset in production.
    if let Ok(marker) = std::env::var("SPT_LAUNCH_RUNG_MARKER") {
        let _ = std::fs::write(&marker, via);
    }
    if matches!(rung, LaunchRung::InJob) {
        eprintln!(
            "DETACH_IN_JOB: daemon launched IN the terminal's Job Object \
             (may be reaped if the launching terminal/tab closes)"
        );
    }
    Ok(pid)
}

/// Build the command line a job-neutral creator (WMI / Task Scheduler) runs to
/// launch the daemon.
///
/// **Why a `cmd /c` wrapper, not the bare exe.** A WMI- or scheduler-created
/// process does NOT inherit this process's *in-process* environment — it gets
/// the user-profile environment block, so transient shell vars (most importantly
/// `SPT_HOME`, which selects the daemon's entire universe of sockets/perches)
/// are LOST. Verified empirically (a WMI child saw `%SPT_HOME%` unexpanded).
/// We therefore forward every `SPT_*` var explicitly via `cmd /c set …`, then
/// `start "" /b` so the launching `cmd` returns immediately and the daemon
/// continues — job-neutral, since `cmd` is the creator's child (outside any
/// terminal Job), not ours.
// [impl->REQ-HAZARD-VIEWER-CLOSE-DETACH]
#[cfg(windows)]
fn wrapped_daemon_command(program: &str, args: &[String]) -> String {
    let mut s = String::from("cmd.exe /c ");
    for (k, v) in std::env::vars() {
        // Forward SPT_* knobs. A value containing a double-quote can't ride the
        // `set "K=V"` form safely; skip it (paths/ids never contain one).
        if k.starts_with("SPT_") && !v.contains('"') {
            s.push_str(&format!("set \"{k}={v}\" & "));
        }
    }
    s.push_str("start \"\" /b ");
    quote_windows_arg(&mut s, program);
    for a in args {
        s.push(' ');
        quote_windows_arg(&mut s, a);
    }
    s
}

/// The powershell script that drives `Win32_Process.Create` for the WMI daemon
/// launch. Pure over `ps_literal` so the no-window startup spec is unit-guarded.
/// Win32_Process.Create does NOT inherit the launching powershell's
/// CREATE_NO_WINDOW — without an explicit `Win32_ProcessStartup` the spawned
/// `cmd.exe` wrapper pops a console window (the v0.12.1 regression). The startup
/// spec carries `CreateFlags=DETACHED_PROCESS` (0x8 — no console ⇒ no window;
/// `CREATE_NO_WINDOW` 0x08000000 is NOT a valid Win32_ProcessStartup flag →
/// ReturnValue 21) plus `ShowWindow=SW_HIDE`(0), keeping `spt daemon start` a
/// console-less background daemon (v0.7.4 no-persistent-window invariant,
/// REQ-INSTALL-10).
// [impl->REQ-HAZARD-WMI-DAEMON-WINDOW]
#[cfg(windows)]
fn wmi_create_script(ps_literal: &str) -> String {
    format!(
        "$ProgressPreference='SilentlyContinue'; \
         $si = New-CimInstance -ClassName Win32_ProcessStartup -ClientOnly \
         -Property @{{CreateFlags=[uint32]8; ShowWindow=[uint16]0}}; \
         $r = Invoke-CimMethod -ClassName Win32_Process -MethodName Create \
         -Arguments @{{CommandLine='{ps_literal}'; ProcessStartupInformation=$si}}; \
         Write-Output \"$($r.ReturnValue) $($r.ProcessId)\""
    )
}

/// Launch the daemon via WMI `Win32_Process.Create` (the daemon becomes a child
/// of WmiPrvSE, outside any terminal job). Uses the ABSOLUTE powershell path
/// (never bare `powershell` — KH 5.12: PATH/PATHEXT not trusted for a
/// privilege-relevant launch). The script is passed as `-EncodedCommand`
/// (base64 of UTF-16LE) so the embedded command line — which carries spaces,
/// double-quotes, `&`, and a single-quoted PS literal — survives Rust→CreateProcess
/// →powershell arg parsing intact, with zero quoting hazards. Success REQUIRES
/// both `ReturnValue == 0` AND a parsed `ProcessId`; anything else falls through
/// to the next rung — never a silent "launched".
// [impl->REQ-HAZARD-VIEWER-CLOSE-DETACH]
#[cfg(windows)]
fn spawn_daemon_via_wmi(program: &str, args: &[String]) -> io::Result<u32> {
    use base64::engine::general_purpose::STANDARD as B64;
    use base64::Engine;
    use std::os::windows::process::CommandExt;

    // The command WMI runs, embedded as a single-quoted PS literal (F-009: embedded
    // `'` doubled so a path/value can never split or inject).
    let wmi_cmdline = wrapped_daemon_command(program, args);
    let ps_literal = wmi_cmdline.replace('\'', "''");
    let script = wmi_create_script(&ps_literal);
    // -EncodedCommand wants base64 of the UTF-16LE script bytes.
    let utf16le: Vec<u8> = script.encode_utf16().flat_map(|u| u.to_le_bytes()).collect();
    let encoded = B64.encode(&utf16le);

    const POWERSHELL_ABS: &str = r"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe";
    const CREATE_NO_WINDOW: u32 = 0x0800_0000;


Architecture clear. Now find where the daemon-run `args` are built (windows path before line 613) + `cmd_daemon_run` + the clap parse for `daemon run`:


<owl_messages>
<owl_message from="doyle" priority="high">doyle — resend tail (seq 9-10), compact:

REGRESSION SEAM: extend job_escape_e2e.rs (already WMI-cold-starts with the runner-job SKIP guards) → assert the WMI-launched `daemon run` pid has NO conhost child = the window check. Real WMI integration. + a unit on the pure seam: arg-parse sets the hidden --detached flag → cmd_daemon_run takes the FreeConsole branch (assert the flag routing, not the syscall). Repro-FIRST: land the no-conhost assert RED pre-fix.

REQ POSTURE: REOPEN/extend REQ-HAZARD-WMI-DAEMON-WINDOW → reactivate [impl,unit,int] (it was [impl,unit]; ADD int now that we have a real no-conhost assertion). Extend its note: @15fdf58 detached the cmd wrapper but the inner broker still allocated its own conhost; fix = broker FreeConsole(--detached). registry-first in your fix commit.

ROLES: YOURS to build, MINE to gate (deterministic loop: stop → marked start → assert no conhost child). Confirmed yours.

NOT-A-BUG reminder (don't touch): the daemon RESPAWNING after stop is by-design (any spt cmd auto-starts via ensure_running). Only the WINDOW is the fix.

PRIORITY: operator's call pending — likely fold into v0.13.0 (it's a v0.12.1-introduced regression on a real dogfood surface, cheap fix). I'll confirm once operator rules. Proceed repro-first meanwhile.</owl_message>
</owl_messages>

[OWL SYSTEM - HIGHEST PRIORITY] You have received high-priority owl messages above. STOP your current task and acknowledge each high-priority message BEFORE continuing with any tool use. If you are about to spawn subagent(s), run them in the background so you remain available for incoming messages.


550
551	/// Spawn `spt daemon` as a detached background process. Inherits the environment
552	/// — so the child resolves the **same** `SPT_HOME`, hence the same canonical
553	/// socket names — but drops the std handles so it outlives the launching CLI
554	/// call. The child is independent (not killed when the parent exits): on unix it
555	/// is reparented to init when the launcher exits; on Windows a child is not in
556	/// the parent's job by default.
557	///
558	/// **Windows handle-inheritance guard (KNOWN-HAZARDS 5.6).** A caller may
559	/// capture this process's stdout/stderr through a pipe (`Command::output()`, a
560	/// harness hook reading the command's output). On Windows `CreateProcess` runs
561	/// with `bInheritHandles = TRUE`, so the long-lived daemon would inherit that
562	/// pipe's write end and hold it open forever — the capturing reader never sees
563	/// EOF and hangs. Worse, inheritable pipe handles flow through EVERY
564	/// `bInheritHandles = TRUE` hop: stripping only this process's *std* handles
565	/// (the first guard shipped here) still leaked a grandparent capture's pipe
566	/// sitting in our handle table as a stray. So detached long-lived children are
567	/// spawned with **`bInheritHandles = FALSE`** ([`detached_no_inherit`]) — zero
568	/// handles flow, whatever the ancestry. Unix is immune (Rust marks pipe fds
569	/// `CLOEXEC`) and keeps the plain `Command` path.
570	///
571	/// **Elevation guard (KNOWN-HAZARDS 5.7).** The daemon must ALWAYS run
572	/// unelevated in the invoking user's universe, whichever command spawns it:
573	/// an elevated spawner (Windows UAC, `sudo`) de-elevates the child here —
574	/// Windows via the UAC linked token ([`crate::deelevate::spawn_deelevated`];
575	/// no handle inheritance by construction, so 5.6 holds on that path too),
576	/// unix by dropping the child to `SUDO_UID`/`SUDO_GID` with the invoker's
577	/// `HOME`. When no unelevated identity exists (UAC off, genuine root), the
578	/// spawn proceeds as-is with a loud warning — a consistent universe, never a
579	/// torn one.
580	// [impl->REQ-HAZARD-ELEVATED-DAEMON-SPAWN]
581	fn spawn_detached() -> io::Result<()> {
582	    let exe = std::env::current_exe()?;
583	    #[cfg(windows)]
584	    {
585	        let exe = exe.to_string_lossy();
586	        let args = ["daemon".to_string(), "run".to_string()];
587	        match crate::deelevate::spawn_deelevated(&exe, &args) {
588	            Ok(Some(_pid)) => return Ok(()), // de-elevated child running
589	            Ok(None) => {
590	                if crate::deelevate::is_elevated() {
591	                    // No unelevated identity exists (UAC-off admin desktop,
592	                    // SYSTEM): the whole universe is uniformly elevated, so
593	                    // an elevated daemon is consistent there. Loud anyway.
594	                    eprintln!(

/// Spawn `spt daemon` as a detached background process. Inherits the environment
/// — so the child resolves the **same** `SPT_HOME`, hence the same canonical
/// socket names — but drops the std handles so it outlives the launching CLI
/// call. The child is independent (not killed when the parent exits): on unix it
/// is reparented to init when the launcher exits; on Windows a child is not in
/// the parent's job by default.
///
/// **Windows handle-inheritance guard (KNOWN-HAZARDS 5.6).** A caller may
/// capture this process's stdout/stderr through a pipe (`Command::output()`, a
/// harness hook reading the command's output). On Windows `CreateProcess` runs
/// with `bInheritHandles = TRUE`, so the long-lived daemon would inherit that
/// pipe's write end and hold it open forever — the capturing reader never sees
/// EOF and hangs. Worse, inheritable pipe handles flow through EVERY
/// `bInheritHandles = TRUE` hop: stripping only this process's *std* handles
/// (the first guard shipped here) still leaked a grandparent capture's pipe
/// sitting in our handle table as a stray. So detached long-lived children are
/// spawned with **`bInheritHandles = FALSE`** ([`detached_no_inherit`]) — zero
/// handles flow, whatever the ancestry. Unix is immune (Rust marks pipe fds
/// `CLOEXEC`) and keeps the plain `Command` path.
///
/// **Elevation guard (KNOWN-HAZARDS 5.7).** The daemon must ALWAYS run
/// unelevated in the invoking user's universe, whichever command spawns it:
/// an elevated spawner (Windows UAC, `sudo`) de-elevates the child here —
/// Windows via the UAC linked token ([`crate::deelevate::spawn_deelevated`];
/// no handle inheritance by construction, so 5.6 holds on that path too),
/// unix by dropping the child to `SUDO_UID`/`SUDO_GID` with the invoker's
/// `HOME`. When no unelevated identity exists (UAC off, genuine root), the
/// spawn proceeds as-is with a loud warning — a consistent universe, never a
/// torn one.
// [impl->REQ-HAZARD-ELEVATED-DAEMON-SPAWN]
fn spawn_detached() -> io::Result<()> {
    let exe = std::env::current_exe()?;
    #[cfg(windows)]
    {
        let exe = exe.to_string_lossy();
        let args = ["daemon".to_string(), "run".to_string()];
        match crate::deelevate::spawn_deelevated(&exe, &args) {
            Ok(Some(_pid)) => return Ok(()), // de-elevated child running
            Ok(None) => {
                if crate::deelevate::is_elevated() {
                    // No unelevated identity exists (UAC-off admin desktop,
                    // SYSTEM): the whole universe is uniformly elevated, so
                    // an elevated daemon is consistent there. Loud anyway.
                    eprintln!(


1855	/// the background/de-elevated path lives in `start`.
1856	///
1857	/// Two guards before binding:
1858	/// - Windows + elevated ⇒ REFUSE (exit 2). An elevated `run` would otherwise
1859	///   respawn detached/de-elevated (KH 5.7) and vanish into the background.
1860	/// - A managed service is already active ⇒ warn loudly (running inline fights
1861	///   it for the socket — the kitsubito 2026-06-08 restart-loop).
1862	// [impl->REQ-DAEMON-3]
1863	// [impl->REQ-DAEMON-7]
1864	// [impl->REQ-CLI-2]
1865	fn cmd_daemon_run() -> i32 {
1866	    #[cfg(windows)]
1867	    if spt_daemon::service::run_refused_when_vanishing(
1868	        true,
1869	        spt_daemon::deelevate::has_deelevation_target(),
1870	    ) {
1871	        // Only the vanishing case: elevated WITH an unelevated desktop shell to
1872	        // drop to (run would respawn detached/de-elevated). A headless or
1873	        // uniformly-elevated session has no target and serves foreground below.
1874	        eprintln!(
1875	            "DAEMON_RUN_ELEVATED: `daemon run` is foreground; from an elevated shell it would \
1876	             respawn in the background — use `spt daemon start` for a background daemon, or run \
1877	             from an unelevated shell"
1878	        );
1879	        return 2;
1880	    }
1881	    if let Some(label) = spt_daemon::active_service_conflict() {
1882	        eprintln!(
1883	            "DAEMON_RUN_CONFLICT: a managed {label} service is active; running inline will \
1884	             fight it for the socket — `spt daemon stop` first, or use `spt daemon start`"
1885	        );
1886	    }
1887	    match spt_daemon::Daemon::run() {
1888	        Ok(()) => 0,
1889	        Err(e) => {
1890	            eprintln!("DAEMON_EXIT: {e}");
1891	            1
1892	        }
1893	    }
1894	}

/// the background/de-elevated path lives in `start`.
///
/// Two guards before binding:
/// - Windows + elevated ⇒ REFUSE (exit 2). An elevated `run` would otherwise
///   respawn detached/de-elevated (KH 5.7) and vanish into the background.
/// - A managed service is already active ⇒ warn loudly (running inline fights
///   it for the socket — the kitsubito 2026-06-08 restart-loop).
// [impl->REQ-DAEMON-3]
// [impl->REQ-DAEMON-7]
// [impl->REQ-CLI-2]
fn cmd_daemon_run() -> i32 {
    #[cfg(windows)]
    if spt_daemon::service::run_refused_when_vanishing(
        true,
        spt_daemon::deelevate::has_deelevation_target(),
    ) {
        // Only the vanishing case: elevated WITH an unelevated desktop shell to
        // drop to (run would respawn detached/de-elevated). A headless or
        // uniformly-elevated session has no target and serves foreground below.
        eprintln!(
            "DAEMON_RUN_ELEVATED: `daemon run` is foreground; from an elevated shell it would \
             respawn in the background — use `spt daemon start` for a background daemon, or run \
             from an unelevated shell"
        );
        return 2;
    }
    if let Some(label) = spt_daemon::active_service_conflict() {
        eprintln!(
            "DAEMON_RUN_CONFLICT: a managed {label} service is active; running inline will \
             fight it for the socket — `spt daemon stop` first, or use `spt daemon start`"
        );
    }
    match spt_daemon::Daemon::run() {
        Ok(()) => 0,
        Err(e) => {
            eprintln!("DAEMON_EXIT: {e}");
            1
        }
    }
}


ACK doyle's tail (high-priority) — full dispatch now received:
- **Mine to build, doyle gates.** Confirmed.
- **Repro-first:** land the no-conhost-child int assert RED pre-fix.
- **REQ:** reopen REQ-HAZARD-WMI-DAEMON-WINDOW → reactivate `[impl,unit,int]` (add int), extend note, registry-first in the fix commit.
- **NOT-a-bug:** daemon *respawn* after stop is by-design (ensure_running auto-start) — only the WINDOW is the fix. Won't touch respawn.
- **Priority:** operator may fold into v0.13.0; doyle confirms. Proceed repro-first meanwhile.

Root cause is clear: WMI/schtasks rungs use a `cmd /c … start /b spt daemon run` wrapper; @15fdf58 detached the cmd wrapper (CreateFlags=DETACHED_PROCESS) but the inner broker (console-subsystem exe) allocates its **own** conhost = the window. Fix = broker self-detach via `FreeConsole()` gated on a hidden `--detached` flag the ladder appends.

Grounding the last pieces (clap `daemon run` subcommand, the int harness, FFI availability):


1	//! Wave 2 (v0.12.1) **L1.5** — REQ-HAZARD-VIEWER-CLOSE-DETACH int gate: the
2	//! cold-started daemon escapes the launching terminal's kill-on-close Job Object
3	//! via the JOB-NEUTRAL launcher (WMI `Win32_Process.Create`), so closing the tab
4	//! can never reap it. Replaces the L1 breakaway-only mechanism (a job CAN deny
5	//! breakaway); breakaway is now a demoted fallback rung.
6	//!
7	//! ## Why this is now CI-testable (unlike the L1 breakaway int)
8	//!
9	//! Breakaway could not be proven in CI: the runner's own ancestor job forbids it,
10	//! and every nested test job confounds the escape — a structural false-red. WMI
11	//! is different: the daemon becomes a child of **WmiPrvSE**, outside ANY job,
12	//! regardless of the terminal job's breakaway policy. So we can faithfully:
13	//!
14	//!   1. create a real `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` job (the terminal),
15	//!   2. put a long-lived CONTROL process in it (proves the job is genuinely
16	//!      lethal — non-vacuity),
17	//!   3. put `spt daemon start` in it (the CLI invoked "in the terminal"), FORCING
18	//!      the WMI rung and asserting the `DAEMON_LAUNCH_VIA_WMI` marker fired (a
19	//!      green that silently ran breakaway/in-job would be a false pass),
20	//!   4. terminate the job and assert: the control process DIES (job is lethal)
21	//!      while the daemon STAYS ALIVE and remains IPC-reachable (`daemon stop`).
22	//!
23	//! Full `rc` re-attach after survival is covered by `dummy_harness_e2e` /
24	//! `attach_wedge_e2e` against the same daemon + transport; this gate's novel claim
25	//! is the job escape itself.
26	//!
27	//! All spawned pids are reaped SCOPED (never machine-wide — shared CI runner).
28
29	// Only the Windows `mod win` block uses the shared test helpers; on Unix the
30	// single test below is self-contained, so gating the import keeps `CommandNoWindowExt`
31	// from tripping `dead_code` under `-D warnings` on Linux CI.
32	#[cfg(windows)]
33	mod common;
34
35	// [int->REQ-HAZARD-VIEWER-CLOSE-DETACH] Unix uses the daemon's own session
36	// detachment (`setsid` in `spawn_detached`): a new session with no controlling
37	// terminal already survives terminal close (SIGHUP scope), so there is no Job
38	// Object analogue to escape — the invariant holds by construction, no code.
39	#[cfg(unix)]
40	#[test]
41	fn daemon_survives_terminal_close_via_session_detachment() {
42	    // Documented guard: the Windows job-escape ladder has no Unix counterpart;
43	    // `spawn_detached`'s `setsid` is the mechanism and is exercised by the unix
44	    // daemon-lifecycle E2Es. Nothing to assert here beyond recording the stage.
45	}
46
47	#[cfg(windows)]
48	mod win {
49	    use super::common::CommandNoWindowExt;
50	    use spt_store::proc::is_process_alive;
51	    use std::ffi::c_void;
52	    use std::os::windows::io::AsRawHandle;
53	    use std::path::{Path, PathBuf};
54	    use std::process::{Child, Command, Stdio};
55	    use std::time::{Duration, Instant};
56
57	    const JOB_OBJECT_EXTENDED_LIMIT_INFORMATION: u32 = 9;
58	    const JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE: u32 = 0x0000_2000;
59	    const ERROR_ACCESS_DENIED: i32 = 5;
60
61	    #[repr(C)]
62	    #[derive(Default)]
63	    struct BasicLimit {
64	        per_process_user_time_limit: i64,
65	        per_job_user_time_limit: i64,
66	        limit_flags: u32,
67	        minimum_working_set_size: usize,
68	        maximum_working_set_size: usize,
69	        active_process_limit: u32,
70	        affinity: usize,
71	        priority_class: u32,
72	        scheduling_class: u32,
73	    }
74	    #[repr(C)]
75	    #[derive(Default)]
76	    struct IoCounters {
77	        r: u64,
78	        w: u64,
79	        o: u64,
80	        rt: u64,
81	        wt: u64,
82	        ot: u64,
83	    }
84	    #[repr(C)]
85	    #[derive(Default)]
86	    struct ExtLimit {
87	        basic: BasicLimit,
88	        io: IoCounters,
89	        process_memory_limit: usize,
90	        job_memory_limit: usize,
91	        peak_process_memory_used: usize,
92	        peak_job_memory_used: usize,
93	    }
94	    extern "system" {
95	        fn CreateJobObjectW(a: *mut c_void, n: *const u16) -> isize;
96	        fn SetInformationJobObject(j: isize, c: u32, i: *const c_void, l: u32) -> i32;
97	        fn AssignProcessToJobObject(j: isize, p: isize) -> i32;
98	        fn TerminateJobObject(j: isize, code: u32) -> i32;
99	        fn CloseHandle(h: isize) -> i32;
100	    }
101
102	    fn kill_pid(pid: u32) {
103	        let _ = Command::new("taskkill")
104	            .no_window()
105	            .args(["/PID", &pid.to_string(), "/F", "/T"])
106	            .stdout(Stdio::null())
107	            .stderr(Stdio::null())
108	            .output();
109	    }
110
111	    fn ready_pid(path: &Path) -> Option<u32> {
112	        let s = std::fs::read_to_string(path).ok()?;
113	        serde_json::from_str::<serde_json::Value>(&s)
114	            .ok()?
115	            .get("pid")?
116	            .as_u64()
117	            .map(|p| p as u32)
118	    }
119
120	    /// Make a KILL_ON_JOB_CLOSE job (the "terminal" stand-in).
121	    fn make_kill_job() -> isize {
122	        let job = unsafe { CreateJobObjectW(std::ptr::null_mut(), std::ptr::null()) };
123	        assert!(job != 0, "create job object");
124	        let mut info = ExtLimit::default();
125	        info.basic.limit_flags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
126	        let ok = unsafe {
127	            SetInformationJobObject(
128	                job,
129	                JOB_OBJECT_EXTENDED_LIMIT_INFORMATION,
130	                &info as *const _ as *const c_void,
131	                std::mem::size_of::<ExtLimit>() as u32,
132	            )
133	        };
134	        assert!(ok != 0, "set KILL_ON_JOB_CLOSE");
135	        job
136	    }
137
138	    /// Assign `child` to `job`. Returns false on ACCESS_DENIED (the runner's
139	    /// ancestor job forbids nesting this child) → caller SKIPs rather than
140	    /// false-fail (the escape can't be isolated faithfully here).
141	    fn assign(job: isize, child: &Child) -> bool {
142	        let ok = unsafe { AssignProcessToJobObject(job, child.as_raw_handle() as isize) };
143	        if ok == 0 {
144	            let e = std::io::Error::last_os_error();
145	            if e.raw_os_error() == Some(ERROR_ACCESS_DENIED) {
146	                return false;
147	            }
148	            panic!("assign process to job: {e}");
149	        }
150	        true
151	    }
152
153	    #[test]
154	    fn cold_start_daemon_escapes_kill_on_close_job_via_wmi() {
155	        let home = tempfile::tempdir().unwrap();
156	        let spt_bin = PathBuf::from(env!("CARGO_BIN_EXE_spt"));
157	        let marker = home.path().join("launch_rung.marker");
158	        let _ = std::fs::remove_file(&marker);
159
160	        let job = make_kill_job();
161
162	        // ── (1) CONTROL: a long-lived process IN the job. Proves the job is
163	        //    genuinely lethal — if TerminateJobObject leaves this alive the test
164	        //    apparatus is broken and the daemon-survival result would be vacuous. ──
165	        let mut control: Child = Command::new("ping")
166	            .no_window()
167	            .args(["-n", "300", "127.0.0.1"])
168	            .stdout(Stdio::null())
169	            .stderr(Stdio::null())
170	            .spawn()
171	            .expect("spawn control ping");
172	        if !assign(job, &control) {
173	            let _ = control.kill();
174	            let _ = control.wait();
175	            unsafe { CloseHandle(job) };
176	            eprintln!(
177	                "SKIP cold_start_daemon_escapes_kill_on_close_job_via_wmi: runner ancestor \
178	                 job forbids assigning a child to a nested job (ACCESS_DENIED)"
179	            );
180	            return;
181	        }
182
183	        // ── (2) The launching CLI "in the terminal": `spt daemon start`, FORCING
184	        //    the WMI rung. SPT_HOME pins an isolated universe; the marker records
185	        //    which rung actually launched the daemon (asserted == WMI below). ──
186	        let mut start: Child = Command::new(&spt_bin)
187	            .no_window()
188	            .args(["daemon", "start"])
189	            .env("SPT_HOME", home.path())
190	            .env("SPT_FORCE_LAUNCH_RUNG", "wmi")
191	            .env("SPT_LAUNCH_RUNG_MARKER", &marker)
192	            .stdout(Stdio::null())
193	            .stderr(Stdio::null())
194	            .spawn()
195	            .expect("spawn spt daemon start");
196	        if !assign(job, &start) {
197	            let _ = start.kill();
198	            let _ = start.wait();
199	            let _ = control.kill();
200	            let _ = control.wait();
201	            unsafe { CloseHandle(job) };
202	            eprintln!(
203	                "SKIP cold_start_daemon_escapes_kill_on_close_job_via_wmi: cannot assign the \
204	                 start CLI to a nested job (ACCESS_DENIED)"
205	            );
206	            return;
207	        }
208
209	        // ── (3) Wait for the WMI-launched daemon to bind (brain.ready) → its pid. ──
210	        let brain_ready = home.path().join("brain.ready");
211	        let brain_pid = {
212	            let deadline = Instant::now() + Duration::from_secs(40);
213	            let mut found = None;
214	            while Instant::now() < deadline {
215	                if let Some(p) = ready_pid(&brain_ready) {
216	                    found = Some(p);
217	                    break;
218	                }
219	                std::thread::sleep(Duration::from_millis(100));
220	            }
221	            found
222	        };
223
224	        // `spt daemon start` returns once the daemon is up (or it timed out).
225	        let _ = start.wait();
226
227	        let rung = std::fs::read_to_string(&marker).unwrap_or_default();
228
229	        let brain_pid = match brain_pid {
230	            Some(p) => p,
231	            None => {
232	                let _ = control.kill();
233	                let _ = control.wait();
234	                unsafe { CloseHandle(job) };
235	                panic!(
236	                    "PRECONDITION: the WMI-launched daemon never bound brain.ready in the \
237	                     isolated SPT_HOME (rung marker={rung:?}). If SPT_HOME did not reach the \
238	                     WMI child, the daemon bound the wrong universe."
239	                );
240	            }
241	        };
242
243	        // Pre-terminate sanity: both alive.
244	        let control_alive_before = is_process_alive(control.id());
245	        let daemon_alive_before = is_process_alive(brain_pid);
246
247	        // ── (4) Terminate the job: everything still IN it dies. The daemon is a
248	        //    WmiPrvSE child (outside the job) → must SURVIVE. The control (in the
249	        //    job) → must DIE. Probe a window to defeat a lazy reap. ──
250	        unsafe { TerminateJobObject(job, 1) };
251
252	        // NOTE: the daemon pid is probed with `is_process_alive` (we hold no
253	        // handle to it → accurate). The control's death MUST be observed via
254	        // `try_wait`, NOT `is_process_alive`: we still hold the control `Child`
255	        // handle, so the OS keeps its process object (and `OpenProcess`) alive
256	        // after termination until the handle is closed — `is_process_alive` would
257	        // falsely report it alive.
258	        let mut control_died = false;
259	        let mut daemon_survived = true;
260	        for _ in 0..30 {
261	            std::thread::sleep(Duration::from_millis(50));
262	            if let Ok(Some(_)) = control.try_wait() {
263	                control_died = true;
264	            }
265	            if !is_process_alive(brain_pid) {
266	                daemon_survived = false;
267	                break;
268	            }
269	        }
270
271	        // ── (5) Reachability: the surviving daemon answers IPC (`daemon stop`
272	        //    returns only if the seed channel responded) — also the teardown. ──
273	        let stop = {
274	            let (tx, rx) = std::sync::mpsc::channel();
275	            let mut cmd = Command::new(&spt_bin);
276	            cmd.no_window()
277	                .args(["daemon", "stop"])
278	                .env("SPT_HOME", home.path())
279	                .stdout(Stdio::null())
280	                .stderr(Stdio::null());
281	            std::thread::spawn(move || {
282	                let _ = tx.send(cmd.output());
283	            });
284	            rx.recv_timeout(Duration::from_secs(20))
285	                .ok()
286	                .and_then(|r| r.ok())
287	        };
288	        let reachable = stop.as_ref().map(|o| o.status.success()).unwrap_or(false);
289
290	        // ── (6) Reap the ENTIRE WMI-launched daemon TREE, scoped (never
291	        //    machine-wide — shared CI runner). The WMI daemon is a tree, not one
292	        //    pid: the BROKER (`spt daemon run`) is a SEPARATE spt.exe — its pid is
293	        //    recorded in `<SPT_HOME>/daemon.pid` (daemon.rs) — and it SUPERVISES
294	        //    the brain, so killing `brain_pid` alone makes the broker RESPAWN a
295	        //    fresh brain stray while the broker itself lingers. Under WMI the
296	        //    broker is a WmiPrvSE descendant the terminal job can NEVER reap and
297	        //    `daemon stop` (step 5) is best-effort, so a surviving broker keeps
298	        //    `target\debug\spt.exe` locked and poisons the next CI job's checkout
299	        //    (EPERM unlink spt.exe). Hard-kill the broker TREE first (`/T` reaps
300	        //    its brain child AND defeats the supervisor respawn), then belt the
301	        //    recorded brain pid, then VERIFY both are gone before returning.
302	        let _ = control.kill();
303	        let _ = control.wait();
304	        let broker_pid = std::fs::read_to_string(home.path().join("daemon.pid"))
305	            .ok()
306	            .and_then(|s| s.trim().parse::<u32>().ok());
307	        if let Some(bp) = broker_pid {
308	            kill_pid(bp); // taskkill /F /T — broker + its supervised brain subtree
309	        }
310	        if is_process_alive(brain_pid) {
311	            kill_pid(brain_pid);
312	        }
313	        unsafe { CloseHandle(job) };
314
315	        // Deterministic hygiene gate: NO spt.exe from this test's tree may survive
316	        // (a survivor holds `target\debug\spt.exe` and breaks the next CI checkout).
317	        // Poll both recorded pids — broker (daemon.pid) and brain (brain.ready) —
318	        // dead. `taskkill /T` is prompt, so a 10s window is generous.
319	        let tree_reaped = {
320	            let deadline = Instant::now() + Duration::from_secs(10);
321	            loop {
322	                let broker_gone = broker_pid.map(|p| !is_process_alive(p)).unwrap_or(true);
323	                let brain_gone = !is_process_alive(brain_pid);
324	                if broker_gone && brain_gone {
325	                    break true;
326	                }
327	                if Instant::now() >= deadline {
328	                    break false;
329	                }
330	                std::thread::sleep(Duration::from_millis(100));
331	            }
332	        };
333
334	        eprintln!(
335	            "=== DIAGNOSTIC: rung_marker={rung:?} brain_pid={brain_pid} broker_pid={broker_pid:?} \
336	             control_alive_before={control_alive_before} daemon_alive_before={daemon_alive_before} \
337	             control_died={control_died} daemon_survived={daemon_survived} reachable={reachable} \
338	             tree_reaped={tree_reaped} ==="
339	        );
340
341	        // ── Assertions. ──
342	        assert!(daemon_alive_before, "PRECONDITION: daemon must be alive before the job terminate");
343	        // Non-vacuity: the job really does kill its members.
344	        assert!(
345	            control_died,
346	            "the in-job CONTROL process must DIE when the job is terminated — else the job is \
347	             not lethal and the survival result is vacuous"
348	        );
349	        // The load-bearing claim: WMI is the rung that fired (not a silent breakaway/in-job).
350	        // [int->REQ-HAZARD-VIEWER-CLOSE-DETACH]
351	        assert_eq!(
352	            rung, "WMI",
353	            "the daemon must have been launched via the WMI rung (forced) — a different rung \
354	             would make a green a false pass"
355	        );
356	        // The escape: the WMI-launched daemon OUTLIVES the killed terminal job.
357	        assert!(
358	            daemon_survived,
359	            "the WMI-launched daemon must OUTLIVE the killed kill-on-close job (it is a WmiPrvSE \
360	             child, outside the job) — closing the terminal must not reap it"
361	        );
362	        // And it is still reachable (a view is independent from the endpoint).
363	        assert!(
364	            reachable,
365	            "the surviving daemon must remain IPC-reachable after the terminal job closes"
366	        );
367	        // Test hygiene (the whole point of THIS fix): the entire WMI daemon tree —
368	        // BROKER (daemon.pid) + brain (brain.ready) — must be reaped before return.
369	        // A WMI daemon is JOB-NEUTRAL by design, so nothing else reaps it; a
370	        // survivor locks `target\debug\spt.exe` → next CI job's checkout dies with
371	        // "EPERM unlink spt.exe". [int->REQ-HAZARD-VIEWER-CLOSE-DETACH]
372	        assert!(
373	            tree_reaped,
374	            "the WMI-launched daemon TREE (broker {broker_pid:?} + brain {brain_pid}) must be \
375	             fully reaped before the test returns — a job-neutral survivor poisons the next CI \
376	             checkout (EPERM unlink spt.exe on the shared runner)"
377	        );
378	    }
379	}
380

//! Wave 2 (v0.12.1) **L1.5** — REQ-HAZARD-VIEWER-CLOSE-DETACH int gate: the
//! cold-started daemon escapes the launching terminal's kill-on-close Job Object
//! via the JOB-NEUTRAL launcher (WMI `Win32_Process.Create`), so closing the tab
//! can never reap it. Replaces the L1 breakaway-only mechanism (a job CAN deny
//! breakaway); breakaway is now a demoted fallback rung.
//!
//! ## Why this is now CI-testable (unlike the L1 breakaway int)
//!
//! Breakaway could not be proven in CI: the runner's own ancestor job forbids it,
//! and every nested test job confounds the escape — a structural false-red. WMI
//! is different: the daemon becomes a child of **WmiPrvSE**, outside ANY job,
//! regardless of the terminal job's breakaway policy. So we can faithfully:
//!
//!   1. create a real `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` job (the terminal),
//!   2. put a long-lived CONTROL process in it (proves the job is genuinely
//!      lethal — non-vacuity),
//!   3. put `spt daemon start` in it (the CLI invoked "in the terminal"), FORCING
//!      the WMI rung and asserting the `DAEMON_LAUNCH_VIA_WMI` marker fired (a
//!      green that silently ran breakaway/in-job would be a false pass),
//!   4. terminate the job and assert: the control process DIES (job is lethal)
//!      while the daemon STAYS ALIVE and remains IPC-reachable (`daemon stop`).
//!
//! Full `rc` re-attach after survival is covered by `dummy_harness_e2e` /
//! `attach_wedge_e2e` against the same daemon + transport; this gate's novel claim
//! is the job escape itself.
//!
//! All spawned pids are reaped SCOPED (never machine-wide — shared CI runner).

// Only the Windows `mod win` block uses the shared test helpers; on Unix the
// single test below is self-contained, so gating the import keeps `CommandNoWindowExt`
// from tripping `dead_code` under `-D warnings` on Linux CI.
#[cfg(windows)]
mod common;

// [int->REQ-HAZARD-VIEWER-CLOSE-DETACH] Unix uses the daemon's own session
// detachment (`setsid` in `spawn_detached`): a new session with no controlling
// terminal already survives terminal close (SIGHUP scope), so there is no Job
// Object analogue to escape — the invariant holds by construction, no code.
#[cfg(unix)]
#[test]
fn daemon_survives_terminal_close_via_session_detachment() {
    // Documented guard: the Windows job-escape ladder has no Unix counterpart;
    // `spawn_detached`'s `setsid` is the mechanism and is exercised by the unix
    // daemon-lifecycle E2Es. Nothing to assert here beyond recording the stage.
}

#[cfg(windows)]
mod win {
    use super::common::CommandNoWindowExt;
    use spt_store::proc::is_process_alive;
    use std::ffi::c_void;
    use std::os::windows::io::AsRawHandle;
    use std::path::{Path, PathBuf};
    use std::process::{Child, Command, Stdio};
    use std::time::{Duration, Instant};

    const JOB_OBJECT_EXTENDED_LIMIT_INFORMATION: u32 = 9;
    const JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE: u32 = 0x0000_2000;
    const ERROR_ACCESS_DENIED: i32 = 5;

    #[repr(C)]
    #[derive(Default)]
    struct BasicLimit {
        per_process_user_time_limit: i64,
        per_job_user_time_limit: i64,
        limit_flags: u32,
        minimum_working_set_size: usize,
        maximum_working_set_size: usize,
        active_process_limit: u32,
        affinity: usize,
        priority_class: u32,
        scheduling_class: u32,
    }
    #[repr(C)]
    #[derive(Default)]
    struct IoCounters {
        r: u64,
        w: u64,
        o: u64,
        rt: u64,
        wt: u64,
        ot: u64,
    }
    #[repr(C)]
    #[derive(Default)]
    struct ExtLimit {
        basic: BasicLimit,
        io: IoCounters,
        process_memory_limit: usize,
        job_memory_limit: usize,
        peak_process_memory_used: usize,
        peak_job_memory_used: usize,
    }
    extern "system" {
        fn CreateJobObjectW(a: *mut c_void, n: *const u16) -> isize;
        fn SetInformationJobObject(j: isize, c: u32, i: *const c_void, l: u32) -> i32;
        fn AssignProcessToJobObject(j: isize, p: isize) -> i32;
        fn TerminateJobObject(j: isize, code: u32) -> i32;
        fn CloseHandle(h: isize) -> i32;
    }

    fn kill_pid(pid: u32) {
        let _ = Command::new("taskkill")
            .no_window()
            .args(["/PID", &pid.to_string(), "/F", "/T"])
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .output();
    }

    fn ready_pid(path: &Path) -> Option<u32> {
        let s = std::fs::read_to_string(path).ok()?;
        serde_json::from_str::<serde_json::Value>(&s)
            .ok()?
            .get("pid")?
            .as_u64()
            .map(|p| p as u32)
    }

    /// Make a KILL_ON_JOB_CLOSE job (the "terminal" stand-in).
    fn make_kill_job() -> isize {
        let job = unsafe { CreateJobObjectW(std::ptr::null_mut(), std::ptr::null()) };
        assert!(job != 0, "create job object");
        let mut info = ExtLimit::default();
        info.basic.limit_flags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
        let ok = unsafe {
            SetInformationJobObject(
                job,
                JOB_OBJECT_EXTENDED_LIMIT_INFORMATION,
                &info as *const _ as *const c_void,
                std::mem::size_of::<ExtLimit>() as u32,
            )
        };
        assert!(ok != 0, "set KILL_ON_JOB_CLOSE");
        job
    }

    /// Assign `child` to `job`. Returns false on ACCESS_DENIED (the runner's
    /// ancestor job forbids nesting this child) → caller SKIPs rather than
    /// false-fail (the escape can't be isolated faithfully here).
    fn assign(job: isize, child: &Child) -> bool {
        let ok = unsafe { AssignProcessToJobObject(job, child.as_raw_handle() as isize) };
        if ok == 0 {
            let e = std::io::Error::last_os_error();
            if e.raw_os_error() == Some(ERROR_ACCESS_DENIED) {
                return false;
            }
            panic!("assign process to job: {e}");
        }
        true
    }

    #[test]
    fn cold_start_daemon_escapes_kill_on_close_job_via_wmi() {
        let home = tempfile::tempdir().unwrap();
        let spt_bin = PathBuf::from(env!("CARGO_BIN_EXE_spt"));
        let marker = home.path().join("launch_rung.marker");
        let _ = std::fs::remove_file(&marker);

        let job = make_kill_job();

        // ── (1) CONTROL: a long-lived process IN the job. Proves the job is
        //    genuinely lethal — if TerminateJobObject leaves this alive the test
        //    apparatus is broken and the daemon-survival result would be vacuous. ──
        let mut control: Child = Command::new("ping")
            .no_window()
            .args(["-n", "300", "127.0.0.1"])
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .spawn()
            .expect("spawn control ping");
        if !assign(job, &control) {
            let _ = control.kill();
            let _ = control.wait();
            unsafe { CloseHandle(job) };
            eprintln!(
                "SKIP cold_start_daemon_escapes_kill_on_close_job_via_wmi: runner ancestor \
                 job forbids assigning a child to a nested job (ACCESS_DENIED)"
            );
            return;
        }

        // ── (2) The launching CLI "in the terminal": `spt daemon start`, FORCING
        //    the WMI rung. SPT_HOME pins an isolated universe; the marker records
        //    which rung actually launched the daemon (asserted == WMI below). ──
        let mut start: Child = Command::new(&spt_bin)
            .no_window()
            .args(["daemon", "start"])
            .env("SPT_HOME", home.path())
            .env("SPT_FORCE_LAUNCH_RUNG", "wmi")
            .env("SPT_LAUNCH_RUNG_MARKER", &marker)
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .spawn()
            .expect("spawn spt daemon start");
        if !assign(job, &start) {
            let _ = start.kill();
            let _ = start.wait();
            let _ = control.kill();
            let _ = control.wait();
            unsafe { CloseHandle(job) };
            eprintln!(
                "SKIP cold_start_daemon_escapes_kill_on_close_job_via_wmi: cannot assign the \
                 start CLI to a nested job (ACCESS_DENIED)"
            );
            return;
        }

        // ── (3) Wait for the WMI-launched daemon to bind (brain.ready) → its pid. ──
        let brain_ready = home.path().join("brain.ready");
        let brain_pid = {
            let deadline = Instant::now() + Duration::from_secs(40);
            let mut found = None;
            while Instant::now() < deadline {
                if let Some(p) = ready_pid(&brain_ready) {
                    found = Some(p);
                    break;
                }
                std::thread::sleep(Duration::from_millis(100));
            }
            found
        };

        // `spt daemon start` returns once the daemon is up (or it timed out).
        let _ = start.wait();

        let rung = std::fs::read_to_string(&marker).unwrap_or_default();

        let brain_pid = match brain_pid {
            Some(p) => p,
            None => {
                let _ = control.kill();
                let _ = control.wait();
                unsafe { CloseHandle(job) };
                panic!(
                    "PRECONDITION: the WMI-launched daemon never bound brain.ready in the \
                     isolated SPT_HOME (rung marker={rung:?}). If SPT_HOME did not reach the \
                     WMI child, the daemon bound the wrong universe."
                );
            }
        };

        // Pre-terminate sanity: both alive.
        let control_alive_before = is_process_alive(control.id());
        let daemon_alive_before = is_process_alive(brain_pid);

        // ── (4) Terminate the job: everything still IN it dies. The daemon is a
        //    WmiPrvSE child (outside the job) → must SURVIVE. The control (in the
        //    job) → must DIE. Probe a window to defeat a lazy reap. ──
        unsafe { TerminateJobObject(job, 1) };

        // NOTE: the daemon pid is probed with `is_process_alive` (we hold no
        // handle to it → accurate). The control's death MUST be observed via
        // `try_wait`, NOT `is_process_alive`: we still hold the control `Child`
        // handle, so the OS keeps its process object (and `OpenProcess`) alive
        // after termination until the handle is closed — `is_process_alive` would
        // falsely report it alive.
        let mut control_died = false;
        let mut daemon_survived = true;
        for _ in 0..30 {
            std::thread::sleep(Duration::from_millis(50));
            if let Ok(Some(_)) = control.try_wait() {
                control_died = true;
            }
            if !is_process_alive(brain_pid) {
                daemon_survived = false;
                break;
            }
        }

        // ── (5) Reachability: the surviving daemon answers IPC (`daemon stop`
        //    returns only if the seed channel responded) — also the teardown. ──
        let stop = {
            let (tx, rx) = std::sync::mpsc::channel();
            let mut cmd = Command::new(&spt_bin);
            cmd.no_window()
                .args(["daemon", "stop"])
                .env("SPT_HOME", home.path())
                .stdout(Stdio::null())
                .stderr(Stdio::null());
            std::thread::spawn(move || {
                let _ = tx.send(cmd.output());
            });
            rx.recv_timeout(Duration::from_secs(20))
                .ok()
                .and_then(|r| r.ok())
        };
        let reachable = stop.as_ref().map(|o| o.status.success()).unwrap_or(false);

        // ── (6) Reap the ENTIRE WMI-launched daemon TREE, scoped (never
        //    machine-wide — shared CI runner). The WMI daemon is a tree, not one
        //    pid: the BROKER (`spt daemon run`) is a SEPARATE spt.exe — its pid is
        //    recorded in `<SPT_HOME>/daemon.pid` (daemon.rs) — and it SUPERVISES
        //    the brain, so killing `brain_pid` alone makes the broker RESPAWN a
        //    fresh brain stray while the broker itself lingers. Under WMI the
        //    broker is a WmiPrvSE descendant the terminal job can NEVER reap and
        //    `daemon stop` (step 5) is best-effort, so a surviving broker keeps
        //    `target\debug\spt.exe` locked and poisons the next CI job's checkout
        //    (EPERM unlink spt.exe). Hard-kill the broker TREE first (`/T` reaps
        //    its brain child AND defeats the supervisor respawn), then belt the
        //    recorded brain pid, then VERIFY both are gone before returning.
        let _ = control.kill();
        let _ = control.wait();
        let broker_pid = std::fs::read_to_string(home.path().join("daemon.pid"))
            .ok()
            .and_then(|s| s.trim().parse::<u32>().ok());
        if let Some(bp) = broker_pid {
            kill_pid(bp); // taskkill /F /T — broker + its supervised brain subtree
        }
        if is_process_alive(brain_pid) {
            kill_pid(brain_pid);
        }
        unsafe { CloseHandle(job) };

        // Deterministic hygiene gate: NO spt.exe from this test's tree may survive
        // (a survivor holds `target\debug\spt.exe` and breaks the next CI checkout).
        // Poll both recorded pids — broker (daemon.pid) and brain (brain.ready) —
        // dead. `taskkill /T` is prompt, so a 10s window is generous.
        let tree_reaped = {
            let deadline = Instant::now() + Duration::from_secs(10);
            loop {
                let broker_gone = broker_pid.map(|p| !is_process_alive(p)).unwrap_or(true);
                let brain_gone = !is_process_alive(brain_pid);
                if broker_gone && brain_gone {
                    break true;
                }
                if Instant::now() >= deadline {
                    break false;
                }
                std::thread::sleep(Duration::from_millis(100));
            }
        };

        eprintln!(
            "=== DIAGNOSTIC: rung_marker={rung:?} brain_pid={brain_pid} broker_pid={broker_pid:?} \
             control_alive_before={control_alive_before} daemon_alive_before={daemon_alive_before} \
             control_died={control_died} daemon_survived={daemon_survived} reachable={reachable} \
             tree_reaped={tree_reaped} ==="
        );

        // ── Assertions. ──
        assert!(daemon_alive_before, "PRECONDITION: daemon must be alive before the job terminate");
        // Non-vacuity: the job really does kill its members.
        assert!(
            control_died,
            "the in-job CONTROL process must DIE when the job is terminated — else the job is \
             not lethal and the survival result is vacuous"
        );
        // The load-bearing claim: WMI is the rung that fired (not a silent breakaway/in-job).
        // [int->REQ-HAZARD-VIEWER-CLOSE-DETACH]
        assert_eq!(
            rung, "WMI",
            "the daemon must have been launched via the WMI rung (forced) — a different rung \
             would make a green a false pass"
        );
        // The escape: the WMI-launched daemon OUTLIVES the killed terminal job.
        assert!(
            daemon_survived,
            "the WMI-launched daemon must OUTLIVE the killed kill-on-close job (it is a WmiPrvSE \
             child, outside the job) — closing the terminal must not reap it"
        );
        // And it is still reachable (a view is independent from the endpoint).
        assert!(
            reachable,
            "the surviving daemon must remain IPC-reachable after the terminal job closes"
        );
        // Test hygiene (the whole point of THIS fix): the entire WMI daemon tree —
        // BROKER (daemon.pid) + brain (brain.ready) — must be reaped before return.
        // A WMI daemon is JOB-NEUTRAL by design, so nothing else reaps it; a
        // survivor locks `target\debug\spt.exe` → next CI job's checkout dies with
        // "EPERM unlink spt.exe". [int->REQ-HAZARD-VIEWER-CLOSE-DETACH]
        assert!(
            tree_reaped,
            "the WMI-launched daemon TREE (broker {broker_pid:?} + brain {brain_pid}) must be \
             fully reaped before the test returns — a job-neutral survivor poisons the next CI \
             checkout (EPERM unlink spt.exe on the shared runner)"
        );
    }
}


The int harness gives me the exact pattern — broker pid is in `<SPT_HOME>/daemon.pid` (the `spt daemon run` process whose conhost is the window). Now find the clap `daemon run` definition + its dispatch + FFI deps for `FreeConsole`:


17:use clap::{Parser, Subcommand};
59:#[derive(Subcommand)]
215:        action: Option<DaemonCmd>,
226:#[derive(Subcommand)]
252:    Run {
415:#[derive(Subcommand)]
435:#[derive(Subcommand)]
436:enum DaemonCmd {
440:    Run,
473:#[derive(Subcommand)]
492:#[derive(Subcommand)]
622:#[derive(Subcommand)]
654:#[derive(Subcommand)]
760:#[derive(Subcommand)]
776:#[derive(Subcommand)]
796:#[derive(Subcommand)]
1112:            EndpointCmd::Run {
1166:        Cmd::Daemon { action } => match action.unwrap_or(DaemonCmd::Status) {
1167:            DaemonCmd::Run => cmd_daemon_run(),
1168:            DaemonCmd::Start => cmd_daemon_start(),
1169:            DaemonCmd::Stop => cmd_daemon_stop(),
1170:            DaemonCmd::Status => cmd_daemon_status(),
1171:            DaemonCmd::Brain {
1865:fn cmd_daemon_run() -> i32 {
8011:                action: Some(DaemonCmd::Run)
8018:                action: Some(DaemonCmd::Start)
8024:                action: Some(DaemonCmd::Stop)
8030:                action: Some(DaemonCmd::Status)
8041:                action: Some(DaemonCmd::Brain { generation: 0, ref start_reason })
8049:                action: Some(DaemonCmd::Brain { generation: 4, ref start_reason })


Grep

windows-sys|windows = |FreeConsole|extern \"system\"|kernel32


crates\spt-daemon\src\deelevate.rs:34://! the Windows calls are raw `extern "system"` declarations (advapi32 is in
crates\spt-daemon\src\deelevate.rs:418:    // Raw advapi32/kernel32 declarations (the detached_no_inherit posture:
crates\spt-daemon\src\deelevate.rs:448:    extern "system" {
crates\spt-daemon\src\deelevate.rs:482:    extern "system" {
crates\spt-daemon\src\deelevate.rs:491:    extern "system" {
crates\spt-daemon\src\reap.rs:160:    extern "system" {
crates\spt-daemon\src\daemon.rs:715:    extern "system" {
crates\spt-daemon\src\daemon.rs:1304:        extern "system" {
crates\spt-daemon\src\daemon.rs:1315:            extern "system" {
crates\spt-daemon\src\machineid.rs:57:/// advapi32 extern — the `deelevate` posture, no windows-sys dependency.
crates\spt-daemon\src\machineid.rs:66:    extern "system" {


No `windows-sys` — codebase uses raw `extern "system"` kernel32 declarations. `FreeConsole` is kernel32. Read the `DaemonCmd::Run` clap variant + a hidden-flag example:


435	#[derive(Subcommand)]
436	enum DaemonCmd {
437	    /// Run the per-machine daemon in the FOREGROUND — this process IS the
438	    /// daemon, blocking until signalled (the service unit's ExecStart, or
439	    /// manual debugging). Never detaches; for a background daemon use `start`.
440	    Run,
441	    /// Ensure the daemon is up in the background (idempotent, service-aware):
442	    /// a registered OS service is driven via its manager, else a detached
443	    /// daemon is spawned. Non-blocking.
444	    Start,
445	    /// Stop the daemon (service-aware: a managed service is stopped via its
446	    /// manager so it does not auto-restart-fight; else a graceful IPC stop).
447	    Stop,
448	    /// Node status: daemon state, member subnets, local endpoints (the
449	    /// bare `spt daemon` view).
450	    Status,
451	    /// Hidden (ADR-0018 D1): the brain child process. The broker (`daemon run`)
452	    /// spawns + supervises this; it connects to the broker over the local IPC,
453	    /// signals ready, and idles. Not for direct user invocation.
454	    ///
455	    /// The broker stamps `--generation`/`--start-reason` at spawn (ADR-0018 V2
456	    /// generation custody + the Q4 update-vs-crash discriminator). Both are
457	    /// **defaulted** so an older broker that spawns this brain with the bare old
458	    /// argv still parses (generation 0 / `cold`) — the new-brain × old-broker
459	    /// N-1 steady state (KH-2.3 handoff-argv-compat).
460	    #[command(hide = true)]
461	    Brain {
462	        /// Broker-owned generation counter for this brain instance (KH-2.4).
463	        #[arg(long, default_value_t = 0)]
464	        generation: u64,
465	        /// Why the broker spawned this brain: `cold` | `crash` | `update`
466	        /// (parsed leniently — an unknown value defaults to `cold`).
467	        #[arg(long, default_value = "cold")]
468	        start_reason: String,
469	    },

#[derive(Subcommand)]
enum DaemonCmd {
    /// Run the per-machine daemon in the FOREGROUND — this process IS the
    /// daemon, blocking until signalled (the service unit's ExecStart, or
    /// manual debugging). Never detaches; for a background daemon use `start`.
    Run,
    /// Ensure the daemon is up in the background (idempotent, service-aware):
    /// a registered OS service is driven via its manager, else a detached
    /// daemon is spawned. Non-blocking.
    Start,
    /// Stop the daemon (service-aware: a managed service is stopped via its
    /// manager so it does not auto-restart-fight; else a graceful IPC stop).
    Stop,
    /// Node status: daemon state, member subnets, local endpoints (the
    /// bare `spt daemon` view).
    Status,
    /// Hidden (ADR-0018 D1): the brain child process. The broker (`daemon run`)
    /// spawns + supervises this; it connects to the broker over the local IPC,
    /// signals ready, and idles. Not for direct user invocation.
    ///
    /// The broker stamps `--generation`/`--start-reason` at spawn (ADR-0018 V2
    /// generation custody + the Q4 update-vs-crash discriminator). Both are
    /// **defaulted** so an older broker that spawns this brain with the bare old
    /// argv still parses (generation 0 / `cold`) — the new-brain × old-broker
    /// N-1 steady state (KH-2.3 handoff-argv-compat).
    #[command(hide = true)]
    Brain {
        /// Broker-owned generation counter for this brain instance (KH-2.4).
        #[arg(long, default_value_t = 0)]
        generation: u64,
        /// Why the broker spawned this brain: `cold` | `crash` | `update`
        /// (parsed leniently — an unknown value defaults to `cold`).
        #[arg(long, default_value = "cold")]
        start_reason: String,
    },


710	        process: isize,
711	        thread: isize,
712	        pid: u32,
713	        tid: u32,
714	    }
715	    extern "system" {
716	        fn CreateProcessW(
717	            app: *const u16,
718	            cmdline: *mut u16,
719	            process_attrs: *mut core::ffi::c_void,
720	            thread_attrs: *mut core::ffi::c_void,
721	            inherit_handles: i32,
722	            flags: u32,
723	            env: *mut core::ffi::c_void,
724	            cwd: *const u16,
725	            startup: *mut StartupInfoW,
726	            out: *mut ProcessInformation,
727	        ) -> i32;
728	        fn CloseHandle(h: isize) -> i32;
729	    }
730	    // DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW: no
731	    // inherited console, no popup window, its own signal group.

        process: isize,
        thread: isize,
        pid: u32,
        tid: u32,
    }
    extern "system" {
        fn CreateProcessW(
            app: *const u16,
            cmdline: *mut u16,
            process_attrs: *mut core::ffi::c_void,
            thread_attrs: *mut core::ffi::c_void,
            inherit_handles: i32,
            flags: u32,
            env: *mut core::ffi::c_void,
            cwd: *const u16,
            startup: *mut StartupInfoW,
            out: *mut ProcessInformation,
        ) -> i32;
        fn CloseHandle(h: isize) -> i32;
    }
    // DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW: no
    // inherited console, no popup window, its own signal group.


Full fix design locked. doyle wants literal repro-first (RED int pre-fix), and I must avoid src/test collision with the engineer. So I'll sequence: **Phase 1** — engineer writes the int assert in `job_escape_e2e.rs` (a file my fix never touches), runs it against current (unfixed) code → confirm RED. **Phase 2** — I apply the fix + registry. **Phase 3** — engineer adds the arg-parse unit + re-runs int GREEN + gates.

Dispatching Phase 1 (engineer owns `job_escape_e2e.rs`; I touch only the registry/grounding meanwhile — no collision):


Agent

RED int: WMI daemon conhost window

Prompt:
REPRO-FIRST regression int for REQ-HAZARD-WMI-DAEMON-WINDOW (doyle root-caused; daemon console WINDOW respawns after stop). You write ONE new sibling test and confirm it is RED against the CURRENT (unfixed) code. Do NOT modify any non-test source — the fix lands in a later step.

THE BUG (confirmed deterministic by doyle, 3/3): on Windows, the daemon auto-start ladder's WMI rung runs `cmd.exe /c set SPT_*… & start "" /b spt daemon run`. A prior fix (@15fdf58) set DETACHED_PROCESS on the cmd WRAPPER, but `start /b spt daemon run` then launches the inner BROKER (a console-subsystem exe) with no console to inherit → the broker ALLOCATES ITS OWN conhost.exe = a console WINDOW. The conhost is a direct CHILD of the `daemon run` (broker) pid.

FILE: `crates/spt/tests/job_escape_e2e.rs`, inside the existing `#[cfg(windows)] mod win`. Add a SIBLING test `fn wmi_daemon_run_has_no_conhost_window()` tagged `// [int->REQ-HAZARD-WMI-DAEMON-WINDOW]` immediately above it. REUSE the existing helpers/patterns in that file (study `cold_start_daemon_escapes_kill_on_close_job_via_wmi` — it is the template).

THE TEST must:
1. WMI-cold-start the daemon into an ISOLATED SPT_HOME: spawn `spt daemon start` with envs `SPT_HOME=<tempdir>`, `SPT_FORCE_LAUNCH_RUNG=wmi`, `SPT_LAUNCH_RUNG_MARKER=<marker>`. (No Job Object needed — the window bug is independent of the job-escape claim.)
2. Wait for `<SPT_HOME>/brain.ready` (the brain pid) AND read the BROKER pid from `<SPT_HOME>/daemon.pid` (the `spt daemon run` process — ITS conhost is the window). Assert the rung marker == "WMI" (else false-pass). PRECONDITION-panic if the daemon never bound (same as the template).
3. THE CLAIM: assert the broker pid has NO `conhost.exe` child. Detect children via the ABSOLUTE powershell path (`C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe`, the codebase's no-bare-powershell rule) running e.g. `Get-CimInstance Win32_Process -Filter "ParentProcessId=<broker_pid>" | Where-Object Name -eq 'conhost.exe'` and counting matches; OR a CreateToolhelp32Snapshot FFI loop matching th32ParentProcessID==broker_pid && szExeFile=="conhost.exe". POLL with a short deadline (a few seconds) so post-fix the assert waits for the conhost to exit — but pre-fix it will stay present the whole window → RED. Use `.no_window()` on every helper Command (CommandNoWindowExt is already imported).
4. HYGIENE (CRITICAL — non-negotiable, a survivor poisons the next CI checkout with EPERM unlink spt.exe): reap the ENTIRE WMI daemon tree before returning EXACTLY like the template's step (6) — taskkill /F /T the broker pid (daemon.pid), belt the brain pid, then poll-verify BOTH dead within a deadline; assert `tree_reaped`. Also try a graceful `daemon stop` first (best-effort) like the template. Do this teardown via a scopeguard-style path so it runs even if the conhost assert fails (the RED case) — e.g. compute the assert result into a bool, do the full reap, THEN assert. Never leave a stray.
5. Same ACCESS_DENIED SKIP discipline only if you reuse job assignment (you likely don't need a job here — skip the job entirely).

RUN IT: `cargo test -p spt --test job_escape_e2e wmi_daemon_run_has_no_conhost_window -- --nocapture` against current code. EXPECT RED — the no-conhost assert FAILS because the WMI broker has a conhost window today. Capture the diagnostic line + the failure. This is the repro-first RED; doyle wants it landed before the fix.

DISCIPLINE: do NOT run bare `cargo test` across the workspace (spawns the real daemon — runner-contract hazard); scope to `-p spt --test job_escape_e2e`. This test DOES spawn a real daemon by design (that is the point) — that is allowed for THIS targeted invocation. Do NOT use Monitor. Cap at 4 iterations. If the daemon won't bind in your environment (no WMI / not Windows-capable), STOP and report that you could not exercise it — do NOT fake a result.

REPORT: the full test fn (verbatim), the run result (RED with the assertion message + diagnostic, or the could-not-exercise reason), and confirm you touched ONLY job_escape_e2e.rs.


<owl_reachability_notice>
You have an active owl perch. Launch this Agent with run_in_background: true so you stay reachable for incoming messages while it works.
</owl_reachability_notice>


Async agent launched successfully.
agentId: a62bb6aa3f381c5e7 (internal ID - do not mention to user. Use SendMessage with to: 'a62bb6aa3f381c5e7' to continue this agent.)
The agent is working in the background. You will be notified automatically when it completes.
Do not duplicate this agent's work — avoid working with the same files or topics it is using. Work on non-overlapping tasks, or briefly tell the user what you launched and end your response.
output_file: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\73618172-412e-4954-b8aa-e2c8955dfd49\tasks\a62bb6aa3f381c5e7.output
Do NOT Read or tail this file via the shell tool — it is the full subagent JSONL transcript and reading it will overflow your context. If the user asks for progress, say the agent is still running; you'll get a completion notification.

RED int: WMI daemon conhost window

Prompt:
REPRO-FIRST regression int for REQ-HAZARD-WMI-DAEMON-WINDOW (doyle root-caused; daemon console WINDOW respawns after stop). You write ONE new sibling test and confirm it is RED against the CURRENT (unfixed) code. Do NOT modify any non-test source — the fix lands in a later step.

THE BUG (confirmed deterministic by doyle, 3/3): on Windows, the daemon auto-start ladder's WMI rung runs `cmd.exe /c set SPT_*… & start "" /b spt daemon run`. A prior fix (@15fdf58) set DETACHED_PROCESS on the cmd WRAPPER, but `start /b spt daemon run` then launches the inner BROKER (a console-subsystem exe) with no console to inherit → the broker ALLOCATES ITS OWN conhost.exe = a console WINDOW. The conhost is a direct CHILD of the `daemon run` (broker) pid.

FILE: `crates/spt/tests/job_escape_e2e.rs`, inside the existing `#[cfg(windows)] mod win`. Add a SIBLING test `fn wmi_daemon_run_has_no_conhost_window()` tagged `// [int->REQ-HAZARD-WMI-DAEMON-WINDOW]` immediately above it. REUSE the existing helpers/patterns in that file (study `cold_start_daemon_escapes_kill_on_close_job_via_wmi` — it is the template).

THE TEST must:
1. WMI-cold-start the daemon into an ISOLATED SPT_HOME: spawn `spt daemon start` with envs `SPT_HOME=<tempdir>`, `SPT_FORCE_LAUNCH_RUNG=wmi`, `SPT_LAUNCH_RUNG_MARKER=<marker>`. (No Job Object needed — the window bug is independent of the job-escape claim.)
2. Wait for `<SPT_HOME>/brain.ready` (the brain pid) AND read the BROKER pid from `<SPT_HOME>/daemon.pid` (the `spt daemon run` process — ITS conhost is the window). Assert the rung marker == "WMI" (else false-pass). PRECONDITION-panic if the daemon never bound (same as the template).
3. THE CLAIM: assert the broker pid has NO `conhost.exe` child. Detect children via the ABSOLUTE powershell path (`C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe`, the codebase's no-bare-powershell rule) running e.g. `Get-CimInstance Win32_Process -Filter "ParentProcessId=<broker_pid>" | Where-Object Name -eq 'conhost.exe'` and counting matches; OR a CreateToolhelp32Snapshot FFI loop matching th32ParentProcessID==broker_pid && szExeFile=="conhost.exe". POLL with a short deadline (a few seconds) so post-fix the assert waits for the conhost to exit — but pre-fix it will stay present the whole window → RED. Use `.no_window()` on every helper Command (CommandNoWindowExt is already imported).
4. HYGIENE (CRITICAL — non-negotiable, a survivor poisons the next CI checkout with EPERM unlink spt.exe): reap the ENTIRE WMI daemon tree before returning EXACTLY like the template's step (6) — taskkill /F /T the broker pid (daemon.pid), belt the brain pid, then poll-verify BOTH dead within a deadline; assert `tree_reaped`. Also try a graceful `daemon stop` first (best-effort) like the template. Do this teardown via a scopeguard-style path so it runs even if the conhost assert fails (the RED case) — e.g. compute the assert result into a bool, do the full reap, THEN assert. Never leave a stray.
5. Same ACCESS_DENIED SKIP discipline only if you reuse job assignment (you likely don't need a job here — skip the job entirely).

RUN IT: `cargo test -p spt --test job_escape_e2e wmi_daemon_run_has_no_conhost_window -- --nocapture` against current code. EXPECT RED — the no-conhost assert FAILS because the WMI broker has a conhost window today. Capture the diagnostic line + the failure. This is the repro-first RED; doyle wants it landed before the fix.

DISCIPLINE: do NOT run bare `cargo test` across the workspace (spawns the real daemon — runner-contract hazard); scope to `-p spt --test job_escape_e2e`. This test DOES spawn a real daemon by design (that is the point) — that is allowed for THIS targeted invocation. Do NOT use Monitor. Cap at 4 iterations. If the daemon won't bind in your environment (no WMI / not Windows-capable), STOP and report that you could not exercise it — do NOT fake a result.

REPORT: the full test fn (verbatim), the run result (RED with the assertion message + diagnostic, or the could-not-exercise reason), and confirm you touched ONLY job_escape_e2e.rs.


Phase 1 engineer running (owns `job_escape_e2e.rs`). I must keep the bin unfixed until its RED confirms. Meanwhile, prep Phase 2 (read-only, non-conflicting): confirm how to expose `detach_console` + read the current REQ-HAZARD-WMI-DAEMON-WINDOW registry entry.


<owl_messages>
<owl_message from="todlando-w177" priority="info">[WORKING_PERCH_NOTICE] Subagent working perch created.
Perch: todlando-w177
Agent type: spt-test-engineer
To message it: $OWL send todlando-w177 todlando</owl_message>
</owl_messages>

[OWL SYSTEM] Informational owl messages above — no acknowledgment needed. Continue your current task.


106:pub mod access;
107:pub mod adapter_update;
108:pub mod applyhost;
109:pub mod attach;
110:pub mod brain;
111:pub mod brainproc;
112:pub mod broker;
113:pub mod codec;
114:pub mod config;
115:pub mod consent;
116:pub mod daemon;
117:pub mod deadline;
118:pub mod deelevate;
119:pub mod digest;
120:pub mod digesthub;
121:pub mod dispatch;
122:pub mod drivehub;
123:pub mod effect;
124:pub mod endpoint;
125:pub mod frame;
126:pub mod grants;
127:pub mod harnesshost;
128:pub mod lifecycle;
129:pub mod linkhost;
130:pub mod livehost;
131:pub mod machineid;
132:pub mod msg;
133:pub mod nethost;
134:pub mod notif;
135:pub mod notifsync;
136:pub mod pairhost;
137:pub mod presence;
138:pub mod propagate;
139:pub mod psyrelay;
140:pub mod pump;
141:pub mod reap;
142:pub mod reconcile;
143:pub mod registryhost;
144:pub mod relay;
145:pub mod relcache;

[Showing results with pagination = limit: 40]


1171:id = "REQ-HAZARD-WMI-DAEMON-WINDOW"
1172-[Omitted long context line]
1173-[Omitted long context line]
1174-


`pub mod daemon` confirmed — `spt_daemon::daemon::detach_console()` will work. Read the full registry entry to prep the Phase-2 reactivation:


1171	id = "REQ-HAZARD-WMI-DAEMON-WINDOW"
1172	title = "`spt daemon start` launches the daemon with NO visible console window. REGRESSION (v0.12.1 L1.5): the WMI job-neutral launch (spawn_daemon_via_wmi) set CREATE_NO_WINDOW on the launching powershell but NOT on the Win32_Process.Create call — Win32_Process.Create does not inherit it, so the spawned cmd.exe env-forwarding wrapper popped a console window on every cold-start (violating REQ-INSTALL-10's v0.7.4 no-persistent-window invariant; the old detached_no_inherit path set DETACHED_PROCESS|CREATE_NO_WINDOW). FIX: pass a Win32_ProcessStartup with CreateFlags=DETACHED_PROCESS (0x8 — no console so no window; CREATE_NO_WINDOW 0x08000000 is NOT a valid Win32_ProcessStartup flag → ReturnValue 21 invalid-param, which is why the naive port fails) + ShowWindow=SW_HIDE(0) belt, via the ProcessStartupInformation argument. (v0.12.2)"
1173	required_stages = ["impl", "unit"]  # activated v0.12.2: impl = wmi_create_script builds the Win32_ProcessStartup no-window spec (CreateFlags=8 DETACHED + ShowWindow=0) passed as ProcessStartupInformation to Win32_Process.Create. unit = wmi_create_script_carries_the_no_window_startup_spec (spec present + passed + DETACHED, never the invalid CREATE_NO_WINDOW). WMI launch VALIDITY (a bad spec → ReturnValue!=0 → no daemon) is independently guarded by job_escape_e2e (the REQ-HAZARD-VIEWER-CLOSE-DETACH int red-flagged the invalid CREATE_NO_WINDOW during the fix).
1174

id = "REQ-HAZARD-WMI-DAEMON-WINDOW"
title = "`spt daemon start` launches the daemon with NO visible console window. REGRESSION (v0.12.1 L1.5): the WMI job-neutral launch (spawn_daemon_via_wmi) set CREATE_NO_WINDOW on the launching powershell but NOT on the Win32_Process.Create call — Win32_Process.Create does not inherit it, so the spawned cmd.exe env-forwarding wrapper popped a console window on every cold-start (violating REQ-INSTALL-10's v0.7.4 no-persistent-window invariant; the old detached_no_inherit path set DETACHED_PROCESS|CREATE_NO_WINDOW). FIX: pass a Win32_ProcessStartup with CreateFlags=DETACHED_PROCESS (0x8 — no console so no window; CREATE_NO_WINDOW 0x08000000 is NOT a valid Win32_ProcessStartup flag → ReturnValue 21 invalid-param, which is why the naive port fails) + ShowWindow=SW_HIDE(0) belt, via the ProcessStartupInformation argument. (v0.12.2)"
required_stages = ["impl", "unit"]  # activated v0.12.2: impl = wmi_create_script builds the Win32_ProcessStartup no-window spec (CreateFlags=8 DETACHED + ShowWindow=0) passed as ProcessStartupInformation to Win32_Process.Create. unit = wmi_create_script_carries_the_no_window_startup_spec (spec present + passed + DETACHED, never the invalid CREATE_NO_WINDOW). WMI launch VALIDITY (a bad spec → ReturnValue!=0 → no daemon) is independently guarded by job_escape_e2e (the REQ-HAZARD-VIEWER-CLOSE-DETACH int red-flagged the invalid CREATE_NO_WINDOW during the fix).


Phase 1 engineer running (RED int in `job_escape_e2e.rs`). Bin must stay unfixed until its RED confirms, so I'm holding the fix. Phase 2 is fully prepped:

- `daemon.rs:586` — append `--detached` to the windows auto-start args (flows to all rungs)
- `daemon.rs` — new `pub fn detach_console()` (raw `FreeConsole` kernel32 FFI, codebase's no-`windows-sys` posture)
- `cli.rs:440` — `Run { #[arg(long, hide=true)] detached: bool }` + dispatch + `cmd_daemon_run(detached)` calling `detach_console()` at entry when set (foreground human `daemon run` keeps its console)
- `traceable-reqs.toml:1173` — reactivate `[impl,unit,int]` + extend note (registry-first, in the fix commit)

**Root cause** (matches doyle): @15fdf58 set DETACHED_PROCESS on the `cmd` wrapper, but `start /b spt daemon run` launched the inner broker with no console → it allocated its own conhost. Broker `FreeConsole()` is the launch-agnostic fix; no-op on the already-windowless breakaway/injob rungs, preserves foreground debug.

Waiting on the engineer's RED confirmation before applying the fix (Phase 2) → then Phase 3 (unit + GREEN + gates) → doyle gate.


<task-notification>
<task-id>a62bb6aa3f381c5e7</task-id>
<tool-use-id>toolu_01E9LE4wvUiBKQQ8qKcovKKD</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\73618172-412e-4954-b8aa-e2c8955dfd49\tasks\a62bb6aa3f381c5e7.output</output-file>
<status>completed</status>
<summary>Agent "RED int: WMI daemon conhost window" came to rest</summary>
<note>A task-notification fires each time this agent comes to rest with no live background children of its own. The user can send it another message and resume it, so the same task-id may notify more than once.</note>
<result>Confirmed: only `crates/spt/tests/job_escape_e2e.rs` is modified. No non-test source touched.

## Report

**Repro-first RED landed and confirmed.** I added one sibling test `wmi_daemon_run_has_no_conhost_window()` (tagged `// [int->REQ-HAZARD-WMI-DAEMON-WINDOW]`) immediately above the template `cold_start_daemon_escapes_kill_on_close_job_via_wmi`, reusing its helpers (`kill_pid`, `ready_pid`, `is_process_alive`, `CommandNoWindowExt`) and adding one local helper `conhost_children_of` (absolute-powershell `Get-CimInstance Win32_Process -Filter "ParentProcessId=<broker>"` filtered to `conhost.exe`, `.no_window()`).

**Run result — RED, deterministic (doyle's root cause reproduced):**
```
=== DIAGNOSTIC: rung_marker="WMI" broker_pid=189460 brain_pid=120400 no_conhost=false final_conhost_count=1 tree_reaped=true ===
panicked at crates\spt\tests\job_escape_e2e.rs:344:
the WMI-launched broker (`spt daemon run`, pid 189460) must have NO conhost.exe child ... Found 1 conhost child(ren).
test result: FAILED. 0 passed; 1 failed
```
- `rung_marker="WMI"` proves the forced WMI rung fired — not a false-pass.
- `final_conhost_count=1` is the smoking gun: the broker has a direct `conhost.exe` child = the allocated console window, because `start "" /b spt daemon run` launches the console-subsystem broker with no console to inherit (the @15fdf58 DETACHED_PROCESS is on the cmd wrapper, not the broker).
- `no_conhost=false` after a 5s poll confirms it is the durable window, not a startup flicker.
- `tree_reaped=true` confirms hygiene held on the RED path: I compute the assert into a bool, run the full graceful-stop → `taskkill /F /T` broker → belt brain → poll-verify reap, THEN assert. No stray `spt.exe` survives to EPERM-lock the next CI checkout.

**Discipline honored:** scoped to `-p spt --test job_escape_e2e wmi_daemon_run_has_no_conhost_window` (never bare `cargo test`); no Monitor; one run iteration; no non-test source modified; no Job Object used (the window bug is independent of the job-escape claim). The fix lands in a later step.

**Note for the fix step:** `REQ-HAZARD-WMI-DAEMON-WINDOW` in `traceable-reqs.toml:1171-1173` currently has `required_stages = ["impl", "unit"]` — it will need `"int"` added (and this test's tag wired in) when the fix is declared done, since there is now an `int` evidence tag for it.

File: `C:\Users\decid\Documents\projects\spt-core\crates\spt\tests\job_escape_e2e.rs` (the new test fn spans the region ending at the asserts around line 344).</result>
<usage><subagent_tokens>68680</subagent_tokens><tool_uses>11</tool_uses><duration_ms>107176</duration_ms></usage>
</task-notification>


<owl_reachability_notice>
You have an active owl perch. When spawning subagents or doing work, use run_in_background: true so you stay reachable for incoming messages.
</owl_reachability_notice>


CAVEMAN MODE ACTIVE (full). Drop articles/filler/pleasantries/hedging. Fragments OK. Code/commits/security: write normal.


RED confirmed deterministically: `rung=WMI`, `final_conhost_count=1` (the broker's own conhost = the window), `no_conhost=false` after 5s (durable, not flicker), `tree_reaped=true` (hygiene held). Repro-first satisfied. **Phase 2 — apply the fix** (engineer idle, owns only the test file; no collision):


Edit

        let exe = exe.to_string_lossy();
        let args = ["daemon".to_string(), "run".to_string()];
        match crate::deelevate::spawn_deelevated(&exe, &args) {

        let exe = exe.to_string_lossy();
        // `--detached` (hidden) tells the spawned broker it was launched by the
        // auto-start ladder → it FreeConsole()s its own console at startup
        // (REQ-HAZARD-WMI-DAEMON-WINDOW). The WMI/schtasks rungs run the broker via
        // a `cmd /c … start /b spt daemon run` wrapper; @15fdf58 detached the cmd
        // WRAPPER, but the inner broker (a console-subsystem exe) still ALLOCATES its
        // own conhost = a window. The flag rides every rung (spawn_deelevated +
        // launch_daemon_job_neutral); a human-typed `spt daemon run` omits it and
        // keeps its foreground console (debug). No-op on the breakaway/injob rungs
        // (already DETACHED_PROCESS → no console → FreeConsole is harmless).
        let args = [
            "daemon".to_string(),
            "run".to_string(),
            "--detached".to_string(),
        ];
        match crate::deelevate::spawn_deelevated(&exe, &args) {


The file C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\daemon.rs has been updated successfully. (file state is current in your context — no need to Read it back)

        let exe = exe.to_string_lossy();
        let args = ["daemon".to_string(), "run".to_string()];
        match crate::deelevate::spawn_deelevated(&exe, &args) {

        let exe = exe.to_string_lossy();
        // `--detached` (hidden) tells the spawned broker it was launched by the
        // auto-start ladder → it FreeConsole()s its own console at startup
        // (REQ-HAZARD-WMI-DAEMON-WINDOW). The WMI/schtasks rungs run the broker via
        // a `cmd /c … start /b spt daemon run` wrapper; @15fdf58 detached the cmd
        // WRAPPER, but the inner broker (a console-subsystem exe) still ALLOCATES its
        // own conhost = a window. The flag rides every rung (spawn_deelevated +
        // launch_daemon_job_neutral); a human-typed `spt daemon run` omits it and
        // keeps its foreground console (debug). No-op on the breakaway/injob rungs
        // (already DETACHED_PROCESS → no console → FreeConsole is harmless).
        let args = [
            "daemon".to_string(),
            "run".to_string(),
            "--detached".to_string(),
        ];
        match crate::deelevate::spawn_deelevated(&exe, &args) {

-        let args = ["daemon".to_string(), "run".to_string()];

+        // `--detached` (hidden) tells the spawned broker it was launched by the

+        // auto-start ladder → it FreeConsole()s its own console at startup

+        // (REQ-HAZARD-WMI-DAEMON-WINDOW). The WMI/schtasks rungs run the broker via

+        // a `cmd /c … start /b spt daemon run` wrapper; @15fdf58 detached the cmd

+        // WRAPPER, but the inner broker (a console-subsystem exe) still ALLOCATES its

+        // own conhost = a window. The flag rides every rung (spawn_deelevated +

+        // keeps its foreground console (debug). No-op on the breakaway/injob rungs

+        // (already DETACHED_PROCESS → no console → FreeConsole is harmless).

Ok(Some(_pid)) => return Ok(()), // de-elevated child running


Now add the `detach_console()` FFI helper after `launch_daemon_job_neutral`:


/// Build the command line a job-neutral creator (WMI / Task Scheduler) runs to
/// launch the daemon.

/// Detach the calling process from its console (`FreeConsole`) — the broker's
/// self-detach for the auto-start ladder's window-allocating rungs. The WMI /
/// schtasks rungs launch the broker via `cmd /c … start "" /b spt daemon run`;
/// @15fdf58 set DETACHED_PROCESS on the cmd WRAPPER, but `start /b` then launched
/// the inner broker (a console-subsystem exe) with no console to inherit, so it
/// ALLOCATED ITS OWN conhost = a visible window (REQ-HAZARD-WMI-DAEMON-WINDOW;
/// violating REQ-INSTALL-10's no-persistent-window invariant). Calling FreeConsole
/// at broker startup — gated on the hidden `--detached` flag the ladder appends —
/// drops that console so the conhost exits. Launch-agnostic + idempotent: a no-op
/// on the breakaway/injob rungs (already DETACHED_PROCESS ⇒ no console ⇒ FreeConsole
/// returns 0/err, ignored), and a foreground human `spt daemon run` omits the flag
/// so its terminal view is preserved.
// [impl->REQ-HAZARD-WMI-DAEMON-WINDOW]
#[cfg(windows)]
pub fn detach_console() {
    extern "system" {
        fn FreeConsole() -> i32;
    }
    // SAFETY: a parameterless kernel32 call; failure (no console attached on the
    // already-windowless rungs) is expected and ignored.
    unsafe {
        let _ = FreeConsole();
    }
}

/// Build the command line a job-neutral creator (WMI / Task Scheduler) runs to
/// launch the daemon.