Skip to main content

supercode_harness/
orchestrator.rs

1//! The orchestrator's home, its daemon lease, and the service unit the
2//! operator verbs print (ORC-7).
3//!
4//! supercode does not perform orchestration; the `supercode-orchestrator`
5//! package does (`docs/ORCHESTRATOR-IR.md` §0.1). This module holds the three
6//! facts supercode's own surfaces need about it:
7//!
8//! * **where its state lives** — `SUPERCODE_ORCHESTRATOR_HOME`, default
9//!   `~/.supercode/orchestrator`, resolved once in
10//!   [`crate::HarnessHomes::orchestrator`]; the root folder IS the `default`
11//!   profile and `profiles/<name>/` are the named ones
12//!   (`docs/ORCHESTRATOR-IR.md` §6).
13//! * **whether the daemon is up** — the lease file `<home>/orchestrator.lock`,
14//!   plus a liveness signal on the pid it names. A lock whose process is gone
15//!   is stale, never "up".
16//! * **what a service unit for it would say, and whether it is installed** —
17//!   [`service_unit`] renders the launchd plist / systemd unit `setup` writes
18//!   under `<home>/service/`; [`install_service`], [`uninstall_service`] and
19//!   [`service_status`] drive `launchctl` / `systemctl --user` over it.
20//!
21//! The lease FILE is written by the daemon itself (`bin/orchestrator.mjs`), not
22//! by this crate: one writer means a service-managed daemon reports the same
23//! lease a foreground one does, and a lock left by a process that is gone is
24//! the daemon's own signal to replay (§4.7).
25//!
26//! Reading the folder is every existing ORCH reader's job: `jobs`, `runs`,
27//! `routes`, `channels`, `triggers`, `profiles` and session discovery point
28//! their Hermes-shaped code paths at these same profile folders.
29
30use std::path::{Path, PathBuf};
31
32use serde::{Deserialize, Serialize};
33
34/// Lease file the daemon writes while it serves a home, relative to the home.
35pub const LOCK_FILE: &str = "orchestrator.lock";
36
37/// Directory `setup` writes the rendered service unit into.
38pub const SERVICE_DIR: &str = "service";
39
40/// The daemon entry inside the `sdk/orchestrator` package.
41pub const DAEMON_ENTRY: &str = "bin/orchestrator.mjs";
42
43/// launchd label / systemd unit name for the orchestrator daemon.
44pub const SERVICE_NAME: &str = "ai.volter.supercode.orchestrator";
45
46/// The lease `<home>/orchestrator.lock` holds: which process is serving this
47/// home, and since when.
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
49pub struct Lease {
50    /// Daemon process id.
51    pub pid: u32,
52    /// RFC3339 instant the daemon recorded at startup.
53    pub started_at: String,
54    /// The home the daemon was started against.
55    pub root: PathBuf,
56}
57
58/// Why an operator verb could not do its work.
59#[derive(Debug, thiserror::Error)]
60pub enum OrchestratorError {
61    /// No lease file, or one that no longer names a live process.
62    #[error("the orchestrator is not running for `{0}` (no live lease at `{1}`)", root.display(), lock.display())]
63    NotRunning {
64        /// The home that was asked about.
65        root: PathBuf,
66        /// Where its lease would be.
67        lock: PathBuf,
68    },
69    /// A lease exists and its process is alive.
70    #[error("the orchestrator is already running for `{}` (pid {pid})", root.display())]
71    AlreadyRunning {
72        /// The home that was asked about.
73        root: PathBuf,
74        /// The live daemon's pid.
75        pid: u32,
76    },
77    /// The Node daemon entry could not be located.
78    #[error("no orchestrator daemon entry found (looked for `{DAEMON_ENTRY}` under: {searched})")]
79    NoDaemonEntry {
80        /// The candidate roots that were searched, joined.
81        searched: String,
82    },
83    /// The lease file could not be read or written.
84    #[error("orchestrator lease `{}`: {source}", path.display())]
85    Lease {
86        /// The lease path.
87        path: PathBuf,
88        /// The underlying I/O failure.
89        source: std::io::Error,
90    },
91    /// A service manager refused, or there is none on this platform.
92    #[error("orchestrator service: {action} failed: {detail}")]
93    Service {
94        /// What was attempted (`install`, `uninstall`).
95        action: &'static str,
96        /// What the service manager (or this module) said about it.
97        detail: String,
98    },
99}
100
101/// The lease path for one home.
102pub fn lock_path(root: &Path) -> PathBuf {
103    root.join(LOCK_FILE)
104}
105
106/// Read the lease, whether or not its process is still alive.
107pub fn read_lease(root: &Path) -> Option<Lease> {
108    let text = std::fs::read_to_string(lock_path(root)).ok()?;
109    serde_json::from_str(&text).ok()
110}
111
112/// Write the lease for a running daemon.
113pub fn write_lease(root: &Path, lease: &Lease) -> Result<(), OrchestratorError> {
114    let path = lock_path(root);
115    if let Some(parent) = path.parent() {
116        std::fs::create_dir_all(parent).map_err(|source| OrchestratorError::Lease {
117            path: path.clone(),
118            source,
119        })?;
120    }
121    let text = serde_json::to_string_pretty(lease).unwrap_or_default();
122    std::fs::write(&path, format!("{text}\n")).map_err(|source| OrchestratorError::Lease {
123        path: path.clone(),
124        source,
125    })
126}
127
128/// Remove the lease file. A missing file is not an error — `stop` is
129/// idempotent by design.
130pub fn clear_lease(root: &Path) -> Result<(), OrchestratorError> {
131    let path = lock_path(root);
132    match std::fs::remove_file(&path) {
133        Ok(()) => Ok(()),
134        Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()),
135        Err(source) => Err(OrchestratorError::Lease { path, source }),
136    }
137}
138
139/// Whether `pid` is a live process this user can signal.
140///
141/// `kill(pid, 0)` is the liveness question POSIX answers without touching the
142/// process. On a non-unix target there is no equivalent that does not start
143/// something, so the lease alone is the answer.
144pub fn pid_is_live(pid: u32) -> bool {
145    #[cfg(unix)]
146    {
147        if pid == 0 {
148            return false;
149        }
150        // SAFETY: signal 0 performs error checking only; it delivers nothing.
151        unsafe { libc::kill(pid as libc::pid_t, 0) == 0 }
152    }
153    #[cfg(not(unix))]
154    {
155        let _ = pid;
156        true
157    }
158}
159
160/// The lease of a daemon that is actually alive right now.
161pub fn live_lease(root: &Path) -> Option<Lease> {
162    read_lease(root).filter(|lease| pid_is_live(lease.pid))
163}
164
165/// Ask the daemon to stop: SIGTERM to the leased pid, then clear the lease.
166///
167/// The daemon's own SIGTERM handler is what closes its adapters; this never
168/// escalates to SIGKILL and never signals anything but the pid the lease
169/// names.
170pub fn stop(root: &Path) -> Result<Lease, OrchestratorError> {
171    let Some(lease) = live_lease(root) else {
172        return Err(OrchestratorError::NotRunning {
173            root: root.to_path_buf(),
174            lock: lock_path(root),
175        });
176    };
177    #[cfg(unix)]
178    // SAFETY: the pid comes from this home's own lease and is known live.
179    unsafe {
180        libc::kill(lease.pid as libc::pid_t, libc::SIGTERM);
181    }
182    clear_lease(root)?;
183    Ok(lease)
184}
185
186/// Locate the Node daemon entry (`sdk/orchestrator/bin/orchestrator.mjs`).
187///
188/// Candidates, in order: `SUPERCODE_ORCHESTRATOR_ENTRY` (an explicit
189/// override, which is also how a test points at a fake), the published
190/// package's `supercode-orchestrator` command on PATH (what `npm install -g
191/// @volter-ai-dev/supercode-orchestrator` puts there), the repo checkout the
192/// running binary sits in, and the current directory's `sdk/orchestrator`.
193pub fn daemon_entry() -> Result<PathBuf, OrchestratorError> {
194    let mut searched = Vec::new();
195    if let Some(explicit) = std::env::var_os("SUPERCODE_ORCHESTRATOR_ENTRY") {
196        let path = PathBuf::from(explicit);
197        if path.is_file() {
198            return Ok(path);
199        }
200        searched.push(path.display().to_string());
201    }
202    // An installed binary has no checkout beside it: the orchestrator is its
203    // own package, and npm links its daemon entry onto PATH by that name.
204    for dir in std::env::var_os("PATH")
205        .iter()
206        .flat_map(std::env::split_paths)
207    {
208        let command = dir.join("supercode-orchestrator");
209        if let Ok(entry) = std::fs::canonicalize(&command) {
210            if entry.is_file() && entry.ends_with(DAEMON_ENTRY) {
211                return Ok(entry);
212            }
213        }
214    }
215    searched.push("supercode-orchestrator on PATH".to_string());
216    let mut roots: Vec<PathBuf> = Vec::new();
217    if let Ok(exe) = std::env::current_exe() {
218        // target/<profile>/supercode → the workspace root is two levels up.
219        roots.extend(exe.ancestors().skip(1).take(4).map(Path::to_path_buf));
220    }
221    if let Ok(cwd) = std::env::current_dir() {
222        roots.push(cwd);
223    }
224    // A locally built binary's target directory can live anywhere (a shared
225    // cargo build dir, another volume), so the checkout it was built from is
226    // the last candidate. On an installed binary this path simply does not
227    // exist and is skipped like any other miss.
228    if let Some(workspace) = Path::new(env!("CARGO_MANIFEST_DIR")).ancestors().nth(2) {
229        roots.push(workspace.to_path_buf());
230    }
231    for root in roots {
232        let candidate = root.join("sdk/orchestrator").join(DAEMON_ENTRY);
233        if candidate.is_file() {
234            return Ok(candidate);
235        }
236        searched.push(candidate.display().to_string());
237    }
238    Err(OrchestratorError::NoDaemonEntry {
239        searched: searched.join(", "),
240    })
241}
242
243/// A rendered service unit: what `setup` prints and writes.
244#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
245pub struct ServiceUnit {
246    /// `launchd` or `systemd`.
247    pub kind: &'static str,
248    /// Where `setup` writes the rendered text under `<home>/service/`.
249    pub path: PathBuf,
250    /// The unit text itself.
251    pub text: String,
252    /// The command an operator (or ORC-10) runs to install it.
253    pub install_command: String,
254}
255
256/// Render the per-platform service unit for one home.
257///
258/// Nothing is installed here: ORC-10 owns installation. `setup` writes this
259/// text under `<home>/service/` so the operator can read exactly what would
260/// be installed before anything registers a daemon.
261pub fn service_unit(root: &Path, entry: &Path, node: &str) -> ServiceUnit {
262    let root_display = root.display().to_string();
263    let entry_display = entry.display().to_string();
264    if cfg!(target_os = "macos") {
265        let path = root.join(SERVICE_DIR).join(format!("{SERVICE_NAME}.plist"));
266        let text = format!(
267            r#"<?xml version="1.0" encoding="UTF-8"?>
268<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
269<plist version="1.0">
270<dict>
271  <key>Label</key><string>{SERVICE_NAME}</string>
272  <key>ProgramArguments</key>
273  <array>
274    <string>{node}</string>
275    <string>{entry_display}</string>
276    <string>--root</string>
277    <string>{root_display}</string>
278  </array>
279  <key>RunAtLoad</key><true/>
280  <key>KeepAlive</key><true/>
281  <key>StandardOutPath</key><string>{root_display}/service/orchestrator.out.log</string>
282  <key>StandardErrorPath</key><string>{root_display}/service/orchestrator.err.log</string>
283</dict>
284</plist>
285"#
286        );
287        let install = format!("launchctl bootstrap gui/$(id -u) {}", path.display());
288        ServiceUnit {
289            kind: "launchd",
290            path,
291            text,
292            install_command: install,
293        }
294    } else {
295        let path = root
296            .join(SERVICE_DIR)
297            .join(format!("{SERVICE_NAME}.service"));
298        let text = format!(
299            "[Unit]\n\
300             Description=supercode orchestrator ({root_display})\n\
301             After=network.target\n\
302             \n\
303             [Service]\n\
304             ExecStart={node} {entry_display} --root {root_display}\n\
305             Restart=on-failure\n\
306             KillSignal=SIGTERM\n\
307             \n\
308             [Install]\n\
309             WantedBy=default.target\n"
310        );
311        let install = format!(
312            "systemctl --user link {} && systemctl --user enable --now {SERVICE_NAME}",
313            path.display()
314        );
315        ServiceUnit {
316            kind: "systemd",
317            path,
318            text,
319            install_command: install,
320        }
321    }
322}
323
324/// What the platform's service manager says about the orchestrator unit.
325#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
326pub struct ServiceState {
327    /// `launchd`, `systemd`, or `none` where neither is available.
328    pub kind: &'static str,
329    /// The label / unit name asked about.
330    pub label: String,
331    /// Whether the service manager holds the unit at all.
332    pub installed: bool,
333    /// The daemon's pid, where the service manager knows it.
334    pub pid: Option<u32>,
335    /// The manager's own words, or why the question could not be asked.
336    pub detail: String,
337}
338
339/// launchd and systemd start a unit with a minimal `PATH`, so the unit names
340/// the interpreter absolutely wherever one can be resolved.
341pub fn absolute_program(program: &str) -> String {
342    if program.contains('/') {
343        return program.to_string();
344    }
345    if let Some(path) = std::env::var_os("PATH") {
346        for dir in std::env::split_paths(&path) {
347            let candidate = dir.join(program);
348            if candidate.is_file() {
349                return candidate.display().to_string();
350            }
351        }
352    }
353    program.to_string()
354}
355
356/// Run a service-manager command and return (success, stdout+stderr).
357fn run_tool(program: &str, args: &[&str]) -> Result<(bool, String), std::io::Error> {
358    let output = std::process::Command::new(program).args(args).output()?;
359    let mut text = String::from_utf8_lossy(&output.stdout).into_owned();
360    text.push_str(&String::from_utf8_lossy(&output.stderr));
361    Ok((output.status.success(), text.trim().to_string()))
362}
363
364#[cfg(target_os = "macos")]
365fn gui_domain() -> String {
366    // SAFETY: `getuid` reads this process's own real user id and cannot fail.
367    format!("gui/{}", unsafe { libc::getuid() })
368}
369
370/// Ask the platform's service manager about the orchestrator unit.
371///
372/// Never starts or installs anything: `status` calls this on every run.
373pub fn service_status(root: &Path) -> ServiceState {
374    platform_status(root)
375}
376
377#[cfg(target_os = "macos")]
378fn platform_status(_root: &Path) -> ServiceState {
379    let label = SERVICE_NAME.to_string();
380    let target = format!("{}/{SERVICE_NAME}", gui_domain());
381    match run_tool("launchctl", &["print", &target]) {
382        Ok((true, text)) => ServiceState {
383            kind: "launchd",
384            label,
385            installed: true,
386            pid: field_of(&text, "pid = ").and_then(|value| value.parse().ok()),
387            detail: field_of(&text, "state = ").unwrap_or_else(|| "loaded".into()),
388        },
389        Ok((false, _)) => ServiceState {
390            kind: "launchd",
391            label,
392            installed: false,
393            pid: None,
394            detail: format!("not bootstrapped in {}", gui_domain()),
395        },
396        Err(error) => ServiceState {
397            kind: "launchd",
398            label,
399            installed: false,
400            pid: None,
401            detail: format!("launchctl unavailable: {error}"),
402        },
403    }
404}
405
406#[cfg(all(unix, not(target_os = "macos")))]
407fn platform_status(_root: &Path) -> ServiceState {
408    let label = SERVICE_NAME.to_string();
409    match run_tool("systemctl", &["--user", "is-active", SERVICE_NAME]) {
410        Ok((active, text)) => {
411            let known = run_tool("systemctl", &["--user", "is-enabled", SERVICE_NAME])
412                .map(|(ok, _)| ok)
413                .unwrap_or(false);
414            ServiceState {
415                kind: "systemd",
416                label,
417                installed: active || known,
418                pid: None,
419                detail: if text.is_empty() {
420                    "unknown".into()
421                } else {
422                    text
423                },
424            }
425        }
426        Err(error) => ServiceState {
427            kind: "systemd",
428            label,
429            installed: false,
430            pid: None,
431            detail: format!("systemctl unavailable: {error}"),
432        },
433    }
434}
435
436#[cfg(not(unix))]
437fn platform_status(_root: &Path) -> ServiceState {
438    ServiceState {
439        kind: "none",
440        label: SERVICE_NAME.to_string(),
441        installed: false,
442        pid: None,
443        detail: "no service manager on this platform".into(),
444    }
445}
446
447/// `key = value` out of a service manager's block output.
448#[cfg(target_os = "macos")]
449fn field_of(text: &str, key: &str) -> Option<String> {
450    text.lines()
451        .find_map(|line| line.trim().strip_prefix(key))
452        .map(|value| value.trim().to_string())
453}
454
455/// Render the unit, hand it to the platform's service manager, and start it.
456///
457/// Refuses a label the manager already holds rather than replacing it: two
458/// homes share one label, so an install that silently took it over would point
459/// a running service at a different folder.
460pub fn install_service(
461    root: &Path,
462    entry: &Path,
463    node: &str,
464) -> Result<(ServiceUnit, ServiceState), OrchestratorError> {
465    let existing = service_status(root);
466    if existing.installed {
467        return Err(OrchestratorError::Service {
468            action: "install",
469            detail: format!(
470                "`{}` is already installed ({}); `supercode orchestrator setup --uninstall` first",
471                existing.label, existing.detail
472            ),
473        });
474    }
475    let unit = service_unit(root, entry, &absolute_program(node));
476    write_unit(&unit)?;
477    platform_install(&unit)?;
478    Ok((unit, service_status(root)))
479}
480
481#[cfg(target_os = "macos")]
482fn platform_install(unit: &ServiceUnit) -> Result<(), OrchestratorError> {
483    let path = unit.path.display().to_string();
484    let (ok, text) =
485        run_tool("launchctl", &["bootstrap", &gui_domain(), &path]).map_err(|error| {
486            OrchestratorError::Service {
487                action: "install",
488                detail: format!("launchctl: {error}"),
489            }
490        })?;
491    if !ok {
492        return Err(OrchestratorError::Service {
493            action: "install",
494            detail: format!("launchctl bootstrap {}: {text}", gui_domain()),
495        });
496    }
497    Ok(())
498}
499
500/// Untested on this box (the receipt is macOS); these are the commands
501/// `service_unit` has always printed as its `install_command`.
502#[cfg(all(unix, not(target_os = "macos")))]
503fn platform_install(unit: &ServiceUnit) -> Result<(), OrchestratorError> {
504    let path = unit.path.display().to_string();
505    for args in [
506        vec!["--user", "link", path.as_str()],
507        vec!["--user", "enable", "--now", SERVICE_NAME],
508    ] {
509        let (ok, text) =
510            run_tool("systemctl", &args).map_err(|error| OrchestratorError::Service {
511                action: "install",
512                detail: format!("systemctl: {error}"),
513            })?;
514        if !ok {
515            return Err(OrchestratorError::Service {
516                action: "install",
517                detail: format!("systemctl {}: {text}", args.join(" ")),
518            });
519        }
520    }
521    Ok(())
522}
523
524#[cfg(not(unix))]
525fn platform_install(_unit: &ServiceUnit) -> Result<(), OrchestratorError> {
526    Err(OrchestratorError::Service {
527        action: "install",
528        detail: "no service manager on this platform".into(),
529    })
530}
531
532/// Stop and unregister the unit, and remove the rendered file `setup` wrote.
533///
534/// Idempotent: a unit the manager does not hold is not an error, because the
535/// state the operator asked for is the state they get.
536pub fn uninstall_service(root: &Path) -> Result<ServiceState, OrchestratorError> {
537    platform_uninstall()?;
538    let unit_path = root.join(SERVICE_DIR).join(unit_file_name());
539    match std::fs::remove_file(&unit_path) {
540        Ok(()) => {}
541        Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
542        Err(source) => {
543            return Err(OrchestratorError::Lease {
544                path: unit_path,
545                source,
546            })
547        }
548    }
549    // `launchctl bootout` returns before the job is torn down, so the state
550    // this reports is the settled one, not the manager mid-teardown.
551    let mut state = service_status(root);
552    for _ in 0..40 {
553        if !state.installed {
554            break;
555        }
556        std::thread::sleep(std::time::Duration::from_millis(100));
557        state = service_status(root);
558    }
559    Ok(state)
560}
561
562#[cfg(target_os = "macos")]
563fn platform_uninstall() -> Result<(), OrchestratorError> {
564    let target = format!("{}/{SERVICE_NAME}", gui_domain());
565    let (ok, text) = run_tool("launchctl", &["bootout", &target]).map_err(|error| {
566        OrchestratorError::Service {
567            action: "uninstall",
568            detail: format!("launchctl: {error}"),
569        }
570    })?;
571    // `bootout` on a label nobody holds says so and exits non-zero.
572    if !ok && !text.contains("No such process") && !text.contains("not find") {
573        return Err(OrchestratorError::Service {
574            action: "uninstall",
575            detail: format!("launchctl bootout {target}: {text}"),
576        });
577    }
578    Ok(())
579}
580
581#[cfg(all(unix, not(target_os = "macos")))]
582fn platform_uninstall() -> Result<(), OrchestratorError> {
583    let _ = run_tool("systemctl", &["--user", "disable", "--now", SERVICE_NAME]);
584    Ok(())
585}
586
587#[cfg(not(unix))]
588fn platform_uninstall() -> Result<(), OrchestratorError> {
589    Ok(())
590}
591
592/// The file name `service_unit` renders for this platform.
593fn unit_file_name() -> String {
594    if cfg!(target_os = "macos") {
595        format!("{SERVICE_NAME}.plist")
596    } else {
597        format!("{SERVICE_NAME}.service")
598    }
599}
600
601/// Write a rendered unit under `<home>/service/`.
602pub fn write_unit(unit: &ServiceUnit) -> Result<(), OrchestratorError> {
603    if let Some(parent) = unit.path.parent() {
604        std::fs::create_dir_all(parent).map_err(|source| OrchestratorError::Lease {
605            path: unit.path.clone(),
606            source,
607        })?;
608    }
609    std::fs::write(&unit.path, &unit.text).map_err(|source| OrchestratorError::Lease {
610        path: unit.path.clone(),
611        source,
612    })
613}
614
615#[cfg(test)]
616mod tests {
617    use super::*;
618
619    /// A scratch home for one test, removed by the test that made it.
620    fn scratch(label: &str) -> PathBuf {
621        let root = std::env::temp_dir().join(format!(
622            "supercode-orchestrator-{label}-{}-{}",
623            std::process::id(),
624            std::time::SystemTime::now()
625                .duration_since(std::time::UNIX_EPOCH)
626                .unwrap()
627                .as_nanos()
628        ));
629        std::fs::create_dir_all(&root).unwrap();
630        root
631    }
632
633    #[test]
634    fn a_lease_round_trips_and_a_missing_one_is_not_running() {
635        let root = &scratch("lease");
636        let root = root.as_path();
637        assert!(read_lease(root).is_none());
638        assert!(live_lease(root).is_none());
639        let lease = Lease {
640            pid: std::process::id(),
641            started_at: "2026-09-04T00:00:00Z".into(),
642            root: root.to_path_buf(),
643        };
644        write_lease(root, &lease).unwrap();
645        assert_eq!(read_lease(root).as_ref(), Some(&lease));
646        // This process is alive, so its own lease reads as live.
647        assert!(live_lease(root).is_some());
648        clear_lease(root).unwrap();
649        assert!(read_lease(root).is_none());
650        // `stop` on a home with no lease refuses by name.
651        assert!(matches!(
652            stop(root),
653            Err(OrchestratorError::NotRunning { .. })
654        ));
655        std::fs::remove_dir_all(root).ok();
656    }
657
658    /// A lease whose process is gone is STALE: `status` must say down, not
659    /// inherit the file's claim.
660    #[test]
661    fn a_stale_lease_is_not_live() {
662        let root = &scratch("stale");
663        let root = root.as_path();
664        write_lease(
665            root,
666            &Lease {
667                // A pid no process can hold (max_pid is far below this).
668                pid: 0x7FFF_FFFF,
669                started_at: "2026-09-04T00:00:00Z".into(),
670                root: root.to_path_buf(),
671            },
672        )
673        .unwrap();
674        assert!(read_lease(root).is_some(), "the file is still there");
675        assert!(live_lease(root).is_none(), "but nothing is serving it");
676        std::fs::remove_dir_all(root).ok();
677    }
678
679    #[test]
680    fn the_service_unit_names_the_home_the_entry_and_its_install_command() {
681        let root = &scratch("unit");
682        let root = root.as_path();
683        let entry = PathBuf::from("/opt/supercode/sdk/orchestrator/bin/orchestrator.mjs");
684        let unit = service_unit(root, &entry, "/usr/bin/node");
685        assert!(unit.text.contains(&root.display().to_string()));
686        assert!(unit.text.contains("orchestrator.mjs"));
687        assert!(unit.text.contains(SERVICE_NAME));
688        assert!(unit
689            .install_command
690            .contains(&unit.path.display().to_string()));
691        assert!(unit.path.starts_with(root.join(SERVICE_DIR)));
692        assert_eq!(
693            unit.kind,
694            if cfg!(target_os = "macos") {
695                "launchd"
696            } else {
697                "systemd"
698            }
699        );
700        std::fs::remove_dir_all(root).ok();
701    }
702
703    /// Asking the service manager is a READ: `status` calls it on every run,
704    /// and must never register or render anything on the way.
705    #[test]
706    fn service_status_reports_the_label_and_installs_nothing() {
707        let root = &scratch("service-status");
708        let root = root.as_path();
709        let state = service_status(root);
710        assert_eq!(state.label, SERVICE_NAME);
711        assert!(
712            matches!(state.kind, "launchd" | "systemd" | "none"),
713            "{state:?}"
714        );
715        assert!(
716            !root.join(SERVICE_DIR).exists(),
717            "asking never writes a unit"
718        );
719        std::fs::remove_dir_all(root).ok();
720    }
721
722    /// launchd and systemd start a unit with a minimal PATH, so a bare program
723    /// name in the unit would not resolve at boot.
724    #[test]
725    fn a_program_is_resolved_absolutely_for_the_service_manager() {
726        assert_eq!(absolute_program("/usr/bin/env"), "/usr/bin/env");
727        let resolved = absolute_program("sh");
728        assert!(resolved.starts_with('/'), "{resolved}");
729        // an unresolvable name is left as it was, for the manager to refuse
730        assert_eq!(
731            absolute_program("definitely-not-a-program"),
732            "definitely-not-a-program"
733        );
734    }
735
736    /// The entry resolver must find the package in this checkout — the
737    /// `start` verb has nothing to spawn otherwise.
738    #[test]
739    fn the_daemon_entry_resolves_in_this_checkout() {
740        let entry = daemon_entry().expect("sdk/orchestrator/bin/orchestrator.mjs");
741        assert!(entry.ends_with(DAEMON_ENTRY));
742    }
743}