This is a submission for the Sanity Challenge, Path Two: Vibe-Code Something Strange
What I Built
I built Everlogue, a home for everything you read: a shared book catalog, private reading shelves, celebrity book clubs, and Ask Everlogue, a reading companion grounded in structured Sanity content.
But I did not want to build another Goodreads clone with a nicer frontend.
I wanted to answer a harder question:
What if the catalog could grow itself, while Sanity decided what was trusted enough to become shared content?
That led me to build Everlogue around multiple ingestion paths, with Sanity as the editorial control system.
Bulk / historical ingestion
Readers can import their Goodreads library. Everlogue parses the CSV, works through the books in the reader's library, matches or enriches metadata, and places books onto the correct Want to Read, Currently Reading, and Read shelves.
This gives a reader a way to bring an existing reading history into Everlogue instead of rebuilding it book by book.
Reader-grown catalog
Readers can also grow the shared catalog.
If someone searches for a book that Everlogue does not have and tries to add it to a shelf, that missing title becomes a catalog request for review.
Instead of every reader creating their own disconnected version of the same book, Everlogue can review the request and turn it into structured shared catalog content.
Continuous / live ingestion: Book Club Watch
The strangest ingestion path became Book Club Watch.
Everlogue follows Reese's Book Club, GMA Book Club, Read With Jenna, and Oprah's Book Club. Keeping those collections current sounds simple until you realize they do not announce their selections on the same schedule.
I modeled each club separately:
Reese
9 AM ET, days 1–8
Stop once the month's pick is already recorded
GMA
10 AM ET every Tuesday
Read With Jenna
9 AM ET Monday + Tuesday
Only during the early-month window
Oprah
9 AM ET Monday / Wednesday / Friday
Recurring checks because there is no fixed release pattern
A Sanity Blueprint deploys four Scheduled Functions to perform those checks.
But finding a title is not permission to publish it.
When Book Club Watch finds a potential selection, it creates a structured bookClubDiscovery document instead of creating a live book document.
The automation proposes.
The editor decides.
Another Sanity Function reacts to that editorial transition.
That distinction became one of the most important architectural decisions in Everlogue.
The catalog can grow itself without allowing an automated scraper to define shared truth.
The resulting catalog is also what powers the rest of Everlogue, including discovery surfaces and Ask Everlogue. I wanted the reading companion to feel like talking to a thoughtful friend in a bookshop, but its recommendations should come from books Everlogue actually knows about — not an invented shelf.
Demo
Rather than a video walkthrough, I documented the workflow with screenshots showing the public app and the Sanity system behind it.
Code
https://github.com/sphcastillo/everlogue
The Book Club Watch infrastructure begins in:
sanity.blueprint.ts
The Blueprint defines:
1 Book Club Watch robot token
4 Scheduled Functions
1 approval-triggered Document Function
The core Watch implementation lives under:
src/lib/book-club-watch/
That code models club schedules, discovery state, source inspection, duplicate protection, approval state, and publishing.
Sanity Studio adds the editorial layer: Review Queue, approved/publication failures, rejected discoveries, published history, run history, and custom actions for the review process.
My Build Process
I built Everlogue with the help of Codex & Cursor, and the most productive prompts were not "build me a Goodreads clone."
They were small jobs with explicit constraints.
Things like:
Parse this Goodreads CSV, preserve the reader's shelf state, and do not create duplicate books we already own.
Check this official book-club source and create a discovery when something changes.
Never create a live book directly from the scheduled function.
Record the Watch run even when nothing changed.
Those constraints mattered because some of my first approaches were wrong.
The first bad assumption: one monthly cron
My first instinct was one Scheduled Function that ran once a month.
That did not survive contact with reality.
The four clubs have different announcement patterns, and Oprah does not have a dependable monthly date at all.
Instead of forcing those sources into one schedule, I modeled the release behavior itself as structured configuration and let each watcher decide whether the current check was meaningful.
The second bad assumption: discovery equals publication
Another early direction collapsed:
"We found the book"
into:
"Create the book"
I did not want that.
A title extracted from a webpage is evidence, not editorial truth.
I split the process into two systems.
Discovery proposes structured content.
Editorial approval publishes structured content.
A discovery moves through states such as:
discovered
↓
needs_review
↓
approved ─────→ published
│
└──────────→ rejected
Studio became the place where the human can inspect the title, authors, source URL, evidence, proposed metadata, existing-book matches, and publication mode.
Only approval allows the publishing function to react.
The publish trigger also checks that the discovery does not already have a catalog book, giving the workflow another layer of duplicate protection.
Going past Studio
The Blueprint became:
Book Club Watch
├── Robot Token
├── Reese Scheduled Function
├── GMA Scheduled Function
├── Read With Jenna Scheduled Function
├── Oprah Scheduled Function
└── Publish Approved Discovery
This is where I hit one of the biggest implementation problems.
My initial Blueprint stack was project-scoped.
The functions passed local tests, the Studio built, and the bundles compiled — but Sanity refused the deployment because Scheduled Functions require an organization-scoped Blueprint stack.
I had to promote the stack from project scope to organization scope before the cron resources could deploy.
That was one of the places where the AI-generated implementation was technically plausible but incomplete. The deployment environment exposed the missing constraint.
Local testing also fooled me
Another confusing moment came after a local test successfully found an Oprah selection.
The terminal showed a real result, but my Studio Review Queue was empty.
The reason was in my own runtime:
if (context.local) {
console.log(await discoverPicks(...))
return
}
Local Function tests intentionally exercised discovery without mutating the dataset.
That forced me to separate two things I had initially treated as the same:
Does source discovery work?
and:
Does the deployed cloud workflow write the correct editorial state?
I then tested the actual deployed cron against a development dataset.
That run discovered Little Wonder by Sophie Chen Keller from an official Oprah source and created a real bookClubDiscovery with:
bookClub
discoveredTitle
discoveredAuthors
selectionMonth
selectionDate
sourceUrl
sourceEvidence
identity
matchExplanation
status
Its status was needs_review, exactly as intended.
Development before production
I eventually standardized Everlogue around two datasets:
development
production
Book Club Watch was first enabled against development.
For testing, I temporarily accelerated the Oprah schedule so I could observe a real cloud execution instead of waiting several days for the normal schedule.
Once I verified the full flow, I restored Oprah's real Monday / Wednesday / Friday schedule.
Before enabling Watch against production, I exported the entire production dataset — 1,644 documents and 327 assets — and added another Blueprint safeguard:
Production cannot run enabled
unless WATCH_PRODUCTION_VALIDATED=true
Only after the development workflow passed did I deploy the production version.
Failure states became part of the product
The final system does not pretend external systems always work.
Book Club Watch records run history and distinguishes outcomes such as:
no change
already recorded
skipped
failed
discovered
Studio keeps rejected discoveries, publication failures, and publishing history instead of hiding them.
One of the live sources returned an HTTP 500 during testing. Google Books enrichment was also unavailable during one discovery.
Neither condition was allowed to silently create bad catalog data.
The result is the part of Everlogue I like most architecturally:
automation can inspect the web and propose structured state, but it does not have unilateral authority over the shared catalog.
The machine proposes.
The editor decides.
Sanity connects the two.
Sanity Project Details
Project ID: 3h0o1unw
Production dataset: production
The production dataset is private because Everlogue contains reader-specific data and internal editorial state.
Book Club Watch was validated against the separate development dataset before production enablement.
Sanity is used for much more than page content in Everlogue. It holds the structured book catalog and editorial content while Studio, Scheduled Functions, Document Functions, Blueprints, and custom document actions form the operational layer around that content.








Top comments (0)