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.