a73x

256c96a0

docs: the Mac gate drives a pristine macOS guest, not the host

a73x   2026-09-03 18:46

Commit message
docs: the Mac gate drives a pristine macOS guest, not the host

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SakwJEwD9dXBoRP5kWbemW

docs/superpowers/plans/2026-09-03-macos-port-step3-darwin-arm.md
Old New
@@ -637,29 +637,54 @@ ssh -o BatchMode=yes squirtle 'cd ~/code/rad/mux && export PATH=/opt/homebrew/bi
637 637
638 --- 638 ---
639 639
640 ### Task 7: `test/mac.sh` — the `make mac` journey 640 ### Task 7: `test/mac.sh` — the `make mac` journey, against a macOS GUEST on the Mac
641
642 **Ruling (user, 2026-09-03):** the gate drives a macOS virtual machine on
643 squirtle, not the host, so every run starts from a pristine image the way
644 `make vm` starts from a scorched-earth reset. The host builds (it holds the
645 toolchain and the shadow SDK); the guest only RUNS the built binaries, which
646 is the stronger claim — a clean macOS with no developer tooling.
647
648 **The VM is a human/host action, never harness code** (the `vm.sh` rule).
649 Tooling: Cirrus Labs `tart` (installed 2026-09-03 on squirtle, `brew trust
650 cirruslabs/cli && brew install cirruslabs/cli/tart`), base image
651 `ghcr.io/cirruslabs/macos-tahoe-base:latest` (macOS 26; pulled once, ~28 GB).
652 The pristine VM is made ONCE by hand and documented in `test/mac.sh`'s header:
653
654 ```sh
655 tart clone ghcr.io/cirruslabs/macos-tahoe-base:latest mux-mac-base
656 tart run --no-graphics mux-mac-base & # user admin, password admin
657 tart exec mux-mac-base sh -c 'mkdir -p ~/.ssh && chmod 700 ~/.ssh && echo "PUBKEY" >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'
658 tart stop mux-mac-base
659 ```
660
661 (`PUBKEY` is the key squirtle's own `~/.ssh/id_*.pub` holds, so the host can
662 ssh into the guest without a password; `tart exec` needs the guest agent the
663 cirruslabs images ship.) `mux-mac-base` is never run again by the harness:
664 each run clones it.
641 665
642 **Files:** 666 **Files:**
643 - Create: `test/mac.sh` 667 - Create: `test/mac.sh`
644 - Modify: `Makefile` (`mac` target from Task 1 already points here) 668 - Modify: `Makefile` (`mac` target from Task 1 already points here), `README.md` (one paragraph under the macOS section: the base-VM recipe above)
645 669
646 **Interfaces:** 670 **Interfaces:**
647 - Consumes: `MUX_MAC` (default `squirtle`), the repo at `~/code/rad/mux` on the box, `deps/mac-sdk/bin` on PATH there. 671 - Consumes: `MUX_MAC` (default `squirtle`), the repo at `~/code/rad/mux` on the host, `deps/mac-sdk/bin` on the host PATH, `tart` on the host, the pristine `mux-mac-base` VM, `test/os_oracle.sh` (its Darwin arm runs INSIDE the guest).
648 672
649 - [ ] **Step 1: The script**, shaped like `test/vm.sh` (same header contract: real box, every claim asserted by a separate ssh through the Darwin oracle, `set -eu`, BatchMode). Legs, each a named step with its own failure line: 673 - [ ] **Step 1: The script**, shaped like `test/vm.sh` (same header contract; `set -eu`; BatchMode; every claim asserted by a separate ssh reading the guest through the oracle). All ssh to the guest goes THROUGH the host: `ssh squirtle "ssh -o BatchMode=yes -o StrictHostKeyChecking=no admin@$GIP '…'"`, one helper `gssh` that quotes once. Legs, each a named step with its own failure line:
650 674
651 1. `push` — `git push "$MAC:code/rad/mux" HEAD:refs/heads/mac-journey` then remote `git checkout -q mac-journey`. 675 1. `push` — `git push "$MAC:code/rad/mux" HEAD:refs/heads/mac-journey`, remote `git checkout -q mac-journey && git reset -q --hard mac-journey`.
652 2. `build` — remote `make build` and `make check`, rc captured. 676 2. `build` — on the host: `make build` and `make check` (rc captured); `deps/zig/zig build test` (rc captured) — the unit suite needs the toolchain and so runs on the host.
653 3. `unit` — remote `deps/zig/zig build test`, rc captured. 677 3. `vm` — on the host: `tart stop mux-mac 2>/dev/null; tart delete mux-mac 2>/dev/null; tart clone mux-mac-base mux-mac; nohup tart run --no-graphics mux-mac >/tmp/mux-mac.log 2>&1 &`; poll `tart ip mux-mac` up to 120 s; poll `gssh true` up to 120 s. Refuse with a clear line if `mux-mac-base` does not exist (`tart list`), pointing at the header recipe.
654 4. `daemon` — remote, under `XDG_RUNTIME_DIR=$(mktemp -d /tmp/muxj.XXXXXX)` exported for every later leg: `zig-out/bin/mux d start -d`; assert via `pid_holds_unix_sock DPID $XDG_RUNTIME_DIR/muxd.sock` from the oracle, and `pid_exe DPID` equals `real_path zig-out/bin/mux`. 678 4. `install` — `scp` (through the host) `zig-out/bin/mux`, `zig-out/bin/ptyclient` and `test/os_oracle.sh` into the guest's `~/mux-e2e/`; `gssh '~/mux-e2e/mux --version'` prints the version; assert `file_mode` of the binary is `755` via the oracle sourced in the guest.
655 5. `session` — `zig-out/bin/mux a run --sock … 'echo mac-$$'`-style (use whichever `mux a` verb the README documents for a one-shot command) and read the pid back off the grid; assert the pid is a child of the daemon via `pid_children`. 679 5. `daemon` — in the guest, with `XDG_RUNTIME_DIR` UNSET (this is the one place the Darwin fallback `/tmp/mux-<uid>` is exercised end to end; the Task 2 ⚠️ item): `~/mux-e2e/mux d start -d`; then assert via the oracle IN the guest: `pid_holds_unix_sock DPID /tmp/mux-501/muxd.sock` (uid 501 is `admin`; read it with `id -u`), `pid_exe DPID` equals `real_path ~/mux-e2e/mux`, and `file_mode /tmp/mux-$(id -u)` is `700`.
656 6. `attach` — `zig-out/bin/ptyclient` drives `mux --sock …` on a real pty, types `printf JOURNEY-%s\\n ok`, waits for `JOURNEY-ok` in the snapshot (the e2e lib has the wait; mirror its shape), detaches with the chord. 680 6. `session` — `~/mux-e2e/mux a …` one-shot (the `mux a` verb README documents) runs `echo mac-$$`; read the pid off the reply; assert it is a child of the daemon via `pid_children`.
657 7. `stop` — `mux d stop`; assert `pid_alive DPID` false within 3 s, socket path gone. 681 7. `attach` — `~/mux-e2e/ptyclient` drives `~/mux-e2e/mux` on a real pty in the guest, types `printf JOURNEY-%s\\n ok`, waits for `JOURNEY-ok` in the snapshot (mirror the e2e lib's wait shape), detaches with the chord.
658 8. Cleanup trap: `mux d stop` on the rig socket if still alive; `rm -rf` the rig dir. 682 8. `stop` — `mux d stop`; `pid_alive DPID` false within 3 s; the socket path gone.
683 9. Cleanup trap: `tart stop mux-mac` on the host, always; the clone is left for inspection and replaced by the next run's clone.
659 684
660 - [ ] **Step 2: Run it** — `make mac` from this box; every leg prints its name and `ok`; exit 0. 685 - [ ] **Step 2: Run it** — `make mac` from this box; every leg prints its name and `ok`; exit 0. Record the wall-clock of the `vm` leg (clone + boot) in the report.
661 686
662 - [ ] **Step 3: Commit** — `test: make mac drives the Mac over ssh the way make vm drives the VM` 687 - [ ] **Step 3: Commit** — `test: make mac drives a pristine macOS guest on the Mac the way make vm drives the VM`
663 688
664 --- 689 ---
665 690