--- name: scenario description: > Pre-implementation edge-case and scenario explorer for gflow-cli. Decomposes a proposed feature or change across 13 structured dimensions specific to gflow-cli's failure surfaces (WAF/reCAPTCHA, Playwright selectors, auth token lifecycle, batch resume, data layer safety, cross-platform paths). Produces a severity-ranked scenario table to feed into the PLAN spec and the test matrix before EXECUTE begins. --- # `scenario` — Edge Case & Scenario Explorer Systematic pre-implementation scenario analysis. For a given feature or change, produces a severity-ranked table of test scenarios across 13 dimensions tuned to gflow-cli's known failure surfaces. Feed the output into PLAN.md tasks and `tests/features/` BDD scenarios before entering EXECUTE mode. --- ## When to invoke Use before implementing any of: - A new generation path (T2V, I2V, R2V, batch manifest runner) - Transport changes (any new HTTP call against `aisandbox-pa.googleapis.com`) - Auth or session changes (new strategy, cookie extraction, SAPISIDHASH wiring) - Selector cascade changes (`ONBOARDING_SELECTORS`, `NEW_PROJECT_SELECTORS`, `FRAME_SLOTS_STRUCT`, `IMAGE_MODEL_OPTION_SELECTORS`) - Data layer changes (schema migration, new `OperationRecorder` callsite, redaction change) - New CLI subcommand, flag, or exit code Skip for: pure doc changes, CHANGELOG/version bumps, `scripts/` tooling with no production callpath. --- ## Invocation ``` /gflow:scenario ``` `` is a brief summary of what you're about to implement. Examples: - "SAPISIDHASH auth header wired into `_post_json` for `aisandbox-pa` routes" - "gflow video batch manifest ledger (skip already-completed rows)" - "Image model picker converted from English has-text to structural anchor" - "CDP-attach transport as opt-in alongside ui_automation" --- ## The 13 dimensions For each dimension, enumerate scenarios that are **non-obvious** — do not list things that a basic happy-path test already covers. Focus on things that break in production but pass in unit tests. ### D1 — Auth & session lifecycle The SAPISID cookie expires. The user re-runs `gflow auth login` mid-batch. A profile is created but the Flow session was never verified. The session is valid for `labs.google` tRPC but not for `aisandbox-pa`. Two profiles are in use simultaneously (Chromium profile-lock). ### D2 — WAF / reCAPTCHA scoring A profile's WAF heat score is elevated from prior automation runs. reCAPTCHA Enterprise detects `navigator.webdriver=true` despite the `--disable-blink-features` stealth flag. The `grecaptcha.execute()` call times out or returns a challenge that requires human interaction. The same token is submitted twice (single-use token reuse). A batch run fires multiple rapid token mints within one session. ### D3 — Selector cascade drift & Locale Invariance (Flow UI updates) Google Flow ships a UI update that renames or restructures a selector anchor. `FRAME_SLOTS_STRUCT` matches zero elements (the PR #70 regression). The structural-first tier matches but selects the wrong element (e.g., two elements matching `div[type='button'][aria-haspopup='dialog']` after Flow adds a new dialog button). A non-English locale is used (e.g. `GFLOW_CLI_LOCALE=pt-BR` or a Brazilian Google account) and single-language English text selectors fail because they lack Tier 1 structural anchors (`a[href*='changelog']`, `button:has(i:text('close'))`) or Tier 2 multi-locale text cascades. A new locale is used whose CMP dialog is not in `_ONBOARDING_TEXT_SELECTORS`. The `--lang=en-US` Chromium arg is removed before `IMAGE_MODEL_OPTION_SELECTORS` is converted to structural anchors. ### D4 — Batch manifest & resume A 50-row TSV manifest dies at row 23 (auth expiry). On resume, rows 1–22 are re-submitted and double-billed. A row has an empty `start_image` field (T2V) while the column parser expected a path. The output path already exists on disk from a prior run (overwrite vs skip decision). Two `gflow video batch` invocations against the same profile run concurrently (Chromium profile-lock). ### D5 — Concurrency & Page pool `GFLOW_CLI_CONCURRENCY=16` fans out 16 simultaneous `page.evaluate()` reCAPTCHA token mints — do they share site key state safely? A Page is returned to the pool while still in a modal dialog (state contamination for the next checkout). A `QueueFull` double-checkin race where two coroutines both attempt to return the same Page object. `asyncio.gather` propagates one failure but leaves the other coroutines in a half-completed state with no cleanup path. ### D6 — Data layer (SQLite / DataStore) `DataStore` migration runs on a schema that's one version ahead (newer-schema detection should raise `DataStoreError` exit 16, not silently corrupt). An `OperationRecorder.on_started` callback raises inside a generation loop — does the batch continue or crash? `GFLOW_CLI_HISTORY_PROMPTS=redacted` is set — does the new callsite respect the redaction gate? A Windows path with a drive letter is stored in the DB and retrieved on Linux (or vice versa). The DB is on a network filesystem and the write locks time out. ### D7 — Error propagation & exit codes A new exception class is added but not registered in `EXIT_CODE_MAP`. A `FlowApiError` subclass is raised inside a retry loop — does `reraise=True` propagate the right typed exception or does tenacity eat it? `WireFormatError` discovery payload logs the route body prefix — does it redact reCAPTCHA tokens and auth headers? An unhandled exception escapes `run_with_handlers` — is it SHA-256-hashed before logging (never raw message)? ### D8 — Cross-platform paths A Windows user has a Unicode character in their `%LOCALAPPDATA%` path. The output directory path contains spaces. `platformdirs` returns a different base path on macOS vs Linux vs Windows — does the feature hardcode any of these? `PYTHONUTF8=1` is not set — are there `cp1252` encoding traps on Windows for prompt text or file names? ### D9 — Transport edge cases The HTTP response body is valid JSON but has an unexpected top-level key (should emit `WireFormatError` with discovery payload, not crash). A video generation response omits `operations[0].operation.name` (the `omni-flash` NULL `flow_operation_id` issue). A status-poll response arrives before the listener is attached (`_attach_status_response_listener` race). A video download URL has a `googleapis.com` host not in the SSRF allowlist. ### D10 — Headless vs headed environment The feature is run in a CI environment with no display server (Linux, no Xvfb). The Playwright context is launched headed (user has `GFLOW_CLI_HEADLESS=false`) and the window is closed manually mid-batch. `gflow auth login --browser internal` is used on a machine where Playwright bundled Chromium is flagged by Google's G12 block. ### D11 — Input validation & boundary values A prompt string is 4001 characters (Flow's apparent limit). A manifest TSV has zero rows (after the header). `--seed` receives a negative integer or a string. `-n 5` is passed to `gflow image t2i` (Flow's UI cap is `x4`). An aspect ratio of `3:2` is passed (valid-looking but not in the whitelist). A `--profile` name contains path-separator characters. ### D12 — Observability & structured log contract A new `structlog` event is emitted — is its key name stable and documented in `docs/ARCHITECTURE.md`? A new `error_raised` path: does it carry the full RFC 9457 Problem Details shape (`type`, `title`, `status`, `detail`, `instance`, `remediation_hint`)? `correlation_id` is bound at the process boundary — does it propagate into the new code path or is it missing from the event? ### D13 — MCP surface parity gflow ships the same capability twice: as a CLI command and as an MCP tool. Every edge case above therefore has an MCP twin — enumerate it, because the paths are not the same code. The MCP tool can run **direct** or **queued through `worker/codec.py`**, so a param that works via the CLI can be accepted and silently dropped on the queued path. Ask: what does this feature look like invoked as an MCP tool? Which errors reach the agent as a Problem Details envelope rather than an exit code? Does the tool's docstring still describe the behaviour truthfully? The six mirror axes are enumerated once, in `skills/check/SKILL.md` step 1b — do not restate them here, scenario's job is only to name the MCP cases that need scenarios. --- ## Artifact location Write the analysis to **`docs/superpowers/plans/-/SCENARIO.md`** (same directory the subsequent `/gflow:plan` writes its `PLAN.md` into) and commit it with the plan on the feature branch. Established by the image-upscale (#171) and locale-selectors (#170) cycles — do not leave the analysis only in conversation. ## Output format ``` # Scenario: ## Coverage map ## Scenario table | # | Dimension | Scenario | Severity | Expected behaviour | Test category | |---|---|---|---|---|---| | 1 | D2 WAF/reCAPTCHA | WAF score elevated on profile from prior run | Critical | WafRejectionError raised with remediation hint "let score decay or use a different profile"; exit code 403-mapped | E2E live (`@pytest.mark.live`) | | … | … | … | … | … | … | Severity: **Critical** (data loss / billed twice / unrecoverable) · **High** (feature broken, workaround exists) · **Medium** (degraded UX, explicit error) · **Low** (cosmetic or edge-only) Test category: **Unit** (no I/O) · **Integration** (mocked HTTP/Playwright) · **BDD** (Gherkin feature file) · **E2E smoke** (`@pytest.mark.smoke`) · **E2E live** (`@pytest.mark.live`, opt-in) ## Must-cover before merge (Critical + High) 1. … ## Deferred (Medium + Low — log as issues, not blockers) 1. … ## Suggested BDD scenarios ```gherkin @e2e @e2e_auth Feature: Scenario: Given … When … Then … ``` **Tag by surface — the tag is the binding, not decoration.** pytest-bdd turns each Gherkin tag into a pytest marker, and `addopts`' `-m 'not e2e …'` filters on exactly that. So the tag decides where the scenario runs and who must run it: | The scenario can only happen… | Feature-level tags | Bound from | |---|---|---| | in a real browser / against real Flow | `@e2e` + one cost tier (`@e2e_auth`, `@e2e_image`, `@e2e_video`, …) | `tests/e2e/test__bdd.py` | | in our own code (parsing, routing, exit codes, redaction) | none | `tests/features/test__steps.py` | One feature file is bound by exactly **one** module — bound twice, its scenarios run twice. `tests/features/test_e2e_binding_guard.py` enforces this offline in four directions (orphan, missing tier, untagged live binding, double binding), so an `@e2e` scenario nobody wrote a test for fails normal CI. Mechanics: [`docs/E2E_TESTING.md`](../../docs/E2E_TESTING.md) § BDD-bound e2e. ## Known-issues cross-reference ``` --- ## Integration & Pipeline Continuation (Next Step Handoff) 1. Run `/gflow:predict` first to validate the approach (GO/CAUTION/STOP). 2. Run `/gflow:scenario` to enumerate edge cases and build the test matrix. 3. Use the "Must-cover before merge" list as the acceptance criteria for the PLAN.md task. 4. Add BDD scenarios **before** coding (TDD is non-negotiable per AGENTS.md), each tagged by its surface per the table above — a browser-only scenario is bound from `tests/e2e/`, and a mocked stand-in does not discharge it ([`skills/issue-resolve`](../issue-resolve/SKILL.md) § The Bug Lane, step 5). 5. **Next step:** Proactively announce: **"BDD Scenarios generated. Next step: Phase 4 Implementation Plan (`/gflow:plan `)."** --- ## Provenance Adapted from `vc-scenario` in [vibecode-pro-max-kit](https://github.com/withkynam/vibecode-pro-max-kit) (assessment 2026-05-28). 13 dimensions re-scoped to gflow-cli's specific failure surfaces: Google WAF/reCAPTCHA, Playwright selector drift, auth token lifecycle, batch resume idempotency, SQLite data layer, RFC 9457 error propagation, cross-platform Windows/macOS/Linux paths.