fix(gate): take the host validation lock for heavy gates
Merged
TH
4h ago+494+494+012345678901234567890123456789-79-79-01234567890123456789
Summary
pnpm gate unit-ci (and other whole-repo gates) did not take the host validation lock. Four worktrees ran it at once on a 12-core Mac; load reached ~1000 and every run hit false 5 s test timeouts.
pnpm gate <id> now runs heavy gates under the same lock that check:affected --run and check hold: same owner, waiter output, dead-owner reclaim, and 2-hour timeout. No second lock.
- Locked (
heavyGateinCHECK_CATALOG,hostLocked):unit,unit-ci,provider-integration(whole-repo suites),fuzz-parsers,mutation,mutation-affected,concurrency-torture(many workers/processes). Cheap gates stay unlocked. - Re-entry: a gate run inside a locked validation sees the inherited
AGENT_DEVICE_VALIDATION_PARENTand runs directly, so nothing deadlocks. - CI:
GITHUB_ACTIONS=trueruns the gate unlocked (single-tenant runners). Unsupported hosts (Windows) also run unlocked. acquireValidationLocktakes an optionalenv, so nested-owner detection reads the same environment as the gate runner.- Docs: AGENTS.md and
docs/agents/testing.mdname the locked gates. To stay inside the guidance byte budgets, I removed a few duplicate sentences intesting.md/pull-requests.md.
The lock protocol (publish, activate, finish) and the signal-to-exit-code mapping have one owner in scripts/check-affected: runLockedCommand and abortOnTerminationSignal. Validation runs and gates both use them.
No linked issue.
Validation
- Tested SHA: 1628711576.
pnpm check:affected --run --head 1628711576exits 0 withexactCommit: true(all runnable checks passed, including lint on the mergedorigin/main).scripts/gate/host-locked.test.ts(7 tests) andhost-lock.test.ts(4 tests) pass. They cover waiting, the exit code, nested no-deadlock, unlocked cheap and GitHub gates, a signal while waiting (143), a signal while the locked command runs (143, command group gone, lock andactive.jsonremoved), andrunLockedCommandpublishing and clearing ownership.pnpm gate unit-ciwaited behind another owner, then ran through the sharedrunLockedCommandpath: exit 0, 16977 tests passed, lock released.- CI owns full coverage, integration, and native lanes.
🤖 Generated with Claude Code