Skip to content

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-swift agent/runtime exists anywhere — rig-agent-runtime ships node/dotnet/python only, and no dev-e-swift fleet is stamped in rig-gitops.
  • iOS builds are meant to run on iBuild-E (Xcode) — reached previously only via the requires-macos label, not the swift stack.

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:

public static bool RequiresMacOs(string? stack)

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:

  1. Issue-assignment webhook (issues.opened, issues.labeled, …) — requiresMacos = requiresMacos || MacOsStackPolicy.RequiresMacOs(stack);
  2. Review-disputed re-dispatchimplementingAgent resolution.
  3. CI-failure re-dispatch — retry path.

src/ConductorE.Api/Services/:

  1. IssueScanService (ScanForUndispatchedIssuesAsync) — missed-issue backfill scanner.
  2. ReconciliationService (DispatchUnblockedIssuesAsync) — dependency-unblock re-dispatch.
  3. SloEnforcerService (ReDispatchAsync) — SLO-breach re-dispatch.
  4. StaleAgentReadyWatcher (TryRescueAsync) — stale queued+null-agent rescue.
  5. 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 the requires-macos label load-bearing for correct routing. Silent failure mode on typo. Rejected.
  • Option 3 (build a real dev-e-swift runtime): iOS builds require Xcode → macOS host → this is what ibuild-e already 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 once ibuild-e returns, 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 discoverable MacOsRequiredStacks surface.
  • Adapter-level regression tests, mirroring the existing stack: ios + escalate: requires-macos → ibuild-e tests, but with stack: swift and no escalate: requires-macos — proving the label is no longer load-bearing for correct routing:
  • IssueScanServiceTests.Scan_StackSwiftWithoutLabel_RoutesToIbuildE
  • ReconciliationServiceTests.DispatchUnblockedIssuesAsync_StackSwiftWithoutLabel_RoutesToIbuildE
  • SloEnforcerServiceTests.Scan_StackSwiftWithoutLabel_RoutesToIbuildE
  • StaleAgentReadyWatcherTests.Scan_StackSwiftWithoutLabel_RoutesToIbuildE

Closes rig-conductor#1949.