Skip to content

Register-when-ready Iroh hosts, bounded dial phases, cold-start release gate - #9752

Closed
azooz2003-bit wants to merge 5 commits into
mainfrom
issue-9724-iroh-register-when-ready
Closed

azooz2003-bit wants to merge 5 commits into
mainfrom
issue-9724-iroh-register-when-ready

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented Aug 7, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #9724 in three layers.

Summary

  • Mac register-when-ready: automatic-mode host startup starts relay activation eagerly and gives the relay a bounded window (automaticRelayReadinessTimeout, default 5s) to become usable, so the binding published at activation carries post-attach dialable hints instead of the half-ready bootstrap snapshot phones were racing. Only the cancellation-safe waitForUsableHomeRelay is awaited; timeout, broker error, or a hung credential install publishes the bootstrap policy exactly as before and leaves repair to the standard refresh-on-online path. Direct-only startup still skips readiness; the strict relay-only barrier is unchanged.
  • Bounded dial phases: CmxIrohClientSession.establishConnection awaited endpoint.connect with no clock on either the public or the private-fallback phase, which is where the dogfood trace burned a 16.2s hang against stale hints. Each phase now races the fork's cancellable ConnectAttempt against dialPhaseTimeout (default 5s); expiry cancels the FFI dial promptly, surfaces dialTimedOut (diagnostic kind timedOut), and a timed-out public phase still falls through to the private fallback.
  • Cold-start release gate: new --mode cold-start (GATE_SCENARIO=cold_start_dial) pins the launch-to-usable deadline to 20s on the gate's phone launch against the freshly relaunched Mac, making this race a permanent tripwire. Also selectable in the iroh-release-gate workflow matrix.

Evidence base: decoded cmuxdiag timeline from the PR 9430 dogfood round (discovery succeeded at t+7s; dial 1 connected then failed pairing after 7.6s; dial 2 hung 16.2s; dial 3 connected in 455ms; usable at ~t+43s), full decode in the issue.

Verification

  • Red/green regression pair for register-when-ready: commit 6c2984a adds the failing test (automatic startup published bootstrap hints, one registration), commit feb58d3 turns it green.
  • swift test --package-path Packages/Shared/CmuxIrohTransport: 572 tests, 64 suites, all pass, including the new hung-dial bound test and the credential-installation race test updated to the bounded contract.
  • swift build --package-path Packages/iOS/CmuxMobileShell compiles the new scenario case.

Build and behavior verification (2026-08-07, tag irdy)

  • The app-target build caught a non-exhaustive scenario switch in the release-gate probe, fixed in 1cd310b (cold-start reuses the standard probe legs).
  • macOS + iOS simulator builds green on tag irdy; sim app signed in and attached to the tagged Mac (workspace list live).
  • Behavior preflight of the fixed path: quit and relaunch the tagged Mac with the sim app open; the sim's terminal-events subscription re-established 22s after relaunch, including full Mac cold start, with no hung-dial churn. The pre-fix baseline on tag asrm was 70+ seconds with a 16.2s transportDialFailed hang (Iroh: phones race half-ready Mac endpoints; register-when-ready + bounded dials + launch-dial release gate #9724).
  • iPhone: dev.cmux.ios.irdy built, signed, and installed on the personal iPhone; the signed launch retry is pending the phone being awake.

Pending verification

Deferred (documented in the issue)

  • Server-side ready flag on iroh_endpoint_bindings needs a schema migration via the cloud-vm-ops flow; this PR is client-only.
  • Between-retry hint re-fetch on iOS (registry context provider snapshot caches) is the remaining Layer 2 half, follow-up PR.

Notes

  • no autoreview, Codex review, Claude review, or second-model review was invoked

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Note

Cursor Bugbot is generating a summary for commit 1592f48. Configure here.


Summary by cubic

Prevents long post-restart dial hangs by publishing relay-ready hints during automatic host startup and by bounding each dial phase. Adds a cold-start release gate that relaunches the Mac and enforces a 20s launch-to-usable deadline via the orchestrator.

  • Bug Fixes

    • Automatic-mode CmxIrohHostRuntime eagerly activates relay and waits up to 5s for readiness before publishing; on timeout/error it publishes bootstrap and refreshes later. Direct-only behavior is unchanged.
    • CmxIrohClientSession bounds each dial phase with dialPhaseTimeout (default 5s); expiry cancels the connect, surfaces dialTimedOut, and public timeouts still fall back to private paths.
    • Diagnostic mapping adds .timedOut for dialTimedOut.
  • New Features

    • Release-gate mode cold-start (cold_start_dial) with a 20s launch-to-usable deadline; added to the workflow matrix, runner admission, probe switch, and scripts/run-iroh-release-gate.sh. The probe reuses standard usability checks; timing is enforced by the script.

Written for commit 1cd310b. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added cold-start release-gate testing for recently launched devices.
    • Added configurable connection and relay-readiness timeouts.
    • Added explicit timeout reporting for connection attempts.
    • Improved automatic relay startup and route publication.
  • Bug Fixes

    • Prevented stalled connections from blocking session establishment.
    • Improved startup when relay credentials or bindings are temporarily unavailable.
  • Tests

    • Added coverage for stalled dialing, cold-start scenarios, relay readiness, and startup lifecycle races.

azooz2003-bit and others added 4 commits August 6, 2026 20:02
A just-launched Mac registers with the broker before its endpoint has
attached to the home relay, so the advertised binding carries no relay
hint and phones race a half-ready endpoint (16s hung dials observed in
dogfood, #9724). The strict
relay-readiness re-register block in CmxIrohHostRuntime.start() only
runs for relay-only mode; automatic mode publishes the bootstrap
snapshot immediately.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Generalize the relay-only readiness barrier opportunistically: automatic
mode now starts relay activation eagerly and gives the relay a bounded
window (automaticRelayReadinessTimeout, default 5s) to become usable, so
the binding published at activation carries post-attach dialable hints
instead of the half-ready bootstrap snapshot phones were racing
(#9724).

Liveness is preserved everywhere: only the cancellation-safe
waitForUsableHomeRelay is awaited against the budget, relay activation
stays on the async sidecar with unchanged retry ownership, and any
timeout, broker error, or hung credential install publishes the
bootstrap policy exactly as before, leaving repair to the standard
refresh-on-online path. Direct-only startup still skips readiness
entirely. The credential-installation race test now injects a tight
budget and asserts publication completes despite a hung install.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dials had no clock anywhere: CmxIrohClientSession.establishConnection
awaited endpoint.connect unbounded on both the public and the private
fallback phase, so a stale-hint or half-ready target hung the attempt
until an outer RPC deadline (16.2s observed in the issue 9724 dogfood
trace). Each phase now races the fork's cancellable ConnectAttempt
against one bound; expiry cancels the FFI dial promptly and surfaces
CmxIrohClientSessionError.dialTimedOut (diagnostic kind timedOut), and
a timed-out public phase still falls through to the private fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New --mode cold-start row: the orchestrator already relaunches the
tagged Mac unconditionally, so the scenario pins the launch-to-usable
deadline (CMUX_ATTACH_READY_TIMEOUT_SECONDS=20) instead of inheriting
the helper's ambient default, making the just-launched-Mac dial race of
#9724 a permanent tripwire: a
half-ready registration or unbounded dial blows the deadline and fails
the gate. The cold_start_dial report scenario reuses the standard
usable-session proofs; the timing assertion lives at the script layer,
which measures true cross-process wall clock.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds bounded Iroh dialing, automatic relay-readiness waiting, and a cold-start release-gate mode. It also adds timeout diagnostics, startup and dial tests, workflow selection, and script configuration.

Changes

Iroh transport readiness

Layer / File(s) Summary
Bounded client dialing
Packages/Shared/CmuxIrohTransport/Sources/CmuxIrohTransport/CmxIrohClientSession.swift, Packages/Shared/CmuxIrohTransport/Sources/CmuxIrohTransport/CmxIrohClientSessionError.swift, Packages/Shared/CmuxIrohTransport/Sources/CmuxIrohTransport/CmxIrohDiagnosticFailure.swift, Packages/Shared/CmuxIrohTransport/Tests/CmuxIrohTransportTests/*
Client sessions apply a configurable five-second timeout to public and fallback dials. Timeout failures use dialTimedOut and map to .timedOut. Tests simulate and verify a hanging dial.
Automatic relay readiness
Packages/Shared/CmuxIrohTransport/Sources/CmuxIrohTransport/CmxIrohHostRuntime.swift, Packages/Shared/CmuxIrohTransport/Tests/CmuxIrohTransportTests/CmxIrohHostRuntimeLifecycleRaceTests.swift, Packages/Shared/CmuxIrohTransport/Tests/CmuxIrohTransportTests/CmxIrohHostRuntimeTests.swift
Automatic startup waits for usable relay connectivity within a configurable timeout. Ready bindings refresh policy and admission state, while startup avoids duplicate relay activation. Tests cover suspended relay installation and publication of relay-ready hints.
Cold-start release-gate wiring
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShellReleaseGateSupport/MobileIrohReleaseGateScenario.swift, ios/cmuxPackage/Sources/CmuxIrohReleaseGateSupport/MobileIrohReleaseGateRunner.swift, ios/cmuxPackage/Tests/cmuxFeatureTests/MobileIrohReleaseGateRunnerTests.swift, scripts/run-iroh-release-gate.sh, .github/workflows/iroh-release-gate.yml
The cold-start mode selects automatic transport and the cold_start_dial scenario. It uses a 20-second launch-readiness timeout and is available from workflow dispatch and the all matrix.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ReleaseGateScript
  participant MobileIrohReleaseGateRunner
  participant CmxIrohHostRuntime
  participant Broker
  participant CmxIrohClientSession

  ReleaseGateScript->>MobileIrohReleaseGateRunner: Start cold_start_dial with automatic transport
  CmxIrohHostRuntime->>Broker: Register and activate relay
  CmxIrohHostRuntime->>Broker: Publish relay-ready route hints
  MobileIrohReleaseGateRunner->>CmxIrohClientSession: Dial the newly launched Mac
  CmxIrohClientSession->>CmxIrohClientSession: Apply dialPhaseTimeout
  CmxIrohClientSession-->>MobileIrohReleaseGateRunner: Return usable session or dialTimedOut
Loading

Suggested reviewers: lawrencecchen


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (2 errors, 2 warnings)

Check name Status Explanation Resolution
Cmux Swift Blocking Runtime ❌ Error Production CmxIrohClientSession.connectBounded adds ContinuousClock().sleep(for: bound) as timing-based synchronization for dial timeout; only the analogous Task.sleep in test scaffolding is allowed. Replace the direct clock sleep with an approved injected cancellation-aware deadline/timer abstraction or endpoint-native timeout signal, while preserving cancellation of the losing dial task.
Cmux Architecture Rethink ❌ Error CmxIrohHostRuntime races relay readiness with a ContinuousClock timeout, then publishes unchanged bootstrap policy on timeout/error; half-ready bindings remain representable and stale-dial races re... Make the host/relay coordinator the single owner of registration readiness: publish only after fresh hints, or atomically mark the binding not-ready; use one event-driven refresh transition instead of a timeout fallback.
Linked Issues check ⚠️ Warning The PR implements register-when-ready, bounded dials, and the release gate, but it does not implement the issue's required between-retry hint refresh. Implement and test broker hint refresh between dial retries, or split that requirement into a separately linked issue before claiming full issue compliance.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (21 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed All listed changes support issue #9724: runtime readiness, bounded dials, diagnostics, regression tests, and the cold-start gate.
Cmux Swift Actor Isolation ✅ Passed Changed transport state remains in actors; the release runner is explicitly @MainActor, and the new scenario is a standalone Sendable enum. No new service protocol, shared Sendable reference, or ba...
Cmux Browser Automation Off-Main ✅ Passed The PR changes only Iroh transport and release-gate files; no browser socket automation, WebKit/AppKit routing, processV2Command, or socket-worker policy code changed.
Cmux Expensive Synchronous Load ✅ Passed Changed production Swift adds Iroh timeout/readiness and release-gate control flow; it adds no agent-history loader, large-file parsing, directory scan, or synchronous load on an interactive/main-a...
Cmux Cache Substitution Correctness ✅ Passed The PR does not replace a fresh persistence/history/undo read with a cache; startup adds a fresh policy resolve with allowCachedFallback: false, and discovery cache reuse is revision-checked.
Cmux No Hacky Sleeps ✅ Passed The only covered diff is a release-gate shell mode that sets a bounded 20-second readiness deadline; it adds no sleep, timer, polling, or delayed dispatch. Workflow and Swift timing are out of scope.
Cmux Algorithmic Complexity ✅ Passed The production diff adds bounded task-group races and scalar policy flow only; it adds no scalable-collection scans, nested loops, sorting, filtering, or in-memory joins.
Cmux Swift Concurrency ✅ Passed The PR adds only structured withThrowingTaskGroup dialing and a test-only cancellable sleep; no new DispatchQueue, Combine, completion-handler, or unowned lifecycle Task patterns appear.
Cmux Swift @Concurrent ✅ Passed New async dial/readiness work is actor-isolated in CmxIrohClientSession and CmxIrohHostRuntime; release-gate changes reuse the existing @MainActor probe flow and add no nonisolated async work.
Cmux Swift Package Boundaries ✅ Passed All changed production Swift remains behind existing SwiftPM targets: CmuxIrohTransport, CmuxMobileShellReleaseGateSupport, and CmuxIrohReleaseGateSupport; no app-root Sources code changed.
Cmux Swiftpm Lockfiles ✅ Passed PR changes only workflow and source/test files; no Package.swift, Package.resolved, .gitignore, or Xcode project changes, and package .gitignores do not ignore Package.resolved.
Cmux Swift Logging ✅ Passed The PR adds no print, debugPrint, dump, NSLog, file, or stdout logging; existing Logger declarations are unchanged, and shell output remains intended CLI output with credential redaction.
Cmux User-Facing Error Privacy ✅ Passed The diff adds no user-facing error or API text with sensitive implementation details; the new failure maps to the generic diagnostic “Timed out,” while gate strings are developer-only.
Cmux Full Internationalization ✅ Passed The PR adds no user-facing UI or localization keys; new Swift text is comments/tests, dialTimedOut maps to existing .timedOut, and CLI/workflow tokens are operational.
Cmux Swiftui State Layout ✅ Passed The PR changes transport and release-gate logic only; changed Swift files add no SwiftUI views, ObservableObject state, GeometryReader, lazy/list row stores, or render-time state writes.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR Swift diff adds no NSWindow, NSPanel, WindowController, WindowGroup, or identifier assignments; scripts/lint_auxiliary_window_close_shortcuts.py also passed.
Cmux Source Artifacts ✅ Passed All 15 changed paths are Swift source/tests, workflow YAML, or a release script; no artifact-like paths, binary files, logs, caches, temp directories, or build outputs appear in the diff.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The production diff adds bounded dialing and relay-readiness behavior, not test accessors or seam-named members; the only DEBUG-gated addition is a release-gate case in dedicated support code.
Cmux No Ambient Global State ✅ Passed The diff adds only instance state and a private helper on existing actors, enum cases, and existing switch branches; it adds no top-level API, mutable global, static namespace, or singleton.
Title check ✅ Passed The title clearly summarizes the three main changes: register-when-ready hosts, bounded dial phases, and the cold-start release gate.
Description check ✅ Passed The description clearly covers the changes, rationale, verification, pending work, and deferrals, but omits the template's Demo Video, Review Trigger, and Checklist sections.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-9724-iroh-register-when-ready

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/iroh-release-gate.yml:
- Line 63: Update the workflow conditions governing automatic-mode setup to
include the cold-start matrix value, specifically the branches near lines 83 and
89. Extend the TAG selection case statement with a unique cold-start) assignment
before invoking the gate script, ensuring cold-start follows the automatic
transport gate path and initializes TAG under set -u.

In
`@ios/cmuxPackage/Sources/CmuxIrohReleaseGateSupport/MobileIrohReleaseGateRunner.swift`:
- Around line 52-54: Update the scenario guard in MobileIrohReleaseGateRunner so
coldStartDial is accepted only when mode is automatic, while preserving standard
and relayOnly behavior. Add rejection tests covering coldStartDial with
directOnly and relayOnly modes.

In
`@Packages/Shared/CmuxIrohTransport/Sources/CmuxIrohTransport/CmxIrohClientSession.swift`:
- Around line 320-324: Add a regression test for the CmxIrohClientSession
connection flow using the dial sequence [.hang, .connection(...)]. Assert the
public attempt produces a timeout, the validated private fallback is selected
and dialed, and admission completes successfully; keep the existing
terminal-timeout and failure-based fallback coverage unchanged.

In
`@Packages/Shared/CmuxIrohTransport/Tests/CmuxIrohTransportTests/CmxIrohClientSessionTests.swift`:
- Around line 31-36: Remove the ContinuousClock setup, started timestamp, and
elapsed-time assertion from CmxIrohClientSessionTests. Keep the await
session.connect() check asserting the typed
CmxIrohClientSessionError.dialTimedOut result; add cancellation completion
signaling only if separate cancellation coverage is required.

In
`@Packages/Shared/CmuxIrohTransport/Tests/CmuxIrohTransportTests/CmxIrohHostRuntimeLifecycleRaceTests.swift`:
- Around line 110-120: Remove the fixed .milliseconds(20) timeout from the
runtime setup in CmxIrohHostRuntimeLifecycleRaceTests and inject a controllable
relay-readiness clock or completion signal instead. After
gate.waitUntilSuspended(), explicitly advance or complete that readiness
mechanism before awaiting start.value, while preserving the existing transport
and binding handlers.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bf5a7474-b94a-4a24-8e62-3fc2801b7990

📥 Commits

Reviewing files that changed from the base of the PR and between 1001368 and 1592f48.

📒 Files selected for processing (14)
  • .github/workflows/iroh-release-gate.yml
  • Packages/Shared/CmuxIrohTransport/Sources/CmuxIrohTransport/CmxIrohClientSession.swift
  • Packages/Shared/CmuxIrohTransport/Sources/CmuxIrohTransport/CmxIrohClientSessionError.swift
  • Packages/Shared/CmuxIrohTransport/Sources/CmuxIrohTransport/CmxIrohDiagnosticFailure.swift
  • Packages/Shared/CmuxIrohTransport/Sources/CmuxIrohTransport/CmxIrohHostRuntime.swift
  • Packages/Shared/CmuxIrohTransport/Tests/CmuxIrohTransportTests/CmxIrohClientSessionTests.swift
  • Packages/Shared/CmuxIrohTransport/Tests/CmuxIrohTransportTests/CmxIrohHostRuntimeLifecycleRaceTests.swift
  • Packages/Shared/CmuxIrohTransport/Tests/CmuxIrohTransportTests/CmxIrohHostRuntimeTests.swift
  • Packages/Shared/CmuxIrohTransport/Tests/CmuxIrohTransportTests/TestDialingIrohEndpoint.swift
  • Packages/Shared/CmuxIrohTransport/Tests/CmuxIrohTransportTests/TestIrohDialResult.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShellReleaseGateSupport/MobileIrohReleaseGateScenario.swift
  • ios/cmuxPackage/Sources/CmuxIrohReleaseGateSupport/MobileIrohReleaseGateRunner.swift
  • ios/cmuxPackage/Tests/cmuxFeatureTests/MobileIrohReleaseGateRunnerTests.swift
  • scripts/run-iroh-release-gate.sh

fail-fast: false
matrix:
mode: ${{ fromJSON(inputs.mode == 'all' && '["automatic","relay-only","relay-expiry","direct-only","private-path"]' || format('["{0}"]', inputs.mode)) }}
mode: ${{ fromJSON(inputs.mode == 'all' && '["automatic","relay-only","relay-expiry","direct-only","private-path","cold-start"]' || format('["{0}"]', inputs.mode)) }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Complete the cold-start workflow wiring.

The new matrix value skips both automatic-mode setup conditions. It also has no TAG case. With set -u, the gate exits when it expands $TAG.

Include cold-start in the conditions at Lines 83 and 89. Add a unique cold-start) TAG=... ;; case before invoking the script.

Based on the PR objective, cold-start must execute the automatic transport gate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/iroh-release-gate.yml at line 63, Update the workflow
conditions governing automatic-mode setup to include the cold-start matrix
value, specifically the branches near lines 83 and 89. Extend the TAG selection
case statement with a unique cold-start) assignment before invoking the gate
script, ensuring cold-start follows the automatic transport gate path and
initializes TAG under set -u.

Comment on lines +52 to +54
guard scenario == .standard
|| scenario == .coldStartDial
|| mode == .relayOnly else { return nil }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Restrict coldStartDial to automatic transport.

This condition accepts cold_start_dial with directOnly or relayOnly. Those runs can pass without testing automatic relay startup and fallback behavior. Require mode == .automatic when scenario == .coldStartDial. Add rejection tests for the other modes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ios/cmuxPackage/Sources/CmuxIrohReleaseGateSupport/MobileIrohReleaseGateRunner.swift`
around lines 52 - 54, Update the scenario guard in MobileIrohReleaseGateRunner
so coldStartDial is accepted only when mode is automatic, while preserving
standard and relayOnly behavior. Add rejection tests covering coldStartDial with
directOnly and relayOnly modes.

Comment on lines +320 to +324
establishedConnection = try await connectBounded(
to: CmxIrohEndpointAddress(
identity: targetIdentity,
pathHints: dialPlan.publicPaths
),
alpn: protocolConfiguration.alpn
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add coverage for timeout-to-private fallback.

A public .dialTimedOut error must continue to the validated private fallback. The new test covers a terminal timeout without fallback. The existing fallback test uses .failure, not .hang.

Add a regression test with [.hang, .connection(...)]. Assert that the public path times out, the private path is validated and dialed, and admission completes.

Based on the PR objective, a timed-out public dial must proceed to private fallback.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/Shared/CmuxIrohTransport/Sources/CmuxIrohTransport/CmxIrohClientSession.swift`
around lines 320 - 324, Add a regression test for the CmxIrohClientSession
connection flow using the dial sequence [.hang, .connection(...)]. Assert the
public attempt produces a timeout, the validated private fallback is selected
and dialed, and admission completes successfully; keep the existing
terminal-timeout and failure-based fallback coverage unchanged.

Comment on lines +31 to +36
let clock = ContinuousClock()
let started = clock.now
await #expect(throws: CmxIrohClientSessionError.dialTimedOut) {
try await session.connect()
}
#expect(clock.now - started < .seconds(2))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the wall-clock assertion.

ContinuousClock and the two-second ceiling make this test depend on runner scheduling. Keep the typed .dialTimedOut assertion. If cancellation needs separate coverage, expose a fixture completion signal and await it.

Proposed fix
-        let clock = ContinuousClock()
-        let started = clock.now
         await `#expect`(throws: CmxIrohClientSessionError.dialTimedOut) {
             try await session.connect()
         }
-        `#expect`(clock.now - started < .seconds(2))

As per coding guidelines, tests must not use wall-clock assertions or hard absolute latency ceilings.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let clock = ContinuousClock()
let started = clock.now
await #expect(throws: CmxIrohClientSessionError.dialTimedOut) {
try await session.connect()
}
#expect(clock.now - started < .seconds(2))
await `#expect`(throws: CmxIrohClientSessionError.dialTimedOut) {
try await session.connect()
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/Shared/CmuxIrohTransport/Tests/CmuxIrohTransportTests/CmxIrohClientSessionTests.swift`
around lines 31 - 36, Remove the ContinuousClock setup, started timestamp, and
elapsed-time assertion from CmxIrohClientSessionTests. Keep the await
session.connect() check asserting the typed
CmxIrohClientSessionError.dialTimedOut result; add cancellation completion
signaling only if separate cancellation coverage is required.

Source: Coding guidelines

Comment on lines +110 to +120
// The in-start readiness pass may wait this long for the relay,
// but a hung credential installation must never block activation
// or binding publication beyond it.
automaticRelayReadinessTimeout: .milliseconds(20),
handleTransport: { session, _ in await session.close() },
handleBinding: { _, _, _ in await bindings.record() }
)
let start = Task { try await runtime.start() }
await gate.waitUntilSuspended()

try await start.value

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Remove the wall-clock dependency from this test.

automaticRelayReadinessTimeout: .milliseconds(20) makes start.value depend on scheduler timing. CI load can make this test flaky. Inject a controllable relay-readiness clock or readiness signal, then advance or complete it after gate.waitUntilSuspended().

As per coding guidelines, tests must await completion signals or use injected virtual clocks instead of fixed-duration waits.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/Shared/CmuxIrohTransport/Tests/CmuxIrohTransportTests/CmxIrohHostRuntimeLifecycleRaceTests.swift`
around lines 110 - 120, Remove the fixed .milliseconds(20) timeout from the
runtime setup in CmxIrohHostRuntimeLifecycleRaceTests and inject a controllable
relay-readiness clock or completion signal instead. After
gate.waitUntilSuspended(), explicitly advance or complete that readiness
mechanism before awaiting start.value, while preserving the existing transport
and binding handlers.

Source: Coding guidelines

The cold-start scenario proves the same usable-session legs as standard;
its launch-to-usable deadline is enforced at the orchestrator script
layer, so the probe switch reuses the standard branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Iroh: phones race half-ready Mac endpoints; register-when-ready + bounded dials + launch-dial release gate

3 participants