Bus: BridgeNavigationDelegate has no didFail arm — silent black HUD on bundle/JS load failure #40

Closed
opened 2026-05-12 19:07:43 +00:00 by KofTwentyTwo · 1 comment
KofTwentyTwo commented 2026-05-12 19:07:43 +00:00 (Migrated from github.com)

Summary

BridgeNavigationDelegate only implements didFinish. If index.html, the JS bundle, or any sub-resource fails to load (bad reference, 404 on file://, JS parse error before mount), the navigation never finishes, startHandshake() is never invoked, the 2-second handshake timeout is never armed, and the user sees a silent black HUD with no banner, no hard-block.

Evidence

  • File: App/AppDelegate.swift:2715-2723 — BridgeNavigationDelegate is a 9-line class with one method (didFinish)
  • File: packages/Bus/Sources/Bus/WebviewBridge.swift:269-288 — startHandshake() is the only place that schedules the 2s timeout
  • File: packages/Bus/Sources/Bus/Handshake.swift:24-26 — HandshakeTiming.timeout = .seconds(2)
  • No didFail / didFailProvisionalNavigation handlers anywhere

Root cause / hypothesis

The 2s timeout in Handshake.swift advertises a 2s SLA. With no didFail handler, the SLA is silently >∞ in the bundle-broken path. The probe handshakeState == .armed passes when reality is OK and provides no signal when reality breaks at the navigation layer.

Recommended fix

Add didFailProvisionalNavigation + didFail to BridgeNavigationDelegate. Route both into a new WebviewBridge.failHandshake(reason:) that transitions to .timedOut (or a new .loadFailed) and presents the same hard-block alert.

nonisolated func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
    MainActor.assumeIsolated { onDidFail(error) }
}

nonisolated func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
    MainActor.assumeIsolated { onDidFail(error) }
}

Test

  • Unit: serve a non-existent index.html URL → assert WebviewBridge.handshakeState transitions to .loadFailed (or .timedOut) within 100ms and hard-block alert fires
  • packages/Bus/Tests/BusTests/ has no didFail* references — add coverage

Source

  • Audit: .planning/audit-2026-05-12/bus-hud-audit.md (2026-05-12) — finding S1 (BridgeNavigationDelegate)
## Summary `BridgeNavigationDelegate` only implements `didFinish`. If `index.html`, the JS bundle, or any sub-resource fails to load (bad reference, 404 on `file://`, JS parse error before mount), the navigation never finishes, `startHandshake()` is never invoked, the 2-second handshake timeout is never armed, and the user sees a silent black HUD with no banner, no hard-block. ## Evidence - File: `App/AppDelegate.swift:2715-2723` — `BridgeNavigationDelegate` is a 9-line class with one method (`didFinish`) - File: `packages/Bus/Sources/Bus/WebviewBridge.swift:269-288` — `startHandshake()` is the only place that schedules the 2s timeout - File: `packages/Bus/Sources/Bus/Handshake.swift:24-26` — `HandshakeTiming.timeout = .seconds(2)` - No `didFail` / `didFailProvisionalNavigation` handlers anywhere ## Root cause / hypothesis The 2s timeout in `Handshake.swift` advertises a 2s SLA. With no `didFail` handler, the SLA is silently >∞ in the bundle-broken path. The probe `handshakeState == .armed` passes when reality is OK and provides no signal when reality breaks at the navigation layer. ## Recommended fix Add `didFailProvisionalNavigation` + `didFail` to `BridgeNavigationDelegate`. Route both into a new `WebviewBridge.failHandshake(reason:)` that transitions to `.timedOut` (or a new `.loadFailed`) and presents the same hard-block alert. ```swift nonisolated func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { MainActor.assumeIsolated { onDidFail(error) } } nonisolated func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { MainActor.assumeIsolated { onDidFail(error) } } ``` ## Test - Unit: serve a non-existent `index.html` URL → assert `WebviewBridge.handshakeState` transitions to `.loadFailed` (or `.timedOut`) within 100ms and hard-block alert fires - `packages/Bus/Tests/BusTests/` has no `didFail*` references — add coverage ## Source - Audit: `.planning/audit-2026-05-12/bus-hud-audit.md` (2026-05-12) — finding S1 (BridgeNavigationDelegate)
KofTwentyTwo commented 2026-05-13 11:52:09 +00:00 (Migrated from github.com)

Closed by v0.1 cluster merges on 2026-05-13. Develop now at a8c7f1a0.

Closed by v0.1 cluster merges on 2026-05-13. Develop now at `a8c7f1a0`.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: KofTwentyTwo/Jarvis#40