Senso's Verification Loop — find a question people in the brand's industry ask AI models where a competitor gets named and the brand does not, get the answer into the brand's own knowledge base, then offer to fix the gap — a verified page, on the Optimization tier; then the next question. Use for "let's go through the Verification Loop", "Verification Loop", "fill another gap", "find another question", "what am I missing from", "how do I rank", "am I being cited", "who's beating us", "where do we stand", "how are our tracked questions doing", "what do the models say about us" and "check what the models say about us" — and when the senso-quickstart router sends a user here. Runs the setup skill first when the organization is not set up. Not for a user who named material to save (senso-context-layer), and not for the gap report on its own (senso-gap-report).
npx @senso-ai/shipables install senso-ai/senso-verification-loopOne round: where they stand → a question the models answer without them → their knowledge base answers it → fix the gap, which is the Optimization tier → the next question. Identical for an organization set up a minute ago and one that has run it for months. This file owns the order, the transitions and the messages; the mechanics live in the references and the modules:
| Step | Read / load |
|---|---|
| The standings — the opener's first message | references/standing-and-competitors-data.md, then references/standing-and-competitors-message.md |
| The question — the opener's second message | references/find-question-brand-is-missing-from.md, references/check-knowledge-base-covers-question.md, then references/standing-and-competitors-message.md again |
| The tracked questions — how they are doing | references/check-tracked-questions-performance.md |
| Importing the question | references/offer-question-and-start-tracking.md |
| Filling the knowledge base | references/fill-missing-answer-in-knowledge-base.md, with senso-context-layer for the writes |
| Fixing the gap — the Optimization tier | Step 5, below — senso request-upgrade fix-answer-gaps |
| The close | references/round-recap-and-next-steps.md |
| The accuracy round — also the Optimization tier | The accuracy round, below |
| Any number said aloud | references/metric-definitions.md — read it before quoting a score |
Never hijack a stated request. "Save revenue.pdf to Senso" is a save — senso-context-layer's
— even on an organization mid-round.
Lead. Do not ask permission to continue. Between steps, say what happens next and what it gets them, then do it. The questions that stay questions are the ones only the user can answer: whether to be the verified source for the finding, whether they want a different question, how a gap gets filled, whether to fix the gap, whether to ask for the upgrade, whether to look for another question. "Shall I continue?" is never one of them.
Every step closes before the next one opens — two lines, both mandatory. Landed, with a number or a name in it; Next, with what it gets them. A "next" without a "so that" is not a transition. "Your context layer now answers that, with a citation to your docs page — that's what any agent on your team would get. Next is fixing the gap — a page with your answer, where models can find and cite it." is the pattern every boundary copies. Both live in the message that ends the turn — the one that asks the user something. A Landed line written between two tool calls is not produced; a step with no question of its own lands at the top of the next message that has one.
Say what each thing is the first time it comes up — and what it buys them, one clause each, once, at the point the term lands in a quoted template — never a glossary up front:
| First time you mention… | what it is | what it buys them |
|---|---|---|
| a tracked question | a question Senso runs against the AI models to see how they answer it | from now on you'll see it change — this is how you'll know any of this worked |
| a gap | a question your industry asks that your context layer can't answer yet | every one is a customer getting sent somewhere else; closing it is the whole job |
| the Optimization tier | the part of your GEO plan that fixes gaps: a page drafted from your context layer, every claim checked against it, published where AI models look for answers | it turns the answer now in your context layer into a page the models can cite |
| a verified source | a page where every line traces back to your own documents | the difference between content and evidence |
One exception, and it is the only one: "verified source" in the opener's ask. "Want to be the verified source for that question?" carries no clause, because the beats above it have just shown the question, who was named and that the user was not — the phrase reads off that ledger. The exception is the position, not the term. Anywhere else it takes its clause.
Never narrate the machinery — and the line during a wait is in their terms. No rules recited, no ids, no internal checks, no retries that worked; a retry is invisible. One short line per wait, about what is happening for them: "Checking where you stand in your industry…", "Finding a question you're missing from, and checking whether your knowledge base already answers it…", "Checking how your tracked questions are doing…". The defect is a line about your own work — "Running the entry check", "Reading the standings references", "Running the standings batch now" — which names skills, files, batches and checks the user has never heard of. What the wait produced comes in the message that ends the turn, never as a progress report.
Never report a setting the user did not ask about. Read name, websites, industry_id,
industry_name out of org get and nothing else. enable_content_generation and
content_auto_publish are false on every new organization, that is correct, and they block
nothing — they switch on scheduled unattended generation, and nothing in this flow needs them.
Never surface them as a blocker; never run generate update-settings.
A choice is offered as options, not as a sentence. With Claude Code's AskUserQuestion, every
two-to-four-answer decision goes through it, yes/no included; the recommended option first, and
the recommendation in the message body too. Free text — what to change in a draft, a real
figure, a path — is never boxed. Without the tool (Codex, Cursor), the quoted prose is the ask. A dismissed
box is not an answer: restate the doors as text once and take a spoken reply.
More than two facts is a list, never a paragraph. Consecutive lines collapse when rendered — that
is "a wall of text", and it is our markdown, not the model. A blank line between items or a -
on each is the whole fix. The opener's first two lines are the one exception, so the ledger
stays the only list in that message. The thing the message is about gets its own line.
Energy comes from the payoff, not from adjectives. A real number, a named competitor, a capability that just switched on. "Now for the exciting part!" is hype where a fact would do.
The user names every path they give you. The standings file this flow writes is named by you and announced, not asked about.
Credits are not a preflight. A 402 anywhere → contact Senso at senso.ai for support on credit
issues, and stop at that step — except the upgrade request in step 5, which has its own line and
names no website.
senso org get --output json --quiet # industry_id present?
senso brand-kit get --output json --quiet # guidelines.brand_name == org name?
senso kb stats --output json --quiet # total_files — anything for the loop to check against?
| Result | Do |
|---|---|
No industry_id | Not set up. Load senso-verification-loop-setup and follow it; it hands back here when the chores are done, and the opener then opens with the setup close. Say nothing about the loop yet |
total_files is 0 | Nothing in the knowledge base — the loop's whole job is checking whether their material answers a question, and there is none. Load senso-verification-loop-setup; it runs only the chores that are missing and returns |
industry_id set, name differs from brand_name or looks internal ("test", "acme's workspace") | Setup's brand-name chore is missing — load senso-verification-loop-setup; it runs only that chore and returns |
| All three fine | The loop. Continue below |
If the router did not hand you updates, add senso whoami --output json --quiet to these
reads. The close uses it: references/round-recap-and-next-steps.md → Update out-of-date
skills — before the first close.
This check runs however the user arrived — from the router, or by saying "Verification Loop" straight to this skill. It is three calls, or four; it is not the state check below, and the opener's batch does not run until it passes.
The standings batch, on arrival: the two position reads in
references/standing-and-competitors-data.md — industries brand, industries domain, and
industries brands when the domain is not cited — and the two state reads below, in one call.
Seconds, and no judgement. The standings message goes out on them and ends the turn. The pick
does not start until the user answers — pulling it forward is the old shape, and it cost the user
five minutes before the first word.
senso gaps list --status all --limit 10 --output json --quiet | jq -c '{total, gaps: .gaps[0:10]}'
senso prompts list --limit 100 --output json --quiet | jq -c '{total, texts: [.prompts[].text[0:80]]}' # the tracked line and door
Project every read in the batch — the raw six came back as 13,500 characters and sat in front
of a forty-second pause before two lines were written. The two position reads are projected in
references/standing-and-competitors-data.md; a field a beat does not name never enters context.
The question batch, on their yes: the prompts page through its jq projection
(references/find-question-brand-is-missing-from.md) — the pick is the first row that passes its
two filters — then one classification search on that question. Two calls and no judgement; the
page is never read row by row. Reading fifty rows to choose a better question cost a minute and
a half of silence and chose a different question every run on the same data. A topic the user
named is the one case the questions are read, and that file owns it.
A returning organization's name is a proxy, not a confirmation. If the diagnostic comes back with nothing recognisable — no mentions at all on a brand with a real site and a real industry — suspect the name before the data.
Opening a returning user on "you have a question you didn't finish" hands them an obligation with
no stake — no standing, no competitor, no reason it matters today. The standings always run. Their
box holds four: two fixed doors, a third when they track questions, and the free slot for the one
gap closest to fixed — Offer the most advanced piece of unfinished work, and only one.
When the state reads come back with anything in them, read
references/offer-unfinished-work-on-return.md for which item takes the slot and the one line
above the box that names it.
Read references/standing-and-competitors-data.md for what the reads mean, then
references/standing-and-competitors-message.md immediately before writing either message. An
opener written without the skeleton in front of you comes out as one paragraph, the question named
instead of quoted, and no ask. That is not a bad paragraph; it is the product failing on the one
screen where someone decides whether it works. Read
references/standing-and-competitors-message-examples.md once per session for the register and
the three shapes beat 4 takes; do not write a fourth example anywhere.
The standings, on arrival — beats 1 and 2, then the ask: "Want to see a question they answer without naming you?" It goes out on the standings batch alone, seconds after they arrive, and ends the turn. The pick has not run and must not — the old shape ran the whole pick before the first word, and the user waited five minutes for it.
Options: Find a question I'm missing from (Recommended) · Show the full standings · Check how
my tracked questions are doing — the third only when prompts list is not empty, and then the
standings carry one line: "You're tracking 4 questions already." Plus the free slot on a
return, above. "Show the full standings" goes to a file, the only one this flow writes to their
disk — its shape is in references/standing-and-competitors-message.md.
The question, on their yes — the first question they are missing from, quoted on its own line,
the ledger, what the search found, and the ask about the finding, never about which prompt: "Want
to be the verified source for that question?" The question batch runs first. The pick is
mechanical and the user judges relevance: "show me other questions" is the next candidate,
"something about " reads the industry's questions for a fit, "we don't do that" deletes
it — all in references/find-question-brand-is-missing-from.md.
Options: Be the verified source (Recommended) · Show me other questions I'm missing from.
Read references/check-tracked-questions-performance.md. Only when they track questions, and
only when asked — the door on the standings, or "how are our tracked questions doing" on entry.
One analytics prompts call, sorted weakest first, and one table: the question, how often it was
asked in the window, how many answers named them, how many cited their site. It ends on the
weakest one — "Want to fix that one?" — and a yes makes it the pick: the classification search,
then step 3 with it. Nothing is imported; it is already tracked.
"Adding this question means Senso starts tracking it — it'll run against the AI models from here
on, and you'll see how they answer it." Choosing the question was the decision; no cost talk, no
second yes/no. Mechanics in references/offer-question-and-start-tracking.md.
The search already ran — the classification before the question message — so show that result; do not run it
again. Read references/fill-missing-answer-in-knowledge-base.md for both outcomes: answered
(the citation, then the model's answer held against it — the one place response_text is ever
shown) and missed (the three doors, the unguessable specific, the verifying search).
One picker carries the whole finding, because the text before a picker is not produced: on a live test the comparison step 3 prepared — what the model says against what their page says — never reached the screen, and the user went from "start tracking" straight to "want to fix the gap?" with no idea what was wrong. A Fix the gap picker without the stats and the model's answer is the defect. Its question, plain text, in this order:
<N> times in the last <window> days, named in none of them, and who
was: "Google, Bing and Semrush were named; you weren't."Options: Fix the gap (Recommended) · Not now.
industries answers row step 3 read. No new call for this message.Nothing is drafted in this flow. No senso engine draft, no evaluation, no publish — not "to
show what it would look like", not after the upgrade request goes in. Writing the page anyway is
the defect: fixing the gap is the Optimization tier, and this step is where the user hears that.
Fixing gaps is part of the Optimization tier on your GEO plan — the page drafted from your context layer, every claim checked, published where AI models cite it. Your organization isn't on it yet. Want me to ask the Senso team to upgrade you? They'll follow up with you directly.
Options: Request the Optimization upgrade (Recommended) · Not now.
The picker's question carries it the same way: "Fixing gaps is part of the Optimization tier on your GEO plan: a page drafted from your context layer, every claim checked, published where AI models cite it. Want me to ask the Senso team to upgrade you? They'll follow up directly."
On "Request the Optimization upgrade":
senso request-upgrade fix-answer-gaps --note "Raised from the Verification Loop on: <question>" --output json --quiet
# → { requestId, request, requestType, category, alreadyRequested, createdAt, orgName }
alreadyRequested | Say |
|---|---|
false | "Done — the Senso team has your request and will follow up about the Optimization tier." |
true | "You already asked on <createdAt, as a date> — the Senso team has it and will follow up. I didn't send it twice." |
402 or any error — say it plainly in the close's gap line: "the upgrade request didn't go
through — ask Senso for the Optimization tier plan upgrade." Do not retry it in a loop, and do not
name a website or an app page.On "Not now": one line — "No problem — the answer stays in your context layer either way." — then the close.
Either answer ends the round. Step 6 follows in the same message.
Read references/round-recap-and-next-steps.md. What the round did, in their terms, then one
ask: whether to find another question to track. One close, one template.
Entry: the industry is set and prompts exist. Not that the last round finished cleanly — a page may have just gone live, or the user may have stopped mid-draft last week; either way they arrive here, and anything unfinished rides in the free slot.
The accuracy round — "check what the models say about us". It checks the answers that name them
against their documents, which is an evaluation, and evaluations are the Optimization tier too.
Never offered at the close. When they ask for it, say so in the step 5 shape — "Checking what the
models say about you against your own documents is part of the Optimization tier" — and on a yes
run senso request-upgrade factual-accuracy --note "Raised from the Verification Loop" --output json --quiet, read the same way. Do not run references/check-accuracy-of-answers-naming-brand.md.
No setup. Material, industry, brand name, content type, brand kit — senso- verification-loop-setup, which this skill calls when its entry check fails and never duplicates.
No commands of its own beyond the reads above and step 5's upgrade request. Knowledge base
writes are senso-context-layer's; the gap report senso-gap-report's. Nothing is drafted,
evaluated or published from here — step 5 is where fixing the gap goes, and it is the Optimization
tier. Do not restate a module's commands here and do not improvise them — if a
module cannot be loaded, stop at the end of the last step that could run and say so.
Inside the loop this file's wording wins for hand-offs and the close. Each module carries its own for when it runs alone; say the loop's version, never both.
| They want | Skill |
|---|---|
| Set or change the industry or the brand name | senso-verification-loop-setup |
| Save, find or organize a document | senso-context-layer |
| The gap report on its own | senso-gap-report |
| Draft or check content, outside a round | senso-generate-verify |
| Publish, retry, unpublish, citations | senso-publish |
| To get started, path not yet chosen | senso-quickstart |
references/troubleshooting.md — prompt import, answers, the model-name mapping, the GEO gate