Just have Claude Code do it for you. Here's a prompt that should get you an app to fetch the blog daily and let you search it, all hosted locally:
# Build prompt: Actualized.org blog reader
Build me a GUI app that lets me read and search every blog post from
https://actualized.org/insights — a local, offline archive with full-text search
that stays current on its own. Start with the backend: getting all the posts off
the site, stored durably, OCR'd where the content is locked inside images, and
kept up to date via a daily automated check. The GUI comes after.
You own the architecture. Everything below is reconnaissance and constraints,
not instructions — make your own engineering calls, and tell me if you disagree
with any framing here. I'm not a developer, so flag tradeoffs in plain terms.
## What I already checked (verified 2026-07-16 — don't repeat these dead ends)
Every convenient structured-data route is closed:
- **WordPress REST API: unavailable, and WordPress doesn't render these pages
anyway.** A WordPress install *does* exist at `/wordpress/` — it serves the
blog's images — but the public site is not WordPress-rendered. Evidence:
`/wp-json/wp/v2/posts` 404s at the root; `/wordpress/wp-json/wp/v2/posts`
404s; the permalink-independent fallback `/?rest_route=/wp/v2/posts` returns
the homepage HTML rather than JSON or a JSON error, meaning nothing
WordPress-shaped handles requests at the root; and a sample post page carries
no WordPress fingerprints (no generator meta, no `wp-includes` scripts, no
`api.w.org` REST link, no `wp-block-*` or `postid-N` classes). Don't burn time
looking for a WordPress-shaped door — there isn't one.
- **RSS: useless.** `https://actualized.org/rss` is a valid feed but only carries
10 items, description excerpts only — no `pubDate`, no `content:encoded`, no
post bodies.
- **No sitemap anywhere.** `/sitemap.xml`, `/sitemap_index.xml`,
`/wp-sitemap.xml`, and `/sitemap-index.xml` all 404. `robots.txt` contains no
`Sitemap:` directive.
So HTML scraping is genuinely the only path. Post URLs must be enumerated by
walking the pagination at `/insights`, which runs to roughly 274 pages.
## Site structure
- Index with numbered pagination at `/insights`, ~274 pages.
- Individual posts at `/insights/[slug]`.
- **Canonical host is `www.actualized.org`.** Post links on the index are
absolute and include the `www.`, though the site also answers without it. Pick
one form and normalize, or you'll end up with duplicate records for one post.
- Each post has a title, a publication date, body content, and social share
buttons (Facebook, Twitter, Pinterest, Email) that are chrome, not content.
**The site's age matters.** `robots.txt` disallows `/includes/`, `/Templates/`
(capital T), `/flash/`, `/downloads/`, `/wordpress/`, and several hand-listed
`.php` files under `/pages/misc/`. Combined with the total absence of WordPress
markup on the public pages, this reads as an old hand-built PHP site with a
WordPress install sitting in a subdirectory doing something behind the scenes —
plausibly the media library and/or an authoring back-office — while custom code
renders the front-end. I never established what WordPress's actual role is, and
it doesn't matter for this build: you're parsing hand-rolled HTML either way.
The implication is what matters — assume post markup varies across eras, and
verify that early rather than trusting page 1 to represent page 274.
## Content characteristics
- Posts are short and structurally simple: usually a single headline followed by
rich text.
- YouTube embeds appear throughout, sometimes several per post. These need to
survive into the archive and be playable/visible in the app.
- **Roughly 30–40% of posts are just an image, and the image is almost always
text.** Not a photo or a diagram — an image containing the actual written
content of the post. For these, the HTML body is nearly empty and everything I
care about is pixels. That's a large minority, not the majority — most posts
are ordinary rich text — but it's far too many to treat as an edge case or
handle by hand. See "OCR" below; this is core scope.
- Images are hosted under `/wordpress/wp-content/uploads/`.
## Constraints
- **Respect `robots.txt`, with one decided exception.** `/insights` and
`/insights/[slug]` are not disallowed — that's the content, and it's fair game.
`/wordpress/` is disallowed, so don't crawl or enumerate it. However, post
images live under `/wordpress/wp-content/uploads/`, and OCR requires
downloading them. I've decided that's fine: those are assets embedded in pages
I'm permitted to read, and fetching them is the same request my browser makes
when I read the post normally. Fetch images **only** by following `src` URLs
found in posts I've already legitimately retrieved. Never traverse, enumerate,
or spider `/wordpress/` itself.
- **Be a good citizen.** Personal-use archive of one site I read. Throttle
politely; there's no reason to hit the server hard. A slow crawl is fine —
run it in the background.
- **Fetch each post once.** The initial crawl should be a one-time cost. See
"Keeping it current" below.
- **Fail loudly, not silently.** A post that doesn't parse should be recorded as
a failure with its URL, not written as an empty or half-empty record. I'd
rather see a list of 40 problems than discover hollow posts in six months.
- Windows. Assume nothing else about the stack — recommend what fits.
## OCR
For roughly a third of posts, the image *is* the article. If the archive only
stores the HTML body for those, it stores nothing, and they're invisible to
search — a third of my archive would be a hole, which defeats the point of the
whole app. So:
**Size this properly before choosing an approach.** If the archive turns out to
be ~2,700 posts, 30–40% means somewhere around 800–1,100 images to OCR — and
every one gets re-processed if I later want a better pass. That's enough volume
that per-image cost and total runtime are real design inputs, not rounding
errors. Tell me what your approach would cost me in both, and if it's a paid
service, what the bill looks like for one full pass.
- Text extracted from post images must land in the database and be **searchable
on equal footing** with regular post text. If I search a phrase that only ever
appeared inside an image, that post has to come back.
- **Track which text came from OCR.** I need to know whether a given post's text
is authored HTML or machine-read pixels — it affects how much I trust it, how
it should be presented, and whether it's worth re-processing later. Don't blend
the two into one indistinguishable blob.
- **Assume the first OCR pass won't be good enough.** OCR engines vary a lot on
this kind of content, and I may want to re-run everything with a better one
later. Re-processing must not require re-downloading anything from the site.
- **Bad OCR must be visible, not silent.** An image that yields zero text, or
obvious garbage, should be flagged as a problem I can review — not written to
the database as if it were a legitimately empty post.
- **These are quote cards, which is good news.** Confirmed image filenames follow
the pattern `leo-quote-<slug>-01.png`, e.g.
`leo-quote-life-is-not-a-physical-process-01.png`,
`leo-quote-my-work-is-10-percent-wrong-01.png`, served from
`/wordpress/wp-content/uploads/`. So these are machine-rendered text on a
background — clean, high-contrast, probably consistent typography — not
photographs or scans. That's the easy case for OCR and should push accuracy
expectations up. Verify the consistency holds across the archive's full
history before relying on it.
- **The filename is a free sanity check.** The slug in each filename is a
human-readable rendering of the quote itself. It's not a substitute for OCR —
it's truncated and lossy — but it's an independent signal you can compare OCR
output against to catch a pass that's silently producing garbage. Worth
storing regardless.
- Still sample real examples across the archive before committing to an
approach. The `-01` suffix hints at multi-image posts or variants; I don't know
what that means.
## Keeping it current
The archive needs to check for new posts **daily**, unattended, and pull anything
it finds into the archive without me doing anything.
Constraints that make this harder than it looks:
- **The machine is a personal Windows desktop.** It sleeps, it gets shut down, it
won't be on at any predictable hour. A schedule that silently skips its window
and waits for tomorrow is not acceptable — missed runs need to catch up on the
next opportunity. Days may pass between runs, and the archive still needs to be
complete afterward.
- **The daily check must be cheap.** Re-walking all ~274 pagination pages every
day to find the zero-to-two posts that are new is wasteful and rude to the
server. The daily path should cost far less than the initial crawl.
- **New posts need the full treatment**, including image download and OCR. A
daily sync that grabs an image-only post but never OCRs it has archived an
empty post and won't tell me.
- **Silent failure is the main risk.** This runs unattended, so the realistic bad
outcome isn't a crash — it's the site changing its markup, the scraper quietly
finding nothing, and me not noticing for months while believing the archive is
current. I need to be able to tell at a glance whether the sync is actually
working. Surfacing "last successful sync" and "last error" somewhere I'll see
matters more than clever recovery logic.
- Don't let a failed or interrupted run corrupt the existing archive. A bad sync
should leave yesterday's good data intact.
## Things I don't know
- Whether I want to be **notified** when new posts arrive, or just have them
appear silently in the app next time I open it. Raise this with me — don't
assume either way.
- Whether the daily check should also catch **edits to existing posts**, or only
genuinely new ones. Same — ask me, and tell me what it costs to do both.
- The real post count. ~274 pagination pages; posts-per-page unconfirmed.
- How many distinct markup variants exist across the site's history.
- Whether publication dates are reliably present and parseable on every post.
- Whether pagination is stable enough to enumerate cleanly (new posts shift it).
- The 30–40% image-post figure is my rough estimate from reading the site, not a
count. Confirm it while you're sampling.
- Whether the quote-card format is consistent all the way back. I only saw
recent ones; the oldest posts may look nothing like them.
- What WordPress is actually doing at `/wordpress/`, beyond serving images. I
didn't establish this and don't think it affects the build — but if you find a
legitimate, robots-respecting structured route into the content that I missed,
I'd rather use it than parse HTML.
- Whether OCR'd text should be **shown to me as readable text**, or only used
behind the scenes to make image posts findable by search. These have very
different accuracy bars. Show me sample OCR output before I decide.
## What I want from you first
Before writing the full scraper, show me what you actually found: what the markup
looks like at both ends of the archive's date range, how many variants you're
dealing with, and what your storage plan is. Then build it once we agree.