Skip to content

Local CI on the Mac mini

The Mac mini runs normal CI and reports commit statuses and PR comments to GitHub. No GitHub Actions runner is involved in the local loop. The CI and Skills CI workflows are manual diagnostics only; the release publishing workflow remains unchanged.

What contributors should expect

Watch the commit status Local CI / Mac mini. It first appears as pending with “Queued for CI on the Mac mini”; success or failure is the suite result, and error means the runner needs attention. The runner polls every two minutes while the maintainer's Mac mini user session is logged in. If an eligible PR has no status after a few minutes, ask a maintainer; the runner may not be active.

Automatic CI covers main and open PRs authored by the repository owner from this repository. It skips fork PRs and PRs from other authors, so they will not receive a status to wait for. The GitHub Actions CI workflows are manual diagnostics only.

The launch agent runs while the clays macOS user is logged in. A file lock allows one service instance at a time. Discovery posts a pending status as soon as it sees an eligible commit, then queues it for a single worker to run suites serially. This lets polling continue while a suite is running, so a newly pushed PR head is visible on GitHub without waiting for the current suite to finish. If a newer head replaces an older head that is still queued, the older queued run is marked superseded. Commit state is persisted to prevent repeat work after restart. Each run gets a separate branch and worktree under .worktrees/; the coordinator checkout is never edited or switched.

Each suite runs static checks, generated docs and LF checks, skills API alignment, all tests on Python 3.10/3.11/3.12, eight packaged examples and seven CLI demos. Each Python version has its own environment. Python 3.11/3.12 run on macOS arm64. Python 3.10 runs in a local Linux arm64 container because its locked SciPy wheel does not load on macOS 27. Windows-specific execution still requires a manually dispatched diagnostic job. Pytest temporary directories are nested so it cannot erase CI logs or environments.

Results use the commit status Local CI / Mac mini. One PR comment is updated on each new commit. Full logs stay on this Mac under ~/Library/Application Support/cranalytics-local-ci/data/<sha>/; no raw command output, credentials or model transcript is posted to GitHub. The comment does name each failed check's command and exit code (first line of the failure only), so a failure can be located without the Mac's logs.

If checks fail, codex exec --model gpt-6-luna gets one attempt, with high reasoning, a workspace sandbox, no network access, no approval prompts, and a 15-minute limit. The controller reruns the full suite. A passing fix is committed and pushed to a separate fix/codex-local-ci-* branch and opened as a draft PR against the failing branch. This explicitly authorizes a stacked repair PR; the parent branch is never pushed by the service. Repair PRs do not trigger further model repairs. Main failures produce a repair PR against main. The original failing commit retains its failing status. A fix never causes an automatic merge. Dependency installation failures skip model repair. Failed or interrupted fixes remain in their worktrees for review.

Install

First authenticate gh auth login and codex login, then verify Luna is available with a current CLI. Install colima and docker with Homebrew, start a dedicated cranalytics-ci Colima profile (2 CPUs, 4 GB RAM, 30 GB disk, VZ/virtiofs), and pull ghcr.io/astral-sh/uv:python3.10-bookworm-slim. The installer pins the pulled image digest. Only the repository is mounted in the VM; containers mount their own task worktree. The profile uses --activate=false, leaving the default Docker context unchanged. From the dedicated setup worktree:

python3 scripts/ci/install_local_ci.py --root /Users/clays/Github/cranalytics

The installer copies the controller and skills check into Application Support so merging/removing the setup worktree cannot break the service. Reinstall after changing the controller. --no-repair installs a checks-only service.

launchctl print gui/$(id -u)/com.cranalytics.local-ci
tail -f "$HOME/Library/Application Support/cranalytics-local-ci/runner.log"

To stop and uninstall the launch agent (keeps logs and configuration):

python3 scripts/ci/install_local_ci.py --root /Users/clays/Github/cranalytics --uninstall

Colima starts only when a pending suite reaches the Linux Python 3.10 job. The dedicated VM stops after that commit's suite and any Luna repair rerun finish. Between jobs, the Docker VM is off. The service uses caffeinate -is to keep the Mini awake while enabled, without changing persistent power settings. Keep the Mac powered and the user session logged in. Uninstalling the launch agent releases the sleep assertion; stop the VM separately with colima --profile cranalytics-ci stop if it is running.

Manual runs and recovery

Run the full suite inside any dedicated worktree without posting or invoking Luna:

python3 scripts/ci/local_ci_runner.py --suite "$PWD"

Runner errors and attempted commits are recorded in data/state.json. To retry a commit, stop the service, inspect/remove its retained worktree and branch, remove only that commit's entry from state.json, then reinstall the service. This explicit retry prevents an unattended failure from repeatedly consuming model usage. After a repair PR is merged or abandoned, remove its worktree and local branch using the repository's normal worktree cleanup procedure. Clean check-only worktrees are removed automatically. Never force-remove a worktree containing an unreviewed fix.

The runner uses the existing local GitHub and ChatGPT credentials. Model inference runs through Codex; “local” refers to CI execution, not offline model hosting.