Skip to content

fix(facebook/feed): exclude Messenger bleed and scope extraction to news feed - #1

Merged
lawmight merged 3 commits into
lawmight/fb-chrome-stackfrom
cursor/facebook-feed-messenger-bleed-469a
Sep 5, 2026
Merged

lawmight merged 3 commits into
lawmight/fb-chrome-stackfrom
cursor/facebook-feed-messenger-bleed-469a

Conversation

@lawmight

@lawmight lawmight commented Sep 5, 2026 •

Copy link
Copy Markdown
Owner

Description

Live smoke of opencli facebook feed --limit 5 on a locked Chrome profile was returning exit 0 with five rows of embedded Messenger/thread UI (empty author, - metrics, "Message sent February 26, 2026" / Teiki Travels bleed).

FB desk isolation update: browser inspection showed only a New Tab open — no Messenger/chat tabs. Bleed is DOM-side: embedded chat chrome on facebook.com home, not wrong-tab selection.

Type of Change

  • 🐛 Bug fix

What changed

Home surface gating (primary fix)

  • On facebook.com home, extraction is strictly scoped to [role=feed"] — never falls back to role=main or document.body (where embedded chat columns live)
  • ensureNewsFeedSurface() navigates with cache-busted home URL and polls until [role=feed"] exists
  • Scroll/lazy-load only runs inside [role=feed"]
  • Fail with no_feed when home loads without the feed landmark

Embedded chat exclusion

  • Reject containers with thread copy (Message sent …, Enter, Send without Share)
  • On home, require author OR post permalink OR "Actions for this post" menu — chat bubbles have none of these
  • Broader embedded-chat selectors (data-pagelet, data-testid, aria-label*=Conversation, etc.)
  • Drop authorless metric-less bleed rows; error when only bleed remains

Explicitly ruled out

  • Leftover Messenger tabs are not the fix target (tab selection de-emphasized)

Tests

  • Fixture updated: embedded chat in role=main without aria-label="Messenger"
  • New cases: home no_feed without feed landmark, chat column beside role=feed, cache-busted navigation
  • 32/32 feed tests pass

Checklist

  • I ran the checks relevant to this PR (npm test -- clis/facebook/feed.test.js)
  • I updated tests or docs if needed
  • Live smoke on tom.coustols recommended after merge

Expected live behavior

Scenario After merge
New tab → facebook.com home with feed column Real posts with authors
Home with embedded chat column, no [role=feed"] yet Clear no_feed error (non-zero)
Chat bleed rows only CommandExecutionError (non-zero), not exit 0 junk
Open in Web Open in Cursor 

Summary by Sourcery

Keep Facebook feed extraction isolated to the home news-feed surface and reject Messenger bleed instead of returning invalid rows.

Bug Fixes:

  • Prevent Facebook feed extraction from returning embedded Messenger or chat UI as news-feed posts.
  • Fail with clear command errors when the home news-feed landmark is unavailable or extraction lands on a Messenger surface.

Enhancements:

  • Scope Facebook home extraction and scrolling to the news-feed landmark while filtering chat content and preserving valid feed rows.

Tests:

  • Add coverage and fixtures for embedded chat bleed, missing feed surfaces, Messenger routes, cache-busted navigation, and mixed valid and invalid rows.

Summary by cubic

Fixes facebook feed returning Messenger/thread UI rows (empty authors, - metrics) instead of news-feed posts when Messenger is open. Extraction now runs on the home news-feed surface only, filters chat DOM, and fails with guidance when only Messenger bleed remains.

  • Selects a non-/messages Facebook tab and navigates with a cache-busted home URL to bypass extension fast-path skips.
  • Scopes container discovery to [role="feed"], falling back to [role="main"] only off the home surface; home extraction never scrapes role=main/body.
  • Detects and skips Messenger/Chats regions using aria labels, pagelets, /messages/t/ links, and thread copy.
  • Redirects /messages URLs and closes an open Messenger drawer before scrolling.
  • Throws a typed error when all rows are authorless with - metrics instead of returning junk.

Written for commit 4f1d7b0. Summary will update on new commits.

Review in cubic

When Messenger drawer or chat UI is open in the locked Chrome profile,
feed extraction was matching role=article nodes and thread chrome instead
of top-level news-feed posts. Symptoms: empty authors, '-' metrics, and
content like 'Message sent …' / 'Enter'.

- Scope container discovery to [role=feed] (fallback: role=main)
- Detect and skip Messenger/chat regions and thread copy
- Prepare step redirects /messages URLs and closes Messenger drawer
- Reject authorless rows that still look like Messenger bleed
- Add fixture + tests for the reported failure mode

Co-authored-by: Tom Coustols <tom.coustols@tcdynamics.fr>
@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Sep 5, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

The PR fixes Facebook feed extraction returning embedded Messenger UI by validating and navigating to the home news-feed surface, restricting scrolling and DOM discovery to [role="feed"], filtering chat-shaped rows and containers, and failing with explicit errors when no genuine feed is available.

Sequence diagram for scoped Facebook feed extraction

sequenceDiagram
    participant CLI as facebook_feed_command
    participant Page as BrowserPage
    participant Facebook as FacebookHome
    participant Feed as NewsFeedSurface

    CLI->>Page: goto(feedNavigationUrl())
    Page->>Facebook: Load cache-busted home URL
    CLI->>Page: evaluate(buildPrepareFeedScript())
    Page->>Facebook: Redirect messages route or dismiss chat chrome
    CLI->>Page: evaluate(buildSurfaceCheckScript())
    Page-->>CLI: ready, feedFound, messengerDom
    alt feed surface unavailable
        CLI-->>CLI: throw CommandExecutionError
    else feed surface ready
        CLI->>Page: evaluate(loadFeedPosts)
        Page->>Feed: Scroll feed element only
        CLI->>Page: evaluate(buildFeedExtractScript(limit))
        Page->>Feed: Discover and filter articles
        Feed-->>CLI: Genuine feed rows
        CLI-->>CLI: Remove bleed rows or throw CommandExecutionError
    end
Loading

Flow diagram for Facebook feed surface validation

flowchart TD
    A[facebook feed command] --> B[Cache-busted facebook.com home navigation]
    B --> C{Messages route?}
    C -->|Yes| D[Throw wrong_surface error]
    C -->|No| E{role=feed exists?}
    E -->|No| F[Throw no_feed error]
    E -->|Yes| G[Scope scrolling and extraction to role=feed]
    G --> H{Rows contain genuine post evidence?}
    H -->|No, chat bleed only| I[Throw Messenger/chat bleed error]
    H -->|Yes| J[Return filtered news-feed posts]
Loading

File-Level Changes

Change Details Files
Constrains Facebook feed acquisition and extraction to a validated news-feed surface.
  • Adds cache-busted home navigation with repeated surface polling.
  • Requires a [role="feed"] landmark on the home surface and returns typed errors for missing feeds or Messenger routes.
  • Scopes lazy loading, article discovery, action anchors, and fallback extraction to the feed root.
  • Retains non-home fallback behavior while preventing home extraction from scraping role=main or document.body.
clis/facebook/feed.js
Filters embedded Messenger and chat DOM content without discarding legitimate feed posts.
  • Detects chat regions through accessibility labels, pagelet/test IDs, message links, thread copy, and send-only controls.
  • Requires author, post permalink, or post-action evidence for home rows.
  • Drops authorless metric-less bleed rows and raises an execution error when only such rows remain.
clis/facebook/feed.js
Expands regression coverage for feed-surface validation and Messenger bleed scenarios.
  • Adds a fixture containing chat content beside a genuine role=feed.
  • Tests missing-feed, mixed-content, Messenger-route, cache-busted navigation, and typed-error behavior.
  • Updates page and extraction helpers to model surface preparation and navigation.
clis/facebook/__fixtures__/feed-messenger-bleed.html
clis/facebook/feed.test.js

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

cursoragent and others added 2 commits September 5, 2026 19:10
Live smoke still returned Messenger thread rows because evaluation could run
against the active Messenger tab or home DOM without a feed landmark.

- Select a non-/messages facebook tab when available
- Navigate with a cache-busting home URL to avoid extension fast-path skips
- Poll until [role=feed] is present; fail on /messages routes
- Refuse extraction when Messenger chrome is visible without a feed landmark
- Drop authorless metric-less rows and error when only bleed remains

Co-authored-by: Tom Coustols <tom.coustols@tcdynamics.fr>
FB desk confirmed bleed happens with only a New Tab open — embedded chat
chrome on facebook.com home, not a leftover Messenger tab.

- Require [role=feed] on facebook.com home; never scrape role=main/body there
- Reject home rows without author/post-menu evidence (chat bubbles lack both)
- Detect embedded chat via Send-without-Share, thread copy, broader selectors
- Scroll only inside role=feed; fail when home feed landmark is missing
- De-emphasize tab selection; focus navigation + DOM surface gating

Co-authored-by: Tom Coustols <tom.coustols@tcdynamics.fr>
@lawmight
lawmight marked this pull request as ready for review September 5, 2026 19:12
@lawmight
lawmight merged commit ac9cd60 into lawmight/fb-chrome-stack Sep 5, 2026
3 checks passed

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="clis/facebook/feed.js" line_range="652" />
<code_context>
-    await page.goto(FACEBOOK_HOME, { settleMs: 4000 });
-  } catch (err) {
+
+  const surface = await ensureNewsFeedSurface(page);
+  if (!surface?.ready) {
+    if (surface?.isMessagesRoute) {
</code_context>
<issue_to_address>
**issue (broader_impact):** A logged-out Facebook session is rejected as a generic `CommandExecutionError` before `buildFeedExtractScript()` can classify the login page as `auth`, so callers no longer receive the existing `AuthRequiredError` contract.

**Triggers:** When navigation lands on `/login` or `/checkpoint`, which has no `[role="feed"]` landmark.

**Suggested fix:** Evaluate the authentication surface before requiring the feed landmark, or have `ensureNewsFeedSurface()` return an explicit auth status for login/checkpoint pages.
</issue_to_address>

### Comment 2
<location path="clis/facebook/feed.js" line_range="123-128" />
<code_context>
+      // Preparation is best-effort; surface checks own final classification.
+    }
+
+    lastSurface = await readFeedSurface(page);
+    if (lastSurface?.ready) return lastSurface;
+
</code_context>
<issue_to_address>
**issue (bug_risk):** A rejected `page.evaluate()` from `readFeedSurface()` escapes `ensureNewsFeedSurface()` without being converted to `CommandExecutionError`, producing an untyped browser/transport failure instead of the command's documented navigation error.

**Triggers:** When the browser disconnects, the page closes, or surface evaluation fails after navigation succeeds.

**Suggested fix:** Wrap `readFeedSurface(page)` and the polling wait in the same error-to-`CommandExecutionError` handling used for `page.goto()`.

```suggestion
    try {
      lastSurface = await readFeedSurface(page);
      if (lastSurface?.ready) return lastSurface;

      if (typeof page.wait === 'function') {
        await page.wait(1);
      }
    } catch (err) {
      throw new CommandExecutionError(
        `Failed to navigate to facebook feed: ${err instanceof Error ? err.message : err}`,
        'Check that facebook.com is reachable and the browser extension is connected.',
      );
    }
```
</issue_to_address>

### Comment 3
<location path="clis/facebook/feed.js" line_range="327" />
<code_context>
+      const hasSendOnly = Array.from(root.querySelectorAll('[aria-label]'))
+        .some((el) => /^(Send|发送)$/i.test(labelOf(el))) && !hasShare;
+      const fullText = textOf(root);
+      if (isMessengerOrChatText(fullText)) return true;
+      if (hasSendOnly && !hasPostMenu) return true;
+      const blocks = visibleBlocks(root);
</code_context>
<issue_to_address>
**issue (bug_risk):** Any genuine post whose text contains `Message sent` followed by a date is classified as Messenger content because the check runs against the entire container text, so the valid post is discarded before extraction.

**Triggers:** When a real post quotes or discusses a message containing text such as `Message sent March 1, 2026`.

**Suggested fix:** Apply thread-copy checks to dedicated chat controls or require multiple Messenger signals instead of rejecting the whole container on one phrase in its content.

```suggestion

```
</issue_to_address>

Sourcery assessment

Approval pending. 3 findings to address first.

Blocking findings: clis/facebook/feed.js:652, clis/facebook/feed.js:128, clis/facebook/feed.js:327


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread clis/facebook/feed.js
await page.goto(FACEBOOK_HOME, { settleMs: 4000 });
} catch (err) {

const surface = await ensureNewsFeedSurface(page);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (broader_impact): A logged-out Facebook session is rejected as a generic CommandExecutionError before buildFeedExtractScript() can classify the login page as auth, so callers no longer receive the existing AuthRequiredError contract.

Triggers: When navigation lands on /login or /checkpoint, which has no [role="feed"] landmark.

Suggested fix: Evaluate the authentication surface before requiring the feed landmark, or have ensureNewsFeedSurface() return an explicit auth status for login/checkpoint pages.

Comment thread clis/facebook/feed.js
Comment on lines +123 to +128
lastSurface = await readFeedSurface(page);
if (lastSurface?.ready) return lastSurface;

if (typeof page.wait === 'function') {
await page.wait(1);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): A rejected page.evaluate() from readFeedSurface() escapes ensureNewsFeedSurface() without being converted to CommandExecutionError, producing an untyped browser/transport failure instead of the command's documented navigation error.

Triggers: When the browser disconnects, the page closes, or surface evaluation fails after navigation succeeds.

Suggested fix: Wrap readFeedSurface(page) and the polling wait in the same error-to-CommandExecutionError handling used for page.goto().

Suggested change
lastSurface = await readFeedSurface(page);
if (lastSurface?.ready) return lastSurface;
if (typeof page.wait === 'function') {
await page.wait(1);
}
try {
lastSurface = await readFeedSurface(page);
if (lastSurface?.ready) return lastSurface;
if (typeof page.wait === 'function') {
await page.wait(1);
}
} catch (err) {
throw new CommandExecutionError(
`Failed to navigate to facebook feed: ${err instanceof Error ? err.message : err}`,
'Check that facebook.com is reachable and the browser extension is connected.',
);
}

Comment thread clis/facebook/feed.js
const hasSendOnly = Array.from(root.querySelectorAll('[aria-label]'))
.some((el) => /^(Send|发送)$/i.test(labelOf(el))) && !hasShare;
const fullText = textOf(root);
if (isMessengerOrChatText(fullText)) return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Any genuine post whose text contains Message sent followed by a date is classified as Messenger content because the check runs against the entire container text, so the valid post is discarded before extraction.

Triggers: When a real post quotes or discusses a message containing text such as Message sent March 1, 2026.

Suggested fix: Apply thread-copy checks to dedicated chat controls or require multiple Messenger signals instead of rejecting the whole container on one phrase in its content.

Suggested change
if (isMessengerOrChatText(fullText)) return true;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants