A scheduled pipeline reads about 48 sources each weekday at 6:30am Eastern, picks the three things that moved, and publishes them here. Every tap you make flows back into what tomorrow’s run selects and what Sunday’s quiz asks.
Ordinary code that calls models at fixed points — no model ever decides which step runs next, so every run takes the same path and fails in the same places. Each stage is its own retried step on Inngest.
The whole design fits in one line: ordinary code, models at fixed points, and every tap read back.
Ordinary code. A normal program runs the same steps in the same order every morning: fetch the sources, pull out what’s new, choose and write, check, save, send the nudge. No model decides what happens next.
Models at fixed points. A model is called at three known steps, each with one job: a fast model pulls items out of each source, a strong model chooses and writes the edition, a mid-size model grades quiz answers. Code checks every answer before it’s used; an edition that doesn’t match the format gets one retry.
Every tap read back. Your thumbs, ✓, Keep and quiz answers are saved, and the next run reads them. That’s all the learning is: better input on the next run, which you can see and undo. Nothing is retrained.
The model decides what’s worth reading. The code decides what happens next.
v1 ran for two weeks as an AI agent: a scheduled Claude session, handed the instructions and a set of tools, that worked through the morning’s job on its own. The writing was good. Getting it to arrive wasn’t.
| v1 — an agent | Diff — a workflow | |
|---|---|---|
| Who picks the next step | The model | The code |
| Fetching sources | Tool calls the agent made, each needing permission | Plain code: no model, no permissions |
| When something goes wrong | The run waited for someone to tap Allow, and hung. Four of its last five runs stalled | Each step retries on its own, and a failure names the step |
| Your feedback | A tap couldn’t reach the next run | Every tap is saved and read by the next run |
| Where you read it | A republished web page | An installable app with a morning nudge and offline reading |
What stayed: the content rules. v1 proved the digest was worth opening, and its two weeks of real editions are where Diff’s rules come from.
The rule of thumb: use an agent when the path can’t be known in advance. Diff’s path is the same every morning, so letting a model choose it only adds ways to fail.
Feedback ranks; it never excludes. There is no downvote and no dismiss.
| Signal | What it changes later |
|---|---|
| Thumbs up + reason | Select sees your last 40. Useful = more of that kind; interesting = widen to adjacent debates; well made = presentation, not topic. |
| Done ✓ | The item stops appearing in Still open. Handled, not rated. |
| Seen | The concept moves from “no signal” to “seen” in the skills map. |
| Quiz answer | Right = proven, re-checked in 7 → 14 → 30 → 60 days. Wrong = back into the dailies and the next quiz. |
| Weekly task | Proves every concept it covers. |
Two guards keep feedback from narrowing what you see: every other weekday the pipeline gets no thumbs at all, and silence never counts as rejection.
| Failure | Fix |
|---|---|
| 4 of 18 runs published nothing — the model thought silently for minutes, indistinguishable from a hang | Stream its thinking; 120 s of silence = hung, a separate 720 s cap = slow |
| Saved-for-Sunday items lost when a Sunday failed | Look back to the last weekly that actually published |
| Every X post rejected for having no date | Decode the date from the post’s ID |
| The audit log grew 4× in three weeks | Structure it; only standing issues carry forward |
| A deploy failed fetching Google Fonts | Bundle the fonts |
Edits are drafts — the 6:30 run never sees them. A preview runs every real stage with the drafts and publishes nothing. Commit writes git first, then the live copy, and refuses if the repo moved underneath it. Live and git never drift.