refactor(ios): decide bridge-failure scope and warning once
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 onba953a8066^(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, zerocircuit-disabledin the session log.
CI obligations: standard lanes. Unresolved risks: none known.
🤖 Generated with Claude Code