Decision matrix 8 min read

2026 Small-Team Shared Remote Mac: Jira Automation tag routing, build locks & concurrency timeout checklist

M

Published April 15, 2026

Meshmac Team

When PMs drive work from Jira and engineers share one or two remote Mac builders, the fragile seam is not “HTTP from Jira Cloud.” It is tag routing without capacity awareness, missing build locks, and Automation rules that assume exclusive access. This matrix ties issue labels to target nodes, names lock primitives, caps flock depth, and lists timeout and backoff defaults so your gateway and shells stay predictable under retries.

Pain scenarios

Typical chain: someone adds build-ios, status churn fires Automation twice, two webhooks enqueue before humans notice—then DerivedData, Simulator, or exportArchive races a signing identity. Label sprawl makes routing tables lie: many cosmetic labels map to one node, so on-call cannot see which lane is saturated.

Interactive work makes it worse: SSH tests in the same Unix account as Automation collide on files and env; VNC debugging plus xcodebuild hides CPU pegging behind UI lag. If GitHub already schedules jobs, Jira scripts must align with runner label routing or you get “green in Jira, red on the Mac” when two schedulers share one disk budget.

Fix serialisation first: routing table, mutexes on shared trees, conservative queue depth—then pool quota checklist when depth grows.

SSH, collaboration, and CI decisions

SSH-first for Automation callbacks: builder account, short-lived keys, keepalives documented. Put pairing, code-server, or Screen Sharing on other accounts or nodes so rules never inherit a GUI env. See SSH forward vs direct Mac and SSH vs VNC guide.

If GitHub owns merge gates, prefer Jira → HTTP → queue → existing labels (merge queue vs runner matrix) instead of fat inline scripts. If Jira orchestrates, expose correlation ids, enqueue latency, and lock-wait metrics like any CI system.

Decision Choose when Watch-out
SSH script runner (per-lane Unix user) Headless builds, flock around shared trees, predictable PATH. Key sprawl; enforce one deploy key per lane plus audit logging.
Runner / queue worker You already standardised on GitHub or a small worker daemon. Two schedulers (Jira + CI) need non-overlapping labels and disk roots.
Desktop / VNC lane Simulator or manual repro steps triggered rarely. Never share a login with Automation; split nodes or time windows.

Label → node, locks, queues, timeouts

Use machine-owned labels (ci/pr, ci/release, hotfix) mapped to builder classes, not people. Table: two-node baseline—tune for your cores, SSD, and Xcode pins. Deep dives: flock FAQ, queue lock FAQ.

Jira label / tag Target node / lane Lock implementation flock / queue ceiling Timeout / backoff
ci/pr Node A — PR builder flock on repo worktree + workflow concurrency group pr-${{issue.key}} Max 2 concurrent shells; pending depth ≤ 8 per node HTTP client 30s connect / 120s read; shell 45m; retry backoff 15s + jitter, cap 3 attempts
ci/release Node B — release host Global mutex file for archive + separate flock for notary stapler 1 concurrent release; queue depth ≤ 3 (alert at 2) Shell 120m; notary step 45m sub-timeout; backoff 60s exp max 10m
hotfix Node B — preempts PR lane only if pool policy allows Priority queue in gateway + cooperative cancel token to PR jobs Burst 1 hotfix; drain PR queue to ≤ 1 before start HTTP 60s; shell 90m; single retry without jitter on 5xx
design-qa (optional) Node A — low priority lane CPU cgroup or nice + separate workdir; no signing Max 1 job; never parallel with ci/release on same disk volume Shell 20m; HTTP 45s; backoff 30s linear max 5 attempts

Permission isolation

Never run Automation as a personal login. Use service accounts per lane, separate homes and SSH keys, least-privilege sudo (ideally none), read-only shared caches, setgid only where artifacts cross accounts, and separate keychains for CI vs interactive signing.

Terminate webhooks on a small gateway (verify HMAC or static secret), then forward over VPN or mesh—same shape as verified webhook → queue.

Unix/VNC boundaries: permission isolation guide; unlock signing keychains only inside the signing flock.

Jira trigger fields & idempotency

Narrow triggers beat broad ones: e.g. transition to “Ready for build” plus label add plus flip a “Build requested” custom field in a second action to stop column-drag thrash. Log webhookEvent, issue.id, issue.key, transition.transitionId, and a hash of labels on the gateway.

Idempotency: dedupe_key = sha256(issue.id + transitionId + ruleId + sorted(labels)); drop duplicates for 15–60 minutes. Return 200 only after durable enqueue; use 429 + Retry-After when saturated. Throttle Jira REST comments (“queued” vs “running”).

Useful fields: Build correlation id, Last automation run, Lock owner (gateway updates when flock is held).

Conflict case FAQ

Two issues share ci/release within a minute—who wins?
First durable enqueue wins; second gets 202 + position. Never double-start on one signing mutex. Reorder only before acquisition via a numeric priority field—not mid-archive.
flock returns instantly but artifacts corrupt—why?
Wrong path locked, or subprocess escaped the locking shell—one wrapper, explicit LOCKFILE. See worktree lockfile matrix.
Jira green, Mac idle—what failed?
Rule accepted HTTP 200 from a health stub. Require JSON {"accepted":true,"task_id":"..."}; correlate with Jira audit timestamps.
Let queue depth grow across time zones?
No—cap per lane; shed with 429 + Retry-After. Add a Mac before raising table ceilings.

Summary & purchase

Ship when labels map to nodes, locks cover trees/signing/Simulator data, and timeouts match real xcodebuild times. Buy another Mac when weekly queue saturation or lock wait exceeds your SLO—not when charts merely look busy. Split PR vs release hardware before heavier orchestration.

Add lanes before your Jira queue tells lies

Compare public MeshMac plans and multi-node packages without signing in, then read the help center for SSH, VNC, and onboarding—also readable with no login. Skim the blog index and homepage for fleet sizing context, keep this matrix next to your runner routing doc, and checkout only when your label table and lock paths are named.

Plans — no login