Repository navigation
fix(migrated): report a refusal flow.google.com states on the wire (#906, #873) - #909
Merged
Merged
Conversation
, #873) flow.google.com answers a refused submit with HTTP 200 and a batchexecute error envelope: a null payload, the gRPC status in slot 5 and Google's ErrorInfo reason inside it. parse_frames kept only frames with a string payload, so every caller saw an empty reply: the video path waited out a retryable 60 s TransportTimeoutError and the image path raised WireFormatError("no ogiZ0b frame"), while Flow's grid showed "We noticed some unusual activity". batchexecute.rpc_errors now returns the envelopes parse_frames skips, and both submit observers map a REASON to a class: PUBLIC_ERROR_UNUSUAL_ACTIVITY to WafRejectionError (the labs path's class for the same reason, retryable flag preserved) and the content-safety reasons to ContentPolicyError. A bare status with no reason is not a refusal - the #723 queued entity submit replies [5] - so it keeps today's behaviour. Measured, not inferred: a submit with its reCAPTCHA token corrupted in flight is refused with that envelope; an aborted submit gets no reply but renders the identical <flow-error-tile>, which is why the grid card cannot be the signal. New e2e (opt-in GFLOW_CLI_E2E_RUN_REFUSAL=1) provokes a real refusal and passed on a probe profile. Diagnosed by @stgmt in #873 and #906. Supersedes #873 and the refusal-card half of #907.
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (21)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ffroliva
added a commit
that referenced
this pull request
Sep 27, 2026
#909 measured that the refusal is on the wire and rewrites this note in full, including the stealth finding added here. Dropping the append avoids a merge conflict between the two PRs.
- log migrated.submit_refused with rpc, code and reasons before raising - state that retryability is the class default, as on labs (no override) - feed the image scenario the 2026-09-27 capture byte for byte - docs: E2E_TESTING opt-in row, DEBUGGING exit-10 row aligned with ACCOUNT_SAFETY, DATA_LAYER waf-rejection wording, KNOWN_ISSUES date - memory: content-policy note points at the wire on flow.google.com; refusal note no longer links a spike that only exists on #908 - CHANGELOG: say the video refusal is covered offline, not observed live
ffroliva
marked this pull request as ready for review
September 27, 2026 20:53
stgmt
added a commit
to stgmt/gflow-cli
that referenced
this pull request
Sep 28, 2026
…com WAF fingerprinting On the migrated host (flow.google.com), patchright's bundled browser carries automation fingerprints that Flow's WAF keys on, so automated submits get PUBLIC_ERROR_UNUSUAL_ACTIVITY even with valid cookies. - GFLOW_CLI_CDP_LAUNCH=1: spawn the installed chrome.exe with a free --remote-debugging-port and attach patchright over CDP instead of launch_persistent_context. Requires GFLOW_CLI_BROWSER_ENGINE=patchright; on any other engine it raises ConfigurationError rather than silently starting the wrong browser. - api/client.py: route _launch_persistent_context through cdp_launch when the env flag is set; drop --disable-blink-features=AutomationControlled from the stock args (it is itself a fingerprint). - transports/ui_automation.py + experimental/evaluate_fetch.py: same flag removal plus ignore_default_args for --enable-automation/--no-sandbox. - migrated_composer.py: replace filter(has=mat-icon-ligature) with XPath descendant selectors — has= runs a page-side evaluate that resolves to zero elements under patchright. Add a scoped-radios retry loop to _open_pane for Angular remounts, model the blocking-overlays XPath, and add GFLOW_HUMAN/GFLOW_HUMAN_TYPE humanized prompt typing plus humanized video/image submit clicks. - Keep upstream ffroliva#909's WafRejectionError wire-read refusal detection; the refusal-card scrape half of this branch is superseded and removed. - tests: extend the FakePage/FakeLocator test double for the new XPath selectors, wait_for_function, and mouse so the migrated suite still exercises the real driver paths. Signed-off-by: stgmt <stgmt@users.noreply.github.com>
stgmt
added a commit
to stgmt/gflow-cli
that referenced
this pull request
Sep 28, 2026
…com WAF fingerprinting On the migrated host (flow.google.com), patchright's bundled browser carries automation fingerprints that Flow's WAF keys on, so automated submits get PUBLIC_ERROR_UNUSUAL_ACTIVITY even with valid cookies. - GFLOW_CLI_CDP_LAUNCH=1: spawn the installed chrome.exe with a free --remote-debugging-port and attach patchright over CDP instead of launch_persistent_context. Requires GFLOW_CLI_BROWSER_ENGINE=patchright; on any other engine it raises ConfigurationError rather than silently starting the wrong browser. - api/client.py: route _launch_persistent_context through cdp_launch when the env flag is set; drop --disable-blink-features=AutomationControlled from the stock args (it is itself a fingerprint). - transports/ui_automation.py + experimental/evaluate_fetch.py: same flag removal plus ignore_default_args for --enable-automation/--no-sandbox. - migrated_composer.py: replace filter(has=mat-icon-ligature) with XPath descendant selectors — has= runs a page-side evaluate that resolves to zero elements under patchright. Add a scoped-radios retry loop to _open_pane for Angular remounts, model the blocking-overlays XPath, and add GFLOW_HUMAN/GFLOW_HUMAN_TYPE humanized prompt typing plus humanized video/image submit clicks. - Keep upstream ffroliva#909's WafRejectionError wire-read refusal detection; the refusal-card scrape half of this branch is superseded and removed. - tests: extend the FakePage/FakeLocator test double for the new XPath selectors, wait_for_function, and mouse so the migrated suite still exercises the real driver paths. Signed-off-by: stgmt <stgmt@users.noreply.github.com>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
flow.google.com answers a refused submit with HTTP 200 and a batchexecute error envelope carrying Google's reason:
batchexecute.parse_frameskept only frames with a string payload, so this frame was dropped. That is the root cause behind what #873, #906 and #907 reported: the video path waited out a retryable 60 sTransportTimeoutError(exit 9), and the image path raisedWireFormatError("no ogiZ0b frame")(exit 7), while Flow's grid showed "We noticed some unusual activity".batchexecute.rpc_errors()returns the envelopesparse_framesskips, via a shared row iterator.parse_frames' return type is unchanged for its five callers.migrated_composer._submit_refusal()runs in both submit observers and maps a reason to a class:PUBLIC_ERROR_UNUSUAL_ACTIVITY→WafRejectionError(exit 10). This is the labs path's class for the same reason, so its retryable flag is preserved rather than re-decided. The remediation says the prompt is not the cause and that nothing was charged.CONTENT_SAFETY_REASONS→ContentPolicyError(exit 5), the same reason set the REST path uses.[5], and that must not become a refusal.Why the wire and not the grid card. A submit aborted in the browser renders the identical
<flow-error-tile>: same ligatures, same structure, different copy. Only the reply tells a refusal from an abort. This was the blocker on #873 and #907.Supersedes #873 and the refusal-card half of #907. Diagnosis by @stgmt, credited in the CHANGELOG.
Refs #906, #873, #907
Evidence
docs/superpowers/spikes/2026-09-27-migrated-refusal-is-on-the-wire.md, readings pre-registered. A submit with its reCAPTCHA token corrupted in flight was refused with the envelope above; an aborted submit gaverequestfailedand no reply.migrated-refusal-is-a-dom-card-not-a-wire-recordcorrected in place (the slug is cited by the council table).Verification status
tests/features/migrated_refusal_envelope.feature, 4 scenarios. Red first: 3 failed for the right reason (the exactWireFormatErrorand timeout seen live). The status-5 guard passed before and after.tests/api/transports/test_batchexecute.pytests/e2e/test_migrated_refusal_live_bdd.py(-m e2e_image, opt-inGFLOW_CLI_E2E_RUN_REFUSAL=1) passed on a probe profile in 14 s. Flow refused, and gflow raisedWafRejectionErrornaming the reason.tests/apiunder-n 8: 1816 passed (develop: 1812 + 4 new)YhhmEf/eb1hJfreply (see Migrated host: Flow refuses Playwright submits with 'unusual activity' card — manual Chrome on the same profile works #906). The video observer shares the framing and is covered offline, and a mutation check showed its call site is load-bearing.MCP / surface parity
No CLI option changed. The typed error comes from the shared migrated transport, so CLI, MCP and worker all receive the same
WafRejectionError/ContentPolicyErrorenvelope.docs/ACCOUNT_SAFETY.mdalready told users to expectWafRejectionError+PUBLIC_ERROR_UNUSUAL_ACTIVITY; that is now true on flow.google.com too.