01 — The loop
Six stages, and only one of them is a person
Blue steps are the machine. The coral step is where it stops and a human writes.
That is not caution for its own sake — §03 has the incident that caused it.
02 — The nine workflows
What each one does, and what it decides on
Everything runs in n8n. Nothing runs anywhere else except Supabase and the hand-run analysis scripts noted in §06.
| # | What it does | Runs | The decision it makes |
|---|---|---|---|
| 002 | Signal Spine — the daily engine. News in, scored opportunities out. | 07:00 daily | Scores everything, then two gates: ≥40 to the board, ≥60 to the digest |
| 004 | Normalise — forces every source into one 12-field shape. | called | Which topic (9 categories, first match wins), is our brand mentioned, which of 14 competitors |
| 014 | SEMrush — search demand for 12 tracked keywords, plus discovery. | Mondays | Drops keywords under 50/month; new keywords need 200/month; max 10 new per run |
| 015 | Search Console — our own search performance, both page and query level. | 07:30 daily | Keeps query rows at ≥10 impressions; page rows unfiltered |
| 009 | Brief Agent — turns one opportunity into a writable brief. | on demand | Picks the highest-scoring un-briefed opportunity, excluding Intel-Only |
| 010 | Web search — grounds the brief in current facts. | called | Up to 4 sources; if empty, the brief is written ungrounded rather than failing |
| 011 | Trend report — what is rising and dying, per topic and per company. | Mon 08:00 | Last 7 days vs the 7 before; needs ≥2 mentions to count |
| 012 | Outcome loop — did the published article do anything? | 09:00 daily | Verdict moved or flat at day 21 — see §05 |
| 003 | Error alert — catches failures from all of the above. | on failure | Writes the failure to the database first, then tries to notify |
Node-level detail — the spine, step by step
002 is generated code, not hand-built in the n8n canvas: scripts/build-spine-workflow.mjs emits it. Edit the builder, never the workflow — a redeploy overwrites UI edits.
Fetch Google News RSS (query built from the entity registry, when:7d) → parse and cap at 25 items, body text truncated to 500 chars → normalise via 004 → on Mondays also pull SEMrush via 014 → combine → dedup within the run → fetch the seen-URL ledger → keep only new → build the scoring prompt → Claude (or the heuristic fallback) → parse and score → gate at 40 → write to Sheet + Supabase → mark processed → gate at 60 → build the digest → post → log.
Two failure behaviours worth knowing. If Claude returns something unparseable and nothing can be salvaged, the run fails loudly and leaves the signals unmarked so the next run retries them — a broken scorer must never look like a quiet news day. If Claude is simply unavailable, the run silently falls back to the fixed formula and reports scorer: heuristic; the ceiling is then 70, so nothing can reach the "Immediate" tier.
The ledger is built from what entered the prompt, never from URLs the model echoed back — otherwise a hallucinated URL would permanently suppress a real article.
No URLs are ever sent to the model. Clusters reference signals by index number. This is not a privacy rule: the corporate network's inspection stalls large high-entropy payloads, and 25 long Google News redirect URLs in a prompt was the cause of a multi-day hang in July.
How a new source gets added — and why it is cheap
Every source writes into the same 12-field shape via 004, so adding one is a new ingest workflow plus one line in the spine — not a change to scoring, dedup, the board, briefs or measurement. That is why SEMrush took days rather than weeks, and why Reddit is already built and waiting only on credentials.
004 also holds the parts most worth arguing about with the SEO team: the topic dictionary (9 categories, ordered specific to generic, first match wins), the competitor list (14 companies with alias patterns — Genesys includes PureCloud, Salesforce includes Agentforce), and the brand matcher. Cognigy counts as NiCE, never a competitor. Bare "NICE" only counts when a CX word is nearby and no medical or smart-home word is — otherwise NHS guidance and Nice the city would flood the board.
03 — Scoring
Where the number comes from
Two parts. A fixed formula does the ranking; the model is allowed a bounded adjustment on top.
| Component | Max | How it is computed |
|---|---|---|
| Topical relevance | 25 | Lookup by topic: AI agents 25 · Contact center 22 · Enterprise AI 21 · Voice AI 20 · Customer experience 18 · Analyst recognition 18 · Workforce management 16 · Competitive landscape 15 · Market moves 14. Anything else scores 8. |
| Presence | 15 | 8 if we are mentioned, plus 3 per competitor mentioned, capped at 7 |
| Engagement | 15 | Logarithmic, so a 10,000-share story does not drown out everything else |
| Recency | 15 | Under 24h 15 · under 48h 10 · under 7 days 5 · older 0. An unreadable date also scores 0 — never assume "now" |
| Fixed subtotal | 0–70, fully reproducible — the same input always gives the same number | |
| Claude's adjustment | −15…+30 | One integer per cluster, clamped in code. It may reward newsworthiness, search-demand potential, a format gap, customer proof or corroboration across several signals — and is explicitly told not to re-score relevance, presence, engagement or recency |
So the measurable half moved into arithmetic and the model kept the judgement half, bounded. The side effect proved the point: through the three weeks the AI budget was exhausted, every part of this kept working — more conservatively, but it kept working. The same principle is why the search analysis in §08 lives in database functions rather than prompts.
What the score turns into
| Score | Status | What happens |
|---|---|---|
| ≥ 80 | Immediate | Board, digest, top of the brief queue |
| ≥ 60 | Weekly Queue | Board and the daily digest |
| ≥ 40 | Monitor | Board only |
| < 40 | dropped | Recorded as seen so it never costs us again, then discarded |
| any | Intel-Only | Overrides every tier. Anything about share price, valuation, earnings or layoffs stays on the board as market intelligence and is never briefed — no enterprise competitor publishes that content and neither do we |
manager_score column. Set it and the brief queue uses yours instead — but the machine's score is never overwritten. The disagreements are kept deliberately: once there are enough of them they become the dataset for recalibrating the weights above, replacing judgement with observation.
04 — Writing
How an article gets written, and exactly where the machine stops
What the brief contains
Nine fields, produced in one pass, grounded in a live web search where one is available.
| Field | What it is |
|---|---|
| Headline angle | A real editorial angle NiCE can credibly own — not a restatement of the topic |
| Hook | One scroll-stopping line |
| Target keywords | 3–6 terms a buyer would actually type |
| Content formats | 1–3 recommended formats, each with a short outline |
| LinkedIn post | Publish-ready: 120–200 words, hook, one concrete insight, CTA, 2–4 hashtags |
| Key messages | 3–5 points to land |
| CTA, owning team, priority | Priority is now / this-week / backlog — "now" only when genuinely timely or competitive |
It lands in three places at once: the running Google Doc the writers work in, Supabase, and the Content Briefs sheet. The Doc entry ends with the line that closes the loop — the webhook call to paste the URL back once it is published.
The brief writer's instructions, verbatim
This is the actual system prompt, unedited. It is worth reading because most of it is editorial policy, not technique — and policy is the part you may want to change.
You are a senior content strategist for NiCE — the enterprise customer-experience / contact-center AI company (flagship product: CXone; recently acquired Cognigy, agentic/ conversational AI — treat Cognigy as part of NiCE, never a competitor). Main competitors: Genesys, Five9, Talkdesk, Verint, Cisco. EDITORIAL SCOPE — hard rule: NiCE is an enterprise B2B vendor. Never produce content angles about stock price, valuation, earnings, or layoffs (NiCE's or anyone's) — no enterprise competitor publishes such content and neither does NiCE. If the opportunity has a financial-news framing, reframe the brief around the underlying product/market/ customer story instead. Your job: turn ONE scored market opportunity into a ready-to-execute content brief the organic/content team can act on today. Be concrete and specific to NiCE's positioning — no generic marketing filler. Confident, expert B2B voice. When the input includes a 'LIVE WEB SEARCH' section, use it to ground the brief in current facts — pull in concrete, specific details (figures, quotes, recent developments) it supports, and fact-check your claims against it. Never invent statistics, quotes, or dates that the search results or opportunity data do not support; if unconfirmed, keep the claim general. Rules: - STYLE: never use em dashes (—) or en dashes (–) anywhere in your output. - headline_angle must be a real editorial angle NiCE can credibly own, not a restatement. - The LinkedIn post must be genuinely publish-ready: strong first-line hook, one concrete insight, a clear CTA, 120-200 words, 2-4 relevant hashtags. - target_keywords must be realistic search terms a buyer would actually type. - priority: 'now' only if timely/competitive, else 'this-week' or 'backlog'. - Return ONLY the structured fields requested.
The output is validated against a strict schema. If the model returns something malformed, a second repair call runs automatically before anything fails.
The em-dash rule is enforced twice — asked for in the prompt, then stripped in code regardless. Em dashes are the tell that makes a manager stop reading and say "this was AI".
The scoring instructions, verbatim
Sent as one message per run, with the day's signals numbered beneath it. Note the explicit prohibition on re-scoring what the formula already covers.
You are an organic-intelligence analyst for NiCE — enterprise CX / contact-center AI (flagship product: CXone; recently acquired Cognigy, agentic/conversational AI — Cognigy news is NiCE news, not competitor news). Competitors include Genesys, Five9, Zendesk, Salesforce, ServiceNow, Google CCAI, Zoom CX, Amazon Connect, Sprinklr, Talkdesk, Verint, Cisco, Avaya, 8x8. EDITORIAL SCOPE: NiCE is an enterprise B2B vendor. Clusters centered on stock price, valuation, earnings or layoffs are market intelligence, not content material — no competitor publishes such content and neither do we. Score them honestly for awareness, but never frame recommended_formats or why_it_matters around share price or financial performance. INPUT: a numbered list of raw signals collected today. Each signal carries a deterministic base_score (0–70) already covering topical relevance, brand/competitor presence, engagement and recency. TASKS: 1. Cluster the signals into distinct topics/opportunities (merge near-duplicates). 2. For each cluster, classify trigger_type — one or more of: Search Demand | Conversation | Format Gap | PR/Narrative | Customer Signal. 3. score_adjustment: one integer from -15 to +30 per cluster. Reward what the base cannot see: PR/newsworthiness, search-demand potential, format-gap severity, customer proof, multi-signal corroboration. Penalize thin, duplicative or off-strategy stories. Do NOT re-score relevance, presence, engagement or recency — the base already covers them. 4. why_it_matters: ≤2 sentences, concrete. 5. recommended_formats (array), primary_team, supporting_teams (array). 6. Include every cluster — the pipeline computes final = base + adjustment and filters at 40 itself.
Could it write the draft too?
Yes, and here is the honest shape of it rather than a pitch.
| What it would take | Effort | The real question |
|---|---|---|
| A drafting step after the brief, grounded in the same web search plus our own top-performing pages | Days, not weeks — the brief already contains the angle, outline, keywords and messages | Not "can it" but "who edits it". An unedited draft is worse than no draft |
| A house style guide the model can be held to | Depends entirely on whether one exists in writing | Without it, output is generic — which is the failure mode everyone fears and correctly dislikes |
| A review gate before anything reaches a CMS | Small | Non-negotiable. The system should never be able to publish |
05 — Measuring
How we would know it worked, and why we do not know yet
Everything in this section works. None of it has been exercised on real content. That is the single most important thing on this page for anyone asking about measurable impact.
What happens when an article is registered
| Step | Detail |
|---|---|
| Register | One call with the brief ID and the published URL. This is the only human step in the loop. |
| Wait | 21 days by default, adjustable per article from 0 to 90. |
| Check 1 — does it rank | Searches the brief's own target keywords and looks for the URL in the results. |
| Check 2 — did we gain share of voice | Compares our share-of-voice change against the top competitor's change over the same period. We must gain more than 2 points relative to them — so a rising tide does not read as our win. |
| Check 3 — are AI engines citing it | Checks whether the URL started being cited after publication. |
| Verdict | moved if any check passes · flat if none do. Evidence for every check is stored, not just the verdict. |
| If flat | The topic re-enters the board automatically as a Refresh at score 55 — high enough to be briefed again with a new angle, not high enough to trigger a digest. That arrow is what makes it a loop rather than a report. |
What we can prove today
The baseline, which is the part that took real work and is genuinely unusual to have.
| Asset | Scale | Why it matters |
|---|---|---|
| 500 days of our own search performance | 8.4M rows | Google keeps ~16 months and deletes the oldest day daily. This history now exists only in our Supabase database and cannot be bought back from any vendor |
| Page-level totals | reconcile exactly | Re-summing our stored data returns the identical figure Google reports — the data is trustworthy, not approximately right |
| Every opportunity ever scored | — | With its base score and the model's adjustment kept separately, so the weights can be recalibrated against outcomes later |
| Trend velocity per topic and per company | weekly | Rising and dying, computed from signals we collected ourselves |
What is needed to close the gap: publish and register roughly ten briefs. At that point the scoring weights stop being a considered guess and start being fitted to observed outcomes. One more dependency: our share-of-voice index was last refreshed 2026-06-17, so checks 2 and 3 would currently return "no data since publication" even for a registered article.
06 — Automation
What runs itself, and the honest answer about how reliably
| Workflow | When | Timezone |
|---|---|---|
| 002 Signal Spine | 07:00 daily | New York |
| 015 Search Console ingest | 07:30 daily | Belgrade |
| 011 Trend report | Mon 08:00 | server default |
| 012 Outcome review | 09:00 daily | server default |
| 014 SEMrush | Mondays | triggered by the spine |
| 009 Brief agent | on demand | — |
This is not theoretical. Nothing ran from 31 July to 3 August. Every workflow was active the whole time; the process was not up. Nobody noticed for four days, because the alerting path writes to Supabase and posts to a test address that discards it.
Moving n8n to a server is the single change that would make "it runs automatically" true without qualification. Everything else on this page is sound and sits on that foundation.
Two scheduling details that are wrong today and should be fixed
The Search Console ingest does not run after the spine, despite being named that. The trigger node is literally called "Daily 07:30 — after the spine", but the spine is pinned to New York and the ingest to Belgrade. In August that is 11:00 UTC versus 05:30 UTC — the ingest fires about five and a half hours before the spine.
It causes no harm today because search data does not yet feed the spine. It will cause a silent wrong-order bug the moment it does. Pin both to one timezone.
Two workflows have no timezone set at all (trend and outcome review) and inherit whatever the server has. Worth making explicit before the pipeline moves anywhere.
What is still done by hand
Nineteen distinct steps. These are the honest ones, not a tidy summary.
Every time n8n starts
- Start it with a specific certificate flag, or every Supabase write fails while Search Console reads keep working — a corporate proxy intercepts
supabase.coand notgoogleapis.com - Keep the machine awake
- Restart by enumerating processes, never by killing one — Windows orphans a child process that then hijacks the next start
The content loop
- Register every published article by hand. Nothing detects publication. This is the step that has never been taken for a real piece
- Run the weekly page-competition snapshot manually
- Run the six search analyses by hand — there is no digest narrating them yet
Configuration
- Paste SQL into the Supabase SQL editor for any schema change — the pipeline is REST-only and deliberately cannot alter its own schema
- Edit the topic-relevance list directly in Supabase, table
gsc_icp_terms(no redeploy needed) - Create credentials; share new Google Docs with the service account
- Drag a workflow between folders — the API cannot do it
07 — The dials
Where to change each thing
Two of these are editable without touching code or redeploying, which makes them the natural first place to tune with the SEO team.
| What you want to change | Where it lives | Needs a deploy? |
|---|---|---|
| Which queries count as "our category" | Supabase table gsc_icp_terms | No — edit rows directly |
| A manual score override on an opportunity | Supabase column manager_score | No |
| The 12 tracked SEMrush keywords and 4 discovery seeds | workflow 014 | Yes |
| Scoring weights, the 40 / 60 gates, the Intel-Only rule, the model | the spine builder script | Yes — regenerate then deploy |
| Topic dictionary, competitor list, brand matching | workflow 004 + the entity registry | Yes — run the sync script |
| The 21-day review window | workflow 012, or per article at registration | Per article: no |
| Trend windows (7 vs 7 days, ≥2 mentions) | workflow 011 | Yes |
| All credentials and secrets | one env file → n8n credentials | Run the credential script |
mape and unknown caller — but it is one person's judgement sitting in a table, and it silently shapes what the whole search side surfaces. It is a table precisely so it can be argued about and edited without an engineer.
08 — GEO
The part of search that is changing, and what we actually know
Only measurements here. Where we do not know something, it says so.
| What we measured | Finding | What it means |
|---|---|---|
| Google Discover presence | zero | Three entire Google surfaces return nothing for us. For a vendor publishing this much thought leadership, that is a strategic gap worth naming |
| Google News presence | zero | |
| Video presence | zero | |
| Keywords where an AI Overview appears | 848 | Identified by SEMrush on our domain and never examined. Cross-referencing these against our zero-click queries would size the AI Overview problem instead of guessing at it |
| Citations by AI engine | Perplexity 24 · Claude 3 · Gemini 3 · GPT 3 | We are cited eight times more often by one engine than by the others. Nobody has investigated why, and it is a genuine lead |
| Whether an AI Overview is taking our clicks | unknown | Search Console cannot answer this at all. SEMrush can, and it is one afternoon of work |
The measurement to settle it exists in a tool we already pay for. That is the highest-value unstarted work on this page.
09 — Opportunities
What to do, split by how long it takes to know if it worked
This week
- Check the SERP features on our zero-click queries. One afternoon in SEMrush against the 848 AI-Overview keywords. Settles the strategy fork in §08
- Consolidate
ai call center. Six of our own pages compete for it; the glossary entry outranks the commercial page; between them they earned one click. This is a page-merge, not a writing job - Publish and register three briefs. Starts the measurement loop that everything else waits on
- Agree the category term list with SEO so the search side stops running on one person's judgement
This quarter
- Move n8n off a laptop. Turns "it runs automatically" from mostly-true into true
- Wire a real alerting channel. The four-day silent outage is the argument; it is a URL and minutes of work
- Feed measured click loss into the refresh loop. Today a page is re-briefed when a search probe cannot find it; we now have the real click history to trigger on actual decline instead
- Weekly narration of the search analyses so the six queries in §06 arrive as a readable summary rather than something someone must remember to run
Once there is outcome data
- Recalibrate the scoring weights against what actually moved. The weights in §03 are considered guesses until then, and we kept the data to fix that
- Decide on draft generation — with evidence about which briefs produce results, not on enthusiasm
- Connect market demand to our own visibility: what the market searches for versus what we are visible for. The data for both halves now exists
10 — Limits
What this does not do
Is everything really in n8n?
Almost. All nine workflows are, and they are the operating system. Two things sit outside it: the Supabase SQL functions that compute the search analyses (deliberately — they run over millions of rows, which is a database's job, and they kept working while the AI budget was out), and a handful of hand-run scripts for the initial history load and the weekly page-competition snapshot.
Nothing is hidden in a spreadsheet formula or someone's local notebook.
How much does it cost to run?
The AI scoring and brief writing are the only per-use cost, and both are bounded — at most 40 signals per day enter the prompt, and one brief is generated per request. Search Console is free within a daily quota we use about a tenth of. SEMrush consumes prepaid units, capped per run. Supabase is roughly $25/month on the Pro tier. There is no per-seat licence and no new vendor.
What is the most likely way this produces something wrong?
Two ways, both known.
The topic dictionary is the weak point. Anything it does not recognise scores 8 out of 25 on relevance, which usually keeps it off the board. A genuinely important story in a category nobody listed is the most likely miss — and it fails quietly, as an absence.
The category term list on the search side is one person's judgement and shapes what surfaces there. Both are editable, both should be reviewed by someone who owns the category.
Can it publish anything by itself?
No, and it has no credential that would let it. It writes to a Google Doc, a spreadsheet, Supabase and a chat channel. It has no CMS access and no ability to obtain any. That is a deliberate boundary, not an oversight, and it should stay that way even if draft generation is added.
What would break it tomorrow?
The laptop being closed — that has already happened. Everything else fails safely: if the AI is unavailable, scoring falls back to arithmetic; if the web search fails, the brief is written ungrounded; if SEMrush errors, the news run continues; if the chat channel is unreachable, the digest is still stored.
The uncomfortable version: it fails quietly and well, which is correct for production and wrong for getting a human's attention. That is what the alerting channel in §09 fixes.
→ What the search data says (007) → The same, as diagrams (008) → What is still blocked (006) → All documents