Fire the Dashboard, Hire the Agent
A complete build recipe: GA4, Search Console, Claude, and GitHub wired into an editorial agent that reads a month of data, writes five ranked recommendations, grades its own previous advice, and pings you — on two cadences, for pennies. Five APIs, zero dashboards, one afternoon.
On this page
Five APIs, zero dashboards, one afternoon.
Every analytics setup I have ever seen dies the same death. Week one, you check the dashboard daily. Week four, weekly. Week twelve, the tab is still pinned and you cannot remember the last time you clicked it. The data kept flowing; the judgment stopped. Dashboards don’t fail because the charts are wrong — they fail because a chart is a question and you were promised an answer.
So I stopped visiting my analytics and made my analytics visit me. Every month, an agent reads my GA4 and Search Console data, compares it against every previous month it has ever seen, writes five ranked content recommendations with the evidence attached, grades the advice it gave me last month against what actually happened, and opens a GitHub issue that lands in my inbox. Every Wednesday, a lighter version tells me how the piece I just published is indexing — before the first click ever arrives.
The whole build took one afternoon. It has zero npm dependencies in the data layer. It runs for less than a takeout coffee per month. And the first report it ever produced found something three months of dashboard-glancing had missed — more on that below, with receipts.
This is the full recipe.
What This System Does
Two scheduled workflows, one shared fetch script, one service account:
- Monthly deep review (3rd of each month): pulls a fixed bundle of GA4 and Search Console reports, hands them to Claude with an analysis rubric, and produces five ranked actions — refresh this decaying page, rewrite that title, write this essay next — each citing the numbers that justify it. It opens a fresh GitHub issue per month.
- Weekly pulse (every Wednesday): pulls the just-finished Mon–Sun week and the week before it, reports three things only — how newly published content is indexing, the biggest search movers, and anything that crossed onto page one. Hard-capped at ~350 words, forbidden from making recommendations, appended as a comment to one rolling issue so it never spams.
- A self-grading loop: every monthly report opens with a scorecard — one line per recommendation from the previous report, stating what the data now shows. The agent is accountable to its own advice.
- Memory as files: every snapshot and every report is committed to the repo. The agent’s institutional memory is a folder of JSON — versioned, diffable, greppable, free.
The One Design Decision That Matters
Before any code: the single choice that makes this work is deterministic data pull, agentic analysis.
The tempting architecture is to hand the agent API credentials and let it decide what to query. Resist that. An agent improvising API calls gives you a different report every run, burns tokens exploring, drifts as models update, and leaves you unable to say why this month’s numbers differ from last month’s — did the traffic change, or did the query? You want the boring half of the pipeline to be perfectly boring.
So the pipeline splits into three zones with a hard boundary between them. A plain script pulls the same report bundle every time — same dimensions, same metrics, same ordering. The output is a JSON snapshot, committed to the repo. Only then does the agent enter, and its job is pure judgment: read this month’s snapshot, read every prior snapshot and report, apply the rubric, write the review. The model spends its intelligence on interpretation, which is the only place intelligence adds anything.
The boundary pays for itself three ways. Reproducibility: two runs on the same month produce the same snapshot, byte for byte. Debuggability: when a number looks wrong, you know it isn’t the model hallucinating a query. Cost: the agent reads a compact JSON file instead of spending tokens spelunking through API pagination.
The Five APIs
| API | Role | Auth |
|---|---|---|
| Google OAuth 2.0 | Turns a service-account key into an access token | Self-signed JWT, RS256 |
| GA4 Data API | What readers did: pages, engagement, channels | Bearer token (Viewer role) |
| Search Console API | What searchers wanted: queries, impressions, positions | Same token, second scope |
| Anthropic API | The judgment: rubric → ranked recommendations | Headless Claude Code in CI |
| GitHub API | Memory (commits) + delivery (issues) | The workflow’s built-in token |
One nuance worth naming: Search Console is the half most people skip, and it’s the more valuable half for editorial decisions. GA4 tells you what happened on the pages you already have. GSC tells you what demand you’re failing to capture — queries where you have impressions but no clicks, pages ranked 8–20 that are one revision away from page one. If you only wire up one Google API, wire that one.
Step 1 — The Google Plumbing (10 Minutes)
A service account is a robot Google identity: it has an email address, you grant it access like a colleague, and it authenticates with a key file instead of a password.
- In Google Cloud Console, create a project, then enable the Google Analytics Data API and the Google Search Console API. Both are free.
- Create a service account. Grant it no project roles — its access comes from the products, not from Cloud IAM. Download a JSON key.
- In GA4: Admin → Property access management → add the service-account email as Viewer.
- In Search Console: Settings → Users and permissions → add the same email.
- Put the entire key file into a repo secret (
GA_SERVICE_ACCOUNT_JSON), and an Anthropic API key into a second (ANTHROPIC_API_KEY).
Keep the robot read-only. It will live in a CI secret forever; a Viewer key that leaks is an annoyance, an Admin key that leaks is an incident.
Step 2 — The Fetch Script, With Zero Dependencies
Google’s client libraries are fine, but for two REST calls they’re a dependency tree you don’t need. A service-account token is just a JWT you sign yourself — Node’s built-in crypto does RS256 natively:
import { createSign } from 'node:crypto';
const b64url = (input) => Buffer.from(input).toString('base64url');
async function getAccessToken(sa, scopes) {
const now = Math.floor(Date.now() / 1000);
const unsigned =
b64url(JSON.stringify({ alg: 'RS256', typ: 'JWT' })) + '.' +
b64url(JSON.stringify({
iss: sa.client_email, // the robot's email
scope: scopes.join(' '), // both scopes, one token
aud: sa.token_uri,
iat: now,
exp: now + 3600,
}));
const signature = createSign('RSA-SHA256').update(unsigned).sign(sa.private_key);
const jwt = `${unsigned}.${b64url(signature)}`;
const res = await fetch(sa.token_uri, {
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: jwt,
}),
});
return (await res.json()).access_token;
}
Request both scopes at once — analytics.readonly and webmasters.readonly — and one token covers both APIs. From there, GA4 is a single endpoint:
async function runReport(token, range, { dimensions = [], metrics, orderBy, limit = 200 }) {
const res = await fetch(
`https://analyticsdata.googleapis.com/v1beta/properties/${PROPERTY_ID}:runReport`,
{
method: 'POST',
headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },
body: JSON.stringify({
dateRanges: [range],
dimensions: dimensions.map((name) => ({ name })),
metrics: metrics.map((name) => ({ name })),
limit,
...(orderBy && { orderBys: [{ metric: { metricName: orderBy }, desc: true }] }),
}),
}
);
if (!res.ok) throw new Error(`GA4 runReport failed: ${res.status} ${await res.text()}`);
// flatten dimensionHeaders/metricHeaders + rows into plain objects
}
Search Console is the same shape against searchconsole.googleapis.com/webmasters/v3/sites/{site}/searchAnalytics/query — dimensions query and page, and note the site identifier is sc-domain:yourdomain.com for domain properties.
The monthly bundle I settled on, all fetched in one Promise.all:
- Totals — sessions, users, new users, pageviews, engagement rate
- Pages — by path: views, users, engagement duration, engagement rate
- Landing pages × channel group — where readers enter, from where, and whether they bounce
- Channels and new vs. returning — the mix, for detecting shifts
- Geo and device — country × sessions (is the audience where your customers are?) and deviceCategory, nothing finer
- Campaigns — source × medium × campaign, the report that makes UTM-tagged links legible. This one earns its place the hard way: most of a small B2B site’s traffic arrives as “Direct” because the places that actually send readers — LinkedIn’s app, DMs, email, clicks inside PDF documents — never pass a referrer. The only fix is on your side of the link: tag everything you distribute (
?utm_source=linkedin&utm_medium=carousel&utm_campaign=<piece>), and this report turns your biggest attribution black hole into named posts and formats. - GSC queries, pages, and countries — clicks, impressions, CTR, position, top 250 each
What’s deliberately not in the bundle: demographics. GA4’s age/gender dimensions require Google Signals and get privacy-thresholded into empty rows at small-site volume — you’d take on a consent-disclosure burden for data you literally cannot see. Skip them until you’re at thousands of users, and even then ask what editorial decision they’d change.
Everything lands in one analytics/snapshots/2026-07.json. This is what the agent actually reads — worth seeing in full shape (abridged here: arrays truncated, numbers illustrative, paths real):
{
"month": "2026-07",
"dateRange": { "startDate": "2026-07-01", "endDate": "2026-07-31" },
"property": "544552955",
"fetchedAt": "2026-08-03T07:02:41.000Z",
"totals": {
"sessions": 4180, "activeUsers": 2960, "newUsers": 2410,
"screenPageViews": 9740, "engagementRate": 0.52,
"averageSessionDuration": 214.6
},
"pages": [ // top 200, by views
{ "pagePath": "/standards/prebid-header-bidding/",
"screenPageViews": 1120, "activeUsers": 830,
"userEngagementDuration": 41830, "engagementRate": 0.61 },
{ "pagePath": "/building/ai-growth-operator/",
"screenPageViews": 640, "activeUsers": 210,
"userEngagementDuration": 20160, "engagementRate": 0.69 },
{ "pagePath": "/writing/the-composable-cdp-how-customer-data-stopped-moving/",
"screenPageViews": 480, "activeUsers": 350,
"userEngagementDuration": 9410, "engagementRate": 0.44 }
// ...
],
"landingPages": [ // × channel group
{ "landingPage": "/standards/retail-commerce-media-measurement/",
"sessionDefaultChannelGroup": "Organic Search",
"sessions": 310, "engagedSessions": 176, "bounceRate": 0.43 }
// ...
],
"channels": [
{ "sessionDefaultChannelGroup": "Organic Search",
"sessions": 1890, "activeUsers": 1420, "newUsers": 1260, "engagedSessions": 1030 },
{ "sessionDefaultChannelGroup": "Direct",
"sessions": 1240, "activeUsers": 780, "newUsers": 540, "engagedSessions": 590 }
// ...
],
"newVsReturning": [
{ "newVsReturning": "new", "activeUsers": 2410, "sessions": 2480, "engagementRate": 0.47 },
{ "newVsReturning": "returning", "activeUsers": 550, "sessions": 1700, "engagementRate": 0.61 }
],
"geo": [ // top 20 countries
{ "country": "United States", "sessions": 2890, "activeUsers": 2010, "engagedSessions": 1560 }
// ...
],
"devices": [
{ "deviceCategory": "desktop", "sessions": 3010, "activeUsers": 2140, "engagementRate": 0.55 },
{ "deviceCategory": "mobile", "sessions": 1170, "activeUsers": 820, "engagementRate": 0.44 }
],
"campaigns": [ // UTM-tagged arrivals, named
{ "sessionSource": "linkedin", "sessionMedium": "carousel",
"sessionCampaignName": "editorial-agent", "sessions": 340, "engagedSessions": 210, "newUsers": 180 }
// ...
],
"gsc": {
"site": "sc-domain:nofluffadvisory.com",
"queries": [ // top 250, by impressions
{ "query": "server side header bidding",
"clicks": 84, "impressions": 3120, "ctr": 0.0269, "position": 8.4 },
{ "query": "commerce media measurement standards",
"clicks": 31, "impressions": 1870, "ctr": 0.0166, "position": 9.5 }
// ...
],
"pages": [
{ "page": "https://nofluffadvisory.com/standards/prebid-header-bidding/",
"clicks": 210, "impressions": 11400, "ctr": 0.0184, "position": 9.8 },
{ "page": "https://nofluffadvisory.com/standards/audio-podcast-advertising/",
"clicks": 4, "impressions": 2410, "ctr": 0.0017, "position": 6.1 }
// ...
],
"countries": [
{ "country": "usa", "clicks": 240, "impressions": 12800, "ctr": 0.0188, "position": 9.6 }
// ...
]
}
}
Notice what the schema is quietly doing for the agent. Every array arrives pre-ranked, so “top pages” and “biggest queries” cost no computation. Per-page depth is one division away (userEngagementDuration / screenPageViews). The landing × channel cross diagnoses entry points without a second query. And the GSC block carries this essay’s favorite pattern in raw form — look at that last row: position 6.1, 2,410 impressions, four clicks. A page-one ranking with a broken snippet, visible straight from the JSON before any model gets involved.
Make Search Console failures non-fatal (write gsc: null and continue) — the two APIs have independent failure modes and half a snapshot beats none.
Step 3 — Memory Is a Folder of JSON
Here is where the system stops being a report generator and starts being an analyst: every snapshot and every report is committed. The agent’s context for month N includes months N−1 through N−5, plus its own previous reports.
That enables the two behaviors that make this worth building:
- Trend lenses. “This page was top-quartile for two months and just lost 40% of its views” is a refresh candidate — a judgment that requires history no dashboard widget volunteers.
- The scorecard. Every monthly report must open by grading the previous one: “We recommended rewriting the title on page X; its CTR went from 0.2% to 1.1%.” One line per past recommendation — worked, no effect, or too early to tell. An agent that never confronts its own track record converges on plausible-sounding advice. One that grades itself converges on advice that survives contact with the data. It also can’t quietly re-recommend last month’s ideas, because its own paper trail is in context.
Snapshots are small (tens of KB), so “committing data to git” is not the crime here it would be elsewhere. You get versioning, diffing, and a free audit trail for zero infrastructure.
Step 4 — The Rubric Is the Product
The agent’s prompt is not “analyze my analytics.” It is a versioned rubric file in the repo that pins five analytical lenses, a noise floor, and an output contract. The lenses:
- Decayers — pages that were top-quartile in prior months and lost >25% month-over-month. Refresh or re-link candidates.
- Overperformers — pages with rising traffic or engagement well above the site median. Double-down topics: follow-up pieces, better internal placement.
- Search opportunities — the GSC lens, highest leverage of the six: queries with impressions at positions 8–20 (an existing page is one revision from page one), decent positions with terrible CTR (title/description rewrites), and query clusters with no matching page at all (new content, with the proposed angle named).
- Hook problems — landing pages with real sessions but high bounce against the site median. The fix is the intro, not a new post.
- Channel shifts — mix changes big enough to alter what you do next. Only reported if actionable.
- Distribution & audience fit — which UTM-tagged posts delivered engaged sessions (and how much traffic went untagged, which means distribution happened but nobody instrumented it), whether the geography matches where the customers are, and device mix only if it’s extreme enough to change design decisions.
Plus the guardrails that keep an LLM honest: ignore anything under 10 sessions (noise floor), maximum five actions (selection pressure — five well-argued actions beat twenty observations), every claim must cite numbers from the snapshot, and never repeat a prior recommendation without flagging it as a repeat. The output format is pinned down to the heading level, which makes reports diffable month over month.
Because the rubric is a file, improving the analyst is a pull request. When a report whiffs, you don’t retrain anything — you edit the rubric and the next run is smarter. That file is the product; everything else is plumbing.
One rubric step is deliberately dormant: priors. The instruction reads, in effect, “once four monthly snapshots exist, mine your own history before recommending — which topics, formats, and title patterns converted impressions into clicks, which decayed fastest — state your strongest priors, and score this month’s candidates against them.” Until month four it silently skips; after that, every report opens with the agent’s accumulated beliefs about what works on this site, argued from its own committed evidence. That’s the honest version of “predictive content generation”: no model to train, no dimension to buy — just enough remembered outcomes, and a rubric that forces the analyst to bet on them.
Step 5 — The Orchestrator
GitHub Actions is the scheduler, secret store, executor, and delivery mechanism in one. The monthly workflow, condensed to its five working parts:
on:
schedule:
- cron: '0 7 3 * *' # 3rd of the month — GSC data lags ~2 days
workflow_dispatch:
inputs:
month: { required: false } # manual backfill: YYYY-MM
permissions:
contents: write # commit snapshots + reports
issues: write # deliver the review
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with: { node-version: '22' }
- name: Fetch analytics snapshot
run: node scripts/analytics-fetch.mjs --month "$MONTH"
env:
GA_SERVICE_ACCOUNT_JSON: ${{ secrets.GA_SERVICE_ACCOUNT_JSON }}
- name: Run the review agent
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
npm install -g @anthropic-ai/claude-code
claude -p "Read the rubric skill and follow it for month $MONTH. \
The snapshot is analytics/snapshots/$MONTH.json. \
Write the report to analytics/reports/$MONTH.md." \
--allowedTools "Read,Glob,Grep,Write"
test -s "analytics/reports/$MONTH.md" || exit 1
- name: Commit and deliver
run: |
git add analytics/
git commit -m "analytics: $MONTH review [skip ci]"
git push
gh issue create --title "Content review: $MONTH" \
--body-file "analytics/reports/$MONTH.md"
env:
GH_TOKEN: ${{ github.token }}
Three details that matter more than they look:
--allowedTools "Read,Glob,Grep,Write"is the agentic-zone boundary enforced. The agent can read the repo and write its report. It cannot call APIs, run shell commands, or touch the network. Least privilege isn’t just for the Google key.[skip ci]in the commit message, or the analytics commit will trigger your site deploy every month for no reason — and in the pathological case, workflows triggering workflows.test -son the report file: headless agents fail soft. Assert the artifact exists or you’ll get green checkmarks and no report.
Delivery through issues instead of email or Slack is a deliberate choice: issues are free, they notify, they hold Markdown, they have open/closed state (a review you’ve acted on gets closed), and the recommendations sit one click from the repo where you’ll implement them.
The Cadence Trap: Why Weekly Analytics Lies
My first instinct after the monthly loop worked: run it weekly. I publish weekly — why analyze monthly?
Because at a small site’s volume, weekly GA4 slices are statistical noise wearing a trend costume. At ~25 sessions a week, a page “dropping 40% week-over-week” went from five views to three. An agent handed a rubric and thin data will still produce five confident recommendations — they’ll just be wrong, differently, every week. Confidently narrated noise is worse than no report, because you’ll act on it. The failure mode of agentic analytics isn’t hallucination; it’s sincerity at the wrong sample size.
But one stream is weekly-viable even at small scale: Search Console impressions. Clicks are scarce; impressions aren’t — a single ranking page can log hundreds per week. And impressions are the earliest signal that new content is being seen by search, weeks before the first click. If you publish weekly, that’s the feedback loop worth tightening.
So the weekly pulse exists under a different contract than the monthly review. It fetches the just-finished Mon–Sun week and the week before (so the agent diffs inside one file), leans almost entirely on GSC, and its rubric is mostly prohibitions: three sections only — how newly published pieces are indexing, movers above a 20-impression floor, and anything that crossed into the click zone (position < 10) or earned a first click. Roughly 350 words. No recommendations allowed. The pulse reports signal; the monthly review does the thinking. And it posts as a comment on one rolling issue rather than opening a new one — 52 issues a year titled “Weekly pulse” is how you train yourself to ignore your own analyst.
Two timing details: run it Wednesday, because Search Console data isn’t stable for about 48 hours, so Sunday’s numbers are trustworthy by Wednesday morning. And cross-reference git log for what was published inside the window — the agent should say “the piece you shipped Tuesday isn’t in GSC yet; expected,” not silently omit it.
What the First Run Actually Found
The system’s first monthly report, on a site whose GA4 tag was five days old, opened with exactly the right caveat (baseline month, no trends yet) and then found the thing the dashboards never surfaced:
Two standards pages rank on page one — position 7.2 with 467 impressions, position 6.1 with 241 — and converted one click between them. At those positions the expected CTR is several percent. The snippet, not the ranking, is losing the click. Fix the titles before writing anything new.
That is a real diagnosis with a real, cheap fix, and it inverts the instinct every content creator has (write more!) into what the data supports (fix the front door first). It also flagged the site’s biggest impression driver sliding onto page two, an engagement overperformer worth a sequel, and — my favorite — noticed that some search queries arriving at the site were LLM-assistant prompts, one with a system prompt still embedded in it. The robots are already reading; the analyst noticed before I did.
The first weekly pulse, three days later, correctly reported five just-shipped landing pages as “not in GSC yet — expected,” a 4× impression jump on one standards page, and two pages crossing into the click zone. Thirty seconds to read, zero advice, exactly the contract.
The Pitfalls That Actually Bit
Field notes, in descending order of how much time each cost:
- Scheduled workflows only run from the repo’s default branch. If your real branch is
masterbut the repo’s default is a stalemain(old repos accumulate this), your cron will simply never fire — no error, no log, nothing. Check Settings → Default branch before trusting any schedule. - GSC lags ~48 hours. Run monthly on the 3rd, weekly on Wednesday. Run on the 1st and the month’s last two days are quietly missing.
- Empty is not broken. A GA4 property with no data returns a happy 200 and zero rows — same shape as a misconfigured property ID. Log totals on every fetch so “the tag wasn’t installed until the 7th” takes thirty seconds to diagnose instead of an afternoon.
[skip ci]or infinite loops. The workflow commits to the branch that triggers your deploys. Tag the commit or enjoy a deployment per report.- Assert the artifact.
claude -pcan complete without writing the file you asked for.test -s report.md || exit 1turns a silent failure into a red X. - Validate date inputs properly. My month regex accepted
2026-13on the first pass. Deterministic zone means deterministic, including the argument parsing.
What It Costs
- Google APIs: free at any volume a content site produces.
- GitHub Actions: each run is 2–3 minutes; even private-repo free tier covers ~50× this usage.
- Anthropic API: the only real cost — one monthly deep-read plus four small weekly pulses. Single-digit dollars per month, usually low single digits.
Call it a coffee per month for an analyst who never skips a shift, reads every number, remembers everything it ever told you, and confronts its own mistakes in writing.
The Tease: Closing the 360
Everything above stops at a recommendation. A human still reads “write the commerce-media explainer” and goes off to write it. That’s the right place to stop building first — but it’s not where this architecture ends, and it’s worth sketching the full shape, because the next ring is where it gets genuinely interesting.
The recommendation is a work order, and work orders can be handed to other agents — this is agent-to-agent orchestration, A2A, and the handoffs are already well-defined by the monthly report’s own structure. The review agent’s “query cluster with no matching page” becomes a deep-research agent’s assignment: sweep the sources, map what already ranks, find the angle nobody has taken. Its research brief becomes a creation agent’s input: a draft in the house voice, built against the same rubric discipline that keeps the analyst honest. The draft flows to curation. It publishes. And then the loop’s most elegant property kicks in: the published piece lands back in next month’s snapshot. Audience interest, discoverability, search impressions, trend movement — the same instruments that recommended the piece now grade its execution. The system self-corrects on the only feedback that matters: whether real people wanted the thing.
One design decision makes or breaks this ring, and it’s the same decision as before, one level up. In the analysis loop, the boundary was the agent never touches the APIs. In the creation loop, the boundary is the agents never touch publish. The human doesn’t sit inside this loop approving tickets — the human is the loop: the taste that kills the plausible-but-generic draft, the institutional knowledge no snapshot contains, the IP and lived operator experience that makes a piece worth reading rather than merely optimized, the deep understanding of advertising that knows why a trend is moving before the data can say so. Agents propose. The operator disposes. Everything upstream of judgment automates; judgment itself compounds.
I haven’t built this ring yet — deliberately. The analysis loop had to run long enough to prove its recommendations deserve execution before execution gets automated; automating a mediocre analyst just ships mediocrity faster. But every interface it needs already exists: the work orders are structured, the snapshots are committed, the scorecard already grades outcomes. When it lands, it will be the same architecture teaching the same lesson at full scale — end-to-end autonomous execution, from analytics to creation and back, with exactly one thing that never automates. The judgment in the middle.
Steal This Pattern
Nothing here is specific to websites. The shape is: deterministic collector → committed snapshot → rubric-driven agent with its own history in context → self-graded, scheduled delivery. The same five roles wire up to ad platform reports, CRM pipeline exports, app-store metrics, support-ticket queues — anywhere a human currently squints at a dashboard monthly and tries to remember what they decided last time.
The rubric file is where your judgment lives; version it like code, because it is. The scorecard is what keeps the agent honest; never skip it. And the cadence should follow the statistics, not the calendar anxiety — analyze at the speed your sample sizes support, pulse at the speed your leading indicators move.
Bonus: the Editorial 360, in build order
If you’re stealing the whole vision rather than one loop, build it in this sequence — each stage generates the evidence the next one runs on, and each has a gate you must pass before the next is worth building:
- The monthly analyst (a weekend). Gate: two consecutive reports whose recommendations you actually acted on. If you ignore your analyst, more automation won’t fix that.
- The weekly pulse (an evening). Gate: a month in, it still holds its word cap and you still read it. A pulse you skim past is a dashboard with a byline.
- Instrument distribution (an hour). UTMs on every link you post, link annotations in every PDF. Gate: your top acquisition source has a name — not “Direct”.
- Priors (a rubric edit, dormant until month four). Gate: the priors survive contact with the next month’s data instead of flattering last month’s.
- Research and creation agents (the A2A ring). Work orders flow from the review agent outward; drafts flow back to your curation gate. Gate: permanent — agents never touch publish.
- Close the loop. The published piece lands in the next snapshot, and the same instruments that proposed it now grade it. Nothing new to build — this stage is the reward for sequencing the first five correctly.
The order is the point. Automating creation before the analyst has proven its judgment just ships mediocrity faster; instrumenting distribution after you’ve built the creation ring means a quarter of flying blind on what worked. Evidence first, execution second — at every ring of the loop.
The dashboard was never the product. The judgment was. Automate the judgment, keep the accountability, and let the tab finally close.