BUILD LOG · 7 MIN READ · JULY 25, 2026

I let Claude Code rebuild my LinkedIn feed. Here's what actually happened.

No VC, no backend, and no single magic prompt. Just a Chrome extension I built by describing the problem out loud, one iteration at a time — including the week LinkedIn quietly broke it.

If you spend time on LinkedIn or X, you already know the feeling. You open the app to catch up with people you actually follow, and instead you scroll past promoted posts, "people you may know" suggestions, and so much AI-generated content that the real posts get buried underneath all of it. I hit that wall often enough that I decided to fix it for myself.

I'm not primarily a Chrome extension developer. What I had was a clear, specific problem and Claude Code sitting in my terminal. So instead of spending a few weekends learning the Manifest V3 APIs from scratch, I described what I wanted, section by section, and let it write the code while I made the decisions — what to hide, how to label it, when to trust it.

The first real prompt

I didn't write a novel. The first ask was close to this:

Build a Chrome Manifest V3 extension that runs on LinkedIn and X.
Scan the feed, detect promoted/sponsored posts and "people you may
know" suggestions, and collapse each one into a small placeholder
bar the user can click to restore. No backend, no build step —
plain JS files Chrome loads directly. Store settings in
chrome.storage.local. Add a popup with on/off toggles per category.

What came back was a real, working skeleton in that first sitting: a content script that scanned the feed, a classifier, a collapse/restore mechanic, and a popup wired to storage. I loaded it unpacked, opened LinkedIn, and it worked well enough to keep going.

That's the part that looks like magic. It isn't the whole story.

Then LinkedIn changed the floor under me

A few weeks in, LinkedIn quietly rewrote how it renders its feed — moving to a server-driven UI, where the page is assembled from a data schema instead of stable, readable class names. Every selector the extension relied on stopped matching overnight. Posts I used to detect in one line of CSS were now nested inside generic, auto-generated wrapper elements with no useful hooks at all.

That's not a bug you patch with a deprecation fix. It meant rebuilding the entire LinkedIn detection layer — new selectors, a text-walking fallback for when selectors fail, and a button-based ancestor search as a second line of defense, so the extension wouldn't go blind the next time LinkedIn shipped a redesign.

The honest version of "AI wrote my app" is that AI wrote a strong first draft. LinkedIn breaking it a few weeks later is the part a one-prompt story usually leaves out.

I found more of these along the way, all small, all real: a circuit breaker meant to detect a dead feed was latching permanently instead of resetting per page load. Custom keyword matching failed silently against curly apostrophes, so "don't" in a user's blocklist wouldn't match "don't" typed with a smart quote. Each one got a fix and a regression test, not a rewrite.

Before
Promoted
AI post
A person you follow
Suggested
After
Promoted post — hidden restore ↺
AI post — resting restore ↺
A person you follow
Suggested — hidden restore ↺
What collapse actually does: not deleted, just folded into a labeled bar you can undo — same mechanic on LinkedIn, X, and YouTube.

What actually shipped

Zenfeed runs the same shared machinery — scan, classify, collapse, restore — across three sites, but each site gets its own adapter, because LinkedIn, X, and YouTube don't expose their feeds the same way. X has stable data-testid hooks, so it needs less guesswork. YouTube uses a different collapse style entirely — blurred in place instead of folded into a bar, so the grid keeps its shape. Here's what's actually behind the toggles:

Hide promoted

Collapses sponsored posts on LinkedIn and "Ad" / "Promoted" tweets on X.

Hide suggested

"People you may know," "Who to follow," "Discover more" — off by default, since it's a looser match.

Rest from AI

A timer that hides AI-flagged posts for a set number of minutes, with a live countdown badge on the toolbar icon.

Custom keywords

Block posts by your own word list, matched case-insensitively, across all three sites.

Hide Shorts

Collapses the entire YouTube Shorts shelf as one unit, not tile by tile. Off by default.

Hide company pages

Filters LinkedIn Company and Showcase Page posts by checking the byline link, not the post text. Off by default.

Why "off by default" shows up twice

Suggested-content and Shorts detection are content-type filters, not unambiguous ad labels. A false positive on day one costs more trust than a miss does, so both ship opt-in. That's a product decision, not a technical limitation — and it's the kind of call that "one prompt" can't make for you.

Making it trustworthy, not just working

A feed extension that's wrong even occasionally is worse than useless — it hides posts you wanted to see. So most of the later work wasn't new features, it was tests: a Playwright suite that drives real page structure per site, plus jsdom unit tests for the parts that don't need a browser. There's a dedicated test for the LinkedIn sidebar ad sweep, one for company-page detection, one for the YouTube Shorts shelf, one just for Rest from AI's countdown logic. Nine separate npm run test:* commands, because "it looks like it works" and "it's still right after LinkedIn's next redesign" are different bars.

On privacy — stated plainly, because it has to be true Everything runs locally, in the browser. There's no server, no tracking, and no account. Settings live in chrome.storage.local, on your machine, nowhere else.

Where it landed

Zenfeed is live on the Chrome Web Store, on version 1.1.4 as of this post. It's free, it's built by one person, and it started as a tool to fix my own feed before it was anything else. It only works on Chrome right now — Firefox handles this kind of extension differently, so that's a later project, not a promise.