diff --git a/docs/superpowers/plans/2026-07-31-xy-start-on-login.md b/docs/superpowers/plans/2026-07-31-xy-start-on-login.md index 52e14f6..6f36b39 100644 --- a/docs/superpowers/plans/2026-07-31-xy-start-on-login.md +++ b/docs/superpowers/plans/2026-07-31-xy-start-on-login.md @@ -38,9 +38,14 @@ | `crates/xy/src/cli/mod.rs` | *Modify:* declare `mod service;` and re-export | | `crates/xy/src/main.rs` | *Modify:* `Service` subcommand arm; reorder path resolution before logger init | | `crates/xy/src/daemon/mod.rs` | *Modify:* drop the now-duplicated `ensure_dirs()` call | -| `crates/xy/tests/service.rs` | *New:* `#[ignore]` end-to-end launchd cycle | | `README.md` | *Modify:* document the five verbs | +No integration test is planned. Driving a real launchd cycle would either +hard-code the live `se.aceofba.xy` label and risk clobbering a working +installation, or require a test-only `--label` flag on the CLI. The load/unload +mechanism is covered instead by the *Manual acceptance* section at the end of +this plan, which is the only check that can actually prove `RunAtLoad` works. + --- ### Task 1: `io::Write` for `RotatingLogWriter` @@ -1161,82 +1166,21 @@ git commit -m "feat(cli): xy service install/uninstall/start/stop/status" --- -### Task 6: End-to-end test and documentation +### Task 6: Documentation -An ignored integration test that exercises a real launchd cycle under a distinct label, plus README coverage. +Document the five verbs in the README. No integration test: driving a real +launchd cycle would either hard-code the live `se.aceofba.xy` label and risk +clobbering a working installation, or require a test-only `--label` flag on the +CLI. The *Manual acceptance* section below covers the mechanism instead. **Files:** -- Create: `crates/xy/tests/service.rs` - Modify: `README.md` **Interfaces:** -- Consumes: the `xy` binary built by cargo. The test shells out rather than linking, because `service.rs` is `pub(crate)`. +- Consumes: nothing from earlier tasks. - Produces: nothing consumed by later tasks. -- [ ] **Step 1: Write the ignored end-to-end test** - -Create `crates/xy/tests/service.rs`: - -```rust -use std::process::Command; - -fn launchctl(verb: &str, arg: &str) -> bool { - Command::new("launchctl") - .arg(verb) - .arg(arg) - .output() - .map(|out| out.status.success()) - .unwrap_or(false) -} - -#[test] -#[ignore = "installs a real launchd agent; run manually with --ignored"] -fn install_start_stop_uninstall_cycle() { - let home = std::env::var("HOME").unwrap(); - let plist = format!("{home}/Library/LaunchAgents/se.aceofba.xy-test.plist"); - - assert!( - !std::path::Path::new(&plist).exists(), - "test agent already installed; remove {plist} first" - ); - - let contents = format!( - r#" - - -Labelse.aceofba.xy-test -ProgramArguments/bin/sleep3600 -RunAtLoad - -"# - ); - - std::fs::write(&plist, contents).unwrap(); - - assert!(launchctl("load", &plist), "load failed"); - assert!(launchctl("unload", &plist), "unload failed"); - - std::fs::remove_file(&plist).unwrap(); - - assert!(!std::path::Path::new(&plist).exists()); -} -``` - -This deliberately drives `launchctl` against a harmless `/bin/sleep` agent under the `se.aceofba.xy-test` label rather than invoking `xy service install`, which would hard-code the real `se.aceofba.xy` label and could disturb a working installation. It verifies the load/unload mechanism Task 4 relies on. - -- [ ] **Step 2: Confirm it is skipped by default** - -Run: `cargo nextest run -p xy` - -Expected: the test is listed as skipped, not run. - -- [ ] **Step 3: Run it explicitly and confirm it passes** - -Run: `cargo nextest run -p xy --run-ignored all -E 'test(/install_start_stop_uninstall_cycle/)'` - -Expected: PASS. Then confirm no residue: `ls ~/Library/LaunchAgents/ | grep xy-test` returns nothing. - -- [ ] **Step 4: Document the feature** +- [ ] **Step 1: Document the feature** Add to `README.md`, after the existing command list: @@ -1264,7 +1208,7 @@ visible only to launchd: launchctl print gui/$UID/se.aceofba.xy ``` -- [ ] **Step 5: Full suite, format, lint** +- [ ] **Step 2: Full suite, format, lint** ```bash cargo nextest run @@ -1272,11 +1216,11 @@ cargo +nightly fmt cargo clippy --workspace ``` -- [ ] **Step 6: Commit** +- [ ] **Step 3: Commit** ```bash -git add crates/xy/tests/service.rs README.md -git commit -m "test(service): ignored launchd cycle test; document xy service" +git add README.md +git commit -m "docs(readme): document xy service verbs" ``` ---