NanoClaw E2E on macOS
For each unexpected failure, preserve the test evidence, then follow the companion
e2e-triage at a safe checkpoint and include its findings
and next actions in the final E2E report. Research each distinct failure once,
including when another E2E skill delegates here; do not delay active prompts,
change acceptance results or submit public issues/comments without authorization.
Install e2e-triage alongside this skill. If its relative link is unavailable,
resolve it by skill name in the agent's installed catalog; if absent, report that
triage was unavailable and continue the authorized test/report without installing
anything implicitly.
Use this skill for a Mac you are working on or a Mac accessible through an existing SSH connection. scripts/macos-run.py creates a separate persistent checkout, runs NanoClaw's setup steps, starts its own LaunchAgent, and requires a real model reply and successful verification.
Run the driver from the NanoClaw source checkout under test. It resolves
--ref locally and fetches that exact commit into the new installation directory.
Local edits are not uploaded. The target can be this Mac or a different Mac;
the operator can also launch the SSH mode from Linux.
Install and target prerequisites
Install this skill and the companion shared installer:
npx skills add nanocoai/nanoclaw-oss-dev-tools --global --skill e2e-exe-dev
npx skills add nanocoai/nanoclaw-oss-dev-tools --global --skill e2e-wizard
npx skills add nanocoai/nanoclaw-oss-dev-tools --global --skill e2e-macos
Both also ship in the repository's Claude Code plugin. The driver locates
e2e-exe-dev/scripts/e2e-install.sh beside its own installed skill; pass
--installer if they are in different locations. Copies and symlinks work.
Check the intended target before installing:
- Python 3.9+, Git, and a regular macOS user account. Do not run as root or use
sudofor the driver.--target-pythonselects another installed Python. - Xcode Command Line Tools, plus Node 22+ or Homebrew so NanoClaw can install Node. The shared bootstrap installs pinned pnpm and checkout dependencies.
- A working Docker CLI and daemon. On Docker Desktop, complete its first-launch GUI setup first. Inspect an existing runtime before deciding to install or start another one. The driver stops when Docker is unavailable; it never applies Linux service/group repairs on a Mac.
- An active GUI login session for that same user. The LaunchAgent is loaded
explicitly into
gui/<uid>, including when launched over SSH. An SSH login alone does not create a GUI session. This first version does not configure an unattended LaunchDaemon or automatic login. - A new, unused installation path with an existing writable parent. Prefer a persistent development directory. Existing directories, even empty ones or earlier test installations, are not adopted or overwritten.
- For SSH: an existing destination and verified host key.
--identity-fileselects a key and disables ssh-agent. Ports and other connection settings belong in the operator's SSH configuration. The skill does not enable Remote Login, enroll keys, or modify SSH configuration.
If prerequisites are missing, prepare them within the user's chosen scope on
the target Mac, then rerun preflight. NanoClaw's setup/install-node.sh and
setup/install-docker.sh contain its macOS installation paths; Homebrew, Xcode
tools, and Docker Desktop can require interaction. Keep that prerequisite work
distinct from a completed E2E result.
Choose the gateway explicitly
Before reading a credential or contacting the Mac, resolve the exact NanoClaw
commit and inspect its provider picker with the sibling e2e-wizard helper:
COMMIT="$(git rev-parse --verify 'HEAD^{commit}')"
PROVIDER_HELPER=/absolute/path/to/installed/e2e-wizard/scripts/provider-options.py
python3 "$PROVIDER_HELPER" --root "$PWD" --revision "$COMMIT"
python3 "$PROVIDER_HELPER" --root "$PWD" --revision "$COMMIT" --provider claude
Show the offered providers to the operator and ask which to test, then show the
selected provider's exact auth prompt/options and ask for the auth method. For
an installable provider, fetch its one nc:copy from-branch: payload from the
owning remote and pass that fetched ref as --payload-ref; record both source
SHAs. The native headless driver currently supports Claude only. A new gateway
uses discovered api or oauth; gateway reuse uses the explicit lifecycle
value existing after the target proves a usable vault credential. Browser,
subscription, and device methods require a separately authorized live human
handoff and are not automated here. skip cannot pass. Read a credential only
after a matching method is selected.
--gateway reuse uses the target user's already-configured OneCLI gateway and
existing Anthropic credential. It never reads or transfers an operator key,
reinstalls the gateway, selects a different endpoint, or replaces a vault secret.
An unavailable gateway or missing credential stops the run. A detected gateway
is a choice to present, not permission to reinstall it.
--gateway install permits a new OneCLI installation. It refuses a detected
existing configuration, OneCLI container, configured endpoint, or occupied
default gateway port. Supply an authorized Anthropic credential using
--key-file, NANOCLAW_E2E_KEY_FILE, or the default
~/.nanoclaw-e2e/anthropic_key on the operator machine. Over SSH, the key
travels through stdin and is temporarily stored in a private file in the new
checkout. That file is removed after the installer exits; the OneCLI vault
retains the credential. No force-replacement mode is supported here.
Creating a persistent checkout/service and making a provider request must be within the user's authorization for that Mac. A request to develop or review the skill alone does not authorize a live install on the operator's personal machine. Prepare the code and a concrete preflight plan before asking for any additional authorization that is actually needed. Reuse prior scoped approval.
Local and SSH runs
Set MACOS_SKILL_DIR to the directory containing this SKILL.md. From the
NanoClaw checkout, plan a local install:
python3 "$MACOS_SKILL_DIR/scripts/macos-run.py" \
--install-dir /Users/operator/work/nanoclaw-e2e \
--gateway reuse --provider claude --auth-method existing --ref origin/main \
--result-file /path/to/macos-result.json --dry-run
For a Mac reached over SSH, add --host and use a path on that Mac:
python3 "$MACOS_SKILL_DIR/scripts/macos-run.py" \
--host operator@mac.example.test \
--install-dir /Users/operator/work/nanoclaw-e2e \
--gateway reuse --provider claude --auth-method existing --ref origin/main \
--result-file /path/to/macos-result.json --dry-run
Use actual usernames, paths and connection settings. Public HTTPS repository
origins are supported; GitHub SSH origins are converted to HTTPS. --repo
selects another public source repository. Private Git authentication is outside
this first version.
Dry run performs read-only target checks, including over SSH, and writes a
local planned report. It does not read credentials or create an installation.
Inspect readiness, the target identity, the checkout path and the gateway
choice. Remove --dry-run to execute the authorized plan. The driver repeats
preflight and checks for changed shared state before creating the directory.
Optional --display-name and --timezone configure the test agent. Each run
uses a new checkout, so its CLI agent and database are separate from existing
installations. It does not connect real messaging channels.
Service and shared-host preservation
The shared installer drives bootstrap, environment, OneCLI/auth, image build, mount configuration, timezone, CLI-agent creation, the model ping, and final NanoClaw verification. The Mac-specific service helper builds the host, stamps the upgrade marker through NanoClaw's CLI, asks NanoClaw for its actual service label, and creates only that checkout's LaunchAgent.
This deliberately replaces the service setup step for this test workflow:
NanoClaw's normal setup/service.ts may clean up unhealthy/dead peer services
and replace ~/.local/bin/ncl. The E2E helper leaves those peers and that link
alone. It refuses an existing plist or loaded label rather than unloading it.
The launcher maintains NanoClaw's PID file so setup/verify.ts can also verify
the process from an SSH bootstrap namespace. Its PATH includes the discovered
Homebrew/user tools; the service is still a native LaunchAgent.
Before and after the run, compare existing NanoClaw plist contents and service
PIDs, the global ncl link, an existing mount allowlist, and originally running
Docker containers. Reuse mode also compares the OneCLI endpoint, config and
credential inventory. An unexpected change prevents a pass and is reported;
there is no automatic rollback of shared state. Changes made concurrently by
another operator can also trigger this check. NanoClaw may add its normal
~/.local/bin PATH entry to shell profiles when configuring OneCLI.
Results and retained installs
Read the local JSON report's status, requested_commit, commit, phase,
plan, and target_run. A pass requires matching run/commit identities, a
successful installer result with ping: ok and service_type: launchd, the
new service actually running, and the shared-state comparison passing.
The new checkout contains detailed logs at logs/e2e/, the installer's
logs/e2e/result.json, and the native driver's .git/nanoclaw-e2e/result.json.
Its .git/nanoclaw-e2e/service.json records the exact LaunchAgent target and
plist. Keep local reports outside source files and credential paths.
Success and failure retain the checkout, agent, image and installed service.
A lost SSH connection or timeout may leave work running; inspect the target
before retrying, and use a new path rather than adopting an uncertain install.
After the local report and the checkout's logs/e2e/, runtime logs, service
record, exact SHAs, and triage output have been copied to durable local storage,
offer to remove a run-owned installation after a pass. Recommend retaining an
unexpected failure until triage and requested inspection are complete. Only
stop/remove a retained install when that cleanup is requested. Immediately
before cleanup, revalidate the install directory and service record, ensure its
driver/controller is inactive, and verify the service points to that checkout.
Afterward verify the checkout service is gone and write a teardown receipt next
to the preserved report. A failed evidence, redaction, local persistence,
triage, ownership, controller, or removal check blocks cleanup. Cleanup may
remove only this checkout and its LaunchAgent; the Mac host, shared Docker,
OneCLI, other services, global ncl link, and unrelated files are outside it.
LaunchAgents depend on the user's login session. Initial readiness does not prove recovery after logout, reboot or Docker Desktop restart. Those are separate tests and are not claimed by this first version.
Troubleshooting
Use the local report to identify the target, checkout, gateway choice and last phase. Inspect the retained installation before choosing a new path for a retry.
| Report or symptom | Inspect on the target Mac | What it distinguishes |
|---|---|---|
| Preflight reports no GUI session | id -u, launchctl print gui/<uid>, and the currently logged-in user |
A valid SSH login from the GUI bootstrap namespace required by LaunchAgents. |
| Docker is unavailable | Docker Desktop state, docker info, and the preflight's recorded runtime |
A missing prerequisite from a NanoClaw installer failure; the driver does not start or repair Docker. |
| Gateway reuse fails | The report's selected gateway mode and the target user's OneCLI health/credential state | Existing gateway availability; reinstalling or replacing it requires a separate explicit choice. |
| The install path or service label already exists | The path, recorded plist, .git/nanoclaw-e2e/service.json, and the matching process arguments |
A retained prior run from an unrelated checkout; the driver never adopts it. |
agent, ping, or verify fails |
logs/e2e/, logs/nanoclaw.log, logs/nanoclaw.error.log, and data/cli.sock |
Host/database readiness from model, socket, or final-verification failure. |
| Shared-state comparison fails | The report's before/after assets, service PIDs, containers, gateway URL and vault inventory | A concurrent operator change from an E2E-owned change; do not call the install a pass. |
| Timeout or lost SSH | The retained process, LaunchAgent target, checkout HEAD and result identities | Work still running from a completed failure; use a new path unless the previous lifecycle is fully understood. |
Source contracts and validation
The initial implementation follows NanoClaw
74224f62a6c08418acccc727114ab02f92e403bf:
setup.sh, setup/install-node.sh, setup/install-docker.sh, setup/onecli.ts,
setup/auth.ts, setup/mounts.ts, setup/service.ts, setup/peer-cleanup.ts,
setup/verify.ts, src/install-slug.ts, scripts/upgrade-state.ts,
scripts/init-cli-agent.ts, and scripts/chat.ts.
Re-check these contracts when NanoClaw's setup changes. Apple's
LaunchAgent guide
and the target Mac's launchctl(1) / launchd.plist(5) manuals describe the
service and GUI-session behavior.
Offline regressions cover transport, exact checkouts, existing-path/service protection, gateway credential preservation, stale results, process timeouts, Mac service dispatch, and shared-state comparison. Record the exact macOS/CPU, NanoClaw SHA, local/SSH mode and successful model/service evidence when claiming live compatibility; a dry run or simulated SSH test is not that evidence.
On 2026-09-11, a live local run passed on macOS 26.6.1 / arm64 with Docker Desktop running and Node 26.8.1, targeting the NanoClaw commit above. It completed in about 65 seconds, including the exact checkout, setup, native LaunchAgent, real CLI-agent reply and successful NanoClaw verification. The shared-state preservation checks passed. A follow-up process check confirmed the new service remained running from the tested checkout with its CLI socket available and no restart. Sanitized installer result:
{
"status": "pass",
"exit_code": 0,
"commit": "74224f62a6c08418acccc727114ab02f92e403bf",
"phase": "complete",
"service_type": "launchd",
"ping": "ok"
}
The initial 86-test offline suite passed, including 24 Mac-specific tests, and hosted CI passed on Ubuntu and macOS for that delivery.
On 2026-09-11, a live SSH run passed on a Mac mini with Apple M4 Pro, 24 GiB RAM, macOS 26.6.2 / arm64, Node 26.8.2 and Docker Desktop's local Linux daemon (Docker Engine 29.7.2). The same NanoClaw commit was tested in a fresh checkout with existing prerequisites and warm image/dependency caches. The complete driver run took about 44 seconds and proved a real CLI-agent reply, successful verification, a running LaunchAgent and unchanged shared state, including the previously running services and containers.
This SSH qualification used the shared installer waiting for data/cli.sock
before init-cli-agent.ts. The first
attempt with 0.4.0 failed before the model probe because host and initializer
concurrently applied fresh SQLite migrations (agent_destinations already
existed). Host logs confirmed the overlap. The failed checkout was retained;
the pass came from a separate fresh checkout, with no NanoClaw source patch or
manual database repair. The two new regressions failed before this ordering
fix and passed afterward; the complete local suite passed 88 tests.
Intel/older macOS, reboot/logout recovery and cold prerequisite setup remain unqualified.