July 6, 2026
11 min readHow to Build Your Own "State of Anything"
The monthly State of AI report on this site is written and published entirely by a scheduled Perplexity task — no human in the loop. Here's the full recipe so you can build your own State of Anything for any subject worth tracking.
Readers kept asking how the monthly State of AI report gets made. The honest answer is that I don't make it — a scheduled Perplexity task does, unattended. This post documents the exact pattern so anyone can point it at their own subject.
Every month, a report called The State of AI appears on this site. I don't write it. A scheduled task in Perplexity researches the month, writes it up, builds the charts, publishes it to two hosts, updates the archive, and emails me the highlights — start to finish, unattended.
When I shared the short version of this, everyone asked the same thing: okay, but how does it actually publish? Fair — that's the part with the sharp edges, and it's the part most "automate my newsletter" guides wave past. So this is the long version, with the real mechanics: the exact steps, the tools, the file paths, and the two bugs that made a "finished" report invisible to the public for weeks.
The subject happens to be AI. The machine doesn't care. Point it at your industry, a competitor, your city's housing market — anything that changes monthly and deserves a sharp briefing.
The one idea
You don't automate the writing. Writing was never the bottleneck — showing up every month is. You automate the showing up: a recurring task, not a one-off prompt, that on a fixed day each month goes and finds what changed, renders it the same way as last time, gets it in front of readers, and tells you it's done. The value isn't any single issue; it's a numbered series that never skips and compounds into an archive nobody else had the discipline to keep.
Below is exactly how mine runs, step by step, including the parts that bit me.
Step 1 — Figure out which issue this is
The first thing the task does is compute the current month, the year, and the volume number. Mine is pinned: Vol. 1 = March 2026, add one per month, so July 2026 is Vol. 5. I write that rule into the instruction verbatim, because an AI left to infer it will eventually miscount — and a wrong volume number stamped onto a permanent archive is exactly the kind of small, load-bearing error that quietly erodes trust in everything else. The dated, numbered sequence is the spine that turns a stack of documents into a publication.
Step 2 — Research five fixed lanes
I don't tell it to "cover AI." I give it five lanes it must fill every issue, in the same order: Capabilities (models, benchmarks, releases), Funding (rounds, valuations, deal flow), Safety & Risk (incidents, jailbreaks, regulation), Geopolitics (export controls, talent, national strategy), and Robotics (humanoids, embodied AI, automation).
The lanes are a contract, not a suggestion. Their entire job is to make Vol. 5 comparable to Vol. 1. The month a huge story doesn't fit, you'll want to add a sixth lane — don't; fold it in or leave it out. Consistency across issues beats completeness in any one of them.
Step 3 — Pull from real data, not just "the web"
Web search alone drifts toward whatever was loudest that week, and you end up summarizing the hype cycle. Mine also queries paid data connectors — CB Insights, PitchBook, and Statista — so the funding figures and market sizes trace back to something accountable. And it must cite every non-trivial claim with a link. Citations do double duty: they let a skeptical reader check you, and they force the model to actually have a source instead of confabulating a clean-looking number. Unsourced stat = liability; linked stat = asset.
Step 4 — Render one self-contained HTML file
The report is a single HTML file — dark intelligence-dashboard look, KPI stat cards, CSS bar charts, data tables, a risk board, Space Grotesk + Inter type, and my name on it. It gets written to a fixed path in the task's workspace (.../state-of-ai-site/index.html).
Two choices worth copying:
- Self-contained. Styles, layout, and data all live in that one file with no external dependencies. It renders identically in five years, hosts anywhere, and can be emailed or archived without breaking. That portability is what makes the next steps trivial.
- Locked design. You style it once and every future issue reuses the exact template with new numbers. The urge to "improve the design a little" each month is how you get an archive where every issue looks slightly off. Content is the only variable.
Pin the branding explicitly in the instruction, because models drift. Mine literally says to use my name and not an earlier project's brand — because one month, without that line, it cheerfully re-branded the whole thing to a name I'd retired.
Step 5 — Publish, and understand it's two systems, not one
This is the step everyone underestimates, so here's exactly what happens and where it broke.
5a. Push the file into the site's repo — via the GitHub API, not git push. The task runs in a sandbox with no local clone of my repository, so there's nothing to git commit against. Instead it drives GitHub's Git Trees API through the gh CLI: it creates a blob from the report HTML, builds a tree that places that blob at web/public/stateofai/reports/{YYYY-MM}.html, creates a commit pointing at the tree, and then updates refs/heads/main to that commit. That four-call dance (blob → tree → commit → update-ref) is how you commit to a repo you don't have checked out. It's not obvious, and it's the single most-asked-about piece.
5b. In the same commit, register the issue in the archive index. The archive page (web/app/stateofai/page.tsx) keeps a REPORTS array, and the task prepends the new issue to the top of it. Be warned: this is publishing by editing source code as if it were data. A malformed entry — one stray comma — breaks the site's build. (If I were starting over, I'd have the index list itself by reading the reports folder, so publishing never touches code. Editing a registry by hand is a bug with a delay on it.)
5c. Now the part that actually bit me: committing is not deploying. My site runs on Railway, and Railway here deploys from a token-uploaded build — it does not auto-deploy when GitHub receives a push. So the cron faithfully pushed each new issue to GitHub… and nothing happened. The file sat in the repo, perfectly finished, while the live site kept serving the old version. Two entire monthly issues were invisible to the public for weeks — present in the repository, absent from the internet — and I only found out when I went looking for June and couldn't reach it.
The fix is to make "deploy" part of the same automatic motion as "commit." The clean version is a GitHub Action that runs railway up on every push to main (with the Railway token stored as a repository secret). Now a push both lands the file and rebuilds the live site — one event, no human in the loop, no silent gap. Whatever your stack, this is the universal trap: content existing in your system is not the same as a visitor being able to see it. Find that seam and automate across it.
5d. Mirror it. The task also deploys a copy to Perplexity's own hosting as a backup, so even if the primary deploy stumbles, the issue is reachable somewhere public the moment it's done.
Step 6 — One more edge: make sure the links actually open
Each issue is shown on the site inside a sandboxed <iframe>. My first version set sandbox="allow-scripts allow-same-origin" — and every one of the ~160 source links (all target="_blank") was dead on left-click, because a sandbox without allow-popups silently blocks new-tab navigation. Right-clicking "open in new tab" still worked, which made it maddening to diagnose. Adding allow-popups allow-popups-to-escape-sandbox fixed it. If you embed your report anywhere, click a real link before you trust it.
Step 7 — Email yourself a receipt
When the run finishes, the task emails me the new issue plus a summary of the key findings. That email is two things: proof the run happened, and a thirty-second read of what changed without opening anything. Automated jobs fail silently — a task that quietly stopped running looks identical to a slow news month. The receipt is how you tell the difference, so make it detailed enough that its absence alarms you.
The traps, in one place
- Commit ≠ deploy. The big one. Pushing to your repo is not the same as shipping to your live site. Two of my issues proved it by going invisible. Automate the deploy; verify it.
- Publishing by editing code. Registering each issue by hand-editing a
REPORTSarray means one typo can break the whole site. Have the index list itself instead. - No local checkout. If your task runs sandboxed, you can't
git push— you drive the GitHub Trees API (blob → tree → commit → update-ref) directly. - Sandbox eats your links. An
<iframe>withoutallow-popupskills everytarget="_blank"link on left-click. Test a real click. - Design & branding drift. Lock the template; pin the brand name in the instruction. Models will "helpfully" wander.
- Unsourced numbers. Demand a link per claim, and wire in real data sources, not just web search.
- Volume miscounting. Put the numbering rule in the prompt in plain words.
- Silent failure. No receipt email means a dead task and a healthy-looking site are indistinguishable.
Why it's worth the setup
Staying genuinely current on a fast field is a tax paid in attention you don't have. This pays it for you, on schedule, forever — consistent, dated, sourced, permanent, and running while I sleep. The bottleneck was never ideas, and after AI it isn't even the writing. It's showing up every single month. So I stopped relying on discipline and built a machine that shows up instead.
Credit where it's due
The State of AI report, and the entire pipeline above, were built and are run by Perplexity. I described what I wanted once — in this much detail — and it does the rest every month. If you want your own, that's where I'd start: write the most specific instruction you can stand to write, because the instruction is the product.