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, fix it from the brand's own material, and publish a verified page every line of which traces to a document they supplied; then the next one. Use for "let's go through the Verification Loop", "Verification Loop", "fill another gap", "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 → a draft, checked claim by claim → a live page → the next one. 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 |
| The draft and the evaluation | senso-generate-verify |
| Publishing | senso-publish |
| The close | references/round-recap-and-next-steps.md |
| The accuracy round | references/check-accuracy-of-answers-naming-brand.md |
| 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 a claim the models make is true, whether the draft is right, whether to publish. "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 I'll draft a page from it; that's what puts 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 |
| an evaluation | a check of every claim in the draft against your context layer | it's what makes the page a verified source — every line traceable to something you gave us |
| publishing / citeables.com | a domain Senso runs that AI agents crawl for answers | a public URL models can cite; nothing on your own site changes |
| 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. Drafting, evaluation and publishing all
work with both false. 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 — a verdict on 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.
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 |
This check runs however the user arrived — from the router, or by saying "Verification Loop" straight to this skill. It is three calls; 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 four 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 content verification --status draft --output json --quiet | jq -c '{total_count, draft_count, items: .items[0:10]}'
senso evals runs --subject-type content --limit 100 --output json --quiet | jq -c '{total, items: .items[0:20]}'
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 draft 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
item closest to being live — 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).
Your context layer now answers "", with a citation to your docs page — that's what any agent on your team would get, not just you.
Next I'll draft a page from it — that's what puts your answer where models can find and cite it. You'll check every line before it goes anywhere.
"Not just you" is the line worth spending — the shared context layer in four words. No self-serve phrases here: "to add anything else, just say save this to Senso" mid-round reads as a question about a task they are not doing, and a user who takes it for one scrolls back hunting for what they missed. The phrases belong in the close.
Load senso-generate-verify and follow it from 3. Write the draft. Setup already created the
content type and settled the brand kit; the author check still runs there, before the first
draft. The loop's own rules on this step:
senso-generate-verify. The loop adds one: an evaluation with unsupported
claims goes back — to the draft, or to the knowledge base through step 3's doors — never forward
to publish.Load senso-publish → Before publishing. The evaluation result is the landed half and the
best evidence in the round — not "eval passed" but "every claim in that page traces to a
document you gave us; that's what makes it the verified source you asked for" — the opener's ask,
kept. Never citable, which describes what a model might do.
"" is checked — five of five claims backed by your knowledge base. Publishing puts it on citeables.com, a domain Senso runs that AI agents crawl for answers — not your website; nothing on your own site changes. No call-to-action card, and it can be unpublished. Publish it?
Options: Publish to citeables.com (Recommended) · Not yet.
citeables.com is the default and the only
thing put in front of them; on a new organization nothing is selected and the module passes the id
explicitly — never the flag or the 400 in the user's hearing. destinations add is out of scope
for the whole loop, even when they ask what else there is; the other Senso domains are not
recited. A user who names one unprompted gets it.Read references/round-recap-and-next-steps.md. Three lines that bookend the opener, the URL,
one pointer at geo.senso.ai, the count of what is left as arithmetic, the four phrases. One close,
one template — a second closing message that opens on "that's one" is the same beat twice.
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". Offered at the close, never run on
a first pass. When they take it, read references/check-accuracy-of-answers-naming-brand.md —
one answer that names them, one evaluation, the verdicts, one or two "is this true?" questions.
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. Knowledge base writes are senso-context-layer's;
drafting and evaluation senso-generate-verify's; publishing senso-publish's; the gap report
senso-gap-report's. 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