feat(ios): W4 app wiring — DI graph, event fan-out, lifecycle, privacy shade

T-iOS-15: AppEnvironment production DI (real Keychain/probe/transports), EventFanOut
(engine.events → TerminalVM + GateVM + activity bridge), scenePhase lifecycle
(.background→close, suspend→rebuild+reopen reclaims size), privacy shade on != .active,
spike screen deleted, cold-start routing
Walkthrough proxies: hosted live-server smoke over the production DI graph (probe→store→
attach→echo→close) + real-Keychain kSecAttrAccessible assertion (closes T-iOS-7 deferral)
Deviation closed: TerminalViewModel.lastSentDims (valid-only) + alive-engine .active →
notifyForegrounded(dims:) (orchestrator fix on behalf of T-iOS-11 owner)
Env finding: repo under ~/Documents → TCC blocks sim-spawned node; SimServerHarness dual-mode
(self-bootstrap / WEBTERM_SERVER_URL via simctl launchctl setenv)
Verified: 214 pkg + 76 app + 10 integration tests green; verify agent 8/8 PASS
This commit is contained in:
Yaojia Wang
2026-07-05 01:04:42 +02:00
parent 5098643355
commit cc4d3129cc
22 changed files with 1752 additions and 106 deletions

View File

@@ -277,4 +277,20 @@ struct TerminalViewModelTests {
let frames = await harness.transport.sentFramesByConnection[0]
#expect(frames == [MessageCodec.encode(.attach(sessionId: nil, cwd: nil))])
}
@Test("lastSentDims: valid resize is remembered for notifyForegrounded; invalid dims never overwrite a good pair")
func lastSentDimsTracksOnlyValidResizes() async throws {
// Arrange
let harness = try Harness()
#expect(harness.viewModel.lastSentDims == nil)
// Act: a valid layout pass, then a bogus one (cols=0 fails Validation).
harness.viewModel.sendResize(cols: 120, rows: 40)
harness.viewModel.sendResize(cols: 0, rows: 40)
await harness.viewModel.waitUntilForwarded(sendCount: 2)
// Assert: the good pair survives this is what the wiring feeds to
// engine.notifyForegrounded(dims:) on the alive-engine .active hop.
#expect(harness.viewModel.lastSentDims == .init(cols: 120, rows: 40))
}
}