|
| 1 | +name: Mac bridge |
| 2 | + |
| 3 | +# Git-bus executor for cloud-agent access to the self-hosted Apple |
| 4 | +# Silicon node (docs/design/mac-bridge-cloud-agent-access.md §2.1). |
| 5 | +# |
| 6 | +# Protocol: an agent pushes a branch `mac-bridge/<preset>-<nonce>` |
| 7 | +# containing the workload tree + a manifest at .mac-bridge/request.json |
| 8 | +# (created by scripts/mac_bridge/request_run.py). This workflow runs the |
| 9 | +# manifest's ALLOWLISTED preset on the kakeya-mac-m4 runner and pushes |
| 10 | +# logs + result JSONs back to the same branch, where the agent fetches |
| 11 | +# them with plain git (and read-only `gh run list`). |
| 12 | +# |
| 13 | +# Security (design doc §3): |
| 14 | +# * Command surface = the preset allowlist in |
| 15 | +# inference_engine/bridge/manifest.py — typed, bounded params; no |
| 16 | +# manifest string ever reaches a shell. Validation is unit-tested |
| 17 | +# at 100% coverage on the Linux gate. |
| 18 | +# * Trigger surface = push permission on mac-bridge/** — the same |
| 19 | +# population that can already execute code on this runner via the |
| 20 | +# `needs-mac-m4` PR label (integration.yaml). |
| 21 | +# * The single Mac is serialized via the concurrency group; every |
| 22 | +# preset carries its own timeout inside the executor and the job |
| 23 | +# has a hard cap below. |
| 24 | +# * K3 acceptance reports produced by a run are validated by the |
| 25 | +# PR #109 evidence gate ON the runner; a non-conforming report |
| 26 | +# fails the bridge run itself. |
| 27 | + |
| 28 | +on: |
| 29 | + push: |
| 30 | + branches: |
| 31 | + # Canonical request namespace. |
| 32 | + - "mac-bridge/**" |
| 33 | + # Cursor cloud agents are typically constrained to an |
| 34 | + # AgentMemory/<name>[-suffix] branch template; this pattern lets |
| 35 | + # them participate without violating their naming policy |
| 36 | + # (request_run.py --branch-prefix/--branch-suffix). |
| 37 | + - "AgentMemory/mac-bridge-*" |
| 38 | + |
| 39 | +concurrency: |
| 40 | + # One Mac: queue bridge runs globally, never cancel a running one |
| 41 | + # (results are expensive; the requester can cancel from the UI). |
| 42 | + group: mac-bridge |
| 43 | + cancel-in-progress: false |
| 44 | + |
| 45 | +permissions: |
| 46 | + contents: write # commit logs/results back to the request branch |
| 47 | + |
| 48 | +jobs: |
| 49 | + bridge: |
| 50 | + name: run allowlisted preset on kakeya-mac-m4 |
| 51 | + runs-on: [self-hosted, macOS, ARM64, kakeya-mac-m4] |
| 52 | + timeout-minutes: 150 |
| 53 | + steps: |
| 54 | + - uses: actions/checkout@v4 |
| 55 | + with: |
| 56 | + # Push results back to the request branch. |
| 57 | + persist-credentials: true |
| 58 | + # k3-* presets load LFS-tracked checkpoints from the repo |
| 59 | + # (e.g. results/research/f_theta_v5_s5_sliding/ |
| 60 | + # f_theta_weights.pt). Without lfs:true the workspace holds |
| 61 | + # pointer files and torch.load fails with the cryptic |
| 62 | + # "Unsupported operand 118" (ASCII 'v' = the first byte of |
| 63 | + # an LFS pointer). |
| 64 | + lfs: true |
| 65 | + |
| 66 | + - name: Show request |
| 67 | + run: | |
| 68 | + echo "=== .mac-bridge/request.json ===" |
| 69 | + cat .mac-bridge/request.json |
| 70 | +
|
| 71 | + - name: Materialize LFS objects (deterministic) |
| 72 | + # checkout@v4's lfs:true proved insufficient on a reused |
| 73 | + # self-hosted workspace: a previous non-LFS checkout left |
| 74 | + # pointer-content files in the worktree, the blob is unchanged |
| 75 | + # on the new branch, so git skips re-smudging and the stale |
| 76 | + # pointer survives (observed live: torch.load failing with |
| 77 | + # "Unsupported operand 118" = ASCII 'v' of an LFS pointer). |
| 78 | + # `git lfs pull` force-materializes; the guard fails fast if |
| 79 | + # any tracked LFS file is still a pointer. |
| 80 | + run: | |
| 81 | + git lfs install --local |
| 82 | + git lfs pull |
| 83 | + bad="" |
| 84 | + while IFS= read -r f; do |
| 85 | + if [ -f "$f" ] && head -c 40 "$f" | grep -q "git-lfs"; then |
| 86 | + bad="$bad $f" |
| 87 | + fi |
| 88 | + done < <(git lfs ls-files -n) |
| 89 | + if [ -n "$bad" ]; then |
| 90 | + echo "::error::LFS pointers not materialized:$bad" |
| 91 | + exit 1 |
| 92 | + fi |
| 93 | + echo "all LFS objects materialized" |
| 94 | +
|
| 95 | + - name: Run preset (allowlist-validated executor) |
| 96 | + env: |
| 97 | + PYTHONPATH: .:sdks/python |
| 98 | + # Machine-local model locations come from the runner env, |
| 99 | + # never from the manifest (docs/ops/mac-m4-runner-setup.md). |
| 100 | + # Precedence: repo Actions variable > ~/kakeya-models/<name> |
| 101 | + # (the documented stable symlink location on the runner) > |
| 102 | + # repo-relative fallback. $HOME needs shell expansion, hence |
| 103 | + # the export block instead of plain env defaults. |
| 104 | + KAKEYA_MAC_VERIFIER_PATH_VAR: ${{ vars.KAKEYA_MAC_VERIFIER_PATH || '' }} |
| 105 | + KAKEYA_MAC_DRAFTER_ID_VAR: ${{ vars.KAKEYA_MAC_DRAFTER_ID || '' }} |
| 106 | + KAKEYA_MAC_FTHETA_DIR_VAR: ${{ vars.KAKEYA_MAC_FTHETA_DIR || '' }} |
| 107 | + HF_HUB_OFFLINE: "1" |
| 108 | + run: | |
| 109 | + default_verifier="$HOME/kakeya-models/gemma-4-26B-A4B-it-mlx-4bit" |
| 110 | + if [ ! -d "$default_verifier" ]; then |
| 111 | + default_verifier="models/gemma-4-26B-A4B-it-mlx-4bit" |
| 112 | + fi |
| 113 | + export KAKEYA_MAC_VERIFIER_PATH="${KAKEYA_MAC_VERIFIER_PATH_VAR:-$default_verifier}" |
| 114 | + export KAKEYA_MAC_DRAFTER_ID="${KAKEYA_MAC_DRAFTER_ID_VAR:-z-lab/gemma-4-26B-A4B-it-DFlash}" |
| 115 | + export KAKEYA_MAC_FTHETA_DIR="${KAKEYA_MAC_FTHETA_DIR_VAR:-results/research/f_theta_v5_s5_sliding}" |
| 116 | + echo "verifier=$KAKEYA_MAC_VERIFIER_PATH" |
| 117 | + python3 scripts/mac_bridge/run_preset.py \ |
| 118 | + --manifest .mac-bridge/request.json |
| 119 | +
|
| 120 | + - name: Commit results back to the request branch |
| 121 | + if: always() |
| 122 | + run: | |
| 123 | + git config user.name "kakeya-mac-bridge" |
| 124 | + git config user.email "mac-bridge@users.noreply.github.com" |
| 125 | + git add -A .mac-bridge/logs results/research 2>/dev/null || true |
| 126 | + if git diff --cached --quiet; then |
| 127 | + echo "no result files to commit" |
| 128 | + else |
| 129 | + git commit -m "mac-bridge results: ${GITHUB_REF_NAME}" |
| 130 | + git push origin "HEAD:${GITHUB_REF_NAME}" |
| 131 | + fi |
| 132 | +
|
| 133 | + - name: Upload results as artifacts |
| 134 | + if: always() |
| 135 | + uses: actions/upload-artifact@v4 |
| 136 | + with: |
| 137 | + name: mac-bridge-${{ github.run_id }} |
| 138 | + path: | |
| 139 | + .mac-bridge/logs/ |
| 140 | + results/research/k3_mac_bridge_*.json |
| 141 | + if-no-files-found: warn |
| 142 | + retention-days: 14 |
0 commit comments