Swift stack routes to iBuild-E instead of non-existent dev-e-swift¶
Problem¶
dashecorp/rig-cockpit-ios .rig-agent.yaml declares stack: swift. The
conductor's stack→agent routing therefore built agentId = "dev-e-swift" and
published to assignments:dev-e-swift@dashecorp (observed live for
rig-cockpit-ios#3 / #17 / #21).
But:
- No
dev-e-swiftagent/runtime exists anywhere —rig-agent-runtimeships node/dotnet/python only, and nodev-e-swiftfleet is stamped inrig-gitops. - iOS builds are meant to run on iBuild-E (Xcode) — reached previously only
via the
requires-macoslabel, not theswiftstack.
Net: all rig-cockpit-ios dev work was silently stranded — dispatched, but no consumer on the far end of the stream.
Decision¶
Adopt Option 1 from the issue: the conductor treats
stack: swift as implying macOS hardware routing. Any incoming issue whose
.rig-agent.yaml names a macOS-required stack is routed to ibuild-e,
independent of whether the operator also applied the requires-macos label.
The decision lives in ConductorE.Core.UseCases.MacOsStackPolicy — a pure,
I/O-free static class:
Backing list: MacOsStackPolicy.MacOsRequiredStacks = ["swift"]. To add another
macOS-only stack in the future, extend that list and add a test case.
Wiring¶
Rig-conductor computes requiresMacos ? "ibuild-e" : $"dev-e-{stack}" at
eight distinct dispatch sites — the webhook happy path plus seven
recovery/scan/re-dispatch paths. All eight now OR the policy into their
existing requiresMacos boolean, so a stack: swift repo cannot leak onto
assignments:dev-e-swift@<tenant> via any path:
src/ConductorE.Api/Program.cs:
- Issue-assignment webhook (
issues.opened,issues.labeled, …) —requiresMacos = requiresMacos || MacOsStackPolicy.RequiresMacOs(stack); - Review-disputed re-dispatch —
implementingAgentresolution. - CI-failure re-dispatch — retry path.
src/ConductorE.Api/Services/:
- IssueScanService (
ScanForUndispatchedIssuesAsync) — missed-issue backfill scanner. - ReconciliationService (
DispatchUnblockedIssuesAsync) — dependency-unblock re-dispatch. - SloEnforcerService (
ReDispatchAsync) — SLO-breach re-dispatch. - StaleAgentReadyWatcher (
TryRescueAsync) — stale queued+null-agent rescue. - MainGuardService — forward-fix issue routing after main-red.
Why so many sites: stack→agent routing was inlined into every publisher
path historically. This PR does not consolidate them into a shared helper
(too broad a refactor for a bug fix), but it does centralize the
swift → macOS decision in MacOsStackPolicy. Every future site that
computes dev-e-{stack} must call MacOsStackPolicy.RequiresMacOs(stack) —
see this doc as the checklist.
Why not Option 2 or 3¶
- Option 2 (drop
stack: swift, gate on label): moves the config burden onto every iOS repo and makes therequires-macoslabel load-bearing for correct routing. Silent failure mode on typo. Rejected. - Option 3 (build a real
dev-e-swiftruntime): iOS builds require Xcode → macOS host → this is whatibuild-ealready is. Building a second macOS runtime under a different name duplicates infra for no gain. Rejected.
Companion work (out of scope for this PR)¶
- Reviving
ibuild-e— currently parked (no healthy target available). Tracked separately; this PR fixes the routing so that onceibuild-ereturns, iOS issues actually reach it.
Tests¶
tests/ConductorE.Core.Tests/MacOsStackPolicyTests.cs— pure Core tests, 14 cases: swift-positive, case-insensitivity, non-macOS stacks unaffected, null/empty/whitespace fall-through, unknown-stack safety, and the discoverableMacOsRequiredStackssurface.- Adapter-level regression tests, mirroring the existing
stack: ios + escalate: requires-macos → ibuild-etests, but withstack: swiftand noescalate: requires-macos— proving the label is no longer load-bearing for correct routing: IssueScanServiceTests.Scan_StackSwiftWithoutLabel_RoutesToIbuildEReconciliationServiceTests.DispatchUnblockedIssuesAsync_StackSwiftWithoutLabel_RoutesToIbuildESloEnforcerServiceTests.Scan_StackSwiftWithoutLabel_RoutesToIbuildEStaleAgentReadyWatcherTests.Scan_StackSwiftWithoutLabel_RoutesToIbuildE
Closes rig-conductor#1949.