feat(ios): W1 leaf packages — ReconnectMachine/PingScheduler, GateState/AwayDigest, HostRegistry, APIClient+pairing probe
T-iOS-5: pure reconnect reducer (1s→30s cap, mirrors terminal-session.ts) + 25s PingScheduler, 16 tests
T-iOS-6: gate epoch tracker (rising-edge semantics, canDecide guard) + AwayDigest reducer, 25 tests
T-iOS-7: HostRegistry with SecItemShim keychain seam, 30 tests, 88.1% own-src coverage
T-iOS-8: APIClient (Origin iff-G invariant) + two-step pairing probe, 45 tests, 98.1% coverage
Contract ruling: probe returns Result<HostEndpoint,_> (Host{id,name} built by pairing VM) —
resolves frozen-contract contradiction reported via BLOCKED protocol; adds Tunables.pairingProbeTimeout(10s)
Verified: 178 tests green across 5 packages; coverage gates pass; zero Owns violations
This commit is contained in:
@@ -28,6 +28,10 @@ public enum Tunables {
|
||||
/// host/attacker-controlled input).
|
||||
public static let titleMaxLength = 256
|
||||
|
||||
/// Overall pairing-probe deadline (§3.4 contract ruling, 2026-07-04): if
|
||||
/// either probe step hangs past this, the probe resolves `.timeout`.
|
||||
public static let pairingProbeTimeout: Duration = .seconds(10)
|
||||
|
||||
/// `URLSessionWebSocketTask.maximumMessageSize`. The default (1 MiB) is too
|
||||
/// small: ring-buffer replay arrives as ONE full-snapshot frame
|
||||
/// (src/session/session.ts:165-171) and JSON escaping inflates control
|
||||
@@ -37,7 +41,8 @@ public enum Tunables {
|
||||
/// COUPLING WARNING: SCROLLBACK_BYTES is a server env knob the client
|
||||
/// cannot discover at runtime (no config handshake). If the host raises it
|
||||
/// past ~2.7 MB (or replay is extremely escape-dense), `receive()` fails
|
||||
/// with NSPOSIXErrorDomain code 40 (ENOBUFS "Message too long") — that MUST
|
||||
/// with NSPOSIXErrorDomain code 40 (EMSGSIZE "Message too long"; 55
|
||||
/// ENOBUFS as fallback classification — T-iOS-2 spike measured) — that MUST
|
||||
/// be classified as the non-retryable `.replayTooLarge` failure and never
|
||||
/// fed into the backoff reconnect loop (plan §3.2 / T-iOS-9/10).
|
||||
public static let maxWSMessageBytes = 16 * 1024 * 1024
|
||||
|
||||
@@ -13,6 +13,7 @@ func tunablesMatchFrozenValueTable() {
|
||||
#expect(Tunables.telemetryStaleTtlMs == 30_000) // public/tabs.ts:45 STATUSLINE_TTL_MS
|
||||
#expect(Tunables.digestFadeDelay == .seconds(8))
|
||||
#expect(Tunables.titleMaxLength == 256)
|
||||
#expect(Tunables.pairingProbeTimeout == .seconds(10)) // §3.4 契约裁定 2026-07-04
|
||||
#expect(Tunables.maxWSMessageBytes == 16 * 1024 * 1024)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user