refactor(ios): decide bridge-failure scope and warning once

Merged
TH
5d ago
+174-72

Summary

One classifier, bridgeFailureDisposition, now decides both whether a bridge failure opens the generation's circuit and which fallback-warning sentence the capture carries. Previously two owners classified the same failure — opensGenerationCircuit for scope, string comparisons against duplicated constants for wording — and they could drift apart (#3874 and #3895 each had to teach both owners the same lesson). Behavior is unchanged for every reachable failure; unknown guest codes keep the conservative generation default.

Touched files: 3. No scope expansion. Production lines: snapshot-route.ts 622 → 599, new policy module 54 — net +31 against the plan's aspirational 30–70-line reduction; the classification data moved out of the route without shrinking, in exchange for one owner of the decision. Gross diff 246 lines including tests.

Validation

Tested SHA: 0a58d5bdab. pnpm typecheck, pnpm lint, pnpm format pass; snapshot-route.test.ts unchanged, all route/policy/observability cases pass. check:affected --run --head receipt recorded per head in comments.

  • Red/green: the capture-scoped second-capture cases FAIL on 989804f11f^ (pre-#3874), the window case FAILs on ba953a8066^ (pre-#3895), all pass at head.
  • Mutation: forcing the foreground consequence to generation scope fails exactly the route's second-capture assertions and policy tests.
  • Live, owned sims: iPhone — alert refusal gave the capture-scoped warning with XCTest serving the alert; after dismissal the same generation captured bridge-served with warnings: null. iPad Stage Manager windowed — the refusal warning repeated identically across 6+ captures, zero circuit-disabled in the session log.

CI obligations: standard lanes. Unresolved risks: none known.

🤖 Generated with Claude Code