End-to-end verification on a connected iPhone 17 Pro Max via CoreDevice
tunnel exposed three bugs the TS-stubbed and macOS-XCTest layers missed:
1. acceptLocalOnly=true was too tight. Network.framework's "local" gate
only allows ::1 / 127.0.0.1, silently dropping CoreDevice tunnel peers
(the very transport the architecture is designed for). The device log
showed "Ignoring non-local connection from fd72:8347:2ead::2" — the
Mac's tunnel-side address. Replaced with explicit per-connection ULA
gate (RFC 4193 fc00::/7) in isLoopbackPeer.
2. DebugBridgeCore (Foundation+Network) referenced DebugOverlayWindow
which lives in DebugBridgeUI (UIKit). Backwards module dep. Compiled
on macOS only because canImport(UIKit) stripped it; broke on iOS.
Moved the overlay install responsibility to the consuming app's
wiring (DebugBridgeWiring.swift.template already shows the pattern).
3. @Observable macro + @Snapshotable property wrapper conflict. Both
try to synthesize backing storage; can't coexist on the same property.
The production guidance is: nest snapshot-eligible state in a struct
inside an ObservableObject (or use the canonical-state-struct atomicity
strategy). Fixture switched to a plain class to demonstrate.
Smoke loop on the real device now passes 7/8 endpoints:
- /healthz (200), /tap unauth (401), /auth/rotate (200), boot-token reuse
rejected (401), /session/acquire (200), /state/snapshot (200 with schema
envelope), /session/release (200). /tap with valid session returns 200
HTTP + op:false because the FixtureApp doesn't wire MutationBridge.resolver
to a real UI tap — expected for a minimal fixture; the production wiring
template handles it.
Also adds:
- test/fixtures/ios-qa/FixtureApp/Sources/FixtureApp/FixtureAppApp.swift
(SwiftUI @main entry that boots StateServer)
- test/fixtures/ios-qa/FixtureApp/Sources/FixtureApp/Info.plist
- test/fixtures/ios-qa/FixtureApp/project.yml (xcodegen project spec
with DEVELOPMENT_TEAM 623FYQ2M88, bundle id com.gstack.iosqa.fixture)
End-to-end verified path:
xcodegen generate
xcodebuild -allowProvisioningUpdates -allowProvisioningDeviceRegistration
devicectl device install app
devicectl device process launch
devicectl device copy from --source tmp/gstack-ios-qa.token
curl -6 http://[<corodevice-ipv6>]:9999/...
Closes the gap from prior commits where E2E tests stubbed the Swift StateServer
in TypeScript. Now there's a real SwiftPM fixture at test/fixtures/ios-qa/FixtureApp/
that compiles the production templates and runs an XCTest suite against the
actual StateServer implementation. Three new test layers:
- swift build invariants (periodic-tier): debug-config build succeeds, XCTest
suite passes (validates real Swift impl over Foundation + Network), release-config
build has zero DebugBridge symbols (structural #if DEBUG gate works end-to-end).
- Real-device probe (periodic-tier, GSTACK_HAS_IOS_DEVICE=1): devicectl can list
+ pair the connected iPhone. Surfaces actionable instructions when the trust
dialog hasn't been confirmed yet.
- Fixture sources copied from ios-qa/templates/ — Package.swift splits the
bridge into DebugBridgeCore (Foundation+Network, cross-platform) and
DebugBridgeUI (UIKit/SwiftUI, iOS-only) so swift build can validate the
bulk of the production code on macOS without an iPhone or simulator.
Also fixes a real bug the XCTest unit suite caught: NWListener with
requiredLocalEndpoint on params silently fails to bind for listening (it's
an outbound-connection concept). Replaced with .requiredInterfaceType=.loopback
+ .acceptLocalOnly=true + a per-connection peer-address check. The fork's
inherited code had this bug; we shipped it untouched in v1.41.0.0 and the
new XCTest suite caught it immediately.