Skip to content

fix facebook feed zero rows after decoy filter - #4

Merged
lawmight merged 1 commit into
lawmight/fb-chrome-stackfrom
cursor/facebook-feed-zero-rows-b9a7
Sep 10, 2026
Merged

lawmight merged 1 commit into
lawmight/fb-chrome-stackfrom
cursor/facebook-feed-zero-rows-b9a7

Conversation

@lawmight

@lawmight lawmight commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Description

After PR #3, live smoke on tom.coustols stopped returning scrambled Adolph decoys (good) but started failing with no feed rows could be extracted despite articles=2, actions=1, mainTextLength=976. The in-page extractor was rejecting scrambled posts before rows were built, so Node never saw decoy rows and fell through to the generic error.

Related: live smoke 2026-09-11, OpenCLI ae8e790

Type of Change

  • 🐛 Bug fix

Changes

  • French locale support: Actions pour cette publication par …, J'aime/Commenter/Partager, Tous:/commentaires/partages, Partagé avec Public
  • Dedupe fix: keep containers when content blocks are empty (decoy-filtered) using author/text fallback keys — previously skipped entirely
  • Rejection diagnostics: diagnostics.rejections[] with reason/author/snippet per candidate (decoy_content, suggestion, messenger, insufficient_evidence, comment_thread, etc.)
  • Fail-closed decoy path at zero rows: all-decoy rejections → anti-scrape CommandExecutionError with decoy_content=N breakdown
  • Richer no_rows hint: includes rejected=N, reason=count, samples=… when articles exist but nothing extracts
  • Accented token handling in scrambled heuristic (preserve é/è/à in token core)

Checklist

  • npm test -- clis/facebook/ — 112 passed (includes Adolph/decoy + French fixtures)
  • Messenger bleed + landmark tests unchanged

Post-merge verification

On tom.coustols desk: opencli facebook feed --limit 5 should either return readable posts (e.g. LVLUP with Lani) OR fail with decoy_content=2 / rejection samples — not a bare articles=2 hint with no explanation.

Open in Web Open in Cursor 

Summary by Sourcery

Improve Facebook feed extraction to support French content and explain zero-row results caused by filtered or unreadable posts.

Bug Fixes:

  • Prevent Facebook feed extraction from returning a generic zero-row error when visible posts are filtered as anti-scrape decoys.
  • Preserve decoy-filtered candidates for accurate deduplication and rejection reporting.
  • Provide targeted anti-scrape errors for all-decoy feeds and richer diagnostics when no rows are extracted.

Enhancements:

  • Add French-locale support for feed authors, actions, metrics, timestamps, and visibility labels.
  • Improve scrambled-text detection for accented Latin characters.

Tests:

  • Add French feed extraction coverage and validation for accented prose.
  • Add coverage for decoy rejection diagnostics, all-decoy error mapping, and no-row rejection breakdowns.

Summary by cubic

Fixes the Facebook feed extractor returning zero rows after decoy filtering, so live feeds with scrambled posts now either return readable posts or fail with a clear anti-scrape error instead of a bare no-rows hint.

Changes

  • Adds French locale support for action menus, metrics, timestamps, and author extraction.
  • Keeps containers in dedupe when content blocks are empty using author/text fallback keys.
  • Records per-candidate rejection reasons (decoy_content, suggestion, messenger, etc.) in diagnostics.
  • Fails with an anti-scrape CommandExecutionError when all candidates are decoys, even at zero rows.
  • Includes rejection counts and samples in the generic no-rows hint.
  • Handles accented characters in the scrambled-text heuristic.

Written for commit 61a0b99. Summary will update on new commits.

Review in cubic

Live smoke after PR #3 hit no_rows with articles=2 because decoy filtering
dropped scrambled posts in-page without surfacing why. Add French action-menu
and metric labels, fix dedupe skipping author-only decoy containers, and
record per-article rejection reasons (decoy/suggestion/messenger/etc).

When all candidates are decoys, fail with anti-scrape error even at zero
rows; otherwise include rejection breakdown in the generic no_rows hint.

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

coderabbitai Bot commented Sep 10, 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 10, 2026

Copy link
Copy Markdown

Reviewer's Guide

The extractor now supports French Facebook feed markup, preserves candidates rejected by readability and scope checks for diagnostics, and distinguishes all-decoy zero-row results from other extraction failures with actionable error details.

Sequence diagram for Facebook feed extraction diagnostics

sequenceDiagram
    participant FacebookPage
    participant Extractor
    participant FeedCommand
    participant CLI

    FacebookPage->>Extractor: buildFeedExtractScript(limit)
    Extractor->>Extractor: dedupe containers with fallback keys
    Extractor->>Extractor: extractPost(container)
    alt readable post
        Extractor-->>FeedCommand: rows and diagnostics
        FeedCommand-->>CLI: extracted feed rows
    else rejected candidate
        Extractor->>Extractor: rejectionSample(container)
        Extractor-->>FeedCommand: no_rows and diagnostics.rejections
        FeedCommand->>FeedCommand: summarizeFeedRejections(diagnostics)
        alt all candidates are decoys
            FeedCommand-->>CLI: CommandExecutionError anti-scrape decoy
        else other candidates rejected
            FeedCommand-->>CLI: CommandExecutionError with rejection breakdown
        end
    end
Loading

Flow diagram for French Facebook feed extraction

flowchart TD
    A[Facebook feed markup] --> B[Recognize French labels and metrics]
    B --> C[Find author and content blocks]
    C --> D{Readable candidate?}
    D -->|Yes| E[Build feed row]
    D -->|No| F[Record rejection reason author and snippet]
    E --> G[Return extracted rows]
    F --> H{All rejections are decoys?}
    H -->|Yes| I[Raise anti-scrape decoy error]
    H -->|No| J[Raise no-rows error with counts and samples]
Loading

File-Level Changes

Change Details Files
Add French-locale parsing throughout feed detection and extraction.
  • Recognize French post-menu, action, metric, timestamp, sharing, and author labels.
  • Preserve accented characters in scrambled-text detection.
  • Add a French fixture and extraction/readability coverage.
clis/facebook/feed.js
clis/facebook/__fixtures__/feed-french-post.html
clis/facebook/feed.test.js
Retain filtered candidate containers so rejected posts can be diagnosed instead of disappearing before extraction.
  • Use URL, content, author, and text fallback keys during container deduplication.
  • Classify rejected candidates such as decoys, suggestions, Messenger content, comment threads, and insufficient evidence.
  • Expose bounded rejection records with reason, author, and snippet in diagnostics.
clis/facebook/feed.js
Return actionable errors when zero rows result from filtering, especially when all candidates are anti-scrape decoys.
  • Summarize rejection counts and sample details for no-row hints.
  • Map all-decoy rejection sets to an anti-scrape CommandExecutionError.
  • Keep the generic no-row error for mixed or non-decoy failures while including rejection breakdowns.
  • Add tests covering decoy diagnostics, all-decoy errors, and enriched no-row hints.
clis/facebook/feed.js
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

@lawmight
lawmight marked this pull request as ready for review September 10, 2026 20:48
@lawmight
lawmight merged commit ae8fe40 into lawmight/fb-chrome-stack Sep 10, 2026
1 check 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 2 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="902" />
<code_context>
+        ).length,
         mainTextLength: textOf(document.querySelector('[role="main"]')).length,
         feedFound: !!document.querySelector('[role="main"] [role="feed"], [role="feed"]'),
+        rejections: rejections.slice(0, 12),
         surface,
       },
</code_context>
<issue_to_address>
**issue (broader_impact):** The extractor truncates rejection diagnostics to the first 12 entries before Node classifies them with `allDecoy`. If the first 12 candidates are decoys but a later rejected candidate has another reason, the omitted candidate cannot prevent the command from incorrectly raising the all-decoy anti-scrape error.

**Triggers:** When more than 12 candidate articles are rejected and the first 12 are decoys.

**Suggested fix:** Compute the rejection summary from all candidates and truncate only the diagnostic payload presented to the caller.
</issue_to_address>

### Comment 2
<location path="clis/facebook/feed.js" line_range="897-899" />
<code_context>
         actionMenuCount: actionMenuAnchors().length,
-        fallbackActionCount: document.querySelectorAll('[role="main"] [aria-label="Like"], [role="main"] [aria-label="赞"], [role="main"] [aria-label="Comment"], [role="main"] [aria-label="评论"]').length,
+        fallbackActionCount: document.querySelectorAll(
+          '[role="main"] [aria-label="Like"], [role="main"] [aria-label="赞"], [role="main"] [aria-label="J\\'aime"], '
+          + '[role="main"] [aria-label="Comment"], [role="main"] [aria-label="评论"], [role="main"] [aria-label="Commenter"]',
+        ).length,
         mainTextLength: textOf(document.querySelector('[role="main"]')).length,
         feedFound: !!document.querySelector('[role="main"] [role="feed"], [role="feed"]'),
</code_context>
<issue_to_address>
**nitpick:** The French fallback selector counts `J'aime`, `Commenter`, and the other like/comment labels but omits the newly supported `Partager` label, even though `fallbackContainers` recognizes it. A French page whose visible fallback evidence includes share actions therefore reports an incomplete `actions` diagnostic and can lose that action evidence in no-row classification.

**Triggers:** When French feed markup exposes `Partager` as the relevant fallback action and the other counted action labels are absent.

**Suggested fix:** Add `[aria-label="Partager"]` to the `fallbackActionCount` selector, keeping it aligned with `fallbackContainers`.
</issue_to_address>

Sourcery assessment

Approval pending. 1 finding to address first.

Blocking findings: clis/facebook/feed.js:902


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

Comment thread clis/facebook/feed.js
).length,
mainTextLength: textOf(document.querySelector('[role="main"]')).length,
feedFound: !!document.querySelector('[role="main"] [role="feed"], [role="feed"]'),
rejections: rejections.slice(0, 12),

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): The extractor truncates rejection diagnostics to the first 12 entries before Node classifies them with allDecoy. If the first 12 candidates are decoys but a later rejected candidate has another reason, the omitted candidate cannot prevent the command from incorrectly raising the all-decoy anti-scrape error.

Triggers: When more than 12 candidate articles are rejected and the first 12 are decoys.

Suggested fix: Compute the rejection summary from all candidates and truncate only the diagnostic payload presented to the caller.

Comment thread clis/facebook/feed.js
Comment on lines +897 to +899
'[role="main"] [aria-label="Like"], [role="main"] [aria-label="赞"], [role="main"] [aria-label="J\\'aime"], '
+ '[role="main"] [aria-label="Comment"], [role="main"] [aria-label="评论"], [role="main"] [aria-label="Commenter"]',
).length,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nitpick: The French fallback selector counts J'aime, Commenter, and the other like/comment labels but omits the newly supported Partager label, even though fallbackContainers recognizes it. A French page whose visible fallback evidence includes share actions therefore reports an incomplete actions diagnostic and can lose that action evidence in no-row classification.

Triggers: When French feed markup exposes Partager as the relevant fallback action and the other counted action labels are absent.

Suggested fix: Add [aria-label="Partager"] to the fallbackActionCount selector, keeping it aligned with fallbackContainers.

@lawmight lawmight mentioned this pull request Sep 10, 2026
2 tasks
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