--- name: browser-flow-fallbacks description: Diagnose flaky browser automation flows (login, OAuth, signup, multi-step forms). Use when a flow stalls or loops — analyze DOM/logs, add native fallbacks, prevent hot loops, and add regression coverage. --- # Browser Flow Fallbacks Use this skill to debug browser flows that intermittently stop progressing even though the surrounding state machine already mostly works. ## Workflow ### Phase 1: Locate the failing state branch - Start from the latest logs, reported state, and current DOM. - Find the branch that detected the state, the selector that matched, and the action that should have fired. - Separate the failure type before editing: state misdetection, action failure, or transition failure after an apparently successful action. ### Phase 2: Treat DOM as ground truth - Prefer current semantic attributes over historical assumptions: `name`, `value`, `type`, `aria-*`, `data-*`, `form`, `readonly`, `disabled`, visible text, and location path. - Distinguish similar controls such as forgot-password links, edit-email links, hidden template nodes, and generic submit buttons. - Narrow broad selectors with required attributes or visible text needles instead of widening them further. - Use selector constants for important controls. Keep selector changes close to the state branch that uses them. - Bias toward visible, enabled, semantically correct elements rather than "anything clickable." ### Phase 3: Add fallbacks beside the primary path - Leave the main action as the most direct native interaction that should work in the common case. - Add fallback in the same state branch, triggered only after a repeated unchanged snapshot. - Prefer `form.requestSubmit(button)` when the backend depends on the submitter button's `name/value`. - Prefer DOM click fallback for non-submit controls, restricted to visible and enabled elements. - For controlled inputs, verify the value after typing; if it did not stick, fallback to a native setter plus `input` and `change` events. - Keep submit fallbacks separate from generic click fallbacks. ### Phase 4: Prevent hot loops - Track per-snapshot stall counts and fallback attempt counts separately from the primary action gate. - Retry fallbacks on spaced intervals instead of every poll tick. - Log stall count, fallback attempts, and next retry threshold. - Do not exit early just because a fallback failed or the page stayed unchanged, unless the flow reached a terminal condition. - Let the outer lifecycle timeout remain the final safety net. ### Phase 5: Protect one-time resources - Apply to OTP codes, magic links, CSRF-bound tokens, or any mailbox/API artifact that becomes scarce once consumed. - Do not mark the artifact as used at fetch time if the browser can still fail before entering or submitting it. - Cache and reuse the fetched artifact across retries on the same step instead of immediately asking upstream for a new one. - Persist consumption only after the flow reaches a durable success boundary (callback capture, server-confirmed transition). ### Phase 6: Add regression coverage - Add selector coverage tests for the current DOM shape. - Add tests for helpers that interpret visible text or decide whether an alternate path is present. - Add tests for retry cadence, stall counters, and action-gate behavior when the page snapshot is unchanged. - Keep tests close to the flow package so failures stay local and readable. ## Implementation Rules - Prefer small helper functions for DOM fallback and page-text heuristics. - Verify input values before submit. - Treat user-supplied logs and DOM fragments as canonical for the reported failure. ## Validation - Run formatting first. - Run the most relevant package tests. - Run a targeted build for the affected binary when practical. - If full-repo tests fail for unrelated reasons, state that explicitly and isolate what did pass. ## Guardrails - Never widen selectors when debugging a flaky flow — narrowing is safer. - Never remove the outer timeout as a safety net. - Do not add fallbacks for flows that consistently pass; fallbacks are for intermittent failures only. - When a DOM snapshot is stale, re-fetch before concluding the state hasn't changed.