nqzai — unit test report

2026-09-20 12:57:46 · commit 40fa7eb · vitest 4.1.10 · gate npm run check
100% pass rate
tests11057
passed11057
failed0
files · suites737 · 3249
duration30011ms

tests by area

Core4951SEO · Keywords2766Chat · Feedback2276Billing · Cost752Email211Middleware · Dispatch101

outcome

11057
passed
0
failed
0
skipped
100%
pass rate

speed (per test)

10389
< 5ms
447
5–20ms
200
20–100ms
21
> 100ms

test layers

Unit tests · Vitest
Deterministic pure-logic — this report.
11057 tests / 737 files
Static guards · npm run check
tenant-scoping, graphql-seams, tool-registry, cost-refs, provider-guards, …
123 checks
LLM quality eval · Claude-as-judge
5 flows
Integration smokes · prod scripts
Live-hitting; manual, secret-gated, not in the push gate.
chat · rls

per-file detail

answer-shapes.vitest.ts
480/480 217ms · 6 suites PASS
src/chat/answer-shapes.vitest.ts
answer-shapes: each predicate fires on its own question · 199 tests
q14 matches: "my pages are indexed but get no impressions"3.1ms
q14 matches: "why do my pages get no clicks"0.8ms
q14 matches: "my pages rank but nobody clicks"0.2ms
q14 matches: "pages are indexed but impressions are tiny, what is actually wrong"0.2ms
q16 matches: "should we prune or merge the underperforming content"1.6ms
q16 matches: "which old blog posts should we delete"0.7ms
q16 matches: "is it worth rewriting our thin articles or killing them"0.2ms
q02 matches: "how do we run a technical seo audit and decide what to fix first"1.3ms
q02 matches: "what should we fix first from the crawl issues"0.8ms
q02 matches: "how do we prioritise the technical seo backlog"0.3ms
q05 matches: "how healthy is the site for crawling and indexation"0.6ms
q05 matches: "what is the state of our indexing"0.3ms
q05 matches: "are there problems with our crawling and rendering"0.1ms
q12 matches: "how should we fix duplicate content and canonicals"0.4ms
q12 matches: "what is our policy for url parameters and facets"0.3ms
q12 matches: "we have a url explosion problem from filters, how do we handle it"0.1ms
q04 matches: "what is the competitive landscape and where can we realistically win"1.6ms
q04 matches: "which competitors can we actually beat"0.8ms
q04 matches: "where is the whitespace against our competition"0.3ms
q25 matches: "a competitor consistently outranks us for the terms that matter, what is the gap we can close this quarter"0.3ms
q25 matches: "we are falling behind one competitor on our main keywords"0.1ms
q25 matches: "how do we close the gap with the competitor ahead of us"0.1ms
q25 matches: "which competitor gaps can we fix this quarter"0.1ms
q25 matches: "how do we beat the competitor that outranks us"0.1ms
q25 matches: "can we win back the terms we are falling behind on against our main rival"0.1ms
q25 matches: "we lost ground against a competitor this year, what should we do about it"0.1ms
q24 matches: "does our content demonstrate e-e-a-t for this niche"0.4ms
q24 matches: "how do we prove our expertise on these pages"0.5ms
q24 matches: "do we have enough trust signals on our content"0.3ms
q24 matches: "our authorship is missing, how do we establish credibility"0.1ms
q09 matches: "which backlinks should we earn, keep, or ignore"0.7ms
q09 matches: "should we disavow the low quality links pointing at us"0.4ms
q09 matches: "what should our link building strategy be"0.1ms
q09 matches: "is our link profile risky"0.1ms
q03 matches: "which keywords should we actually target given intent and revenue"0.7ms
q03 matches: "which search terms are worth targeting this year"0.3ms
q03 matches: "help me prioritise the keywords we are tracking"0.1ms
q03 matches: "what keywords should we stop targeting"0.1ms
q29 matches: "what should our robots.txt and sitemap actually contain"0.6ms
q29 matches: "should we block these pages in robots txt or noindex them"0.3ms
q29 matches: "our sitemap and robots file seem to disagree"0.2ms
q29 matches: "what rules should the sitemap follow"0.2ms
q10 matches: "how will we measure seo success and prove roi"1.3ms
q10 matches: "what kpis should we report on for organic search"0.7ms
q10 matches: "how do i justify the seo spend to the business"0.3ms
q10 matches: "how do we prove search is worth it"0.1ms
q27 matches: "can we use ai to draft content and stay eligible"2.3ms
q27 matches: "is it safe to use ai generated content on our blog"1.1ms
q27 matches: "what should our policy be for ai written articles"0.2ms
q27 matches: "will ai content get us penalised"0.1ms
q17 matches: "how long will seo take and what will it cost"0.9ms
q17 matches: "what happens to our rankings if we stop doing seo"0.4ms
q17 matches: "how many months before organic traffic moves"0.1ms
q17 matches: "if we pause content for a quarter what do we lose"0.1ms
q07 matches: "how do we migrate the site without losing organic traffic"0.7ms
q07 matches: "we are moving to a new domain, how do we keep our rankings"0.5ms
q07 matches: "what do we need to do for seo before a redesign"0.2ms
q07 matches: "replatforming the cms — what breaks in search"0.9ms
q19 matches: "how do we work with developers so seo recommendations actually ship"0.6ms
q19 matches: "our seo tickets never get done by engineering"0.3ms
q19 matches: "how do we get the dev team to implement these fixes"0.1ms
q20 matches: "how should our seo strategy change over the next few years"0.4ms
q20 matches: "what is our long term strategy as search becomes generative"0.4ms
q20 matches: "where should we invest in search over three years"0.1ms
q26 matches: "how do i explain this to a non-technical exec"0.2ms
q26 matches: "how do we present a delayed seo result to the board"0.1ms
q26 matches: "how should i brief the ceo on this"0.1ms
q30 matches: "how should seo and paid search work together"0.5ms
q30 matches: "our social and email and seo teams compete with each other"0.3ms
q30 matches: "how do we coordinate organic and ads"0.1ms
q18 matches: "how do we rank in cities where we have no office"0.6ms
q18 matches: "how do we show up in nearby towns we serve"0.2ms
q18 matches: "we want to target more locations, how do we rank there"0.1ms
q15 matches: "this page ranks well but nobody converts"0.5ms
q15 matches: "our landing pages get traffic but bounce is high"0.6ms
q15 matches: "the money page ranks on page one and conversions are weak"0.1ms
q22 matches: "is our crawl budget being wasted"0.5ms
q22 matches: "which url patterns should we block from crawling"0.2ms
q22 matches: "googlebot is spending time on pages that do not matter"0.1ms
q23 matches: "which core web vitals failures actually matter for us"0.3ms
q23 matches: "is page speed worth fixing on our site"0.2ms
q23 matches: "what should we fix for lcp and cls"0.1ms
q23 matches: "which core web vitals problems should we fix first"0.1ms
q23 matches: "what core web vitals issues should we prioritise"0.1ms
q08 matches: "how should we structure our topic clusters"0.5ms
q08 matches: "how do we organise internal links to build topical authority"0.3ms
q08 matches: "what should our site architecture look like for these topics"0.1ms
q13 matches: "how do we expand into other countries without cannibalising ourselves"0.5ms
q13 matches: "should we launch a german language version of the site"0.4ms
q13 matches: "we want to enter a new market, how do we rank there"0.1ms
q06 matches: "how do we recover from a core update hit"0.3ms
q06 matches: "we think we got hit by the helpful content update, what now"0.2ms
q06 matches: "how do we recover from a manual action"0.2ms
q11 matches: "are ai overviews taking our clicks"1.4ms
q11 matches: "how do ai answers affect our click through rate"0.7ms
q11 matches: "we are losing clicks to generative search"0.1ms
q11 matches: "are google ai overviews reducing traffic to our site"0.1ms
q11 matches: "did overviews cause our organic traffic drop"0.1ms
q11 matches: "our impressions are up and clicks are down since overviews appeared"0.2ms
q21 matches: "which rich results can we win"0.6ms
q21 matches: "should we add faq schema to our pages"0.2ms
q21 matches: "is our structured data valid and worth expanding"0.2ms
q28 matches: "should we be doing video for seo"0.5ms
q28 matches: "how do we optimise our youtube videos for search"0.2ms
q28 matches: "is video worth it for our search traffic"0.1ms
q28 matches: "should we invest in video for search"0.1ms
q28 matches: "is it worth investing in youtube for organic traffic"0.1ms
ai21 matches: "how do wikipedia and wikidata affect whether ai engines cite us"0.3ms
ai21 matches: "is our brand recognised as an entity by the knowledge graph"0.2ms
ai21 matches: "does our knowledge panel affect ai citations"0.1ms
ai21 matches: "does our schema sameas list affect whether ai engines cite us"0.1ms
ai21 matches: "how does entity markup change how ai engines recognise our brand"0.1ms
ai19 matches: "which passages can an ai engine actually quote from our pages"0.4ms
ai19 matches: "what makes a passage extractable by ai"0.2ms
ai19 matches: "why do ai answers paraphrase us instead of quoting us"0.2ms
ai19 matches: "is our markup why nothing of ours gets quoted by ai"0.1ms
ai19 matches: "how do we make our content liftable by ai engines"0.1ms
ai02 matches: "why do some llms confidently cite us while others ignore us entirely"0.8ms
ai02 matches: "why does chatgpt cite us but gemini does not"0.5ms
ai02 matches: "why do the engines disagree about whether to mention us"0.2ms
ai02 matches: "some ai assistants name us and others never do — why"0.3ms
ai02 matches: "which engines cite us and which ignore us"0.4ms
ai25 matches: "how do we win best x and comparison prompts without becoming a listicle farm"0.5ms
ai25 matches: "who gets cited when someone asks ai for the best tools in our category"0.3ms
ai25 matches: "should we publish a best-of roundup to win ai comparison prompts"0.1ms
ai25 matches: "are we in the top-10 listicles that llms cite"0.1ms
ai25 matches: "can we win the best-x comparison prompts our buyers ask chatgpt"0.1ms
ai26 matches: "what is the relationship between classic top rankings and the chance of being cited"1.0ms
ai26 matches: "does ranking number one mean ai will quote us"0.5ms
ai26 matches: "do rankings still matter now that ai answers the question"0.7ms
ai26 matches: "are rankings dead"0.1ms
ai26 matches: "we rank in the top 3 but chatgpt never cites us — why"0.6ms
ai26 matches: "is our google ranking connected to whether perplexity mentions us"0.3ms
ai04 matches: "is it technically possible to track whether chatgpt or perplexity mention us"1.3ms
ai04 matches: "can we even measure if ai assistants cite our site"0.6ms
ai04 matches: "is there a way to monitor whether gemini recommends us"0.1ms
ai04 matches: "how do we track mentions in ai answers"0.1ms
ai04 matches: "how would we know if chatgpt is citing our pages"0.1ms
ai04 matches: "can we measure whether ai assistants name us without buying a tool"0.1ms
ai04 matches: "is it possible to see if perplexity surfaces our brand"0.1ms
ai04 matches: "why can we not track whether chatgpt mentions us"0.1ms
ai01 matches: "how do we measure success when ai gives the complete answer and the click never happens"0.3ms
ai01 matches: "what should we report now that ai answers the question without a click"0.2ms
ai01 matches: "how do we measure zero-click success"0.1ms
ai01 matches: "what do we judge informational pages on when nobody clicks"0.1ms
ai13 matches: "does focusing on ai search cannibalize our traditional seo programme"0.7ms
ai13 matches: "will geo work hurt our existing seo"0.4ms
ai13 matches: "is ai search at the expense of classic organic rankings"0.1ms
ai13 matches: "does ai visibility work come at the cost of our seo programme"0.1ms
ai22 matches: "how do we attribute pipeline from ai exposure when sessions send no referrer"0.2ms
ai22 matches: "how do we prove revenue from ai search visibility"0.1ms
ai22 matches: "can we tie deals back to ai answers"0.1ms
ai22 matches: "how do we attribute revenue when chatgpt sends no referrer"0.1ms
ai24 matches: "how should untranslated or multi-market content be handled when citation follows language"0.2ms
ai24 matches: "do we need translated pages to get cited in other languages"0.1ms
ai24 matches: "will our english pages get cited in german ai answers"0.1ms
ai24 matches: "should we localise our content for ai visibility in other languages"0.1ms
ai14 matches: "should our b2b ai search strategy differ from b2c when we want visibility inside the tools"0.7ms
ai14 matches: "do b2b and b2c need different geo playbooks"0.4ms
ai14 matches: "is ai visibility different for enterprise buyers than for consumers"0.2ms
ai14 matches: "our b2b and b2c buyers ask llms different things — should the playbook differ"0.6ms
ai16 matches: "how do we optimise technical documentation so ai tools recommend our use cases"0.6ms
ai16 matches: "why does chatgpt cite our marketing pages instead of our docs"0.3ms
ai16 matches: "are our api docs even readable by ai crawlers"0.1ms
ai16 matches: "what stops our documentation being quoted by ai assistants"0.1ms
ai05 matches: "how does query fan out change how we structure long form content"0.1ms
ai05 matches: "should we write one long page or a page per sub-query for ai search"0.1ms
ai05 matches: "do ai engines break one question into several retrievals"0.1ms
ai07 matches: "how do we meet e-e-a-t so ai systems treat us as a primary source not a recap"0.7ms
ai07 matches: "why do the engines quote the journalist who wrote about us instead of us"0.4ms
ai07 matches: "are we being treated as a middleman by ai answers"0.2ms
ai20 matches: "how should we publish our original data so engines cite us instead of a recap"0.5ms
ai20 matches: "is our gated pdf study hurting us with ai search"0.3ms
ai20 matches: "where should our benchmark findings live so they get picked up"0.1ms
ai09 matches: "how do gbp reviews local citations and reddit mentions change whether ai recommends us"0.4ms
ai09 matches: "does our g2 profile matter for ai recommendations"0.2ms
ai09 matches: "do reviews influence which vendors llms pick"0.1ms
ai09 matches: "do reddit threads affect whether chatgpt recommends us"0.1ms
ai06 matches: "what is the practical difference between ranking for keywords and being chosen for conversational prompts"0.5ms
ai06 matches: "how does keyword ranking differ from being cited in ai answers"0.3ms
ai06 matches: "should our briefs list keywords or prompts"0.1ms
ai15 matches: "what happens to market share if rivals industrialise ai search before we do"0.2ms
ai15 matches: "what if our competitors get to ai visibility first"0.2ms
ai15 matches: "are we falling behind competitors on ai answers"0.1ms
ai08 matches: "what do we do when a model hallucinates about our brand"0.2ms
ai08 matches: "chatgpt is saying something false about us"0.2ms
ai08 matches: "ai answers keep getting our company wrong"0.1ms
ai11 matches: "should we hire a specialist ai search agency or can our team adapt"7.5ms
ai11 matches: "do we need an aeo agency or can we do geo in house"0.4ms
ai11 matches: "is an ai visibility retainer worth it versus hiring"0.1ms
ai12 matches: "what does an ai search audit check that a technical seo audit misses"0.2ms
ai12 matches: "how does a geo audit differ from a normal technical audit"0.1ms
ai12 matches: "what extra does an aeo audit add beyond our seo audit"0.1ms
ai17 matches: "what should our geo measurement contract contain"0.5ms
ai17 matches: "which aeo kpis should we report instead of a vendor blended score"0.2ms
ai17 matches: "how should we measure ai visibility without a vendor score"0.1ms
ai18 matches: "should we publish llms.txt and allow ai crawlers"0.3ms
ai18 matches: "do we block gptbot and google-extended or allow them"0.2ms
ai18 matches: "what is our policy on ai bots reading the site"0.1ms
answer-shapes: near-misses route nowhere · 32 tests
nothing matches: "make me a video about widgets"1.2ms
nothing matches: "run a speed test"0.3ms
nothing matches: "check my page speed"0.3ms
nothing matches: "build me a new website"0.3ms
nothing matches: "write me an article with ai"0.2ms
nothing matches: "generate a blog post about widgets"0.2ms
nothing matches: "draft the copy using ai"0.3ms
nothing matches: "can we generate a new article with ai"0.3ms
nothing matches: "should you write me an ai blog post"0.5ms
nothing matches: "submit my sitemap"0.3ms
nothing matches: "resubmit the sitemap to google"0.3ms
nothing matches: "show me my organic traffic"0.2ms
nothing matches: "show me my backlinks"0.1ms
nothing matches: "check my backlinks"0.1ms
nothing matches: "run a backlink scan"0.2ms
nothing matches: "what is the backlink gap against acme.com"0.2ms
nothing matches: "find keywords for my business"0.1ms
nothing matches: "find keyword ideas worth targeting"0.1ms
nothing matches: "research which keywords are worth targeting"0.2ms
nothing matches: "keyword ideas for dental implants"0.2ms
nothing matches: "what is the search volume for kyc software"0.2ms
nothing matches: "track these keywords"0.1ms
nothing matches: "what is the keyword gap between us and acme.com"0.1ms
nothing matches: "how do we close the gap with competitor rival-brand.io this quarter"0.2ms
nothing matches: "write me an article about dental implants"0.2ms
nothing matches: "run an audit"0.3ms
nothing matches: "find my competitors"0.2ms
nothing matches: "fix my indexing"0.1ms
nothing matches: "what is a canonical tag"0.2ms
nothing matches: "hi"0.1ms
nothing matches: "how is my domain authority compared to theirs"0.2ms
nothing matches: "show me my backlinks"0.1ms
answer-shapes: NO SENTENCE MATCHES TWO PREDICATES · 231 tests
exactly one match for [q14] "my pages are indexed but get no impressions"0.4ms
exactly one match for [q14] "why do my pages get no clicks"0.3ms
exactly one match for [q14] "my pages rank but nobody clicks"0.2ms
exactly one match for [q14] "pages are indexed but impressions are tiny, what is actually wrong"0.2ms
exactly one match for [q16] "should we prune or merge the underperforming content"0.2ms
exactly one match for [q16] "which old blog posts should we delete"0.2ms
exactly one match for [q16] "is it worth rewriting our thin articles or killing them"0.1ms
exactly one match for [q02] "how do we run a technical seo audit and decide what to fix first"0.3ms
exactly one match for [q02] "what should we fix first from the crawl issues"0.2ms
exactly one match for [q02] "how do we prioritise the technical seo backlog"0.3ms
exactly one match for [q05] "how healthy is the site for crawling and indexation"0.2ms
exactly one match for [q05] "what is the state of our indexing"0.2ms
exactly one match for [q05] "are there problems with our crawling and rendering"0.1ms
exactly one match for [q12] "how should we fix duplicate content and canonicals"0.1ms
exactly one match for [q12] "what is our policy for url parameters and facets"0.3ms
exactly one match for [q12] "we have a url explosion problem from filters, how do we handle it"0.1ms
exactly one match for [q04] "what is the competitive landscape and where can we realistically win"0.2ms
exactly one match for [q04] "which competitors can we actually beat"0.1ms
exactly one match for [q04] "where is the whitespace against our competition"0.1ms
exactly one match for [q25] "a competitor consistently outranks us for the terms that matter, what is the gap we can close this quarter"0.2ms
exactly one match for [q25] "we are falling behind one competitor on our main keywords"0.1ms
exactly one match for [q25] "how do we close the gap with the competitor ahead of us"0.1ms
exactly one match for [q25] "which competitor gaps can we fix this quarter"0.2ms
exactly one match for [q25] "how do we beat the competitor that outranks us"5.3ms
exactly one match for [q25] "can we win back the terms we are falling behind on against our main rival"0.2ms
exactly one match for [q25] "we lost ground against a competitor this year, what should we do about it"0.3ms
exactly one match for [q24] "does our content demonstrate e-e-a-t for this niche"0.2ms
exactly one match for [q24] "how do we prove our expertise on these pages"0.3ms
exactly one match for [q24] "do we have enough trust signals on our content"0.2ms
exactly one match for [q24] "our authorship is missing, how do we establish credibility"0.1ms
exactly one match for [q09] "which backlinks should we earn, keep, or ignore"0.2ms
exactly one match for [q09] "should we disavow the low quality links pointing at us"0.2ms
exactly one match for [q09] "what should our link building strategy be"0.2ms
exactly one match for [q09] "is our link profile risky"0.2ms
exactly one match for [q03] "which keywords should we actually target given intent and revenue"0.2ms
exactly one match for [q03] "which search terms are worth targeting this year"0.2ms
exactly one match for [q03] "help me prioritise the keywords we are tracking"0.3ms
exactly one match for [q03] "what keywords should we stop targeting"0.2ms
exactly one match for [q29] "what should our robots.txt and sitemap actually contain"0.2ms
exactly one match for [q29] "should we block these pages in robots txt or noindex them"0.2ms
exactly one match for [q29] "our sitemap and robots file seem to disagree"0.1ms
exactly one match for [q29] "what rules should the sitemap follow"0.2ms
exactly one match for [q10] "how will we measure seo success and prove roi"0.2ms
exactly one match for [q10] "what kpis should we report on for organic search"0.2ms
exactly one match for [q10] "how do i justify the seo spend to the business"0.2ms
exactly one match for [q10] "how do we prove search is worth it"0.2ms
exactly one match for [q27] "can we use ai to draft content and stay eligible"0.2ms
exactly one match for [q27] "is it safe to use ai generated content on our blog"0.2ms
exactly one match for [q27] "what should our policy be for ai written articles"0.2ms
exactly one match for [q27] "will ai content get us penalised"0.2ms
exactly one match for [q17] "how long will seo take and what will it cost"0.2ms
exactly one match for [q17] "what happens to our rankings if we stop doing seo"0.2ms
exactly one match for [q17] "how many months before organic traffic moves"0.2ms
exactly one match for [q17] "if we pause content for a quarter what do we lose"0.2ms
exactly one match for [q07] "how do we migrate the site without losing organic traffic"0.2ms
exactly one match for [q07] "we are moving to a new domain, how do we keep our rankings"0.3ms
exactly one match for [q07] "what do we need to do for seo before a redesign"0.2ms
exactly one match for [q07] "replatforming the cms — what breaks in search"5.8ms
exactly one match for [q19] "how do we work with developers so seo recommendations actually ship"0.4ms
exactly one match for [q19] "our seo tickets never get done by engineering"0.2ms
exactly one match for [q19] "how do we get the dev team to implement these fixes"0.3ms
exactly one match for [q20] "how should our seo strategy change over the next few years"0.3ms
exactly one match for [q20] "what is our long term strategy as search becomes generative"0.2ms
exactly one match for [q20] "where should we invest in search over three years"0.2ms
exactly one match for [q26] "how do i explain this to a non-technical exec"0.4ms
exactly one match for [q26] "how do we present a delayed seo result to the board"0.4ms
exactly one match for [q26] "how should i brief the ceo on this"0.2ms
exactly one match for [q30] "how should seo and paid search work together"0.3ms
exactly one match for [q30] "our social and email and seo teams compete with each other"0.6ms
exactly one match for [q30] "how do we coordinate organic and ads"0.2ms
exactly one match for [q18] "how do we rank in cities where we have no office"0.3ms
exactly one match for [q18] "how do we show up in nearby towns we serve"0.2ms
exactly one match for [q18] "we want to target more locations, how do we rank there"0.2ms
exactly one match for [q15] "this page ranks well but nobody converts"0.2ms
exactly one match for [q15] "our landing pages get traffic but bounce is high"0.2ms
exactly one match for [q15] "the money page ranks on page one and conversions are weak"0.2ms
exactly one match for [q22] "is our crawl budget being wasted"0.2ms
exactly one match for [q22] "which url patterns should we block from crawling"0.2ms
exactly one match for [q22] "googlebot is spending time on pages that do not matter"0.2ms
exactly one match for [q23] "which core web vitals failures actually matter for us"0.2ms
exactly one match for [q23] "is page speed worth fixing on our site"0.2ms
exactly one match for [q23] "what should we fix for lcp and cls"0.8ms
exactly one match for [q23] "which core web vitals problems should we fix first"0.2ms
exactly one match for [q23] "what core web vitals issues should we prioritise"0.3ms
exactly one match for [q08] "how should we structure our topic clusters"0.4ms
exactly one match for [q08] "how do we organise internal links to build topical authority"0.2ms
exactly one match for [q08] "what should our site architecture look like for these topics"0.2ms
exactly one match for [q13] "how do we expand into other countries without cannibalising ourselves"0.2ms
exactly one match for [q13] "should we launch a german language version of the site"0.3ms
exactly one match for [q13] "we want to enter a new market, how do we rank there"0.2ms
exactly one match for [q06] "how do we recover from a core update hit"0.5ms
exactly one match for [q06] "we think we got hit by the helpful content update, what now"0.2ms
exactly one match for [q06] "how do we recover from a manual action"0.3ms
exactly one match for [q11] "are ai overviews taking our clicks"0.3ms
exactly one match for [q11] "how do ai answers affect our click through rate"0.2ms
exactly one match for [q11] "we are losing clicks to generative search"0.2ms
exactly one match for [q11] "are google ai overviews reducing traffic to our site"0.2ms
exactly one match for [q11] "did overviews cause our organic traffic drop"0.2ms
exactly one match for [q11] "our impressions are up and clicks are down since overviews appeared"0.2ms
exactly one match for [q21] "which rich results can we win"0.2ms
exactly one match for [q21] "should we add faq schema to our pages"0.3ms
exactly one match for [q21] "is our structured data valid and worth expanding"0.2ms
exactly one match for [ai21] "how do wikipedia and wikidata affect whether ai engines cite us"0.2ms
exactly one match for [ai21] "is our brand recognised as an entity by the knowledge graph"0.2ms
exactly one match for [ai21] "does our knowledge panel affect ai citations"0.2ms
exactly one match for [ai21] "does our schema sameas list affect whether ai engines cite us"0.2ms
exactly one match for [ai21] "how does entity markup change how ai engines recognise our brand"0.2ms
exactly one match for [ai06] "what is the practical difference between ranking for keywords and being chosen for conversational prompts"0.2ms
exactly one match for [ai06] "how does keyword ranking differ from being cited in ai answers"0.2ms
exactly one match for [ai06] "should our briefs list keywords or prompts"0.2ms
exactly one match for [ai15] "what happens to market share if rivals industrialise ai search before we do"0.2ms
exactly one match for [ai15] "what if our competitors get to ai visibility first"0.2ms
exactly one match for [ai15] "are we falling behind competitors on ai answers"0.9ms
exactly one match for [ai09] "how do gbp reviews local citations and reddit mentions change whether ai recommends us"0.2ms
exactly one match for [ai09] "does our g2 profile matter for ai recommendations"0.4ms
exactly one match for [ai09] "do reviews influence which vendors llms pick"0.2ms
exactly one match for [ai09] "do reddit threads affect whether chatgpt recommends us"0.2ms
exactly one match for [ai26] "what is the relationship between classic top rankings and the chance of being cited"0.2ms
exactly one match for [ai26] "does ranking number one mean ai will quote us"0.2ms
exactly one match for [ai26] "do rankings still matter now that ai answers the question"0.2ms
exactly one match for [ai26] "are rankings dead"0.2ms
exactly one match for [ai26] "we rank in the top 3 but chatgpt never cites us — why"0.9ms
exactly one match for [ai26] "is our google ranking connected to whether perplexity mentions us"0.3ms
exactly one match for [ai04] "is it technically possible to track whether chatgpt or perplexity mention us"0.4ms
exactly one match for [ai04] "can we even measure if ai assistants cite our site"0.2ms
exactly one match for [ai04] "is there a way to monitor whether gemini recommends us"0.3ms
exactly one match for [ai04] "how do we track mentions in ai answers"0.2ms
exactly one match for [ai04] "how would we know if chatgpt is citing our pages"0.2ms
exactly one match for [ai04] "can we measure whether ai assistants name us without buying a tool"0.2ms
exactly one match for [ai04] "is it possible to see if perplexity surfaces our brand"0.2ms
exactly one match for [ai04] "why can we not track whether chatgpt mentions us"0.3ms
exactly one match for [ai01] "how do we measure success when ai gives the complete answer and the click never happens"0.3ms
exactly one match for [ai01] "what should we report now that ai answers the question without a click"0.2ms
exactly one match for [ai01] "how do we measure zero-click success"0.2ms
exactly one match for [ai01] "what do we judge informational pages on when nobody clicks"1.6ms
exactly one match for [ai13] "does focusing on ai search cannibalize our traditional seo programme"10.2ms
exactly one match for [ai13] "will geo work hurt our existing seo"0.7ms
exactly one match for [ai13] "is ai search at the expense of classic organic rankings"0.4ms
exactly one match for [ai13] "does ai visibility work come at the cost of our seo programme"0.4ms
exactly one match for [ai22] "how do we attribute pipeline from ai exposure when sessions send no referrer"0.3ms
exactly one match for [ai22] "how do we prove revenue from ai search visibility"0.3ms
exactly one match for [ai22] "can we tie deals back to ai answers"0.3ms
exactly one match for [ai22] "how do we attribute revenue when chatgpt sends no referrer"0.3ms
exactly one match for [ai24] "how should untranslated or multi-market content be handled when citation follows language"0.3ms
exactly one match for [ai24] "do we need translated pages to get cited in other languages"0.3ms
exactly one match for [ai24] "will our english pages get cited in german ai answers"0.3ms
exactly one match for [ai24] "should we localise our content for ai visibility in other languages"0.3ms
exactly one match for [ai14] "should our b2b ai search strategy differ from b2c when we want visibility inside the tools"0.2ms
exactly one match for [ai14] "do b2b and b2c need different geo playbooks"0.2ms
exactly one match for [ai14] "is ai visibility different for enterprise buyers than for consumers"0.2ms
exactly one match for [ai14] "our b2b and b2c buyers ask llms different things — should the playbook differ"0.6ms
exactly one match for [ai16] "how do we optimise technical documentation so ai tools recommend our use cases"0.6ms
exactly one match for [ai16] "why does chatgpt cite our marketing pages instead of our docs"0.2ms
exactly one match for [ai16] "are our api docs even readable by ai crawlers"0.2ms
exactly one match for [ai16] "what stops our documentation being quoted by ai assistants"0.2ms
exactly one match for [ai05] "how does query fan out change how we structure long form content"0.2ms
exactly one match for [ai05] "should we write one long page or a page per sub-query for ai search"0.2ms
exactly one match for [ai05] "do ai engines break one question into several retrievals"0.2ms
exactly one match for [ai07] "how do we meet e-e-a-t so ai systems treat us as a primary source not a recap"0.2ms
exactly one match for [ai07] "why do the engines quote the journalist who wrote about us instead of us"0.2ms
exactly one match for [ai07] "are we being treated as a middleman by ai answers"0.2ms
exactly one match for [ai20] "how should we publish our original data so engines cite us instead of a recap"0.2ms
exactly one match for [ai20] "is our gated pdf study hurting us with ai search"0.2ms
exactly one match for [ai20] "where should our benchmark findings live so they get picked up"0.2ms
exactly one match for [ai25] "how do we win best x and comparison prompts without becoming a listicle farm"0.2ms
exactly one match for [ai25] "who gets cited when someone asks ai for the best tools in our category"0.2ms
exactly one match for [ai25] "should we publish a best-of roundup to win ai comparison prompts"0.2ms
exactly one match for [ai25] "are we in the top-10 listicles that llms cite"0.5ms
exactly one match for [ai25] "can we win the best-x comparison prompts our buyers ask chatgpt"0.2ms
exactly one match for [ai02] "why do some llms confidently cite us while others ignore us entirely"0.2ms
exactly one match for [ai02] "why does chatgpt cite us but gemini does not"0.2ms
exactly one match for [ai02] "why do the engines disagree about whether to mention us"0.2ms
exactly one match for [ai02] "some ai assistants name us and others never do — why"0.4ms
exactly one match for [ai02] "which engines cite us and which ignore us"0.2ms
exactly one match for [ai19] "which passages can an ai engine actually quote from our pages"0.2ms
exactly one match for [ai19] "what makes a passage extractable by ai"0.2ms
exactly one match for [ai19] "why do ai answers paraphrase us instead of quoting us"0.2ms
exactly one match for [ai19] "is our markup why nothing of ours gets quoted by ai"0.2ms
exactly one match for [ai19] "how do we make our content liftable by ai engines"0.2ms
exactly one match for [ai08] "what do we do when a model hallucinates about our brand"2.6ms
exactly one match for [ai08] "chatgpt is saying something false about us"0.3ms
exactly one match for [ai08] "ai answers keep getting our company wrong"0.2ms
exactly one match for [ai11] "should we hire a specialist ai search agency or can our team adapt"0.2ms
exactly one match for [ai11] "do we need an aeo agency or can we do geo in house"0.2ms
exactly one match for [ai11] "is an ai visibility retainer worth it versus hiring"0.2ms
exactly one match for [ai12] "what does an ai search audit check that a technical seo audit misses"0.3ms
exactly one match for [ai12] "how does a geo audit differ from a normal technical audit"0.3ms
exactly one match for [ai12] "what extra does an aeo audit add beyond our seo audit"0.2ms
exactly one match for [ai17] "what should our geo measurement contract contain"0.2ms
exactly one match for [ai17] "which aeo kpis should we report instead of a vendor blended score"0.2ms
exactly one match for [ai17] "how should we measure ai visibility without a vendor score"0.2ms
exactly one match for [ai18] "should we publish llms.txt and allow ai crawlers"0.3ms
exactly one match for [ai18] "do we block gptbot and google-extended or allow them"0.2ms
exactly one match for [ai18] "what is our policy on ai bots reading the site"0.2ms
exactly one match for [q28] "should we be doing video for seo"0.2ms
exactly one match for [q28] "how do we optimise our youtube videos for search"0.2ms
exactly one match for [q28] "is video worth it for our search traffic"0.2ms
exactly one match for [q28] "should we invest in video for search"0.2ms
exactly one match for [q28] "is it worth investing in youtube for organic traffic"0.2ms
exactly one match for [none] "make me a video about widgets"0.2ms
exactly one match for [none] "run a speed test"0.1ms
exactly one match for [none] "check my page speed"0.2ms
exactly one match for [none] "build me a new website"0.2ms
exactly one match for [none] "write me an article with ai"0.2ms
exactly one match for [none] "generate a blog post about widgets"0.2ms
exactly one match for [none] "draft the copy using ai"0.2ms
exactly one match for [none] "can we generate a new article with ai"2.0ms
exactly one match for [none] "should you write me an ai blog post"0.2ms
exactly one match for [none] "submit my sitemap"0.2ms
exactly one match for [none] "resubmit the sitemap to google"0.2ms
exactly one match for [none] "show me my organic traffic"0.2ms
exactly one match for [none] "show me my backlinks"0.5ms
exactly one match for [none] "check my backlinks"2.0ms
exactly one match for [none] "run a backlink scan"0.5ms
exactly one match for [none] "what is the backlink gap against acme.com"0.4ms
exactly one match for [none] "find keywords for my business"0.3ms
exactly one match for [none] "find keyword ideas worth targeting"0.3ms
exactly one match for [none] "research which keywords are worth targeting"0.2ms
exactly one match for [none] "keyword ideas for dental implants"0.2ms
exactly one match for [none] "what is the search volume for kyc software"0.2ms
exactly one match for [none] "track these keywords"0.2ms
exactly one match for [none] "what is the keyword gap between us and acme.com"0.2ms
exactly one match for [none] "how do we close the gap with competitor rival-brand.io this quarter"0.2ms
exactly one match for [none] "write me an article about dental implants"0.2ms
exactly one match for [none] "run an audit"0.3ms
exactly one match for [none] "find my competitors"0.3ms
exactly one match for [none] "fix my indexing"0.2ms
exactly one match for [none] "what is a canonical tag"0.1ms
exactly one match for [none] "hi"0.1ms
exactly one match for [none] "how is my domain authority compared to theirs"0.1ms
exactly one match for [none] "show me my backlinks"0.1ms
answer-shapes: the Q04/Q25 carve-out is explicit, not positional · 2 tests
a sentence with both signals goes to Q25 only0.2ms
a landscape question without a shortfall still goes to Q040.2ms
answer-shapes: E-E-A-T does not steal backlink vocabulary · 3 tests
not eeat: "how is my domain authority"0.1ms
not eeat: "what is our link authority"0.1ms
not eeat: "domain authority vs page authority"0.1ms
an ORDER never reaches the brief shortcut · 13 tests
gated: "run my AI visibility panel now for kakunin.ai — …"16.3ms
gated: "freeze my weekly AI visibility panel to the exac…"4.0ms
gated: "Freeze the weekly panel to these exact prompts: …"0.8ms
gated: "Freeze my weekly tracking panel to exactly these…"0.5ms
gated: "turn on weekly ai visibility tracking for kakuni…"0.3ms
gated: "disable the weekly cron…"0.2ms
gated: "lock my prompt set…"0.2ms
gated: "pause the campaign…"0.3ms
the audit-scope predicate no longer claims a freeze order at all0.2ms
the three REAL audit-scope questions still match — the fix narrowed, it did not delete2.5ms
"audit trails" is not an audit we run0.3ms
a bare "vs" needs a second audit before it counts as a scope comparison0.6ms
and real QUESTIONS are still claimed — the gate must not have swallowed the layer12.9ms
cost-quote-gate-parity.vitest.ts
198/198 52ms · 6 suites PASS
src/billing/cost-quote-gate-parity.vitest.ts
quoted price never differs from gated price · 190 tests
enrich_contacts (agent): approval card matches priceOfTool3.4ms
enrich_contacts (agent): gate message quotes the same figure priceOfTool decides on0.8ms
enrich_contacts (shortcut): approval card matches priceOfTool0.5ms
enrich_contacts (shortcut): gate message quotes the same figure priceOfTool decides on0.3ms
enrich_contacts: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.8ms
seo_offpage_audit (agent): approval card matches priceOfTool0.5ms
seo_offpage_audit (agent): gate message quotes the same figure priceOfTool decides on0.3ms
seo_offpage_audit (shortcut): approval card matches priceOfTool0.3ms
seo_offpage_audit (shortcut): gate message quotes the same figure priceOfTool decides on0.8ms
seo_offpage_audit: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.5ms
seo_backlinks (agent): approval card matches priceOfTool0.4ms
seo_backlinks (agent): gate message quotes the same figure priceOfTool decides on0.2ms
seo_backlinks (shortcut): approval card matches priceOfTool0.5ms
seo_backlinks (shortcut): gate message quotes the same figure priceOfTool decides on0.3ms
seo_backlinks: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.5ms
seo_request_indexing (agent): approval card matches priceOfTool0.3ms
seo_request_indexing (agent): gate message quotes the same figure priceOfTool decides on0.2ms
seo_request_indexing (shortcut): approval card matches priceOfTool0.1ms
seo_request_indexing (shortcut): gate message quotes the same figure priceOfTool decides on0.2ms
seo_request_indexing: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.4ms
seo_enrich_keywords (agent): approval card matches priceOfTool0.4ms
seo_enrich_keywords (agent): gate message quotes the same figure priceOfTool decides on0.3ms
seo_enrich_keywords (shortcut): approval card matches priceOfTool0.3ms
seo_enrich_keywords (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
seo_enrich_keywords: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.3ms
seo_competitor_gap (agent): approval card matches priceOfTool0.2ms
seo_competitor_gap (agent): gate message quotes the same figure priceOfTool decides on0.1ms
seo_competitor_gap (shortcut): approval card matches priceOfTool0.1ms
seo_competitor_gap (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
seo_competitor_gap: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.2ms
seo_backlink_gap (agent): approval card matches priceOfTool0.1ms
seo_backlink_gap (agent): gate message quotes the same figure priceOfTool decides on0.1ms
seo_backlink_gap (shortcut): approval card matches priceOfTool0.1ms
seo_backlink_gap (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
seo_backlink_gap: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.4ms
seo_backlink_deep_scan (agent): approval card matches priceOfTool0.3ms
seo_backlink_deep_scan (agent): gate message quotes the same figure priceOfTool decides on0.1ms
seo_backlink_deep_scan (shortcut): approval card matches priceOfTool0.1ms
seo_backlink_deep_scan (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
seo_backlink_deep_scan: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.1ms
full_seo_audit (agent): approval card matches priceOfTool0.1ms
full_seo_audit (agent): gate message quotes the same figure priceOfTool decides on0.1ms
full_seo_audit (shortcut): approval card matches priceOfTool0.1ms
full_seo_audit (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
full_seo_audit: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.2ms
keyword_volumes (agent): approval card matches priceOfTool0.2ms
keyword_volumes (agent): gate message quotes the same figure priceOfTool decides on0.3ms
keyword_volumes (shortcut): approval card matches priceOfTool0.1ms
keyword_volumes (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
keyword_volumes: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.2ms
scan_product (agent): approval card matches priceOfTool0.2ms
scan_product (agent): gate message quotes the same figure priceOfTool decides on0.1ms
scan_product (shortcut): approval card matches priceOfTool0.2ms
scan_product (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
scan_product: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.2ms
define_icp (agent): approval card matches priceOfTool0.1ms
define_icp (agent): gate message quotes the same figure priceOfTool decides on0.1ms
define_icp (shortcut): approval card matches priceOfTool0.1ms
define_icp (shortcut): gate message quotes the same figure priceOfTool decides on0.2ms
define_icp: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.1ms
generate_emails (agent): approval card matches priceOfTool0.3ms
generate_emails (agent): gate message quotes the same figure priceOfTool decides on0.1ms
generate_emails (shortcut): approval card matches priceOfTool0.1ms
generate_emails (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
generate_emails: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.1ms
search_leads (agent): approval card matches priceOfTool0.3ms
search_leads (agent): gate message quotes the same figure priceOfTool decides on0.1ms
search_leads (shortcut): approval card matches priceOfTool0.1ms
search_leads (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
search_leads: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.2ms
backlink_outreach_search (agent): approval card matches priceOfTool0.2ms
backlink_outreach_search (agent): gate message quotes the same figure priceOfTool decides on0.1ms
backlink_outreach_search (shortcut): approval card matches priceOfTool0.1ms
backlink_outreach_search (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
backlink_outreach_search: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.2ms
share_of_model (agent): approval card matches priceOfTool0.3ms
share_of_model (agent): gate message quotes the same figure priceOfTool decides on0.2ms
share_of_model (shortcut): approval card matches priceOfTool0.6ms
share_of_model (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
share_of_model: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.3ms
seo_geo_visibility (agent): approval card matches priceOfTool0.3ms
seo_geo_visibility (agent): gate message quotes the same figure priceOfTool decides on0.1ms
seo_geo_visibility (shortcut): approval card matches priceOfTool0.1ms
seo_geo_visibility (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
seo_geo_visibility: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.4ms
ai_overview_visibility (agent): approval card matches priceOfTool0.2ms
ai_overview_visibility (agent): gate message quotes the same figure priceOfTool decides on0.1ms
ai_overview_visibility (shortcut): approval card matches priceOfTool0.3ms
ai_overview_visibility (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
ai_overview_visibility: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.1ms
entity_audit (agent): approval card matches priceOfTool0.5ms
entity_audit (agent): gate message quotes the same figure priceOfTool decides on0.1ms
entity_audit (shortcut): approval card matches priceOfTool0.1ms
entity_audit (shortcut): gate message quotes the same figure priceOfTool decides on0.4ms
entity_audit: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.1ms
web_search (agent): approval card matches priceOfTool0.1ms
web_search (agent): gate message quotes the same figure priceOfTool decides on0.1ms
web_search (shortcut): approval card matches priceOfTool0.1ms
web_search (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
web_search: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.2ms
verify_contacts (agent): approval card matches priceOfTool0.5ms
verify_contacts (agent): gate message quotes the same figure priceOfTool decides on0.1ms
verify_contacts (shortcut): approval card matches priceOfTool0.1ms
verify_contacts (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
verify_contacts: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.2ms
seo_onpage_audit (agent): approval card matches priceOfTool0.3ms
seo_onpage_audit (agent): gate message quotes the same figure priceOfTool decides on0.1ms
seo_onpage_audit (shortcut): approval card matches priceOfTool0.1ms
seo_onpage_audit (shortcut): gate message quotes the same figure priceOfTool decides on0.2ms
seo_onpage_audit: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.3ms
seo_serp_spider (agent): approval card matches priceOfTool0.3ms
seo_serp_spider (agent): gate message quotes the same figure priceOfTool decides on0.1ms
seo_serp_spider (shortcut): approval card matches priceOfTool0.1ms
seo_serp_spider (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
seo_serp_spider: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.2ms
seo_keyword_metrics (agent): approval card matches priceOfTool0.2ms
seo_keyword_metrics (agent): gate message quotes the same figure priceOfTool decides on0.1ms
seo_keyword_metrics (shortcut): approval card matches priceOfTool0.1ms
seo_keyword_metrics (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
seo_keyword_metrics: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.5ms
google_god_mode_report (agent): approval card matches priceOfTool0.3ms
google_god_mode_report (agent): gate message quotes the same figure priceOfTool decides on0.1ms
google_god_mode_report (shortcut): approval card matches priceOfTool0.2ms
google_god_mode_report (shortcut): gate message quotes the same figure priceOfTool decides on0.2ms
google_god_mode_report: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.2ms
aeo_visibility (agent): approval card matches priceOfTool0.4ms
aeo_visibility (agent): gate message quotes the same figure priceOfTool decides on0.6ms
aeo_visibility (shortcut): approval card matches priceOfTool0.2ms
aeo_visibility (shortcut): gate message quotes the same figure priceOfTool decides on0.2ms
aeo_visibility: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.9ms
tap_volume (agent): approval card matches priceOfTool0.5ms
tap_volume (agent): gate message quotes the same figure priceOfTool decides on0.2ms
tap_volume (shortcut): approval card matches priceOfTool0.2ms
tap_volume (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
tap_volume: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.2ms
aeo_full_audit (agent): approval card matches priceOfTool0.1ms
aeo_full_audit (agent): gate message quotes the same figure priceOfTool decides on0.1ms
aeo_full_audit (shortcut): approval card matches priceOfTool0.1ms
aeo_full_audit (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
aeo_full_audit: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.1ms
seo_geo_research (agent): approval card matches priceOfTool3.3ms
seo_geo_research (agent): gate message quotes the same figure priceOfTool decides on0.1ms
seo_geo_research (shortcut): approval card matches priceOfTool0.2ms
seo_geo_research (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
seo_geo_research: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.1ms
aeo_page_check (agent): approval card matches priceOfTool0.1ms
aeo_page_check (agent): gate message quotes the same figure priceOfTool decides on0.1ms
aeo_page_check (shortcut): approval card matches priceOfTool0.1ms
aeo_page_check (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
aeo_page_check: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.1ms
seo_content_ideas (agent): approval card matches priceOfTool0.1ms
seo_content_ideas (agent): gate message quotes the same figure priceOfTool decides on0.1ms
seo_content_ideas (shortcut): approval card matches priceOfTool0.1ms
seo_content_ideas (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
seo_content_ideas: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.1ms
seo_write_content (agent): approval card matches priceOfTool0.1ms
seo_write_content (agent): gate message quotes the same figure priceOfTool decides on0.1ms
seo_write_content (shortcut): approval card matches priceOfTool0.1ms
seo_write_content (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
seo_write_content: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.1ms
find_competitors (agent): approval card matches priceOfTool0.1ms
find_competitors (agent): gate message quotes the same figure priceOfTool decides on0.1ms
find_competitors (shortcut): approval card matches priceOfTool0.1ms
find_competitors (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
find_competitors: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.1ms
seo_content_brief (agent): approval card matches priceOfTool0.1ms
seo_content_brief (agent): gate message quotes the same figure priceOfTool decides on0.1ms
seo_content_brief (shortcut): approval card matches priceOfTool0.1ms
seo_content_brief (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
seo_content_brief: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.1ms
seo_content_quality (agent): approval card matches priceOfTool0.1ms
seo_content_quality (agent): gate message quotes the same figure priceOfTool decides on0.0ms
seo_content_quality (shortcut): approval card matches priceOfTool0.1ms
seo_content_quality (shortcut): gate message quotes the same figure priceOfTool decides on0.0ms
seo_content_quality: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.1ms
set_campaign_sequence (agent): approval card matches priceOfTool0.1ms
set_campaign_sequence (agent): gate message quotes the same figure priceOfTool decides on0.1ms
set_campaign_sequence (shortcut): approval card matches priceOfTool0.1ms
set_campaign_sequence (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
set_campaign_sequence: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.1ms
seo_keywords (agent): approval card matches priceOfTool0.2ms
seo_keywords (agent): gate message quotes the same figure priceOfTool decides on0.1ms
seo_keywords (shortcut): approval card matches priceOfTool0.1ms
seo_keywords (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
seo_keywords: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.1ms
create_marketing_plan (agent): approval card matches priceOfTool0.1ms
create_marketing_plan (agent): gate message quotes the same figure priceOfTool decides on0.1ms
create_marketing_plan (shortcut): approval card matches priceOfTool0.2ms
create_marketing_plan (shortcut): gate message quotes the same figure priceOfTool decides on0.1ms
create_marketing_plan: resolveCostApproval's gate decision agrees with priceOfTool, in both directions0.2ms
the two tools this sprint flips · 4 tests
seo_offpage_audit: agent-route quote (104K-shape) clears the 75K threshold0.4ms
seo_offpage_audit: a bare shortcut dispatch never pays a floor it does not spend — and still gates0.2ms
seo_competitor_gap: agent-route quote clears the threshold (no shortcut exists — always agent-priced)0.2ms
no tool flips gating state between routes (the set is now empty)1.0ms
search_leads never double-counts its own floor · 1 test
table entry already includes ORCHESTRATION_FLOOR — withRouteFloor must not add a second copy0.6ms
a direct()/shortcut route never gains a floor it never pays · 1 test
google_god_mode_report: shortcut price is exactly the provider-only table figure0.2ms
priceOfTool never adds a floor to a genuinely free tool · 1 test
an unpriced tool stays at zero regardless of route0.2ms
costOfCall itself is unchanged — it stays the provider-only primitive · 1 test
costOfCall never includes the floor; only priceOfTool (agent route) does2.6ms
playbooks.vitest.ts
161/161 89ms · 10 suites PASS
src/seo/playbooks.vitest.ts
every playbook satisfies the invariants, in every state · 126 tests
dev_handoff — empty3.0ms
dev_handoff — populated0.3ms
dev_handoff — location saved but serves unanswered0.3ms
dev_handoff — content checked, nothing else0.2ms
dev_handoff — a tracked market0.3ms
dev_handoff — a crawl on file0.2ms
dev_handoff — a crawl that found bots blocked0.2ms
generative_strategy — empty0.2ms
generative_strategy — populated0.3ms
generative_strategy — location saved but serves unanswered0.3ms
generative_strategy — content checked, nothing else0.2ms
generative_strategy — a tracked market0.1ms
generative_strategy — a crawl on file0.1ms
generative_strategy — a crawl that found bots blocked0.1ms
exec_brief — empty0.3ms
exec_brief — populated0.2ms
exec_brief — location saved but serves unanswered0.2ms
exec_brief — content checked, nothing else0.2ms
exec_brief — a tracked market0.2ms
exec_brief — a crawl on file0.2ms
exec_brief — a crawl that found bots blocked0.2ms
channel_model — empty0.2ms
channel_model — populated0.2ms
channel_model — location saved but serves unanswered0.2ms
channel_model — content checked, nothing else0.1ms
channel_model — a tracked market0.1ms
channel_model — a crawl on file0.2ms
channel_model — a crawl that found bots blocked0.2ms
service_area — empty0.3ms
service_area — populated0.1ms
service_area — location saved but serves unanswered0.1ms
service_area — content checked, nothing else0.1ms
service_area — a tracked market0.1ms
service_area — a crawl on file0.1ms
service_area — a crawl that found bots blocked0.1ms
international — empty0.3ms
international — populated0.1ms
international — location saved but serves unanswered0.1ms
international — content checked, nothing else0.1ms
international — a tracked market0.1ms
international — a crawl on file0.1ms
international — a crawl that found bots blocked0.1ms
hallucination_recovery — empty0.3ms
hallucination_recovery — populated0.1ms
hallucination_recovery — location saved but serves unanswered0.1ms
hallucination_recovery — content checked, nothing else0.2ms
hallucination_recovery — a tracked market0.2ms
hallucination_recovery — a crawl on file0.2ms
hallucination_recovery — a crawl that found bots blocked0.1ms
ai_search_staffing — empty0.2ms
ai_search_staffing — populated0.1ms
ai_search_staffing — location saved but serves unanswered0.1ms
ai_search_staffing — content checked, nothing else0.1ms
ai_search_staffing — a tracked market0.2ms
ai_search_staffing — a crawl on file0.1ms
ai_search_staffing — a crawl that found bots blocked0.1ms
ai_audit_scope — empty0.3ms
ai_audit_scope — populated0.2ms
ai_audit_scope — location saved but serves unanswered0.1ms
ai_audit_scope — content checked, nothing else0.1ms
ai_audit_scope — a tracked market0.1ms
ai_audit_scope — a crawl on file0.2ms
ai_audit_scope — a crawl that found bots blocked0.4ms
geo_measurement_contract — empty0.4ms
geo_measurement_contract — populated0.2ms
geo_measurement_contract — location saved but serves unanswered0.1ms
geo_measurement_contract — content checked, nothing else0.2ms
geo_measurement_contract — a tracked market0.1ms
geo_measurement_contract — a crawl on file0.1ms
geo_measurement_contract — a crawl that found bots blocked0.1ms
crawler_policy — empty0.2ms
crawler_policy — populated0.1ms
crawler_policy — location saved but serves unanswered0.1ms
crawler_policy — content checked, nothing else0.1ms
crawler_policy — a tracked market0.1ms
crawler_policy — a crawl on file0.2ms
crawler_policy — a crawl that found bots blocked0.1ms
rival_industrialisation — empty0.3ms
rival_industrialisation — populated0.2ms
rival_industrialisation — location saved but serves unanswered0.2ms
rival_industrialisation — content checked, nothing else0.2ms
rival_industrialisation — a tracked market0.2ms
rival_industrialisation — a crawl on file0.2ms
rival_industrialisation — a crawl that found bots blocked0.2ms
kpi_contract — empty0.3ms
kpi_contract — populated0.1ms
kpi_contract — location saved but serves unanswered0.1ms
kpi_contract — content checked, nothing else0.2ms
kpi_contract — a tracked market0.2ms
kpi_contract — a crawl on file0.1ms
kpi_contract — a crawl that found bots blocked0.2ms
ai_cannibalisation — empty0.3ms
ai_cannibalisation — populated0.1ms
ai_cannibalisation — location saved but serves unanswered0.1ms
ai_cannibalisation — content checked, nothing else0.1ms
ai_cannibalisation — a tracked market0.1ms
ai_cannibalisation — a crawl on file0.1ms
ai_cannibalisation — a crawl that found bots blocked0.1ms
ai_pipeline_attribution — empty0.2ms
ai_pipeline_attribution — populated0.1ms
ai_pipeline_attribution — location saved but serves unanswered0.1ms
ai_pipeline_attribution — content checked, nothing else0.3ms
ai_pipeline_attribution — a tracked market0.1ms
ai_pipeline_attribution — a crawl on file0.1ms
ai_pipeline_attribution — a crawl that found bots blocked0.1ms
multi_market_language — empty0.3ms
multi_market_language — populated0.1ms
multi_market_language — location saved but serves unanswered0.1ms
multi_market_language — content checked, nothing else0.2ms
multi_market_language — a tracked market0.2ms
multi_market_language — a crawl on file0.1ms
multi_market_language — a crawl that found bots blocked0.1ms
fan_out — empty0.3ms
fan_out — populated0.1ms
fan_out — location saved but serves unanswered0.1ms
fan_out — content checked, nothing else0.1ms
fan_out — a tracked market0.1ms
fan_out — a crawl on file0.1ms
fan_out — a crawl that found bots blocked0.1ms
original_data — empty0.3ms
original_data — populated0.1ms
original_data — location saved but serves unanswered0.1ms
original_data — content checked, nothing else0.1ms
original_data — a tracked market0.1ms
original_data — a crawl on file0.1ms
original_data — a crawl that found bots blocked0.1ms
the invariants are real — playbookProblems catches each violation · 6 tests
rejects a vague trigger0.4ms
rejects a signal with no window0.2ms
rejects a rule with no signal at all0.2ms
rejects an open-ended decision0.3ms
rejects an unanchored playbook with no reason given0.2ms
accepts a null anchor WITH a reason — that is the honest case0.2ms
Q19: the definition of ready has teeth · 3 tests
the gate is admission to the sprint, not deprioritisation0.4ms
anchors on the platform's own record of unshipped work0.3ms
says plainly when there is no such record0.3ms
Q20: the defund clause survives · 4 tests
stops commissioning recap content rather than merely adding bets0.6ms
makes the defund falsifiable by output going DOWN0.4ms
forces the willing-to-stop question rather than assuming it1.3ms
requires a kill condition on each bet0.2ms
Q26: the format constraint is the answer · 4 tests
is one page and one decision0.3ms
caps options at two plus doing nothing0.5ms
puts rankings in the appendix as a diagnostic0.4ms
anchors on the revenue bridge being unavailable, and does not offer to assemble one0.4ms
Q30: ownership by query class, and one count · 4 tests
assigns demand rather than coordinating calendars0.3ms
makes the taxonomy falsifiable by someone declining work0.3ms
ends the double-count and says how you would spot it0.3ms
states which channels it can and cannot see0.2ms
Q18: the one unanswered question is the anchor · 5 tests
says the serves-at-location answer is missing when it is0.4ms
adapts once the answer exists0.5ms
warns that claiming an address you do not have is a suspension risk, not a ranking one0.4ms
rejects the city-page-with-swapped-name pattern, checkably0.4ms
says when no address is saved at all0.3ms
withContentSignals reads the shared checks rather than re-querying · 2 tests
counts failing pages by check name, de-duplicated by URL0.4ms
does not query content_quality itself2.2ms
playbooks never speak in internal vocabulary (GS-005) · 1 test
keeps field and table names out of every playbook in every state48.1ms
Q13: staffing is the SEO decision, not a detail after it · 6 tests
forbids translating a market nobody will keep current0.8ms
makes the staffing rule falsifiable rather than aspirational0.3ms
requires one address structure chosen before the first page0.2ms
requires the counterpart declaration to be two-way0.3ms
anchors on the single tracked market, and says what that limits0.3ms
separates a language from a country, because they need different answers0.3ms
tool-format.vitest.ts
127/127 132ms · 25 suites PASS
src/chat/tool-format.vitest.ts
parseTextToolCall (text-JSON salvage, live leak 2026-07-22) · 3 tests
salvages the exact live leak shape3.3ms
handles arguments/args keys and fenced JSON0.6ms
never salvages unregistered tools, malformed JSON, or JSON inside a real answer0.5ms
parseJsonObjectLoose (truncation-tolerant structured output) · 3 tests
parses clean and prefixed JSON0.5ms
recovers the leading object from a truncated tail0.4ms
returns null when nothing parses0.3ms
extractTldr (server-authored lead, chat-anatomy v2) · 5 tests
lifts the TL;DR line and returns the body without it1.1ms
tolerates marker variants the model produces0.7ms
passes through text without the marker unchanged0.3ms
a TL;DR with no body is just a short answer, not a lead0.4ms
never invents a lead mid-text0.3ms
deriveReportTldr · 8 tests
uses structured score data instead of report HTML or prose0.8ms
uses a structured finding count when no score is available0.2ms
keeps an honest report-ready lead when there is no stable summary field0.2ms
does not add a lead to errors or background handles0.2ms
uses the off-page findings for conclusion, why, and next action20.9ms
grounds the marketing-plan lead in plan_score and the thesis, not a generic lead0.9ms
falls back to an open-item count when no thesis is recorded0.3ms
returns null for an empty plan rather than a generic "is ready" lead0.2ms
deriveSubstantiveAnswerTldr · 1 test
uses a conservative first-answer fallback only for clearly substantive replies0.8ms
humanizeToolName · 2 tests
turns snake_case into a readable label0.4ms
handles single-word tool names0.2ms
formatToolResult — generate_emails surfaces dropped drafts · 3 tests
warns when some drafts were dropped on hallucinated contact ids6.2ms
does not claim success when every draft was dropped0.6ms
stays clean when nothing was dropped0.4ms
resolveSilentTurnText — no bare "Done." on a failed turn · 5 tests
relays the error when every tool call errored and the model was silent0.4ms
the relayed no-drafts copy satisfies the honest-copy regression regex0.2ms
keeps "Done." for a genuinely silent SUCCESS (no errors)0.4ms
keeps "Done." when only SOME tools errored (a later one may have carried the turn)0.2ms
keeps "Done." when there were no tool calls at all0.2ms
overrideBareAcknowledgment — no bare "Done." the MODEL itself authored on a failed turn · 7 tests
replaces a bare "Done." with the tool error when every call errored0.3ms
replaces bare acks case/punctuation-insensitively ("ok", "Got it!", "Sure")0.3ms
leaves a genuinely engaged reply alone, even a short one0.2ms
leaves "Done." alone on a real success0.2ms
leaves "Done." alone when only SOME tools errored0.2ms
is a no-op on empty modelText (resolveSilentTurnText owns that case)0.2ms
replaces a bare "Done." with an honest no-action message when zero tools ran0.2ms
getNextStepSuggestions — background-job ACK · 3 tests
never emits an "undefined" chip on the seo_write_content ack4.2ms
returns generic keep-working chips for every background tool ack12.3ms
still returns the real chips once the result carries fields0.5ms
send_emails result formatting · 3 tests
all-success stays terse0.2ms
failures list per-recipient reasons (live downvote: "Sent 0/3, no reason provided")0.5ms
skipped-unsafe note still appears alongside failures0.3ms
deriveSubstantiveAnswerTldr — substantive guarantee · 6 tests
still returns null for a short answer on a NON-substantive turn (unchanged)0.6ms
returns a lead for that same answer when the turn IS substantive0.6ms
never labels a question, even on a substantive turn0.2ms
never labels an approval/gate reply, even on a substantive turn0.3ms
never labels an error, even on a substantive turn0.2ms
still refuses a trivially short reply even when substantive0.2ms
deriveReportTldr — seo_google_merge answers the question asked · 4 tests
leads with direction and magnitude when a comparison window exists0.5ms
says "up" when the period improved — direction is read from the data, not assumed0.2ms
admits it is a snapshot when no comparison was run, rather than implying a trend0.3ms
never fabricates a drag page when the insight rows are empty0.2ms
isSubstantiveSynthesis — answer vs handoff · 6 tests
keeps a real explanation1.0ms
does NOT keep a bare handoff — the artifact path must be untouched0.3ms
does not keep a bare acknowledgment0.2ms
is conservative — a short reply falls back to today behaviour0.2ms
requires more than one sentence — a single long line is not an explanation0.3ms
keeps a long answer that merely OPENS with a handoff-ish phrase0.2ms
search_leads next steps put verification before drafting · 3 tests
offers verification first when the results landed in a named list0.5ms
offers verification first when there is no list0.2ms
does not push verification on a failed or empty search0.3ms
formatToolResult seo_google_merge — tables, not bullets, and cost is never silent · 3 tests
renders the share-of-voice breakdown as a markdown pipe table0.5ms
states cost explicitly — never silent about a free tool (CLAUDE.md §4)0.3ms
by-channel and top-sources sections are tables too, once ecommerce is tracked0.5ms
formatToolResult — remaining row-shaped sites converted to tables · 10 tests
seo_competitor_gap top_pages renders as a Page/URL table0.8ms
seo_content_quality categories and top fixes render as tables0.5ms
google_god_mode_report PSI runs render as a Strategy/Performance/LCP/INP table0.4ms
safe_browsing_check_v2 rows render as a URL/Status/Threats table0.8ms
gtm_audit_v2 environments render as an Environment/Type/Debug table1.1ms
sov_trend current standings render as a Brand/Share table5.4ms
seo_geo_research cited competitors render as a Lee-signals table2.4ms
ga_traffic sources view renders as a Source / medium table0.6ms
ga_traffic default view renders as a Channel table0.3ms
ga4_report_v2 rows render as a dimension/metric table0.7ms
formatToolResult — re-audit sweep: mis-judged bullet sites converted to tables · 9 tests
ga4_metadata dimensions/metrics render as Name/Type tables (custom flag is a real column)0.9ms
share_of_model top_entity_language renders as a Query/Mentions table (count was previously dropped)0.5ms
share_of_model and ai_overview_visibility leaderboards render as Domain/Citations tables (count+domain, not caught by the " • " grep)0.5ms
entity_audit entities and competitor comparison render as tables (multiple real fields were flattened into one bullet)0.4ms
the backlink report renders referring domains as a DR/Links/Value table0.6ms
seo_list_keywords renders tracked keywords as a Keyword/Volume/CPC/Rank table0.9ms
gsc_performance and gsc_performance_v2 rows render as tables0.7ms
psi_audit_v2 and crux_history_v2 render as tables0.7ms
find_competitors renders as a Domain/Title table0.4ms
connector-degraded chips · 4 tests
appends the connect chip without dropping the tool's own next steps0.3ms
never duplicates the chip when the tool already offered it0.2ms
leaves an ordinary result untouched0.1ms
renders the note itself — it is a *_note key, so the generic appender carries it0.2ms
search reasoning: rationale OR concern, never both · 4 tests
renders the rationale when the segment fits0.3ms
renders the concern INSTEAD, never under the rationale heading0.2ms
still shows the leads — the caution never deletes work the user paid for0.3ms
says nothing when the model returned neither0.2ms
gated tools render the gate, not the success case · 5 tests
does not claim a campaign resumed when it is only asking to confirm0.2ms
does not claim DNS records were applied when it is only asking to confirm0.2ms
still reports a real resume as done0.1ms
leaves send_emails to render its own gate, table and all0.2ms
falls back to asking for confirmation, never to the success branch1.0ms
confirm chips are matched by their intent rules · 8 tests
resume_campaign offers a confirm chip the router recognises20.0ms
resume_campaign offers a cancel chip the router recognises3.3ms
cloudflare_fix_email_dns offers a confirm chip the router recognises0.7ms
cloudflare_fix_email_dns offers a cancel chip the router recognises0.4ms
send_emails offers a confirm chip the router recognises0.6ms
set_standing_instruction offers a confirm chip the router recognises0.3ms
set_standing_instruction offers a cancel chip the router recognises0.5ms
does not treat a bare affirmative as a confirmation for either gate1.1ms
confirm phrases survive ordinary typing · 9 tests
"yes, resume it" routes to resume_confirm0.2ms
"Yes, resume it." routes to resume_confirm0.1ms
"YES, RESUME IT" routes to resume_confirm0.1ms
"yes resume it" routes to resume_confirm0.1ms
"no, leave it paused" routes to resume_cancel0.5ms
"yes, apply the dns fixes" routes to dns_confirm0.2ms
"yes, send them" routes to send_confirm0.2ms
"yes, save it" routes to standing_confirm0.2ms
still refuses a bare affirmative1.1ms
pause/resume reach their tool without asking the model nicely · 11 tests
"Resume the campaign called "Eval Pause Fixture"." → resume_start0.2ms
"resume my campaign" → resume_start0.1ms
"Restart the campaign" → resume_start0.2ms
"unpause our campaign now" → resume_start0.2ms
"Please resume the campaign Q3 founders" → resume_start0.2ms
"Stop the campaign called "Q3 founders" right now." → pause_start0.2ms
"pause my campaign" → pause_start0.2ms
"Halt the campaign" → pause_start0.2ms
"Please pause our campaign" → pause_start0.2ms
keeps the confirm phrases as confirmations, not as fresh requests1.6ms
does not claim bare resume/stop phrasings that are not about a campaign2.2ms
campaignNameFromCommand · 2 tests
reads a delimited name, straight or curly quoted0.6ms
returns null rather than guessing when no name is delimited0.2ms
lead-corpus.vitest.ts
122/122 56ms · 8 suites PASS
src/tools/lead-corpus.vitest.ts
corpus shape · 2 tests
carries the 24 incident prompts and 20 paraphrases3.5ms
every entry either has arguments or a named question — no silent gaps2.5ms
expressibility — every corpus request validates · 55 tests
i01: founders working on fact checking browser extensions1.3ms
i02: founders working on political bias in search results0.2ms
i03: founders working on source credibility scoring0.4ms
i04: founders working on claim verification workflows0.7ms
i05: founders working on newsroom fact checking tools0.4ms
i06: founders working on misinformation on social platforms0.2ms
i07: founders working on automated content moderation0.4ms
i08: founders working on propaganda detection techniques0.3ms
i09: founders working on clickbait detection techniques0.4ms
i10: founders working on satire versus fake news detection0.3ms
i11: founders working on echo chambers and filter bubbles0.1ms
i12: founders working on health misinformation tracking0.1ms
i13: founders working on AI answer engine citations0.1ms
i14: founders working on brand mentions in AI assistants0.2ms
i15: founders working on featured snippet optimisation0.1ms
i16: founders working on zero click search results0.1ms
i17: founders entity based SEO0.2ms
i18: founders working on knowledge graph optimisation0.2ms
i19: founders working on schema markup for publishers0.2ms
i20: founders building topical authority SEO content strategy0.2ms
i21: founders working on content decay and content refresh0.1ms
i22: founders working on programmatic SEO for directories0.2ms
i23: founders working on misinformation research0.4ms
i24: founders working on political bias in search results0.2ms
l01: Founders, CEOs, Marketing Managers and E-commerce Managers at sm0.6ms
l02: automotive brands specialising in aftermarket accessories for mo1.4ms
l03: decision makers at plumbing companies in USA0.2ms
l04: founders of newly funded SaaS startups who need marketing agency0.2ms
l05: course creators with 500+ students0.2ms
l06: home appliances distributors in India1.6ms
l07: sustainability-focused e-commerce brands0.2ms
l08: catering services0.1ms
l10: find me leads for underwater basket-weaving0.1ms
l11: dentists in 100010.3ms
l12: coffee shops near Indiranagar 5600380.2ms
p01: find founders building tools for programmatic SEO on directory s0.3ms
p02: I need founders whose product does programmatic SEO for director0.2ms
p03: get me startup founders in the programmatic SEO for directories 0.1ms
p04: who are the founders working on programmatic SEO for directories0.2ms
p05: founders doing fact checking browser extensions0.2ms
p06: people who founded companies making browser extensions for fact 0.2ms
p07: founders in the automated content moderation space0.1ms
p08: startup founders building automated content moderation0.1ms
p09: founders focused on AI answer engine citations0.1ms
p10: who is building for AI answer engine citations — get me their fo0.4ms
p11: founders whose companies handle zero click search results0.2ms
p12: founders tackling zero click search results0.4ms
p13: Pakistani e-commerce businesses — get me the founders, CEOs, mar0.7ms
p14: small and medium e-commerce companies based in Pakistan; I want 0.2ms
p15: motorcycle aftermarket accessories brands in India — founders, C0.7ms
p16: Indian companies making aftermarket accessories for motorcycles,0.9ms
p17: seed and Series A SaaS founders who would need a marketing agenc0.1ms
p18: SaaS companies that just raised seed or Series A in 2026 — found0.1ms
p19: find dentists in the 10001 zip code0.8ms
p20: I want a list of dental practices in 10001, New York0.2ms
the intent survives — it is carried, not discarded · 33 tests
i01 keeps "fact checking browser extensions"0.4ms
i02 keeps "political bias in search results"0.1ms
i03 keeps "source credibility scoring"0.1ms
i04 keeps "claim verification workflows"0.1ms
i05 keeps "newsroom fact checking tools"0.1ms
i06 keeps "misinformation on social platforms"0.1ms
i07 keeps "automated content moderation"0.1ms
i08 keeps "propaganda detection techniques"0.1ms
i09 keeps "clickbait detection techniques"0.1ms
i10 keeps "satire versus fake news detection"0.3ms
i11 keeps "echo chambers and filter bubbles"0.1ms
i12 keeps "health misinformation tracking"0.1ms
i13 keeps "AI answer engine citations"0.1ms
i14 keeps "brand mentions in AI assistants"0.1ms
i15 keeps "featured snippet optimisation"0.1ms
i16 keeps "zero click search results"0.1ms
i17 keeps "entity based SEO"0.2ms
i18 keeps "knowledge graph optimisation"0.1ms
i19 keeps "schema markup for publishers"0.1ms
i20 keeps "topical authority SEO content strategy"0.1ms
i21 keeps "content decay and content refresh"0.1ms
i22 keeps "programmatic SEO for directories"0.1ms
i23 keeps "misinformation research"0.1ms
i24 keeps "political bias in search results"0.1ms
l01 keeps "e-commerce businesses"0.1ms
l02 keeps "aftermarket accessories for motorcycles"0.1ms
l03 keeps "plumbing companies"0.3ms
l04 keeps "SaaS startups that need marketing agency"0.1ms
l05 keeps "course creators with 500+ students"0.2ms
l06 keeps "home appliances distribution"0.2ms
l07 keeps "sustainability-focused e-commerce brands"0.1ms
l08 keeps "catering services"0.4ms
l10 keeps "underwater basket-weaving"0.2ms
phrasing stops mattering — paraphrases canonicalise · 20 tests
p01 matches i221.2ms
p02 matches i220.4ms
p03 matches i220.7ms
p04 matches i220.2ms
p05 matches i010.2ms
p06 matches i010.1ms
p07 matches i070.2ms
p08 matches i070.1ms
p09 matches i130.1ms
p10 matches i130.2ms
p11 matches i160.1ms
p12 matches i160.1ms
p13 matches l010.2ms
p14 matches l010.1ms
p15 matches l020.1ms
p16 matches l020.1ms
p17 matches l040.1ms
p18 matches l040.1ms
p19 matches l110.1ms
p20 matches l110.1ms
field-level phrasing tolerance — resolved, not rejected, not guessed · 4 tests
resolves the spellings a model actually writes1.5ms
resolves a city to its country instead of 400ing the run0.4ms
resolves punctuation drift in the industry taxonomy0.7ms
refuses to guess when nothing resolves, and offers no misleading candidates6.9ms
serialisation slips are coerced, not punished · 2 tests
accepts a stringified boolean0.4ms
still rejects a boolean field that carries something else0.2ms
the incident itself cannot recur through this schema · 3 tests
the exact sentence is rejected from the industry field, with candidates2.2ms
the same sentence is accepted in topic0.3ms
a title-cased duplicate of the sentence is rejected too3.9ms
tier-specific serialisation slips (found on the live path, not by the replay) · 3 tests
accepts a stringified array0.5ms
still rejects a bare sentence in an array field — coercion is not permission1.7ms
leaves a non-JSON string alone rather than inventing an array0.4ms
contracts.vitest.ts
97/97 53ms · 23 suites PASS
src/planner/contracts.vitest.ts
stripFabricatedMagnitude (Track B #1 — no unsupported percentage claims) · 5 tests
strips a "by N%" magnitude, keeps the directional claim3.1ms
strips multiple magnitudes in the same statement0.5ms
strips a decimal or tilde-prefixed magnitude0.3ms
leaves directional-only text untouched0.3ms
does not mangle an unrelated number (no percent sign)0.3ms
resolveMeasurement (audit P0 — measurement contract) · 4 tests
campaign target wins: replies, up1.7ms
keyword → rank, down0.7ms
aeo channel with a site → aeo_score; seo/content → onpage_score0.6ms
no site and no target → null (explicit non-learning class)0.5ms
validateToolArgs (audit P1 — beyond tool-name whitelisting) · 2 tests
accepts valid args and tolerates schema-less tools0.6ms
rejects missing/empty required, unknown args, wrong types1.0ms
validateBetCount (Growth Bets — 3-5 range, never padded) · 5 tests
accepts 3-5 non-empty bets on a plan with >=5 initiatives0.6ms
rejects fewer than 3 or more than 5 bets when >=5 initiatives exist0.4ms
allows fewer than 3 bets when the plan genuinely has fewer initiatives (no padding)0.3ms
rejects a padded/empty bet (0 linked initiatives) outright, regardless of count0.2ms
rejects zero bets on a plan that has initiatives0.2ms
assignHorizon (execution horizons — foundation/build/scale) · 3 tests
the hard-blocker item is always foundation, regardless of position0.2ms
buckets by position into roughly-even thirds0.2ms
a recurring signal (rank tracking) biases one band later than its raw position0.2ms
deriveImpactRating / feasibilityFromEffort (no fabricated numeric confidence) · 5 tests
the hard-blocker item is always high impact — it unblocks everything else0.2ms
a real, distinctly-owned measurement signal is high impact0.2ms
a real but SHARED measurement signal (Track B #3) is medium, not high0.1ms
an unmeasurable initiative defaults to medium — never fabricated higher0.1ms
feasibility is a direct remap of effort, defaulting to medium0.2ms
valueAtStakeFor (highest-risk helper — must never fabricate or misplace a benchmark) · 3 tests
returns a reference band, always carrying unverified:true, for a known unmeasurable channel0.2ms
NEVER returns a figure when a real measurement exists — even for a channel with a known band0.1ms
returns null (honest absence) for a channel with no reference band — never a placeholder0.2ms
reflectHypothesisStatus (audit US5 — reflection) · 1 test
derives status from measured outcomes only0.4ms
planner contracts · 15 tests
is stable across normalization-equivalent inputs1.7ms
differs by action, target, and keyword0.8ms
treats missing target/keyword as empty, not undefined-stringified0.3ms
position: ±1 place is neutral, beyond is directional (down = better)0.4ms
clicks: relative floor with absolute-count guard0.3ms
impressions floor0.2ms
missing values are neutral, never a verdict0.3ms
unknown metric uses the default floor0.2ms
walks the happy path0.4ms
allows proposed to skip straight to invoked (Execute chip without explicit approve)0.2ms
pre-completed states can block or be dismissed; completed cannot0.4ms
blocked is retryable (→proposed) or dismissable, nothing else0.3ms
only proposed can expire to not_run0.3ms
completed can close as done_unmeasured (the explicit non-learning terminal)0.2ms
terminal states never move, including self-transitions everywhere3.3ms
resolveExecutionMode (FR-003 — two states, no third) · 3 tests
is executable only when a tool is actually attached0.3ms
degrades to manual for every shape that carries no runnable tool0.4ms
never returns a third state, whatever it is handed2.4ms
manualInstructionFor (FR-001 — plain language, never a validator message) · 2 tests
never emits internal vocabulary for any reason1.4ms
tells the user what to do, and stays silent when there is nothing to add0.3ms
coerceToolArgs (T011b — repair the unambiguous, invent nothing) · 5 tests
drops keys the schema never declared0.4ms
coerces unambiguous type mismatches in both directions0.3ms
leaves a non-numeric string alone rather than coercing it to NaN0.2ms
fills a missing required arg ONLY from caller-supplied context0.3ms
is a no-op for a tool with no declared parameters0.3ms
computePlanProgress (surface 2 — progress from the ledger, no new state) · 9 tests
separates work done from time spent — the pairing the panel was missing1.3ms
counts every terminal-done status as done, including the unmeasured one0.4ms
measured counts only items with a real verdict — done is not measured1.5ms
next_recheck_at is the soonest check STILL AHEAD — a past-due one is not "next"0.3ms
reports no recheck rather than a stale one when every check is past due0.2ms
flags stalled ONLY past the horizon with nothing done0.3ms
caps time at 100% — a plan can overrun, but "142% elapsed" is noise0.2ms
invents no timeline for a plan that has no horizon or no created_at0.3ms
an empty plan is 0%, never NaN0.3ms
sanitizeStoredReason (the panel reads status_reason back out of the DB) · 3 tests
replaces every legacy validator message that was persisted before the fix0.9ms
leaves a legitimate stored reason untouched0.4ms
normalises empty/absent to null0.2ms
computePlanProgress — lineage across superseded plans · 5 tests
counts work finished under an earlier plan without moving those rows0.4ms
ages the chain from the FIRST plan, not the newest — re-planning does not reset the clock0.2ms
stall is judged on the chain — re-planning cannot hide a stuck user from the warning0.2ms
is NOT stalled when the chain is long but work actually happened0.2ms
a first plan reports plan 1 and no carried work — lineage stays silent0.4ms
classifyDirective (ordered, first-match-wins — exactly one class per item) · 5 tests
returns predictive only when BOTH a baseline and a method exist0.5ms
a hard blocker is always corrective — a missing prerequisite is a fact, not an opinion0.2ms
a measured defect is corrective; measured headroom without a defect is suggestive0.2ms
an unmeasurable ACTION is suggestive; unmeasurable non-action is advisory0.2ms
never returns anything outside the vocabulary, whatever it is handed1.1ms
directiveProvenanceLegal (FR-031 — the rule the two axes exist to express) · 2 tests
rejects a corrective claim resting on anything but a measurement0.5ms
allows every other pairing — the constraint is deliberately narrow0.5ms
classifyProvenance (recorded is never promoted to measured) · 2 tests
separates what we measured from what they told us0.3ms
an industry reference outranks everything — it is the least trustworthy class0.2ms
groupByDirective (FR-030 — an empty class states its own absence) · 3 tests
always returns all four groups, in fixed order, even when every one is empty0.6ms
places each item in exactly one group and never duplicates it1.3ms
drops an unclassified (pre-013) row from every group rather than guessing one0.2ms
enforceMeasurementKeyCap · 6 tests
keeps at most 2 per key and drops the rest, preserving order0.6ms
reproduces the fixture baseline: 6-on-one-key becomes 20.5ms
counts each key separately — distinct signals do not contend0.4ms
never drops an unmeasurable item — it is not competing for a signal0.5ms
honours the exemption — the hard-blocker gate unblocks the rest and must survive0.4ms
is a no-op on an already-compliant plan1.0ms
enforceMeasurementKeyCap — carried-forward work spends the signal budget · 3 tests
LIVE DEFECT: 2 carried + 2 new on one key produced FOUR owners of one signal0.6ms
leaves room for exactly the unspent remainder0.4ms
an empty budget behaves exactly as before — no behaviour change for a first plan0.5ms
classifyDirective on carry-forward — no partially-classified plan · 2 tests
LIVE DEFECT: pre-013 rows carried onto a classified plan rendered into NO group0.7ms
a historical row is never retroactively called corrective without evidence0.2ms
measurement budget counts COMMITMENTS, not unactioned advice · 4 tests
52 proposed rows spend NOTHING — new proposals survive0.6ms
real commitments DO still spend it — the cap is not simply disabled0.4ms
a blocked item counts — a failed execution attempt IS a commitment0.3ms
terminal statuses never spend budget — finished work is not in flight0.3ms
no-schema-speak.vitest.ts
96/96 27ms · 4 suites PASS
src/tools/no-schema-speak.vitest.ts
every validation rule produces a human sentence · 86 tests
has rules to check (the enumeration is real, not vacuous)1.9ms
the rule statements themselves ARE schema-speak — which is why they must never ship0.6ms
search_leads / "local_requires_category" never reaches a user as schema text0.8ms
search_leads / "local_requires_place" never reaches a user as schema text0.4ms
search_leads / "postal_code_pairs_with_country_code" never reaches a user as schema text0.3ms
search_leads / "people_fields_only_on_people_search" never reaches a user as schema text0.4ms
search_leads / "local_fields_only_on_local_search" never reaches a user as schema text0.3ms
search_leads / "audience_needs_a_handle" never reaches a user as schema text0.3ms
search_leads / "narrow_only_on_request" never reaches a user as schema text0.3ms
seo_keywords / "topic_is_a_seed_not_a_sentence" never reaches a user as schema text0.8ms
seo_list_keywords / "no_arguments_means_no_narrowing" never reaches a user as schema text0.2ms
seo_keyword_metrics / "one_keyword_not_a_question" never reaches a user as schema text0.1ms
seo_enrich_keywords / "omit_to_enrich_everything" never reaches a user as schema text0.1ms
find_competitors / "named_domain_is_the_subject" never reaches a user as schema text0.1ms
seo_competitor_gap / "competitor_must_be_named_or_saved" never reaches a user as schema text0.1ms
seo_backlink_gap / "gap_needs_our_baseline" never reaches a user as schema text0.1ms
seo_backlink_verify / "blocked_is_not_absent" never reaches a user as schema text0.2ms
seo_backlink_deep_scan / "repeat_scans_are_incremental" never reaches a user as schema text0.1ms
seo_backlink_value / "one_property_only" never reaches a user as schema text0.1ms
seo_backlink_value / "zero_is_not_worthless" never reaches a user as schema text0.1ms
seo_content_ideas / "ideas_are_not_a_brief" never reaches a user as schema text0.1ms
seo_content_brief / "brief_targets_one_primary_keyword" never reaches a user as schema text0.1ms
seo_write_content / "one_of_keyword_or_source_url" never reaches a user as schema text0.1ms
seo_write_content / "reuse_a_brief_you_already_have" never reaches a user as schema text0.1ms
seo_onpage_audit / "named_domain_is_the_subject" never reaches a user as schema text0.1ms
seo_offpage_audit / "named_domain_is_the_subject" never reaches a user as schema text0.1ms
seo_backlinks / "named_domain_is_the_subject" never reaches a user as schema text0.2ms
seo_serp_spider / "verify_index_is_opt_in" never reaches a user as schema text0.1ms
seo_serp_spider / "serp_features_keywords_is_opt_in" never reaches a user as schema text0.1ms
entity_audit / "do_not_guess_identity" never reaches a user as schema text0.1ms
entity_audit / "site_only_never_claim_listings" never reaches a user as schema text0.1ms
entity_audit / "ask_before_assuming_local" never reaches a user as schema text0.1ms
aeo_page_check / "one_page_not_a_site" never reaches a user as schema text0.1ms
aeo_full_audit / "composite_is_opt_in" never reaches a user as schema text0.2ms
sov_trend / "state_the_recurring_cost" never reaches a user as schema text0.2ms
panel_segments / "state_the_recurring_cost" never reaches a user as schema text2.1ms
panel_segments / "keyword_panels_are_derived" never reaches a user as schema text0.2ms
tap_volume / "queries_is_always_an_array" never reaches a user as schema text0.1ms
seo_generate_llms_txt / "generated_not_published" never reaches a user as schema text0.1ms
seo_request_indexing / "submission_is_irreversible_and_per_url" never reaches a user as schema text0.1ms
seo_monitor / "reads_history_never_measures" never reaches a user as schema text0.1ms
seo_rank_track / "keywords_are_search_terms" never reaches a user as schema text0.2ms
seo_content_quality / "one_page_needs_a_page" never reaches a user as schema text0.1ms
seo_google_merge / "traffic_is_not_mentions" never reaches a user as schema text0.1ms
domain_email_readiness_audit / "audit_before_first_send" never reaches a user as schema text0.1ms
cloudflare_fix_email_dns / "this_writes_live_dns" never reaches a user as schema text0.2ms
cloudflare_fix_email_dns / "only_three_fixes_exist" never reaches a user as schema text0.2ms
generate_dns_fix_prompt / "instructions_not_changes" never reaches a user as schema text0.1ms
get_usage_breakdown / "spend_is_not_a_diagnosis" never reaches a user as schema text0.1ms
list_campaigns / "no_arguments_means_no_narrowing" never reaches a user as schema text0.1ms
list_sequences / "no_arguments_means_no_narrowing" never reaches a user as schema text0.1ms
list_connectors / "check_before_claiming" never reaches a user as schema text0.1ms
show_marketing_plan / "plan_status_only" never reaches a user as schema text0.2ms
create_campaign / "creating_is_not_sending" never reaches a user as schema text0.2ms
campaign_stats / "rates_need_a_denominator" never reaches a user as schema text0.2ms
create_sequence / "authoring_is_not_enrolling" never reaches a user as schema text0.1ms
connect_connector / "named_domain_is_the_subject" never reaches a user as schema text0.1ms
set_standing_instruction / "standing_means_every_future_message" never reaches a user as schema text0.1ms
scan_product / "scan_their_site_not_ours" never reaches a user as schema text0.1ms
define_icp / "proposal_not_verdict" never reaches a user as schema text0.1ms
backlink_outreach_search / "discovery_saves_real_contacts" never reaches a user as schema text0.1ms
create_marketing_plan / "plans_are_asked_for_explicitly" never reaches a user as schema text0.1ms
propose_outbound_run / "run_proposes_never_executes" never reaches a user as schema text0.2ms
aeo_visibility / "engines_are_the_cost_lever" never reaches a user as schema text0.1ms
aeo_visibility / "site_omitted_means_saved_site" never reaches a user as schema text0.1ms
generate_emails / "one_targeting_field" never reaches a user as schema text0.1ms
generate_emails / "never_invent_a_contact_id" never reaches a user as schema text0.1ms
list_contacts / "all_prevents_a_silently_partial_answer" never reaches a user as schema text0.1ms
add_contacts / "typed_contacts_are_saved_not_searched" never reaches a user as schema text0.1ms
list_sent_emails / "sent_log_is_this_tool" never reaches a user as schema text0.1ms
send_emails / "confirm_is_never_self_granted" never reaches a user as schema text0.1ms
enrich_contacts / "one_targeting_field" never reaches a user as schema text0.1ms
enrich_contacts / "vague_quality_is_not_a_filter" never reaches a user as schema text0.2ms
enrich_contacts / "redo_needs_enriched_any" never reaches a user as schema text0.1ms
verify_contacts / "never_pick_a_target_for_a_paid_run" never reaches a user as schema text0.2ms
verify_contacts / "do_not_re_verify_by_default" never reaches a user as schema text0.1ms
assign_to_campaign / "campaign_must_exist" never reaches a user as schema text0.1ms
enroll_in_sequence / "enrolment_starts_sends" never reaches a user as schema text0.1ms
start_sequence / "starting_is_sending" never reaches a user as schema text0.1ms
set_campaign_sequence / "attaching_is_not_launching" never reaches a user as schema text0.1ms
diagnose / "diagnose_before_paid_audit" never reaches a user as schema text0.1ms
diagnose / "no_revenue_data_means_no_cause" never reaches a user as schema text0.1ms
diagnose / "one_measurement_is_not_a_trend" never reaches a user as schema text0.1ms
seo_geo_research / "topic_research_not_brand_measurement" never reaches a user as schema text0.1ms
web_search / "search_before_paid_report_on_someone_elses_site" never reaches a user as schema text0.1ms
read_url / "one_page_per_call" never reaches a user as schema text0.1ms
the exact live payload · 2 tests
produces a question, not the rule statement1.4ms
names a field in plain words when it has nothing better to say0.2ms
a cross-field rule never blames a single field · 4 tests
people_fields_only_on_people_search asks which search the user meant1.3ms
local_fields_only_on_local_search asks which search the user meant0.5ms
still names a genuinely bad value when the error IS one field0.3ms
blocks BEFORE the cost-approval card rather than after the click0.5ms
the guardrail is the floor under all of it · 4 tests
redacts field=value syntax that reaches user-visible text by any other route2.6ms
redacts a parameter LIST0.2ms
leaves a single snake_case word alone — it may be the user's own term0.2ms
leaves ordinary prose untouched0.2ms
tool-outcomes.vitest.ts
87/87 26ms · 8 suites PASS
src/runtime/tool-outcomes.vitest.ts
isExpectedToolOutcome — recurring Sentry noise is suppressed · 37 tests
suppresses: Campaign name required2.9ms
suppresses: Insufficient token balance for on-page audit 1.0ms
suppresses: No contacts found in list(s): new. No contact0.5ms
suppresses: No contacts found in list(s): test. Available0.6ms
suppresses: flexifunnels.com is your own site, not a comp0.6ms
suppresses: No product brief set. Run scan_product with y0.4ms
suppresses: Which site? Tell me the domain (or set your s0.2ms
suppresses: found 0 results0.3ms
suppresses: generated 0 drafts0.5ms
suppresses: sent 0 of total0.3ms
suppresses: No contacts found0.1ms
suppresses: Unknown connector "instagram". Available: sla0.1ms
suppresses: tool scan_product: Failed to fetch URL: HTTP 0.2ms
suppresses: Failed to fetch URL: Invalid URL: www.pointto0.2ms
suppresses: tool scan_product: Failed to fetch URL: Fetch0.6ms
suppresses: tool send_emails: Too many at once (7). Send 0.1ms
suppresses: tool send_emails: You have no drafts waiting 0.1ms
suppresses: tool seo_onpage_audit: The SEO data provider 0.3ms
suppresses: tool send_emails: sent 0 of total0.2ms
suppresses: tool send_emails: Those email ids aren't vali0.1ms
suppresses: tool send_emails: No draft emails to send. Ge0.1ms
suppresses: tool seo_keywords: Which topic or keyword sho0.5ms
suppresses: tool seo_geo_visibility: No site set. Add you0.3ms
suppresses: tool aeo_page_check: Give me the URL to audit0.2ms
suppresses: tool seo_keywords: No keywords to enrich. Add0.6ms
suppresses: tool seo_keywords: None of those look like ke1.3ms
suppresses: tool seo_google_merge: No GA4 property found 0.3ms
suppresses: tool seo_gtm: No Google Tag Manager accounts 0.2ms
suppresses: tool seo_redirect_fix: No Cloudflare zone fou0.1ms
suppresses: tool seo_pagespeed: No CrUX field data for ht0.1ms
suppresses: tool seo_onpage_status: No on-page crawl is i0.2ms
suppresses: tool fix_dns: No safe auto-fixable DNS issues0.3ms
suppresses: tool set_standing_instruction: A standing ins0.8ms
suppresses: tool create_report: A report title is require0.4ms
suppresses: tool assign_contacts: No campaign specified. 0.8ms
suppresses: tool gsc_inspect: No URLs to inspect — the si0.1ms
null/empty is not reportable0.2ms
isExpectedToolOutcome — genuine faults still reach Sentry · 9 tests
reports: apify trigger HTTP 4000.5ms
reports: Mixpanel export HTTP 5000.4ms
reports: unexpected response shape from provider0.1ms
reports: DFS_TASK_TIMEOUT0.1ms
reports: Cannot read properties of undefined (rea0.1ms
reports: Failed to render the Product panel: Type0.1ms
reports: GA4 property lookup failed: HTTP 503 fro0.1ms
reports: Cloudflare zone lookup returned malforme0.1ms
reports: on-page crawl is in progress but the sta0.1ms
recurring expected outcomes (2026-08-01 sweep) · 8 tests
treats I couldn't find a list called "this-list-does-not-exist-xyz". Your lists are: AI brand mentions founders, … as expected, not a fault0.2ms
treats I can only draft and send to your saved contacts, and [email] isn't one yet — so I've not queued anything. Add them as a contact, then ask me to draft. as expected, not a fault0.1ms
treats None of those contacts are confirmed deliverable. Drop that condition, or pick a different list. as expected, not a fault0.1ms
treats No email provider is connected yet, so nothing was sent. Add your sending credentials once in the Connectors panel — your 31 drafts are saved and untouched. as expected, not a fault0.1ms
still reports the genuine fault: Nhost GraphQL error: field 'enriched_source' not found in type: 'contacts_set_input'0.2ms
still reports the genuine fault: APIFY_BACKLINKS_ERROR_4000.1ms
still reports the genuine fault: tool 'seo_serp_spider' timed out after 120000ms0.1ms
still reports the genuine fault: report contract violation (campaign_stats): funnel.opened=1 exceeds sent=00.1ms
a tool asking the user a question is blocked, never failed · 3 tests
recognises the search_leads missing-argument refusal by its marker0.3ms
would NOT have caught that wording by prose alone — which is why the marker exists0.2ms
does not let the marker whitewash a genuine fault0.7ms
the clarify refusals across the tool surface carry markers · 5 tests
blocks, not fails: { needs_clarification: true, error: 'Which site? Tell me the domain (or set your site URL in the Product panel).' }0.2ms
blocks, not fails: { needs_clarification: true, error: 'What should the standing instruction be?' }0.1ms
blocks, not fails: { needs_clarification: true, error: 'What message should I post to Slack?' }0.1ms
blocks, not fails: { needs_clarification: true, error: 'Which topic or keyword should I find ideas for? Give me a short search term — for example "ai seo tools".' }0.1ms
blocks, not fails: { needs_clarification: true, error: 'What should I write about? Give me a topic/keyword, or a URL to rewrite.' }0.1ms
an error KEY is not an error VALUE · 6 tests
does NOT call a successful search failed just because the key exists0.3ms
does NOT call an honest zero-result search failed either0.1ms
still recognises a real failure0.2ms
treats an EMPTY-STRING error as a real failure, not a success0.2ms
ignores non-objects rather than guessing0.2ms
regression: the OLD predicate called every search a failure0.2ms
expected outcomes found in the 2026-08-25 telemetry sweep · 11 tests
treats as expected: read_url own-site internal directive (15 events, largest family)0.5ms
treats as expected: rag_readiness honest empty on a thin/client-rendered page (11 events)0.1ms
treats as expected: pause_campaign named a campaign that does not exist0.1ms
treats as expected: resume_campaign, same class0.1ms
treats as expected: enroll_in_sequence named a list that does not exist or is empty0.1ms
treats as expected: a third-party site refused us with HTTP 4030.2ms
treats as expected: a third-party page could not be read0.1ms
treats as expected: a run that dropped an unrecognized engine and PROCEEDED anyway0.1ms
treats as expected: a crawl that reached the host and returned zero pages0.2ms
treats as expected: the same, after JS rendering had already been tried0.1ms
treats as expected: the pre-v2.565.0 phrasing still resolving from held jobs0.1ms
genuine faults from the same window still report · 8 tests
still reports: a hallucinated tool name reaching the dispatch default0.1ms
still reports: an unresolved hallucinated name with no canonical target0.5ms
still reports: a plan that failed while persisting0.1ms
still reports: a drafting step that genuinely failed — the copy says so0.1ms
still reports: a raw TypeError surfacing through a tool result0.2ms
still reports: the zero-yield breaker tripping0.1ms
still reports: a provider 5020.1ms
still reports: a provider 4000.1ms
brand-boilerplate.vitest.ts
81/81 19ms · 5 suites PASS
src/seo/brand-boilerplate.vitest.ts
the live incident · 2 tests
rejects the exact page that produced it3.4ms
rejects the string itself wherever it is declared1.0ms
boilerplate the crawler will meet · 42 tests
rejects "Loading..."0.5ms
rejects "Loading"0.2ms
rejects "Just a moment..."0.3ms
rejects "One moment"0.3ms
rejects "Redirecting…"0.3ms
rejects "Please wait"0.3ms
rejects "Checking your browser before accessing"0.3ms
rejects "Attention Required! | Cloudflare"0.3ms
rejects "Verifying you are human"0.2ms
rejects "Access Denied"0.2ms
rejects "Forbidden"0.2ms
rejects "403 Forbidden"1.0ms
rejects "404"0.2ms
rejects "404 Page Not Found"0.1ms
rejects "Not Found"0.1ms
rejects "Error"0.1ms
rejects "Site Maintenance"0.1ms
rejects "Under Construction"0.1ms
rejects "Coming Soon"0.5ms
rejects "Please enable JavaScript"0.2ms
rejects "JavaScript is required"0.2ms
rejects "You need to enable JavaScript to run this app"0.2ms
rejects "React App"0.3ms
rejects "Vite + React"0.3ms
rejects "Web site created using create-react-app"0.2ms
rejects "Untitled"0.1ms
rejects "Untitled Document"0.2ms
rejects "Document"0.1ms
rejects "New Page"0.1ms
rejects "Default Web Site Page"0.1ms
rejects "Home"0.1ms
rejects "Homepage"0.1ms
rejects "Welcome"0.3ms
rejects "index.html"0.2ms
rejects "My Website"0.2ms
rejects "App"0.2ms
rejects "Website"0.1ms
rejects "Domain for sale"0.1ms
rejects "This domain is parked"0.2ms
rejects ""0.1ms
rejects " "0.1ms
rejects "A"0.1ms
real brands that merely CONTAIN those words · 29 tests
keeps "Home Depot"0.3ms
keeps "Homebase"0.3ms
keeps "HomeAway"0.3ms
keeps "Welcome Pickups"0.1ms
keeps "Error Solutions Ltd"0.1ms
keeps "Loading Dock Equipment Co"0.1ms
keeps "Documental"0.1ms
keeps "Document Crunch"0.1ms
keeps "AppLovin"0.1ms
keeps "Appian"0.1ms
keeps "Apptio"0.1ms
keeps "Website Toolbox"0.1ms
keeps "Indexed Finance"0.1ms
keeps "Index Ventures"0.1ms
keeps "Moment Energy"0.1ms
keeps "Just Eat"0.1ms
keeps "Wait Less Health"0.1ms
keeps "Access Softek"0.1ms
keeps "Denied Claims Recovery"0.1ms
keeps "Maintenance Connection"0.1ms
keeps "Construction Junction"0.1ms
keeps "Soon Technologies"0.1ms
keeps "JavaScript Mastery"0.1ms
keeps "Untitled Art"0.1ms
keeps "Coming of Age Media"0.1ms
keeps "Forbidden Root Brewery"0.1ms
keeps "The 404 Agency"0.1ms
keeps "Reactive Apps Inc"0.1ms
keeps "Nuxt Labs"0.1ms
the ladder falls through instead of giving up · 5 tests
takes og:site_name when the JSON-LD org name is boilerplate0.3ms
takes the WebSite name when both stronger tiers are boilerplate0.2ms
takes the good half of a mixed title0.2ms
scans past a boilerplate Organization to a real one0.2ms
returns null only when EVERY tier is boilerplate0.2ms
unchanged behaviour for ordinary sites · 3 tests
still prefers Organization JSON-LD0.4ms
still prefers the shorter title segment as the brand0.2ms
still returns null on a page that declares nothing0.2ms
contract-gaps.vitest.ts
80/80 34ms · 4 suites PASS
src/tools/contract-gaps.vitest.ts
list_contacts — the silently-partial answer · 5 tests
declares `all`, the opt-out that had no way to be expressed3.9ms
declares `campaign`, so a named campaign can be asked for directly0.3ms
still accepts the two it always had0.5ms
says in its own rules why `all` matters1.2ms
rejects an off-schema field rather than ignoring it0.5ms
send_emails — channel stays OUT while the feature is off · 6 tests
does not declare channel — a dormant capability is not part of the contract0.3ms
rejects channel if the model tries it anyway0.5ms
says nothing about Gmail in the model-facing copy0.7ms
keeps the two-step confirm contract declarable0.4ms
rejects a fabricated draft id shape0.4ms
still states that confirm is never self-granted0.3ms
enrich_contacts — the single-list ceiling · 2 tests
declares `list_names`, which the dispatch already preferred0.2ms
still accepts the singular and specific contacts0.3ms
every schematised tool holds the same line · 67 tests
every one is registered1.0ms
seo_backlink_gap rejects unknown fields and names no vendor or USD price2.3ms
seo_backlink_verify rejects unknown fields and names no vendor or USD price0.3ms
seo_backlink_deep_scan rejects unknown fields and names no vendor or USD price0.3ms
seo_backlink_value rejects unknown fields and names no vendor or USD price0.4ms
search_leads rejects unknown fields and names no vendor or USD price0.4ms
seo_keywords rejects unknown fields and names no vendor or USD price0.3ms
aeo_visibility rejects unknown fields and names no vendor or USD price0.3ms
generate_emails rejects unknown fields and names no vendor or USD price0.3ms
list_contacts rejects unknown fields and names no vendor or USD price0.2ms
send_emails rejects unknown fields and names no vendor or USD price0.3ms
enrich_contacts rejects unknown fields and names no vendor or USD price0.2ms
verify_contacts rejects unknown fields and names no vendor or USD price0.2ms
assign_to_campaign rejects unknown fields and names no vendor or USD price0.2ms
enroll_in_sequence rejects unknown fields and names no vendor or USD price0.2ms
set_campaign_sequence rejects unknown fields and names no vendor or USD price0.2ms
pause_campaign rejects unknown fields and names no vendor or USD price0.2ms
resume_campaign rejects unknown fields and names no vendor or USD price0.1ms
seo_geo_research rejects unknown fields and names no vendor or USD price0.2ms
diagnose rejects unknown fields and names no vendor or USD price0.4ms
seo_list_keywords rejects unknown fields and names no vendor or USD price0.9ms
seo_keyword_metrics rejects unknown fields and names no vendor or USD price0.4ms
seo_enrich_keywords rejects unknown fields and names no vendor or USD price0.7ms
find_competitors rejects unknown fields and names no vendor or USD price0.4ms
seo_competitor_gap rejects unknown fields and names no vendor or USD price0.4ms
seo_content_ideas rejects unknown fields and names no vendor or USD price0.4ms
seo_content_brief rejects unknown fields and names no vendor or USD price0.3ms
seo_write_content rejects unknown fields and names no vendor or USD price0.5ms
seo_onpage_audit rejects unknown fields and names no vendor or USD price0.3ms
seo_offpage_audit rejects unknown fields and names no vendor or USD price0.7ms
seo_backlinks rejects unknown fields and names no vendor or USD price0.2ms
seo_serp_spider rejects unknown fields and names no vendor or USD price0.2ms
entity_audit rejects unknown fields and names no vendor or USD price0.3ms
aeo_page_check rejects unknown fields and names no vendor or USD price0.2ms
aeo_full_audit rejects unknown fields and names no vendor or USD price0.3ms
sov_trend rejects unknown fields and names no vendor or USD price0.4ms
panel_segments rejects unknown fields and names no vendor or USD price0.4ms
tap_volume rejects unknown fields and names no vendor or USD price0.3ms
add_contacts rejects unknown fields and names no vendor or USD price0.4ms
list_sent_emails rejects unknown fields and names no vendor or USD price0.3ms
start_sequence rejects unknown fields and names no vendor or USD price0.2ms
seo_generate_llms_txt rejects unknown fields and names no vendor or USD price0.2ms
seo_request_indexing rejects unknown fields and names no vendor or USD price0.2ms
seo_monitor rejects unknown fields and names no vendor or USD price0.2ms
seo_rank_track rejects unknown fields and names no vendor or USD price0.2ms
seo_content_quality rejects unknown fields and names no vendor or USD price0.2ms
seo_google_merge rejects unknown fields and names no vendor or USD price0.3ms
domain_email_readiness_audit rejects unknown fields and names no vendor or USD price0.3ms
cloudflare_fix_email_dns rejects unknown fields and names no vendor or USD price1.2ms
generate_dns_fix_prompt rejects unknown fields and names no vendor or USD price0.3ms
list_campaigns rejects unknown fields and names no vendor or USD price0.2ms
list_sequences rejects unknown fields and names no vendor or USD price0.2ms
list_connectors rejects unknown fields and names no vendor or USD price0.2ms
show_marketing_plan rejects unknown fields and names no vendor or USD price0.2ms
create_campaign rejects unknown fields and names no vendor or USD price0.2ms
campaign_stats rejects unknown fields and names no vendor or USD price0.2ms
create_sequence rejects unknown fields and names no vendor or USD price0.3ms
connect_connector rejects unknown fields and names no vendor or USD price0.3ms
set_standing_instruction rejects unknown fields and names no vendor or USD price0.2ms
scan_product rejects unknown fields and names no vendor or USD price0.2ms
backlink_outreach_search rejects unknown fields and names no vendor or USD price0.2ms
create_marketing_plan rejects unknown fields and names no vendor or USD price0.3ms
propose_outbound_run rejects unknown fields and names no vendor or USD price0.2ms
web_search rejects unknown fields and names no vendor or USD price0.2ms
read_url rejects unknown fields and names no vendor or USD price0.2ms
define_icp rejects unknown fields and names no vendor or USD price0.2ms
get_usage_breakdown rejects unknown fields and names no vendor or USD price0.2ms
marketing-plan-report.vitest.ts
72/72 79ms · 14 suites PASS
src/reports/marketing-plan-report.vitest.ts
create_marketing_plan report · 25 tests
renders the checklist with statuses, effort, token-only costs, and Execute chips3.8ms
explains a non-executable PROPOSED item in plain language, never a validator message0.6ms
renders the measurement contract honestly: measuring line vs non-learning notice0.6ms
renders strategy thesis + hypotheses with live status1.0ms
renders the SCR-labeled diagnosis: Situation, Complication, Resolution3.9ms
Complication states plainly when no blocker was found, never a blank section0.5ms
show_marketing_plan reuses the renderer and adds the Recommendations section0.6ms
returns null for errors and empty plans0.4ms
forensic contract: cap and status vocabulary hold on the fixture, and violations are caught2.9ms
forensic contract: plan_score stays in range, delta recomputes, no stray baseline on unmeasurable work0.8ms
groups initiatives under their named Growth Bet (US2)1.0ms
renders three named execution horizon sections when initiatives carry a horizon (US4)0.4ms
falls back to the flat/measurable-split layout when no initiative carries a horizon (FR-015)0.6ms
renders Impact/Feasibility badges when present, falls back to Effort otherwise (US3, FR-015)1.0ms
the cost chip and Effort fallback share the rating badge's slim geometry, not the bulkier chip-tag0.3ms
Execute is the primary CTA (brand green modifier); Dismiss stays secondary/ghost0.3ms
renders Value-at-Stake as an explicitly unverified, visually distinct benchmark (US5)0.3ms
renders a pre-feature plan (no new fields at all) completely and without error0.4ms
forensic contract: prior-session fixtures (plan_score, bare fixture) still pass cleanly0.4ms
forensic contract: bet count outside [3,5] on a >=5-initiative plan is caught0.4ms
forensic contract: an invalid horizon value is caught0.2ms
forensic contract: the hard-blocker item must be horizon=foundation0.6ms
forensic contract: value_at_stake without unverified:true is caught0.3ms
forensic contract: value_at_stake coexisting with a real measurement is caught0.2ms
forensic contract: a numeric (non-enum) impact/feasibility rating is caught0.2ms
create_marketing_plan — execution integrity (specs/013 US1) · 4 tests
renders a manual step as a deliberate item, not a degraded one0.5ms
never renders internal vocabulary — the exact strings that shipped in the fixtures0.6ms
an executable item still gets its Execute affordance0.3ms
renders a pre-013 plan (no execution_mode at all) exactly as before — FR-0250.9ms
create_marketing_plan — forensic contract (specs/013 US1) · 4 tests
fails a plan whose copy carries a validator message0.4ms
fails an item that claims to be executable with nothing to execute0.3ms
fails a third execution state — FR-003 admits exactly two0.3ms
passes a well-formed plan carrying both modes0.3ms
show_marketing_plan — progress markers (specs/013 surface 2) · 5 tests
shows work done and time spent together — the pairing the panel lacked31.2ms
names the next measurement date instead of leaving the user to guess0.6ms
states plainly when nothing has been re-measured yet0.4ms
names a stalled plan outright rather than rendering a quiet 0%0.9ms
renders no progress strip for a plan with no progress data — FR-0250.4ms
show_marketing_plan — the ledger still holds pre-013 leaks (specs/013 surface 2) · 1 test
never renders a legacy validator message read back out of the DB0.4ms
show_marketing_plan — lineage across re-plans (specs/013 Option A) · 3 tests
tells the user their earlier progress survived the re-plan0.4ms
stays silent on a first plan — lineage on plan 1 would be noise0.3ms
singularises correctly — one day, one item0.4ms
marketing plan — organised by directive (specs/013 US10) · 5 tests
renders all four classes in fixed order — Fix, Try, Understand, Expect0.7ms
an empty class states its own absence rather than vanishing (FR-030)0.3ms
never invents a Predictive item — the class is gated until audit Q30.7ms
keeps 005 horizons and Growth Bets nested INSIDE a directive, not replaced (FR-026)0.7ms
a pre-013 plan with no directive on any row renders exactly as before (FR-025)0.4ms
marketing plan — directive forensic contract (specs/013 US10) · 5 tests
fails a corrective item resting on nothing measurable — FR-0310.5ms
fails a generated Predictive item — gated until audit Q3 (RULING-2)0.2ms
fails a PARTIALLY classified plan — rows without a directive fall out of every group0.2ms
fails an unknown directive value0.2ms
passes an all-unclassified pre-013 plan and a fully-classified one alike0.3ms
marketing plan — leads with a judgement (specs/013 US4) · 2 tests
leads with the finding, and demotes the objective below it0.3ms
falls back to the old inventory line rather than fabricating a finding (GS-009)0.3ms
marketing plan — the complication is theirs, not ours (specs/013 US5) · 3 tests
keeps capability-unlock copy OUT of the Complication0.7ms
each gap carries the action that resolves it0.4ms
renders no gaps block at all on a pre-013 plan (FR-025)0.3ms
marketing plan — never instructs a rebuild (specs/013 US7) · 1 test
states the grounding gap as a finding, with no instruction to pay again0.7ms
marketing plan — the invisible moat, said out loud (specs/013 US8) · 5 tests
states how many dismissed actions were excluded — a chatbot cannot do this0.6ms
singularises, and stays silent when nothing was excluded0.5ms
explains what the plan score measures0.4ms
a day-0 plan says FIRST BASELINE, never "unchanged 0 points"0.5ms
still reports a REAL zero-change once something has been re-measured0.4ms
show_marketing_plan — the shell is hydrated on EVERY surface (GS-005) · 4 tests
the builder still emits placeholders — they are filled downstream, by design0.3ms
artifact mode fills title, timestamp and id1.2ms
live mode does NOT claim to be a saved artifact0.8ms
render integrity flags an unhydrated shell, so no future surface can ship one silently1.9ms
show_marketing_plan — one population per number (the 9 / 6 / 13 screen) · 5 tests
the header counts the PLAN, and says so0.3ms
the header count reconciles with the progress denominator0.3ms
orphan recommendations are counted separately, not folded in or dropped0.2ms
no "Suggested" tile when there are none — a zero tile is a slot filled to look complete0.3ms
singular reads as English0.3ms
misroute-guard.vitest.ts
69/69 56ms · 8 suites PASS
src/chat/misroute-guard.vitest.ts
isDiagnosticQuestion · 22 tests
recognises "why is my AI visibility down?"3.8ms
recognises "why am I not getting any replies?"1.3ms
recognises "why is my traffic dropping"0.3ms
recognises "what is holding my site back"0.3ms
recognises "did anything change with my rankings"0.2ms
recognises "what happened to my open rate"0.2ms
recognises "how come nobody replies to my emails"0.3ms
recognises "what's wrong with my deliverability"0.2ms
recognises "why are my citations falling"0.4ms
recognises "what is causing the drop in leads"0.3ms
does not fire on the conversational "why do you need my website URL?"0.2ms
does not fire on the conversational "why can I not skip this step"0.4ms
does not fire on the conversational "what happened to the button that was here"0.3ms
does not fire on the conversational "why is this taking so long"0.1ms
does not fire on the conversational "why does the chat keep asking for my domain"0.3ms
leaves the measurement/action ask "how is my AI visibility" alone0.7ms
leaves the measurement/action ask "check my AI visibility" alone0.3ms
leaves the measurement/action ask "run a full AEO audit" alone0.1ms
leaves the measurement/action ask "find me 50 CTOs at fintech startups" alone0.1ms
keeps the first-person complaint "why can't I get any replies" diagnostic0.2ms
keeps the first-person complaint "why am I not getting replies" diagnostic0.1ms
needs a subject or a decline, not a bare why0.5ms
isEvidenceFirstGated · 19 tests
never gates diagnose itself — that would be a loop0.3ms
never gates the core tool web_search0.2ms
never gates the core tool read_url0.1ms
never gates the core tool generate_emails0.2ms
never gates the core tool send_emails0.2ms
never gates the core tool list_contacts0.9ms
never gates the core tool add_contacts0.2ms
never gates the core tool list_campaigns0.1ms
never gates the core tool create_campaign0.1ms
never gates the core tool search_leads0.1ms
never gates the core tool scan_product0.2ms
never gates the core tool campaign_stats0.1ms
never gates the core tool create_marketing_plan0.1ms
never gates the core tool show_marketing_plan0.1ms
never gates the core tool propose_outbound_run0.2ms
never gates the core tool diagnose0.2ms
gates the paid report tools a why-question actually misroutes into0.5ms
gates every non-core tool the billing table prices1.8ms
does not gate an unpriced tool0.2ms
hasExplicitActionIntent · 2 tests
sees the action in a mixed ask17.3ms
does not count diagnose itself as an action5.2ms
a factual lookup does not collect an evidence step · 5 tests
stands down on "How much have I spent so far, and on what?"0.9ms
stands down on "How many contacts do I have?"0.2ms
stands down on "Which campaigns are running?"0.2ms
still augments a real why-question whose tools are all free2.4ms
still REPLACES a paid report on a question that is not diagnostic by shape0.4ms
resolveMisroute · 9 tests
redirects a paid report on a why-question1.0ms
carries the user's own words through so diagnose answers what was asked0.5ms
stands down when the model already called diagnose0.4ms
allows the paid tool once diagnose has already run0.2ms
fires at most once per run0.1ms
stands down when the message also carries an explicit action ask0.6ms
AUGMENTS a turn with no priced tool in it — the evidence step is not about cost0.8ms
leaves a measurement ask completely alone0.3ms
replaces only the priced calls and dedupes0.3ms
diagnose intent registration · 2 tests
is registered on the LLM router0.2ms
does not outrank search_leads for a lead ask0.5ms
the ladder — which rung a why-question goes to (Phase 2) · 8 tests
sends a question about SOMEONE ELSE'S page to the primitives, not to diagnose0.6ms
sends a question about the tenant's OWN numbers to diagnose0.4ms
treats a named URL as outside when we do not know the tenant's site0.2ms
ignores www and scheme when deciding whose site it is0.4ms
flags the tenant's OWN site when it is named alongside a competitor's0.3ms
does not claim the own site was named when only outsiders were0.2ms
caps how many pages one redirect will read0.3ms
still stands down when the user explicitly asked for the paid thing2.4ms
a revenue question is REPLACED by diagnose even when the model chose only free tools (2026-09-15) · 2 tests
seo_google_merge is displaced for "why did my sales drop"0.7ms
a non-revenue why-question on a free tool still augments, as before0.3ms
plans.vitest.ts
66/66 41ms · 12 suites PASS
src/billing/plans.vitest.ts
plan resolution · 4 tests
follows the payment signal by default3.1ms
lets an explicit override win, so testing never needs a fake payment row0.4ms
ignores a malformed override rather than failing open to paid0.6ms
accepts case and whitespace, since the value comes from a settings row0.2ms
depth resolution · 5 tests
narrows to the plan cap but never widens past what was asked0.3ms
leaves a dimension the plan does not cap to the tool default0.2ms
reports when it trimmed, which is what warrants an offer0.4ms
mirrors the caps that are live today0.8ms
models search_leads as delivery-capped over a fixed fetch, and marks it withholding0.5ms
defaults are fail-safe · 7 tests
falls back to the plan default for an unlisted tool0.9ms
never denies on an unknown plan id — it falls back to free, not open0.3ms
denies only what was already paid-only0.7ms
bounds the run count of EVERY paid tool on the free plan0.2ms
bounds an unlisted paid tool by default, and leaves free tools alone0.4ms
never counts a paid user — the balance is their bound1.5ms
classifies every tool exactly once0.9ms
uplift offers · 9 tests
leads with what was delivered, not with what is missing0.4ms
makes NO offer when nothing was actually withheld0.2ms
never upsells a plan that already has the thing0.1ms
covers every exit reason — a capped exit with no offer is a dead end0.6ms
only claims results are "ready" when they were actually paid for0.3ms
offers once for the biggest shortfall, not once per capped dimension0.3ms
says nothing when the run was not trimmed0.2ms
reads withholds from the PLAN, not from the caller0.4ms
never quotes USD0.5ms
plan simulation · 3 tests
allows everything when the plan sets no run caps0.5ms
reports what a candidate plan would have prevented1.5ms
is empty-safe0.2ms
coverage · 1 test
resolves a limit for every priced tool, on every plan10.3ms
identity-constrained dimensions · 5 tests
intersects with the allowlist before applying the count0.6ms
never lets a count cap pick an engine the plan does not permit0.4ms
still yields a usable run when the ask and the allowlist do not overlap0.2ms
leaves paid selections alone0.2ms
applies the count cap on dimensions with no allowlist1.4ms
cron entitlements · 4 tests
caps the weekly rank scan for free, which runs for every tenant and is not opt-in0.4ms
withholds the weekly SoV composite from free — one run is a full AEO fan-out0.2ms
disables an undeclared cron rather than defaulting it on0.2ms
leaves a dimension the plan does not cap alone0.3ms
search_leads run cap (the -1.87M incident) · 3 tests
allows three free runs — a COUNT still, because the provider path still bills late0.3ms
does not cap the paid plan0.2ms
cannot be solved by depth — cost is identical at either delivery cap0.3ms
depth caps added in the second pass · 10 tests
seo_enrich_keywords caps keywords at 10 free / 300 paid0.3ms
seo_keyword_metrics caps keywords at 10 free / 200 paid0.2ms
seo_serp_spider caps pages at 15 free / 30 paid0.2ms
seo_onpage_audit caps pages at 50 free / 300 paid0.1ms
seo_competitor_gap caps rows at 10 free / 25 paid0.2ms
seo_offpage_audit caps rows at 10 free / 25 paid0.5ms
seo_backlinks caps rows at 5 free / 10 paid0.2ms
brings seo_enrich_keywords under the bonus0.4ms
caps both once a real row parameter exists0.2ms
leaves aeo_full_audit undeclared, since its sub-tool governs the fan-out0.2ms
web_search is priced per QUESTION, not per run (ledger #13) · 2 tests
does not inherit the paid default meant for 10-50x pricier tools0.4ms
is still bounded on free — cheap is not unlimited0.2ms
appsumo (lifetime deal) — 3 stack levels · 13 tests
is reachable only through the override or a real redemption — never from hasPaidTopUp alone0.3ms
a real top-up does NOT lift a redeemed buyer to paid — the caps are permanent by design0.2ms
the test override still wins over a real redemption, for QA0.2ms
falls back to free, not open, on the malformed-override path0.3ms
keeps paid-level depth at every stack level — a lifetime deal is not a crippled starter tier0.5ms
scales run counts 1x/2x/4x across stack levels — a stacking INCENTIVE, not linear 1x/2x/3x0.4ms
caps aeo_visibility tighter than its neighbours at every level — it alone was 39.6% of measured real spend0.3ms
caps the #2 real driver too, even though it is not an LLM/CoT tool0.2ms
still bounds search_leads by count, same reasoning as the -1.87M incident on free0.3ms
gives web_search its own generous line instead of the generic fail-safe default0.2ms
bounds an unlisted paid tool by default at every level — no unlimited surface on a flat one-time price0.4ms
leaves seo_request_indexing denied, same as free, since the underlying provider question is still open0.2ms
withholds the weekly SoV composite by default, same reasoning as free0.3ms
filter-corpus.vitest.ts
64/64 42ms · 5 suites PASS
src/tools/filter-corpus.vitest.ts
every corpus request has a schema-valid destination · 35 tests
f01 "enrich all the contacts in the list developer-leads that a" validates on enrich_contacts5.3ms
f02 "re-enrich everyone in developer-leads, including the ones " validates on enrich_contacts0.6ms
f03 "re-enrich my developer-leads, the data is stale" validates on enrich_contacts0.6ms
f04 "refresh the research on everyone in developer-leads" validates on enrich_contacts0.3ms
f05 "enrich the developer-leads contacts we have not researched" validates on enrich_contacts0.8ms
f06 "which of my contacts have never actually been verified?" validates on list_contacts0.4ms
f07 "show me the contacts with a confirmed working email" validates on list_contacts0.3ms
f06-inv "list everyone whose email we have checked and confirmed" validates on list_contacts0.4ms
f08 "show everyone in developer-leads except the ones in do-not" validates on list_contacts1.5ms
f09 "who in developer-leads still doesn't have an email address" validates on list_contacts0.6ms
f10 "show me everyone I haven't emailed yet" validates on list_contacts0.9ms
f11 "list the people we have already reached out to" validates on list_contacts0.3ms
f12 "enrich the developer-leads contacts that have an email and" validates on enrich_contacts0.3ms
f13 "show my unverified contacts that I have not contacted yet" validates on list_contacts0.5ms
f16 "draft emails to everyone in developer-leads I haven't emai" validates on generate_emails0.5ms
f17 "verify the ones with no verification yet in developer-lead" validates on verify_contacts0.3ms
f18 "re-verify everything in developer-leads, I do not trust th" validates on verify_contacts0.4ms
f19 "add everyone in developer-leads I haven't contacted to the" validates on assign_to_campaign0.4ms
f20 "enroll the developer-leads contacts with a verified email " validates on enroll_in_sequence0.2ms
f01 reaches the same filter whether the list arrives as list_name or in_lists1.5ms
f02 reaches the same filter whether the list arrives as list_name or in_lists0.7ms
f03 reaches the same filter whether the list arrives as list_name or in_lists0.3ms
f04 reaches the same filter whether the list arrives as list_name or in_lists0.2ms
f05 reaches the same filter whether the list arrives as list_name or in_lists0.2ms
f08 reaches the same filter whether the list arrives as list_name or in_lists0.2ms
f09 reaches the same filter whether the list arrives as list_name or in_lists0.2ms
f12 reaches the same filter whether the list arrives as list_name or in_lists0.1ms
f16 reaches the same filter whether the list arrives as list_name or in_lists0.2ms
f17 reaches the same filter whether the list arrives as list_name or in_lists0.2ms
f18 reaches the same filter whether the list arrives as list_name or in_lists0.1ms
f19 reaches the same filter whether the list arrives as list_name or in_lists0.2ms
f20 reaches the same filter whether the list arrives as list_name or in_lists0.2ms
folds list_names (plural) too, and de-duplicates against the filter0.8ms
does not invent a list from the literal strings a model sends for "none"0.2ms
covers both tools and every kind, so the corpus cannot quietly narrow0.4ms
a complement is a DIFFERENT query — the §11.6 acceptance · 4 tests
f01 and f02 do not compile to the same where0.8ms
f06 and f06-inv do not compile to the same where0.3ms
f10 and f11 do not compile to the same where0.7ms
the owner's inverse drops the enriched predicate entirely, rather than negating it0.5ms
paraphrases converge on one canonical form · 4 tests
f03 resolves identically to f020.3ms
f04 resolves identically to f020.2ms
f05 resolves identically to f010.2ms
f07 resolves identically to f06-inv0.2ms
the ceiling holds: an ask outside the vocabulary has no destination · 2 tests
f14 "enrich the ones that replied but never opened th" cannot be expressed, and that is correct0.4ms
f15 "enrich the good ones" cannot be expressed, and that is correct0.2ms
every corpus filter can explain itself to the user · 19 tests
f01 produces a disclosure sentence0.4ms
f02 produces a disclosure sentence0.4ms
f03 produces a disclosure sentence0.2ms
f04 produces a disclosure sentence0.2ms
f05 produces a disclosure sentence0.2ms
f06 produces a disclosure sentence0.2ms
f07 produces a disclosure sentence0.3ms
f06-inv produces a disclosure sentence0.2ms
f08 produces a disclosure sentence0.2ms
f09 produces a disclosure sentence0.4ms
f10 produces a disclosure sentence0.2ms
f11 produces a disclosure sentence0.3ms
f12 produces a disclosure sentence10.2ms
f13 produces a disclosure sentence0.5ms
f16 produces a disclosure sentence0.3ms
f17 produces a disclosure sentence0.1ms
f18 produces a disclosure sentence0.2ms
f19 produces a disclosure sentence0.2ms
f20 produces a disclosure sentence0.1ms
present.vitest.ts
59/59 52ms · 11 suites PASS
src/chat/present.vitest.ts
records() — the invariant that makes the defect unrepresentable · 4 tests
returns null for zero rows, so no presenter can emit a header with no body3.8ms
returns null when every row is empty0.7ms
returns null with no columns0.5ms
caps rows and never lets total fall below what is shown2.8ms
wave 1 presenters · 12 tests
list_contacts keeps the column contract records-as-tables.vitest.ts already pins1.9ms
list_contacts says it is a page when it is one, and does not when it is not0.5ms
list_contacts carries the filter scope, so a narrowed list never reads as complete0.4ms
falls back to the email when a contact has no name0.3ms
campaigns, sequences and connectors each present as records1.6ms
campaign_stats is one funnel row with measured zeros — [6.4.1]1.3ms
create_campaign confirms itself with the name on screen — [6.1.1]0.8ms
seo_keywords drops an all-empty CPC column and says the ranking is not profitability — [4.8.1]0.9ms
an empty listing presents nothing, so the caller keeps its honest no-records copy0.2ms
errors and confirmation gates are not record listings0.2ms
an unpresented tool returns null rather than throwing — most tools are still migrating0.5ms
covers exactly the migrated tools — a floor, so a deleted presenter fails loudly0.4ms
marketing plan — the tools the judge caught at 0.30 · 6 tests
create_marketing_plan renders initiatives as records, not prose0.8ms
show_marketing_plan renders initiatives as records, not prose0.4ms
carries the plan score and thesis as scope rather than letting the model invent a header0.2ms
a null token_estimate stays empty — it means no extra cost, not a missing value0.2ms
never denominates the estimate in currency0.3ms
returns null on an empty plan, so no header ships without rows0.2ms
wave 4 — the report tools · 6 tests
every one is modelPathOnly and consumes nothing4.0ms
the manifest still goes to the model even though nothing is stripped0.4ms
the ASCII share bar becomes a real number0.3ms
seo_google_merge share-of-voice columns are named for what they hold0.4ms
backlink value is withheld where DR is unknown, as the markdown did0.5ms
a blocked backlink is never folded into present or absent0.7ms
wave 3 — a number keeps its value and the column wears the unit · 7 tests
numeric cells are raw; the prefix/suffix/decimals live on the column0.7ms
the em-dash for "no data" becomes null, never a string in a number column0.5ms
seo_list_keywords keeps the Rank column when any row has a position, or when the read failed with a note0.6ms
crux history takes the TAIL — the recent periods, not the oldest fifty0.5ms
a multi-table tool emits one block per table, and drops the empty ones1.2ms
every wave-3 presenter declares NO lead — the formatter keeps its synthesis sentence1.4ms
a failed PSI run keeps its row and reports the error in the fix column0.2ms
wave 2 — the leads/email family · 7 tests
search_leads renders from `all`, not the five-row `preview`1.1ms
search_leads consumes BOTH preview and all1.5ms
wave 2 declares NO lead — the formatter keeps its framing1.1ms
a search still running, or out of credits, presents nothing0.3ms
generate_emails and backlink_outreach_search carry every row1.5ms
NO sibling array survives that duplicates the presented rows0.7ms
Email is a DECLARED column on both, which is what switches selection on0.4ms
modelResultView — the model cannot transcribe what it cannot see · 2 tests
removes the consumed key and says so, keeping everything else intact2.1ms
is a no-op without a presentation, so unpresented tools are untouched0.3ms
blocksToMarkdown — the fallback serialisation · 2 tests
caps at the prose limit and names the remainder1.2ms
escapes a pipe that would otherwise split the row0.7ms
stripRenderedTables — the belt · 6 tests
removes a rowless table even when the turn attached no block at all1.5ms
leaves a real table alone when nothing was rendered for the user0.5ms
removes a populated duplicate only when a block was attached, and counts it0.3ms
handles alignment separators and several tables in one answer0.3ms
is a cheap no-op on text with no pipe at all0.4ms
never throws on a non-string0.3ms
entity_audit blocks — the three blocks say three different kinds of thing · 3 tests
declares Priority as severity, not status — so the pills rank instead of going flat grey1.5ms
marks the coverage disclosure as a note, and ONLY that block0.6ms
still emits the disclosure when the site is clean — that is the case it exists for1.0ms
a lead replaces a count line, never the model's answer · 4 tests
keeps the model's answer when the caller declares it model-authored0.6ms
still replaces the formatter count line the lead exists for0.2ms
falls back to the formatter when there is no lead and no message0.2ms
list_contacts still declares the lead — the wave-1 case is unchanged0.2ms
counterplan-eeat-wiring.vitest.ts
58/58 68ms · 16 suites PASS
src/seo/counterplan-eeat-wiring.vitest.ts
Q25 and Q24 are reachable from the sentence · 4 tests
both flags are computed and both are in the shortcut condition5.8ms
each is traceable to its own route, not merged into a neighbour0.8ms
the DISPATCHER publishes the brief's own headline, not a hardcoded one1.2ms
ONE renderer serves them, like every other brief1.3ms
both are built BEFORE the empty-evidence gate · 3 tests
the counter-plan is built above the gate0.6ms
the E-E-A-T brief is built above the gate0.3ms
and both are in the chain the gate returns1.3ms
the rival reading is shared, not duplicated · 2 tests
Q04 and Q25 gather ONCE between them1.6ms
each brief is computed once1.0ms
neither steals the tool it sits beside · 3 tests
a NAMED competitor domain still belongs to the paid keyword-gap tool1.2ms
"find my competitors" still goes to the tool that finds them1.2ms
backlink authority questions do not become E-E-A-T briefs1.1ms
both briefs answer rather than refuse when nothing is on file · 2 tests
the counter-plan says what was never measured1.5ms
the E-E-A-T brief says what was never checked1.4ms
Q09 and Q03 are reachable and traceable · 5 tests
both flags are computed and both are in the shortcut condition4.1ms
each has its own routeDebug label0.7ms
the same renderer serves them0.8ms
both are built above the empty-evidence gate and are in its chain0.7ms
each is computed once1.0ms
Q09 and Q03 do not steal the tools they sit beside · 3 tests
backlink fetch/scan/verify phrasings keep their tools1.0ms
a named competitor domain still belongs to the paid backlink gap tool0.2ms
keyword research and lookup phrasings keep their tools0.8ms
Q29 and Q10 are reachable and traceable · 4 tests
both flags are computed and in the shortcut condition4.1ms
each has its own routeDebug label and the shared renderer serves it1.3ms
Q29 is the FOURTH consumer of the shared technical evidence, not a new reader0.9ms
both are above the empty-evidence gate and in its chain0.7ms
Q29 and Q10 do not steal their neighbours · 3 tests
sitemap submission requests keep their tool0.9ms
a request to SHOW a number is a report, not a measurement question1.0ms
reporting questions with no search subject stay away0.4ms
Q27 and Q17 are reachable and traceable · 5 tests
both flags are computed and in the shortcut condition3.5ms
each has its own routeDebug label and the shared renderer serves it1.3ms
Q27 reads content_quality THROUGH Q24 rather than re-querying it0.8ms
Q17 does not shadow the keyword growth horizon0.4ms
both are above the empty-evidence gate and in its chain0.7ms
Q27 and Q17 do not steal their neighbours · 3 tests
a request to WRITE something keeps going to the writing tool1.5ms
measurement questions stay with Q100.9ms
"how long" about something other than search does not match0.3ms
Q07 is reachable, traceable, and shares the index reader · 5 tests
the flag is computed and in the shortcut condition2.3ms
reads the index through the shared gatherer, keeping ONE reader of serp_spider_urls0.8ms
uses the SHARED url normaliser rather than a fourth local one0.4ms
is above the empty-evidence gate and in its chain0.5ms
does not fire on a request to BUILD a site1.2ms
the playbook questions are reachable and rendered by their OWN block · 5 tests
all five route through the shortcut2.5ms
each is traceable to its own label1.4ms
renders through a SEPARATE block, not the hypothesis renderer1.1ms
survives the empty-evidence gate — a playbook never has evidence0.7ms
is computed once, from one shared gather0.5ms
the playbook predicates do not steal the briefs · 5 tests
Q02 keeps "prioritise the technical seo backlog"0.6ms
Q17 yields to Q20 on a multi-year investment question0.6ms
Q10 yields to Q26 when an audience is named0.5ms
one channel alone is not a channel-model question0.5ms
a place word alone is not a service-area question0.5ms
Q08 and Q13 are reachable · 3 tests
Q08 routes as a brief and Q13 as a playbook1.3ms
Q08 is above the empty-evidence gate and in its chain0.7ms
Q08 reuses the shared query classifier rather than re-deriving it0.5ms
the brief owns its title on BOTH return paths · 3 tests
computes the answer chain ONCE, above the gate0.9ms
the main return prefers the brief's headline over the generic one0.4ms
the empty-evidence return still uses it too0.2ms
gate.vitest.ts
56/56 35ms · 11 suites PASS
src/connectors/gate.vitest.ts
connectorGate — one shape for every connector · 10 tests
carries the connector, the chip target, and composed copy5.5ms
uses the registry label, so a rename propagates everywhere at once0.9ms
appends a genuine alternative when the caller has one0.5ms
slack offers a connect route0.4ms
google offers a connect route0.6ms
notion offers a connect route0.9ms
apollo offers a connect route0.6ms
wordpress offers a connect route0.6ms
vercel offers a connect route0.7ms
cloudflare offers a connect route0.7ms
connectorGate — DISABLED is not DISCONNECTED · 3 tests
a disabled connector offers NO connect route1.5ms
says the capability is unavailable, and points at what still works0.3ms
ignores `unlocks` for a disabled connector — there is nothing to unlock0.4ms
connectorGate — a gate is not a defect · 10 tests
slack gate is an EXPECTED outcome2.7ms
shopify gate is an EXPECTED outcome1.5ms
google gate is an EXPECTED outcome1.5ms
gmail gate is an EXPECTED outcome0.2ms
notion gate is an EXPECTED outcome0.2ms
apollo gate is an EXPECTED outcome0.1ms
wordpress gate is an EXPECTED outcome0.2ms
vercel gate is an EXPECTED outcome0.2ms
cloudflare gate is an EXPECTED outcome0.1ms
a genuine fault is still reported0.6ms
connectorDegraded — proceeded, but say what is missing · 1 test
keeps the payload and explains the gap without erroring0.9ms
isConnectorGate · 2 tests
recognises both enabled and disabled gates0.4ms
does not claim ordinary errors0.2ms
preflightConnector — answer before the turn is spent (§3.5) · 8 tests
a Google-gated tool with no Google connection is stopped up front0.5ms
names Search Console for a gsc_ tool and Analytics for a ga tool0.3ms
proceeds when the connector is live0.3ms
proceeds for a tool no connector blocks0.2ms
never pre-empts search_leads — it switches to the In-house source0.2ms
never pre-empts send_emails — it falls back to the configured provider0.1ms
never pre-empts domain_email_readiness_audit — it still returns its findings0.1ms
fails OPEN — a flaky connection read must not block a runnable tool0.3ms
reverse index · 3 tests
connectorBlocking maps a tool to the connector that makes it impossible0.3ms
toolsRequiring lists what a connector unlocks1.2ms
every blocked tool is also declared in actions — blocks is a SUBSET0.9ms
funnel-selling copy (§5.2) — sell breadth only where it exists · 7 tests
a broad connector names what else the one connection buys0.3ms
slack does not invent breadth0.3ms
notion does not invent breadth0.1ms
wordpress does not invent breadth0.1ms
vercel does not invent breadth0.1ms
apollo does not invent breadth0.2ms
names no internal tool slugs — CLAUDE.md §41.0ms
per-connector chip (§5.4) · 3 tests
names the connector, so the chip is a decision not a navigation instruction0.4ms
no chip for a disabled connector — it would be a dead end0.2ms
keeps the panel-chip wire format the client already parses0.2ms
the email-provider gate leads with the button, not the prose · 5 tests
renders a human chip label, not the internal identifier0.4ms
offers the connect route0.3ms
states the block in ONE sentence and leaves the instruction to the button0.4ms
still protects the user's work when the caller passes it0.2ms
is recognised as a connector gate, so the chip layer picks it up0.2ms
connectorDegraded — say what was lost, and offer the fix · 4 tests
marks the result so the chip layer can see it0.4ms
is NOT a blocking gate — a degraded run produced a real answer0.2ms
offers no chip for a DISABLED connector — a panel with nothing to connect is a dead end0.2ms
says nothing for an ordinary result0.2ms
args.vitest.ts
55/55 53ms · 17 suites PASS
src/leads/args.vitest.ts
personaFromArgs — mapping, not guessing · 4 tests
reads the audience the model declared instead of classifying the sentence2.3ms
marks executive seniority from the titles asked for, not from words in prose0.4ms
department is always undefined now that business_function no longer exists0.3ms
takes geography from the declared country, never from a parsed city0.3ms
planFromArgs — strategy from declared fields · 3 tests
routes local business by the declared search_type, not a postcode regex0.9ms
routes to named domains when the model supplied them0.3ms
sends niche audiences to their curated source and everything else to the provider0.3ms
localPlanFromArgs — the pairing rule the geocode requires · 2 tests
pairs postcode with country and never with a locality0.3ms
falls back to free-text location when no postcode was given0.2ms
describeArgs — one request, one string · 2 tests
produces the same description regardless of how the request was phrased0.5ms
describes a local search by category and place0.2ms
argsFromLegacyQuery — carries the words, guesses nothing · 1 test
puts the whole sentence in topic and infers no provider filter1.6ms
list naming and Apollo titles · 2 tests
names a list from the request when the user did not0.4ms
prefers the titles the user asked for over the audience default0.9ms
rankByTopic — the other half of the topic contract · 4 tests
surfaces the lead whose organisation matches the intent0.5ms
keeps provider order when the topic says nothing0.3ms
drops nothing — ranking reorders, it never filters0.9ms
ignores words that carry no signal, so a whole sentence still ranks0.2ms
suggestIndustries — propose, never auto-filter · 5 tests
matches the taxonomy on the user's own words10.8ms
returns nothing when the overlap is not distinctive1.0ms
ignores taxonomy filler that would otherwise match everything0.2ms
prefers the more specific name among equal matches0.7ms
never proposes a value the provider would reject2.2ms
corpusRequestFromArgs · 5 tests
sends the role as a filter and never inside the query2.0ms
takes titles ONLY from person_titles, never the audience default0.7ms
sends the topic as match text and the industry as a filter — never both0.8ms
asks a local search for the business category and no role at all0.8ms
returns an empty query rather than a stray separator when nothing was given0.4ms
describeArgs survives model-shaped arguments · 3 tests
does not throw when an array field arrives as a bare string1.2ms
describes a scalar exactly as it describes the one-element array0.2ms
still handles null, undefined and empty arrays without inventing text1.1ms
corpusRequestFromArgs — who gets ranked first · 3 tests
a LOCAL search stops preferring business mailboxes0.2ms
a PEOPLE search keeps preferring them — the B2B default is unchanged0.5ms
the flag travels as an explicit boolean, never undefined1.2ms
corpusRequestFromArgs — firmographics reach the corpus · 6 tests
passes industry, geography and a headcount RANGE1.5ms
takes the WIDEST bounds across several requested bands0.6ms
omits bounds entirely when no size was asked for0.5ms
KEEPS A TRANSLATED INDUSTRY OUT OF THE QUERY — it is a filter, not match text1.0ms
KEEPS AN UNTRANSLATED industry in the query — there the text is all we have1.7ms
local business still sends no role and no firmographics0.3ms
country reaches the corpus in the alphabet the corpus stores · 4 tests
translates the provider’s country NAME into ISO-20.7ms
leaves an ISO code alone0.4ms
sends NOTHING rather than a string too long for the column0.4ms
never emits anything but two letters5.3ms
corpusRequestFromArgs — local business keeps its own geography (RCA 2026-08-29) · 5 tests
carries the locality into regions instead of dropping it0.6ms
carries the country, so the search does not silently fall back to the US default0.4ms
still ranks consumer mailboxes fairly and asks for no job title0.3ms
leaves a postal code unapplied rather than sending it as a region0.2ms
omits country when none was given rather than inventing one0.2ms
person_locality reaches the corpus as a region (migration 151) · 1 test
sends a city, not just a US state0.4ms
describeArgs treats a named company domain as a complete ask (2026-09-15) · 2 tests
"contacts at stripe.com" is described, not refused0.2ms
an empty ask is still empty0.2ms
a country we cannot filter on is DECLARED, never defaulted (2026-09-15) · 3 tests
"Antarctica" is carried out as countryUnmatched with no country filter0.4ms
a resolvable name and a bare ISO code carry no unmatched marker0.9ms
no country named means no marker either0.3ms
speech-act-orders.vitest.ts
51/51 28ms · 5 suites PASS
src/chat/speech-act-orders.vitest.ts
orders are recognised as orders · 16 tests
directive: "run my AI visibility panel now for kakunin.ai — 12 promp…"13.2ms
directive: "freeze my weekly AI visibility panel to the exact 8 prom…"2.6ms
directive: "Freeze the weekly panel to these exact prompts: best KYC…"0.2ms
directive: "Freeze my weekly tracking panel to exactly these 8 promp…"0.2ms
directive: "turn on weekly ai visibility tracking for kakunin.ai…"0.2ms
directive: "run an seo audit…"0.2ms
directive: "disable the weekly cron…"0.1ms
directive: "lock my prompt set…"0.1ms
directive: "pause my active campaign…"0.3ms
directive: "stop weekly tracking…"0.2ms
directive: "schedule the panel for Sundays…"0.1ms
directive: "re-run the AI visibility check…"0.1ms
directive: "why is my visibility down, and find me 50 CTOs at fintec…"0.1ms
directive: "do it…"0.1ms
directive: "ok do it…"0.9ms
directive: "go ahead…"0.1ms
the failures this module exists for stay questions · 14 tests
question: "We have 200 tokens of budget and one week. Is it smarter…"0.2ms
question: "budget is 200 tokens and one week, technical SEO or thre…"2.1ms
question: "technical SEO or three articles, given 200 tokens?…"0.7ms
question: "budget for one week. technical fixes or new articles?…"0.1ms
question: "limited budget this week. fix technical SEO, or write th…"0.1ms
question: "with one week and 200 tokens, should we fix technical SE…"0.1ms
question: "is it worth fixing technical SEO or writing three articl…"0.1ms
question: "one week of budget: fix the technical issues, or publish…"0.1ms
question: "Explain how AI search engines decide which sites to cite…"0.1ms
question: "my numbers dropped…"0.1ms
question: "why is my AI visibility down…"0.1ms
question: "Am I visible in AI search?…"0.1ms
question: "how do I improve the SEO of vercel.com?…"0.1ms
the whole 161K-token paraphrase class, counted0.8ms
the product can read its own copy · 13 tests
tool-format.ts weeklyTrackingLine: "stop weekly tracking"0.1ms
tool-format.ts weeklyTrackingLine: "track weekly with ChatGPT only"0.1ms
dashboard.ts prompt library: "Show my saved contacts"0.1ms
tool-format.ts chip: "Show my Share of Voice trend"0.1ms
tool-format.ts chip: "Show my tracked keywords"0.1ms
skills.ts chip: "View usage"0.1ms
dashboard.ts prompt library: "Fix my safe email DNS issues in Cloudflare"0.1ms
tool-format.ts chip: "Re-run AI visibility check"0.1ms
tool-format.ts chip: "Rewrite this page for AEO+GEO"0.1ms
tool-format.ts chip: "Try the audit again"0.1ms
dashboard.ts prompt library: "Assign my contacts to my active campaign"0.1ms
dashboard.ts prompt library: "Enroll my contacts in my welcome sequence"0.1ms
dashboard.ts prompt library: "Show campaign performance for my active campaign"0.1ms
the previous predicate's false positives stay fixed · 4 tests
not an order: "do we have enough trust signals on our content…"0.3ms
not an order: "do we need an aeo agency or can we do geo in house…"0.1ms
not an order: "do we block gptbot and google-extended or allow them…"0.2ms
not an order: "our social and email and seo teams compete with each oth…"0.3ms
the closed-class guard is what makes the wide vocabulary safe · 4 tests
a declared verb after an overt subject is not an imperative0.2ms
politeness is stripped before the guard, or "can you check X" reads as the auxiliary0.2ms
an explanation verb is imperative in form and still not an order0.2ms
a verb whose complement is `why` orders understanding, not work0.1ms
directive-measurement.vitest.ts
47/47 50ms · 10 suites PASS
src/seo/directive-measurement.vitest.ts
Q29: robots governs crawl, not indexation · 6 tests
leads the decision with the distinction the question turns on3.3ms
explains why blocking cannot remove a page0.5ms
confirms the hypothesis from the pair, not from a single count0.5ms
kills it when nothing blocked is indexed0.5ms
names example URLs so the finding is checkable1.0ms
always states the correct division of labour, even with no fault found0.7ms
Q29: a clean sitemap can never be confirmed, only a dirty one disproved · 4 tests
states the coverage limit when status is mostly unknown0.5ms
adds a rule to generate the sitemap from the source rather than check it after0.4ms
does NOT report a coverage percentage when nothing was checked0.7ms
drops the caveat when coverage is complete0.4ms
Q29: contradictions are pairs and are reported as such · 3 tests
flags blocked-and-in-the-sitemap as two files disagreeing0.5ms
keeps the staging hypothesis permanently untested0.9ms
separates "never checked" from "checked and clean"0.4ms
Q10: rankings and domain rating are demoted UNCONDITIONALLY · 6 tests
stays a diagnostic for {}18.3ms
stays a diagnostic for {"rows":[{"keyword":"a","impressions":5000,"c1.1ms
stays a diagnostic for {"searchRead":false,"analyticsRead":false}0.4ms
stays a diagnostic for {"rows":[{"keyword":"acmewidgets","impression0.3ms
says so in the decision, in the rule's own terms0.3ms
produces the contract even with nothing connected0.4ms
Q10: branded demand is separated from earned demand · 3 tests
flags branded credit when most clicks are people searching the name0.6ms
kills it when the clicks are mostly earned0.3ms
reports visibility without visits as the case for the rule0.5ms
Q10: the answer box is a signature, never a sighting · 5 tests
detects the pattern0.5ms
refuses to claim it observed one0.4ms
ignores queries below the sample floor0.2ms
ignores queries too low to have earned a click0.3ms
ignores a healthy CTR0.2ms
Q10: no ROI number is offered without a revenue join · 5 tests
keeps the revenue hypothesis untested and names WHICH of the three states it is in0.4ms
distinguishes analytics being SILENT from nothing being counted0.6ms
warns against quoting a return figure0.4ms
marks the assisted-enquiry KPI as an intention rather than a number0.5ms
asks for the one figure that would change that, and states the lag0.4ms
Q29 and Q10: no internal vocabulary reaches the user (GS-005) · 1 test
keeps field and table names out of the prose1.2ms
policy briefs deliver their policy even when nothing survives · 3 tests
Q29 states the division of labour rather than refusing0.5ms
Q10 states the contract rather than refusing0.4ms
and BOTH still carry the honest coverage sentence — the policy is added, not substituted0.5ms
countUrlSignals: the derivation nothing was testing · 11 tests
a null status is UNKNOWN, never 2000.8ms
counts a known status0.3ms
a null status in the sitemap is not counted as a broken sitemap entry0.3ms
counts blocked-and-indexed as the pair it is, and keeps examples0.6ms
caps the example list at five1.6ms
counts blocked-and-submitted, and a 4xx in the sitemap0.9ms
counts only ALLOWED parameter URLs — a blocked one is already handled0.4ms
treats `unverified` as not-checked, never as refused0.2ms
collects the indexed URL LIST, not just the count (Q07 needs the list)0.3ms
caps the indexed URL list2.9ms
does not mutate the base it was given0.5ms
industry-vocabulary.vitest.ts
43/43 85ms · 10 suites PASS
src/leads/industry-vocabulary.vitest.ts
the case this exists for · 2 tests
finds farms when the user says agriculture4.8ms
says what it searched instead of what was typed1.0ms
tenant be12ebf7's ICP — the six words that returned nothing · 8 tests
places permanent crop in the vocabulary rather than dropping it to free text0.9ms
places tree fruit in the vocabulary rather than dropping it to free text0.4ms
places nuts in the vocabulary rather than dropping it to free text0.5ms
places citrus in the vocabulary rather than dropping it to free text0.4ms
places berries in the vocabulary rather than dropping it to free text0.5ms
places vines in the vocabulary rather than dropping it to free text0.3ms
reaches wine and spirits, which no key could reach at all2.1ms
resolves the whole ICP to farms and vineyards, not to nothing0.9ms
exact stored values pass through untouched · 3 tests
does not expand a term that IS a stored value0.5ms
emits no note when nothing was expanded0.5ms
is case- and whitespace-insensitive0.3ms
word containment, NOT similarity · 4 tests
resolves software to the stored values containing it as a word0.4ms
does NOT match a town because it shares letters with an industry0.5ms
A ONE-LETTER FRAGMENT IS NOT A WORD: e-commerce must not resolve to e-learning0.4ms
still honours genuinely short industry words the user typed themselves0.4ms
phrases fall back to their words, and the exceptions are listed · 4 tests
reaches the head word of a phrase0.3ms
does NOT send defense contractors to construction firms0.4ms
does NOT send veterinary clinics to hospitals0.2ms
does NOT drag oil and gas into a renewable energy search0.3ms
the table normalises its OWN keys, or half of them are unreachable · 4 tests
oil and gas reaches oil & energy0.3ms
m&a reaches investment banking0.2ms
k-12 reaches primary/secondary education0.2ms
import/export reaches import and export0.2ms
the corpus decides what is offerable · 3 tests
never returns a value the corpus does not hold0.3ms
reports an unplaceable term rather than dropping it0.7ms
keeps the matched part and declares the rest on a mixed request0.5ms
translation and relaxation are two different promises · 4 tests
does not fold food manufacturing into agriculture in the CORE tier0.4ms
widens ONLY when the core tier found nothing, and reports it as relaxed0.5ms
never widens when the core tier could serve the request0.5ms
gives the widening its own sentence and its own verb0.4ms
widenIndustries — the retry a zero-result search asks for · 5 tests
offers adjacent values even when the core tier resolved fine1.2ms
EXCLUDES everything the first pass already tried0.9ms
offers nothing for a concept with no adjacent entry, so no retry is made0.5ms
names the widening as a widening, not as a translation0.6ms
says nothing when nothing was widened0.2ms
the tables cannot rot silently · 6 tests
every target in BOTH tiers is spelled as the corpus spells it1.4ms
resolves every stored corpus value to itself17.6ms
resolves EVERY value the model is allowed to emit35.9ms
does not let a generic tail eat the sector in <Subject> Manufacturing0.9ms
keeps the tail when the tail IS the sector0.4ms
covers the concepts a B2B user actually types2.8ms
reveal.vitest.ts
43/43 35ms · 9 suites PASS
src/leads/shared/reveal.vitest.ts
verification age · 3 tests
treats a missing verification date as NOT verified2.9ms
holds a verification current up to the window and not past it0.5ms
refuses to treat an unparseable date as a verification0.2ms
source ownership · 2 tests
answers NULL when the corpus did not tell us — never false, never true0.4ms
counts our own corpus as owned and a bought rung as not0.5ms
revealSelection · 11 tests
claims purpose "reveal" — the caller never supplies it2.8ms
bills an UNVERIFIED identifier at the reduced rate, and still reports it as unverified0.9ms
bills a currently-verified identifier whose verdict is VALID1.1ms
reveals but bills NOTHING when the verdict says the address is dead1.0ms
bills nothing when the corpus refuses (empty result is not an error)3.8ms
bills nothing when the rights gate rejects the call outright2.0ms
bills nothing on a repeat reveal of the same identifier1.2ms
does not disclose anything when the selection is not this tenant's1.3ms
falls back through all three projections when the corpus predates migration 060, and stamps null lineage1.2ms
bridges to a contact whose provenance says the corpus supplied it and nobody checked it1.7ms
still records the disclosure when the contact bridge fails0.8ms
what the user is told · 3 tests
never calls an unchecked address verified0.4ms
states the age when the check has lapsed0.2ms
says nothing was charged on a repeat0.2ms
contact provenance columns · 1 test
never travels with verified_at0.3ms
verificationSells — recency AND a valid verdict · 9 tests
sells a fresh VALID verdict0.3ms
never sells a proven-dead address, however fresh the check0.2ms
does not sell risky (catch-all) — the domain answered, the mailbox did not0.2ms
does not sell unknown — the probe never completed0.2ms
does not sell unverified0.2ms
fails closed when the corpus does not tell us the verdict0.2ms
a stale VALID verdict does not sell either — both halves are required0.3ms
never sells without a timestamp at all0.2ms
leaves verificationIsCurrent as a RECENCY test — it drives the staleness copy0.3ms
verificationPriceFactor — the half-price ruling · 8 tests
pays FULL for a confirmed mailbox0.2ms
pays HALF for a DERIVED catch-all, which has no mailbox date at all0.2ms
pays HALF for a PROBED catch-all too0.2ms
pays NOTHING for an address we proved bounces, however fresh0.2ms
drops a STALE domain classification to the unverified rate, not to half0.2ms
FAILS CLOSED when the corpus cannot say WHAT it checked0.2ms
prices genuinely UNVERIFIED data down, not to zero (owner ruling 2026-08-18)0.3ms
never pays full price for a domain-scoped "valid"0.2ms
revealNote — what the customer is told they bought · 2 tests
says the DOMAIN was checked, not the address, when charging half price0.3ms
still warns plainly when nothing was checked at any level0.2ms
an outage is reported as unavailable, never filed as a refusal · 4 tests
the ownership check not answering is `unavailable`, not `not_found`1.1ms
a RAISE from the ownership RPC is still `not_found`0.4ms
the corpus not answering is `unavailable` after ONE round trip — no fall-through to two more projections1.4ms
a rights rejection is still `refused`, after all three projections1.9ms
outcome-rollup.vitest.ts
41/41 24ms · 10 suites PASS
src/billing/outcome-rollup.vitest.ts
grouping: the RUN is the unit, not the call · 4 tests
sums every row sharing a runId into one run5.0ms
separates distinct runs of the same outcome0.6ms
ignores rows with no run attribution0.4ms
never lets a negative amount enter a cost stat0.5ms
measured vs modelled · 3 tests
one non-reported row disqualifies the WHOLE run0.3ms
a floor-sourced row also disqualifies it0.2ms
unmeasured runs count toward n but never toward the distribution0.5ms
quantiles are nearest-rank, never interpolated · 2 tests
returns an observed value0.8ms
is total on the edges0.4ms
cost floor by sampling tier · 6 tests
tier A uses p90 and requires n>=300.9ms
tier B bounds with max x 1.25 at n>=3, because a p90 is not computable0.7ms
tier B still refuses below n=30.3ms
tier C is deferred — never priceable this sprint0.6ms
an all-zero distribution is an instrumentation gap, not a free outcome0.3ms
reports WHY an outcome is unpriceable, so it is never silently estimated0.2ms
orchestration attribution · 5 tests
folds a turn's orchestration into its single outcome0.6ms
splits equally across a turn's outcomes, not proportionally to their cost0.4ms
does not double-count when one turn runs the same tool twice0.8ms
leaves a no-tool turn's orchestration unattributed1.3ms
reproduces the canary: run-level rollup saw 32% of the real cost0.8ms
token conversion stays identical to the biller · 2 tests
uses the same anchor as usage.ts0.3ms
produces the same tokens as apiCostToTokens0.3ms
what counts as a measurement · 6 tests
a provider-reported figure always counts0.2ms
a verified flat rate counts0.3ms
an estimate from a provider that DOES report is a capture miss, never a measurement0.3ms
an unverified constant is never a measurement0.2ms
floor and NULL are never measurements0.2ms
unblocks the canary run that the strict rule rejected0.2ms
gemini reconciliation (July 2026 Google invoice) · 4 tests
derives the all-in per-request rate the API_COST constant now carries0.3ms
quantifies how far the old constant was out0.2ms
shows the bill is tokens, not a grounding-request fee0.2ms
gemini now reports its own cost rather than leaning on a constant0.2ms
outcomes that escaped the pilot · 4 tests
a turn with no run_tool ANYWHERE strands its whole cost0.4ms
declaring the outcome recovers the whole run0.4ms
a zero-spend run is still counted, and still collects its orchestration0.7ms
the marker's $0 is its exact cost, not a missing measurement0.3ms
failed runs never enter a price · 5 tests
counts only delivered runs toward the priceable set1.0ms
a $0 failed run cannot drag the floor below the real cost0.5ms
an all-failed outcome is unpriceable, and says why0.4ms
reads the status off the tool_run marker row0.3ms
treats an unknown exit as not priceable0.4ms
capability-denial.vitest.ts
41/41 21ms · 5 suites PASS
src/chat/capability-denial.vitest.ts
detects a capability claim · 9 tests
flags "I am not able to execute this task as it requires additional tools and functionality beyond what is available in the given functions."3.2ms
flags "That is beyond what is available in the given functions."0.6ms
flags "I don't have a tool for that."0.2ms
flags "I do not have access to a tool that can send email."0.2ms
flags "There is no function available to do that."0.2ms
flags "That is outside the scope of the available functions."0.3ms
flags "This is not available in my current toolset."0.2ms
flags "I lack the tools to complete this request."0.3ms
flags "I cannot do that without the appropriate function."0.3ms
stays off everything else — the corpus that matters · 19 tests
ignores "I need your website URL first — tell me your domain and I will take it from there."0.7ms
ignores "I need a product brief before I can tailor that."0.3ms
ignores "Which list should I save these to?"0.1ms
ignores "You're out of tokens for this billing period. Top up to keep going."0.1ms
ignores "That confirmation timed out — approvals expire after 10 minutes."0.2ms
ignores "Insufficient token balance for lead search (~663K tokens)."0.2ms
ignores "I hit a safety check on that response and can't share it as written. Could you rephrase what you need, and I'll try again?"0.1ms
ignores "I couldn't find any leads matching that."0.1ms
ignores "No contacts saved yet."0.1ms
ignores "I can only send to 200 recipients in one go."0.1ms
ignores "State-level targeting is not available yet, so I matched on country instead."0.1ms
ignores "I'll use the lead search function to find them."0.1ms
ignores "Running the lead search tool now."0.5ms
ignores "That feature is ready whenever you are."0.1ms
ignores "I've queued this feature for you."0.5ms
ignores "This is a straightforward template rendering — no tool needed. Here is your drafted email:"0.1ms
ignores "No tool is required for this."0.1ms
ignores "No function is necessary — the variables are already resolved."0.1ms
ignores empty and nullish input0.2ms
the correction states the fact and does not command · 3 tests
names the tool the model said it lacked5.4ms
leaves a genuinely-wrong tool free to be declined honestly0.5ms
removes only the explanation we disproved0.2ms
the fallback is honest about what did NOT happen · 4 tests
never implies the action ran0.6ms
withdraws the false reason rather than repeating it0.3ms
does not invent a cause we have not established0.3ms
reads as prose, not as an identifier0.2ms
the gate in runChatV2 — refutability is the whole safety property · 6 tests
requires the named tool to have actually been offered0.2ms
only applies to the high-stakes intents, at confident classification0.3ms
corrects at most once per run1.1ms
never spends the last turn on a correction0.1ms
does not touch tool_choice — nothing is forced0.1ms
reports every occurrence, because the rate is the point0.1ms
heavy-tier-phase2.vitest.ts
41/41 61ms · 11 suites PASS
src/reports/heavy-tier-phase2.vitest.ts
campaign_stats — an active campaign that never sent is stalled, not healthy · 3 tests
says stalled, and says what to check4.8ms
is no longer headed "signals healthy"1.0ms
a live campaign that IS sending is untouched1.2ms
campaign_stats — advice needs a measurement behind it · 2 tests
does not talk about subject lines on a campaign that never sent one0.7ms
still gives the tuning advice once there is something to tune0.9ms
campaign_stats — the funnel and the send count measure different populations · 4 tests
labels the contradiction instead of hiding it0.5ms
does NOT silently correct the numbers0.5ms
stays quiet when the campaign has actually sent0.6ms
stays quiet on an all-zero funnel — there is no contradiction to explain0.8ms
entity_audit — a score from one query is provisional · 4 tests
greys the score and labels it rather than painting full confidence1.2ms
says how little it read, in words1.0ms
does NOT change the arithmetic0.3ms
a broader audit scores in colour with no caveat0.8ms
search_leads — the batch path carried a count and nothing else · 4 tests
the async result now carries what the renderer reads0.3ms
derives tiers per lead, not as a hard-coded label0.3ms
bounds `all` but keeps `found` true, and says when the cap bit0.5ms
the renderer shows rows and an honest overflow once the shape is right1.1ms
campaign_stats — the funnel is rebuilt on emails_sent · 6 tests
queries this campaign's own replies, clicks and bounces0.4ms
the reply RATE no longer counts replies to other campaigns0.4ms
the contact lifecycle survives under its own name, never mixed into the funnel0.6ms
a current run cannot render the impossible state at all0.5ms
shows the delivery stages once there is delivery0.6ms
an artifact stored BEFORE the rebuild keeps its old shape and its label (trap 14)5.6ms
seo_google_merge — an absence claim needs enough data to be an absence · 4 tests
says "not enough to tell", not "nothing found", on thin data1.7ms
names the threshold and the best page against it, not the site total3.5ms
engagement and striking-distance say WHY they cannot answer1.3ms
a site with real volume still gets the clean-bill verdict0.6ms
the backlink report analyses the anchors instead of asking the user to · 5 tests
never tells the reader to go and check the sample2.3ms
classifies the anchors and names the pattern1.8ms
calls out a keyword-heavy profile as the thing to dilute2.0ms
recognises branded anchors as healthy1.5ms
states the provider profile total as coverage, not as the sample1.2ms
generate_emails — markup never reaches a draft body · 3 tests
strips incoming markup before the link guards inspect the body0.4ms
only strips when markup is actually present0.4ms
does not strip the html WE add afterwards1.3ms
seo_content_quality — "which category leads" named the weakest one · 4 tests
names the weakest category, and the heading matches the answer0.5ms
contrasts it with the strongest, so the gap is the finding0.3ms
picks the weakest by SCORE, not by array position0.8ms
stops reporting our own check coverage as a finding about the page0.5ms
domain_email_readiness_audit — the parts must add up to the header · 2 tests
accounts for every issue, whatever its severity11.9ms
says nothing extra when the two named severities already cover them1.2ms
numeric-band.vitest.ts
40/40 28ms · 7 suites PASS
src/tools/numeric-band.vitest.ts
the dead end this replaces · 2 tests
the lexical ladder offers nothing for a band mismatch4.2ms
and when it does offer something, it is worse than nothing2.1ms
parseBand · 3 tests
reads the shapes models actually emit2.2ms
refuses anything that is not a band0.7ms
refuses a backwards range rather than silently swapping it0.4ms
expandNumericBand · 6 tests
maps the LinkedIn bands onto ours by OVERLAP1.1ms
handles the open-ended top band0.4ms
reports whether the match widened the request0.5ms
returns nothing for a non-band value0.9ms
never fires on a vocabulary that is not numeric bands0.5ms
returns nothing when the range misses every band0.2ms
the live call, end to end through the validator · 6 tests
no longer rejects the LinkedIn bands1.5ms
produces legal, de-duplicated bands1.6ms
discloses the widening instead of applying it silently0.5ms
says nothing when our own bands are used0.3ms
still rejects a value that is not a size at all0.6ms
keeps the RAW values on the rejected path so the error names what the model sent0.4ms
the words a brief is actually written in · 15 tests
resolves mid-market0.4ms
resolves midmarket0.2ms
resolves SMB0.2ms
resolves SME0.6ms
resolves enterprise0.3ms
resolves startup0.4ms
resolves large0.3ms
resolves small business0.5ms
resolves Fortune 5000.4ms
reads a number carrying ordinary words: 200-2000 employees0.5ms
reads a number carrying ordinary words: over 10000.4ms
reads a number carrying ordinary words: at least 2000.3ms
reads a number carrying ordinary words: 200+ employees0.3ms
reads a number carrying ordinary words: 1,000 employees0.3ms
reads a number carrying ordinary words: no more than 500.3ms
a segment name is an interpretation, and is said out loud · 4 tests
discloses what it took the word to mean, even though the edges line up exactly0.4ms
tells the user how to override it0.3ms
uses the OTHER sentence for a boundary that genuinely moved0.5ms
says nothing when the value was already one of ours0.3ms
it cannot leak into a vocabulary that is not sizes · 4 tests
does not turn enterprise into a band on a non-band enum0.6ms
does not turn large into a band on a non-band enum0.2ms
does not turn medium into a band on a non-band enum0.2ms
does not turn small into a band on a non-band enum0.1ms
keyword-guard.vitest.ts
39/39 15ms · 7 suites PASS
src/seo/keyword-guard.vitest.ts
isRegistrableKeyword — the write-path junk guard · 5 tests
accepts normal search terms4.6ms
rejects the exact junk classes seen in prod0.8ms
enforces the length ceiling and non-empty rule0.6ms
rejects control chars and non-ascii (en/US registry only)0.2ms
rejects sentence-like blobs over the word-count backstop0.7ms
normalizeKeywordForVolume — DFS forbidden-symbol stripping · 4 tests
strips the "?" that 40501-rejected whole enrichment batches in prod (2026-07-09)0.4ms
strips other DFS-forbidden symbols and collapses whitespace0.7ms
keeps clean keywords untouched (hyphens and apostrophes survive)0.2ms
returns empty string for symbol-only input0.2ms
classifyIntent — precedence · 2 tests
transactional beats commercial beats informational0.4ms
defaults unmodified topics to informational0.2ms
computeKES — (volume × cpc) / difficulty · 3 tests
computes the efficiency score with difficulty0.3ms
treats null difficulty as 1 (volume×cpc first-pass) and null vol/cpc as 00.3ms
floors difficulty at 1 so a 0 difficulty never divides by zero0.2ms
opportunity scoring — first-party GSC keywords must not sink to zero · 2 tests
weights striking-distance positions highest0.3ms
blends KES with behavioral demand so a KES=0 GSC keyword still scores0.3ms
a URL is an observation, not a target · 14 tests
rejects "https://golfstreams.me/"0.2ms
rejects "https://portal.web.nhk/kakunin"0.1ms
rejects "https://www.kakunin.cloud/"0.1ms
rejects "www.kakunin.ai"0.1ms
rejects "rhetoric.com"0.2ms
rejects "nqz.ai"0.1ms
rejects "freeconvert.com"0.1ms
rejects "iancloud.ai"0.1ms
rejects "rhetoric-index.org"0.1ms
rejects "advoira.com"0.1ms
rejects "rhetoricaudit.com"0.1ms
keeps "openai.com pricing" — a domain inside a phrase is a real query0.2ms
keeps "is notion.so down" — a domain inside a phrase is a real query0.1ms
keeps "g2.com vs capterra reviews" — a domain inside a phrase is a real query0.1ms
a string with no word in it cannot be targeted · 9 tests
rejects "509 x 3"0.1ms
rejects "4 x 4"0.1ms
keeps "iso 27001"0.1ms
keeps "sox 404"0.1ms
keeps "pci dss 4.0"0.1ms
keeps "soc 2"0.2ms
keeps "eu ai act 2026"0.1ms
keeps "ai 2026"0.1ms
keeps "web 3.0 identity"0.4ms
lead-estimate.vitest.ts
38/38 17ms · 8 suites PASS
src/billing/lead-estimate.vitest.ts
the quote follows the rung that will actually answer · 10 tests
quotes the CORPUS when the corpus runs first — as a range, not a point2.7ms
is cheaper than the provider quote it replaces — the DIRECTION is the invariant0.6ms
lets a BRAND-NEW user run a lead search on the signup bonus alone0.4ms
SCALES with limit, so one number cannot be wrong in both directions at once0.5ms
a free-tier search fits comfortably inside the signup bonus0.2ms
scales with the ask, because the corpus bills per delivered lead0.3ms
reverts to the PROVIDER quote the moment paid sources are allowed0.3ms
quotes the provider when the corpus is not in play at all0.4ms
local_business quotes the corpus range like every shape — the Maps rung is gone, so its ceiling must not gate the run (2026-09-18)0.4ms
never assumes the corpus is free — it reads the price0.3ms
the ladder stops where the quote stops · 3 tests
refuses to buy from a provider without allow_paid_sources0.3ms
the stop still sits below the corpus tier, so the quote covers everything above it0.6ms
returns what it DID find rather than failing the search0.5ms
every quoting path prices the same search the same way · 4 tests
estimateForCall routes search_leads through searchLeadsEstimate WITH the context0.2ms
the agent loop actually passes the tenant context, not undefined0.5ms
the lead shortcut passes it too0.3ms
the two paths produce the SAME number for the same search0.9ms
search_candidates orders totally, not just by score · 2 tests
both ORDER BYs end in the unique entity id0.2ms
the tiebreak is the LAST key — it must not outrank score0.2ms
lead search quote reads the mode from the arguments that drive the run · 3 tests
quotes the local-business ceiling when the shape is local, even if search_type says people0.2ms
a postal code alone is enough — it is a local-only field0.2ms
leaves a genuine people search on the cheap corpus quote0.2ms
the lead quote is a range, and the gate reads its ceiling · 5 tests
quotes corpus-rate at the bottom and provider-rate at the top0.2ms
the spread is real — a range whose ends are equal is not a range0.2ms
SAFETY DOES NOT MOVE: the ceiling is still the provider rate0.2ms
a call whose LOW end is under the gate threshold still gates on its ceiling0.8ms
the gate message names BOTH ends, not just the ceiling0.8ms
searchLeadsEstimate sizes to the requested count · 7 tests
reads `count`, the field the schema actually declares0.3ms
a LARGER ask quotes MORE — the under-quote direction is the unrecoverable one0.4ms
still honours `limit` for internal callers that build args by hand0.2ms
`count` wins when both are present0.2ms
falls back to 25 only when neither is given, and never to zero or NaN0.5ms
clamps an absurd ask rather than quoting it0.1ms
the corpus rung sizes to the ask too0.2ms
the lead-search floor is the one this tool pays · 4 tests
is well above every measured run, and well below the generic agent floor0.3ms
does not move ORCHESTRATION_FLOOR, which governs every other tool0.4ms
is the fixed part of the quote at every size0.4ms
the size-that-fits offer subtracts the same floor it quoted0.2ms
shared-tier.vitest.ts
38/38 51ms · 10 suites PASS
src/leads/shared-tier.vitest.ts
the flag · 3 tests
is off unless explicitly set to 13.8ms
is SEPARATE from the route flag — a reachable API is not an earned tier position0.6ms
costs nothing at all when off — the fetch is never even called2.7ms
a hostile client cannot reach the caller · 5 tests
a client that hangs forever returns on time, not never5.5ms
a REJECTED promise degrades, never throws0.7ms
a SYNCHRONOUS throw inside the fetch degrades too0.6ms
a wrong-shaped return is treated as nothing, not spread into the ladder1.0ms
passes real results straight through0.5ms
the budget · 2 tests
clears a Neon cold start at the current corpus scale, and still leaves search_leads enough of its 120s2.9ms
a timeout does NOT leave a timer holding the isolate open2.3ms
silence is not an answer · 1 test
a degraded tier says so, because "nobody like that" and "we could not ask" differ2.0ms
the lead-source question is only asked when it has two real answers · 4 tests
checks the Apollo connection BEFORE rendering the picker0.3ms
falls through to the SAME selection path rather than a second lead flow0.7ms
SAYS the choice was made for them, with the way to change it0.7ms
still asks when Apollo IS connected — then both answers are real0.2ms
our corpus is searched before the provider, and priced to the market · 11 tests
runs above the paid boundary, not below it0.3ms
no longer triggers the dead people-search actor0.5ms
costs the user $0.01 per lead — the market rate we chose to match0.6ms
is CHEAPER than the provider rung, never dearer0.4ms
records the repricing as a DECISION, with the market data behind it0.5ms
the estimate must read the price, never assume the corpus is free0.3ms
bills on DELIVERY, and bills it in exactly ONE place0.9ms
only counts rows that can actually receive an email0.2ms
hands the SHORTFALL down the ladder rather than replacing the run0.2ms
reports the split instead of one blended list2.0ms
never names a vendor in a user-facing label (CLAUDE.md §4)3.7ms
the tenant reaches the gate · 4 tests
a tenant-list flag arms the WRAPPER, not just the factory1.0ms
refuses a tenant not on the list0.5ms
refuses when no tenant is supplied at all0.3ms
the ladder passes its tenant to the gate1.3ms
sharedLeadsTier — a skipped rung explains itself · 4 tests
returns a note when the tier is off, not a bare empty result0.5ms
says NOT SEARCHED rather than empty — they are different facts0.5ms
names no internal flag or vendor0.4ms
stays silent when the tier is ON and simply found nothing0.6ms
a rung may answer with { leads, note } (2026-09-15) · 1 test
the note reaches the caller and the leads are the leads0.8ms
the wrapper passes the budget down and the degraded sentence up · 3 tests
the fetch receives the budget it must fit inside0.4ms
its own deadline and failure outcomes are degraded, and say so as OUR fault6.5ms
a rung outcome carrying `degraded` reaches the caller; one without stays shaped as before0.5ms
registry.vitest.ts
38/38 713ms · 10 suites PASS
src/tools/registry.vitest.ts
tool-name aliases (LLM hallucination guard) · 5 tests
maps confirmed hallucinated names to their canonical tool2.7ms
maps the two misfires found in the 2026-08-30 sweep0.5ms
every alias TARGET is a real registered tool0.9ms
passes real/unknown tool names through untouched0.3ms
has no chained or circular aliases (every target is a terminal dispatch name)1.1ms
V2_SYSTEM execution discipline · 5 tests
orders action on explicit asks instead of option menus0.5ms
forbids re-asking facts already established in the conversation0.6ms
requires tailored output when a brief is stored0.4ms
onboarding gate blocks tools, never knowledge — direct answers come first0.3ms
keeps the mandated pre-execution questions carved out (no contradiction with gates)0.6ms
connectAlias (connect_<name> hallucination guard) · 3 tests
resolves fused connect names with the connector arg1.5ms
ignores the real tool and unknown suffixes0.2ms
suffix list stays in sync with connect_connector's enum in V2_TOOLS0.8ms
find_competitors exposure (cheap competitor discovery) · 2 tests
find_competitors is registered so the LLM can actually call it0.7ms
routes bare "who are my competitors" to find_competitors, not the expensive aeo_visibility0.4ms
create_marketing_plan dual registration · 8 tests
is registered in V2_TOOLS so the LLM can call it0.2ms
is in the planner executable map so dispatch can run it692.5ms
its description never says free and prices in tokens only0.8ms
show_marketing_plan is dual-registered too0.5ms
create_marketing_plan has an LLM-synthesis-class timeout, not the 30s default0.6ms
B3 routing: ADVISORY asks route to the planner, imperatives to their tool1.1ms
regression 2026-07-22: "quick wins" must never DUMP a saved plan0.5ms
RESPONSE SHAPE contract: TL;DR-first exists and exempts short/gate turns0.3ms
V2_SYSTEM no-ask turns · 1 test
forbids tool calls on gratitude/acknowledgement/greeting turns0.2ms
V2_SYSTEM out-of-scope asks · 3 tests
instructs a direct, useful answer first — not an immediate redirect0.2ms
forbids inventing capabilities nqzai does not have when bridging back0.2ms
does not license skipping a tool call that DOES cover the ask0.2ms
V2_SYSTEM tabulation rule · 5 tests
scopes tabulation to data the MODEL composed — tool records are rendered for it0.2ms
forbids the header-and-separator-only table that shipped the 2026-08-28 defect0.2ms
caps column count so tables do not overflow the chat column0.1ms
still tells the model when NOT to tabulate0.2ms
keeps cells free of bold, consistent with the sparing-bold rule0.1ms
seo_google_merge compare exposure · 4 tests
exposes a compare parameter the model can actually set0.2ms
the parameter description says a snapshot cannot explain a change0.2ms
the TOOL makes compare mandatory for change questions0.3ms
the TOOL forbids escalating to paid tools before running the comparison0.2ms
tool-name aliases found in the 2026-08-25 sweep · 2 tests
maps the two confirmed misfires to their canonical tool0.2ms
leaves an unresolved hallucination alone rather than guessing a target0.2ms
panel-segments.vitest.ts
38/38 20ms · 8 suites PASS
src/seo/panel-segments.vitest.ts
segmentKey · 2 tests
is stable, slugged and namespaced by kind2.1ms
never yields a bare namespace for unusable input0.3ms
cleanSeededPrompts — the seed must survive hygiene · 7 tests
keeps the seed and lowercases it0.3ms
FIRST occurrence wins, so a seeded prompt is not replaced by an unseeded duplicate1.0ms
drops prompts that are too short or too long rather than truncating them0.4ms
accepts bare strings as well as objects0.3ms
normalises whitespace so two spellings of one prompt are one prompt0.3ms
respects the cap1.0ms
an empty seed string is null, never the empty string1.5ms
deriveKeywordSegment — the Q26 primitive · 9 tests
carries the keyword itself, not just the origin label1.8ms
reuses the registry junk gate rather than inventing a second one1.4ms
orders by impressions — a weekly charge should measure the terms that carry traffic0.2ms
keeps terms with no impressions rather than dropping them0.3ms
is tagged as a keyword segment with no locale0.2ms
asks an already-question query verbatim instead of wrapping it0.3ms
never emits a double question mark0.2ms
the SEED keeps the query exactly as Search Console reported it0.1ms
honours its limit0.6ms
seedIndex — the join Q26 performs against rank data · 3 tests
groups prompts by the term they measure0.3ms
omits unseeded prompts — they cannot be joined to a rank0.2ms
a keyword named __proto__ cannot reach Object.prototype0.2ms
segmentBudget — segments are a recurring spend multiplier · 5 tests
multiplies prompts by the engines the run will ACTUALLY use1.2ms
reports the YEARLY commitment, because weekly is what is being approved0.2ms
counts distinct engines — a duplicated engine is not a second fan-out0.2ms
never counts more than the active ceiling0.3ms
zero engines is zero cells, not a silent fallback to a default fan-out0.2ms
parseSegments / serializeSegments · 8 tests
round-trips a keyword segment WITH its seeds intact0.5ms
keeps two audience segments distinct — the Q14 comparison depends on it0.4ms
drops a duplicate key rather than storing two indistinguishable panels0.3ms
keeps locale ONLY on a language segment0.3ms
drops a segment with no usable prompts instead of keeping an empty shell0.6ms
rejects an unknown kind rather than defaulting it0.2ms
survives malformed storage without throwing — it is read on a cron0.4ms
enforces the active ceiling on read, not just on write0.4ms
segmentPromptTexts · 2 tests
yields exactly what seoGeoVisibility takes0.3ms
is empty, never undefined, for a missing segment0.2ms
upsertSegment · 2 tests
replaces by key and keeps the others in order0.4ms
adds when the key is new0.2ms
traffic-incident.vitest.ts
38/38 29ms · 11 suites PASS
src/seo/traffic-incident.vitest.ts
the two-source rule is structural, not advisory · 3 tests
never returns a confident verdict on one source2.4ms
every confident verdict in every shape names >= 2 sources1.6ms
an untested hypothesis always says what would settle it0.4ms
the rival hypothesis is KILLED by the scan that was ignored · 5 tests
marks it killed, not merely unmentioned0.6ms
says so out of the tenant's OWN measurements1.1ms
a scan that never ran is NOT "no competitors" — GS-0040.4ms
survives when a rival really is ahead, or a new one arrived0.4ms
does not claim movement it cannot see — a truncated read drops the third source0.9ms
tracking is the cheapest thing to rule out, so it is tested first · 4 tests
survives when the two systems disagree2.1ms
is killed when they moved together0.5ms
names the missing connector when only one system is present0.3ms
a move smaller than the stated threshold is not a direction0.2ms
indexation is never KILLED, because we cannot see index coverage · 2 tests
has only two possible verdicts0.6ms
survives only when the on-page audit and Search Console fell together0.2ms
breadth separates an update from a local fault · 3 tests
survives when three or more measurements fell together0.2ms
does NOT kill it when there are too few measurements to judge breadth0.3ms
is killed when one fell and its neighbours held0.3ms
seasonality is the question only the user can answer · 1 test
is always untested, in every shape, and always ends in a question0.4ms
the decision rule produces an instruction, never a summary · 7 tests
names the single survivor to act on0.6ms
refuses to pick between two survivors, and names the discriminator0.5ms
SAYS SO when nothing survives — the honest non-answer0.5ms
STILL stops when nothing at all could be tested0.4ms
the one-pager is always complete0.4ms
states the period, because a delta with no period cannot be checked0.2ms
degrades honestly with no readings at all — GS-009, never padded0.3ms
no internal vocabulary reaches the reader — GS-005 · 1 test
never names a tool, a field or a vendor0.7ms
a movement question about search reaches diagnose deterministically · 4 tests
routes on all three conditions, not on the intent alone2.7ms
runs diagnose and NOTHING else0.6ms
stands down on a compound ask, asking the SAME registry the AEO shortcut asks0.7ms
the shape axis is what keeps a STATE question out of it2.9ms
the brief never misdescribes its own evidence · 4 tests
an AMBIGUOUS spread is not reported as too few measurements0.4ms
still says "too few" when there genuinely are too few0.3ms
does not claim there are no readings when other measurements have two0.3ms
says it plainly when there is genuinely nothing0.2ms
the second Google-traffic reading can come from Search Console windows ([1.5.2] 2026-09-15) · 4 tests
a single stored snapshot no longer means "no second reading" when the history is on file1.2ms
a snapshot that already has a direction keeps it — the windows do not override a measured pair0.4ms
a thin pair of windows states the numbers and the pages but claims no direction0.7ms
without the history the wording is unchanged0.3ms
contracts.vitest.ts
37/37 51ms · 9 suites PASS
src/adoption/contracts.vitest.ts
adoption contracts · 3 tests
classifies current usage and preserves lapsed state3.0ms
assigns the same user to the same experiment bucket1.5ms
blocks unsafe and capped sends with machine-readable reasons24.7ms
evaluateNudgePolicy — bounced and tool_cooldown in isolation · 4 tests
blocks on bounced alone1.0ms
blocks on a tool nudged within the last 21 days0.9ms
does NOT block a tool nudged 21+ days ago0.5ms
a genuinely clean candidate is eligible0.6ms
computeConsecutiveIgnored · 5 tests
counts leading sent-but-unopened rows, newest first0.4ms
stops at the first opened send0.3ms
stops at the first non-terminal row — a rejected/pending draft says nothing about whether the USER ignored anything0.3ms
empty history is zero, not ignored0.3ms
an engaged user (most recent opened) is never in a backoff streak0.3ms
adoptionAutoSendActive · 3 tests
'1'/'true' = active now; junk/empty = off0.7ms
date value stays off before the date and turns on from 00:00 UTC that day1.2ms
malformed dates never activate0.3ms
isAdoptionUserExcluded · 3 tests
excludes a test tenant by id0.3ms
excludes an internal account by email (case/space-insensitive) — the newly-honored form0.3ms
lets a genuine user through0.3ms
summarizeAdoptionProfile · 3 tests
summarizes an empty profile honestly6.3ms
names never-used families plainly, without a fabricated count0.6ms
states an active family with real counts and recency, giving genuine contrast material0.4ms
extractPreSendCount · 2 tests
reads the invocations value captured at send/assignment time0.4ms
defaults to 0 for an empty or malformed bundle0.4ms
buildAccountFactEvidence · 10 tests
cites real contacts for a lead_discovery pitch0.7ms
cites unsent drafts for an email_generation pitch0.3ms
combines sent + replied for an outreach pitch0.3ms
still cites outreach when only replies exist (sent=0 but replied>0 is real signal)0.2ms
cites tracked keywords for a rank_tracking pitch0.2ms
cites content pieces for a reporting pitch0.2ms
returns null rather than a fabricated zero when the fact is empty0.3ms
returns null for ai_visibility — no bulk-fetchable account table yet, stays honest rather than fabricate one0.5ms
returns null for an unknown family0.2ms
floors negative/fractional inputs defensively rather than emitting a nonsense count0.3ms
detectAdoptionConversion · 4 tests
is a conversion: count increased AND last use postdates the send0.2ms
is NOT a conversion: no last_invoked_at at all0.1ms
is NOT a conversion: count did not increase, even with a recent timestamp0.2ms
is NOT a conversion: count increased but the last-seen timestamp predates the send (stale window recompute, not new activity)0.2ms
draft-batching.vitest.ts
37/37 357ms · 10 suites PASS
src/campaigns/draft-batching.vitest.ts
planDraftBatches · 6 tests
splits a 53-contact list so no batch can overflow the output budget — the incident case5.6ms
every recipient it attempts appears exactly once, in order2.7ms
a list inside the cap defers nothing0.9ms
handles an empty list without producing an empty batch0.8ms
opener mode batches larger — it emits one short opener each, not a full body0.7ms
a full-body batch fits the output budget with headroom1.5ms
runDraftBatches · 5 tests
accumulates every batch result in order1.9ms
one failing batch does not discard the others, and its reason is reported0.9ms
respects the concurrency cap — never more than N calls in flight5.0ms
passes a batch index so recipient numbering stays continuous across batches0.9ms
surfaces a non-Error throw as a string rather than "undefined"0.7ms
remaining count is derived from what landed, not what was planned · 3 tests
53 requested, 40 attempted, 32 delivered → 21 remaining, never 131.3ms
delivered + remaining always equals the original list size0.5ms
a fully delivered list leaves nothing remaining, so no note is shown0.3ms
wall-clock deadline · 4 tests
stops starting waves past the deadline and keeps what it already drafted0.5ms
always runs the first wave, even with a zero budget — never returns nothing0.4ms
a run inside its budget completes every batch0.7ms
a full-size run fits in ONE wave, so the deadline never bites in the normal case0.4ms
a single slow wave cannot eat the whole budget · 3 tests
abandons a wave that outlives the deadline and reports why65.0ms
keeps what earlier waves returned when a later one is abandoned61.8ms
does not abandon a wave that finishes inside the budget0.4ms
one slow batch does not discard its siblings · 2 tests
keeps the batches that finished when one hangs — the zero-drafts incident61.3ms
reports WHICH batch was abandoned, not just that something was63.4ms
personalized mode is bounded by research, not by the output budget · 2 tests
drafts no more contacts than it researches — otherwise openers are generic0.6ms
is stricter than the full-body cap, since research costs a call per contact0.2ms
deferredDraftNote · 4 tests
is empty when nothing was deferred, so callers can append unconditionally0.2ms
names both counts so the user knows what is still waiting0.2ms
reads correctly for a single deferred contact0.1ms
never puts a dollar figure in front of the user0.1ms
salvage — a run that produced nothing retries small · 4 tests
returns something when every batch times out31.0ms
does not fire when the run already produced drafts0.7ms
is itself bounded, and says so41.5ms
can be switched off entirely0.4ms
draftTimeBudget · 4 tests
gives the full budget (minus the post-processing reserve) when setup was instant0.3ms
shrinks the wall clock (not just ignores) when setup already spent real time0.2ms
never returns a budget that would push total elapsed past the tool ceiling, below the floor threshold0.3ms
floors the wall clock so a slow setup does not starve the main wave to near-zero — even trading away the no-overrun guarantee0.3ms
message-contract.vitest.ts
37/37 16ms · 2 suites PASS
src/chat/message-contract.vitest.ts
layer 2 — the assembled reply, per site-scoped tool · 36 tests
renders something only this tool could have produced3.5ms
I5 — no JS placeholder reaches the user0.6ms
I1 — no verdict word survives a degraded sample0.4ms
I2 — does not disclose a limitation and then act as if it had not0.5ms
I3 — no status code the outbound guardrail would silently rewrite0.5ms
I4 — degraded language never leaks into a healthy reply0.5ms
renders something only this tool could have produced0.5ms
I5 — no JS placeholder reaches the user0.5ms
I1 — no verdict word survives a degraded sample1.6ms
I2 — does not disclose a limitation and then act as if it had not0.3ms
I3 — no status code the outbound guardrail would silently rewrite0.4ms
I4 — degraded language never leaks into a healthy reply0.2ms
renders something only this tool could have produced0.4ms
I5 — no JS placeholder reaches the user0.2ms
I1 — no verdict word survives a degraded sample0.1ms
I2 — does not disclose a limitation and then act as if it had not0.1ms
I3 — no status code the outbound guardrail would silently rewrite0.1ms
I4 — degraded language never leaks into a healthy reply0.1ms
renders something only this tool could have produced0.4ms
I5 — no JS placeholder reaches the user0.2ms
I1 — no verdict word survives a degraded sample0.2ms
I2 — does not disclose a limitation and then act as if it had not0.1ms
I3 — no status code the outbound guardrail would silently rewrite0.1ms
I4 — degraded language never leaks into a healthy reply0.2ms
renders something only this tool could have produced0.5ms
I5 — no JS placeholder reaches the user0.2ms
I1 — no verdict word survives a degraded sample0.2ms
I2 — does not disclose a limitation and then act as if it had not0.1ms
I3 — no status code the outbound guardrail would silently rewrite0.2ms
I4 — degraded language never leaks into a healthy reply0.2ms
renders something only this tool could have produced0.3ms
I5 — no JS placeholder reaches the user0.2ms
I1 — no verdict word survives a degraded sample0.1ms
I2 — does not disclose a limitation and then act as if it had not0.1ms
I3 — no status code the outbound guardrail would silently rewrite0.1ms
I4 — degraded language never leaks into a healthy reply0.1ms
site-scoped tools with NO chat renderer · 1 test
is a known, listed gap1.4ms
guardrail.vitest.ts
37/37 156ms · 7 suites PASS
src/runtime/guardrail.vitest.ts
scanOutbound — BLOCK (fail-closed) · 4 tests
blocks and replaces a message containing the canary3.9ms
blocks secret-shaped strings (keys, JWTs, PEM)1.3ms
blocks a LABELED admin secret, but NOT a report hash + the word "admin"2.3ms
does NOT block reports that embed non-user UUIDs (report_id / run_id)0.4ms
scanOutbound — REDACT (fail-open) · 9 tests
redacts backend vendor names0.5ms
keeps user-facing allowlist names (Google products, Apollo picker)0.8ms
lets the Apollo partner referral link + CTA pass through untouched1.5ms
redacts USD amounts (token-only billing)0.6ms
redacts internal tool names and raw error codes0.8ms
redacts a name-dropped planner tool (live-hit 2026-07-22 regression)1.2ms
INTERNAL_TOOL_NAMES covers every V2_TOOLS entry (registry drift)123.1ms
a bare-word tool name is redacted as an IDENTIFIER but not as English1.1ms
underscore names stay redacted in plain prose — they are never English0.4ms
scanOutbound — allowCurrency (commerce turns, NQZAI-50/4R regression) · 9 tests
keeps merchant store revenue amounts when allowCurrency is set0.4ms
still redacts USD by default (platform-cost rule unchanged)0.3ms
TENANT_CURRENCY_TOOLS covers commerce AND Google revenue-report tools0.6ms
does not redact a dollar figure the user themselves wrote2.2ms
still redacts OUR costs on the same turn the user quoted THEIR figure0.4ms
matches the user echo across formatting differences, not across values0.3ms
with no userEcho, behaviour is exactly as before0.2ms
isRedactionBug: usd on a tenant-money turn pages; everything else is telemetry0.5ms
allowCurrency does NOT weaken vendor/secret rails0.5ms
scanOutbound — clean replies pass through UNCHANGED (anti-over-blocking) · 1 test
leaves ordinary intelligent replies byte-for-byte identical1.5ms
a redacted sentence still reads like English · 8 tests
rewrites: The seo_onpage_audit tool could not read your site.1.0ms
rewrites: the seo_onpage_audit tool could not read your site.0.3ms
rewrites: I used the search_leads function to find them.0.3ms
rewrites: The "diagnose" tool needs your domain.0.3ms
rewrites: Calling the "diagnose" function now.0.3ms
an unlabelled name still becomes the placeholder, with the frame repaired0.6ms
keeps a parenthetical that now names the operation, drops one that would be only a placeholder0.5ms
leaves the ordinary phrase alone when we did not put it there0.5ms
scanInbound — flag, never block · 2 tests
flags extraction / injection / fishing attempts2.3ms
does NOT flag normal product questions0.4ms
free-claim redaction · 4 tests
rewrites a claim that something is free0.5ms
rewrites the adjective form that names one of our no-extra-cost paths0.5ms
leaves negations, product nouns and idioms alone0.4ms
does not rewrite a tenant-money turn or the user's own words0.4ms
monitor-insight.vitest.ts
36/36 21ms · 10 suites PASS
src/chat/monitor-insight.vitest.ts
monitorInsight — "went up" is not "improved" · 4 tests
reads a RISE in pages-not-indexed as a loss3.6ms
reads a FALL in pages-not-indexed as a win0.5ms
reads NEW AI answer gaps as a loss and closed ones as a win0.5ms
reads regressed pages as a loss even though the count is positive0.4ms
monitorInsight — the verdict is the point · 4 tests
states the count of what improved and what slipped, before naming any of it0.4ms
says so plainly when everything moved one way0.3ms
puts a current level with its change on the chips, and nothing else1.6ms
keeps event counts out of the chips — they are not a reading0.5ms
monitorInsight — the count must cover every delta the tool reported · 3 tests
counts backlinks and referring domains as two distinct losses0.5ms
shows both on the chips, each with its own level1.4ms
does not double-count users against sessions0.4ms
monitorInsight — display order is declared once · 3 tests
orders traffic, then off-page, then AI, then on-page, then pages0.9ms
gives every movement a distinct rank1.1ms
has a written phrase for every declared measure1.2ms
monitorInsight — link velocity qualifies the verdict, it does not join the count · 5 tests
adds the qualifier as its own sentence, after the verdict0.3ms
states BOTH periods, because the word alone is not falsifiable0.3ms
says nothing when velocity is stable0.2ms
says nothing when the two periods are missing0.3ms
is not counted as a measure0.2ms
monitorInsight — Search Console joined with Analytics · 5 tests
counts search clicks and the two underperforming-page counts0.4ms
prints revenue as a DIRECTION with no figure — it is the tenant's own money, uncurrencied0.4ms
does not count google_merge sessions against ga_traffic sessions0.2ms
does not count joined_rows — measurement coverage is not a result0.2ms
does not count the ambiguous opportunity metric0.2ms
monitorInsight — takes the tool deltas, never its own · 2 tests
reports the tool delta even when it disagrees with current minus prior0.4ms
drops a null delta rather than coercing it to a zero movement0.2ms
monitorInsight — refuses rather than half-claims · 5 tests
returns null when nothing moved0.2ms
returns null when a section has no prior to compare against0.1ms
returns null on no history, an error, or no sections at all0.2ms
names the checks that ran for the first time instead of dropping them silently0.2ms
carries no caveat when every section had a prior0.2ms
monitorInsight — reads as English · 2 tests
singularises every unit it prints0.5ms
never prints a signed magnitude like "down -5"0.4ms
monitorInsight — a thin AI-visibility delta is qualified · 3 tests
states the movement AND why it cannot be acted on0.2ms
carries the first-run note and the thin note together0.3ms
says nothing when the samples carry their own weight0.3ms
ai-search-playbook-wiring.vitest.ts
36/36 41ms · 6 suites PASS
src/seo/ai-search-playbook-wiring.vitest.ts
each AI-search playbook is REACHABLE, not merely built · 6 tests
Q08 → hallucination_recovery: predicate fires, key is registered, both routes carry it10.0ms
Q11 → ai_search_staffing: predicate fires, key is registered, both routes carry it2.0ms
Q12 → ai_audit_scope: predicate fires, key is registered, both routes carry it2.6ms
Q17 → geo_measurement_contract: predicate fires, key is registered, both routes carry it1.4ms
Q18 → crawler_policy: predicate fires, key is registered, both routes carry it2.0ms
the isPlaybook expression is one statement — the roster cannot drift apart2.3ms
each playbook answers ITS question, not its neighbour's · 6 tests
Q08 — hallucination_recovery carries its own template and clears the invariants2.8ms
Q11 — ai_search_staffing carries its own template and clears the invariants0.9ms
Q12 — ai_audit_scope carries its own template and clears the invariants1.0ms
Q17 — geo_measurement_contract carries its own template and clears the invariants0.7ms
Q18 — crawler_policy carries its own template and clears the invariants0.7ms
no two of the five produce the same headline0.4ms
an absence claim names its boundary — the two that did not, live · 6 tests
Q18 anchors on the crawl when one exists, and stops claiming nobody looked0.8ms
Q18 still says so — with its boundary — when no crawl is on file0.3ms
Q18 reads blocked bots as blocked, not merely as "checks exist"0.9ms
`na` is excluded from the access denominator0.5ms
Q12 stops claiming no content checks exist once it is given them0.5ms
the dispatcher hands content signals to EVERY playbook that anchors on them0.5ms
a playbook does not deny a panel the tenant has already run · 7 tests
Q08 anchors on the grounded run instead of denying it0.8ms
Q08 treats an UNGROUNDED run as no incident log — with a different reason0.3ms
Q08 still says so plainly when no run exists at all0.2ms
Q17 anchors on the frozen panel and its baseline0.4ms
Q17 distinguishes "ran but not frozen" from "never ran"0.3ms
an EMPTY frozen prompt list is not a frozen panel0.2ms
the gather reads both, or the anchors above can never fire in production0.6ms
a coverage anchor never states good news as a double negative · 4 tests
zero on both counts reads as the good news it is0.5ms
real shortfalls are still counted, on either axis or both0.3ms
never emits two negations in ONE clause, at any input0.6ms
every playbook that states coverage uses the shared helper1.7ms
Q15 rival industrialisation is anchored on the measured shortlist gap · 7 tests
states the gap in the direction it actually runs0.5ms
reads AHEAD as defending a lead, not as a gap to close0.3ms
a panel with no comparison prompt is not a gap of zero0.3ms
no run at all is a third sentence again0.7ms
warns against out-publishing, which is the expensive wrong answer0.3ms
prices capacity when initiatives are stacking up unrun0.2ms
the gather computes the gap from COMPARISON prompts only0.3ms
keyword-evidence.vitest.ts
36/36 42ms · 9 suites PASS
src/seo/keyword-evidence.vitest.ts
striking distance — ONE definition, extracted from the planner · 4 tests
takes positions 4-20 and nothing else2.8ms
is exclusive at 3 and inclusive at 20 — the boundaries the planner set2.2ms
ranks a distant term with traffic above a close term without0.3ms
excludes a keyword with no position — unranked is not position zero0.5ms
the horizon weights a score that already had both halves · 5 tests
is 1:1 when unset — an un-asked tenant sees no behaviour change0.4ms
favours behaviour for quick wins and estimate for the long game0.6ms
inverts the ranking between the two horizons1.0ms
still scores a big unranked term above zero under quick wins0.3ms
scores an unmeasured keyword at zero rather than inventing a value0.8ms
the horizon is stated, never applied silently · 4 tests
returns a note naming the ranking AND how to change it0.6ms
says nothing when unset — there is no preference to disclose0.4ms
parses only the two real values; everything else is unset, not a default0.4ms
names the settings key the SETTING_KEYS array must carry0.5ms
asking — only when the answer would actually change · 4 tests
asks when the two horizons name different leaders0.6ms
stays silent when both horizons agree — the question would be noise0.2ms
stays silent with nothing to rank0.2ms
offers exactly two options and is not a refusal1.1ms
an uncaptured property is a finding, not an absence · 3 tests
names the gap, the count, and that the figures are estimates16.8ms
says nothing once behaviour exists0.3ms
says nothing when there are no keywords at all — that is a different problem0.2ms
no surface re-forks the definition · 3 tests
the planner calls the shared definition instead of repeating the filter0.5ms
seo_list_keywords no longer orders the registry by recency2.1ms
diagnose reads keyword evidence1.6ms
ensureGscCaptured — auto, but guarded · 7 tests
captures when the property matches and nothing is on file1.7ms
refuses when Search Console is connected for a DIFFERENT property0.4ms
refuses when nothing is connected0.3ms
does not re-ask within a day of the last attempt0.3ms
tries again once a day has passed0.9ms
stamps the attempt BEFORE calling, so a failure still counts as an attempt0.8ms
reports an empty property as no_data, which is a finding rather than an error0.4ms
gscCaptureNote — provenance is stated in every branch that has one · 3 tests
says a capture just ran, with the count and the window0.8ms
says so when Search Console is absent or points elsewhere0.3ms
stays quiet when nothing happened worth reporting0.2ms
the diagnose renderer shows the keyword half · 3 tests
renders the keyword counts, the striking-distance table, provenance and horizon0.3ms
renders all four capture outcomes that have something to disclose0.2ms
flags an unmeasured profile rather than printing a bare zero0.3ms
local-presence.vitest.ts
36/36 54ms · 7 suites PASS
src/seo/local-presence.vitest.ts
buildFindings · 8 tests
a complete, consistent, structured site produces NO findings5.0ms
leads with the address conflict — it is the one that blocks everything downstream0.8ms
reports a phone conflict separately from an address conflict0.5ms
escalates the missing-schema finding when there is also no address0.6ms
discloses when the address is only page text, because that weakens every later comparison0.4ms
does not raise the text-only disclosure when the address came from structured data0.4ms
never names a vendor or a schema internal to the user0.7ms
never quotes a dollar figure — the product bills in tokens0.4ms
the consistency gate · 2 tests
a self-contradicting site is what stops a paid listing comparison0.4ms
does not fire on the same address written two ways0.7ms
buildNotChecked · 4 tests
is never empty — the blind spots are structural, not situational0.7ms
names the listings explicitly, since that is what users ask about0.5ms
names the aggregator layer, which is unreachable at any price0.4ms
keeps the no-vendor-names and no-dollars rules0.6ms
the tool is reachable for the question a user actually asks · 2 tests
entity_audit advertises the address/phone capability in the words users type32.8ms
and carries the rule that stops it claiming listings were read1.2ms
findings are gated on the tenant actually being local · 8 tests
REGRESSION: says nothing local when the answer is unknown0.5ms
REGRESSION: says nothing local when the tenant serves remotely0.3ms
says all of it when the tenant IS local0.5ms
defaults to suppressed when the argument is omitted entirely0.3ms
a site contradicting ITSELF is universal — never gated0.6ms
a phone contradiction is universal too0.3ms
carries the question only while the answer is unknown0.3ms
and the model is told not to assume the answer from the industry0.4ms
mergeBusinessLocation · 9 tests
THE REGRESSION: an explicit answer persists, so the question is asked once0.7ms
stores the address the site states, with which tier it came from0.4ms
never lets an observation revise the user's own answer0.3ms
a failed fetch does not erase a good stored address0.3ms
clears confirmed_at when the site now says something else0.3ms
KEEPS confirmed_at when the address only changed cosmetically0.3ms
reports changed=false when nothing moved, so an audit is not a settings write0.2ms
survives a malformed stored blob instead of throwing0.4ms
the answer is reachable from chat — entity_audit declares the parameter0.3ms
mergeBusinessLocation — the write-churn guard · 3 tests
REGRESSION: a reformatted phone is not a change0.3ms
REGRESSION: a reformatted address is not a change0.4ms
but a REAL move still writes0.3ms
diagnose.vitest.ts
35/35 85ms · 11 suites PASS
src/chat/diagnose.vitest.ts
one measurement is a point, not a direction · 3 tests
cannot explain a change from a single measurement3.0ms
can explain once there are two0.4ms
a delta is null, never 0, when there is no prior0.3ms
the forensic contract catches a dishonest diagnosis · 5 tests
passes a well-formed one1.5ms
rejects a delta the two displayed values do not support0.4ms
rejects claiming a trend when nothing has moved0.3ms
rejects a thing listed as never-measured that also carries a score0.3ms
rejects an out-of-range score0.3ms
it renders INLINE in the chat body, not as another artifact · 5 tests
returns inline:true so the chat renders it in-body with charts0.3ms
draws an Apex chart of the scores0.3ms
orders the chart weakest-first — it shows where the problem is, not a victory lap0.2ms
shows movement only where a comparison exists0.2ms
names what was never measured, so partial does not read as complete1.0ms
the model is pointed at it · 2 tests
V2_SYSTEM says call diagnose before any paid audit on a why question27.0ms
costs nothing, and the registry says so0.4ms
a WHY question reaches the diagnostic tool · 3 tests
the visibility intent stands down on a why question, via the shared vocabulary8.1ms
recognises the phrasings people actually use1.2ms
leaves a MEASUREMENT ask alone — that one still wants the number0.3ms
the reading names the runs it was built from · 3 tests
cites every dated point, or the contract fails0.3ms
passes when each one is cited0.3ms
renders each source with its own date and age3.3ms
the token meter is never blank · 1 test
the snapshot answer carries the running total like every other exit4.8ms
every dispatched tool is actually routable · 3 tests
parsed both lists (the assertion is real, not vacuous)0.5ms
no case is stranded without a routing entry0.4ms
diagnose specifically1.2ms
the evidence covers what the platform actually stores · 3 tests
every dashboard pillar is known to the diagnosis0.8ms
reads the traffic history the diagnostic rule itself names0.3ms
records WHY the overlapping snapshot types are excluded0.4ms
the copy says each thing once, and reads rates not counts · 3 tests
does not repeat the staleness number the renderer already prints0.5ms
diagnoses the reply RATE, not just a zero0.3ms
separates the open-rate cause from the reply-rate cause0.4ms
diagnose — a named absence carries the way out of it (GS-006) · 4 tests
every gap label the evidence module can emit has an action1.0ms
the action is what to ASK FOR, never a tool name (GS-005)0.4ms
an unknown label returns null rather than an invented next step0.2ms
the report renders the action beside the gap, and still renders one without21.7ms
panel-structure.vitest.ts
35/35 38ms · 8 suites PASS
src/ui/panel-structure.vitest.ts
Outbound workspace panel · 4 tests
sidebar opens the hub, not straight into Contacts4.0ms
only pushes past the hub when a destination is named0.8ms
#leadView does not carry its own slide transform inside a screen0.5ms
the lead card is a screen on the stack, not a bespoke .open toggle1.5ms
Keywords / AI Prompts panel · 4 tests
#kwSelectionBar comes after #keywordsPanelList so sticky-bottom works1.0ms
#aipSelectionBar comes after #aiPromptsList so sticky-bottom works0.7ms
filter pills have space before the block beneath them0.9ms
the bulk bar is a single aligned row0.7ms
Keywords panel — Track B tiers · 3 tests
uses the shared wide frame, not a bespoke width1.9ms
Vol and CPC are separately targetable so one tier can drop without the other1.1ms
drops only P2/P3 columns at the narrow breakpoint2.5ms
Keywords panel — screen containment · 3 tests
the panel is on the v4 screen stack, with no tabs left1.1ms
each screen contains its own table, summary and bulk bar0.6ms
both bulk toolbars offer CSV export0.5ms
Track B wide frame · 1 test
is a class, not an ID selector0.3ms
wireframe empty states · 5 tests
the wireframe primitive exists and is inert — no animation on its ghosts0.7ms
ghost scaffolding is hidden from assistive tech, the caption is not0.6ms
every dashboard section that can be empty ships a hint and a next step1.4ms
an empty section renders the wireframe INSTEAD of a row of em-dashes4.8ms
the wireframe CTA is wired to a handler, not left inert0.6ms
AI visibility surface · 7 tests
an errored engine is carried as a distinct status, never as a zero0.9ms
the trend plots real elapsed time and never interpolates0.7ms
the freshness bar states the shape of the measurement, not just its age0.6ms
you are always in the competitor table, even at zero0.7ms
every count in the matrix carries its denominator0.4ms
the page roll-up leads with the weakest page0.6ms
detail renders around the AEO section, not stacked before it0.6ms
prompt library on the panel system · 8 tests
uses the Track B panel shell, and the legacy one is gone from the markup0.9ms
the legacy shell CSS was deleted, not merely orphaned1.3ms
is registered as a panel, so the system owns Esc / click-outside / exclusion0.4ms
opening closes every other panel and records focus for return0.3ms
search states its denominator while filtering0.3ms
searching opens matching sections instead of hiding matches behind a closed one0.3ms
typing survives the re-render — focus and caret are restored0.3ms
Escape clears the filter before it closes the panel0.4ms
judge-coverage.vitest.ts
34/34 45ms · 10 suites PASS
src/admin/judge-coverage.vitest.ts
canonicalOperation · 5 tests
folds a retired name into its replacement4.7ms
keeps a variant visible but canonicalises its base0.8ms
leaves a live tool name alone0.5ms
does not inherit the rate-cap alias that merges two LIVE tools0.6ms
maps only retired names, and only onto live tools2.5ms
computeJudgeCoverage · 6 tests
names what was NEVER judged — absent is not healthy1.8ms
flags a score built on too few samples2.1ms
counts a retired name toward its replacement, not as its own tool1.1ms
counts a variant toward its parent tool for coverage1.6ms
surfaces an operation that is not a registered tool at all0.6ms
never counts judge_failure rows as coverage0.9ms
computeQualityTrend · 3 tests
reports sample counts so n=1 cannot pass for a rating0.8ms
draws one series for a capability, not one per historical name13.9ms
still averages within a day0.6ms
judge coverage — the 2026-08-19 wiring defects · 3 tests
does not count operational telemetry as a judged capability0.9ms
credits an async job to its TOOL, not to a capability called async_job0.5ms
still flags a genuinely unknown name — the alarm must not be disabled, only de-noised0.4ms
chat as a shortcut capability · 2 tests
does not report a no-tool conversation turn as an unrecognised id0.7ms
does not report the shortcut-owned Google composite as an unrecognised id0.5ms
quality rows that are counters, not judgements · 3 tests
does not report a report-render counter as a judged capability0.4ms
excludes a counter nobody has declared yet, because it carries no score0.3ms
still flags a scored row under an unknown name0.3ms
invoked-but-unjudged vs never-invoked · 5 tests
separates a tool that ran and was never judged from one nobody ran0.9ms
reports the run count, so the bucket can be ranked by how much went ungraded0.4ms
refuses to split when there are no run counts, and says so0.3ms
treats an omitted run list the same as an empty one — never as "nothing ran"0.3ms
credits a variant run row to its parent tool0.3ms
runs the platform refused · 3 tests
does not count a rejected or capped run as something the judge could have graded0.4ms
counts the executions and ignores the refusals, in the same window0.3ms
treats a run with no recorded status as an execution0.3ms
a mismatched window manufactures coverage gaps · 3 tests
does not call a tool unjudged when its judged row is in the same window as its runs0.4ms
reports it as unjudged when the judged rows are missing from the set it was given0.3ms
carries the truncation flag through so a paging cap cannot read as a coverage gap0.4ms
no code claims a sampler that does not exist · 1 test
has scrubbed the 1-in-5 claim from this module1.5ms
canonical-question-routing.vitest.ts
34/34 78ms · 2 suites PASS
src/chat/canonical-question-routing.vitest.ts
every canonical question routes to at most ONE predicate · 31 tests
the fixture still holds all 28 AI questions3.6ms
the SEO 30 are still readable from the pillar page1.5ms
Q01 is not ambiguous15.6ms
Q02 is not ambiguous6.1ms
Q03 is not ambiguous1.4ms
Q04 is not ambiguous1.5ms
Q05 is not ambiguous0.8ms
Q06 is not ambiguous1.2ms
Q07 is not ambiguous1.3ms
Q08 is not ambiguous0.8ms
Q09 is not ambiguous1.0ms
Q10 is not ambiguous0.7ms
Q11 is not ambiguous1.3ms
Q12 is not ambiguous1.0ms
Q13 is not ambiguous0.7ms
Q14 is not ambiguous0.8ms
Q15 is not ambiguous1.3ms
Q16 is not ambiguous0.6ms
Q17 is not ambiguous0.4ms
Q18 is not ambiguous5.9ms
Q19 is not ambiguous1.1ms
Q20 is not ambiguous0.7ms
Q21 is not ambiguous0.5ms
Q22 is not ambiguous5.6ms
Q23 is not ambiguous0.4ms
Q24 is not ambiguous1.7ms
Q25 is not ambiguous1.0ms
Q26 is not ambiguous0.6ms
Q27 is not ambiguous0.2ms
Q28 is not ambiguous0.6ms
no SEO question is ambiguous either13.7ms
the two that were live, pinned by name · 3 tests
Q08 goes to the hallucination playbook, not the core-update recovery brief0.8ms
Q18 goes to the crawler-policy playbook, not the AI-content-policy brief0.7ms
and the carve-outs did not steal the incumbents0.8ms
crawl-vitals.vitest.ts
34/34 17ms · 8 suites PASS
src/seo/crawl-vitals.vitest.ts
Q22: the never-cut list protects the site from this brief · 13 tests
never recommends cutting /assets, whatever the numbers say2.8ms
never recommends cutting /static, whatever the numbers say0.5ms
never recommends cutting /_next, whatever the numbers say0.3ms
never recommends cutting /js, whatever the numbers say0.2ms
never recommends cutting /css, whatever the numbers say0.2ms
never recommends cutting /images, whatever the numbers say0.2ms
never recommends cutting /api, whatever the numbers say0.2ms
never recommends cutting /checkout, whatever the numbers say0.2ms
never recommends cutting /cart, whatever the numbers say0.2ms
never recommends cutting /pricing, whatever the numbers say0.2ms
still flags an ordinary pattern with the same numbers0.2ms
carries both safety clauses in the decision whenever a cut is proposed1.3ms
refuses to cut anything when no pattern is being declined0.4ms
Q22: it never claims to have seen the crawler · 5 tests
keeps the log-based hypotheses untested0.4ms
says outright that this is read from what the site advertises, not what was fetched0.4ms
requires a real population before calling something a pattern0.3ms
flags a pattern only when the index declines most of it0.2ms
surfaces a blocked-and-indexed contradiction before recommending more rules0.2ms
pathPrefix and pattern folding — the derivation, tested directly · 2 tests
groups on the first path segment0.4ms
folds URLs into patterns with their sitemap and index state1.8ms
Q23: no field data is an ANSWER, not a missing measurement · 4 tests
says do not spend a sprint, in as many words1.0ms
applies the rule rather than refusing to answer0.3ms
names the traffic as the finding, not the speed0.3ms
the ask says there is nothing to approve, and why that is useful0.2ms
Q23: the crawler fetch time is never allowed to stand in for field data · 3 tests
states what the lab number is and is not0.3ms
keeps lab-versus-field untested, because there is nothing to disagree with0.4ms
never promotes the lab number to a verdict, and never RULES OUT a field failure it could not see0.6ms
Q23: when field data exists, the earning pages decide the sprint · 4 tests
confirms the overlap between failing and earning0.5ms
takes one template and names the element0.5ms
says re-measure after weeks of traffic, not the next morning0.2ms
never claims to know which element is responsible0.3ms
Q22 and Q23: no internal vocabulary reaches the user (GS-005) · 1 test
keeps field and table names out of the prose0.7ms
Q23: nothing checked is not nothing failing · 2 tests
does not claim a clean result when no page was checked0.4ms
still says nothing to schedule when pages WERE checked and passed0.3ms
rca-evidence-quality.vitest.ts
33/33 28ms · 7 suites PASS
src/admin/rca-evidence-quality.vitest.ts
the minority population cannot be crowded out · 7 tests
reserves the organic floor when internal traffic dominates3.9ms
caps organic at the floor when BOTH populations are plentiful0.6ms
gives organic the whole budget when internal traffic is light0.4ms
never exceeds the cap, on any mix2.0ms
reports both totals so truncation can be read per population2.0ms
degrades to a plain cap when the floor is absurd0.6ms
an empty window produces an empty sample, not a throw0.9ms
full-population counts, so absence means something · 3 tests
counts every row, not just the sampled ones1.2ms
splits operations by traffic type1.1ms
an operation with zero organic rows is ABSENT from the organic map0.4ms
the build join is arithmetic, not prose · 4 tests
picks the release that was live at the timestamp0.5ms
a timestamp exactly at a deploy belongs to that deploy0.1ms
returns null — never the oldest build — for undatable observations0.2ms
returns null for a missing timestamp and survives an empty timeline0.1ms
the writer records what it already knew · 4 tests
BOTH judge paths write all five fields — ensemble and single1.8ms
reads reqCtx.toolCalls SYNCHRONOUSLY, before the detached judge0.5ms
counts top-level tools only, on the same branch that mints the run id0.3ms
the counter is zeroed per unit of work at BOTH boundaries0.6ms
the reader treats a missing field as unknown, not false · 4 tests
absent tool-outcome keys read as null0.5ms
rows are tagged BEFORE they are sampled0.4ms
judge health is reported per traffic population, not only in aggregate0.2ms
full-population counts ship alongside the sample0.2ms
a fix verdict needs an exercise denominator · 8 tests
counts only runs at or after the fix date0.5ms
matches the CANONICAL operation, not the raw ledger variant0.2ms
separates organic runs from internal ones0.2ms
reports zero for a path nothing exercised0.2ms
returns one row per fix, in order, even with no traffic at all0.2ms
the prompt states the count inline and forbids a bare "unproven"2.5ms
the sweep agenda is asked for by name1.3ms
still renders without coverage, so the old call shape cannot crash a run1.3ms
an infrastructure change is not a sweep item · 3 tests
an entry with only pseudo-operations is not applicable0.2ms
a marker mixed with a REAL operation stays applicable and counts the real one0.2ms
the prompt says n/a rather than 0 for those entries1.2ms
intent-router.vitest.ts
33/33 61ms · 9 suites PASS
src/chat/intent-router.vitest.ts
chatRouter explicit off-page audit · 1 test
routes a named off-page audit directly instead of opening the broad SEO picker29.7ms
chatRouter aeo_intent · 5 tests
does NOT hijack the AEO writer chip "Write <topic> in AEO mode" (live 2026-07-12)5.5ms
does not hijack generic content-writing asks that mention AEO1.4ms
still routes visibility asks to aeo_intent0.4ms
still routes "am I cited by chatgpt" to aeo_intent0.6ms
page readiness asks stay off the engine selector (registry #9)0.3ms
keyword asks no longer hijacked by a deleted shortcut · 6 tests
"give me keyword ideas for media bias detection" is not claimed by a keyword shortcut0.4ms
"create a content brief for the keyword media literacy tools" is not claimed by a keyword shortcut0.3ms
"track my keyword rankings for rhetoric audit" is not claimed by a keyword shortcut0.3ms
"what are the most profitable keywords for my niche" is not claimed by a keyword shortcut0.8ms
"show me my saved keywords" is not claimed by a keyword shortcut0.4ms
the economics ask reaches the model rather than a veto regex0.2ms
standing-instruction confirm/cancel shortcuts (injection-persistence guard) · 2 tests
matches the exact confirm/discard chip phrases0.4ms
does not fire on unrelated affirmatives (only the exact chip text confirms a persist)1.4ms
chatRouter lead-gen asks that mention SEO · 3 tests
does not hijack a lead search into the SEO route picker6.3ms
leaves other find-a-prospect phrasings alone too0.6ms
still routes genuine own-site SEO asks to the picker0.6ms
lead_disambig matches the PURPOSE, not the noun (r-owned-inventory-first) · 4 tests
catches a role-noun ask with an outreach purpose0.3ms
still catches the generic nouns it always did0.3ms
still stands down when the user explicitly wants NEW people0.2ms
does not swallow an ask that merely contains a purpose-shaped phrase0.3ms
recoverLeadQuery · 6 tests
reports the chip decision instead of silently discarding it0.3ms
handles the pre-2026-07-31 chip wording still sitting in live histories0.2ms
does not claim the decision when the user never made it0.2ms
never returns the prefix as part of the audience0.1ms
survives an empty or whitespace input without inventing a decision0.5ms
does not carry state between calls — the regex is global-flag free0.2ms
backlink_worth shortcut · 2 tests
owns the worth / value phrasings for backlinks0.2ms
does not take a backlink question that is not about worth0.6ms
a machine-authored message is never a question (2026-09-17) · 4 tests
a next-action chip matches next_action_exec and nothing else0.2ms
a cost confirm matches cost_confirm — the approved spend must run, not be re-quoted (bklink 2026-09-18)0.3ms
an initiative execute matches no phrasing intent (index.ts handles it before the router)0.2ms
the diagnose shortcut is guarded at its entry5.0ms
answer-completeness.vitest.ts
33/33 27ms · 7 suites PASS
src/seo/answer-completeness.vitest.ts
readAnswerCompleteness — the denominator (AEO-001) · 5 tests
counts answers RECEIVED, not executions sent4.8ms
separates an engine that answered nothing from one that answered some (AEO-002)2.1ms
works on artifacts stored long before the field existed0.9ms
a complete run needs no caveat, and printing one anyway trains readers to skip them0.5ms
names the silent engines rather than counting them1.2ms
costPerAnswer (AEO-012) · 2 tests
divides by answers received, not by queries sent1.1ms
is null rather than Infinity when nothing came back1.3ms
engine labels — one definition (trap 4) · 2 tests
knows the surfaces that only the client copy used to know0.7ms
an unknown engine reads like a name, never like a column1.1ms
the artifact states its denominator (R-C acceptance) · 5 tests
the SPEC acceptance case: "0 of 27 answers", never a bare 0% over 450.6ms
names both silent engines instead of dropping them off the chart0.5ms
a count and the list beside it describe the SAME population0.5ms
reports cost per answer received0.3ms
a fully-answered run carries no coverage banner1.0ms
sample adequacy — a rate over n=1 (AEO-001, magnitude) · 10 tests
THE REGRESSION: the completeness layer reports a 1-of-1 run as perfectly complete0.4ms
reads the shape off the matrix, counting answers received0.4ms
names the single engine rather than counting to one0.2ms
says a single answer can only score 0% or 100%, and what to do about it0.4ms
fires on the ENGINE axis alone — 8 answers, all from one engine0.5ms
fires on the ANSWERS axis alone — 3 answers across 3 engines0.6ms
stays silent on a sample that carries its own weight0.6ms
counts answers RECEIVED, so silent engines cannot pad the sample past the threshold0.5ms
an unknown shape makes no thin-sample claim (GS-004 — never looked is not a finding)0.3ms
makeSampleShape carries the trend points, which have no matrix to count0.5ms
a stored sov row states the request, not the run · 7 tests
THE REGRESSION: never claims prompts that were never sent0.6ms
THE OTHER DIRECTION: a defaulted engine list must not suppress the single-engine caveat0.5ms
an unknown engine count neither raises the caveat nor clears it0.5ms
an empty engine_names array is an absence, not zero engines0.3ms
THE SELF-CONTRADICTION: no step arithmetic when the rate is not over these answers0.8ms
n=1 drops the 0%-or-100% claim when the number is a blend0.4ms
the matrix path is unaffected — it counts the run and keeps the full basis0.4ms
the artifact discloses the sample (AEO-001 on the report surface) · 2 tests
states the basis beside the headline, not two sections below it0.3ms
warns that the number cannot be acted on0.3ms
observability.vitest.ts
32/32 23ms · 6 suites PASS
src/observability.vitest.ts
calcLlmCost · 3 tests
prices a known model from its real per-M rates2.0ms
scales linearly with token counts0.4ms
returns 0 for a zero-token call0.4ms
LLM_COST_PER_M coverage — models that were silently mispriced · 4 tests
prices the live CoT reasoning model1.1ms
prices both router bake-off candidates0.5ms
prices the AEO engine-probe models and the RCA models0.8ms
never prices a model below the fallback it would otherwise silently use2.7ms
toLogAttribute · 4 tests
types integers, doubles and strings distinctly0.6ms
writes booleans as STRINGS so they can actually be read back1.0ms
every existing reader already string-compares these0.3ms
stringifies non-finite numbers and objects instead of sending invalid JSON types0.4ms
buildSentryLogEnvelope · 2 tests
is a 3-line envelope with a log item header Sentry accepts5.1ms
carries the message contract + typed attributes, and drops null attributes0.8ms
buildGenAiTransactionEnvelope · 6 tests
emits a transaction whose child span follows the gen_ai convention0.7ms
never gives the transaction root the same op as the child span0.7ms
marks a failover attempt internal_error so dashboards separate wasted spend0.3ms
records finish_reason and tags a truncated completion0.3ms
tags a clean completion truncated:no so the two are separable, not just absent-vs-present0.2ms
omits the tag entirely when no finish_reason was reported0.2ms
isInternalTraffic — our own traffic must be distinguishable in Sentry · 13 tests
tags the eval tenant by email0.2ms
tags the owner accounts0.3ms
tags eval-harness sessions with no email at all0.2ms
leaves a real tenant alone — tagging everything would be the same blindness0.2ms
matches the harness prefix, not a substring anywhere0.1ms
is case- and whitespace-insensitive on the email0.1ms
tags a test tenant whose session prefix no harness owns0.3ms
tags the exact event the 2026-08-30 weekly RCA called ORGANIC0.2ms
tags the OWNER by id, which TEST_CAP_USER_IDS alone could not0.2ms
does NOT tag a real tenant just because the set is present0.3ms
is inert without a set, rather than tagging everything0.3ms
is case-insensitive on the id, since UUIDs get written both ways0.2ms
reads the SAME set the rest of the product calls internal0.4ms
present-wave5.vitest.ts
32/32 56ms · 10 suites PASS
src/chat/present-wave5.vitest.ts
web_search — a ranked list is records · 4 tests
renders every result as a row4.3ms
position is a NUMBER, so the table sorts by rank rather than by string0.8ms
the model no longer receives the rows it used to re-type1.8ms
a zero-result search presents nothing — that case is an ANSWER, and prose says it0.6ms
diagnose — the measurements behind the diagnosis · 4 tests
shows previous NEXT TO the change — a drop of 12 differs from 90 and from 200.9ms
a first measurement has a null change, never a zero0.3ms
gaps stay with the model — an absence stripped of its remedy is worse than the sentence0.6ms
nothing measured yet presents nothing rather than an empty frame0.3ms
enrich_contacts — all of the research, not the first three · 2 tests
every enriched contact reaches the screen1.1ms
falls back to the company one-liner when there is no per-person signal0.6ms
send_emails — the recipients that did NOT get it · 3 tests
one row per failure, so a reason can be traced to an address0.4ms
a clean send presents nothing — a table of addresses that worked tells nobody anything0.2ms
the confirm gate is left alone — it has its own preview table0.3ms
seo_serp_spider — one table over every problem URL · 3 tests
classifies each problem and drops the healthy pages0.5ms
says whether the verdict was CONFIRMED — unverified is not the same claim0.3ms
a fully healthy site presents nothing0.3ms
seo_monitor — what moved, flattened out of the prose · 3 tests
reads every section, including the aeo_visibility the formatter never had a branch for0.9ms
a metric with no value at all is omitted rather than shown as a blank row0.6ms
no history presents nothing0.4ms
seo_keyword_metrics read a key that is never returned · 3 tests
presents who currently ranks — the half of a difficulty score that means something0.5ms
the formatter no longer reads related_keywords27.8ms
and now shows the score, intent and batch that were computed and dropped0.7ms
seo_aeo_check shared a case body with two tools that return something else · 3 tests
no longer prints undefined over a result that is entirely present0.5ms
presents the nine checks, with the fix on the row that failed0.8ms
the RAG-shaped siblings still take the RAG branch0.6ms
the invariant every presenter inherits · 3 tests
no presenter can build a rowless block2.4ms
rows are capped at BLOCK_ROW_CAP, not at the tool's own slice0.8ms
an errored result is never presented0.8ms
a presenter that takes the rows away says so, and says what that forbids · 4 tests
the model does not receive the rows it must not transcribe1.1ms
and is told explicitly that it cannot know what any row says0.7ms
the counts and column names ARE given, so the intro can be true0.5ms
a report tool keeps the ORIGINAL note — it still has to read the rows1.5ms
stream-guard.vitest.ts
32/32 1511ms · 3 suites PASS
src/chat/stream-guard.vitest.ts
stream-guard — the property that decides whether streaming ships · 21 tests
is byte-identical to scanOutbound across every chunking: canary79.3ms
is byte-identical to scanOutbound across every chunking: openai key95.2ms
is byte-identical to scanOutbound across every chunking: jwt105.0ms
is byte-identical to scanOutbound across every chunking: bearer55.0ms
is byte-identical to scanOutbound across every chunking: aws key58.2ms
is byte-identical to scanOutbound across every chunking: google key41.2ms
is byte-identical to scanOutbound across every chunking: testomat41.3ms
is byte-identical to scanOutbound across every chunking: pem44.6ms
is byte-identical to scanOutbound across every chunking: admin secret66.8ms
is byte-identical to scanOutbound across every chunking: vendor56.5ms
is byte-identical to scanOutbound across every chunking: vendor 271.0ms
is byte-identical to scanOutbound across every chunking: usd106.9ms
is byte-identical to scanOutbound across every chunking: usd words63.8ms
is byte-identical to scanOutbound across every chunking: raw error94.9ms
is byte-identical to scanOutbound across every chunking: internal tool75.9ms
is byte-identical to scanOutbound across every chunking: bare tool65.1ms
is byte-identical to scanOutbound across every chunking: prompt internals77.6ms
is byte-identical to scanOutbound across every chunking: schema assign51.9ms
is byte-identical to scanOutbound across every chunking: internal directive87.7ms
never releases the redacted form of a span it has not finished reading71.3ms
does not cut inside a dotted span that a rule is still matching80.9ms
stream-guard — retraction, and the direction it fails in · 2 tests
reports retract only when a BLOCK lands after bytes were already shown6.4ms
emits nothing further once blocked, however much more arrives0.3ms
stream-guard — word-level cadence · 9 tests
advances a word at a time, not a sentence and not a paragraph0.6ms
never cuts mid-word0.8ms
holds everything back until there is more than the character holdback0.2ms
stalls rather than guessing while an unbounded rule is still arriving0.2ms
an open INTERNAL directive falls back to sentence granularity0.2ms
sentenceBoundary still describes the worst case honestly0.3ms
is a no-op on empty and whitespace input0.3ms
delivers the whole answer at end() even if nothing ever streamed0.3ms
passes turn options through to the rail unchanged6.8ms
icp-extract.vitest.ts
32/32 29ms · 9 suites PASS
src/leads/icp-extract.vitest.ts
the evidence check · 5 tests
accepts a quote that occurs in the brief3.4ms
REJECTS a paraphrase — the most common and most convincing fabrication0.7ms
survives curly quotes, dashes and re-wrapped whitespace0.6ms
rejects a span too short to distinguish one claim from another0.6ms
accepts a four-character noun, because that IS the evidence for a title0.4ms
a field with no valid evidence does not exist · 3 tests
drops a value whose quote is absent from the brief, and names the axis as silent4.2ms
drops a field with a perfect quote but no values1.3ms
returns nothing at all rather than guessing when the response is not JSON0.3ms
confidence is derived from the quote, not reported by the model · 3 tests
marks a value the brief names as STATED0.6ms
marks a value we inferred as IMPLIED, even when the quote is real0.8ms
does not read "farm" as stated because the quote says "farmingdale"0.6ms
values are coerced onto the vocabulary the rest of the system speaks · 3 tests
maps the model’s LinkedIn band onto OUR enum, wider rather than narrower1.3ms
keeps an ISO country code and drops a country NAME2.4ms
caps a runaway list instead of widening the search the user pays for0.8ms
the output is executable, and goes through the same translation a typed request does · 3 tests
translates the industry into what the corpus actually stores1.0ms
carries an industry the corpus has never heard of as unmatched, never as a silent drop1.2ms
sets no argument for an axis the brief is silent on0.7ms
a thin brief is an answer, not a failure · 2 tests
refuses to extract below the shared floor0.3ms
says what to add rather than inventing a plausible buyer0.5ms
what the user reads · 3 tests
shows the quote beside every reading, so a wrong one is correctable1.0ms
names the axes the brief was silent on instead of quietly leaving them out0.4ms
says so plainly when nothing survived0.1ms
the brief the extractor is allowed to read · 6 tests
passes a normal brief through untouched0.2ms
KEEPS THE TAIL when a brief is too long — that is where the buyer map lives0.2ms
keeps the head too — an industry inferred with no idea what the product is is a guess0.2ms
marks the join, so the model cannot quote across the seam0.3ms
stays within the stated cap0.2ms
is null-safe0.2ms
a headcount range keeps its top end · 4 tests
keeps the band covering the TOP of the stated range0.8ms
covers the whole range, not a prefix of it0.6ms
errs WIDER, never narrower — the direction numeric-band exists to guarantee0.4ms
still bounds the payload — you cannot name more bands than exist0.5ms
page-visibility.vitest.ts
32/32 29ms · 11 suites PASS
src/seo/page-visibility.vitest.ts
the two-source rule holds here too · 3 tests
no confident verdict rests on fewer than two named sources, in any shape5.0ms
CTR alone never confirms the snippet — that would be one system twice1.7ms
every untested hypothesis says what would settle it0.9ms
the snippet hypothesis · 3 tests
survives when the click gap and a title/description fault land on the SAME page0.6ms
is KILLED when the clicks are missing and the crawl finds no snippet fault0.6ms
joins on a NORMALISED url — scheme, www and trailing slash must not break the match0.3ms
the hypotheses that can never be killed here · 3 tests
query mismatch is never killed — settling it needs a per-query reading1.2ms
thin variant is never killed — the crawl finding nothing is not proof of nothing1.1ms
depth is ALWAYS untested — there is no internal-link graph in this codebase1.5ms
the SERP-feature hypothesis needs the AI reading as its second source · 2 tests
survives only when a click gap AND an AI visibility reading both exist0.5ms
is untested when the AI reading is missing, and says which reading is missing0.6ms
the premise not holding is an ANSWER, not an empty report · 2 tests
says so plainly when no page is indexed-and-invisible1.4ms
NEVER MEASURED and MEASURED-AND-CLEAN do not render alike — GS-0040.5ms
the subjects are the question · 2 tests
covers BOTH failure kinds — selection and click0.4ms
de-duplicates a page that fails both ways0.4ms
the decision rule follows the template, not the list order · 3 tests
a mismatch says retarget, never "polish it"0.5ms
says STOP and names the next pull when nothing survives0.4ms
the one-pager is always complete0.6ms
no internal vocabulary reaches the reader — GS-005 · 1 test
never names a tool, a field, an issue code or a vendor0.9ms
Q14 is reachable and rendered · 5 tests
routes from the SENTENCE, not from an intent that cannot be trusted with it3.5ms
is distinguishable in the trace from Q010.3ms
the dispatch attaches the brief and gathers its own evidence0.3ms
ONE renderer serves both briefs0.5ms
does not steal the turn from the index checker0.9ms
Q14 survives a tenant with no pillar evidence · 3 tests
is computed BEFORE the empty-evidence gate0.5ms
the empty-evidence branch returns the brief rather than a generic refusal0.6ms
is computed ONCE — two copies is the defect brief-kit was extracted to prevent0.4ms
a clean reading is an ANSWER, not an absence · 5 tests
kills the snippet hypothesis when the clicks were read and are healthy0.5ms
kills the thin-variant hypothesis when the crawl ran and flagged nothing0.2ms
kills the serp-feature hypothesis when the AI check ran and there is no click gap0.2ms
does NOT report five-of-five untested on a site that was fully measured0.1ms
still reports untested when Search Console reported no impressions at all0.2ms
rank-vs-citation.vitest.ts
32/32 25ms · 11 suites PASS
src/seo/rank-vs-citation.vitest.ts
bandFor · 1 test
bands by position, with unranked as its own state not a worst band3.2ms
joinRows — the join is exact, or it is nothing · 6 tests
joins on the seed, normalised for case and whitespace only1.7ms
refuses a NEAR match rather than pairing two different questions1.4ms
counts unseeded prompts separately — they are not failures, they are unjoinable0.5ms
drops a prompt NO engine answered — silence is not "not cited"1.4ms
an errored engine does not dilute a prompt another engine answered0.4ms
the FIRST ranking row for a term wins, so a duplicate cannot flip the band0.5ms
bandStats — a rate below MIN_BAND is suppressed, not rounded · 3 tests
reports counts and a null rate under the floor0.6ms
reports a rate once the band clears the floor0.6ms
bandLine never prints a blank where a rate was suppressed0.7ms
the rank-lifts-citation hypothesis refuses a comparison it cannot make · 3 tests
is UNTESTED when either band is under the floor, and says which0.8ms
survives when both bands clear the floor AND the gradient runs the right way1.0ms
is KILLED when the gradient runs the wrong way — ranking did not lift citation0.5ms
the findings a reader acts on · 3 tests
names the terms that rank 1-3 and are still not quoted0.5ms
counts citations won outside the classic top 100.5ms
the decision names the passage work, not a generic next step0.4ms
the commercial trade-off is never argued from data we do not have · 1 test
stays untested and names what it would need, whatever the panel says0.9ms
an unseeded panel cannot answer this at all, and says so · 3 tests
joins nothing and reports the unseeded count rather than a zero rate0.4ms
every evidence-bearing hypothesis is untested, none are killed0.3ms
the untested reasons name the missing panel, not a missing number1.2ms
the situation line distinguishes NO RUN from AN UNJOINABLE RUN · 2 tests
names the unjoinable run and why it cannot be paired0.5ms
says plainly when there is no run at all0.4ms
an ungrounded run is labelled as recall, not retrieval · 2 tests
says so in the situation line0.4ms
a grounded run carries no such caveat0.3ms
the brief holds the contract every brief holds · 3 tests
owns its own headline0.4ms
carries situation, so_what, ask and owner0.4ms
names no tool and no field name in the claims0.5ms
Q26 is wired everywhere a brief has to be wired · 5 tests
the dispatcher builds it0.4ms
it is in the answer chain, so the turn can select it0.4ms
it is in BOTH payload lists0.3ms
the router recognises it and labels the intent0.5ms
the renderer picks it up BEFORE the incident fallback0.6ms
claim-grounding.vitest.ts
31/31 23ms · 7 suites PASS
src/chat/claim-grounding.vitest.ts
the shipped defect · 3 tests
marks the assertion about a page it could not read6.6ms
leaves the page it DID read completely alone0.8ms
separates "could not read" from "never opened"0.9ms
what does NOT ground a claim · 2 tests
a search result is a snippet, not a page read0.6ms
an own-site refusal is grounded once diagnose actually runs0.5ms
the false positives the catalog eval caught (2026-08-11) · 3 tests
does NOT mark a page that aeo_page_check examined0.3ms
does NOT mark the tenant OWN site, whose read_url refusal is our own routing rule0.5ms
still marks a THIRD-PARTY page that genuinely could not be read1.0ms
it must not cry wolf · 7 tests
says nothing on a turn that gathered no evidence at all0.6ms
does not mark a sentence that is already honest about its footing0.5ms
ignores reference hosts — "Google shows an AI Overview" is not a page claim0.3ms
ignores a bare citation line — a link list asserts nothing0.7ms
marks once per page, not once per sentence1.6ms
caps the total number of marks0.5ms
rebuilds the text byte-exact apart from the marks0.3ms
host extraction · 3 tests
reads a host from a URL, a bare domain and a port0.6ms
finds every host named in prose, in order, deduped0.9ms
does not read a FILENAME as a host — SEO prose is full of them0.3ms
evidenceSummary · 7 tests
is null on a turn that gathered no evidence0.3ms
splits what it read from what it could not, and keeps the URL it opened0.4ms
keeps the FIRST url per host, not the last0.2ms
marks the tenant's own site as read, never as a gap0.3ms
adds hosts the ANSWER names that nobody opened, as "not checked"0.2ms
does not list a host twice when it was both tried and named0.2ms
never lists a search result as read — a snippet is not a page0.2ms
www/apex are the same source · 6 tests
does not mark a page it read because the prose dropped the www0.2ms
does not mark it the other way round either0.2ms
never reports the same source as BOTH read and unread0.3ms
lists one source when both spellings were read0.3ms
still marks a host nobody opened, www or not0.2ms
a host we tried and failed is not re-listed when the prose uses the other spelling0.2ms
diagnose-focus.vitest.ts
31/31 27ms · 7 suites PASS
src/chat/diagnose-focus.vitest.ts
focusOf — the question names the subject · 4 tests
reads the two phrasings that collided live as DIFFERENT questions4.6ms
separates the four subjects nqzai actually measures0.6ms
lets the specific subject win over the broad one0.3ms
falls back to general — the previous behaviour — when it recognises nothing0.4ms
orderForFocus — reorders, never filters · 7 tests
leads a traffic question with the search facts0.5ms
leads a reply question with outbound0.3ms
keeps the original movement-then-weakest order for a general question1.1ms
returns every fragment whatever the focus2.9ms
pins the staleness caveat last, whatever was asked0.7ms
is stable — same-rank fragments keep the order they were composed in0.4ms
gives the two colliding questions different leading facts0.4ms
headlineFor — the title cannot disagree with the ordering · 3 tests
quotes the question when there is one0.4ms
names the subject when there is no question0.4ms
truncates a long question instead of spilling it0.2ms
diagnose hands over an exact content prompt · 5 tests
offers a QUALITY CHECK for a term whose page already ranks1.7ms
offers a BRIEF for a term with no presence0.5ms
does not offer a brief for the same term the quality check covers0.5ms
still leads with the tool's own chips when it asked a question0.3ms
falls back cleanly when no keywords were measured0.4ms
shapeOf — is this about a CHANGE or a STATE · 6 tests
reads Q01, the most-asked question in the set, as movement2.5ms
reads a RISE as movement too — same shape, same lead0.3ms
reads a comparison with no verb of motion as movement0.6ms
reads a STATE question as state — Q03 and Q14 must not be re-ordered0.5ms
does not read the IDIOMATIC down/up as movement0.5ms
an empty or unrecognised question stays state — the conservative fallback0.2ms
a movement question leads with what moved · 5 tests
hoists movement above the search facts for a decline question0.3ms
leaves a STATE question exactly as it was — no regression0.3ms
never hoists movement above provenance0.2ms
hoists to the front when the topic has no provenance in its list0.5ms
still drops nothing, whatever the shape0.5ms
the diagnose dispatch consults both axes · 1 test
passes the shape through, rather than computing focus and forgetting it2.4ms
onboarding-gates.vitest.ts
31/31 278ms · 9 suites PASS
src/chat/onboarding-gates.vitest.ts
the shortcut gate reads the sentence, not just the URL · 3 tests
scan_product is not dispatched on URL-presence alone17.7ms
and that check sits on the SAME condition as the scan, not after it10.5ms
does NOT restrict onboarding to the first turn10.8ms
the agent-loop surface reads it too, and does not strand a task · 5 tests
a first-turn OFFER still gets the cheap single-tool surface10.3ms
a first-turn TASK gets the full surface, not an empty one25.0ms
a first turn with NO url that asks for work also gets the full surface12.2ms
and "asks for work" is the intent registry, not a new heuristic16.2ms
a first turn that asks for NOTHING still gets only the account reads26.9ms
the ownership question is attached, and yields to anything already asked · 4 tests
fires for a first-turn task that named a URL — OR for any scan that claimed a site13.0ms
never replaces a gate, a picker, or chips the turn already produced16.5ms
the chips name all three real answers, including "not my site"1.9ms
is asked AFTER the work, not as a precondition9.0ms
the first turn reports what it cost · 1 test
the onboarding scan return carries cumulative, like every other return on its path8.7ms
the trace does not claim a page belongs to the user · 1 test
scan_product names the host it is fetching, not "your website"2.5ms
the ownership chips have handlers, and only one of them writes identity · 5 tests
all three chips route somewhere — none is decorative text8.5ms
the URL comes from KV, never from re-reading the message11.4ms
only the "my company" branch scans; the other two write nothing9.3ms
a missing or expired pending ask falls through rather than guessing a domain9.1ms
answering clears the pending ask, so it cannot be re-answered later14.9ms
the scan reply itemises what the turn actually did · 3 tests
reads the seed back rather than claiming numbers it cannot know13.5ms
names the keywords, the competitors and where to find them3.1ms
a zero count is omitted, never announced as an absence2.5ms
the gate blocks tailoring, not the tenant's own data · 4 tests
still blocks the tools that need product context0.4ms
names the account reads that are NOT gated0.4ms
no longer claims to apply to EVERY request without exception0.3ms
every tool it exempts is a real registered tool0.3ms
the prompt may not promise a tool the surface does not carry · 5 tests
the ungated list in V2_SYSTEM is BUILT from the constant, not retyped0.7ms
and the surface filters on that same constant9.3ms
every ungated tool is a real registered tool0.8ms
every ungated tool is FREE — a gated surface must not smuggle in spend0.3ms
the account reads ride the scan-only arm too9.1ms
sov.vitest.ts
31/31 27ms · 8 suites PASS
src/seo/sov.vitest.ts
makeSovEntity · 2 tests
builds aliases and domain tokens from name + domain4.6ms
normalizes domains0.6ms
extractMentions — kinds and prominence · 7 tests
classifies a recommendation as recommended with weight 3 (+ early bonus)1.6ms
classifies a bullet-list item as listed0.9ms
classifies passing prose as mentioned0.6ms
falls back to cited_only when the entity appears only in citation URLs0.6ms
is word-bounded — "personal" does not match Persona0.4ms
matches bare-domain citations (AI Overview leaderboard style)0.6ms
does NOT match citation domains via a generic first word of a multi-word brand1.0ms
computeSov — aggregation · 9 tests
aggregates raw counts across engines before dividing2.2ms
excludes errored cells from every denominator0.6ms
citation rate counts cells whose citations include our domain0.6ms
weighted SOV favors the recommended slot0.7ms
reports per-engine breakdown0.5ms
computes HHI and concentration band0.6ms
synthetic cells count toward shares but not mention/citation rates0.7ms
handles the empty matrix without NaN0.3ms
flags low confidence on tiny samples and thin auto competitor sets1.0ms
classifyPromptType · 3 tests
detects comparison prompts0.9ms
detects branded prompts0.3ms
defaults to category0.2ms
computeSov — prompt-type cut · 1 test
breaks SOV down by prompt type when cells carry it0.5ms
isReferenceHost — auto-derive junk filter · 2 tests
filters reference/authority hosts that are never competitors0.8ms
keeps plausible commercial rivals0.3ms
competitor set persistence helpers · 3 tests
parses stored JSON and drops junk entries1.3ms
returns [] on malformed input1.3ms
set signature is order-independent and change-sensitive0.5ms
orderStoredCompetitors — user-first, honor full set · 4 tests
orders user entries before auto and never drops a user pick for an auto one0.8ms
honors up to 12 (the modal cap), not the old 50.5ms
respects an explicit smaller limit and re-screens auto entries against the reference filter0.4ms
a user-entered reference host is taken at face value (never second-guessed)0.3ms
approval-card.vitest.ts
30/30 213ms · 7 suites PASS
client/approval-card.vitest.ts
"Not now" is an answer, and it has to reach the server · 3 tests
posts a message the server recognises as a cancellation45.5ms
is inert while a turn is running, exactly like Confirm10.4ms
still dims the card, so the click is visibly acknowledged8.0ms
the jsdom environment is actually present · 1 test
has a document and a mount point2.5ms
CLAUDE.md §4 — what the card may say about money · 6 tests
never renders a currency symbol or the word dollar11.5ms
never names a backend vendor8.0ms
never says "free" — it says "No extra cost"6.2ms
renders a RANGE when the maximum exceeds the estimate5.1ms
renders a single figure when there is no range, not "X to X"2.7ms
labels the mode: paid operation vs confirmation gate10.4ms
the advisory block · 7 tests
renders ABOVE the cost summary — advice before price4.9ms
strips markdown from chip labels16.8ms
POPULATES the composer and does NOT send — the whole point of an alternative6.9ms
sends each chip its OWN text when several are present4.8ms
renders no advisory block when there is no note, even if chips are present2.5ms
drops empty chips rather than rendering blank buttons3.1ms
survives a telemetry callback that throws3.8ms
Confirm · 4 tests
sends the confirm prompt and locks itself3.1ms
does nothing while a send is already in flight1.8ms
cannot be double-fired by two fast clicks2.4ms
falls back to defaults when the server sends no labels1.6ms
supersession — two live Confirm buttons is a way to pay for the wrong quote · 4 tests
disables the older card when a newer one arrives8.4ms
refuses to send from a superseded card even if its button is re-enabled3.4ms
leaves an already-dismissed card alone rather than relabelling it2.9ms
is a no-op with no cards0.7ms
refuses to render rather than rendering something wrong · 5 tests
does nothing without a wrapper, an approval, or a .msg-text host1.1ms
treats a server-supplied title and message as TEXT, never as markup3.3ms
escapes a tool label, which goes through innerHTML3.3ms
names an unlabelled tool "Operation" rather than leaving it blank2.6ms
renders with no tools at all22.8ms
sessions.vitest.ts
30/30 34ms · 12 suites PASS
src/admin/sessions.vitest.ts
a report IS a delivery — the counter and the classifier now agree · 3 tests
counts a session that shipped only a report as delivered3.8ms
still counts a session that shipped nothing as nothing0.4ms
and the totals ask that function rather than re-deriving it1.0ms
the signature that a human found by reading 17 transcripts · 4 tests
flags high score + short session + nothing ran5.2ms
does NOT flag a session where a tool actually ran4.6ms
does NOT flag a long session, even with nothing run0.9ms
does NOT flag a session the judge already marked bad1.7ms
the signature is what RENDERED, not what dispatched · 4 tests
a tool ran, nothing rendered, judge happy → SILENT FAILURE1.9ms
a tool ran AND rows reached the screen → not a failure1.2ms
ABSENT instrumentation falls back — it is never read as zero1.5ms
and the fallback still catches a no-tool session0.5ms
onboarding is not an outcome — the ground-truth check that FAILED · 4 tests
a session where only the site scan ran is NOT tool_ran0.7ms
scan + diagnose together are still only onboarding0.5ms
onboarding PLUS a real tool is tool_ran0.5ms
wasted spend counts onboarding-only sessions too0.3ms
internal matching collapses +tag and gmail-dot variants · 3 tests
a probe address is excluded by the plain entry0.4ms
gmail dots collapse too0.5ms
a genuinely different address is NOT swallowed0.5ms
tool execution is counted from the dispatcher, never from the judge · 1 test
a session with tool_run rows is tool_ran even when the judge says nothing ran0.8ms
the DETERMINISTIC path is no longer invisible (migration 166) · 2 tests
a shortcut turn with NO llm row still lands on its session0.5ms
its own session_id WINS over the turn_id fallback0.5ms
the spine: turn_id joins the ledger to a session · 2 tests
provider and judge spend land on the right session via turn_id0.4ms
a ledger row whose turn has no session is skipped, never guessed0.3ms
internal accounts are excluded by default · 1 test
the test tenant does not pollute the real-user picture0.7ms
the totals that make the case · 2 tests
counts sessions where nothing ran, and what they cost0.4ms
reports the judge as a share of spend — it was 23.6% of everything0.3ms
the transcript drill-down · 2 tests
reads the same key the chat writes0.5ms
returns null rather than throwing on a missing or corrupt entry0.4ms
outcome reads the render manifest, not just the tool list · 2 tests
a session that rendered an ARTIFACT is tool_ran, whatever tools it used0.4ms
the tool list survives as the fallback for pre-manifest sessions0.4ms
speech-act.vitest.ts
29/29 640ms · 8 suites PASS
src/chat/speech-act.vitest.ts
a question about the world is not a command, however the topic reads · 6 tests
the incident: Explain how AI search engines decide which sit9.4ms
a definition ask: What is the difference between SPF and DKIM, a0.8ms
out of domain entirely: What is a good recipe for chicken biryani?0.4ms
a comparison of concepts: How does answer engine optimisation differ fro0.4ms
a phrasing nobody listed: Curious whether backlinks still matter as much12.6ms
a bare topic: thoughts on programmatic SEO0.4ms
a question about THEIR data is still a question, but not a general one · 5 tests
not general: Am I visible in AI search?0.5ms
not general: my numbers dropped0.3ms
not general: how do I improve my seo0.4ms
not general: why is our traffic down0.4ms
not general: We have 200 tokens of budget and one week. Is 0.2ms
a command is recognised wherever in the sentence it appears · 5 tests
at the start0.4ms
after politeness0.3ms
IN A LATER CLAUSE — the regression that first-token-only testing caused2.7ms
but NOT a work verb that is merely being discussed0.2ms
and NOT an explanation verb, which is imperative in form only0.2ms
naming a specific page makes a question specific, not general · 2 tests
a comparison of two named URLs needs evidence, not prose0.2ms
a bare domain counts too0.1ms
the defaults, and why they lean the way they do · 2 tests
empty input is neither a command nor a general question0.2ms
an unrecognised sentence defaults to QUESTION, and that is the cheap failure0.2ms
the predicate decides whether a PICKER opens, and nothing else · 4 tests
a noun-phrase data request is called "general" — which is why it must not gate tools0.2ms
the tool surface no longer consults it9.3ms
the guard has no `answer` rung to drop a data request into0.6ms
what still protects an explanatory turn is the COST GATE, which the user can see0.4ms
an advisory ask still reaches the planner · 3 tests
the planner tools are exempt from the evidence-first redirect593.9ms
and a genuinely paid report is still gated0.8ms
the exemption reads the canonical planner set, not a second list0.6ms
the planner is not augmented with a competing answer · 2 tests
the guard stands down once the model has chosen a planner tool0.9ms
and still fires when the model reached for a paid report instead2.1ms
aeo-wave5.vitest.ts
29/29 21ms · 4 suites PASS
src/tools/aeo-wave5.vitest.ts
the AEO cluster · 15 tests
entity_audit accepts the empty call5.0ms
aeo_full_audit accepts the empty call0.7ms
sov_trend accepts the empty call0.3ms
tap_volume accepts the empty call0.5ms
seo_generate_llms_txt accepts the empty call0.3ms
entity_audit rejects the retired domain alias0.6ms
aeo_full_audit rejects the retired domain alias0.3ms
sov_trend rejects the retired domain alias0.4ms
tap_volume rejects the retired domain alias0.4ms
seo_generate_llms_txt rejects the retired domain alias0.4ms
entity_audit accepts a named site1.3ms
aeo_full_audit accepts a named site0.2ms
sov_trend accepts a named site0.2ms
tap_volume accepts a named site0.2ms
seo_generate_llms_txt accepts a named site0.4ms
aeo_page_check: one name, both forms · 3 tests
takes a full page URL0.4ms
takes a bare domain under the same field0.2ms
rejects site, which was the alias on this tool0.2ms
type tolerance is gone · 3 tests
sov_trend engines must be an array of known engines2.7ms
tap_volume queries must be an array0.7ms
entity_audit execs must be an array1.1ms
fallbacks go only where validation replaced them · 8 tests
entity_audit no longer reads tool.domain0.7ms
tap_volume no longer reads tool.domain0.3ms
sov_trend no longer reads tool.domain0.3ms
seo_generate_llms_txt no longer reads tool.domain0.2ms
aeo_page_check no longer reads tool.site0.4ms
the scalar branches are gone0.6ms
still-legacy full_seo_audit KEEPS its fallback0.2ms
still-legacy share_of_model KEEPS its fallback0.2ms
contact-filter.vitest.ts
29/29 17ms · 7 suites PASS
src/tools/contact-filter.vitest.ts
the operator is a field, not a parsed token · 4 tests
compiles the owner's minus — "the ones not already enriched"3.2ms
compiles its INVERSE — "including the ones already enriched" — to no clause at all0.5ms
distinguishes "any" from omitted at the TOOL level, which is where it matters1.7ms
compiles union, intersection and subtraction without a new pattern for each0.6ms
verification is four-state because yes/no would have to guess · 3 tests
separates "never checked" from "checked and bad" — the pair that decides spend0.6ms
"valid" means WE confirmed it — a provider claim never qualifies0.3ms
offers a deliberate "any" so a re-verify is expressible1.0ms
contacted reads lead_status through the one helper · 2 tests
treats NULL and "new" as not contacted, matching every other read in the codebase0.3ms
expresses "already contacted" as the exact complement, so the two cannot drift0.3ms
a filter the dispatch cannot honour is a rejection, never a silent drop · 3 tests
throws when a list field was set and no ids were resolved for it0.7ms
names the offending field so the caller can say which part it could not honour0.2ms
treats an EMPTY resolution as a real answer, not a missing one0.2ms
the filter discloses itself · 2 tests
produces the sentence fragment the formatter renders0.4ms
says nothing when nothing was asked1.3ms
the validator enforces the filter, it does not merely carry it · 12 tests
enrich_contacts declares the shared filter0.2ms
enrich_contacts accepts every legal predicate0.2ms
enrich_contacts REJECTS a value outside the vocabulary rather than dropping it0.5ms
enrich_contacts rejects an invented predicate — the ceiling is the point0.6ms
enrich_contacts rejects an operator STRING — no expression language grows here0.3ms
list_contacts declares the shared filter0.1ms
list_contacts accepts every legal predicate0.1ms
list_contacts REJECTS a value outside the vocabulary rather than dropping it0.2ms
list_contacts rejects an invented predicate — the ceiling is the point0.2ms
list_contacts rejects an operator STRING — no expression language grows here0.1ms
a bad predicate is never DROPPED to keep the turn alive0.4ms
keeps list names inside the filter within the same bounds as the legacy field0.3ms
model-facing copy · 3 tests
tells the model the inverse is a thing it can ask for0.5ms
states the redo rule so a "refresh everything" ask cannot be silently narrowed0.3ms
names no vendor and no USD price0.4ms
tool-tiers.vitest.ts
29/29 61ms · 7 suites PASS
src/tools/tool-tiers.vitest.ts
tool-tiers — coverage stays in sync with V2_TOOLS · 3 tests
every V2_TOOLS entry is covered by CORE or a family4.3ms
no tiered name references a tool that does not exist in V2_TOOLS0.6ms
no tool appears in more than one family (each has exactly one home)2.2ms
resolveTurnTools · 4 tests
'all' returns V2_TOOLS unchanged (safe default, today's live behavior)0.9ms
empty family set returns exactly the CORE tools plus the search_tools meta-tool3.8ms
requesting a family adds its tools on top of CORE4.6ms
an unknown family key is ignored, not thrown2.0ms
listToolFamilies · 1 test
lists every family with a non-empty tool list0.8ms
search_tools meta-tool · 5 tests
is appended to every tiered tool list, so the long tail is always reachable2.5ms
is NOT added to the untiered full list (flag off = today's surface, unchanged)0.6ms
enumerates the real families in its schema, so the model cannot invent one0.7ms
returns the family's tool names on a valid call0.4ms
returns a self-correcting error naming the real families on an unknown one0.4ms
tieringEnabled — rollout gate · 2 tests
defaults to off for unset/off/garbage0.8ms
accepts the documented on values0.4ms
family hints — a bare family name is not an index · 7 tests
every family has a hint, so the rendered list can never be half-annotated0.7ms
no hint exists for a family that does not0.5ms
the meta-tool description carries the hints, not just the names0.3ms
names PANELS in the AI-search hint — the exact word that failed0.3ms
preloads campaigns for a drip/sequence ask ([6.2.1] 2026-09-15) and seo for backlinks, nothing else4.9ms
loads the family a sentence names, and nothing else (widened 2026-09-17 on a measured 19K-per-turn cost)2.8ms
tells the model to load a family before denying the capability0.3ms
the Jev tool shortlist (2026-09-19): CORE_MIN + one family, and the way back · 7 tests
CORE_MIN is a strict subset of CORE and keeps both research primitives0.6ms
coreMin with a family resolves to CORE_MIN + that family + search_tools, and nothing else1.8ms
coreMin without a family, or with the core family loaded, is the full CORE (never a smaller surface than today)2.1ms
search_tools('core') loads the full CORE list, so a shortlisted turn can always get back0.6ms
every tool of a sub-grouped family is in at least one sub-group, every sub-group has a hint, and no group is the whole family6.6ms
a sub-group narrows the family; a family with no sub-groups is loaded whole4.9ms
the shortlisted surface is small enough to be worth it: every family, narrowed where it has sub-groups, ≤ 6,000 schema tokens6.8ms
ai-policy-playbooks.vitest.ts
29/29 22ms · 6 suites PASS
src/seo/ai-policy-playbooks.vitest.ts
all four are registered and hold the shared invariant in every input state · 8 tests
kpi_contract is registered3.1ms
kpi_contract passes the invariant empty and populated2.9ms
ai_cannibalisation is registered0.3ms
ai_cannibalisation passes the invariant empty and populated0.6ms
ai_pipeline_attribution is registered0.2ms
ai_pipeline_attribution passes the invariant empty and populated0.7ms
multi_market_language is registered0.4ms
multi_market_language passes the invariant empty and populated0.5ms
Q01 — the scoreboard, not the click loss · 5 tests
names four layers rather than a number1.4ms
anchors on the frozen panel when one exists — that is layer 21.5ms
an EMPTY frozen panel is not a frozen panel1.9ms
with no panel it still says the contract can be signed first0.4ms
refuses a single blended visibility score as the scoreboard0.4ms
Q13 — one backlog, and capacity is the anchor · 4 tests
anchors on recommendations never run0.4ms
does not anchor on a backlog of zero — nothing unrun is not a finding0.3ms
refuses a second content workstream outright0.3ms
treats chat-only tactics as off-page rather than a replacement0.4ms
Q22 — three layers, and no invented revenue · 4 tests
forbids converting a visibility score into revenue0.3ms
requires the citation series to move FIRST before brand lift counts0.3ms
anchors on a real run when one exists, and quotes its citation rate0.5ms
with no run, it says the money layer has no instrument here AT ALL0.2ms
Q24 — language is the subject, and the tracked market is the anchor · 4 tests
anchors on the single tracked market, which IS the finding0.2ms
with no market set, says so rather than assuming one0.2ms
NEVER claims to have checked hreflang — nothing in this codebase reads it0.6ms
localises the third-party graph, not only the site0.3ms
all four are routed and labelled · 4 tests
kpi_contract is selected by its predicate and labelled in the turn0.8ms
ai_cannibalisation is selected by its predicate and labelled in the turn0.3ms
ai_pipeline_attribution is selected by its predicate and labelled in the turn0.2ms
multi_market_language is selected by its predicate and labelled in the turn0.3ms
nap.vitest.ts
29/29 31ms · 8 suites PASS
src/seo/nap.vitest.ts
normAddress · 6 tests
REGRESSION: folds "Ste B" as a suite, never as street + stray letter6.0ms
REGRESSION: "201 W 5th St" equals "201 West 5th Street"0.5ms
folds spelled-out states to their abbreviation0.5ms
treats # and Suite as the same unit marker0.3ms
is punctuation- and case-insensitive0.5ms
does NOT collapse genuinely different addresses1.6ms
normPhone · 3 tests
ignores formatting and country code0.6ms
distinguishes genuinely different numbers0.4ms
returns empty for junk rather than a partial match1.0ms
normName · 2 tests
folds legal suffixes so they never read as a conflict0.5ms
keeps distinct businesses distinct0.5ms
walkJsonLd · 3 tests
REGRESSION: finds an address nested on parentOrganization2.7ms
terminates on a self-referential graph instead of hanging1.2ms
walks arrays0.4ms
extractNapClaims · 5 tests
reads structured data and records its provenance1.0ms
falls back to footer text when there is no JSON-LD6.4ms
does not crash on malformed JSON-LD, and degrades to the text tier0.4ms
reports no LocalBusiness when only a generic Organization is present0.4ms
ignores phone-shaped strings inside scripts and markup0.2ms
htmlToText · 1 test
drops tags, scripts and styles0.5ms
napConsistency · 5 tests
REGRESSION: cosmetic differences across pages are NOT a conflict0.5ms
flags a genuine two-address conflict0.3ms
flags a genuine phone conflict but not a reformatted one0.3ms
treats a brand and its parent legal entity as two names, without calling it a conflict0.6ms
is empty and conflict-free for a site that states nothing1.2ms
bestNap · 4 tests
prefers structured data over footer text and reports which it used0.4ms
falls back to text and SAYS so, so the caller can disclose the weaker claim0.1ms
returns nulls when nothing was found0.2ms
ranks sources jsonld > microdata > text0.2ms
tech-briefs.vitest.ts
29/29 53ms · 8 suites PASS
src/seo/tech-briefs.vitest.ts
Q05 — unverified is not absent · 4 tests
never counts an unchecked URL as one Google refused24.4ms
the gatherer counts ONLY exact_absent as refused1.5ms
kills the hypothesis when nothing submitted was refused0.7ms
finds orphans — in the index, absent from the sitemap0.6ms
Q05 — the health verdict is a definition, evaluated · 4 tests
is FALSE when submitted far exceeds indexed0.5ms
is TRUE only when nothing is refused and nothing is broken0.5ms
NOT HEALTHY and NOT MEASURED are different — null, never false0.4ms
the ratio threshold is stated, not buried0.5ms
Q05 — what cannot be seen is named, not dropped · 3 tests
render comparison is always untested — there is no second render to diff1.4ms
says language-alternate tags are read by nothing available today0.5ms
never-checked and checked-and-clean do not render alike2.7ms
Q02 — Impact is measured, never page count · 5 tests
ranks by share of impressions, not by how many pages a crawler flagged1.1ms
divides by effort, and the weights are stated0.5ms
halves confidence when the affected pages earn nothing0.6ms
says Impact is UNKNOWN rather than substituting page count0.7ms
calls the order PROVISIONAL when a fault survives but impact is unmeasured0.5ms
Q02 — cosmetic work WAITS, it is not merely ranked lower · 4 tests
holds cosmetic items while a blocking issue exists1.6ms
a 90%-impact cosmetic item still waits — the rule beats the score0.3ms
releases cosmetic work only when index is CLEAN and nothing blocks0.8ms
UNKNOWN is not green — with no index check the hold stays on0.4ms
Q02 — an unmapped issue code is listed, never dropped · 1 test
keeps it with a readable name and no invented severity0.7ms
both briefs — the shared rules still hold · 3 tests
no confident verdict on fewer than two sources1.2ms
every untested hypothesis says what would settle it0.4ms
no internal vocabulary reaches the reader — GS-0051.2ms
Q02 and Q05 are reachable and rendered · 5 tests
route on the sentence, and are traceable apart2.8ms
share ONE gatherer0.3ms
are built before the empty-evidence gate that swallowed Q140.7ms
ONE renderer serves all five briefs1.0ms
do not steal the tools they sit next to1.5ms
backlink-wall-clock.vitest.ts
28/28 15ms · 6 suites PASS
src/campaigns/backlink-wall-clock.vitest.ts
1. one deadline, taken once, spent by every leg · 7 tests
the whole-run deadline fits inside the tool budget with real slack2.7ms
the deadline is taken ONCE, before any leg spends against it0.6ms
discovery stops starting new SERP queries past its share0.5ms
the harvest gets the REMAINDER, not a flat number0.8ms
a spent budget skips the harvest instead of starting it with nothing left0.3ms
storage keeps a reserve, so the last leg is not the one that overruns0.3ms
a budget-truncated run SAYS SO0.7ms
2. the abort actually cancels something · 4 tests
the shared actor poll honours a signal0.4ms
apifyDomainContacts accepts one and forwards it to the poll0.5ms
the dispatch passes the turn signal into BOTH legs0.4ms
withTimeout still aborts AND rejects — the fix is downstream, not here0.3ms
3. the budget is measurable now · 3 tests
every tool run records its wall-clock0.4ms
the duration is on the tool_run marker, beside status0.7ms
it is a STRING, so a fast run recording 0 survives0.5ms
the guard now covers the tool it was written for · 2 tests
backlink_outreach_search is in DEADLINES0.3ms
it still covers the original tool too0.2ms
the WORK is sized to the budget, not just the clock · 6 tests
derives how many domains the remaining budget can finish0.2ms
the pick takes the MINIMUM of what was asked, what the plan allows, and what fits0.2ms
always attempts at least one domain0.2ms
the harvest budget is computed BEFORE the domain count that depends on it0.5ms
a clock-capped run says so, and says it DIFFERENTLY from a truncated one0.3ms
the per-domain estimate is stated as measured, with margin0.2ms
the Apify sweep: every leg states a bound inside its tool budget · 6 tests
seo_offpage_audit declares a budget instead of inheriting 30s0.3ms
no longer contains an authority leg to bound0.5ms
every apifyRunSync leg that remains still fits inside its tool budget0.4ms
apifyRunSync REQUIRES a wait bound — no default to inherit0.4ms
every call site passes a bound1.0ms
the guard asserts the signature BEFORE it decides to pass0.5ms
capability-suite-2026-09-18.vitest.ts
28/28 97ms · 3 suites PASS
src/chat/capability-suite-2026-09-18.vitest.ts
capability suite 2026-09-18 — connector slice · 4 tests
cap-conn-diagnose-nudge: a diagnosis whose gaps are closed by Google carries a Connect Google chip5.0ms
cap-conn-backlink-value-nudge: "nothing on file" hands the user the scan as a chip0.3ms
cap-conn-rank-track-nudge: the manual-registration result renders no blank "for :"6.1ms
cap-list-connectors-direct: dormant connectors (Shopify, Gmail-send) are not in the catalog2.3ms
capability suite 2026-09-18 — core slice · 11 tests
cap-read-url-nc: a recited INTERNAL refusal is removed whole, and a domain dot is not a sentence end3.2ms
cap-read-url-direct: the egress fetch identifies itself (Wikipedia answers a bare fetch with 403)0.6ms
cap-web-search-direct: read_url is told never to guess an address1.5ms
cap-add-contacts-direct: a saved contact offers the contacts panel and the next use1.2ms
cap-generate-emails-direct: a named list is a complete draft ask and routes deterministically22.7ms
cap-generate-emails-nc: nobody named → the lists as a picker, chips carry the names, no parameter names in prose1.0ms
cap-campaign-stats-nc: a named campaign that does not exist is named back, with the ones that do2.2ms
cap-create-campaign-direct: a campaign created "for the dentist list" is filled at creation2.2ms
cap-scan-product-nc: a failed scan offers the saved site as a click0.2ms
cap-list-contacts-nc: a list that does not exist offers the ones that do as chips0.4ms
cap-scan-product-nc: a domain that does not resolve is named as such, not as a temporary error0.4ms
capability suite 2026-09-19 — leads + campaigns slice · 13 tests
cap-enroll-sequence-gate: an enrolment says it is paused, renders verbatim, and offers the start2.5ms
cap-enrich-direct: "the dentist list" finds the list called dentist1.0ms
cap-backlink-outreach: a linking-sites ask is not the saved-contacts disambiguation, and preloads leads4.7ms
cap-pause-nc: pausing with no name lists the campaigns instead of "Campaign not found."1.1ms
cap-backlinks-nc-own-site: a backlink report on the saved site carries the deep-scan door on its card0.9ms
cap-keyword-metrics-direct: a search-volume ask preloads seo0.4ms
cap-find-competitors-direct / cap-competitor-gap-nc-own-site: platforms are screened before display; the own-site gap is a stop with chips2.3ms
cap-sov-weekly-commit: switching weekly tracking on is a two-step confirm with the quote, never a same-turn write15.6ms
cap-aeo-visibility-selector: "check my AI visibility" reaches the AEO shortcut — the classifier calls the same ask aeo_check5.7ms
cap-aeo-visibility-nc-cited: an adverb between the engine and the verb still reads as a visibility ask0.5ms
cap-aeo-visibility-nc-cited (second run): "backlink tools" in a visibility question is not a second request0.8ms
jev-content-fact-check: "write a 700-word article about <topic>" reaches the writer deterministically9.7ms
cap-aeo-visibility-card-not-now: the engine picker's Run message is a protocol message — its prompts payload is never read as a question1.2ms
false-shortfall.vitest.ts
28/28 36ms · 6 suites PASS
src/chat/false-shortfall.vitest.ts
parseTokenFigure · 2 tests
reads the three shapes the agent writes8.9ms
returns null rather than a wrong number on junk1.0ms
assertedBalances — against the real sentences · 9 tests
finds 666,139 in production message 02.3ms
finds 666,139 in production message 10.7ms
finds 666,139 in production message 20.6ms
finds 666,139 in production message 30.4ms
finds 666,139 in production message 40.7ms
finds 666,139 in production message 50.4ms
does NOT mistake the price for the balance1.0ms
ignores small numbers that are counts, not balances1.8ms
finds nothing in a reply that asserts no balance0.4ms
contradictsLedger · 5 tests
FIRES on the incident: 666,139 claimed against a real 1,538,8691.1ms
stays QUIET when the refusal is honest — the figure matches the ledger0.3ms
tolerates rounding — "about 1.5M" against 1,538,869 is not a lie0.4ms
stays quiet when the balance read FAILED — we have nothing to contradict0.2ms
stays quiet on a reply that quotes no balance at all0.2ms
redactStaleBalances — stopping the number compounding · 4 tests
removes the figure from every real sentence1.1ms
leaves the PRICE intact — a quoted cost is not a balance0.2ms
leaves a reply with no balance completely untouched0.2ms
a redacted message no longer contradicts the ledger — the loop is broken4.4ms
redactHistoryForModel · 4 tests
cleans assistant turns and leaves USER turns exactly as typed0.5ms
returns untouched messages BY REFERENCE so a clean turn allocates nothing0.3ms
handles the full six-message poisoned history0.4ms
survives a malformed entry rather than throwing mid-turn2.0ms
wired into the chat loop · 4 tests
the model's copy of history is redacted before the model sees it0.9ms
does NOT redact on the way to storage1.1ms
the false-balance probe runs against the turn balance1.1ms
the balance read reports its failure instead of swallowing it2.1ms
judge-prompt.vitest.ts
28/28 25ms · 8 suites PASS
src/chat/judge-prompt.vitest.ts
resolveJudgeUserPrompt — control-token resolution · 6 tests
resolves Cost confirm to the original request with an approval note2.7ms
resolves Next action chips the same way0.4ms
skips earlier control messages when walking history0.4ms
falls back to a semantic marker when history is unavailable0.3ms
passes real user messages through untouched0.3ms
classifies all known control prefixes0.6ms
buildOutcomeNote — ground-truth outcome signal · 2 tests
empty when the run had no soft failure0.3ms
carries the failure, the score cap, and the healthy-zero exception0.5ms
buildStoredContextLine · 2 tests
marks a stored key as a failure-to-ask and a missing key as correct-to-ask1.0ms
covers all four ground-truth keys0.5ms
clampJudgeVerdict · 2 tests
enforces the disproportionate <= 0.6 taxonomy cap (observed 0.7 verdict 2026-07-16)0.4ms
leaves other modes untouched and clamps to [0,1]0.3ms
JUDGE_POLICY_GROUND_TRUTH · 4 tests
names every mandated behavior class the judge was falsely penalizing0.5ms
does not excuse bad answers — accuracy framing stays0.2ms
recognizes a genuinely-required clarifying question as a correct turn0.3ms
keeps the anti-deferral guard so it does not excuse menus or re-asking0.2ms
clean empty vs non-delivery · 7 tests
does not tell the judge to score a clean zero as an incomplete outcome0.5ms
still gives the judge the ground truth — it must not infer zero rows from prose0.3ms
names the real states that produce a legitimate zero0.3ms
redirects the judgement to how the empty was HANDLED0.2ms
an ERRORED zero still gets the full non-delivery note0.2ms
a delivered turn still gets no note at all, either way0.3ms
a declared honest empty still short-circuits before either branch0.2ms
buildStoredContextLine carries the VALUE, not only the fact of storage (2026-09-15) · 4 tests
names the stored site so the judge cannot call it an invented domain0.8ms
quotes the brief so claims drawn from it are grounded, and bounds the excerpt0.6ms
an absent value stays the plain NOT-stored line with no quote0.3ms
the policy declares token quotes as system ground truth0.2ms
the conversational judge reads an HTML reply as text (source pin) · 1 test
index.ts strips tags off a no-tool HTML reply before slicing it for the judge7.2ms
outbound-wave8.vitest.ts
28/28 16ms · 8 suites PASS
src/tools/outbound-wave8.vitest.ts
zero-argument tools reject invented narrowing · 8 tests
list_campaigns accepts the empty call3.0ms
list_sequences accepts the empty call0.3ms
list_connectors accepts the empty call0.2ms
show_marketing_plan accepts the empty call0.2ms
list_campaigns rejects an invented filter instead of ignoring it0.5ms
list_sequences rejects an invented filter instead of ignoring it0.3ms
list_connectors rejects an invented filter instead of ignoring it0.2ms
show_marketing_plan rejects an invented filter instead of ignoring it0.2ms
the three undeclared aliases · 3 tests
campaign_stats takes campaign, not name0.9ms
set_standing_instruction takes text, not instruction0.6ms
backlink_outreach_search takes topic, not query0.5ms
create_marketing_plan — declared, and genuinely read · 2 tests
accepts both fields the callee consumes0.8ms
constrains horizon to the values the implementation honours1.2ms
create_sequence keeps its typed step item · 3 tests
accepts a well-formed step0.5ms
still requires a name0.1ms
accepts omitted steps — they are generated from the brief0.1ms
connect_connector · 2 tests
takes a known connector0.2ms
rejects an unknown one rather than opening a panel for nothing0.3ms
the dispatch no longer reads what it does not declare · 4 tests
campaign_stats no longer coalesces name0.4ms
set_standing_instruction no longer coalesces instruction0.2ms
backlink_outreach_search no longer coalesces query0.1ms
leaves the unregistered delete_campaign its fallback0.1ms
the migration is complete outside commerce · 3 tests
every tool is still registered1.1ms
NOTHING remains unschematised — the last 7 went by deregistration, not migration0.4ms
the drift ledger holds nothing but the verified dormant entry1.1ms
typed object items in arrays · 3 tests
enforces the item required-fields, not just the item type0.2ms
rejects a non-object element0.2ms
still validates string-item arrays the old way0.3ms
primitives.vitest.ts
28/28 255ms · 3 suites PASS
src/tools/primitives.vitest.ts
web_search · 6 tests
passes `site` through so a competitor-scoped lookup needs no new code4.4ms
normalises results to a stable shape2.2ms
treats a ZERO-result search as a finding, not a silent empty0.8ms
caps the result count no matter what is asked for1.2ms
returns an error rather than throwing when search is down — a dead tool must not kill the turn1.4ms
refuses an empty query without calling the provider1.1ms
read_url — the skeleton · 18 tests
extracts the fields a ranking comparison turns on3.7ms
ignores script and style content — a <h1> inside a script is not a heading1.4ms
separates internal from external links, and skips mailto2.6ms
counts words on the FULL text even when the text is truncated3.9ms
surfaces the egress layer's OWN refusal rather than paraphrasing it0.9ms
goes through the egress choke point, never a bare fetch0.8ms
reports a redirect target so the model knows what it actually read0.5ms
reports a non-2xx as a FAILURE TO READ, never as a measurement0.5ms
says a 403 REFUSED the research, rather than implying the page is broken0.5ms
distinguishes rate-limiting from refusal — one is temporary0.4ms
blames US, not the site, for a timeout or an oversized page0.4ms
names the loopback case on a 52x instead of letting it read as an outage0.4ms
never fetches the TENANT'S OWN site — it points at the stored evidence instead0.4ms
matches the tenant's site regardless of www or scheme0.3ms
reads OUR OWN host through the SELF binding, never over the network — [5.0.2]34.6ms
the owner's own tenant (site = our host) is still answered from stored evidence0.5ms
says so when a page returns nothing readable, instead of an empty skeleton0.3ms
echoes the extract hint without letting it change what was measured0.7ms
a primitive result must have a VOICE, not a JSON dump · 4 tests
renders search results as prose187.7ms
says plainly when a search found nothing — that is an answer, not an empty render0.7ms
renders a page as a comparable summary, not a blob0.8ms
surfaces an error as the message rather than rendering an empty skeleton0.4ms
brand-platform-name.vitest.ts
28/28 15ms · 3 suites PASS
src/seo/brand-platform-name.vitest.ts
the generator rule — evidence from the page, not a list · 4 tests
rejects the exact page that produced the incident5.5ms
needs no entry for the NEXT builder1.7ms
does NOT reject a real brand merely because a generator is present0.6ms
falls through to a better tier rather than giving up0.3ms
the platform denylist — for builders that set no generator · 15 tests
rejects Hostinger Horizons as a whole-string brand0.3ms
rejects Wix as a whole-string brand0.4ms
rejects Squarespace as a whole-string brand0.4ms
rejects Shopify as a whole-string brand0.3ms
rejects Webflow as a whole-string brand0.4ms
rejects WordPress as a whole-string brand0.2ms
rejects Blogger as a whole-string brand0.3ms
rejects Weebly as a whole-string brand0.8ms
rejects Carrd as a whole-string brand0.1ms
rejects Linktree as a whole-string brand0.1ms
rejects GoDaddy as a whole-string brand0.1ms
rejects Facebook as a whole-string brand0.4ms
rejects Instagram as a whole-string brand0.1ms
rejects LinkedIn as a whole-string brand0.1ms
catches the Facebook case with no generator present0.5ms
real brands that merely CONTAIN a platform word — the discipline of this file · 9 tests
keeps Wix Filtration Products0.2ms
keeps Shopify Plus Agency0.1ms
keeps WordPress Maintenance Co0.1ms
keeps Facebook Marketing Partners LLC0.1ms
keeps Squarespace Circle Consultants0.5ms
keeps Hostinger Reseller Group0.1ms
keeps X-Ray Diagnostics0.1ms
keeps Blogger Outreach Ltd0.1ms
names with a plausible non-platform reading are deliberately NOT listed0.3ms
commit-diff.vitest.mjs
28/28 24ms · 6 suites PASS
scripts/lib/commit-diff.vitest.mjs
compareCommitFiles · 7 tests
THE 2026-08-22 INCIDENT: 30 declared, 31 committed3.9ms
catches the quieter twin: a file you named that never made it in0.7ms
passes only on an exact match0.7ms
normalises ./ and trailing slashes rather than reporting them as mismatches0.5ms
collapses a duplicated declaration instead of failing on it0.4ms
an EMPTY declaration does not silently pass a real commit0.3ms
ignores blank and whitespace-only paths in either list0.4ms
parseNumstat · 4 tests
reads added/deleted counts per file0.9ms
keeps a binary file as null counts, NOT as zero0.4ms
returns nothing for empty or non-numstat input rather than inventing rows0.6ms
handles paths containing spaces0.5ms
summarise · 2 tests
puts the biggest change first, because the stray hunk is usually the small one0.6ms
labels binaries instead of printing +null -null0.3ms
renames · 8 tests
parses the -z rename record as ONE row carrying both names1.7ms
consumes exactly two extra fields, so neighbours are not swallowed0.4ms
a pure rename is declared by EITHER name0.4ms
declaring BOTH names is not a mismatch0.4ms
declaring NEITHER name still fails, by the real destination path0.4ms
a rename WITH content edits reports its real line counts1.3ms
summarise shows both names so a move does not read as a new file0.2ms
a renamed BINARY file keeps null counts0.3ms
commits with no renames are unaffected · 3 tests
-z output without any rename parses exactly as the newline form does3.9ms
plain string entries still work — the old call shape is not broken0.2ms
a NUL-free stream never enters the -z branch0.1ms
the caller cannot quietly drop -z · 4 tests
throws on a brace-compressed rename instead of treating it as a path1.1ms
names the fix in the error, not just the problem0.3ms
catches the un-braced form too — git omits braces when there is no common prefix0.2ms
an ordinary path is not mistaken for one0.6ms
table-columns.vitest.ts
27/27 36ms · 7 suites PASS
src/chat/table-columns.vitest.ts
classifyColumn · 5 tests
needs EVERY cell to parse, not most4.7ms
ignores empty cells when deciding, but not when they are all there is0.6ms
calls a column prose only when it is genuinely paragraphs0.9ms
does not mistake a percentage or a thousands separator for text0.3ms
only calls a column url when every cell is an absolute url0.3ms
compareCells · 4 tests
sinks empty cells in BOTH directions0.6ms
orders numbers numerically, not lexically10.6ms
is case-insensitive on text, so Acme and acme do not split the sort0.8ms
reverses on descending1.2ms
numericValue · 2 tests
strips separators and percent signs0.5ms
sorts unparseable values to the bottom rather than to zero0.3ms
chat table sorting binds to live column position · 3 tests
has a makeSortable to check0.2ms
reads th.cellIndex at click time0.2ms
does not address rows through the captured loop index0.3ms
statusTone — a pill that lies about a verdict is worse than no pill · 4 tests
reads the many words a dozen tools use for the same verdict0.6ms
is NEUTRAL on anything it does not recognise, never a guess0.3ms
never confuses the two halves of a catch-all7.3ms
styles every verdict entity_audit issues0.6ms
classifyColumn — status is inferred, severity never is · 4 tests
infers a status column so a MODEL-authored table gets pills too0.3ms
requires EVERY cell to be a known verdict — one stranger and it is text0.3ms
NEVER infers severity — high is bad here and good in an authority column0.6ms
does not steal a column that a stronger kind already claims0.3ms
severityTone — a separate vocabulary, because "high" means both things · 5 tests
ranks severity words1.2ms
does NOT leak severity into the status vocabulary0.4ms
dispatches on the DECLARED kind, not the word0.3ms
sorts severity by RANK, never alphabetically1.0ms
sinks an unknown severity below every known one instead of guessing a rank0.4ms
docs-citation.vitest.ts
27/27 19ms · 8 suites PASS
src/seo/docs-citation.vitest.ts
ownPageKind — a path heuristic that admits what it cannot tell · 3 tests
recognises the shelves docs actually live on3.4ms
recognises the marketing site, including the root1.0ms
an unrecognised path is UNKNOWN, never quietly counted as marketing0.4ms
ownCitedPages — OURS only · 2 tests
classifies our pages and ignores everyone else's0.5ms
keeps sample URLs so the classifier can be audited against the reader's own IA1.0ms
retrievalBlockers · 2 tests
keeps only failures that would stop a bot READING the page0.5ms
a passing check is never a blocker1.7ms
what it can answer WITHOUT a docs panel · 3 tests
names marketing-instead-of-docs when every quoted page of ours is marketing1.0ms
kills it when documentation IS reaching the answer1.1ms
reports bot-access blockers from the crawl alone0.4ms
what it REFUSES to answer without a docs panel · 4 tests
will not judge constrained how-to prompts from a buyer panel0.5ms
distinguishes a docs panel that is CONFIGURED but unmeasured from one that does not exist0.7ms
answers it once the docs panel HAS been measured0.4ms
page SHAPE and versioned canonicals stay untested — nothing here reads a page0.5ms
no citations of ours is not a finding about documentation · 2 tests
does not claim marketing is being cited instead0.3ms
the situation line says the run quoted nothing of ours0.2ms
the brief holds the contract · 3 tests
owns a headline and the one-pager fields0.5ms
names no tool or field name in a claim0.4ms
an ungrounded run is labelled as recall0.2ms
Q16 and Q14 are wired everywhere a brief has to be wired · 8 tests
Q16: dispatcher builds it and it is in the answer chain0.6ms
Q16: in BOTH payload lists0.3ms
Q16: routed and labelled in the turn0.4ms
Q16: rendered before the incident fallback0.6ms
Q14: dispatcher builds it and it is in the answer chain0.3ms
Q14: in BOTH payload lists0.2ms
Q14: routed and labelled in the turn0.2ms
Q14: rendered before the incident fallback0.4ms
version-order.vitest.mjs
27/27 25ms · 8 suites PASS
scripts/lib/version-order.vitest.mjs
version-order — the bug this replaces · 3 tests
rejects the exact pair the old inequality certified7.8ms
still rejects an unchanged version, which inequality also caught0.6ms
accepts a genuine forward move2.2ms
version-order — numeric, never lexicographic · 2 tests
compares each component as a number, where string comparison gets it wrong0.6ms
orders across every component0.4ms
version-order — unreadable input is not a pass · 7 tests
returns null / not-forward for 2.491 vs 2.491.00.5ms
returns null / not-forward for v2.491.0 vs 2.491.00.3ms
returns null / not-forward for vs 2.491.01.1ms
returns null / not-forward for null vs 2.491.02.6ms
returns null / not-forward for 2.491.0 vs undefined0.4ms
returns null / not-forward for 2.a.0 vs 2.491.00.5ms
names it unreadable rather than calling it backwards0.3ms
parseVersion · 1 test
parses three plain integers and nothing else0.8ms
version-order — the four merge cases · 2 tests
row 4 — branch bumped against a quiet main — is HEALTHY and clean0.2ms
row 3 — both sides landed on the same number — is a FAILURE despite merging clean0.2ms
isReleaseWorthy — a test file is not a release · 4 tests
counts shipped code0.8ms
does NOT count tests, even under client/ and src/0.4ms
does not count docs, scripts or CI0.4ms
is safe on empty input0.2ms
bumpVersion · 3 tests
moves the component it is asked to move and zeroes the ones below0.3ms
carries past 9 rather than wrapping — the repo lives at 2.6xx with two-digit patches0.2ms
returns null rather than a plausible string on bad input0.3ms
allocateVersion — the collision this repo actually had · 5 tests
two branches from the same base no longer compute the same number1.3ms
bumps from the BRANCH when the branch is the one that is ahead0.6ms
never returns a number that is behind either side0.5ms
refuses rather than guessing when either side is unreadable0.5ms
crosses a minor boundary correctly when main has moved a long way0.3ms
email-pattern-lead.vitest.ts
27/27 23ms · 6 suites PASS
src/leads/shared/email-pattern-lead.vitest.ts
a constructed address is priced down, never at the verified rate · 3 tests
sells at the unverified tier3.6ms
is a QUARTER of a verified lead, not a whole one0.5ms
never claims the verified tier1.1ms
provenance keeps a guess apart from an observation · 3 tests
travels as its own source, not as corpus0.4ms
never claims a verified status0.3ms
is spelled out for the reader, because "unverified" is the same word for both1.0ms
the address is built from normalized_name, and only when the name can carry the shape · 3 tests
applies the pattern0.9ms
returns null for a name that cannot be split — a mononym or an initial0.6ms
returns null for a shape it does not know0.4ms
the corpus call · 7 tests
is a MUTATION and passes args as the generated wrapper, not raw jsonb1.1ms
asks for BOTH floors, and they are stricter than the inference pass stores at0.7ms
the evidence floor is not redundant with confidence0.4ms
sends the confidence floor and de-duplicates the ids1.4ms
drops a row the corpus returned without every ingredient0.5ms
returns nothing rather than throwing when the corpus cannot answer1.7ms
makes no round trip for an empty id list0.8ms
disclosure is ledgered before it is charged · 7 tests
bills once and reports the disclosure1.8ms
does NOT bill a repeat — the ledger row carries a revealed_at we did not just write0.6ms
delivers NOTHING when the ledger write returns no row — an unrecorded sale cannot be refunded0.6ms
records what produced the address, so a bad pattern is findable from its refunds0.6ms
is idempotent on the PERSON, not the address — the same human must not be sold twice0.5ms
uses a no-op update on conflict, because DO NOTHING returns no row0.9ms
does not charge when billing is disarmed, but still ledgers the disclosure0.7ms
the corpus refuses to shadow a real address, and says so in SQL · 4 tests
enforces the evidence floor in the RPC, not only in the caller0.5ms
excludes anyone holding an active business email0.4ms
treats a consumer mailbox as NOT a business address0.3ms
is VOLATILE, so Hasura exposes it under mutation_root like every other corpus RPC0.2ms
search.vitest.ts
27/27 618ms · 3 suites PASS
src/leads/shared/search.vitest.ts
shared catalog search · 20 tests
runs the vector stage for a TITLES-ONLY request (no query text)6.8ms
with neither query nor titles, only the browse branch runs0.6ms
ranks deterministically using the versioned hybrid weights11.9ms
unions duplicate retrieval candidates by canonical entity and retains signals/citations2.8ms
never exposes mutable facts without their field citation2.4ms
falls back to exact and lexical candidates when vector retrieval fails1.6ms
runs all three stages concurrently, not sequentially — a hung stage never blocks the others31.3ms
a throwing exact stage degrades on its own, without taking down lexical/vector0.8ms
skips the exact stage on prose — it cannot match, and it costs a 12.6M-row scan0.6ms
still runs exact for the shapes it CAN match0.7ms
the empty-query browse path still reaches exact — that is a different branch entirely0.3ms
vector is ON now that the corpus is embedded and 087 gave the function a real branch0.2ms
SKIPS vector — not degrades it — when the caller injects no embedder0.3ms
DEGRADES vector when the embedder is present but cannot answer0.5ms
does NOT fall back to trigram retrieval when the embedding is unavailable0.4ms
sends the embedding and K to the RPC, and only on the vector stage0.6ms
a slow embedder degrades the vector stage without delaying the others21.1ms
a SKIPPED stage is not a DEGRADED one0.6ms
uses an injected parameterized RPC contract and forces US/policy inputs0.4ms
filters non-US, disallowed sources, suppressed entities, and invalid identifiers defensively0.6ms
the lexical stage yields once the other stages have filled the ask · 4 tests
supersedes a lexical stage still running after the grace when vector already filled the ask21.9ms
waits for lexical in full when the other stages came back short120.1ms
keeps a lexical stage that finished inside the grace5.7ms
lexicalGraceMs: null restores the full wait regardless120.2ms
a titles-only request does not wait for the browse-by-filters scan once vector has filled the ask · 3 tests
supersedes a slow exact stage on the browse branch (no free text)21.3ms
a domain query still waits for exact in full — there it is the indexed, authoritative stage120.7ms
a browse request whose vector stage came back short waits for exact in full121.0ms
zero-yield.vitest.ts
26/26 15ms · 5 suites PASS
src/runtime/zero-yield.vitest.ts
resultYield · 3 tests
reads the common count and array shapes3.0ms
returns null — not zero — when it cannot tell0.6ms
refuses to read a yield from a run that never delivered0.5ms
the breaker · 9 tests
opens on the Nth consecutive measured zero, and not before1.3ms
would have stopped the 2026-07-29 incident at the threshold0.6ms
resets completely on any successful run0.5ms
re-closes once a run delivers again0.3ms
reports the trip ONCE, not on every subsequent refusal0.4ms
never counts a null reading toward the streak0.6ms
does nothing for a tool that is not armed0.5ms
is inactive when KV is unbound, like every other gate here0.2ms
names the operation, never the provider0.5ms
an async run in flight is not a yield measurement · 2 tests
returns null while the provider run is still in flight0.2ms
still measures a completed run honestly, including a real zero0.2ms
resultYield reads the SEO shapes, not just the outbound ones · 11 tests
measures an empty keywords result as 0, not as "no reading"0.2ms
measures an empty competitors result as 0, not as "no reading"0.2ms
measures an empty gaps result as 0, not as "no reading"0.1ms
measures an empty ideas result as 0, not as "no reading"0.1ms
measures an empty pages result as 0, not as "no reading"0.2ms
measures an empty issues result as 0, not as "no reading"0.2ms
measures an empty backlinks result as 0, not as "no reading"0.1ms
counts a non-empty SEO result0.2ms
still returns null for a shape it was never taught0.1ms
does not disturb the existing precedence0.4ms
arms no new circuit breaker1.0ms
an honest SEO zero is expected, not a Sentry fault · 1 test
classifies the zero-yield phrase as an expected outcome1.1ms
aipolicy-horizon.vitest.ts
26/26 51ms · 7 suites PASS
src/seo/aipolicy-horizon.vitest.ts
Q27: the calendar clause is a precondition, not a suggestion · 5 tests
states it as a gate on the tool being allowed at all5.5ms
carries it as a rule in the procedure, always1.3ms
answers YES with a condition rather than refusing the question0.5ms
requires a named human to accept responsibility, not merely to edit0.9ms
will not confirm a page-level claim on the content check ALONE0.7ms
Q27: it never claims to detect AI · 2 tests
frames the finding as whether a person added anything1.1ms
says the useful question is not which tool wrote it0.5ms
Q27: the tagging gap is the structural finding · 5 tests
confirms it when drafts cannot be traced to live pages0.5ms
says the comparison cannot be reconstructed later0.6ms
kills it when everything is traceable0.9ms
leaves the regulated-subject question to the user, always1.0ms
delivers the procedure even with nothing measured1.8ms
Q17: a range, never a date · 4 tests
says so outright and explains the cost of a date23.7ms
returns three bands, each a month RANGE rather than a point2.9ms
tells the reader not to plan against the stretch band0.7ms
shortens the range when the site is already established0.9ms
Q17: stopping is modelled, not waved away · 5 tests
counts weakly-held separately from strongly-held0.4ms
produces a number for six and twelve months0.8ms
prices the exposure once revenue is measurable0.9ms
states that the decay fractions are a judgement (GS-003)0.6ms
excludes branded terms from the starting position0.3ms
Q17: cost is asked for, never invented · 4 tests
keeps cost untested and says why0.4ms
quotes no figure and asks for the one input0.4ms
keeps capacity and rival velocity untested0.3ms
delivers the ranges even with nothing on file0.9ms
Q27 and Q17: no internal vocabulary reaches the user (GS-005) · 1 test
keeps field and table names out of the prose1.1ms
tracking-feasibility.vitest.ts
26/26 20ms · 8 suites PASS
src/seo/tracking-feasibility.vitest.ts
the empty account gets the whole answer, not a refusal · 5 tests
still answers YES and names the missing piece2.7ms
does not open with STOP — that is a diagnosis word and this is a policy0.6ms
tells them not to buy a tracker first0.3ms
its situation line says the absence is the subject, not a limit0.3ms
the ask is the panel, with a weekly owner0.4ms
the grounding kill · 4 tests
an ungrounded run KILLS the retrieval hypothesis rather than footnoting it0.8ms
says why it is easy to miss — every cell still fills0.5ms
the decision refuses to let a memory score be reported as visibility0.3ms
a grounded run lets it survive1.3ms
mention and citation are different events · 2 tests
a run with mentions and zero sources is killed, not called partial0.6ms
counts both, so the reader can see the gap between them0.7ms
the frozen-list hypothesis needs a REPEAT, not a run · 4 tests
one run on the panel is not a frozen list0.5ms
two runs on the SAME panel make it frozen0.6ms
two runs on DIFFERENT panels do not — that is two questions, not two readings1.3ms
an untagged run counts for nothing — it cannot be matched to a panel1.6ms
a panel under the floor is a spot check, not a measurement unit · 2 tests
is killed, and names the floor and why it exists0.4ms
primaryPanel picks the largest, so one thin panel cannot hide a real one0.4ms
the referral leg is corroboration and says so · 3 tests
when referrals exist it still refuses to be the measurement0.5ms
no analytics is untested, not killed — nothing was looked at0.4ms
analytics connected with no AI referrals is expected, and said so0.3ms
the brief holds the shared contract · 3 tests
carries every one-pager field and its own headline0.7ms
the two-source rule holds — no verdict rests on one reading0.4ms
never claims to measure citation PERFORMANCE — that is Q02/Q19/Q260.4ms
Q04 is wired on every surface · 3 tests
is built, chained, in BOTH payload lists and rendered0.5ms
renders ahead of the incident fallback, or it never reaches the page0.8ms
is routed and labelled in the turn0.6ms
preflight.vitest.ts
26/26 24ms · 7 suites PASS
src/tools/intelligence/preflight.vitest.ts
dispatch · 4 tests
says nothing for a tool with no assessor — so a gate can call it unconditionally4.0ms
does not even count a tool it has no assessor for0.9ms
NEVER throws when an assessor throws — an advisory check cannot break the action it advises on2.0ms
NEVER throws when telemetry fails — the finding still reaches the user2.0ms
measurement — the gap §9 names · 1 test
counts a silent run as well as a firing one, so a RATE is computable0.9ms
output invariants — an assessor cannot break the card it rides on · 4 tests
drops a finding with no note: a chip with no stated reason is a mystery button0.4ms
bounds the note and the chips rather than trusting an assessor to1.6ms
drops empty chips instead of rendering a blank button0.4ms
survives an assessor returning junk0.8ms
registry · 7 tests
carries both adopted tools, and the barrel is what loads them1.2ms
a retired tool name cannot dodge the check1.4ms
every external-effect tool has an assessor or a written exemption0.4ms
every exemption states a reason, because "no check needed" is only useful when argued1.4ms
every SPEND-VARYING tool has a decision too — the axis the first version missed0.3ms
"not yet" and "never" stay in different maps0.5ms
a wanted tool that grows an assessor leaves the debt list0.4ms
attachAdvisory · 4 tests
carries the finding as a field, leaving the card copy untouched0.4ms
returns the card unchanged when there is nothing to say0.3ms
does not mutate the card it was handed0.3ms
never invents a confidence or a verdict0.3ms
historyWithAdvisory · 2 tests
keeps the note in the record even though the card no longer prints it0.3ms
leaves the message alone when there is no finding0.2ms
note clamping · 4 tests
never cuts mid-word0.5ms
prefers stopping on a complete sentence0.5ms
does not keep a sentence break that throws away most of the finding0.5ms
leaves a note under the cap completely alone0.3ms
vitals.vitest.ts
25/25 48ms · 8 suites PASS
src/dashboard/vitals.vitest.ts
the spine is fixed · 4 tests
opens with what is waiting and closes with the constraint24.2ms
says "Nothing" rather than 0 when nothing is waiting1.9ms
flags waiting work and offers the way to it0.6ms
renders a missing balance as null, never as zero0.4ms
a number it cannot stand behind is not shown · 3 tests
suppresses the visibility score when the sample is too thin0.9ms
says "too thin" rather than an em dash, which would imply unmeasured0.6ms
carries the trend only when there is one0.4ms
the gap between tracked and ranked is its own row · 3 tests
shows how many of the tracked keywords have ever been ranked0.5ms
warns when NONE of them have been checked0.4ms
drops the row entirely once every tracked keyword has a rank2.5ms
problems appear, non-problems do not take up space · 4 tests
shows unindexed pages with the way to investigate1.4ms
omits the row at zero rather than printing a permanent "0"0.4ms
shows site health moving, which is what the tenant could not see0.4ms
omits the delta when the score has not moved0.2ms
dormant modules stay VISIBLE, muted — never omitted · 2 tests
shows every module as a dormant row with a way in0.7ms
never leaves a dormant row without a command0.3ms
outbound keeps only the rows that pass the overnight test · 4 tests
drops the odometers0.9ms
keeps bounces and whether anything is running0.6ms
hides bounces at zero but still says nothing is running0.3ms
goes dormant when outbound has never been used, even with contacts on file5.9ms
the panel stays a panel · 4 tests
does not outgrow the sidebar on the busiest tenant1.4ms
never drops a warning, a dormant row, or the spine — only healthy detail0.8ms
returns the rows untouched when they already fit0.7ms
never returns fewer than the spine0.3ms
fmtTokens · 1 test
matches the panel formatting so nothing jumps0.3ms
capabilities-answer.vitest.ts
25/25 58ms · 7 suites PASS
src/chat/capabilities-answer.vitest.ts
it renders as HTML, and safely · 8 tests
is a styled HTML body, not markdown3.8ms
uses NO italics anywhere — owner ruling, italics read as low confidence0.5ms
escapes the stored domain — settings are user-supplied input0.4ms
prices nothing in dollars and names no vendor (CLAUDE.md §4)0.4ms
survives the outbound guardrail intact8.0ms
gives every row all three cells — a ragged table is worse than a list0.7ms
has no coloured left rule on the cards — owner ruling, it reads as an alert0.4ms
lays sections out horizontally, and still collapses on a narrow panel0.6ms
clubbing must never lose a capability · 5 tests
puts every item in exactly one themed row0.8ms
catches an item that matches NO theme instead of dropping it1.5ms
sinks the catch-all to the bottom — it is the least specific row0.3ms
shows the real commands in the row, not just a category noun0.7ms
states the total and prices each themed row in tokens0.5ms
it ends by ASKING, not by listing (CLAUDE.md §3a) · 2 tests
closes with exactly one question0.5ms
offers the right first move for what they actually hold0.5ms
it adapts to what we already hold · 3 tests
says it already knows the domain0.4ms
asks for the domain once when we have none, instead of pretending0.3ms
names the user when we have a name, and does not invent one when we do not0.3ms
the intelligence layer is part of the answer, not a footnote · 3 tests
covers the BEFORE-the-spend half, not only the research half0.4ms
states all four differentiators as observable behaviour0.3ms
promises the unverified marker the code actually renders0.2ms
the chips give somewhere to go · 2 tests
offers four concrete starters, site-specific when we know the site1.0ms
still offers four when we know nothing about them0.3ms
routing — the shortcut must not eat real questions · 2 tests
catches the phrasings users actually open with16.8ms
does NOT eat a real question that starts the same way16.8ms
note-priority.vitest.ts
25/25 35ms · 7 suites PASS
src/leads/note-priority.vitest.ts
the CA repro: paid-boundary stop must not be misattributed to missing product brief · 3 tests
does NOT return the product-brief catch-all when a real paid shortfall explains the 0 results3.4ms
the catch-all only fires when there truly is no other explanation (paidShortfall === 0)0.9ms
a real sourceNote always wins over the catch-all, brief missing or not0.6ms
person_locality: a location filter must explain itself, never go silent (widened to cities 2026-08-29) · 3 tests
discloses the limitation even when the corpus/paid tiers would otherwise explain 0 results0.5ms
fires even on a NONZERO result — those results matched on something other than state0.4ms
does not fire when person_locality was never requested0.3ms
sharedNote must reach the user, not be silently dropped · 2 tests
surfaces diag.sharedNote when the corpus tier had an honest WHY and nothing else outranks it0.5ms
sourceNote still outranks sharedNote when both are present0.4ms
existing priority order is preserved · 6 tests
credit error always wins0.5ms
apollo error suppresses the note entirely (handled by `error` field elsewhere)0.4ms
free-tier cap message renders with the real requested/cap numbers — when the cap actually cut the delivery0.4ms
poor-fit verdict rides even on a delivered, non-empty batch0.5ms
all-owned-tier note fires only when something was actually found0.3ms
a fully satisfied, well-fitting, product-brief-having search gets no note at all0.2ms
unappliedOwnedTierFilters — say what did not run, and nothing else · 5 tests
reports ONLY revenue — the one with no column and no source data2.0ms
does NOT claim industry or company size were dropped — they are applied now0.6ms
says nothing at all when the paid provider ran — it applies revenue too1.7ms
reports only what was actually requested1.0ms
leaves the single-string note picker free to say something else0.3ms
a degraded corpus is the headline · 3 tests
beats the cap note, the coverage note and the paid boundary on a zero result0.4ms
beats "all from your own contacts" on a partial result, but not the fit verdict0.4ms
a provider credit error still comes first — the search did not run at all0.2ms
the cap note is only for a cap that actually cut something · 3 tests
stays silent on a zero result0.2ms
stays silent when fewer than the cap came back13.2ms
never says "you asked for N" about a number the user did not type0.5ms
paid-escalation.vitest.ts
25/25 13ms · 7 suites PASS
src/leads/paid-escalation.vitest.ts
when the free sources come up short · 4 tests
escalates on a full miss2.7ms
escalates on a partial fill0.4ms
BUYS THE SHORTFALL, NOT THE ORIGINAL ASK0.3ms
carries every other filter through unchanged0.9ms
when escalating would be wrong · 6 tests
does not escalate a search the user already paid for0.3ms
does not escalate when the ask was filled0.3ms
does not escalate a FAILED search0.2ms
does not escalate while a provider run is already in flight0.3ms
is null-safe on both sides0.4ms
ignores a nonsense shortfall rather than quoting one0.5ms
what the user reads · 4 tests
leads with what was already tried, not with the price0.6ms
says how many were found when some were0.4ms
names no vendor0.5ms
quotes no dollars — this product bills in tokens0.2ms
the chip text, for the user who still types it · 4 tests
is one string built in one place0.3ms
round-trips: what we emit, we recognise0.4ms
tolerates a user editing it before sending0.2ms
does not fire on an unrelated sentence0.2ms
the card replaces the prose, it does not sit under it · 5 tests
drops the offer made in words, because the card makes it with a button0.8ms
KEEPS the result — this strips the offer, never the answer0.3ms
leaves a delivering reply completely untouched0.2ms
leaves no censored-looking gap behind0.2ms
is null-safe0.2ms
no paid quote on a degraded run · 1 test
stands down when the corpus could not finish0.3ms
no paid quote over a refused delivery · 1 test
stands down when the relevance gate refused candidates0.2ms
pillar-page-parity.vitest.ts
25/25 188ms · 2 suites PASS
src/routes/pillar-page-parity.vitest.ts
both pillar pages carry the whole template · 22 tests
has a hero image WITH intrinsic dimensions132.6ms
shares that image socially, or every share is a bare link2.5ms
carries the outcome panel — the block an engine lifts to answer "what is this page for"1.1ms
carries the diagnosis block1.9ms
carries the interactive simulator1.5ms
wraps its body in <article> and uses <section> for each h23.2ms
serves Last-Modified, so a crawler can skip an unchanged page1.6ms
carries all three JSON-LD blocks2.5ms
its WebPage schema names a language, an image and a publisher3.8ms
states a question count that matches the questions it lists4.7ms
has no stray unstyled robots directive in the body3.9ms
has a hero image WITH intrinsic dimensions2.7ms
shares that image socially, or every share is a bare link2.1ms
carries the outcome panel — the block an engine lifts to answer "what is this page for"2.0ms
carries the diagnosis block1.7ms
carries the interactive simulator1.7ms
wraps its body in <article> and uses <section> for each h21.6ms
serves Last-Modified, so a crawler can skip an unchanged page1.1ms
carries all three JSON-LD blocks1.3ms
its WebPage schema names a language, an image and a publisher2.2ms
states a question count that matches the questions it lists2.2ms
has no stray unstyled robots directive in the body1.9ms
/ai-visibility-answered meets the SERP budgets it was rewritten for · 3 tests
title fits the ~60-character cut, differentiator included2.1ms
description fits the ~160-character cut1.7ms
title and description agree with the question count rather than hardcoding it2.0ms
tier-search.vitest.ts
25/25 451ms · 7 suites PASS
src/leads/shared/tier-search.vitest.ts
revealCorpusCandidates · 7 tests
reveals only up to the shortfall the ladder still needs26.6ms
never treats a phone identifier as an email address19.5ms
never spends a REVEAL round trip on a candidate with no identifier, and batches the P2 ask23.5ms
offers a constructed address ONLY for the shortfall, after every held address is used27.2ms
lets one refused reveal cost one lead, not the rung24.8ms
marks every corpus lead unverified, with the corpus as the source32.1ms
dedupes one address that appears on two candidate entities27.1ms
corpusLeadTier · 6 tests
arms only the tenants named in the flag1.4ms
still arms everyone on the literal 10.4ms
is absent without a tenant, even with the flag on0.3ms
is absent when the tier flag is off0.3ms
exists only when both hold0.2ms
caps the corpus ask at 25 — the ask drives the wall clock0.3ms
the stage deadline fits inside the rung deadline · 1 test
gives a stage enough room to actually finish0.8ms
the relevance gate sits BEFORE reveal (2026-09-15, [3.1.3]) · 2 tests
a no-fit sample reveals nothing, bills nothing, and says so34.2ms
an unreadable verdict fails OPEN — reveal runs exactly as before26.3ms
the reveal phase accounts for every candidate it was given · 2 tests
counts failures by reason and keeps the sample, while delivering what it can21.8ms
a reveal that never returns is a timeout, not a hang45.5ms
the rung sizes every phase inside the budget it was given · 5 tests
derives the stage deadline from the budget minus the post-search reserve0.8ms
a search every stage of which stalled says the database was slow — and spends nothing more36.5ms
a superseded lexical stage is not a stalled one0.5ms
matched people it could not release: says so, in our words, as our fault52.9ms
the wording never names a vendor or an internal stage name0.6ms
"show the closest matches anyway" skips the relevance gate · 2 tests
a no-fit verdict carries the marker the reply reads19.5ms
with acceptClosest the gate is not even asked, and the candidates are delivered24.6ms
format.vitest.ts
24/24 21ms · 6 suites PASS
client/format.vitest.ts
fmtApprovalTokens — the §4 surface · 3 tests
says "No extra cost" for zero, never "0" and never "Free"3.2ms
never emits a currency symbol at any magnitude1.1ms
rounds by magnitude the way the card reads1.1ms
esc and escVitals — two escapers that disagree, on purpose · 4 tests
both escape the four HTML-significant characters0.8ms
DISAGREE on null, and both behaviours are live0.6ms
escVitals also blanks 0, which esc renders0.4ms
escapes an ampersand FIRST, so an escape is never double-escaped0.5ms
relTime · 4 tests
reads "now" under a minute0.5ms
counts minutes, then hours, then days1.1ms
rounds rather than truncates at each boundary0.5ms
does not crash on a future timestamp1.5ms
truncateRecentTitle · 6 tests
passes a short title through, trimmed0.5ms
falls back to "Chat" only for a FALSY title0.3ms
returns EMPTY for a whitespace-only title — the fallback does not catch it0.2ms
keeps a 44-character title whole and clips a 45-character one0.4ms
uses three ASCII dots, not a single ellipsis character0.4ms
does not leave a dangling space before the dots0.3ms
stripChipMd · 3 tests
strips ** and __ emphasis the model emits0.4ms
is not a markdown parser and leaves everything else alone0.3ms
renders null and undefined as empty, not as "null"0.4ms
detectDelimiter · 4 tests
recognises a tab-separated header4.2ms
recognises a semicolon-separated header (decimal-comma locales)0.3ms
defaults to comma0.3ms
is not fooled by a comma inside a quoted header when tabs separate0.2ms
visibility-response.vitest.ts
24/24 67ms · 8 suites PASS
src/chat/visibility-response.vitest.ts
the reply is scannable, not a wall of bullets · 4 tests
renders markdown pipe tables the chat renderer can draw34.9ms
leads with the verdict and the movement, not a label1.0ms
shows no composite line when the blend equals the citation rate0.9ms
draws a bar beside every rate, never instead of one0.6ms
nothing the old prose carried was lost in the reformat · 5 tests
keeps the run cost0.8ms
keeps every engine, including the weak ones1.0ms
keeps the share-of-voice denominator caveat, which is the whole reason that number differs0.7ms
keeps the auto-detected competitor-set disclosure0.5ms
keeps both gap types distinct — a missing page and an ignored page need different work1.4ms
Google's two surfaces are reported separately · 2 tests
names AI Overviews and AI Mode with their own rates3.6ms
omits the table entirely rather than showing a surface that did not run1.6ms
an unreachable engine is called out before any table · 1 test
says it was excluded, not scored zero1.5ms
a percentage over one answer states what it rests on · 4 tests
THE REGRESSION: the headline names the basis, not just the shape asked for1.0ms
warns, in the same reply, that the number cannot be acted on0.6ms
still carries the stored mention-event caution beside the share0.6ms
an adequate run keeps the old sentence and gains no warning0.4ms
sov_trend states the sample behind each point · 3 tests
qualifies the current figure and the delta beside it8.3ms
puts the basis on every HISTORY row, so the column cannot read as volatility0.7ms
carries both cautions — they count different things0.7ms
pctBar · 1 test
is proportional and clamped0.6ms
a composite run quotes the CITATION rate, not the blend · 4 tests
the headline is 42%, the citation rate — never the 26% blend0.9ms
the headline is never below every engine in the table beneath it0.9ms
still reports the blend — labelled as a score across surfaces, naming its legs0.7ms
refuses a delta across a change of engine set, and says why0.4ms
model-cooldown-attribution.vitest.ts
24/24 28ms · 2 suites PASS
src/llm/model-cooldown-attribution.vitest.ts
a cooldown entry is a claim about the model, not about our request · 18 tests
404 is model health — cool it down2.3ms
408 is model health — cool it down0.4ms
409 is model health — cool it down0.2ms
429 is model health — cool it down0.3ms
500 is model health — cool it down0.2ms
502 is model health — cool it down0.2ms
503 is model health — cool it down0.1ms
504 is model health — cool it down0.1ms
400 is OUR payload — do not cool it down0.2ms
401 is OUR payload — do not cool it down0.2ms
402 is OUR payload — do not cool it down0.1ms
403 is OUR payload — do not cool it down0.1ms
413 is OUR payload — do not cool it down0.1ms
422 is OUR payload — do not cool it down0.1ms
404 is health, not a payload error — that is the whole reason this is a list0.1ms
both !res.ok branches gate markModelBad on the classifier2.3ms
a truncation at our own ceiling does not mark the model bad0.6ms
no write site is gated on isLast any more — the safety net lives on the READ0.3ms
the tool-call timeout floor clears observed successful latency · 6 tests
is above the slowest chat model call that actually returned0.3ms
leaves real headroom, because the sample is right-censored0.1ms
still leaves the ceiling meaningfully above the floor0.1ms
a short prompt gets exactly the floor, and scaling still adds per 1K tokens0.4ms
never exceeds the ceiling however large the prompt17.9ms
the reasoning floor is not weakened by the raise0.3ms
clarify-gate.vitest.ts
24/24 17ms · 5 suites PASS
src/runtime/clarify-gate.vitest.ts
the incident prompt is stopped before it spends · 2 tests
asks instead of spending on "founders working on programmatic SEO for directories"4.5ms
explains what IS usable, so it reads as help rather than refusal1.4ms
a well-specified prompt is never interrogated · 8 tests
proceeds when the request declares a industry0.5ms
proceeds when the request declares a specific title0.3ms
proceeds when the request declares a exec title with an industry0.2ms
proceeds when the request declares a geography0.2ms
proceeds when the request declares a domain0.2ms
proceeds when the request declares a a local search, which carries its own targeting0.2ms
proceeds when the request declares a all three0.3ms
no longer treats business_function as a handle — the field does not exist any more0.3ms
scope · 4 tests
never gates a tool that spends no provider money0.3ms
leaves a paid tool with no probe alone rather than inventing a question0.2ms
gates a paid-plan user too0.4ms
only ever probes tools that actually cost money0.6ms
per-tool probes · 8 tests
asks which competitor, since a gap analysis has nothing to compare without one0.6ms
asks for keywords on the per-keyword billed tools0.6ms
does not gate seo_enrich_keywords on its own default mode0.2ms
accepts a saved site instead of asking for a domain0.4ms
off-page takes the same saved-site escape as backlinks — it is the one that broke live0.3ms
an ARGS key can no longer open the gate — only the caller-supplied fact can0.2ms
every tool the caller looks the site up for is a tool that probes for one1.6ms
never probes verify_contacts — the tool's own picker names the lists, this layer cannot0.3ms
rendering · 2 tests
leads with the question and carries chips, not prose instructions0.7ms
names no vendor0.6ms
aeo-cross.vitest.ts
24/24 24ms · 2 suites PASS
src/seo/aeo-cross.vitest.ts
deriveAeoFindings — the joins · 10 tests
J1: a page that is READY and never retrieved4.9ms
J2: a page AI already cites is the weakest answer on the site0.8ms
J2 matches through www and trailing-slash drift1.1ms
J3: entity collision AND zero citations is a diagnosis, not two facts0.4ms
J3 does NOT fire when the brand is cited despite a collision0.4ms
J4: the winning shape is one nobody can publish their way into1.1ms
J4 stays silent when the winning shape IS something we can publish0.4ms
findings come back in directive order (GS-011)0.5ms
nothing on file produces nothing — never a padded finding1.1ms
every absence copy states a CONSEQUENCE, not an instruction1.4ms
aeo_full_audit artifact (R-G acceptance) · 14 tests
no longer staples three whole sub-reports together0.5ms
leads with the join, and renders all four directive groups with their absences0.5ms
shows both sides of the evidence for each finding0.4ms
names every source, its age, and whether it was on file at all0.2ms
the score is greyed and labelled provisional while the picture is partial0.2ms
a cross-check source that is absent does not grey the score0.5ms
a complete, fresh picture scores in colour with no provisional label0.3ms
a tenant with nothing on file gets guidance, never an error card0.3ms
renders both sides, both timestamps and the countable difference0.6ms
leads on the contradiction rather than on the score0.2ms
states the presence reading the "effectively absent" narrative contradicted0.2ms
claims the sources agree ONLY when the checks actually ran0.5ms
timestamps same-day sources to the minute, not "measured today"1.2ms
an artifact stored BEFORE R-G still renders its nested sub-reports (trap 14)5.2ms
aeo-disagreements.vitest.ts
24/24 15ms · 4 suites PASS
src/seo/aeo-disagreements.vitest.ts
deriveAeoDisagreements — the live contradiction · 8 tests
does NOT report two runs 27 minutes apart as a contradiction2.0ms
still catches the pair that WAS one run — 0% and 40%, 36 seconds apart2.1ms
names the countable difference — the surfaces — not a guessed mechanism (AEO-008)0.7ms
goes quiet once the run publishes one share to both records — the v2.491.0 unification0.4ms
catches the stale competitor set that could only ever score zero0.3ms
catches the 33/100 headline sitting beside a real citation0.3ms
does NOT call the same day's 100-to-0 swing a disagreement0.3ms
reports every one of them — this is the tool's stated purpose, not a footnote1.1ms
deriveAeoDisagreements — silence when the sources agree · 9 tests
returns an empty list rather than padding it0.6ms
does not invent a share-of-voice conflict from noise0.4ms
says nothing about a second run that is not on file0.4ms
does not call a thin run a competitor-set mismatch0.3ms
fires only when the two runs are actually comparable0.5ms
will not let two single answers contradict each other0.7ms
will not call a changed question a changed answer0.3ms
does not compare two runs a month apart as a same-picture swing0.3ms
every disagreement carries two sides, a countable difference and a reading1.0ms
deriveAeoDisagreements — the converse of the live defect · 1 test
flags a strong score over a run that cited nothing0.6ms
aeo_full_audit dispatch — the payload actually carries them · 6 tests
is a real slice of the dispatch, not an empty string that passes everything0.2ms
loads the share-of-voice series — the second number nothing could see0.2ms
computes and RETURNS both, or the modules are dead code0.4ms
puts them ahead of the evidence tail, so the model budget cannot drop them0.3ms
stamps every source with the timestamp the ledger renders0.2ms
reports the disagreement count in telemetry0.2ms
landscape-policy.vitest.ts
24/24 56ms · 7 suites PASS
src/seo/landscape-policy.vitest.ts
Q12 — the rule is the deliverable, not the list · 6 tests
returns written rules, each tied to what was measured4.8ms
writes NO rule for a problem this site does not have1.8ms
carries the "align to Google" clause, which is the counter-intuitive half0.7ms
orphans are the facet signature, and need BOTH systems1.3ms
variants are NEVER killed — a crawler cannot tell a meaningful difference from a cosmetic one1.2ms
"Google ignored the hint" is always untested — we hold only what the page declares1.2ms
Q04 — classifying who holds the result · 1 test
matches on host or subdomain, never substring1.1ms
Q04 — the scorecard follows the park rule · 4 tests
AVOID beats everything — a term held by a marketplace is parked at any position1.4ms
WIN is striking distance, using the IMPORTED definition19.0ms
a term we do not rank for is WATCH, and says why rather than guessing0.8ms
leads with the parking when nothing is winnable — that IS the saving0.8ms
Q04 — the half of the rule we cannot compute is said out loud · 4 tests
never claims rival authority0.5ms
the links hypothesis is permanently untested, and says why rank is not a substitute0.5ms
whitespace is untested — a domain does not tell you a page format0.4ms
does not re-derive striking distance0.5ms
both — shared rules and reachability · 5 tests
no confident verdict on fewer than two sources0.7ms
no internal vocabulary reaches the reader — GS-0050.9ms
route from the sentence, traceable apart, one renderer11.0ms
do not steal the tools they sit beside1.1ms
Q12 reuses the SAME technical evidence — a third read could disagree with the other two2.2ms
Q12 delivers its policy when nothing survives · 2 tests
says what it can rather than returning a bare STOP, and keeps the coverage sentence0.9ms
DOES point at the rules when it has some0.2ms
Q04 — striking distance: the reading is having positions, not finding one · 2 tests
KILLS page_two when positions were read and none sit in the range0.3ms
still reports untested when no term has a position at all0.4ms
vendor-leak.vitest.ts
24/24 20ms · 2 suites PASS
src/seo/vendor-leak.vitest.ts
the guardrail rail sees a vendor name inside an identifier · 11 tests
redacts DATAFORSEO_CREDENTIALS_MISSING5.2ms
redacts APIFY_RUN_FAILED0.4ms
redacts OPENROUTER_KEY missing0.3ms
redacts NHOST_ADMIN_SECRET not set0.3ms
redacts HASURA_GRAPHQL_ADMIN_SECRET0.4ms
redacts MILLIONVERIFIER_TIMEOUT0.3ms
redacts PROXYCURL_4020.3ms
still catches the prose form it always caught0.4ms
does NOT redact a vendor name buried inside a real word0.7ms
leaves the deliberate user-facing allowlist alone0.6ms
covers every name on the list, so a supplier added later is protected too2.2ms
friendlySeoError never hands a vendor or a machine code to the user · 13 tests
maps DATAFORSEO_CREDENTIALS_MISSING to a human sentence1.1ms
maps DFS_NO_TASK to a human sentence0.8ms
maps VALUESERP_KEY not configured to a human sentence0.3ms
maps APIFY_NO_KEY (apify/actor-id) to a human sentence0.2ms
maps APIFY_ERROR_500 (apify/actor-id): body to a human sentence0.3ms
maps APIFY_NO_CREDITS (apify/actor-id) to a human sentence0.2ms
maps No SERP provider configured — add SERPER_KEY via wrangler secret to a human sentence0.5ms
says WHICH KIND of failure it was, rather than falling through to the generic0.6ms
PASSES THROUGH a sentence already written for a user1.2ms
a site-crawl timeout is no longer blamed on the provider0.2ms
keeps the branch order that names the right subsystem0.4ms
is idempotent — a friendly message survives a second pass0.2ms
and the belt still holds if a future branch forgets: scanOutbound catches the raw form2.2ms
set-bounds-and-quotes.vitest.ts
23/23 13ms · 2 suites PASS
src/billing/set-bounds-and-quotes.vitest.ts
§5 — every set-operating tool can be told how many · 12 tests
generate_emails accepts a count3.4ms
generate_emails tells the model to use it0.6ms
enrich_contacts accepts a count0.5ms
enrich_contacts tells the model to use it0.5ms
verify_contacts accepts a count0.5ms
verify_contacts tells the model to use it0.4ms
assign_to_campaign accepts a count0.4ms
assign_to_campaign tells the model to use it0.2ms
enroll_in_sequence accepts a count0.7ms
enroll_in_sequence tells the model to use it0.5ms
the two tools that BILL PER CONTACT are among them0.5ms
the tool that ARMS REAL SENDS is among them0.7ms
§2 — a quote that reads its own arguments · 11 tests
backlink_outreach_search has a dynamic estimator0.4ms
backlink_outreach_search falls back to the table when the count is unknown0.9ms
seo_serp_spider has a dynamic estimator0.2ms
seo_serp_spider falls back to the table when the count is unknown0.3ms
tap_volume has a dynamic estimator0.2ms
tap_volume falls back to the table when the count is unknown0.3ms
google_god_mode_report has a dynamic estimator0.2ms
google_god_mode_report falls back to the table when the count is unknown0.3ms
a small ask is quoted below a large one — the whole point0.4ms
a known count reserves no ceiling0.3ms
LLM-length spread is left static rather than dressed as per-unit0.3ms
tool-message.vitest.ts
23/23 80ms · 6 suites PASS
src/chat/tool-message.vitest.ts
toolMessageContent — the JSON is always valid · 3 tests
returns a small result byte-identical to JSON.stringify3.9ms
never emits a cut token, however the payload is shaped37.1ms
survives a circular result instead of killing the turn0.5ms
toolMessageContent — what was dropped is named · 6 tests
drops the largest key and says which one4.1ms
keeps the small answer keys and sheds only the evidence tail7.5ms
preserves the original key order of whatever survived0.7ms
marks a truncated bare string inside the value0.5ms
marks a shortened array with how many items went13.9ms
reports total loss honestly rather than emitting a stump1.1ms
the search_leads shape — the omission must not read as a failure · 3 tests
sheds the bulk keys and keeps every semantic key the model reasons from1.2ms
sheds `contact_ids` too, with no per-key list to maintain0.7ms
tells the model the field is intact, not missing — and not to apologise0.9ms
toolMessageBudget — a schema rejection is a different payload · 1 test
gives a rejection the larger ceiling and everything else the default0.3ms
the industry rejection reaches the model whole · 4 tests
would have been cut in half by the old 4000-character slice0.7ms
delivers all 318 labels, as valid JSON, under the rejection budget1.6ms
includes the values that sit past the old cut — the ones the retry needs1.9ms
still bounds a rejection that carries several vocabularies0.8ms
withSoftFailureDisclosure · 6 tests
leaves a healthy result completely untouched0.3ms
names the actual shortfall, not a generic warning0.2ms
keeps the original payload — the disclosure is additive, never a replacement0.1ms
makes reporting MANDATORY, not advisory0.2ms
closes the gap-filling routes by name0.3ms
tells the model that saying so IS the complete answer0.3ms
openapi.vitest.ts
23/23 32ms · 6 suites PASS
src/tools/openapi.vitest.ts
search_leads schema — the incident this exists to prevent · 8 tests
rejects a sentence in the industry enum5.9ms
gives free intent a home that is not an enum0.8ms
constrains every field that the provider constrains1.1ms
exposes the filters the provider still offers0.4ms
exposes the fields the rebuild introduced1.9ms
leaves person_titles free text — the provider matches titles loosely0.3ms
takes no free-text query field — that field IS the defect0.3ms
refuses unknown arguments rather than passing them through0.2ms
search_leads schema — both live branches are covered · 3 tests
carries the local-business fields, not only people search0.8ms
states the postal-code/country pairing rule0.7ms
constrains country_code to the maps actor's own ISO-2 list0.7ms
enum ↔ resolver agreement (the drift that would 400 live) · 2 tests
every geography enum value resolves through the country ladder11.1ms
company sizes use the UNSPACED ranges the provider currently requires1.4ms
copy rules on a document a model reads and a browser can fetch · 3 tests
names no backend vendor1.3ms
quotes no dollar amount for what nqzai charges0.3ms
never calls anything free1.3ms
buildToolsOpenApi · 5 tests
emits OpenAPI 3.1 with one operation per schematised tool0.7ms
derives the request schema from TOOL_SCHEMAS — no second copy0.3ms
reports enforcement state truthfully0.2ms
documents the rejection response so a caller can retry correctly0.2ms
uses the origin it was given0.2ms
getToolSchema · 2 tests
returns null for an unschematised tool rather than a partial object0.3ms
is not fooled by inherited object keys0.2ms
citation-sources.vitest.ts
23/23 23ms · 6 suites PASS
src/seo/citation-sources.vitest.ts
classifyPageShape · 6 tests
puts a roundup before an article even though roundups live at blog paths5.6ms
recovers a roundup from the TITLE when the slug hides it0.7ms
host rules beat slug rules0.4ms
separates a homepage from a product page0.5ms
never throws on junk, and junk is not silently a homepage0.3ms
every shape maps to exactly one fix owner (AEO-009)0.7ms
captureSources · 5 tests
rank is the engine ORDER, 1-based — not a search ranking1.4ms
a repeated URL keeps its BEST rank instead of becoming two rows2.2ms
drops non-http junk without consuming a rank1.1ms
truncation is REPORTED, never silent0.9ms
attaches a title when the provider supplied one, and omits the key when not1.7ms
rivalSources — durability, not volume (AEO-006) · 3 tests
breadth across engines outranks raw citation count1.0ms
excludes our own host — this table answers "who instead of us"0.8ms
an errored cell contributes nothing, and a pre-R-A cell is not read as empty0.4ms
shapeMix — what KIND of page wins here (AEO-007) · 2 tests
counts rival citations only, with top-3 share per shape1.0ms
an uncaptured run yields a zero total, which callers must render as "never looked"0.2ms
fixesByOwner — who has to do the work (AEO-009) · 3 tests
groups by owner and orders by where the citations actually are1.0ms
targets carry the URL, not just the host — a domain is not actionable0.5ms
excludes our own pages and returns nothing on an uncaptured run0.4ms
rankDistribution — AEO-004, a count equal to the execution count is a constant · 4 tests
marks the leading run whose count equals the answer count0.6ms
only a LEADING run is structural — a deep coincidence is not0.3ms
a genuinely uneven distribution carries no note it did not earn0.2ms
returns empty rather than inventing buckets on an uncaptured run0.2ms
merge-compare.vitest.ts
23/23 127ms · 6 suites PASS
src/seo/merge-compare.vitest.ts
merged_totals reads the source comparison, never a second summation · 5 tests
reports the decline the source measured3.4ms
never disagrees with compare.gsc.totals — they are now the same object0.4ms
is null when a source ran no comparison — not a zero prior0.4ms
takes sessions and revenue from GA4 deltas0.5ms
survives a GA4 block whose deltas are null (empty window)0.5ms
an empty analytics window is not a window of zeros · 5 tests
suppresses the deltas and names the reason when the current period has no rows0.8ms
does not report -100% for a period Analytics never measured0.6ms
does NOT claim a comparison when both periods are empty — nothing was measured0.4ms
still compares when the current period has rows0.5ms
reports both row counts so a reader can check the basis0.4ms
the model timeout scales with what the model was asked to read · 5 tests
leaves a small turn on the old fixed budget0.4ms
gives the 105K-token turn enough budget to have finished2.2ms
caps a pathological prompt rather than holding the turn open86.0ms
keeps the 60s floor for reasoning calls0.4ms
handles null/empty message lists0.8ms
a paid call that degrades must not degrade invisibly · 1 test
nameThemes reports its failure instead of swallowing it1.5ms
one source being silent must not read as no data at all · 6 tests
states the Search Console comparison plainly, with direction23.1ms
says Analytics is UNMEASURED and forbids generalising it to the whole period0.5ms
never claims there is no prior data when one source has it0.5ms
says so plainly when no comparison was requested0.2ms
reports an up-move with a sign0.2ms
handles Search Console having nothing to compare0.1ms
the model sees the summary, not two page rows · 1 test
orders the merge result so compare precedes rows0.9ms
overview-attribution.vitest.ts
23/23 45ms · 6 suites PASS
src/seo/overview-attribution.vitest.ts
movements joins on keywords present in BOTH windows · 3 tests
a keyword in only one window is not a movement2.4ms
excludes keywords under the impressions floor on EITHER side1.3ms
matches case-insensitively, so one casing change is not a lost query0.3ms
the arithmetic test — the template's "fake CTR crash" · 6 tests
impressions up, clicks held, position held → arithmetic0.4ms
clicks actually down is NOT arithmetic — that is a real loss0.2ms
a position that slid is a ranking story, not an answer box0.2ms
impressions barely moving is not inflation0.2ms
a CTR that did not fall is never arithmetic0.3ms
a missing position on either side refuses rather than assuming it held0.2ms
real click loss · 2 tests
is measured against the earlier reading, not an absolute0.3ms
a query with no clicks to begin with cannot have lost any0.2ms
attributionStats suppresses shares below the floor · 3 tests
reports readable=false rather than a number computed from four queries1.0ms
and readable once the floor is met0.3ms
the arithmetic share is null when nothing fell — never 0%, which reads as a finding0.2ms
Q03 legs inside the Q11 brief · 6 tests
kills the premise when clicks did not fall — there is no loss to attribute29.6ms
confirms the arithmetic fall and says it would fund the wrong work1.1ms
does NOT call it arithmetic when the clicks really went0.7ms
NEVER attributes the fall to an answer box — permanently untested0.8ms
names the confounders by name rather than hedging0.6ms
the causal hypothesis never claims two sources — that would be one system twice0.6ms
no history is a state, not a silence · 3 tests
one window says so, and says it answers itself0.6ms
a span mismatch is reported as its own cause, not as missing data0.5ms
an account with no history at all still gets the other hypotheses2.4ms
primary-source.vitest.ts
23/23 21ms · 8 suites PASS
src/seo/primary-source.vitest.ts
allCitedHosts · 2 tests
counts every citation, ours included — the denominator is ALL of them3.8ms
an errored engine contributes nothing either way1.6ms
INTERMEDIARY_KINDS excludes the sources that ARE primary · 1 test
a standards body or regulator is never a middleman1.2ms
the middleman test · 3 tests
survives when intermediaries outweigh our own pages0.8ms
is killed when our own pages carry the answer0.8ms
names the entity-card gap only when NOTHING of ours is cited0.5ms
too few citations is not a position in a source graph · 3 tests
every citation-based hypothesis is untested below the floor1.1ms
names how many there actually were0.3ms
a single source is not two — the confident branch stays out of reach0.8ms
what it deliberately does NOT answer · 3 tests
the page side is deferred to the E-E-A-T review, and says so0.6ms
it still reports the author count it saw, as corroboration only0.4ms
WHY a page was quoted is not recorded, so that stays untested0.2ms
the brief holds the contract · 4 tests
states the shares in its situation line0.4ms
says plainly when there is no run0.3ms
an ungrounded run is labelled recall0.4ms
carries the one-pager fields and a headline of its own0.8ms
Q07, Q05 and Q20 are wired · 3 tests
Q07 is built, chained, in both payload lists and rendered0.9ms
Q05 and Q20 are registered playbooks reachable from the dispatcher0.6ms
all three are routed and labelled in the turn1.2ms
a tie is not a finding, and an unclassified majority is not a source graph · 4 tests
a dead heat between the two shares is untested, not a reassuring "killed"0.8ms
refuses when most citations are domains nothing recognises1.3ms
the situation line states the unrecognised share rather than hiding it0.6ms
still answers when the gap is wide and the classification is good0.4ms
serp-spider.vitest.ts
23/23 132ms · 4 suites PASS
src/seo/serp-spider.vitest.ts
robots.txt disallow parsing · 3 tests
parses typical disallow rules for global agent *3.7ms
correctly matches URLs against disallow rules1.8ms
always probes orphans + exact_absent gaps in full; caps only bulk healthy/ambiguous2.2ms
serpdex durable queue · 13 tests
enqueues onto SERPDEX_QUEUE when the binding is present (no waitUntil)2.1ms
falls back to ctx.waitUntil when the queue is unbound54.9ms
report counts only exact_absent as gaps — a verified-indexed sitemap URL is never a gap36.1ms
report renders round-3 fixes: orphan reconciliation, dynamic actions, canonical, redirect, score note2.3ms
report surfaces the paid-tier upsell when free verification was capped1.1ms
crawl-only (no verify) report is PROVISIONAL — unconfirmed, not "0 gaps healthy"1.7ms
provisional CAPPED run tells the user to top up, not to re-run (re-run checks the same set)1.2ms
SCORING BUG (owner, 2026-08-02): a capped run that DID exact-verify some URLs must not score as if it verified all of them2.8ms
unconfirmed sitemap URLs are never submittable even alongside a confirmed gap0.8ms
paid tier hides the token top-up upsell (already unlocked)0.5ms
runSerpdexJob marks the task failed AND rethrows so the queue retries12.4ms
threads serp_features_keywords into the queued job payload0.7ms
omits serpFeaturesKeywords from the job entirely when no keywords were requested0.3ms
seo_serp_spider report — SERP-intelligence section (three states) · 4 tests
never requested, FREE tier → shows a labeled dummy-data preview with a LOCKED button0.6ms
never requested, PAID tier → same dummy preview, but the button is live (unlocked)0.5ms
requested on the free tier → locked state with a top-up CTA, no fabricated results0.4ms
populated (paid tier) → aggregate counts + per-keyword PAA/related drill-down0.7ms
fetchSerpIntelligence — SERP-features add-on · 3 tests
extracts and dedupes peopleAlsoAsk + relatedSearches per keyword (Serper.dev's confirmed response shape)1.2ms
caps at 5 keywords, silently dropping the rest — matches the schema's maxItems1.0ms
one keyword failing (provider hiccup) does not drop the others — empty findings, not a thrown batch0.8ms
contracts.vitest.ts
22/22 14ms · 5 suites PASS
src/reports/contracts.vitest.ts
report contract registry — predicate hygiene · 2 tests
every registered contract has at least one executable predicate (no empty stubs)2.7ms
an unregistered non-prose type is itself reported as a violation0.6ms
report contract predicates — fixture execution · 9 tests
entity_audit: sound fixture passes, count-mismatch + out-of-range fails0.8ms
score bounds are validated for every scored report (god_mode health + backlinks DR)0.5ms
entity_audit: the glenindia.com mislabel (partial verdict on a different-entity match) is caught0.7ms
share_of_model leg: the isOurs-phantom shape (coverage>0, no ours cited) is caught0.8ms
share_of_model leg: citing cannot exceed answered0.3ms
campaign_stats: opened emails cannot exceed sent emails1.0ms
campaign_stats: a stage bucket cannot exceed the audience it is drawn from0.5ms
campaign_stats: replies with nobody parked at "opened" is VALID, not a violation0.3ms
campaign_stats: a contact at "opened" from an earlier campaign with 0 sent is VALID0.3ms
aeo_visibility transparency contract · 3 tests
a positive score with no prompt matrix is a violation1.9ms
a positive score backed by prompts is sound0.4ms
a zero/absent score is not asserted (nothing measured yet)0.2ms
aeo_full_audit — a cited site can never be graded absent · 6 tests
a sound synthesis result passes0.4ms
catches the live defect: presence graded absent while the run cited us0.3ms
catches a citation count that exceeds the answers received (AEO-001)0.2ms
catches a synthesis run that never checked for disagreements at all0.3ms
catches a one-sided "disagreement"0.2ms
legacy artifacts (no synthesis flag, no presence) still pass0.2ms
campaign_stats and campaign_dashboard are ONE contract, not two copies · 2 tests
the two entries are the same object0.1ms
and the alias still enforces the funnel invariants0.4ms
disambiguation-rules.vitest.ts
22/22 13ms · 4 suites PASS
src/tools/disambiguation-rules.vitest.ts
the four rules that belong to no tool · 5 tests
ADVISORY vs IMPERATIVE — the planner's entire reachability3.5ms
and never by dumping a SAVED plan, which happened once0.8ms
AN EXPLICIT COUNT IS THE ANSWER — applies to every list-returning tool0.6ms
ANSWER, DO NOT RE-ASK — a specified request is not a question0.4ms
the legacy alias names, which are the MODEL'S vocabulary and not any tool's0.9ms
the seventeen that moved are on their tools · 12 tests
item (1) is on search_leads0.5ms
item (2) is on list_contacts0.6ms
item (5) is on seo_onpage_audit0.4ms
item (6) is on seo_backlink_deep_scan0.4ms
item (7) is on seo_serp_spider0.4ms
item (10) is on aeo_full_audit0.5ms
item (11) is on search_leads0.3ms
item (13) is on seo_geo_research0.2ms
item (14) is on generate_emails0.3ms
item (15) is on seo_google_merge0.2ms
item (16) is on seo_backlink_value0.2ms
item (17) is on find_competitors0.1ms
the two defects found while reading it · 2 tests
no route to full_seo_audit survives, in EITHER arrow spelling0.4ms
the stray "(17b)" that numbered two rules 17 is gone0.2ms
the duplication is gone · 3 tests
the eighteen-item routing table is not in V2_SYSTEM0.2ms
the residue is a quarter of what it replaced0.3ms
V2_SYSTEM stays under its ratchet0.2ms
backlink-value.vitest.ts
22/22 45ms · 5 suites PASS
src/seo/backlink-value.vitest.ts
source matching · 2 tests
reads the host half of GA4 source/medium3.5ms
matches subdomains in both directions0.7ms
computeBacklinkValue — measured, never appraised · 12 tests
uses GA4 revenue when there is revenue22.1ms
prices traffic at the tenant's OWN value per session, not a benchmark1.0ms
refuses to price traffic at all when the property records no revenue1.3ms
values a domain ONCE however many links it has0.8ms
sums a host split across mediums instead of counting it twice0.7ms
distinguishes "sent nothing" from "we could not look"0.6ms
flags value landing on dead pages as recoverable1.6ms
does not call a domain wasted when only SOME of its targets are dead0.6ms
applies NO nofollow or hosting discount — those are rules about crawlers, not visitors2.0ms
ignores (direct) and (not set), which are not referring domains0.3ms
leads with concentration when a few domains carry everything0.5ms
says so plainly when there is nothing on file0.2ms
why the property guard has to exist upstream · 1 test
matches purely on source host, with no property awareness1.7ms
the connected property parses to a host in both GSC forms · 3 tests
reduces a domain property to its host3.5ms
reduces a URL-prefix property to the same host0.4ms
agrees with normDomain on a bare host, so the guard is stable across all three0.2ms
a report must not contradict its own columns · 4 tests
calls sessions-without-revenue unpriced, never "no traffic"0.5ms
says visits ARRIVED when they did, even with nothing to price them by0.5ms
still says nothing arrived when nothing did0.3ms
carries DR, follow-state and anchors through for the profile section0.8ms
engine-divergence.vitest.ts
22/22 28ms · 6 suites PASS
src/seo/engine-divergence.vitest.ts
the overlap measure itself · 3 tests
is Jaccard — shared over union, not shared over either side3.0ms
two engines that retrieved NOTHING share nothing — never 100%0.7ms
hostOf strips www and drops anything unparseable rather than guessing0.9ms
the real run: engines are not reading the same web · 6 tests
corpus_disjoint survives, and quotes the CLOSEST pair not the average0.5ms
names two engine FAMILIES as its sources — the engines are the independent systems1.5ms
counts the prompts no engine cited at all, without folding them into a cause0.4ms
bot_access is RULED OUT on a site whose checks all pass — not left untested1.0ms
third_party_cited reads owned citations against third-party ones0.5ms
a www-prefixed owned URL still counts as ours0.3ms
bot access is the one cause with a genuinely separate second reading · 2 tests
a blocked crawler makes it survive, and it is named1.1ms
with no crawl on file it is untested, not silently ruled out1.1ms
what it refuses to say · 5 tests
the ranking hypothesis is permanently untested, with the reason0.9ms
an UNGROUNDED run says the engines answered from memory, not that nothing ran0.8ms
no run at all is a different sentence again0.5ms
the ask names the ranking blind spot when the join is absent1.9ms
claims no traffic or revenue consequence it has not observed0.6ms
the Google-grounded / chat split · 2 tests
survives only on a real gap, and names both sides with their rates2.0ms
is ruled out when the two families are close — no separate programmes on this evidence0.5ms
the decision when several findings hold at once · 4 tests
does not tell the reader to run what it just read0.6ms
says they are separate plays, and names them5.3ms
access still takes precedence INSIDE the multi-survivor case1.3ms
a SINGLE survivor still gets its own actFirst line, not the plan wording0.9ms
keyword-map.vitest.ts
22/22 45ms · 7 suites PASS
src/seo/keyword-map.vitest.ts
Q03: the revenue gate is named, never quietly dropped · 5 tests
keeps the revenue hypothesis permanently untested27.7ms
says volume alone is not a reason to target1.1ms
calls the ordering a map of what is reachable, not what is worth reaching0.4ms
asks for the one figure that would close the gate0.5ms
still asks for sign-off when every term landed in one bucket3.1ms
Q03: the kill list is produced and counted · 3 tests
kills terms with neither volume nor a single impression1.3ms
names the kill list in the ask, because the template asks for it to be signed off0.5ms
says plainly why a killed term is on the list at all0.6ms
Q03: the map separates the four kinds of work · 4 tests
parks terms already at the top as no-compete0.7ms
sends striking-distance terms to consolidate, not to a new page0.6ms
targets measured demand with no page behind it0.7ms
ranks improvement above production in the decision0.4ms
Q03: the unevidenced share is surfaced, not hidden · 2 tests
warns when most of the list carries no demand figure0.9ms
does not warn when the list is well evidenced0.5ms
Q03: brand detection is conservative on purpose · 5 tests
matches the domain label0.4ms
does not match an unrelated commercial term0.4ms
matches the brand when punctuation surrounds it0.7ms
still does not match a different word that merely contains the brand0.4ms
refuses to guess from a very short domain label0.3ms
Q03: never looked is not nothing found (GS-004) · 2 tests
separates "nothing tracked" from "tracked but never measured"0.7ms
leaves the page-type hypothesis untested — positions are stored, page types are not0.4ms
Q03: no internal vocabulary reaches the user (GS-005) · 1 test
keeps table and field names out of the prose0.8ms
recovery-generative-eligibility.vitest.ts
22/22 49ms · 4 suites PASS
src/seo/recovery-generative-eligibility.vitest.ts
Q06: recovery is subtraction · 7 tests
leads with do not publish more of the same4.3ms
identifies the template that moved rather than the whole domain1.3ms
will not report a percentage fall from a template that had nothing to fall from0.4ms
needs a real early baseline before calling a fall0.5ms
refuses to compare a window too short to split1.6ms
rules out the measurement BEFORE any programme when analytics is silent1.1ms
keeps the manual action unknown and says it decides the answer0.6ms
Q11: not all of search, and never a share from a tiny sample · 8 tests
classifies seen-and-not-clicked as overview-prone21.9ms
classifies a healthy first-page query as classic1.0ms
refuses to classify below the sample floor0.6ms
says leave the stable ones alone, in as many words2.8ms
never states a share of voice from a tiny sample0.7ms
excludes branded queries from the classification0.9ms
never claims to have seen the results page1.5ms
uses the SAME thresholds as the measurement contract0.4ms
Q21: titles before markup · 6 tests
leads with the cheaper fix1.4ms
finds pages ranking well and barely clicked2.1ms
never calls found markup VALID or eligible0.9ms
never claims to know which features are winnable0.9ms
warns about retired features and the 28-day window0.5ms
treats absent markup as an eligibility gap, not a ranking cause0.6ms
all three keep internal vocabulary out (GS-005) · 1 test
no field or table names in the prose1.6ms
feedback-rca.vitest.ts
21/21 12ms · 5 suites PASS
src/admin/feedback-rca.vitest.ts
feedback-rca — retry predicate · 5 tests
retries on a request timeout (AbortError)2.6ms
retries on a length-truncated empty/short reasoning reply0.7ms
does NOT retry (propagates) a genuine error0.6ms
retries on an OpenRouter 400/404 (unknown or delisted model id) — 2026-07-21 model swap safety net0.5ms
does NOT retry 401/403 even though they are 4xx — same key would fail identically on the fallback0.3ms
feedback-rca — repo file targeting · 4 tests
ranks the file that actually threw above one that merely shares a word with the operation1.2ms
a directory or basename match beats an incidental substring anywhere in the path0.5ms
drops generic verb prefixes from a culprit so every handler does not match everything0.9ms
still works with no culprits at all (telemetry-only run)0.5ms
feedback-rca — F6 dynamic file budget · 3 tests
floors at the original static budget for a narrow window0.3ms
scales up for a window implicating many operations0.2ms
caps at double the static budget so the prompt stays bounded0.2ms
feedback-rca — F4 tenant identity · 4 tests
tags rows whose user_id is a known internal/eval account1.0ms
does not tag a real customer row0.2ms
treats a missing user_id as NOT internal rather than throwing0.2ms
preserves every other field on the row untouched0.3ms
verifyRcaPaths — a cited path that does not exist must say so · 5 tests
flags the exact fabricated path from the 2026-08-17 report0.8ms
stays silent when every cited path resolves0.2ms
is silent on a report that cites no source paths at all0.2ms
strips trailing punctuation before deciding a path is unknown0.2ms
lists each unknown path once, however often it is cited0.2ms
state.vitest.ts
21/21 14ms · 6 suites PASS
src/outbound-run/state.vitest.ts
bounced_hard vs bounced_soft — the defect this module must not reintroduce · 5 tests
bounced_hard is terminal3.2ms
bounced_soft is NOT terminal on its own — a full mailbox is not a dead address0.8ms
bounced_soft blocks the next send only once it hits SOFT_BOUNCE_TOLERANCE0.5ms
a terminal state blocks regardless of soft-bounce count0.4ms
bounced_soft can transition back to send_queued (retryable); bounced_hard cannot transition anywhere0.5ms
approval expiry is derived from day close, never independent — §14 q2 · 3 tests
derives expiry as exactly the day close time0.3ms
an active approval past its expiry is expired1.0ms
a revoked approval is expired regardless of the clock0.3ms
digest margin is checkable, not a hope — §3 · 3 tests
rejects a digest fired after close (the failure mode the doc names)0.4ms
rejects a digest inside the minimum margin0.5ms
accepts a digest at or before the minimum margin0.3ms
dedupe key is run-scoped, not day-scoped — the fix for invariant 1 · 2 tests
the same contact on two different days of the same run gets the SAME key0.2ms
the same contact in a different run gets a different key0.9ms
idempotency key: revision-independent for paid non-send stages — §9.4 · 3 tests
enrich/verify keys are identical across a revision bump0.4ms
draft keys DO vary by revision — a new revision is a genuinely new artifact0.3ms
draft without a revision throws rather than silently omitting it1.3ms
state-transition tables — a sample of the doc's §4 diagrams, not exhaustive · 5 tests
run: draft cannot jump straight to running0.3ms
run: completed and stopped are terminal0.2ms
day: awaiting_approval can close (expired unapproved) or proceed to sending0.3ms
lead: validated_risky can be excluded (not explicitly allowed) or drafted (allowed)0.2ms
lead: invalid is terminal0.2ms
diagnose-brief-render.vitest.ts
21/21 44ms · 5 suites PASS
src/reports/diagnose-brief-render.vitest.ts
no brief renders the literal "undefined" to a user · 10 tests
competitive_landscape has no undefined in its HTML23.1ms
competitor_counter_plan has no undefined in its HTML0.6ms
eeat_proof has no undefined in its HTML0.5ms
backlink_policy has no undefined in its HTML0.4ms
keyword_map has no undefined in its HTML0.5ms
directive_policy has no undefined in its HTML1.7ms
measurement_contract has no undefined in its HTML0.7ms
ai_content_policy has no undefined in its HTML0.4ms
investment_horizon has no undefined in its HTML0.9ms
migration_runbook has no undefined in its HTML0.4ms
the Measured block belongs to the index-health brief alone · 2 tests
does not appear on briefs whose counts have different keys6.8ms
DOES appear for index health, so the guard did not simply delete the block1.0ms
each brief is published under its OWN headline · 2 tests
the counter-plan names the rival, not a content-pruning list0.5ms
every brief supplies a non-empty headline naming the site0.4ms
playbooks render through their own block, with the anchor visible · 5 tests
renders rules with their trigger, signal and owner0.7ms
renders the ANCHOR — a playbook without it reads as tailored when it is not0.3ms
renders the REASON when there is no anchor, rather than nothing2.6ms
renders decisions WITH their options — an option list is the point0.4ms
does NOT print an empty "Causes tested" heading0.5ms
a specific brief outranks the generic incident brief · 2 tests
renders the migration runbook, not the incident brief beside it0.4ms
still renders the incident brief when it is the only one0.3ms
user-scores.vitest.ts
21/21 14ms · 7 suites PASS
src/scoring/user-scores.vitest.ts
recencyDecay · 3 tests
is 1.0 for today and ~0.5 at the 7-day half-life2.5ms
decays smoothly, unlike 1/(d+1) which cliff-drops day 0→10.2ms
handles junk input0.3ms
smoothedRate · 4 tests
does not give a 1-for-1 user a perfect score (the whitepaper bug)0.4ms
converges to the true rate with volume0.2ms
is the 0.5 prior when there were no attempts0.2ms
clamps successes above attempts (defensive)0.2ms
consistencyScore · 3 tests
needs at least 3 active days0.3ms
is 1 for perfectly regular usage0.3ms
is lower for erratic usage than regular usage0.3ms
percentileRank · 3 tests
is outlier-robust where min-max is not0.4ms
ranks within 0..100 with mean-rank ties0.2ms
handles empty reference and NaN values1.2ms
shrinkToPrior · 3 tests
regresses to the prior with no evidence0.4ms
weights evidence at 50% at n00.2ms
approaches the observed value with lots of evidence0.2ms
assignTier · 1 test
maps the 2x2 engagement×proficiency grid0.3ms
computeBadges + badgePoints · 4 tests
gives a fresh user no badges1.2ms
awards milestone badges at thresholds2.3ms
finisher needs 5+ attempts, not a lucky 1-for-10.4ms
every badge key computeBadges can emit exists in the BADGES catalog0.8ms
enqueueable-tools.vitest.ts
21/21 6ms · 1 suite PASS
src/runtime/enqueueable-tools.vitest.ts
isEnqueueableTool · 21 tests
allows the declared background tool aeo_full_audit2.5ms
allows the declared background tool aeo_visibility0.3ms
allows the declared background tool entity_audit0.2ms
allows the declared background tool aeo_page_check0.3ms
allows the declared background tool backlink_outreach_search0.2ms
allows the declared background tool seo_backlink_verify0.1ms
allows the declared background tool seo_write_content0.2ms
allows the declared background tool seo_google_merge0.2ms
allows the declared background tool google_god_mode_report0.2ms
allows the declared background tool seo_onpage_audit0.2ms
allows the Tier-B safety-net tool seo_offpage_audit0.2ms
allows the Tier-B safety-net tool seo_competitor_gap0.1ms
allows search_leads, which runs its own durable delivery path0.2ms
refuses the action tool send_emails0.2ms
refuses the action tool enroll_in_sequence0.2ms
refuses the action tool set_campaign_sequence0.1ms
refuses the action tool resume_campaign0.1ms
refuses the action tool launch_campaign0.1ms
refuses the action tool cloudflare_fix_email_dns0.1ms
refuses the action tool connect_connector0.1ms
refuses an unknown or forged tool name0.5ms
audits-wave4.vitest.ts
21/21 10ms · 4 suites PASS
src/tools/audits-wave4.vitest.ts
the audit cluster · 12 tests
seo_onpage_audit accepts the empty call — "my site" is the common case3.4ms
seo_offpage_audit accepts the empty call — "my site" is the common case0.4ms
seo_backlinks accepts the empty call — "my site" is the common case0.3ms
seo_serp_spider accepts the empty call — "my site" is the common case0.2ms
seo_onpage_audit accepts a named site0.9ms
seo_offpage_audit accepts a named site0.3ms
seo_backlinks accepts a named site0.3ms
seo_serp_spider accepts a named site0.3ms
seo_onpage_audit rejects the retired domain alias0.6ms
seo_offpage_audit rejects the retired domain alias0.3ms
seo_backlinks rejects the retired domain alias0.2ms
seo_serp_spider rejects the retired domain alias0.1ms
per-tool arguments · 2 tests
seo_onpage_audit bounds the crawl depth0.4ms
seo_serp_spider keeps verify_index opt-in and typed0.3ms
fallbacks go only where validation replaced them · 6 tests
seo_onpage_audit no longer reads tool.domain0.5ms
seo_offpage_audit no longer reads tool.domain0.2ms
seo_backlinks no longer reads tool.domain0.2ms
seo_serp_spider no longer reads tool.domain0.1ms
still-legacy full_seo_audit KEEPS its fallback0.2ms
still-legacy share_of_model KEEPS its fallback0.3ms
the contract-seams parser does not depend on formatting · 1 test
no longer slices the properties block by indentation0.4ms
backlink-tier-value.vitest.ts
21/21 32ms · 3 suites PASS
src/seo/backlink-tier-value.vitest.ts
computeLinkValues — the backlink report headline · 17 tests
prices a dofollow link off the DR band it falls in3.5ms
bands on the FLOOR, so a DR just under a threshold does not take the tier above0.7ms
prices nofollow off its own much lower ladder, not as a discount on dofollow0.6ms
prices an unknown follow state off the dofollow ladder — the link still exists0.5ms
returns a value for EVERY link, not one per domain2.3ms
decays repeat links from ONE domain — the trust is the domain's, not the URL's0.4ms
does not decay across DIFFERENT domains — each starts at full value0.2ms
ranks the dofollow link first, so the decay lands on its nofollow sibling0.2ms
prices a dead target at zero — it buys nothing until redirected18.8ms
prices search engines and aggregators at zero rather than at their enormous DR0.5ms
counts an unrated link rather than silently pricing it at the bottom band0.4ms
never lets the gross carry a hosting haircut — the discounts sit beside it0.5ms
halves independent and CDN-fronted links in the realistic figures0.4ms
counts an unresolved host as zero in BOTH realistic figures, never as independent0.2ms
states the range and the haircut in the note that travels with the number0.6ms
sorts links by what they are worth, so the report leads with the ones that matter0.2ms
takes NO traffic input at all0.7ms
registrableRoot / isSubdomain · 3 tests
identifies a subdomain whose DR would be its parent's0.6ms
treats a bare root as its own root0.2ms
keeps multi-part suffixes intact0.2ms
a subdomain carries its parent's rating, and the hosting haircut is what answers it · 1 test
prices a shared-hosting subdomain at a twentieth once its cluster is known0.2ms
competitor-counter-plan.vitest.ts
21/21 43ms · 7 suites PASS
src/seo/competitor-counter-plan.vitest.ts
Q25: "consistently" means more than once · 5 tests
one term ahead is a coincidence, not a dominant rival3.7ms
two terms ahead is a pattern0.9ms
a rival BEHIND us on a term is not counted as holding it0.5ms
a term we do not rank for at all counts as theirs0.4ms
picks the rival ahead on the most terms0.3ms
Q25: the plan is capacity-bound and the cut is visible · 4 tests
caps the plays24.1ms
COUNTS what it left out rather than dropping it silently (GS-004)1.2ms
hands the capacity call to the user instead of inventing a team size0.7ms
ranks by what the term already earns2.2ms
Q25: work we cannot win is never funded · 3 tests
excludes terms topped by a platform result from the plays1.5ms
warns against funding them in the decision when other work exists0.5ms
reports a real STOP rather than manufacturing a play0.5ms
Q25: what we cannot see stays unseen · 3 tests
never concludes anything about the rival's authority0.5ms
never kills the cannibalisation hypothesis, because no URL is recorded0.3ms
distinguishes "no rank readings" from "nobody is ahead"1.4ms
Q25: speculative work is labelled and goes last · 2 tests
groups zero-demand terms into one play at the end0.7ms
does not invent a speculative play from a single term0.3ms
Q25: no internal vocabulary reaches the user (GS-005) · 1 test
keeps table and field names out of the prose0.5ms
Q25: a cause the evidence contradicts is RULED OUT, not "not tested" · 3 tests
kills the absence hypothesis when we rank for everything they hold0.4ms
does not report a fully-read site as entirely untested0.6ms
when nothing was scanned, every thin reason says so — never "we rank somewhere"0.7ms
domain-extract.vitest.ts
21/21 13ms · 3 suites PASS
src/seo/domain-extract.vitest.ts
extractExplicitDomain · 8 tests
recovers the domain from the prompts that misrouted4.2ms
returns '' when the user named no domain — the saved-site fallback must still win0.9ms
ignores domains inside email addresses — a recipient is never an audit target0.3ms
still finds a real target alongside an email address0.2ms
normalises case and strips www0.2ms
takes the first domain when several are named0.3ms
does not match ordinary prose that merely contains a dot0.6ms
handles subdomains and hyphenated hosts0.3ms
pickContextMessage · 10 tests
walks back past a depth chip to the turn that named the domain1.1ms
walks back past the other chip/confirm tokens0.7ms
skips consecutive control tokens to the newest real message0.5ms
leaves an ordinary message untouched0.3ms
falls back to the control token when no real prior turn exists0.2ms
takes the NEWEST real message, not the oldest0.4ms
walks back past the raw route chip the UI posts0.2ms
walks back past the "SEO route: <choice>" confirm form0.1ms
does NOT treat a typed request as a chip — the em-dash is the tell0.3ms
route chip then depth chip still recovers the request two turns back0.2ms
god-mode style asks resolve a real domain or nothing at all · 3 tests
recovers the domain when one is actually named0.2ms
returns nothing for a phrase that names no domain0.3ms
never reads a recipient address as an audit target0.2ms
keyword-themes.vitest.ts
21/21 236ms · 5 suites PASS
src/seo/keyword-themes.vitest.ts
buildThemes — groups and sizes, never judges · 7 tests
groups the registry and sums volume per theme2.9ms
sorts by VOLUME, so the biggest market leads even with few members0.6ms
keeps a one-keyword theme when it is the biggest market present0.5ms
carries NO verdict field — relevance is the one call nqzai must not make1.2ms
counts members with no volume rather than treating them as zero0.3ms
falls back to the strongest keyword as the name, never an invented one1.9ms
returns an empty summary rather than throwing on no keywords1.1ms
nameThemes — a label on measured data, or nothing · 2 tests
keeps the deterministic names when the model call fails154.7ms
never changes the volumes, whatever the naming does41.0ms
themeNote — describes, hands the judgement back · 4 tests
states the largest theme, its share, and whose call relevance is22.5ms
never calls a theme off-brand, noise, or a distraction1.0ms
says the sizes are a floor when volume is missing somewhere0.6ms
says nothing when there are no themes0.3ms
themeNamingBudget — NQZAI-8H regression · 5 tests
never returns the 208 that failed four times, at the theme count that produced it0.7ms
leaves the maximum theme count below its own ceiling0.4ms
scales with the fan-in rather than returning a constant0.3ms
keeps real headroom per line, not a fitted estimate0.3ms
still bounds an absurd fan-in — derived, not unbounded0.3ms
theme names are SELECTED from the cluster, not generated (2026-09-20) · 3 tests
candidates are the shared phrases first, then the keywords themselves, capped and title-cased1.4ms
one choice per theme over its own candidates, with a none option, judging the name only1.1ms
the select path runs before the generative one and is gated on its flag word1.1ms
publish-detect.vitest.ts
21/21 57ms · 6 suites PASS
src/seo/publish-detect.vitest.ts
slugTokens · 3 tests
keeps the identifying words and drops the scaffolding3.6ms
keeps digits — they are often the only thing separating two articles1.8ms
strips the host and the file extension from a URL1.1ms
detectPublished — what it claims · 2 tests
matches a real generated title to a real blog URL2.0ms
a URL carrying extra path segments still matches — containment is one-directional0.6ms
detectPublished — what it refuses · 7 tests
two drafts on one topic claim NOTHING0.5ms
one draft matching two pages claims NOTHING0.4ms
a page that has not changed since before we wrote is not ours0.4ms
a page edited AFTER we wrote stays a candidate0.7ms
a thin title cannot claim a page — it would claim the whole niche0.6ms
a partial overlap is not a match0.4ms
an unrelated page on the same site is left alone0.2ms
collectSitemapUrls · 4 tests
follows a subdomain listed in the apex sitemap — the whole reason this is not one fetch37.3ms
never leaves the tenant's registrable domain1.2ms
a fetch failure yields no URLs rather than throwing0.5ms
returns nothing for a blank site rather than fetching a bare https://0.4ms
an inference may never overwrite an observation · 2 tests
a detected write only ever fills an empty slot0.2ms
a connector write is NOT restricted the same way — re-publishing is legitimate0.2ms
the weekly pass · 3 tests
no sitemap means no inference, not a confident zero0.2ms
one tenant's unreachable sitemap does not end the pass for everyone0.2ms
runs on the existing zero-spend Monday cron, with its own monitor0.3ms
appsumo.vitest.ts
20/20 29ms · 5 suites PASS
src/billing/appsumo.vitest.ts
planIdForStackCount — pure · 3 tests
maps 1/2/3 directly3.3ms
caps at appsumo3 — plans.ts defines no level beyond it, even though AppSumo allows stacking up to 100.6ms
floors at appsumo1 for a non-positive count, defensively0.5ms
getAppsumoPlanForUser / getAppsumoStackCount · 3 tests
returns null — no entitlement — for a user with zero redemptions1.1ms
maps a real count to the right plan id0.7ms
fails to null/0 rather than throwing on a ledger read error — same posture as hasPaidTopUp0.6ms
redeemAppsumoCode — the compare-and-swap · 8 tests
happy path: claims the code, grants the flat base amount, reports the new stack level3.5ms
a SECOND code for the same user stacks — this is how stacking actually works, not a separate code SKU0.8ms
rejects an empty/whitespace code without ever calling the ledger1.3ms
the cap is checked BEFORE the CAS — a sold-out deal never touches a specific code row0.9ms
is NOT fooled by an unused margin below the cap — reaching it exactly still blocks0.4ms
reports already_redeemed when the CAS loses to a concurrent (or earlier) redemption of the SAME code0.9ms
reports invalid_code when the code has never existed at all0.5ms
reads a still-unissued code after a failed claim as cap_reached, not invalid_code0.6ms
redeemAppsumoCode — side effects on the happy path · 4 tests
drops the cached balance so the buyer sees their tokens immediately1.6ms
fires the redemption event with the resolved level, not a hardcoded one7.1ms
marks the person as appsumo but NOT as paying — they paid AppSumo, not us2.0ms
still succeeds when telemetry throws0.6ms
constants match the locked doc (docs/APPSUMO_LTD_PRICING.md §4b) · 2 tests
base grant is the ~70%-off-at-$49 figure, not the earlier 25M cost-safety guess0.3ms
code cap and max stack level match the locked ladder0.3ms
estimate-tolerance.vitest.ts
20/20 15ms · 5 suites PASS
src/billing/estimate-tolerance.vitest.ts
three dials, three questions · 3 tests
the entry buffer is 25K — lowered so small balances stay usable (owner 2026-09-01)2.8ms
the overrun alarm is NOT dragged down with the buffer1.0ms
is NOT aliased to the cost-card threshold1.5ms
ENTRY — the gate DEMANDS a buffer above the reservation · 5 tests
a run that would land ABOVE zero after an optimistic estimate is admitted0.3ms
a run that could land BELOW zero is refused — this is the whole point0.4ms
SMALL BALANCES STAY USABLE — the reason 100K was lowered to 25K0.5ms
the gate demands the buffer rather than permitting an overdraft1.3ms
the downsize suggestion respects the buffer too0.2ms
EXIT — an estimate missing a leg reports itself · 7 tests
the seo_serp_spider case fires0.7ms
an ordinary approximation error does NOT fire0.5ms
a run that comes in UNDER its reservation is silent0.3ms
is wired at teardown and attributes to the tool that ran0.7ms
measures THIS run, not the whole turn0.9ms
clears the pair only for the run that OWNS the window0.7ms
the alarm itself only fires for the owning run0.5ms
the buffer bounds approximation, never bugs — stated so it is not mistaken for a cap · 3 tests
the spider overrun dwarfs the buffer, so the buffer alone could never have stopped it0.2ms
the code says so where someone would otherwise assume protection0.6ms
the two dials are on different axes and documented as such0.6ms
a failed ledger read is UNKNOWN, never BROKE · 2 tests
strict mode rethrows instead of reporting an empty account0.5ms
the swallow-everything catch is gone0.3ms
consent-gate.vitest.ts
20/20 26ms · 4 suites PASS
src/email/consent-gate.vitest.ts
consent gate — the unsubscribe complaint · 8 tests
blocks a marketing kind for a suppressed user13.8ms
inactive_3_day_reminder is marketing and honours the opt-out0.8ms
unfinished_setup_nudge is marketing and honours the opt-out0.4ms
weekly_product_progress_digest is marketing and honours the opt-out0.4ms
sov_weekly_digest is marketing and honours the opt-out0.5ms
seo_rank_digest is marketing and honours the opt-out0.3ms
commerce_weekly_digest is marketing and honours the opt-out0.4ms
still delivers operational mail to a suppressed user1.4ms
consent gate — the account-deletion complaint · 3 tests
blocks marketing to a deleted account0.4ms
blocks OPERATIONAL mail to a deleted account too0.4ms
allows the deletion confirmation itself through the deleted block3.8ms
consent gate — failure and bypass behaviour · 7 tests
fails CLOSED when the consent lookup errors0.6ms
records a feature event when it blocks, with the reason0.4ms
leaves no delivery row behind when it blocks0.3ms
refuses marketing with no userId0.2ms
allows the declared operator test send0.3ms
treats an unregistered kind as marketing (fail-closed default)0.8ms
keeps the operator report kinds operational so an owner opt-out cannot mute them0.2ms
footer — the promise must match the enforcement · 2 tests
puts the unsubscribe link on marketing mail0.3ms
omits it from operational mail and says why instead0.3ms
first-turn-shape.vitest.ts
20/20 10ms · 3 suites PASS
src/chat/first-turn-shape.vitest.ts
a bare domain is an offer — the turn onboarding was built for · 8 tests
drashti@homhub.ai: https://nuxt.homhub.ai2.8ms
rajputsudheer8127: https://creditcarddotcom.netlify.app/0.6ms
iamahacker.unofficial: https://www.vidtwo.com/0.3ms
mganwerbaig: Chipperstreeservice.net0.4ms
janavijadhav504: https://www.ycis.ac.in/0.2ms
balajistoneexports: "https://udaipurcabservice.com/" that is my0.2ms
framing words around a domain are still an offer1.0ms
naming onboarding itself is an offer, whatever verb carries it0.3ms
a URL the sentence asks us to WORK on is a task · 8 tests
chandramedia2221: Read https://vercel.com/blog and list the headings0.3ms
hards2000: Run an SEO audit for https://prakashinfotech.com/0.2ms
vibinchitradevi: https://www.easc-cs-cybersecurity.com/ do site au0.1ms
khanmeha938: keywords for seo websire is https://www.dminternatio0.1ms
samridhibhatia014: How can I improve this website what score you will g1.0ms
talkeriq: run a SEO,AEO & GEO review on https://talkeriq.com/0.3ms
promisesociety5: Analyze how AI answer engines discover, describe and0.1ms
phrasings nobody has written yet still classify as tasks0.4ms
the failure directions are not symmetric, so the default is task · 4 tests
a long remainder is a task even when every word looks harmless0.2ms
no URL at all is a task — there is nothing to onboard0.2ms
is not stateful — URL_RE is /g, and .test() on a global regex advances lastIndex0.4ms
an offer misread as a task loses nothing; the reverse loses the account0.2ms
misroute-standdown.vitest.ts
20/20 48ms · 4 suites PASS
src/chat/misroute-standdown.vitest.ts
whyNoMisroute names every stand-down · 3 tests
agrees with resolveMisroute on when the guard fires18.2ms
distinguishes the four remaining reasons9.1ms
is recorded on the turn, both branches5.9ms
keyword economics survive the guardrail · 3 tests
allows currency for every tool that emits CPC0.8ms
still redacts currency for a tool with no tenant money in it0.3ms
grants the exemption from ANY tool in the turn, not just the last4.6ms
augment: the evidence step happens even when nothing costs money · 7 tests
fires in augment mode for a free tool, dropping nothing2.1ms
still REPLACES a gated tool — augment did not weaken the original guard0.6ms
replaces only the gated tool and keeps the free one0.5ms
stays silent when the model already chose diagnose0.3ms
stays silent for a non-diagnostic question0.3ms
stays silent when the user also asked for an action0.2ms
stays silent once diagnose has already run this turn0.2ms
every declared answer-call predicate reaches the diagnose dispatch condition · 7 tests
has the dispatch block to test against (not a stale anchor)0.4ms
every `const isQ*/isAi*` alias declared for dispatch is used in the OR-condition1.6ms
isAi01 specifically dispatches0.4ms
isAi13 specifically dispatches0.4ms
isAi22 specifically dispatches0.4ms
isAi24 specifically dispatches0.3ms
isAi04 specifically dispatches0.3ms
records-as-tables.vitest.ts
20/20 47ms · 6 suites PASS
src/chat/records-as-tables.vitest.ts
mdTable · 3 tests
emits a header, a separator and one row per record3.9ms
returns nothing for no rows, so callers do not print an empty header0.5ms
escapes pipes and newlines that would otherwise split the row1.4ms
records render as tables, everywhere they are listed · 7 tests
contacts7.5ms
leads found by a search — as a BLOCK, and every lead, not the first five2.0ms
campaigns0.8ms
sequences0.6ms
email drafts — as a block, every recipient, not three of them1.2ms
backlink outreach prospects — as a block1.0ms
no stacked blank lines where a table was removed0.9ms
SEO report bodies list records as tables · 5 tests
on-page audit — issues carry a count and a share, weak pages carry a fault list0.7ms
on-page audit — a broken page outranks a numerous one in the next moves0.4ms
off-page audit — anchors and ranking keywords are records, not bullets19.0ms
full audit — leads with the constraint and tables the join, never "N/2 sections"0.9ms
full audit — a blocked half states the consequence, never the raw error0.4ms
prose stays prose · 1 test
a one-column list is still a list — a table would be worse0.4ms
the model is told the same rule the formatters follow · 1 test
V2_SYSTEM tells the model the records are already tabulated, and never to draw one0.6ms
full_seo_audit — every named gap has a way through · 3 tests
offers the connector for the missing source that unlocks the most2.4ms
falls back to the ordinary next steps when nothing is missing0.5ms
picks the gaps that are actually absent, not a fixed list0.4ms
sov-insight.vitest.ts
20/20 20ms · 5 suites PASS
src/chat/sov-insight.vitest.ts
sovTrendInsight — a card may only claim a movement it can support · 4 tests
states the movement first, then the standing4.3ms
marks which metric is the tenant, and gives the rival NO delta3.5ms
picks the LEADING rival, not the first in the list0.4ms
appends the displacement sentence when the tool produced one0.5ms
sovTrendInsight — one movement, one number · 4 tests
takes the tool delta rather than computing a second opinion0.7ms
claims no movement at all when the tool supplies none0.4ms
explains the absent comparison instead of qualifying a narrowed window0.5ms
carries the break through to the series so the chart can break the line0.5ms
sovTrendInsight — an overtake between two parties with no share is not an event · 2 tests
drops the displacement sentence when nobody is being cited0.7ms
keeps it when someone actually holds share0.4ms
sovTrendInsight — refuses rather than half-claims · 5 tests
returns null on a single measurement — a trend through one point is a snapshot0.5ms
returns null with no series at all0.4ms
drops malformed points instead of charting NaN0.6ms
says "unchanged" rather than "up 0 points"0.2ms
survives a result with no entities, falling back to the domain4.0ms
sovTrendInsight — the sample behind the movement · 5 tests
THE REGRESSION: does not assert a 100-point move off two single answers0.5ms
qualifies on the THINNER end of the delta, not just the latest run0.2ms
carries the set-change caveat AND the thin-sample one when both apply0.2ms
states the basis but no caveat when the sample carries its own weight0.3ms
says nothing about the sample when the payload predates the field0.2ms
aeo-family-rules.vitest.ts
20/20 15ms · 5 suites PASS
src/tools/aeo-family-rules.vitest.ts
the seven rules that were NOT already on their tool · 8 tests
MOVE 1 — readiness is not visibility, stated in the direction that FAILS3.7ms
MOVE 2 — aeo_visibility names the surfaces users ask for by name1.2ms
MOVE 3 — sov_trend: only `delta` is a movement0.6ms
MOVE 4 — sov_trend: relay comparability_note when delta is null0.5ms
MOVE 5 — sov_trend: latest_basis names the surfaces0.4ms
and the incident those three exist for is recorded where the rule lives0.5ms
MOVE 6 — aeo_full_audit makes no measurements of its own0.5ms
MOVE 7 — tap_volume states its cost and refuses speculative calls0.9ms
the llms.txt discriminator survived, via the tool it gets confused with · 2 tests
aeo_page_check says it scores rather than generates, and names the generator1.0ms
and states that generator is free, so cost is not inferred from the family0.4ms
aeo_full_audit no longer contradicts itself about its own price · 3 tests
the stale "three paid legs" claim is gone0.9ms
but the routing half of that rule survives0.2ms
and it says what to do when nothing has been measured0.4ms
the family stays discoverable, because every tool in it is deferred · 5 tests
no AEO tool is in CORE, so the pointer is the only always-on signal0.5ms
the pointer separates this family from SEO in one line0.3ms
it names each distinct question, in the user's words0.5ms
it warns that readiness and visibility sound alike0.3ms
and that one tool is very expensive0.3ms
the duplication is gone · 2 tests
the per-bullet routing table is not in V2_SYSTEM0.2ms
V2_SYSTEM stays under its ratchet0.3ms
competitor-annotation.vitest.ts
20/20 16ms · 4 suites PASS
src/seo/competitor-annotation.vitest.ts
annotateStoredCompetitors — the live divergence · 4 tests
returns every stored entry, so nothing is dropped on the round trip3.9ms
marks exactly the three the engine screens out1.7ms
names the reason in the user's language, not the filter's0.9ms
a counted entry carries no reason — an explanation implies a problem0.3ms
the annotation is DERIVED from the engine, never a second copy of the rule · 7 tests
case 0: counted set === orderStoredCompetitors output1.1ms
case 1: counted set === orderStoredCompetitors output0.5ms
case 2: counted set === orderStoredCompetitors output0.3ms
case 3: counted set === orderStoredCompetitors output0.2ms
case 4: counted set === orderStoredCompetitors output0.2ms
case 5: counted set === orderStoredCompetitors output0.2ms
case 6: counted set === orderStoredCompetitors output0.2ms
annotateStoredCompetitors — the other ways an entry falls out · 5 tests
never screens an entry the user typed1.4ms
explains a duplicate as a duplicate, not as a screened host0.8ms
explains the cap as the cap0.8ms
screens a platform and a directory with their own reasons0.3ms
does not throw on a malformed or absent row0.3ms
GET /api/product/brief serves the annotated set · 4 tests
is a real slice of the route0.6ms
uses the shared helper0.2ms
no longer parses the competitor set by hand0.2ms
the write path still stores the COMPLETE set, screened or not0.9ms
content-assessment.vitest.ts
20/20 23ms · 5 suites PASS
src/seo/content-assessment.vitest.ts
countSourcing — testing the rule, not prompting it · 5 tests
counts a percentage claim and notices it has no source3.3ms
a claim with a link in the same sentence is sourced1.2ms
a heading is a title, not a statistic0.3ms
a small bare number is not a claim0.5ms
recognises all three claim shapes0.4ms
assessContent — corrective findings rest on two measured facts · 5 tests
flags writing a second page for a term we already rank for1.1ms
does NOT flag a term we rank badly for — there is no signal to split0.6ms
flags a prior draft for the same keyword, and says whether it went live6.5ms
flags unattributed claims only when they dominate1.4ms
says nothing about sourcing on a draft with too few claims to judge1.5ms
assessContent — suggestive and advisory · 4 tests
says when nothing will measure the term we just wrote for0.5ms
reports the publish rate only once it is a pattern1.7ms
stays quiet when most drafts did go live0.3ms
a clean draft produces no findings at all0.2ms
assessmentLead · 3 tests
leads with the count of blockers and names the first0.4ms
says nothing is blocking when only softer findings exist0.2ms
returns null on a clean draft rather than inventing a concern0.2ms
the rendered report leads with the judgement · 3 tests
replaces the prose-about-our-own-output lead0.5ms
groups by directive and only shows groups that have something0.2ms
an artifact stored before the assessment existed still renders its old lead1.0ms
content-inventory.vitest.ts
20/20 55ms · 7 suites PASS
src/seo/content-inventory.vitest.ts
KILL is the only irreversible label, so it carries the strictest test · 4 tests
needs BOTH readings to agree — no earnings and no links4.1ms
is WITHHELD ENTIRELY when the link index has never been read2.0ms
a page with links is never killed, however little it earns0.5ms
says so in the situation line when nothing can be killed17.9ms
the four buckets follow the template rule, in its order · 3 tests
earning beats everything — a page with clicks is KEEP even if the crawl calls it thin0.6ms
impressions without clicks is IMPROVE — the intent exists and the page is losing it0.5ms
thin AND drawing impressions is COMBINE — there is demand, wrong page answering it0.4ms
the redirect map · 4 tests
sends everything that moves to the page that actually EARNS1.1ms
never redirects a page to itself0.4ms
leaves redirect_to null when there is no page worth redirecting into3.2ms
the leak hypothesis is killed when everything moving has a destination6.3ms
the judgement we refuse to make · 1 test
toxic links is ALWAYS untested — we count links, we do not judge them1.3ms
honesty about the population · 4 tests
never implies the labelled set is the whole site6.0ms
degrades honestly with nothing on file0.9ms
the two-source rule holds across every shape0.9ms
labels each page exactly once, even when it appears in both inputs0.3ms
no internal vocabulary reaches the reader — GS-005 · 1 test
never names a tool, a field or an issue code0.9ms
Q16 is recognised and reachable · 3 tests
recognises the question and refuses a production request2.1ms
routes from the sentence and is traceable2.9ms
the dispatch attaches it, computed before the empty-evidence gate0.7ms
diagnose-intents.vitest.ts
20/20 19ms · 5 suites PASS
src/seo/diagnose-intents.vitest.ts
isRevenueOutcomeQuestion · 8 tests
recognises: Why did my sales drop last quarter?4.1ms
recognises: revenue is down 30% this month, what happened0.8ms
recognises: orders fell off a cliff after the redesign0.3ms
recognises: why are conversions lower than in May0.3ms
leaves alone: why is my AI visibility down0.3ms
leaves alone: why am I not getting replies0.2ms
leaves alone: show my sales pipeline0.3ms
leaves alone: what is holding my site back0.2ms
isSetupCheckQuestion · 7 tests
recognises: Something feels off with my setup — can you check what I have configured and what is missing?1.2ms
recognises: what do I have set up0.6ms
recognises: am I ready to send campaigns?0.4ms
recognises: what connections are configured0.2ms
leaves alone: why is my traffic down0.3ms
leaves alone: which keywords are missing from my site0.4ms
leaves alone: check my backlinks0.2ms
revenueInsufficiencyResult · 1 test
refuses to attribute, names what is on file and what is needed, and tells the model so1.8ms
setupCheckResult · 3 tests
names what THIS tenant has and lacks, and one concrete next step1.0ms
with nothing configured, the first missing item is the site and the headline counts the gaps0.5ms
with everything configured, says so and moves to growth0.4ms
the revenue branch keys on a CONNECTED STORE with orders (source pin, 2026-09-15) · 1 test
reads commerce_shops beside commerce_orders and requires both4.2ms
extractable-passages.vitest.ts
20/20 14ms · 6 suites PASS
src/seo/extractable-passages.vitest.ts
a clean site is RULED OUT, not untestable · 6 tests
not_in_html is killed on a clean site, with both readings named3.7ms
no_boundaries is killed on a clean site, with both readings named0.9ms
no_machine_claim is killed on a clean site, with both readings named0.5ms
buried is killed on a clean site, with both readings named0.9ms
nothing_to_lift is killed on a clean site, with both readings named0.5ms
the decision counts the ruled-out causes and the untestable one SEPARATELY0.9ms
each cause survives on its own evidence and nothing else · 5 tests
not_in_html survives, and it is the ONLY survivor0.4ms
no_boundaries survives, and it is the ONLY survivor0.2ms
no_machine_claim survives, and it is the ONLY survivor0.2ms
buried survives, and it is the ONLY survivor0.3ms
nothing_to_lift survives, and it is the ONLY survivor0.2ms
`na` is not a pass · 2 tests
an na SSR check leaves the cause killed rather than surviving0.3ms
an empty status is not read as a failure either0.3ms
one reading is never enough — the two-source rule holds · 3 tests
crawl only: every cause is untested, and the ask names the review1.4ms
review only: every cause is untested, and the ask names the crawl0.5ms
neither: the ask says BOTH are needed and why0.3ms
passage shape is untested no matter what the page scores say · 2 tests
stays untested on a perfectly clean site0.5ms
the ask surfaces it, so the reader is told what nobody looked at0.5ms
the brief never claims a citation outcome it has not observed · 2 tests
says nothing about rankings, traffic or citation counts0.4ms
names no vendor0.3ms
migration-runbook.vitest.ts
20/20 30ms · 5 suites PASS
src/seo/migration-runbook.vitest.ts
Q07: the two systems are joined, not concatenated · 5 tests
merges a URL that is both linked and indexed into ONE entry5.7ms
keeps genuinely different URLs apart0.7ms
sums referring domains across several links to one target0.5ms
ranks linked URLs above indexed-only ones0.5ms
counts the three kinds separately13.1ms
Q07: the gate is a precondition, and single-hop survives · 5 tests
says do not go live, not "check afterwards"0.6ms
keeps the single-hop clause and says why a chain is the trap0.8ms
warns against redirecting everything to the homepage0.5ms
carries the ninety-day and two-week windows0.6ms
states the gate even with no list yet1.1ms
Q07: the list never claims to be complete · 4 tests
says it is a floor while per-URL click data is missing0.7ms
names exactly what it would miss0.4ms
drops the caveat if click data ever becomes available0.3ms
caps the named list but keeps the true count1.0ms
Q07: what it cannot see stays unseen · 5 tests
keeps the staging leak untested0.3ms
keeps the false-cliff hypothesis untested and asks for the one action0.2ms
flags already-dead targets as the failure in miniature0.3ms
separates "nothing read" from "nothing to lose"0.4ms
says a lost link cannot be re-earned, which is why linked URLs lead0.3ms
Q07: no internal vocabulary reaches the user (GS-005) · 1 test
keeps field and table names out of the prose0.5ms
page-conversion.vitest.ts
20/20 18ms · 5 suites PASS
src/seo/page-conversion.vitest.ts
the three states are kept apart · 4 tests
names silence as unmeasured, never as zero2.3ms
names "nothing configured to count" separately from silence0.5ms
says plainly when no reading exists at all0.7ms
a silent window produces NO conversion verdict on any page2.2ms
the triage routes the work, and twice it routes it away from search · 5 tests
low engagement is a job for whoever owns the page, not a search rewrite0.6ms
engaged but not converting means a different KIND of page, not more copy0.5ms
cannot triage when nothing is counting, and says measurement comes first0.4ms
triageFor refuses to triage an unmeasured or uncounted site0.3ms
ignores pages without enough clicks to judge0.4ms
what it will not conclude · 3 tests
never decides a query mismatch from the words1.8ms
keeps the speed hypothesis untested — no field data is stored for anyone0.5ms
reports silence as the finding rather than a failure to answer0.5ms
no internal vocabulary reaches the user (GS-005) · 2 tests
keeps field and table names out of the prose0.9ms
says "engagement" rather than "bounce", because engagement is what is on file0.9ms
foldOutcomeRows: the derivation that had no cover · 6 tests
decides the state from the ROW COUNT, never from summed zeros0.6ms
treats a missing row count as silence rather than as measurement0.2ms
sums a URL across its per-date rows2.6ms
weights engagement by sessions, not by dates0.6ms
weights position by impressions — the only average of a position that means anything0.3ms
reports whether anything is counted or earned0.3ms
vitals-render.vitest.ts
19/19 90ms · 7 suites PASS
client/vitals-render.vitest.ts
the jsdom environment is actually present · 1 test
has a document with a body10.6ms
a value we do not have · 3 tests
renders an em dash, not a zero23.4ms
renders an em dash for undefined too — `== null` must catch both4.2ms
still renders a REAL zero as "0" — the rule is about absence, not about the digit4.2ms
deltas · 4 tests
shows a down arrow and the magnitude for a negative delta5.7ms
shows an up arrow for a positive delta3.1ms
renders NO arrow for a zero delta — "▲ 0" would claim a movement that did not happen2.4ms
renders no arrow when there is no delta at all2.4ms
tone and dormancy · 2 tests
marks a warning row without marking it dormant4.0ms
renders a dormant module as one muted row rather than omitting it4.0ms
an actionable row is a way in, not a dead end · 5 tests
dispatches its command on click4.1ms
dispatches on Enter and on Space, so it is reachable from the keyboard3.1ms
sends the row's OWN command when several rows are actionable3.7ms
is not actionable, and does not throw, when no dispatcher is supplied1.0ms
survives a dispatcher that throws — a dead send must not blank the panel2.1ms
rendering is idempotent and text is not interpreted · 3 tests
replaces the previous rows rather than appending to them2.7ms
treats a label as text, never as markup1.9ms
paints nothing and does not throw on a null host or null rows1.2ms
the failure line · 1 test
is ONE honest line, not a grid of em dashes pretending to be measurements4.4ms
cost-exposure.vitest.ts
19/19 15ms · 5 suites PASS
src/billing/cost-exposure.vitest.ts
one dial · 2 tests
the threshold IS the acceptable deficit — changing it is a single edit3.0ms
never sits below the cost of asking0.6ms
every wide-ceiling tool prices the CALL, not the average · 4 tests
finds the tools whose ceiling outruns their typical0.3ms
verify_contacts (37,500 typical / 750,000 ceiling) has a per-call estimator1.2ms
seo_onpage_audit (25,000 typical / 150,000 ceiling) has a per-call estimator0.2ms
seo_keywords (72,800 typical / 95,000 ceiling) has a per-call estimator0.7ms
a tool that reserves far more than it spends is tracked, not forgotten · 3 tests
no NEW tool starts reserving a ceiling it will not spend1.0ms
a tool that gains a per-call estimator is REMOVED from the list0.3ms
aeo_visibility now prices the call the user actually chose0.9ms
costOfCall answers for every tool, priced or not · 4 tests
prices an unpriced tool at zero rather than refusing to answer0.3ms
prefers the per-call estimate over the table when arguments decide the cost0.4ms
never reads approvalMode off a per-call estimate0.4ms
quotes the ceiling when the arguments cannot say how big the call is1.2ms
count-in-args tools price the call, not the ceiling · 6 tests
seo_request_indexing: 20 URLs no longer reserves the 200-URL ceiling1.2ms
keyword volume is dominated by the PER-TASK charge, not the keyword count0.3ms
crosses a real step only when a new TASK is needed0.2ms
falls back to the flat ceiling when the caller named no items0.6ms
seo_geo_visibility needs BOTH dimensions before it prices a real call0.5ms
every cleared tool is out of the frozen list and into the estimators0.4ms
alerts.vitest.ts
19/19 34ms · 3 suites PASS
src/admin/alerts.vitest.ts
computeAlerts · 12 tests
is silent on an empty/unknown input — absence is not a breach3.9ms
does not treat a null metric as healthy OR as breached0.6ms
escalates the request quota from warning to critical0.9ms
fires the Durable Object storage tripwire on any non-zero byte2.1ms
ignores a judge failure rate below the attempt floor — 1 of 2 is 50% and means nothing0.9ms
ignores a downvote rate below the response floor0.8ms
treats truncation as INFO — incomplete numbers, not a broken system0.7ms
says when capabilities were never judged — an unjudged tool looks exactly like a healthy one0.7ms
flags names judged that are not registered capabilities as a wiring smell0.4ms
does not put a balance figure in the title — the panel is the place to read it0.6ms
sorts critical before warning before info0.9ms
gives every alert an action, not just a colour15.7ms
judge-coverage alerts name the items · 4 tests
lists the unrecognised names in the detail0.4ms
lists unjudged capabilities too1.3ms
says "1 run", not "1 runs"0.5ms
bounds a long list and says how many are hidden, rather than truncating silently0.4ms
unjudged alert distinguishes a defect from an empty week · 3 tests
is a WARNING when the tools actually ran, and names the run counts0.4ms
raises no unjudged alert when nothing was invoked-but-ungraded0.3ms
drops to INFO and stops claiming invocation when run counts are unavailable0.4ms
unfulfilled-promise.vitest.ts
19/19 39ms · 5 suites PASS
src/chat/unfulfilled-promise.vitest.ts
the four shapes, taken from the transcripts verbatim · 5 tests
a tool call the model TYPED — promisesociety5, the one that cost a whole session4.6ms
catches the PRE-redaction spelling too0.4ms
an announcement — adityakushwaha1.2ms
an announcement — v80813952981.1ms
a bare acknowledgement — anchalsharma and 26muthumari0.4ms
WORDING ALONE IS NEVER THE DEFECT — delivery is half the test · 4 tests
the same announcement is FINE when the result arrived0.4ms
an artifact counts as delivery0.3ms
CHIPS count — a picker is a real turn0.4ms
an approval card counts — a decision was delivered0.3ms
what it must NOT flag · 5 tests
a real answer that merely mentions running something0.5ms
a long answer whose LAST line happens to be an announcement0.3ms
an honest report of a failure is not a promise0.3ms
HTML in a report body is not a typed tool call0.3ms
empty or junk input0.6ms
what the user is told instead · 2 tests
says nothing ran, never guesses WHY, and offers one action1.6ms
the bare-ack wording admits the specific lie it told0.2ms
it runs on the live path, with delivery derived from the response itself · 3 tests
the agent route checks every turn7.0ms
delivery comes from deriveRenderManifest, NOT reqCtx.renderedManifest6.5ms
the rewrite is COUNTED, not just silenced11.6ms
write-intent.vitest.ts
19/19 21ms · 5 suites PASS
src/chat/write-intent.vitest.ts
asksForAWrite · 7 tests
recognises the sentences that actually failed9.6ms
leaves read requests alone — the shortcut exists for these0.8ms
does not fire on a write verb buried inside a read request0.8ms
is not fooled by substrings0.4ms
reads polite and compound phrasings0.5ms
ignores control tokens, which the shortcut also excludes0.3ms
is empty-safe1.7ms
shouldKeepGoingAfterLookup · 2 tests
needs BOTH a lookup and a write request1.7ms
does not cover the expensive audit whose render really is the answer0.4ms
the loop honours it · 2 tests
the shortcut stands down when the helper says keep going1.1ms
and the stand-down is counted, so the rate is measurable rather than assumed0.7ms
writeInstructionCount · 5 tests
counts the two-instruction sentence that failed0.4ms
counts one instruction as one0.3ms
counts a read as zero, so a plain question never keeps the loop alive0.2ms
does not count a verb inside a noun phrase0.2ms
KNOWN FALSE POSITIVE: "and <verb>" in a noun phrase counts as an instruction0.3ms
shouldKeepGoingAfterLookup — the second reason · 3 tests
keeps going after ONE write tool when the message asked for two things0.2ms
still halts after one write tool for a single instruction0.2ms
is unchanged for lookups0.3ms
product-scan.vitest.ts
19/19 22ms · 6 suites PASS
src/leads/product-scan.vitest.ts
parseProductProfile · 5 tests
parses valid JSON with surrounding prose and coerces field types6.5ms
returns null on malformed JSON and on empty identity0.8ms
parses fenced JSON (```json blocks)0.5ms
salvages a truncated (finish=length) payload1.8ms
caps list lengths1.6ms
compileBriefFromProfile · 3 tests
keeps the legacy line shape extractCompanyName/extractProductName parse1.0ms
appends the richer fields after the legacy block0.3ms
omits empty fields entirely0.8ms
mergeScanCompetitors · 4 tests
appends new scan finds as auto without touching user entries2.4ms
filters own domain (incl. subdomains), reference hosts, invalid domains, and dupes0.7ms
returns null when nothing new survives, so callers skip the write0.4ms
caps the stored set at 120.6ms
compactProductContext · 3 tests
builds priority-first lines from the profile within the budget1.0ms
a small budget still keeps the identity lines (never a mid-line cut)0.4ms
falls back to a brief slice without a profile0.2ms
repairTruncatedJson · 2 tests
closes open arrays/objects and drops a cut-off partial string0.5ms
drops a dangling key with no value0.3ms
discoverAuxPages · 2 tests
returns same-origin high-signal pages, deduped, capped at 20.7ms
excludes the scanned page itself and tolerates bad base URLs0.3ms
artifact-card.vitest.ts
19/19 31ms · 4 suites PASS
src/reports/artifact-card.vitest.ts
buildArtifactCard · 5 tests
carries the markdown as copy_text so a paste keeps its structure3.0ms
falls back to stripped HTML for a preview when the tool has no markdown1.1ms
drops <style> bodies rather than previewing raw CSS0.3ms
caps the preview — the card is collapsed, the full text rides in copy_text0.6ms
omits preview and copy_text entirely when there is nothing to show0.4ms
buildArtifactCard publish routing · 5 tests
offers publish when a connector is live1.0ms
reports a disconnected connector rather than hiding it — the card shows a connect CTA0.4ms
lists every resolved destination, not just the first0.8ms
omits a destination the tool never resolved, while keeping one it did0.3ms
offers NOTHING when the tool resolved no connector state at all0.3ms
messageRepeatsDocument · 6 tests
catches the model repeating the whole article as its message0.4ms
still catches it with a lead-in and light edits0.2ms
does NOT flag a genuine summary that quotes the opening0.2ms
does NOT flag a diagnosis that happens to accompany a report0.2ms
does not fire on short documents, where summary and duplicate are indistinguishable0.2ms
handles empty and missing input without throwing2.1ms
documentHandoffMessage · 3 tests
names the document and its size, and points at the card18.6ms
omits the size rather than claiming 0 words0.3ms
does not try to summarise the document0.3ms
ops-wave6.vitest.ts
19/19 17ms · 6 suites PASS
src/tools/ops-wave6.vitest.ts
seo_request_indexing — the external one · 5 tests
is classified as leaving nqzai2.7ms
takes an array of urls1.1ms
accepts the empty call0.4ms
rejects the retired singular url0.5ms
clamps an over-long batch instead of refusing it1.9ms
seo_rank_track — the alias was in the schema · 3 tests
takes the array0.4ms
rejects the singular that used to be DECLARED alongside it0.6ms
requires at least one keyword0.5ms
seo_content_quality · 4 tests
takes the url0.5ms
rejects the retired page alias0.4ms
rejects the retired site alias0.2ms
requires a page — this tool cannot run on nothing0.5ms
seo_monitor · 2 tests
accepts the empty call and a named site0.4ms
rejects the retired domain alias0.2ms
seo_google_merge — held back from wave 4 for a reason · 3 tests
declares the window arguments the helpers actually consume0.5ms
declares the row limits with bounds instead of hiding them0.4ms
rejects an invented argument0.2ms
the SEO section is done · 2 tests
every remaining tool.domain fallback belongs to a shortcut capability2.6ms
the registry still holds every tool1.1ms
seo-keywords-schema.vitest.ts
19/19 14ms · 3 suites PASS
src/tools/seo-keywords-schema.vitest.ts
seo_keywords is schematised · 4 tests
is registered as a schema-first tool3.6ms
declares topic as the only required field1.1ms
exposes include_volumes to the model, which the deleted shortcut could not0.4ms
bounds the topic so a sentence cannot pass as a seed term0.5ms
the model-facing definition comes from the schema · 2 tests
generates a tool definition rather than a hand-written registry entry0.7ms
names no vendor and no USD price0.7ms
argument validation — the incident prompts and their paraphrases · 13 tests
accepts the seed term "ai seo tools"1.3ms
accepts the seed term "media bias detection"0.3ms
accepts the seed term "fact checking"0.7ms
accepts the seed term "programmatic seo"0.3ms
accepts the seed term "b2b lead generation"0.3ms
accepts a full economics ask once the model has structured it0.4ms
rejects the whole user sentence — what extractKeywordTopic used to produce0.3ms
rejects an empty or one-character topic rather than researching nothing0.3ms
rejects a missing topic, so the model asks instead of guessing0.8ms
rejects an off-schema field instead of silently ignoring it0.3ms
`query` is MIGRATED to topic, not ignored — the value survives0.2ms
rejects a limit below the minimum but clamps one above the maximum0.2ms
rejects a wrong-typed include_volumes rather than coercing it0.3ms
brand.vitest.ts
19/19 21ms · 4 suites PASS
src/seo/brand.vitest.ts
buildBrandIdentity · 7 tests
prefers the product-brief company name over the domain prefix4.5ms
explicit brand wins over everything1.1ms
falls back to the domain prefix with no brief1.0ms
domainTokens compact multi-word names down to URL-matchable tokens0.6ms
drops sub-3-char tokens (alias noise guard)0.5ms
the structured profile company/product win over the brief-regex derivation1.4ms
explicit brand still outranks the profile company0.5ms
extractDeclaredBrand · 5 tests
prefers Organization JSON-LD name over everything3.0ms
handles @graph arrays and picks the Organization node1.5ms
falls back to og:site_name when no Organization schema0.6ms
falls back to the shortest <title> segment (the brand, not the tagline)0.8ms
malformed JSON-LD does not throw; returns null when nothing authoritative0.6ms
brandTextRegexes — response-text matching · 3 tests
matches the company name in engine prose (the exact reported miss)0.3ms
matches the product name too0.3ms
word boundaries prevent substring false positives1.6ms
isOurs — alias-aware citation-domain matching · 4 tests
matches the exact domain and subdomains (unchanged)0.4ms
matches brand-name domains that differ from the primary domain0.6ms
legacy single-token callers still work0.4ms
rival domains stay rivals0.4ms
domain-rating-batch.vitest.ts
19/19 63ms · 3 suites PASS
src/seo/domain-rating-batch.vitest.ts
one request, not one per domain · 5 tests
rates eight domains with a single call36.5ms
POSTs a targets array — never the GET form that returns one rating for the whole set1.5ms
strips the trailing slash the endpoint returns0.9ms
chunks at the measured ceiling of 10009.4ms
sends each distinct domain once1.3ms
why a rating is missing stays as precise as the single-fetch path · 7 tests
a domain the response omits is unrated, not an error0.9ms
a 403 marks every domain in the chunk `auth`0.9ms
a 429 is rate_limited, and anything else is error2.0ms
a failed chunk does not take a healthy one down with it2.7ms
rejects junk before spending a request on it1.2ms
treats a 0 as unrated, not as a rating of zero0.8ms
makes no request at all when nothing is rateable0.6ms
readRating · 7 tests
keeps a real rating0.3ms
rounds, because the endpoint answers in floats0.3ms
treats 0 as unknown, not as the bottom band0.2ms
keeps a sub-1 rating, because 0.1 is a measurement and 0 is not0.3ms
never returns a positive rating that would store as 00.3ms
is exclusive at zero, not inclusive0.2ms
rejects a non-number rather than coercing it0.6ms
keyword-performance-history.vitest.ts
19/19 22ms · 5 suites PASS
src/seo/keyword-performance-history.vitest.ts
the history row is written alongside the current-state row · 5 tests
writes both tables from one pull5.5ms
carries the same identity columns as the current-state row1.9ms
normalises a URL-prefix property to the same host as its domain property0.7ms
writes nothing at all when the pull resolved no property2.2ms
writes no history when every row was filtered out0.8ms
a row states the window it describes — the basis, not the pull date · 7 tests
stores the window the caller actually asked for0.9ms
does not assume 28 days — a 90-day pull is stored as 900.7ms
derives the length from the dates rather than trusting a caller count0.5ms
a single-day window is 1, not 00.5ms
records when we LOOKED separately from what the reading covers0.8ms
every row in one pull shares one window and one pull date0.4ms
refuses to write history for a pull that cannot state its basis, and reports it0.9ms
the population is IDENTICAL to the current-state write · 1 test
holds exactly the same keywords, in the same order1.2ms
the upsert conflict target — the Hasura constraint trap · 4 tests
names a constraint the migration actually declares0.5ms
the declared constraint is the grain: one row per user, site, keyword, WINDOW0.3ms
the earlier day-keyed grain is gone, not merely unused0.3ms
re-reading the same window UPDATES the metrics and never the conflict keys1.6ms
a failed history write is reported, never swallowed · 2 tests
reports the failure and still writes the registry0.9ms
a failed CURRENT-state write still leaves the observation recorded0.6ms
panel-segments-surface.vitest.ts
19/19 10ms · 6 suites PASS
src/seo/panel-segments-surface.vitest.ts
the panel surface exists and is dispatched · 2 tests
handles list, add and remove2.3ms
reads and writes the panel setting rather than a private store0.6ms
the recurring cost is quoted from the SHARED function · 4 tests
prices with aeoVisibilityFanoutCostUsd, the same one the approval card uses0.4ms
does not multiply a per-cell price itself0.4ms
reports the YEARLY commitment, not just the weekly figure0.3ms
quotes the engines the run will actually use, never the plan maximum0.2ms
a keyword panel is DERIVED, never typed · 4 tests
builds it from live search data0.2ms
refuses rather than inventing a panel when there is nothing to seed from0.2ms
requires prompts for every OTHER kind0.3ms
enforces the ceiling that bounds the recurring charge0.3ms
a run can be attributed to the panel it measured · 3 tests
aeo_visibility resolves the named panel BEFORE the library fallback1.0ms
the geo leg passes the resolved panel down to the runner0.8ms
the snapshot is tagged ONLY when a panel was named0.2ms
the model is told what a panel costs · 3 tests
the schema carries a rule ordering it to state weekly AND yearly0.3ms
names no USD price — this product bills in tokens0.2ms
aeo_visibility declares the segment parameter it now accepts0.2ms
the panel tool is findable for the DELETE intent · 3 tests
names the delete intent in the words a user says0.3ms
says what these panels are NOT, because the word is overloaded0.3ms
the summary itself advertises deletion, not just creation0.3ms
small-helpers.vitest.ts
19/19 29ms · 5 suites PASS
src/seo/small-helpers.vitest.ts
safeCacheKeyMeta — must never log a user-derived key · 4 tests
takes the namespace before the first colon2.9ms
degrades to "unknown" rather than leaking a key with no separator0.5ms
degrades on a leading colon rather than emitting an empty scope0.2ms
never returns the raw key in either field0.4ms
hashKey · 2 tests
is stable and distinguishes near-identical keys1.1ms
is non-reversible and fixed-shape, including for empty input1.2ms
cleanDomain · 5 tests
strips scheme, path, query and case0.7ms
prefers the URL out of a markdown link0.3ms
falls back to the label when the markdown link has no URL0.4ms
strips stray wrapping punctuation0.3ms
returns empty for empty input instead of throwing0.4ms
extractCanonicalFromHtml · 5 tests
reads a canonical link regardless of attribute order or quoting0.7ms
reads og:url with content on EITHER side of property0.2ms
returns nulls when absent rather than empty strings0.4ms
does not match a rel that merely CONTAINS canonical0.2ms
ignores tags past the head budget instead of scanning a whole huge page0.4ms
topUpMessage · 3 tests
names the estimate, the balance and the action17.4ms
never shows a NEGATIVE balance to the user0.5ms
quotes tokens, never currency0.3ms
lead-normalize.vitest.mjs
19/19 20ms · 5 suites PASS
scripts/lib/lead-normalize.vitest.mjs
normalizePersonName — the organization key · 5 tests
lowercases and strips punctuation the way the stored rows were written5.3ms
KEEPS the legal suffix1.4ms
folds accents rather than dropping the name0.5ms
drops digits — so two differently-numbered companies collide, by design1.1ms
refuses a name with no letters, and one that is absurdly long0.7ms
normalizeUsDomain — the other half of the key · 3 tests
strips scheme, www and trailing dot0.7ms
keeps a subdomain that is not www0.3ms
returns null rather than a guess for junk1.0ms
normalizeEmail · 2 tests
lowercases and validates the domain half1.0ms
rejects anything that is not an address0.5ms
parseEmployeeBand — migration 136 stores bounds, not labels · 3 tests
reads the source vocabulary1.4ms
treats an open top as open, not as a number0.3ms
contributes nothing rather than a zero when it cannot parse0.7ms
validity gates — refuse what we cannot recognise · 6 tests
accepts the band vocabulary the source actually uses0.4ms
REJECTS the real junk that landed in Company Size0.4ms
accepts real industry labels including punctuated ones0.7ms
REJECTS prose and the lost-quoting signature0.6ms
is SHAPE-based, not an allow-list, so a new legitimate industry still passes0.2ms
accepts place names and rejects sentences in the place columns0.9ms
gsc-query-quality.vitest.ts
18/18 10ms · 3 suites PASS
src/admin/gsc-query-quality.vitest.ts
classifyQuery — real noise from the live table · 12 tests
flags scraper query "ai seo tools" -site:reddit.com -site:twitter.com -site:x.com2.3ms
flags scraper query "moz" "serp" -site:reddit.com -site:twitter.com -site:x.com0.3ms
flags scraper query "backlinko" -site:reddit.com -site:twitter.com -site:x.com0.3ms
flags scraper query "otterly.ai" -site:reddit.com -site:twitter.com -site:x.com0.4ms
flags stacked quoted phrases — the compound-query signature of a research tool1.3ms
leaves a SINGLE quoted phrase alone — people really do search exact phrases0.5ms
keeps the genuine query nqzai0.3ms
keeps the genuine query nqz.ai0.3ms
keeps the genuine query ai marketing planner0.2ms
keeps the genuine query domain reputation checker0.3ms
separates zero-impression rows as no_signal rather than calling them junk0.5ms
does not mistake a hyphenated word for a negative operator0.2ms
classifyQueries · 2 tests
splits the list, counts every bucket, and never silently drops a row0.6ms
ranks useful queries by IMPRESSIONS, surfacing demand we are failing to convert0.3ms
queryOpportunity · 4 tests
calls out striking distance in the 4-20 band0.3ms
does not call position 2 striking distance — that win is already banked0.3ms
flags ranks-well-but-unclicked as a title/meta problem, not a ranking one0.1ms
stays silent when there is too little data to claim anything0.1ms
shopify-oauth.vitest.ts
18/18 37ms · 6 suites PASS
src/commerce/shopify-oauth.vitest.ts
normalizeShopDomain · 2 tests
accepts canonical myshopify domains, case/protocol/path-insensitive3.0ms
rejects lookalikes and junk1.0ms
verifyCallbackHmac · 6 tests
accepts a correctly signed callback query (hmac + signature excluded, keys sorted)8.6ms
rejects a tampered param and a wrong secret2.4ms
rejects missing/garbage hmac0.5ms
accepts a signature over the RAW percent-encoded query string1.7ms
accepts a signature over the re-encoded canonicalization1.7ms
still rejects tampering under all canonicalizations1.9ms
verifyWebhookHmac · 1 test
accepts the raw body signed with the app secret and rejects tampering1.9ms
verifySessionToken · 3 tests
accepts a valid token and returns the normalized shop1.9ms
rejects expired, wrong-aud, wrong-signature, malformed4.3ms
rejects a dest that is not a myshopify domain0.7ms
shopifyConnectAllowed (App Store review gate) · 3 tests
unset allowlist → gated for everyone (safe default during review)0.3ms
allowlisted email connects; others gated; case/space-insensitive0.4ms
'*' opens the gate for everyone, including no email (Shopify reviewer)0.2ms
shop claim tokens (Shopify-initiated install account-link) · 3 tests
round-trips shop + claim and survives URL encoding3.0ms
rejects tampered and garbage tokens1.1ms
rejects claims older than the pending TTL1.3ms
esp-webhooks.vitest.ts
18/18 77ms · 4 suites PASS
src/email/esp-webhooks.vitest.ts
webhook URL token · 6 tests
round-trips the tenant it was minted for23.2ms
rejects a token minted for a DIFFERENT provider1.6ms
rejects a token whose user id has been swapped1.2ms
rejects a token minted under a different worker secret0.8ms
rejects malformed tokens rather than throwing0.9ms
gives different tenants different tokens2.0ms
provider allow-list · 1 test
accepts only the four providers that can actually send0.6ms
Svix signature (Resend) · 3 tests
rejects a payload with no signature headers35.5ms
rejects a signature outside the replay window1.6ms
accepts a correctly signed payload and rejects a tampered body2.3ms
payload parsing · 8 tests
Mailjet: reads its own hard_bounce verdict rather than the free-text error1.2ms
Resend: Permanent / Transient / Undetermined map to hard / soft / unknown1.4ms
SendGrid: classifies through the SAME function the DSN parser uses1.0ms
Mailtrap: a named bounce with no readable code stays HARD0.4ms
ignores every event that does not mean "stop sending here"0.8ms
marks spam complaints distinctly, and always as terminal0.6ms
drops events with no recipient instead of inventing one0.3ms
survives payloads that are not the shape the provider documents0.6ms
judge-acknowledgement.vitest.ts
18/18 16ms · 4 suites PASS
src/chat/judge-acknowledgement.vitest.ts
acknowledgement by asking for the prerequisite · 5 tests
counts the real 0.2 cases as acknowledged5.3ms
still catches the concealment shape this guard exists for0.8ms
is not satisfied by a reply that merely ends in a question0.4ms
requires the reply to ask for the SAME thing the tool said was missing0.4ms
leaves a genuine no-failure turn alone0.6ms
the failure and the reply may use different words for the same thing · 6 tests
domain <-> website URL is the same prerequisite0.7ms
site url <-> domain, both directions0.4ms
lead <-> contact <-> list is the same prerequisite0.3ms
recognises the two ask shapes that were being read as concealment0.4ms
and neither shape can be satisfied by narrative prose0.4ms
but a different prerequisite still does not acknowledge0.4ms
STATING the zero is owning up — the empty result the reply printed · 5 tests
accepts a printed count1.9ms
accepts the same fact stated without a digit1.3ms
and the whole reply now reads as acknowledged, which is the point0.9ms
does NOT excuse a broken run, only an empty one0.4ms
does NOT accept a reply that conceals the emptiness0.4ms
the clamp that produced the 0.2 constant · 2 tests
still pins a genuinely concealed failure, so this fix narrowed the trigger not the guard0.3ms
does not clamp when the failure was acknowledged0.3ms
judge-honesty.vitest.ts
18/18 13ms · 5 suites PASS
src/chat/judge-honesty.vitest.ts
failureWasAcknowledged · 5 tests
is false when the reply never mentions that anything went wrong4.0ms
is TRUE for the live fabrication — which is why this detector alone was not enough0.8ms
is true when the reply plainly says it did not work0.9ms
is true when there was no failure at all, so callers can ignore it0.4ms
only reads the opening of a long reply — a buried admission is not an admission1.0ms
the note handed to the judge states the fact, not a suspicion · 4 tests
names the tool, the failure, and that nothing was saved0.9ms
classifies it as hallucination and caps hard0.6ms
says fluency is aggravating, not mitigating0.4ms
leaves the honest exit open0.5ms
caps bind deterministically, because prose caps do not bind LLMs · 5 tests
a concealed failure is clamped whatever the model said0.6ms
dead_end is capped at 0.6 — the rubric said so and nothing enforced it0.4ms
hallucination is capped even without the platform signal0.3ms
leaves a clean verdict alone0.3ms
still clamps disproportionate0.3ms
an acknowledged failure keeps the softer, existing treatment · 1 test
gets the ordinary outcome note, not the contradiction note0.6ms
the invariant that actually catches the live case · 3 tests
EVERY failed run tells the judge nothing was persisted0.5ms
tells the judge to cross-check against stored context, which it already receives0.2ms
forecloses the specific excuse the live reply used0.3ms
local-business.vitest.ts
18/18 17ms · 3 suites PASS
src/leads/local-business.vitest.ts
extractPostal · 8 tests
maps 5-digit anchored zips to US3.1ms
maps 6-digit codes to India0.5ms
never matches bare counts or short codes0.6ms
country cue beats digit inference — 75001 France is Paris, not Texas0.4ms
country cue unlocks 4-digit postals0.4ms
alphanumeric postals require a matching country cue0.9ms
postal AFTER the city is caught — live retainly miss0.5ms
cue alias forms work — UK, USA0.5ms
detectLocalBusiness · 8 tests
detects category + zip4.2ms
detects category + pincode (India)1.5ms
zip alone is enough — no recognised category needed0.7ms
detects category + free-text city0.5ms
falls back to persona geography when the query has no location tail0.3ms
rejects B2B persona queries even with a location0.1ms
rejects local category with no resolvable place0.2ms
detects medical categories with a place0.6ms
the maps actor is gone, and stays gone · 2 tests
exports no actor id and no input builder0.5ms
still parses historical run payloads0.3ms
onboarding-first-turn.vitest.ts
18/18 69ms · 6 suites PASS
src/leads/onboarding-first-turn.vitest.ts
a Worker must not fetch its own zone · 4 tests
recognises our own host, with or without www or a path3.9ms
leaves every real customer domain alone0.6ms
follows APP_BASE_URL rather than a hardcoded literal0.6ms
does not throw on junk input0.6ms
reasoning must not quote our own instruction machinery · 3 tests
redacts the identifiers that mark a leak as a leak4.1ms
redacts rather than blocks — the surrounding sentence is usually harmless0.5ms
leaves ordinary language alone0.6ms
scanProductSite refuses our own zone before it fetches · 2 tests
returns a typed SELF_ZONE error and never makes a request1.1ms
says what actually happened and asks for something usable1.0ms
a failed scan carries its own routing, not just an error · 1 test
the dispatch returns brief_saved:false plus the three ways forward2.0ms
domainFromMessage (shared by every shortcut that names a subject) · 4 tests
pulls the domain a user named in a natural request0.8ms
returns null when no domain is named — the saved site must still resolve0.3ms
ignores pasted file paths, which are not subjects0.2ms
takes the FIRST domain when several appear — the subject, not an aside0.2ms
scanProductSite — the scan is enrichment, the URL is a fact · 4 tests
SELF_ZONE still saves nothing — there the URL is not worth keeping0.8ms
a refused crawl still saves the site — the business is real, our crawler was blocked47.6ms
does NOT save when the domain never resolved — that is a typo, not a block1.9ms
never claims a save it did not make — no userId, no flag1.0ms
product-context.vitest.ts
18/18 13ms · 2 suites PASS
src/leads/product-context.vitest.ts
repairTruncatedJson · 10 tests
leaves already-valid JSON parseable and unchanged in meaning3.0ms
closes an object cut off mid-structure0.5ms
DROPS a value truncated mid-string rather than keeping a half-word0.4ms
drops a dangling key that has no value yet0.4ms
drops a trailing separator rather than emitting {"a":1,}0.3ms
closes nested structures in the right order0.3ms
is not fooled by braces or quotes INSIDE a string0.3ms
preserves escaped characters through the repair0.2ms
keeps bare literals that completed0.3ms
always returns something JSON.parse accepts, even for junk3.2ms
autoLinkBrand · 8 tests
links the first occurrence of the company name0.6ms
links only ONCE, not every mention1.1ms
does nothing when the body already contains a link0.2ms
does nothing without a URL0.3ms
adds https:// to a bare domain rather than emitting a relative href0.6ms
treats a brand name with regex metacharacters literally0.4ms
falls back to the product name when the company name is absent from the body0.2ms
leaves the body untouched when neither name appears0.2ms
plan-builder.vitest.ts
18/18 9ms · 6 suites PASS
src/planner/plan-builder.vitest.ts
the hard blocker names the work it actually blocks · 5 tests
fires only when the plan contains site-dependent work1.9ms
outbound and commerce are NOT site-dependent0.5ms
seo, aeo and content ARE0.3ms
no longer claims the whole plan is unmeasurable0.4ms
the string has ONE definition — it had two0.6ms
CreatePlan mutation is not a "//"-poisoned GraphQL document · 1 test
the CreatePlan mutation literal contains no JS-style "//" line0.4ms
blockedFamilyLine (no raw tool/readiness keys to the user) · 3 tests
renders known families and prerequisites in plain English0.4ms
never contains a snake_case identifier for any known family0.6ms
falls back to a humanized (not raw) label for an unmapped family/prerequisite1.3ms
neverUsedFamiliesLine (no raw tool keys to the user) · 2 tests
renders known families in plain English0.3ms
never contains a snake_case identifier0.4ms
countSharedMeasurements / decorateInitiative (Track B #3 — honest shared-signal copy) · 3 tests
counts items sharing the same adapter+key, ignores unmeasurable items0.3ms
adds a "shared" note only when the same signal backs more than one item0.5ms
never adds a shared note to a non-learning (unmeasurable) item0.2ms
isTopicGrounded (no ungrounded topic reaches a paid tool call) · 4 tests
passes a topic that shares a real word with the tenant context0.3ms
rejects a topic with zero word overlap — the live incident case0.1ms
is case-insensitive0.2ms
passes through when the value has no substantive (length > 3) words to check0.1ms
plan-source-ledger.vitest.ts
18/18 52ms · 6 suites PASS
src/planner/plan-source-ledger.vitest.ts
buildPlanSourceLedger — every audit type is accounted for · 3 tests
covers EVERY SNAPSHOT_TYPE, present or not4.5ms
an absent stored source explains what its absence cost, in readable English1.8ms
no row is labelled with its raw internal key (GS-005)2.8ms
buildPlanSourceLedger — "not on file" is never "could not be read" (GS-004) · 4 tests
an errored audit read says so, and is flagged0.7ms
a legitimately absent audit says never run — reporting OUR outage as THEIR missing data is the defect0.5ms
a failed keyword read is distinguished from a tenant with no keywords0.5ms
a PARTIAL keyword failure keeps the half we do hold0.9ms
buildPlanSourceLedger — provenance classes are real distinctions (GS-003) · 4 tests
stored context is RECORDED, audits are MEASURED7.7ms
age is null when the source carries no timestamp — never 00.7ms
a dated snapshot reports its real age0.8ms
today reads as "measured today", not "0 days old"0.5ms
buildPlanSourceLedger — keyword coverage states what is MEASURED, not just held · 1 test
separates registry membership from measured performance0.6ms
planSourceSummary — counts, not adjectives · 2 tests
counts present sources and names failures0.6ms
says nothing about failures when there were none0.3ms
the plan report renders the ledger · 4 tests
shows each source with its provenance class25.3ms
says the ledger was captured at BUILD time, not now0.6ms
a plan that PREDATES the ledger renders no ledger block at all0.8ms
names failed reads in the section header, so a degraded plan cannot look complete0.5ms
aeo-phase2.vitest.ts
18/18 12ms · 4 suites PASS
src/reports/aeo-phase2.vitest.ts
aeo_page_check — crawlability is a gate, not a component (P-001/P-002) · 4 tests
a blocked page is never called "partially AI-ready"2.7ms
the score is greyed and stage-labelled, without changing the arithmetic0.4ms
never asserts reachability it did not check0.9ms
a crawlable page is unaffected0.6ms
aeo_page_check — one list, ordered by impact (P-003/P-004) · 6 tests
the blocker outranks every measured deduction0.6ms
measured deductions outrank structural checks, largest first0.3ms
the heavier GEO pillar comes before the lighter one0.3ms
states the ordering rule, and does not claim a precision it lacks0.9ms
names the stack on fixes when we know it (P-004)0.3ms
does not list the crawl block twice0.5ms
seo_geo_research — not ranking is not zero (G-001) · 2 tests
renders no gauge at all rather than a confident zero0.5ms
a ranking page still gets its gauge0.7ms
seo_geo_research — the competitor delta IS the report (G-002/G-003) · 6 tests
compares our signals against the cited pages on the same axes0.3ms
uses the MEDIAN, so one outlier cannot set the target0.3ms
ranks the axes we are BEHIND on first0.2ms
a failed gap analysis is never rendered as its own content (G-003, trap 1)0.2ms
when nothing could be measured it says so, rather than showing an empty section0.5ms
a real gap analysis still renders normally0.4ms
bug-report.vitest.ts
18/18 25ms · 3 suites PASS
src/support/bug-report.vitest.ts
sanitizeBugReport · 9 tests
requires a title and a description of real length4.9ms
falls back to normal severity rather than rejecting an unknown one0.9ms
keeps a valid severity0.3ms
STRIPS THE QUERY STRING from the page URL0.8ms
keeps the fragment, because the SPA deep-links with it0.4ms
keeps an unparseable URL rather than dropping it — a malformed URL is still a clue0.4ms
strips the query string from every network-trail path too0.4ms
truncates the network trail from the FRONT, keeping the most recent calls1.6ms
drops junk entries rather than storing empty rows1.3ms
screenshotKey · 1 test
partitions by UTC year and month so a lifecycle rule can act on a prefix1.0ms
submitBugReport · 8 tests
writes the screenshot to R2 BEFORE the row, and stores the key it actually wrote7.9ms
NEVER takes the reporter from the payload — user_id is the one passed in0.5ms
files the report anyway when the R2 binding is unbound, and says the screenshot did not save0.7ms
deletes the orphaned screenshot when the row insert fails1.1ms
refuses a user who is over the hourly limit, and names the number0.5ms
FAILS OPEN when the rate-limit query itself throws0.3ms
records no screenshot key when none was supplied0.6ms
still files the report when the email lookup fails0.3ms
content-wave3.vitest.ts
18/18 11ms · 4 suites PASS
src/tools/content-wave3.vitest.ts
seo_write_content: the two modes are declared · 10 tests
accepts keyword mode4.1ms
accepts rewrite mode0.3ms
lets a ready-made brief be passed, which was impossible before0.7ms
rejects the retired topic alias0.5ms
rejects the retired title alias0.3ms
rejects the retired url alias0.3ms
rejects the retired rewrite_url alias0.2ms
rejects the retired content_brief alias0.5ms
bounds length through the schema: floor errors, ceiling clamps0.4ms
tells the model both modes exist0.6ms
seo_content_brief · 3 tests
accepts the declared shape0.2ms
rejects the retired topic alias0.2ms
requires the keyword0.3ms
seo_content_ideas · 2 tests
accepts the empty call and a named site0.3ms
rejects an invented argument0.2ms
the coalescing chains are gone · 3 tests
seo_write_content reads one name per concept0.7ms
seo_content_brief no longer coalesces topic0.3ms
leaves still-legacy tools their fallbacks0.2ms
audience-split.vitest.ts
18/18 10ms · 6 suites PASS
src/seo/audience-split.vitest.ts
audienceView · 3 tests
measures the comparison-prompt shape of a panel2.2ms
a use-case panel reads as far less comparison-shaped0.3ms
builds the source diet from OTHER people's hosts, not ours0.4ms
dietDivergence compares SHARES, never raw counts · 2 tests
two panels of different sizes are still comparable0.3ms
sorts by the widest gap first0.5ms
with fewer than two audience panels it REFUSES, and names what to build · 4 tests
no panels: both comparison hypotheses untested0.5ms
ONE panel is still a refusal — one panel has no difference in it0.5ms
a CONFIGURED but unmeasured panel changes the instruction, not the verdict0.4ms
ignores panels of another kind — a language panel is not a second audience0.4ms
with two audience panels it answers · 4 tests
compares the prompt shapes with both panels as sources1.1ms
names the source kind that diverges most0.4ms
kills the diet hypothesis when the two panels draw on the same kinds0.4ms
the decision funds two backlogs rather than asking which to pick0.2ms
the two hypotheses nothing here can test · 3 tests
what the answer TEXT said is not recorded, so the integration claim stays untested0.2ms
self-reported influence needs a CRM field that does not exist0.2ms
they stay untested even with both panels measured — more panels cannot supply them0.2ms
the brief holds the contract · 2 tests
the headline changes with what could actually be done0.4ms
carries the one-pager fields0.4ms
backlink-gap.vitest.ts
18/18 47ms · 4 suites PASS
src/seo/backlink-gap.vitest.ts
computeBacklinkGap — the subtraction · 6 tests
returns their referring domains that are absent from ours4.8ms
compares on the referring DOMAIN, not the page URL1.5ms
collapses many links from one site into ONE prospect0.8ms
ranks by domain rating, highest first0.7ms
marks a domain nofollow_only only when EVERY observed link was nofollow0.5ms
drops rows with no usable referring URL rather than inventing a prospect0.3ms
computeBacklinkGap — basis is the honesty gate · 3 tests
is `no_baseline` when we hold none of our own links0.6ms
is `measured` once we hold any of our own referring domains0.4ms
reports their distinct domain count, not their link count9.0ms
backlinkGapNote — the disclosure that travels with it · 6 tests
refuses the word "gap" with no baseline, and names the fix1.1ms
says how partial the sample is when we know our true total17.3ms
stays quiet about partiality when the sample IS the whole profile0.5ms
claims completeness ONLY when the provider confirmed the set was exhausted0.4ms
does not infer completeness from the counts happening to match0.3ms
never claims completeness while also admitting the sample is partial0.3ms
the rendered reply cannot contradict its own disclosure · 3 tests
with NO baseline it never tells the user to pitch anyone7.5ms
with a baseline the pitch IS the next move0.4ms
never calls it a gap in the heading without a baseline0.5ms
backlink-policy.vitest.ts
18/18 14ms · 6 suites PASS
src/seo/backlink-policy.vitest.ts
Q09: disavow is reachable ONLY through a concentrated network · 5 tests
a very low-authority domain is IGNORED, never disavowed4.2ms
spammy-looking anchors and zero DR still do not trigger disavow0.4ms
fires only once enough domains share one network0.7ms
tells the reader NOT to build a disavow file when no network is found0.5ms
always says the manual-action half of the rule is not visible from here0.8ms
Q09: the other three clauses survive · 3 tests
says do not buy links, naming paid guest posts0.6ms
measures in-niche referring domains, not a rating0.7ms
produces an earn plan rather than a rating target0.8ms
Q09: reclaim is found and ranked first · 2 tests
classifies links pointing at dead pages as reclaim1.0ms
puts the redirect at the top of the plan — nothing to ask anyone for0.4ms
Q09: what we cannot see stays unseen · 3 tests
never concludes how rivals earned their links0.5ms
never concludes anything about unlinked mentions0.2ms
distinguishes "no link data" from "no links"1.0ms
Q09: findNetworks prefers the narrower signal · 3 tests
groups on prefix rather than ASN when both exist0.3ms
falls back to ASN when no prefix is known0.2ms
ignores domains with neither signal0.2ms
Q09: no internal vocabulary reaches the user (GS-005) · 2 tests
keeps table and field names out of the prose0.7ms
states the low-authority threshold as a judgement, not a field0.3ms
competitor-gap.vitest.ts
18/18 33ms · 3 suites PASS
src/seo/competitor-gap.vitest.ts
computeKeywordGap — the subtraction · 6 tests
removes keywords the tenant demonstrably ranks for3.6ms
removes keywords the tenant merely tracks, too — tracking one means it is not news0.7ms
matches on the normalized key, so casing and punctuation cannot split one keyword in two1.6ms
de-duplicates the competitor list — a provider can return one keyword per match type0.6ms
preserves provider order and the full row, so volume/difficulty survive to the table1.5ms
drops empty/unusable competitor keywords rather than counting them as gaps0.7ms
computeKeywordGap — basis is the honesty gate · 7 tests
is `measured` only when GSC ranking data exists0.9ms
is `registry_only` when we hold tracked keywords but no ranking evidence0.7ms
is `unknown` when we hold nothing — an empty registry is not evidence of a gap0.6ms
counts the union, so a keyword in both sets is not compared against twice1.7ms
is `read_failed`, not `registry_only`, when the GSC read errored and a registry exists0.8ms
is `read_failed`, not `unknown`, when the GSC read errored and nothing else is held0.2ms
a failed read still subtracts the registry — an interest list is valid either way0.3ms
gapBasisNote — the disclosure that must travel with the gap · 5 tests
states the measured baseline size16.0ms
says plainly that a registry-only gap is NOT a ranking claim0.6ms
refuses to call an unknown baseline a gap0.3ms
says "could not be read" for read_failed, never "isn't connected"0.6ms
is grammatical at n=1 — singular/plural is user-visible copy0.3ms
content-keyword-join.vitest.ts
18/18 28ms · 4 suites PASS
src/seo/content-keyword-join.vitest.ts
the stance is decided by POSITION, and by nothing else · 6 tests
calls a striking-distance term improve, not new2.6ms
calls a top-3 term defend — a new page competes with the winning one0.3ms
calls position 44 new — far enough that writing beats re-pointing0.3ms
does NOT call a tracked-but-unranked term improve — tracking is an intention0.4ms
matches on the normalized key, so casing and punctuation cannot split one term in two0.4ms
keeps the measured row when two entries normalize to the same key0.4ms
nothing is ever dropped · 4 tests
returns every idea it was given, whatever the stance1.1ms
re-ordering preserves every idea and is stable within a stance0.5ms
inverts for the long game, and leaves an unset horizon untouched0.4ms
puts defend last under BOTH horizons — real information, nobody's next action0.4ms
the basis is stated, because a count off no data is wrong rather than small · 5 tests
is measured when any row carries a position0.2ms
is registry_only when rows exist but none is ranked0.2ms
is unknown with nothing to compare against1.5ms
refuses to claim anything when the basis is unknown0.9ms
names the weaker basis rather than presenting it as a measurement16.7ms
the note says the thing the tool got wrong before · 3 tests
names the already-ranking term and its position, and says re-point0.6ms
warns about competing with your own top-3 page0.3ms
says nothing when there are no ideas0.2ms
cron-balance-guard.vitest.ts
18/18 25ms · 4 suites PASS
src/seo/cron-balance-guard.vitest.ts
cron balance guard · 7 tests
both scheduled spend paths call the guard2.8ms
the keyword guard runs BEFORE the paid SERP call0.7ms
the SOV guard runs BEFORE the composite tool call0.4ms
a balance-lookup failure blocks the run rather than authorising it0.4ms
a balance-lookup failure is reported, not swallowed silently0.5ms
a blocked run tells the user — activity + email, deduped per week0.3ms
the email template exists and names which tracking paused6.7ms
scheduled-run completion emails · 5 tests
the rank digest is gated on measurement, not on movement0.6ms
the rank digest still says something true when nothing moved1.0ms
a completed AI-visibility run emails the user unconditionally0.6ms
the prior score is read BEFORE the run overwrites it0.6ms
a first run is reported as a first run, not as an unchanged score0.3ms
guard phase is decoupled from the SERP scan budget · 3 tests
the guard phase has its own budget, separate from the SERP-scan budget0.2ms
every user in byUser gets a guard check before any user enters the SERP scan0.2ms
a guard-phase failure for one user is reported, not left to abort the batch0.2ms
cronBalanceGuard runtime behavior · 3 tests
sufficient balance: proceeds, tells nobody anything1.9ms
insufficient balance: blocks AND notifies — the exact path that had a 0% send rate5.4ms
a throwing balance lookup: blocks AND reports — never silent, never authorises spend1.1ms
dfs-location.vitest.ts
18/18 56ms · 6 suites PASS
src/seo/dfs-location.vitest.ts
resolveDfsLocation — country level · 2 tests
maps a supported ISO to its location_code3.2ms
is case- and whitespace-insensitive0.4ms
resolveDfsLocation — the silent-US defect · 3 tests
flags assumedUs for an unsupported country0.5ms
flags assumedUs when nothing at all is supplied0.4ms
does NOT flag assumedUs when the US was actually requested0.2ms
resolveDfsLocation — city level · 5 tests
emits DFS-format location_name for city + country0.3ms
includes the region when given, in DFS city,region,country order0.3ms
never emits location_name for a country it cannot NAME0.3ms
ignores a city with no country rather than guessing one2.8ms
treats a whitespace-only city as absent0.7ms
dfsLocationCode — back-compat · 2 tests
is unchanged for every supported ISO0.8ms
still returns 2840 for unknown, empty and absent input0.3ms
the two market tables agree · 2 tests
covers exactly the same ISO set0.3ms
supports city level for every market it supports at country level0.4ms
ai_visibility_check carries the location disclosure · 4 tests
emits label, assumed and city_level36.3ms
keeps location_assumed === false instead of collapsing it to null1.0ms
records a true assumption as true2.8ms
is null — not false — when the caller said nothing, so old runs are not counted as resolved2.6ms
link-verify.vitest.ts
18/18 22ms · 5 suites PASS
src/seo/link-verify.vitest.ts
relToVerdict · 1 test
reads the strongest credit-withholding token3.2ms
hostMatches · 2 tests
matches across www and subdomains1.0ms
does not match a different domain that merely ends similarly0.8ms
classifyLinkPage · 6 tests
finds the anchor and reads its rel2.1ms
reports no_link when the page does not link to us at all0.4ms
reports the STRONGEST link when a page links to us more than once0.4ms
handles single quotes, unquoted attributes and protocol-relative hrefs0.8ms
ignores relative hrefs — they cannot point at another domain0.3ms
strips markup out of the anchor text0.4ms
mergeVerdict — the two-pass rule · 3 tests
calls a page that blocks us but answers a browser BLOCKED, not dead and not dofollow0.5ms
calls a page that answers nobody dead0.9ms
uses the parse result when the page was readable0.5ms
summarizeVerification · 6 tests
excludes blocked pages from the dofollow denominator0.9ms
counts removed links and dead pages together as lost0.4ms
never claims a percentage when nothing was confirmed present6.3ms
says nothing is verified when no page could be read0.3ms
states the result is first-hand, unlike every provider-reported figure0.4ms
handles an empty run without dividing by zero1.3ms
visibility-movement.vitest.ts
18/18 14ms · 3 suites PASS
src/seo/visibility-movement.vitest.ts
the basis decides whether a delta exists at all · 11 tests
THE LIVE CASE: 29% over four engines vs 42% over three is NOT +134.5ms
same engine set — the delta is real and signed0.7ms
engine ORDER is not a basis change0.7ms
an engine ADDED is a basis change, not just an engine removed0.4ms
the same engines on DIFFERENT questions is not comparable0.6ms
question ORDER is not a change, but question MEMBERSHIP is0.6ms
case and surrounding whitespace do not make a new panel0.4ms
when BOTH halves change, the note says so rather than naming one0.3ms
an empty prompt set is never comparable, even to another empty one1.1ms
no previous run reads as a baseline, never as "no change"1.0ms
a genuinely unchanged rate says so rather than printing +00.4ms
what it will not do · 3 tests
never returns a delta when the current engine list is empty0.2ms
ignores a previous reading with no usable percentage0.3ms
the phrase never carries a number the movement says is absent0.6ms
the previous rate is read BEFORE the legs that overwrite it · 4 tests
previousCitationRate is called before the GEO leg dispatches0.6ms
there is exactly one call site — a second could sit on the wrong side0.3ms
a run compared with itself would report "no change", which is the symptom to recognise0.4ms
the REAL comparison for that run refuses a delta — 3 engines then, 4 now0.3ms
gc-safety.vitest.mjs
18/18 14ms · 5 suites PASS
scripts/lib/gc-safety.vitest.mjs
the safe case · 1 test
allows a prune when every worktree is idle3.7ms
uncommitted work blocks — this is the whole point · 2 tests
blocks on a single staged file0.8ms
names the offending worktree, not just "unsafe"0.5ms
recent activity blocks · 5 tests
blocks when an index was written inside the quiet period0.4ms
blocks on an index written THIS INSTANT — the real 2026-08-29 case0.4ms
allows once the index is older than the quiet period0.4ms
honours a caller-supplied quiet period, and defaults to 10 minutes0.4ms
does not block on a FUTURE index mtime (clock skew must not read as activity)0.3ms
locks and in-flight operations block · 6 tests
blocks when a lock file is present1.0ms
blocks during a rebase0.9ms
blocks during a merge0.3ms
blocks during a cherry-pick0.2ms
blocks during a bisect0.2ms
reports every independent reason rather than stopping at the first1.2ms
FAILS CLOSED — an unreadable state is never an all-clear · 4 tests
blocks when the dirty count could not be read0.8ms
blocks when the index mtime could not be read0.2ms
blocks when the gatherer returned NOTHING — a broken instrument is not an idle repo0.4ms
reports the unreadable worktree ONCE, without inventing derived reasons0.5ms
coverage.vitest.ts
18/18 14ms · 4 suites PASS
src/leads/shared/coverage.vitest.ts
classifyCoverage · 5 tests
calls an empty axis ABSENT — applying it can only return zero3.2ms
calls a minority axis PARTIAL — the filter works, the pool is thin0.8ms
calls a well-populated axis BROAD and says nothing0.6ms
treats exactly the threshold as broad0.4ms
ONE row is partial, not absent0.5ms
a stale measurement is not evidence · 3 tests
ages out to PARTIAL rather than to its last value0.4ms
does not guess BROAD when nothing has been measured0.4ms
treats an unparseable timestamp as stale rather than as now0.3ms
what the user is told · 5 tests
says we hold none of it when an axis is absent0.6ms
does NOT claim a partial filter was dropped1.4ms
quotes the WORST axis as an UPPER BOUND when several are partial0.6ms
uses the user’s words, not the column names1.3ms
says nothing at all when there is nothing to disclose0.2ms
the state note now reads the measurement · 5 tests
says we found nobody THERE, not that location targeting is unavailable0.7ms
STOPS claiming we have none once even a little exists0.4ms
still says a paid source would not add it — that part did not expire0.3ms
invents no percentage when coverage was never measured0.4ms
keeps outranking the empty-result explanations0.2ms
chat-anatomy.vitest.ts
17/17 154ms · 3 suites PASS
client/chat-anatomy.vitest.ts
environment · 1 test
has a DOM — otherwise every test below is vacuously absent5.3ms
decorateTraceBlock — the header says what it can prove · 7 tests
counts steps and states the elapsed the caller measured41.7ms
says "Worked" with no time rather than inventing one11.1ms
names failures in the collapsed state, where they are easiest to hide8.3ms
names unfinished steps too, and does not call them failures5.9ms
refuses to decorate a tree with no steps2.8ms
is idempotent — history replay must not stack two headers6.6ms
collapsing hides the tree and reports the state5.7ms
renderInsightCard — a card may not outlive its content · 9 tests
renders label, body and the metric with its own sign31.5ms
marks which metric is the tenant4.1ms
gives a rival NO delta element rather than a zero4.2ms
renders NOTHING when there is no text1.8ms
renders nothing for a null insight or a missing host2.7ms
is idempotent — history replay re-renders the same turn2.7ms
a caution is visually distinct from a rationale4.2ms
draws a sparkline only when there are at least two points9.5ms
carries the caveat when the card has one2.6ms
stripe-signature.vitest.ts
17/17 31ms · 4 suites PASS
src/billing/stripe-signature.vitest.ts
verifyStripeSignature — accepts only genuine, fresh signatures · 8 tests
accepts a correctly signed, current payload12.7ms
rejects a signature made with the WRONG secret2.0ms
rejects when the BODY was altered after signing1.3ms
rejects a stale signature outside the tolerance window (replay)1.3ms
rejects a FUTURE timestamp too — tolerance is absolute, not one-sided0.5ms
rejects a missing, empty or malformed header rather than throwing0.6ms
rejects a non-numeric timestamp instead of treating NaN age as in-window0.3ms
rejects a truncated signature that is a PREFIX of the real one1.7ms
isTopUpPack · 1 test
rejects values that are not declared packs, including prototype keys1.4ms
top-up packs · 3 tests
offers exactly $10 / $50 / $100, priced at the flat retail rate3.2ms
serves the list and the sentence from that one table1.4ms
accepts only the three live ids0.6ms
isOurCheckoutSession · 5 tests
claims a session that points back at our own success_url0.5ms
does NOT claim a sibling product on the same account — the exact 2026-09-13 session0.4ms
still claims OUR session when the metadata is gone — the case the alarm exists for0.4ms
claims our host however it is cased — a false negative here silences the alarm0.3ms
refuses every host that merely CONTAINS ours0.8ms
usage.vitest.ts
17/17 19ms · 5 suites PASS
src/billing/usage.vitest.ts
search_leads TOOL_COST_ESTIMATE · 2 tests
tokens = ORCHESTRATION_FLOOR + provider cost, with the LLM term declared separately4.0ms
maxTokens (local-business ceiling) uses the same floor on the $0.40 worst case0.6ms
FREE_TIER_LEAD_SEARCH_CAP · 1 test
is 100.8ms
searchLeadsEstimate · 6 tests
quotes a people search as a RANGE from the corpus rate to the provider rate1.2ms
scales BOTH ends of the range with the requested count0.7ms
keeps the full local-business worst-case ceiling for a local search0.3ms
keeps the ceiling for a local search declared without a postcode0.4ms
never returns a lower ceiling than the base tokens figure0.8ms
the people-search ceiling is its OWN derived one, never the local-business table figure2.1ms
buildCostGateMessage / buildCostApprovalData — search_leads override · 4 tests
message quotes THIS search's own range, never the local-business ~2.0M1.5ms
message still carries the ~2.0M ceiling for a local-business search1.1ms
card totals reflect the override, not the flat TOOL_COST_ESTIMATE ceiling0.6ms
with no override, falls back to costOfCall — the SAME figure resolveCostApproval decides on, not a different (flat table) one0.7ms
searchLeadsEstimate — derived ceilings must reach the balance gate · 4 tests
marks per-argument estimates as ceilings so the static table cannot override them0.4ms
exposes a per-lead scale so a refusal can offer the size that fits0.5ms
prices the corpus rung per lead too, and far below the provider rung0.3ms
local business keeps the static table and claims no ceiling0.5ms
search-leads-payload.vitest.ts
17/17 21ms · 5 suites PASS
src/campaigns/search-leads-payload.vitest.ts
the keys that change what the turn MEANS survive the cut · 5 tests
keeps error, note and the in-flight markers readable at 24 leads4.2ms
keeps error, note and the in-flight markers readable at 100 leads2.0ms
puts error FIRST — it changes what every other key means1.5ms
keeps a paid run in flight visible, so the turn cannot be summarised as finished4.5ms
keeps the paid-shortfall block readable — it is what the escalation gate is built from0.8ms
the fixture matches production · 1 test
a lead is about a kilobyte, as measured — not the 293 chars this test first assumed0.4ms
the bulk goes last, where losing it costs least · 3 tests
all is the biggest key and is emitted after everything semantic1.6ms
the arrays are what overflow, not the semantics0.6ms
preview is a strict prefix of all, so dropping all loses no distinct row1.4ms
THERE IS NO SMALL-RESULT CASE · 3 tests
overflows the model window at THREE leads0.6ms
still shows every semantic key at three leads0.4ms
shows everything only when there are no leads at all0.3ms
the ordering function itself · 5 tests
is total — every key survives, exactly once, with its value untouched0.3ms
puts an UNKNOWN key in the middle, where the model can see it0.2ms
leaves a key it has never heard of alone when there is no bulk at all0.2ms
ignores classified keys that are absent0.2ms
classifies the keys this fix was written for0.6ms
email-identity.vitest.ts
17/17 10ms · 5 suites PASS
src/auth/email-identity.vitest.ts
canonicalAuthEmail — the credential · 3 tests
trims and lowercases2.0ms
PRESERVES gmail dots and +tags — they are part of the credential0.4ms
never throws on junk input0.4ms
normalizeEmail — dedupe/referral only · 4 tests
collapses gmail dots, +tags, and googlemail0.5ms
strips +tags but KEEPS dots on non-gmail domains0.3ms
handles an address with no @ without mangling it0.2ms
uses the LAST @ so a local part containing @ cannot shift the domain0.3ms
the two normalizers are NOT interchangeable · 2 tests
diverge on exactly the inputs that cause lockouts1.2ms
agree on a plain address, which is why the swap is easy to miss0.6ms
fraud checks · 3 tests
flags disposable domains case- and whitespace-insensitively0.3ms
does not flag an address with no domain0.3ms
catches self-referral regardless of case/whitespace, and tolerates no referrer1.0ms
presentableName · 5 tests
prefers a real stored name0.4ms
REJECTS the Nhost default, which is the email itself0.4ms
prettifies the local part when there is no usable name0.2ms
drops a +tag rather than rendering it as part of the name0.2ms
returns empty rather than a stray character when there is nothing to work with0.2ms
lifecycle-consent-userid.vitest.ts
17/17 9ms · 3 suites PASS
src/email/lifecycle-consent-userid.vitest.ts
every marketing lifecycle send identifies its recipient · 11 tests
inactive_3_day_reminder passes a userId2.5ms
unfinished_setup_nudge passes a userId0.4ms
weekly_product_progress_digest passes a userId0.3ms
seo_rank_digest passes a userId0.3ms
sov_weekly_digest passes a userId0.8ms
inactive_3_day_reminder is still marketing-class, so the userId is load-bearing0.5ms
unfinished_setup_nudge is still marketing-class, so the userId is load-bearing0.2ms
weekly_product_progress_digest is still marketing-class, so the userId is load-bearing0.2ms
seo_rank_digest is still marketing-class, so the userId is load-bearing0.3ms
sov_weekly_digest is still marketing-class, so the userId is load-bearing0.2ms
the ids come from the same value the dedupeKey already used1.1ms
the kinds that legitimately have no user id are operational · 3 tests
signup_validation_reminder is operational — it fires BEFORE an account exists1.0ms
all three waitlist kinds agree, because they share one pre-account audience0.3ms
the admin adoption sample refuses to send without one, rather than silently blocking0.4ms
the guard that would have caught it · 3 tests
check-email-consent asserts the CALLER, not only the gate0.2ms
it refuses to pass on an empty parse, in both directions0.2ms
it brace-matches the argument literal instead of slicing a window0.1ms
site-ownership-claim.vitest.ts
17/17 14ms · 2 suites PASS
src/chat/site-ownership-claim.vitest.ts
the claim we were deaf to · 7 tests
hears the live message3.1ms
does NOT change what firstTurnShape decides — that call is load-bearing1.9ms
accepts the ordinary ways people say it1.1ms
refuses a bare domain — "audit stripe.com" is a COMPETITOR audit0.6ms
refuses a THIRD PARTY possessive, however it is phrased1.0ms
refuses when BOTH readings are present — an ambiguous claim is not a claim0.3ms
is safe on junk input1.2ms
the write is guarded at the call site · 10 tests
only writes on an explicit claim0.5ms
only writes from the message typed THIS turn, never from history or a chip0.5ms
never overwrites a site the tenant already has0.5ms
applies the platform-profile guard — "my site" about facebook.com is not a website0.3ms
is best-effort — a failed write must not cost the audit the user asked for0.3ms
also builds the product brief, not just the site row0.2ms
does not re-scan a tenant that already has a brief0.3ms
writes the site BEFORE scanning, so a failed scan still leaves the domain saved0.3ms
the scan cannot cost the user the audit they asked for0.2ms
SAYS it read their site — a silent profile of someone's business is not acceptable0.2ms
icp-ledger.vitest.ts
17/17 14ms · 5 suites PASS
src/leads/icp-ledger.vitest.ts
what counts as a criterion · 2 tests
counts only the axes the user actually named6.9ms
keeps an axis the user named even when we can do nothing with it0.8ms
absent coverage is not a thin filter · 5 tests
offers the paid source for an axis a provider CAN answer0.9ms
does not offer to buy something nobody sells0.4ms
keeps the verdict and changes only the offer once the user has said yes to paid0.4ms
treats PARTIAL coverage as a filter that ran — because it did0.3ms
treats UNKNOWN coverage as a filter that ran, not as an absent one0.4ms
translation is not relaxation (owner correction) · 4 tests
calls a vocabulary translation EXACT and still says what was searched0.4ms
says nothing when the user’s word IS the stored value0.4ms
declares a term the vocabulary has never heard of, and does not offer to buy it0.6ms
does NOT declare the whole axis unmatched when only one of two terms missed0.3ms
the headline is the fraction · 5 tests
names the count, the fraction, and what was missed0.9ms
says nothing at all when everything the user asked for was honoured0.2ms
refuses differently when NOTHING could be matched0.2ms
drops the count when the caller has none to give0.4ms
uses the singular for one contact0.3ms
the per-axis view · 1 test
renders as bullets and marks each axis0.5ms
icp-suggestions.vitest.ts
17/17 205ms · 5 suites PASS
src/leads/icp-suggestions.vitest.ts
chips are built from the ICP, one title group each · 6 tests
yields at most three chips, each carrying the brief's evidence and executable arguments7.0ms
the label names every filter that will run and no number1.3ms
a dropped axis leaves BOTH the arguments and the label1.1ms
a thin brief or a brief with no buyer yields nothing — the fallback producer runs instead0.6ms
title groups: one each up to the cap, then two per chip in the extractor's order0.6ms
title case keeps acronyms and small words readable0.4ms
a click is matched to the stored label, however the client sends it back · 4 tests
normalises quotes, case, trailing punctuation and spacing0.9ms
round-trips through the session store and matches the clicked label1.6ms
pending arguments survive the picker turn and are cleared once read1.3ms
no session store → no match, no throw1.7ms
coverage decides what a chip may ask for · 1 test
drops an axis the corpus cannot serve, or serves for too little of it — never the title64.7ms
the scan entry point · 3 tests
uses the extractor when it reads a buyer, stores the chips, and returns the labels44.4ms
falls back to the persona-query producer when the brief has no buyer in it40.3ms
never throws — an extractor outage is the fallback, not a failed scan32.8ms
the chat router honours a clicked chip before any intent handler · 3 tests
matches the chip right after the intent match, gates the disambiguation on it, and routes it into the lead path1.0ms
the selection path runs the chip's stored arguments instead of re-structuring the sentence1.0ms
every scan site produces its chips through the extractor, with the old producer as fallback2.4ms
inventory.vitest.ts
17/17 8ms · 6 suites PASS
src/leads/inventory.vitest.ts
shouldSkipInventory · 2 tests
skips when the user explicitly asks for new/fresh/more contacts2.5ms
does not skip plain discovery queries0.3ms
buildInventorySignals · 1 test
lowercases + dedupes titles, drops query stopwords from keywords0.5ms
buildInventoryWhere · 4 tests
always scopes to the user and excludes verifier-rejected emails0.4ms
uses title synonyms and named domains as primary signals0.3ms
falls back to keyword terms only when no title/domain signal exists0.6ms
returns null on zero signal — never an unfiltered dump of recent contacts0.4ms
rankOwnedContacts · 3 tests
ranks title+verified matches above topic-only matches, tags _tier owned0.6ms
respects the limit0.2ms
topic hits are capped so they cannot outrank a title match0.3ms
rankOwnedContacts subject-relevance gate · 2 tests
title-only hit is excluded when the query has subject keywords it does not overlap0.3ms
title-only hit still qualifies when the query has NO subject keywords0.2ms
rankOwnedContacts industry gate · 5 tests
REGRESSION: an industry-filtered search does not return contacts from another industry0.3ms
matches by STEM, so "Dentists" evidences itself against "Dentistry"0.3ms
drops short words so "Oil and Gas" cannot let "and" vouch for the whole table0.2ms
an explicitly named domain still wins over the taxonomy0.2ms
no industry filter → behaviour is exactly as before0.2ms
provenance.vitest.ts
17/17 14ms · 5 suites PASS
src/leads/provenance.vitest.ts
a timestamp is evidence, so it rides only with a verdict · 2 tests
writes verified_at for an API verdict2.9ms
writes NO verified_at for a provider claim — the whole defect0.8ms
isApiVerified answers the question that decides money · 4 tests
is true only for a valid verdict we produced0.4ms
is FALSE for a provider claim, however confident it looks0.3ms
is FALSE for legacy rows, because they are indistinguishable from claims0.3ms
is FALSE for a verdict that came back bad0.2ms
the user-facing phrasing never overstates what we know · 4 tests
never calls a provider claim "verified"0.8ms
says "verified" only for a confirmed address0.3ms
is honest about legacy rows rather than silently promoting them0.9ms
names no vendor0.7ms
the vocabulary is closed · 1 test
has exactly the six values the columns may hold0.7ms
summariseVerification · 6 tests
counts a provider claim as unproven, never as checked0.9ms
forbids the deliverability claim outright when nothing was checked0.9ms
bounds the claim to the checked subset on a mixed set0.3ms
lets a fully checked set say so0.3ms
separates a bad verdict from an absent one0.3ms
handles an empty set without inventing a rate1.0ms
reply-signals.vitest.ts
17/17 17ms · 3 suites PASS
src/leads/reply-signals.vitest.ts
detectCompetitorMentions — the signal · 4 tests
finds a competitor named as a domain3.6ms
finds a competitor named as a proper noun1.7ms
derives the brand token from the domain when no name is stored0.9ms
reports each competitor once, however many times it appears1.3ms
detectCompetitorMentions — the false positives it must not produce · 5 tests
does NOT fire on a lowercase common-word use of a brand name0.3ms
does NOT fire on a short or generic stored name0.8ms
does NOT match a brand token inside a longer word0.3ms
returns nothing for an empty or unreadable body rather than guessing0.4ms
returns nothing when the tenant tracks no competitors0.3ms
replyBodyText — reading only what the human typed · 8 tests
drops headers, so our own infrastructure cannot register as a mention1.4ms
cuts quoted history, so OUR message coming back cannot trigger a mention1.7ms
cuts an Outlook-style original-message divider0.3ms
drops residual quote lines0.3ms
decodes quoted-printable, which would otherwise silently match nothing0.9ms
decodes base64 single-part bodies0.4ms
strips HTML tags rather than matching inside markup1.4ms
returns empty string for undecodable input — callers must read that as "could not read"0.4ms
godmode-phase2.vitest.ts
17/17 69ms · 6 suites PASS
src/reports/godmode-phase2.vitest.ts
google_god_mode_report — no internal vocabulary reaches the user (GS-005) · 3 tests
status pills name the operation, not the internal key or the vendor39.5ms
the lead no longer recites the vendor stack1.1ms
an error banner names the section in English, not by key0.8ms
google_god_mode_report — "unavailable" is not "error" (GS-004) · 3 tests
a section with no data renders neutral, not as a fault0.8ms
OUR unbuilt capability says so, and explicitly implies nothing about their site0.7ms
unavailable sections are still SAID — dropping them is the other half of the defect0.6ms
google_god_mode_report — one population per number · 2 tests
the session tiles state their scope0.7ms
the all-channel tile is omitted when we do not have the number, never zeroed0.7ms
google_god_mode_report — leads with a judgement (GS-001) · 3 tests
names the top problem, not the user's own counts1.5ms
a clean read says so rather than padding a finding (GS-009)4.6ms
the counts survive as context, with their scope attached0.9ms
sov_trend — a share is a claim about a field, not about one comparison · 3 tests
100% against ONE competitor is PROVISIONAL10.1ms
drops the caveat once the comparator set can support the claim1.2ms
names zero competitors honestly rather than saying "0 tracked competitors"1.0ms
sov_trend — the problem count counts THEIR site, not our settings · 3 tests
weekly tracking being off is not a signal needing attention0.8ms
the tracking prompt is still shown — demoted, not deleted0.9ms
a REAL problem still raises the count0.9ms
report-fix-prompts.vitest.ts
17/17 41ms · 4 suites PASS
src/reports/report-fix-prompts.vitest.ts
entity_audit report — density + fix prompts · 4 tests
renders every fetched KG field, not just the verdict8.2ms
surfaces errored queries instead of dropping them0.7ms
has a per-finding copy button for every non-recognized entity and none for recognized0.8ms
§17: signal-overview bento (per verdict), plain headers, feedback mount2.0ms
stack-aware prompts (fingerprint attached by dispatch) · 3 tests
entity_audit prompts carry Next.js placement when fingerprinted1.2ms
aeo_page_check header names the stack and placement (WordPress+Yoast)0.9ms
no fingerprint → generic phrasing, no stack claims0.7ms
seo_write_content — publish routing by connector + stack · 6 tests
connector live → still no baked buttons; connector state must not reach stored HTML19.7ms
WordPress site, no connector → slot + stack passed through for hydration1.7ms
non-WordPress stack → stack-aware coding-agent fallback, no publish markup1.0ms
legacy result without wp_connected flag → NO optimistic one-click publish0.5ms
is a publish-first deliverable: no findings bento, no KPI tiles / Q&A grid, article + publish + feedback0.6ms
renders GFM: bold, links, ordered lists, fenced code, and tables (no raw markdown)1.0ms
aeo_page_check report — per-finding + top-level fix prompts · 4 tests
shows the all-fixes button at the top and per-cluster buttons0.3ms
gives every failed finding its own copy button, pass rows none0.2ms
prompts are self-contained (carry the page URL and the finding)0.2ms
§17: signal-overview bento (3 dimensions), plain headers, feedback mount0.4ms
blocked-outcome.vitest.ts
17/17 21ms · 2 suites PASS
src/runtime/blocked-outcome.vitest.ts
isBlockedOutcome — deliberate stops · 14 tests
cost gate (the screenshot) is blocked, not failed7.5ms
clarify question is blocked, not failed0.6ms
picker is blocked, not failed0.4ms
balance gate is blocked, not failed0.3ms
plan/test cap is blocked, not failed0.3ms
zero-yield breaker is blocked, not failed0.2ms
plan-cap uplift is blocked, not failed0.2ms
plan cap with NO upsell to offer is blocked, not failed0.2ms
connector gate is blocked, not failed0.5ms
a genuine provider failure is NOT blocked3.5ms
a thrown-shaped error is NOT blocked3.1ms
a plain result is NOT blocked0.1ms
null is NOT blocked0.2ms
recognises a marker-less expected refusal by its prose0.2ms
a gate turn offers ONE action · 3 tests
renders no failure chips under an approval card1.9ms
lets a clarify question keep its own suggestion chips — they ARE the answer path0.3ms
leaves genuine failures their recovery chips0.3ms
comparison-prompts.vitest.ts
17/17 29ms · 5 suites PASS
src/seo/comparison-prompts.vitest.ts
the source taxonomy keeps "unclassified" visible · 4 tests
never calls an unrecognised domain a vendor5.1ms
a rival is only ever one the tenant NAMED0.6ms
classifies the categories it does recognise, including subdomains0.7ms
counts CITATIONS, not distinct domains0.5ms
comparison intent · 2 tests
catches how buyers really write it, both directions0.4ms
does not swallow ordinary questions0.4ms
the real shape: everything ruled out is the ANSWER · 5 tests
reads only the comparison prompts, not the whole panel1.1ms
the comparison rate is reported AGAINST the panel-wide rate0.7ms
the listicle premise is RULED OUT, with the number0.7ms
does not start a listicle programme, and says why1.5ms
the honest coverage statement travels WITH the recommendation0.3ms
when the findings do hold · 3 tests
absence from comparisons survives and leads the decision8.1ms
a review-platform category flips the listicle premise3.1ms
rivals out-citing us survives only when the tenant NAMED them1.1ms
what it refuses to say · 3 tests
a panel with NO comparison prompt says so, and names the fix0.7ms
the constraint-table and treadmill hypotheses stay untested0.6ms
a high unclassified share is disclosed in the ask0.6ms
competitor-set.vitest.ts
17/17 22ms · 5 suites PASS
src/seo/competitor-set.vitest.ts
the reference-host screen covers the class that leaked, not just the instances · 7 tests
screens out sk.sagepub.com3.8ms
screens out emerald.com1.0ms
screens out ideas.repec.org0.4ms
screens out www.tandfonline.com0.5ms
screens out onlinelibrary.wiley.com0.4ms
does NOT screen out the real rivals0.7ms
cannot catch forensicsciencesimplified.org, and is not expected to0.8ms
filterDerivablePool — one definition of "surviving candidate" · 2 tests
drops us, our subdomains, and screened hosts; ranks by citation count1.3ms
normalizes before comparing, so one rival cited two ways is one candidate0.3ms
a stored AUTO set is not permanent · 4 tests
is replaced when the run cites a field it shares nothing with5.7ms
is KEPT when even one stored rival is still cited — a partly-right set is not churned1.4ms
is KEPT when the run surfaced too thin a pool to overturn it0.8ms
is KEPT when there is no pool at all (a run where nobody was cited)0.7ms
a USER-confirmed set is never second-guessed · 3 tests
survives zero overlap with a broad pool0.9ms
protects an auto entry sitting alongside a user one0.7ms
an explicit argument still wins over everything and becomes the durable set0.5ms
the screen and the re-derive agree · 1 test
never installs a domain that would have counted as overlap1.1ms
content-pieces.vitest.ts
17/17 28ms · 5 suites PASS
src/seo/content-pieces.vitest.ts
saveContentPiece · 5 tests
records the piece and returns its id8.2ms
never throws — the article is already in the user's hands0.9ms
returns null rather than a fabricated id when the insert returns nothing1.2ms
refuses a row that could never be read back or would read as a real piece1.0ms
a rewrite carries source_url and no keyword0.6ms
listContentPieces · 3 tests
is scoped to the tenant AND the site, newest first0.9ms
does NOT select body — a listing must not drag every draft across the wire0.5ms
clamps the limit and degrades to empty rather than throwing1.9ms
priorPiecesForKeyword · 2 tests
matches case-insensitively — the same term typed twice is the same term0.6ms
an empty result and a failed read are indistinguishable, so it may only ever SOFTEN0.8ms
markContentPiecePublished · 5 tests
records the URL and how we know it2.0ms
scopes the write to the tenant, not just the row id0.3ms
refuses a URL without provenance, matching the CHECK constraint0.7ms
reports false when no row matched, rather than implying success0.3ms
never throws — the article is already live on their site5.3ms
saveContentPiece persists the brief · 2 tests
writes the brief verbatim alongside the article0.5ms
records ABSENCE as null, so it never reads as a recorded-but-blank brief0.6ms
cross-audit.vitest.ts
17/17 19ms · 3 suites PASS
src/seo/cross-audit.vitest.ts
cross-audit — the join between on-page and off-page · 9 tests
finds an inbound link landing on a dead page — a fault neither half can see alone7.0ms
finds authority landing on a page Search Console reports as not indexed1.1ms
ranks on-page issues by what the page actually earns — the stake the on-page audit lacks0.8ms
promotes a ranking page to corrective only when the fault is blocking0.6ms
every finding is legal on the provenance axis (FR-031)0.7ms
emits NOTHING when either half failed — a one-sided join is a fabrication1.1ms
matches URLs across providers that disagree about protocol, www and trailing slash0.8ms
flags an anchor/ranking divergence as advisory, never as something to go fix0.4ms
does not flag a divergence when the anchors and the rankings share terms2.5ms
cross-audit — locating the constraint · 4 tests
names authority when the pages are clean and the site has none0.7ms
names the pages when there is authority going unconverted0.3ms
orders the work when both are weak — pages before links0.3ms
refuses to name a constraint it cannot measure0.4ms
cross-audit — gaps are consequences, never plumbing (GS-004/GS-005) · 4 tests
reports a failed half as what the report cannot answer0.5ms
never names a sub-audit, a cache, a step or a vendor0.5ms
says an unconnected Search Console is why unindexed pages cannot be found0.2ms
reports missing link destinations rather than silently finding nothing0.2ms
eeat-proof.vitest.ts
17/17 24ms · 5 suites PASS
src/seo/eeat-proof.vitest.ts
Q24: the takedown clause · 5 tests
says TAKEDOWN, not refresh, when pages are unattributed4.7ms
states the clause CONDITIONALLY — it never asserts the tenant is YMYL0.8ms
never decides whether the tenant is YMYL0.7ms
asks the one question that changes the work0.4ms
does not raise takedown when nothing is unattributed0.6ms
Q24: the two-source rule is structural · 4 tests
will not confirm the authorship gap on the content check alone0.9ms
confirms it once both readings are present1.0ms
leaves the off-site gap untested when the brand was never read0.8ms
surfaces a weak brand as a gap no page edit can close1.3ms
Q24: never looked is not the same as nothing wrong (GS-004) · 4 tests
says so plainly when no content check exists1.2ms
reports a clean result differently from an unmeasured one0.4ms
keeps the editorial-process hypothesis permanently untested1.0ms
never kills the about-page mismatch, because nothing compares those pages0.9ms
Q24: gaps name work, not categories (GS-005) · 2 tests
uses no internal field names in user-facing text0.8ms
every gap carries an owner and a named fix0.6ms
Q24: a cause the evidence contradicts is RULED OUT, not "not tested" · 2 tests
kills the authorship hypothesis when both readings exist and the pages are clean5.5ms
thin reasons name WHICH reading is missing rather than asserting a clean result1.0ms
onboarding-candidates.vitest.ts
17/17 20ms · 4 suites PASS
src/seo/onboarding-candidates.vitest.ts
onboarding competitor candidates · 7 tests
captures the category owners ranking for the tenant's own niche term5.0ms
never offers the tenant their own site as a competitor0.7ms
screens out the directories a category SERP is full of0.4ms
does not re-offer a domain already on the confirmed list0.5ms
caps the list — this is a confirmation prompt, not an inventory1.8ms
keeps TRUE SERP position, so "who owns this category" is not reordered0.4ms
yields nothing from an empty SERP rather than inventing a candidate0.4ms
the candidate key is readable · 2 tests
__competitor_candidates__ is registered in SETTING_KEYS1.1ms
candidates are a SEPARATE key from the confirmed set0.9ms
the reference class the screen missed · 3 tests
screens a college library guide hosted on a .com0.6ms
still admits the three that were actually right0.3ms
drops it from a candidate list without disturbing the rest0.6ms
the confirmation gate is wired end to end · 5 tests
reads a real file, not an empty string that passes everything0.4ms
GET /api/product/brief returns the candidates2.4ms
PUT /api/product/brief writes back what is still pending0.5ms
the panel renders them with an accept and a dismiss2.6ms
accepting one promotes it as user-sourced, so auto-derive cannot screen it away0.4ms
trend-series.vitest.ts
17/17 15ms · 3 suites PASS
src/seo/trend-series.vitest.ts
buildTrendSeries · 6 tests
one measurement is a point, not a direction (the competitor defect)3.0ms
drops unusable rows rather than defaulting them to zero1.1ms
sorts oldest-first however the caller supplied them0.6ms
identifies gaps that are long RELATIVE to this series cadence0.9ms
an outlier cannot raise the threshold it is judged against0.5ms
two runs a day apart are not a "long gap" whatever the multiple says0.3ms
renderTrendSvg · 7 tests
positions X by real elapsed time, not by run index2.8ms
draws the unknown stretch dashed, and says so in the tooltip1.7ms
Y starts at zero so a small change cannot be cropped into a collapse0.9ms
does not distort its own marks0.3ms
is self-contained — an exported artifact has no access to the panel stylesheet0.4ms
the axis states how many days carry data (AEO-013)0.4ms
a steady series gets no dashed-gap sentence it did not earn0.2ms
sov_trend artifact (R-E acceptance) · 4 tests
is no longer an evenly-spaced bar chart of user-initiated runs0.3ms
states its coverage and marks the unknown stretch0.2ms
keeps the set-changed warning, as a ringed point rather than an asterisk0.2ms
a single-run history renders the honest empty state, not a line from zero0.5ms
us-locality-extract.vitest.ts
17/17 13ms · 5 suites PASS
src/leads/shared/us-locality-extract.vitest.ts
the copied normalisers stay byte-identical to the ingest · 4 tests
canonicalLinkedIn has the same body in both files2.8ms
text has the same body in both files0.5ms
produces the canonical form the corpus stores in contact_identifier.normalized_value1.0ms
rejects anything that is not an /in/ profile0.6ms
columns are resolved by name, never by position · 2 tests
matches case-insensitively and ignores surrounding space0.4ms
returns -1 for a column the file does not have0.3ms
the misaligned-row anchor · 7 tests
reads a well-formed row1.8ms
accepts a row damaged AFTER the anchor — the prefix is provably intact0.7ms
REJECTS a row shifted before the anchor, rather than writing a region into locality1.8ms
REJECTS a shift that lands a company URL on the anchor0.9ms
drops a row too short to reach either column0.4ms
drops a row with a profile but no locality0.3ms
lowercases and collapses whitespace, because locality is a FILTER KEY0.3ms
the extractor is importable without running · 2 tests
guards main() on being the entry point0.5ms
renames the output only on success, so a killed run cannot look complete0.2ms
the ingest no longer claims the US export lacks Locality · 2 tests
does not repeat the false ABSENT-in-the-US comment0.2ms
still reads the column0.3ms
cost-based-billing.vitest.ts
16/16 17ms · 6 suites PASS
src/billing/cost-based-billing.vitest.ts
the ×10 margin invariant · 3 tests
P / V is exactly 102.4ms
any cost billed through the rule yields exactly ×10 revenue0.6ms
LLM and provider spend use the SAME conversion (no separate margin)0.4ms
cost-based vs legacy billing, per model · 2 tests
legacy over-charges cheap models and under-charges expensive ones0.5ms
cost-based lands every model on 10x0.4ms
free-model cost floor · 4 tests
imputes the owner-set floor for a $0/$0 model instead of charging nothing0.3ms
uses the exact owner-specified rates ($0.019 in / $0.30 out per 1M)1.1ms
a free-model turn still bills a non-zero token amount at 10x0.3ms
does NOT apply the floor to a merely-cheap model (only $0 on BOTH sides)0.3ms
billingModel gate · 2 tests
defaults to legacy (no live change without an explicit flip)0.6ms
accepts the documented on value0.5ms
credit preservation under cost-based billing · 3 tests
a granted balance with no spend is fully available0.2ms
spend is deducted from the grant at cost×10, not at raw token count0.2ms
credits never silently vanish when cost_usd is 0 on the grant rows0.5ms
admin console tracks the live billing model · 2 tests
legacy revenue is token-count driven7.6ms
cost mode reports revenue as cost × 10, restoring a true margin0.3ms
credit-invalidates-balance.vitest.ts
16/16 13ms · 4 suites PASS
src/billing/credit-invalidates-balance.vitest.ts
A — the admin grant · 3 tests
invalidates the cached balance2.2ms
reads the post-grant balance, not a cached one0.5ms
invalidates BEFORE it reads — order is the whole property0.4ms
B — the Stripe top-up · 3 tests
goes through the shared helper, not a copy of the key0.3ms
no longer hand-rolls the cache key0.4ms
and usage.ts is still the only place that spells the key2.1ms
C — the agent may not quote a balance it did not read · 6 tests
injects the current balance as ground truth every turn0.5ms
forbids quoting a balance from earlier in the conversation0.6ms
forbids claiming a check that did not happen0.4ms
sends spend-history questions to the ledger, not to this figure0.6ms
says nothing at all when the read fails0.6ms
is fetched in the same round as the other per-turn ground truth0.7ms
D — the AppSumo redemption grant · 4 tests
invalidates the cached balance after crediting0.9ms
invalidates AFTER the credit row is written — order is the whole property0.3ms
uses the shared helper, never its own key1.2ms
does not mark an AppSumo buyer as paying — they paid AppSumo, not us0.2ms
spend-blocked.vitest.ts
16/16 179ms · 4 suites PASS
src/billing/spend-blocked.vitest.ts
the gate records that spend is blocked · 2 tests
sets the flag on refusal, and only on refusal5.5ms
is reset per request, so one broke turn cannot mute the next1.2ms
the loop stands down instead of retrying · 5 tests
strips only the PRICED tools12.1ms
leaves the free tools, which are what a broke turn should fall back on14.8ms
does NOT exit the loop — that would ship the silent-turn fallback10.2ms
fires at most once per run29.1ms
tells the model to name the cheapest thing that WOULD fit11.9ms
the APPROVAL gate stands down the same way — one behaviour, two entry points · 4 tests
calls the SAME function rather than re-implementing the strip10.1ms
keeps the refusal text as the FALLBACK, not as the answer12.0ms
continues the turn when something free survived16.1ms
and the stand-down tells the model to do the free part first11.9ms
the turn has a ceiling of its own, and it is the 100K the owner set · 5 tests
is the same 100K, not a second opinion0.6ms
is checked BEFORE a round, not after9.4ms
stands the turn down rather than breaking out of it10.0ms
fires once, and never on the first round11.8ms
tells the model to answer and to name what it skipped10.2ms
bug-reports.vitest.ts
16/16 75ms · 3 suites PASS
src/admin/bug-reports.vitest.ts
list · 5 tests
REJECTS an unknown status by name instead of returning an empty list49.0ms
reports the TRUE total from the aggregate, not the length of the page4.3ms
still renders the list when the badge counts fail, and admits the counts are unknown2.6ms
names BOTH causes when the table cannot be read1.8ms
403s without the admin secret0.9ms
update · 7 tests
stamps triaged_at and resolved_at when a report is closed2.6ms
CLEARS both timestamps when a report is reopened2.4ms
clears resolved_at when moving back to a non-terminal state1.3ms
rejects an unknown status rather than writing it and failing the CHECK constraint0.9ms
an empty note CLEARS the field rather than being ignored as falsy3.3ms
refuses an update that changes nothing0.8ms
404s on an id that does not exist0.9ms
screenshot · 4 tests
404s when the report has no screenshot0.9ms
410s — not 404 — when the object has aged out of its retention window0.6ms
serves the bytes privately, never with a public cache directive0.7ms
503s with a named cause when storage is not configured0.4ms
build-stamp-alert.vitest.ts
16/16 12ms · 2 suites PASS
src/admin/build-stamp-alert.vitest.ts
build_id must be a commit sha · 12 tests
a real 40-char sha raises nothing3.6ms
THE INCIDENT: a semver raises a critical alert naming the value0.7ms
a NULL stamp fires — that is how the incident actually presented0.5ms
but a caller that never asked about builds gets NO alert0.4ms
rejects abc1230.3ms
rejects dfb12e320dd7bde52c490ac360a2f08380e491b0.4ms
rejects dfb12e320dd7bde52c490ac360a2f08380e491b8a0.3ms
rejects DFB12E320DD7BDE52C490AC360A2F08380E491B80.2ms
rejects not-a-sha0.9ms
rejects dfb12e320dd7bde52c490ac360a2f08380e491b8 0.3ms
is CRITICAL, because it invalidates every other signal on the page0.7ms
reads the worker's OWN stamp rather than fetching /api/version0.7ms
the deploy boundary refuses an unstamped ship · 4 tests
npm run deploy runs the precondition FIRST0.4ms
the stamp flag is no longer OPTIONAL0.6ms
the precondition exits non-zero and demands a lowercase 40-hex sha0.8ms
it points at the resolver instead of just refusing0.5ms
forecasting.vitest.ts
16/16 12ms · 3 suites PASS
src/commerce/forecasting.vitest.ts
poissonTailAtLeast · 3 tests
degenerate cases2.5ms
matches known Poisson values0.5ms
monotone: more stock → lower stockout probability0.5ms
computeVariantRisk · 8 tests
gates insufficient history honestly, stating exactly what is missing0.7ms
computes demand rate, cover, and Poisson stockout probability0.5ms
deep stock → healthy or overstocked, never fabricated risk0.4ms
untracked inventory is labelled, not guessed0.4ms
horizon constant sanity0.3ms
reorder suggestion targets 30 days of cover at the observed rate0.4ms
reorder is zero when cover already exceeds the target, and zero for untracked stock0.9ms
without a selection, the row is labelled as the rate baseline0.2ms
selectDemandModel (champion/challenger ladder) · 5 tests
short history → baseline, challengers never compete0.8ms
strong weekly pattern → seasonal challenger promoted via backtest0.9ms
structureless demand → champion retained with an honest reason0.5ms
crostonRate: intermittent demand estimated, single-demand series refused0.3ms
selection drives the risk math when supplied1.2ms
send-readiness.vitest.ts
16/16 12ms · 4 suites PASS
src/email/send-readiness.vitest.ts
counting — mirrors the send loop, or the preview lies in the other direction · 3 tests
counts an invalid verdict and an unsubscribe as blocked, exactly as the send loop skips them2.7ms
a blocked recipient is NOT also counted unverified — one recipient, one fact0.6ms
treats a PROVIDER's claim of 'valid' as unverified, because it is0.9ms
restraint — when it must say nothing · 4 tests
silent on an all-verified batch0.4ms
silent on an empty batch rather than reporting a 0-of-0 problem0.3ms
silent on a SMALL unverified batch — mailing two people you know about is normal0.3ms
silent when unverified addresses are a minority, even in a large batch0.4ms
the finding — what it says when it does speak · 6 tests
states the number that will ACTUALLY send, not just the number skipped0.6ms
says plainly when nothing would go out at all0.6ms
folds the unverified risk INTO the blocked sentence when it dominates — one finding, one chip0.5ms
reproduces the live tenant shape: 186 recipients, 3 blocked, 183 unverified1.7ms
does NOT add the deliverability clause when the sendable remainder is mostly verified0.4ms
warns on a majority-unverified batch and offers verification as the fix1.0ms
the rules every adoption inherits · 3 tests
never offers a "send anyway" chip — the preview's own confirm IS that0.4ms
never names a vendor and never prices in dollars (CLAUDE.md §4)0.4ms
phrases the finding as an observation before a decision, never a refusal0.2ms
corpus-criteria.vitest.ts
16/16 20ms · 4 suites PASS
src/leads/corpus-criteria.vitest.ts
the denominator is what the user named · 3 tests
counts every axis they filled and nothing else3.2ms
has no criteria at all for a local business search0.5ms
does not treat a state as one of the criteria0.4ms
the finding · 7 tests
names the fraction and both missing axes on the real 2026-08-22 request3.6ms
is in the FUTURE tense, because nothing has run yet1.3ms
offers the free repair first and the paid one second0.9ms
does not offer paid sources to someone who already said yes1.8ms
SAYS NOTHING when every criterion can be matched0.7ms
says nothing when the user named no criteria at all0.3ms
says nothing when coverage has never been measured0.6ms
coverage that is thin but real is a different sentence, not the same one · 5 tests
treats 13% industry coverage as a filter that RUNS0.6ms
still says the pool is thin, because 13% presented as the whole corpus is the same lie1.4ms
offers no narrowing chip for a thin pool — every filter named will run0.6ms
will not quote a percentage from a measurement that has expired1.1ms
keeps the combined note inside the card’s 400-character ceiling1.7ms
R4 — the one case we refuse · 1 test
changes the sentence entirely when nothing at all can be matched0.8ms
scan-identity-claim.vitest.ts
16/16 108ms · 3 suites PASS
src/leads/scan-identity-claim.vitest.ts
the scan decides whose site it is before it writes · 6 tests
reads the ESTABLISHED site rather than trusting the host it was handed3.8ms
treats a subdomain of the established site as the same site, in BOTH directions0.7ms
gates EVERY identity write, not just the visible one0.5ms
the FAILURE path is gated too0.7ms
still returns the research answer it was asked for0.4ms
discloses in DATA, never in a directive the model would recite0.5ms
only a real ownership assertion may replace an established site · 5 tests
the "that site is my company" chip claims — it IS the consent1.7ms
a first-turn SITE OFFER claims0.6ms
the settings form and the admin rescan claim0.8ms
the AEO scan OFFER does NOT claim — its domain came from the question0.6ms
the model cannot make the claim: it is not a tool argument at all2.2ms
numotocare scanning talkeriq.com — the reported defect · 5 tests
does not repoint __site_url__, and writes no identity at all88.1ms
still answers the question it was asked2.9ms
WITH the ownership claim, it writes — the user said it is theirs1.7ms
a tenant with NO site established is unchanged — onboarding still works1.0ms
refreshing their OWN site still writes, subdomain included1.0ms
attention.vitest.ts
16/16 80ms · 2 suites PASS
src/runtime/attention.vitest.ts
the side-effect axis · 6 tests
is separate from the cost axis — a free tool can still be consequential71.7ms
is separate from the cost axis in the other direction — a priced tool can be a pure read0.9ms
classifies arming a send the same as sending0.6ms
treats stopping sends as safe, unlike starting them0.4ms
treats an unknown tool as consequential, not as a read0.4ms
never writes unclassified down as if it were a decision1.2ms
gateForAttention · 10 tests
never blocks an interactive turn, whatever the effect0.8ms
lets an unattended run read freely — that is what makes diagnose safe on a schedule0.4ms
blocks a consequential tool when the run has no grant at all1.1ms
allows exactly what the grant names0.3ms
blocks a tool the cron does not currently call but might tomorrow0.2ms
names the tool and the authority so a block is reviewable0.2ms
refuses an external tool listed in the ordinary tool list, and says why0.2ms
allows an external tool only when named in externalTools0.3ms
ships with no grant that authorises an external action0.8ms
blocks an unclassified tool even under a grant that does not name it0.3ms
refund-eligibility.vitest.ts
16/16 8ms · 3 suites PASS
src/runtime/refund-eligibility.vitest.ts
zero yield · 6 tests
counts a lead search that found nothing2.4ms
does NOT count a lead search that found something0.3ms
does NOT count an errored call — that is already the error counter’s job0.4ms
does NOT count a clean audit that legitimately found zero0.4ms
treats a MISSING count as "no claim", not as zero0.4ms
keeps the eligible set small and deliberate1.0ms
refund eligibility · 5 tests
offers the claim when the only call found nothing0.4ms
still offers it when every call errored — the original behaviour, unchanged0.2ms
offers it when one call failed and the other yielded nothing0.3ms
does NOT offer it when something on the turn actually delivered0.3ms
does NOT offer it on a turn that ran no tools at all0.2ms
endedByAsking · 5 tests
does not offer a refund on the same card that asks for money0.2ms
suppresses a refund on an ERRORED turn that ends in a gate too0.2ms
still refunds a zero-yield turn that ends with an ANSWER0.2ms
treats a missing flag as "did not ask", so existing callers are unchanged0.2ms
does not resurrect a turn that was never eligible0.2ms
deliverability-wave7.vitest.ts
16/16 18ms · 4 suites PASS
src/tools/deliverability-wave7.vitest.ts
cloudflare_fix_email_dns · 9 tests
is external and unpriced — no cost gate stands in front of it2.7ms
accepts the three fixes the code can actually apply1.8ms
rejects mx_missing, which the code cannot perform1.8ms
rejects dkim_missing, which the code cannot perform0.8ms
rejects spf_multiple, which the code cannot perform0.3ms
rejects anything_at_all, which the code cannot perform0.3ms
accepts the empty call — omitting fixes applies every safe issue found0.3ms
rejects the retired domain alias0.4ms
the enum matches the branches the implementation has1.2ms
domain_email_readiness_audit · 3 tests
accepts the empty call and its two flags0.4ms
rejects the retired aliases0.3ms
no longer reads the undeclared fallback flag3.4ms
generate_dns_fix_prompt · 3 tests
accepts the empty call and a focused issue0.6ms
takes any issue id, because it only produces text0.2ms
rejects the retired domain alias0.2ms
the alias era is over · 1 test
no alias seams remain in the drift ledger1.8ms
generate-emails-schema.vitest.ts
16/16 40ms · 4 suites PASS
src/tools/generate-emails-schema.vitest.ts
the arguments the dispatch has always read are now declarable · 5 tests
declares contact_ids, which the dispatch reads and the registry omitted2.3ms
declares list_names, which the dispatch reads and the registry omitted0.2ms
declares campaign, which the dispatch reads and the registry omitted0.2ms
still declares the three it always had0.3ms
lets the model target specific contacts, several lists, or a campaign2.1ms
targeting is constrained · 6 tests
accepts a realistic single-list call with an angle0.4ms
rejects an invented contact id shape rather than dropping it silently at save time0.5ms
rejects a mode outside the two the dispatch implements0.6ms
rejects an off-schema field instead of silently ignoring it0.5ms
states that targeting fields are mutually exclusive1.2ms
rejects a sentence long enough to be obviously not a list name0.4ms
the dead shortcuts are gone · 3 tests
draft_for no longer claims its protocol message19.1ms
draft_mode no longer claims its protocol message8.1ms
draft_contact still fires — it is live and parses only a uuid2.0ms
model-facing copy · 2 tests
tells the model to carry the user's angle rather than defaulting to generic copy0.3ms
names no vendor and no USD price0.7ms
keywords-wave1.vitest.ts
16/16 14ms · 4 suites PASS
src/tools/keywords-wave1.vitest.ts
seo_keyword_metrics · 5 tests
accepts the one declared name3.8ms
rejects the retired query alias0.8ms
rejects the retired topic alias0.3ms
requires the keyword — a paid lookup must not run on nothing0.6ms
explains a rejection in words a user can read1.1ms
seo_enrich_keywords · 3 tests
takes an array0.7ms
accepts an omitted list0.6ms
rejects the retired singular alias0.4ms
seo_list_keywords · 4 tests
accepts the empty call it actually makes0.3ms
rejects the invented argument {"limit":10} instead of ignoring it0.3ms
rejects the invented argument {"filter":"saas"} instead of ignoring it0.2ms
rejects the invented argument {"site":"example.com"} instead of ignoring it0.3ms
the deleted fallbacks are actually gone · 4 tests
seo_keyword_metrics no longer coalesces query/topic0.5ms
seo_enrich_keywords no longer reads a singular keyword0.3ms
leaves the still-legacy tools their fallbacks0.4ms
the drift ledger no longer lists them as open1.9ms
cluster-structure.vitest.ts
16/16 18ms · 4 suites PASS
src/seo/cluster-structure.vitest.ts
the exclusions are what make this brief truthful · 6 tests
does NOT report a site: operator query as cannibalisation4.2ms
does NOT report a PUNCTUATED brand form as cannibalisation0.9ms
does NOT report the brand name as cannibalisation0.5ms
DOES report a real commercial query on two pages2.3ms
ignores a query that only one page answers0.5ms
ranks overlaps by how many clicks are being split0.5ms
a hub is only named when something actually leads · 3 tests
names the highest-earning page as the de facto hub1.0ms
names NO hub when nothing in the group earns anything0.4ms
does not call a page or two a cluster0.5ms
the decision carries the clause that gets ignored · 6 tests
says narrow rather than start another1.5ms
names the specific query to resolve first0.7ms
states that the linking half is advice, not a reading0.3ms
keeps the internal-link hypothesis untested and says why precisely1.1ms
keeps capacity with the user — it decides how wide is safe0.5ms
separates "no reading" from "nothing wrong"0.8ms
no internal vocabulary reaches the user (GS-005) · 1 test
keeps field and table names out of the prose0.7ms
keyword-scoring.vitest.ts
16/16 10ms · 4 suites PASS
src/seo/keyword-scoring.vitest.ts
classifyIntent · 3 tests
reads buying modifiers as transactional, comparison as commercial3.0ms
defaults an unmodified seed to informational, not commercial0.4ms
is case-insensitive0.4ms
computeKES · 4 tests
is volume × cpc ÷ difficulty0.6ms
treats a missing difficulty as 1 rather than dividing by zero or bailing0.4ms
floors difficulty at 1 so a zero-difficulty row cannot produce Infinity0.4ms
is 0 when there is no volume or no cpc, not NaN0.7ms
strikingDistanceWeight · 4 tests
weights positions 11–20 highest — one push lands page 10.5ms
discounts already-won positions rather than rewarding them0.7ms
gives partial credit for a known-impressions/unknown-rank row0.5ms
is continuous across every boundary (no gap that zeroes a band)0.7ms
computeOpportunityScore · 5 tests
blends estimated and behavioural demand0.4ms
ranks a GSC keyword with real impressions ABOVE a KES-0 row — the regression it exists for0.2ms
still scores a pure-estimate row with no GSC data0.2ms
is 0, not NaN, for a row with nothing known0.2ms
never returns negative for any sane input0.3ms
link-feasibility.vitest.ts
16/16 14ms · 4 suites PASS
src/seo/link-feasibility.vitest.ts
page strength counts VOICES, not links · 4 tests
ten links from one domain are one referring domain3.2ms
keeps the best DR and a followed link when a domain links twice1.8ms
excludes dead links — support that has already decayed supports nothing0.3ms
matches target URLs the way the content join does0.4ms
the four verdicts, and the two that are absences · 5 tests
linked: at or above the median of the site's own linked pages0.7ms
thin: below this site's median, and the median is reported as the basis0.5ms
orphan: the site HAS links and none point here — that is a measurement0.7ms
unknown when no links are stored at all — never orphan0.5ms
unknown when Search Console attributes no page to the term0.7ms
ordering surfaces the crossable distances, and drops nothing · 3 tests
ranks linked, then unknown, then thin, then orphan0.6ms
keeps every keyword — an orphan term is slower, not absent1.4ms
puts unknown ABOVE thin — not knowing is not evidence of weakness0.3ms
the note claims nothing it cannot support · 4 tests
says nothing at all when no links are stored0.4ms
names the orphan case as a link problem, not an on-page one0.4ms
states the median as the basis for calling a page thin0.6ms
calls an unattributed term unknown rather than poor0.3ms
no-clean-verdict-without-reading.vitest.ts
16/16 19ms · 2 suites PASS
src/seo/no-clean-verdict-without-reading.vitest.ts
no brief claims a clean result when nothing was read · 13 tests
cluster_structure does not assert a clean finding on an empty input4.0ms
vitals_priority does not assert a clean finding on an empty input1.4ms
crawl_budget does not assert a clean finding on an empty input0.9ms
page_conversion does not assert a clean finding on an empty input2.1ms
migration_runbook does not assert a clean finding on an empty input0.9ms
backlink_policy does not assert a clean finding on an empty input0.9ms
keyword_map does not assert a clean finding on an empty input1.1ms
eeat_proof does not assert a clean finding on an empty input0.9ms
competitor_counter_plan does not assert a clean finding on an empty input1.7ms
recovery_programme does not assert a clean finding on an empty input1.1ms
generative_clicks does not assert a clean finding on an empty input1.4ms
rich_result_eligibility does not assert a clean finding on an empty input0.8ms
video_decision does not assert a clean finding on an empty input1.3ms
the invariant is real — it catches the shape it exists for · 3 tests
flags the exact sentence Q08 shipped0.4ms
flags the exact sentence Q23 shipped0.2ms
does not flag an honest unmeasured sentence0.2ms
query-quality.vitest.ts
16/16 17ms · 3 suites PASS
src/seo/query-quality.vitest.ts
classifyQuery — measured against the owner's real export · 8 tests
excludes every operator dork in the export3.1ms
excludes the generated test tokens that reached the index0.8ms
FLAGS pasted assistant prompts without excluding them1.4ms
leaves every genuine human query alone0.9ms
catches a non-English pasted prompt on sentence structure alone0.4ms
does not mistake a domain name for a sentence boundary0.8ms
needs BOTH length and instruction shape before calling something a prompt0.9ms
treats an empty or blank query as real rather than junk0.4ms
assessQueryQuality — the denominator is the finding · 4 tests
separates excluded from real, and keeps flagged prompts inside real1.7ms
bands positions over REAL queries only0.7ms
reports zero click-through as measured, not as null0.4ms
returns null click-through when there is nothing to divide0.5ms
queryQualityNotes — both disclosures, separately · 4 tests
says what was removed and that it is not a judgement0.5ms
says what was FLAGGED and why it was kept0.4ms
leads the band read with the top-3 absence, which is the actionable part0.2ms
says nothing at all when there is nothing to disclose1.6ms
sov-basis.vitest.ts
16/16 13ms · 5 suites PASS
src/seo/sov-basis.vitest.ts
contributingEngines — the basis a share was measured on · 5 tests
excludes an engine with a null share and no mentions3.8ms
names Google AI Overviews as the sole basis of the 40% point0.6ms
names ChatGPT as the sole basis of the 100% the audit read0.5ms
counts a zero share that was genuinely measured0.6ms
returns nothing rather than throwing on an artifact with no by_engine0.7ms
basisKey — two points on one line, or two different measurements · 2 tests
sees the live basis change that was reported as growth0.9ms
is stable under engine ordering0.4ms
engineWords — never the internal keys · 2 tests
names the surfaces the way the user sees them0.5ms
says "no engine" rather than an empty string0.3ms
normalizeEngineKeys — the stored field carries three shapes, only one of them clean · 5 tests
passes the clean shape through untouched (83 rows)0.5ms
strips the model suffix — a CLAUDE.md §4 leak, not a cosmetic one2.1ms
drops a token that is not an engine at all0.4ms
never prints an unrecognised token verbatim0.2ms
does not throw on a malformed or absent field0.3ms
syntheticContributed — shared with the SOV-unification work · 2 tests
is true when Google AI Overviews drove the share0.4ms
is false when only real answers produced it0.3ms
synthesis-joins.vitest.ts
16/16 47ms · 7 suites PASS
src/seo/synthesis-joins.vitest.ts
J5 — a page that earns traffic is carrying faults · 2 tests
finds the page with real sessions, and ignores the one with none33.1ms
emits nothing when analytics is absent — never guesses stake from the crawl alone1.2ms
J6/J7 — the site-level checks the crawl summary was throwing away · 3 tests
duplicate titles are a defect no per-page check can see1.6ms
a FAILED domain probe is a finding; an UNREPORTED one is not1.4ms
emits nothing at all when the summary was never captured0.6ms
J11 — SSL expiry, the first honest Predictive item (RULING-2) · 4 tests
fires inside the window and STATES ITS METHOD, which is what the gate requires1.0ms
stays silent far from expiry — a date months out is not a prediction worth making0.4ms
reports an ALREADY-expired certificate as fact, not as a forecast0.4ms
ignores a missing or unparseable date rather than inventing one0.5ms
J8 — quality on a page that already ranks · 2 tests
is SUGGESTIVE — a low score on an earned position is headroom, not a fault1.0ms
says nothing about a page that ranks and scores well0.4ms
J9 — ranks in Google, absent from AI answers · 2 tests
is ADVISORY: both sides measured, the causal reading is not0.7ms
does not fire for a site that is visible in both, or measured in neither0.3ms
J10 — tracked keywords with no coverage · 1 test
names only the terms with no ranking anywhere, highest volume first1.2ms
the synthesis set as a whole · 2 tests
every finding is legal on the provenance axis (FR-031)1.1ms
emits NOTHING from an empty tenant — no source, no finding0.4ms
version-conflict.vitest.mjs
16/16 19ms · 5 suites PASS
scripts/lib/version-conflict.vitest.mjs
the defect itself · 2 tests
setVersion alone leaves a conflicted file conflicted, and says nothing4.4ms
assertResolved is what turns that into a failure, with the same message npm gave2.2ms
resolving a version-only conflict · 5 tests
produces valid JSON carrying the allocated number2.2ms
keeps the content that was never in dispute0.5ms
does the same for src/version.ts0.6ms
handles one block per commit, as a multi-commit rebase produces0.4ms
leaves a clean file exactly as it found it0.4ms
what it refuses to resolve · 3 tests
refuses when the branch also changed a dependency2.1ms
refuses a diff3 block rather than guessing at three sides1.1ms
refuses a marker with no terminator instead of eating the rest of the file0.5ms
assertResolved · 4 tests
catches markers0.9ms
catches a marker-free file that is still not JSON0.6ms
passes a clean file through unchanged0.2ms
does not try to JSON.parse a TypeScript file0.5ms
hasConflictMarkers · 2 tests
finds each marker kind at line start0.5ms
is not fooled by content that merely looks like a marker0.3ms
locality-accent.vitest.ts
16/16 11ms · 5 suites PASS
src/leads/shared/locality-accent.vitest.ts
156 · the normalisation expression is identical on both sides of the join · 3 tests
every normalisation in the migration uses the same wrapper2.7ms
the writer normalises the stored column and the reader normalises the argument1.6ms
the join is on the normalised key, scoped to the country0.4ms
156 · the refresh is additive, because a rebuild would race live searches · 3 tests
inserts with ON CONFLICT DO NOTHING0.3ms
never deletes or truncates the variant map0.4ms
only stores forms that differ from their own key0.3ms
156 · the alias function still does everything 151 did · 3 tests
returns the input unconditionally, group or no group0.6ms
still expands curated synonymy through locality_alias group_key0.4ms
unions the observed variants ON TOP of base, not instead of it0.7ms
156 · it does not touch the query plans it was designed around · 2 tests
does not redefine search_candidates0.3ms
creates no index on an unaccent expression0.7ms
156 · the self-verification checks all four things that can fail apart · 5 tests
asserts the gap is closed for both named cities0.3ms
asserts the cities that already worked did not regress0.5ms
asserts migration 155's California guard survives0.7ms
asserts the US is bit-for-bit unchanged until the backfill runs0.2ms
asserts the alias arrays stay narrow0.2ms
telemetry-primitives.vitest.ts
15/15 19ms · 4 suites PASS
src/admin/telemetry-primitives.vitest.ts
timingSafeEqual · 5 tests
matches identical strings, including empty2.7ms
rejects a differing character at any position0.8ms
rejects a PREFIX of the real secret0.4ms
rejects a value that is longer than the secret0.3ms
compares BYTES, so multi-byte characters cannot alias0.6ms
betaPosteriorSummary · 5 tests
brackets the mean and returns a valid interval3.1ms
is WIDE with no evidence — the property that stops a 3-sample "trend"0.5ms
narrows as evidence accumulates at the same rate0.8ms
never reports certainty from a one-sided sample2.0ms
is symmetric under swapping successes and failures1.1ms
mergeGenAiModelStats · 3 tests
joins failures onto totals by model and sorts by volume3.3ms
a failure-only model absent from totals does not crash the join0.4ms
missing latency aggregates become null, not NaN0.3ms
accumFeatureRow · 2 tests
merges ok/fail counts across sources and keeps the newest lastSeen0.5ms
drops unparseable or non-positive durations instead of poisoning percentiles0.5ms
enrich-contacts.vitest.ts
15/15 22ms · 3 suites PASS
src/campaigns/enrich-contacts.vitest.ts
enrich_contacts cap arithmetic · 6 tests
never attempts more than the fan-out cap3.0ms
counts the remainder instead of dropping it — the whole defect0.7ms
reports no remainder when the list fits0.4ms
never reports a negative remainder if the page outruns a stale count0.7ms
excludes the already-enriched in the QUERY, which is what the cap then applies to1.8ms
and drops that exclusion when the user asked to redo the work0.4ms
the continue promise must be keepable · 2 tests
orders the refresh fan-out by enrichment age, so "the next 10" is a different ten1.1ms
keeps a created_at tiebreak, so the default mode is unchanged0.4ms
enrich_contacts result rendering · 7 tests
shows the remainder sentence the dispatch produced6.5ms
discloses when no list was named and we chose the target0.6ms
says nothing about scope when the user DID name a list0.4ms
offers a one-click continue chip naming the same list1.8ms
names what it actually found, not just how many0.5ms
says nothing extra when the research came back empty1.1ms
offers no continue chip when nothing is left0.3ms
reply-triage.vitest.ts
15/15 29ms · 4 suites PASS
src/email/reply-triage.vitest.ts
reply triage — decision table · 7 tests
no triage (flag off, empty body, failure) is exactly the old behaviour5.6ms
an out-of-office at high confidence keeps the sequence running and is not a reply2.6ms
an out-of-office that Jev is not sure about, or that does not read as automatic, falls back to the pause0.7ms
a stop request unsubscribes at the lowest bar, whatever the intent label says2.4ms
interest and refusal set the contact status the filters and dashboard already offer2.1ms
a question or a wrong-person reply pauses like before but says what happened0.9ms
thresholds are stakes-based: stop is the lowest bar, out-of-office the highest1.0ms
reply triage — state and questions · 3 tests
the state names the reply as data, caps it, and carries the subject1.0ms
the intent choice covers every label, and the two nouls are literal single conditions1.8ms
is rolled out and gated on its own flag word1.1ms
reply triage — handler wiring (src/index.ts email()) · 3 tests
the reply branch triages before it writes, and every write is driven by the decision3.2ms
a null leadStatus skips the contact write instead of stringifying it1.3ms
unsubscribe stamps unsubscribed_at like the manual route; interested never overwrites unsubscribed0.7ms
the send and the contact never contradict each other · 2 tests
no branch marks the contact replied without marking the send replied1.3ms
and the legacy fallback still does both, because an unknown reply IS a reply0.5ms
answer-budget.vitest.ts
15/15 13ms · 5 suites PASS
src/chat/answer-budget.vitest.ts
nothing is dropped — it is deferred, named and offered · 4 tests
every fragment is either kept or deferred, never lost2.8ms
names what was held back and offers a chip for it0.9ms
does not describe the deferred material as missing0.5ms
stays silent when everything fits1.2ms
three things can never be deferred · 3 tests
keeps the LEAD however long it is — an answer that defers its answer is useless0.5ms
keeps the CAVEAT — deferring a qualifier while keeping the claim is the one misleading cut0.6ms
keeps an ASK — a question held back is a question never asked0.4ms
ordering is not the budget's business · 2 tests
preserves the order it was given0.3ms
fills in order rather than picking the shortest fragments1.2ms
labels · 4 tests
uses a fragment's own label over the topic default1.4ms
falls back to a topic label when the fragment names nothing0.4ms
dedupes fragments that share a topic instead of repeating one chip0.9ms
caps the chip row at three0.3ms
degenerate inputs · 2 tests
handles empty, null and single-fragment lists0.9ms
drops blank fragments rather than emitting empty paragraphs0.5ms
continuation.vitest.ts
15/15 11ms · 3 suites PASS
src/chat/continuation.vitest.ts
trimToSentenceBoundary · 4 tests
keeps a cleanly-terminated reply unchanged3.5ms
cuts a mid-sentence truncation back to the last full sentence0.5ms
cuts a truncated list item back to the last completed line0.3ms
returns the text unchanged when no usable boundary exists0.4ms
stitchContinuation · 4 tests
dedupes the overlap a model re-emits0.4ms
joins non-overlapping parts with a single space0.5ms
handles empty continuation / empty partial0.4ms
nudge forbids repetition and restarts0.4ms
a continuation that RESTARTS is not stitched, it replaces · 7 tests
detects the restart0.6ms
keeps the rewrite alone — the partial is cut off, the rewrite is not0.8ms
does NOT mistake a genuine continuation for a restart0.5ms
needs a meaningful partial before it will call anything a restart0.2ms
still prefers verbatim overlap when the model repeats its last words0.2ms
trims the head to a sentence boundary before joining a non-overlapping continuation0.3ms
keeps a short head intact rather than trimming most of it away0.8ms
honesty.vitest.ts
15/15 21ms · 5 suites PASS
src/chat/honesty.vitest.ts
the defects that passed `requireTool: true` · 3 tests
catches a success claim with no number — the sentence that hid a 10-of-50 run7.3ms
passes the same claim once it carries the count1.1ms
catches a bare acknowledgement after a tool ran0.6ms
copy invariants are checked on EVERY row, not opted into · 4 tests
catches a vendor name0.7ms
catches nqzai priced in dollars0.9ms
catches "free"2.6ms
does NOT flag the user's own business figures in currency0.4ms
a blocked turn must offer a way through · 3 tests
flags an accurate refusal that offers nothing0.6ms
passes once the turn carries an action0.6ms
says nothing about turns that are not blocked0.6ms
per-row expectations · 2 tests
requires the sentence a scenario is about0.9ms
forbids the sentence a scenario must not produce2.3ms
countAgrees — the strongest assertion, where the artifact is available · 3 tests
passes when the prose matches the artifact0.5ms
fails when the prose inflates the count0.5ms
fails when the reply states no count at all0.3ms
report-judge.vitest.ts
15/15 11ms · 2 suites PASS
src/chat/report-judge.vitest.ts
scoreReportDimensions · 10 tests
every dimension key has a passing fixture (fixtures stay in sync with the dimension set)3.0ms
scores 1.0 and no failure modes when every dimension passes1.1ms
surfaces every distinct failure mode, not just the first-failing dimension0.5ms
maps a single failing dimension to its taxonomy bucket, not just the first key0.5ms
a thin-but-honest report fails sufficient_depth into thin_report (the 2026-07-09 gap)0.4ms
a self-contradicting report fails internally_consistent (the 2026-07-14 forensic gap)0.5ms
a mislabelled/out-of-range composite fails scores_sound0.5ms
dedupes when two failing dimensions share the same failure_mode bucket0.8ms
treats missing/undefined dimensions as false, not a crash0.5ms
ignores unknown extra keys and only scores the fixed dimension set0.4ms
a defective artifact caps the score, however sound the data underneath · 5 tests
blank sections cap at 0.5 even when all eight data dimensions pass1.4ms
a rendered self-contradiction caps too — the KPI-vs-section case0.2ms
the actual report: blank sections AND a contradiction0.2ms
the cap is a CEILING, never a floor — a bad report does not get lifted to 0.50.4ms
a clean artifact is unaffected — the mean still governs0.3ms
seo-audit-routing.vitest.ts
15/15 53ms · 3 suites PASS
src/chat/seo-audit-routing.vitest.ts
the judged-20% turn · 2 tests
routes the reported prompt to the full audit, not the crawl-budget picker28.2ms
does not reach onpage_start, whose handler answers with a depth question9.4ms
the adjacency class — a scope word separated from "seo audit" by one qualifier · 6 tests
run a full technical seo audit -> full_seo_audit1.2ms
run a complete technical seo audit -> full_seo_audit1.2ms
full technical SEO audit please -> full_seo_audit1.0ms
can you run a complete technical seo audit and tell me what to fix first -> full_seo_audit0.4ms
leaves the plain "full seo audit" phrasing exactly as it was1.8ms
routes "run every seo audit" to the full audit instead of asking which one1.0ms
what must NOT change — the depth picker is right when on-page IS the ask · 7 tests
run an on-page audit -> onpage_start1.4ms
run an on-page seo audit -> onpage_start2.0ms
run a technical seo audit -> onpage_start0.4ms
audit my pages -> onpage_start1.0ms
keeps off-page and Serpdex on their own intents0.9ms
keeps the bare "audit my site" on the route picker, which asks WHICH audit0.6ms
does not swallow a lead search that merely mentions SEO1.2ms
turn-budget.vitest.ts
15/15 43ms · 4 suites PASS
src/chat/turn-budget.vitest.ts
a stated limit is read · 3 tests
the live message that started this3.9ms
the ways people write a ceiling1.1ms
THE SMALLEST STATED AMOUNT WINS0.4ms
and a number that is not a budget is left alone · 4 tests
needs the word tokens AND a limiting phrase0.5ms
ignores an amount too small to buy a turn0.3ms
empty input is not a budget0.4ms
is not stateful across calls — AMOUNT_RE is /g0.6ms
the ceiling reuses the existing gate rather than growing a second one · 5 tests
is min(balance, stated) — a budget larger than the balance does not unlock money2.1ms
the accumulator still applies, so a fan-out cannot walk past it one tool at a time2.6ms
the scaled-down offer sizes off the CEILING, not the balance2.3ms
names the limit that actually bound, instead of telling them to top up2.8ms
null falls through to the balance — no invented default2.1ms
the budget is derived where EVERY path passes · 3 tests
is set in setUserMessage, not inside the agent loop11.9ms
the model is told the limit before it plans7.0ms
and it tells the model to plan, not just to stop3.9ms
turn-plan.vitest.ts
15/15 36ms · 5 suites PASS
src/chat/turn-plan.vitest.ts
derivePlan · 5 tests
returns a multi-step plan for a genuinely compound ask21.6ms
returns null for a single-intent ask (no plan worth persisting)4.9ms
returns null for a no-ask turn1.2ms
caps step count so a match storm cannot write a runaway plan0.7ms
truncates the stored original message0.4ms
markStepDone · 3 tests
marks a matching step and reports the change0.5ms
is a no-op for a tool not in the plan (caller can skip the KV write)0.2ms
is idempotent — re-running the same tool does not re-report a change0.3ms
isPlanComplete · 2 tests
is false while any step remains1.7ms
is true once every step is done0.6ms
planStatusLine · 4 tests
names completed steps as do-not-repeat and remaining steps in order1.0ms
says nothing is done yet when the plan has not started0.2ms
returns empty string for a complete plan (nothing to inject)0.4ms
returns empty string for an empty plan0.2ms
turnPlanKey · 1 test
is tenant- and session-scoped0.9ms
described-product.vitest.ts
15/15 16ms · 3 suites PASS
src/leads/described-product.vitest.ts
described-product: the incident conversation · 5 tests
picks the opening description and nothing else4.8ms
classifies every turn the way the conversation reads1.6ms
scores the bare domain at ZERO prose — it is a fact, not a description0.4ms
never reads a confirm token as prose, however long the uuid0.5ms
would have carried the description into the scan turn0.3ms
described-product: precision guards · 6 tests
rejects a long message that claims no ownership1.0ms
rejects a short ownership claim — it is worse context than a scanned page0.6ms
accepts the common phrasings a founder actually uses0.5ms
keeps the newest messages when more than the cap qualify2.0ms
never exceeds the prompt cap0.8ms
returns empty for a conversation with no description0.5ms
isSubstantiveBrief: nothing may be derived from a name · 4 tests
rejects the exact brief the incident produced0.5ms
accepts a real compiled brief0.5ms
rejects empty, null and whitespace without throwing0.3ms
rejects two lines that are still only a name0.1ms
person-name.vitest.ts
15/15 8ms · 3 suites PASS
src/leads/person-name.vitest.ts
sanitizePersonName — real values observed in production · 9 tests
salvages the person out of "Jeferson (tolefitness.com)" rather than trusting or dropping it3.0ms
rejects "Wallace (teamcastro.co)" — a .co domain still reads as a domain0.3ms
KEEPS a real person — the case that must not regress0.3ms
rejects role and generic inboxes rather than greeting "Hi Wizard,"0.3ms
rejects a bare domain, an address, and digits0.3ms
rejects an organisation, and a sentence masquerading as a name0.3ms
handles null, empty and whitespace without throwing0.3ms
is idempotent — sanitizing a clean name changes nothing0.3ms
works with no email supplied (CSV import, manual add)0.2ms
personFirstName drives the greeting · 3 tests
gives a first name for a real person1.2ms
gives null for the polluted values, so the caller says "Hi there,"0.6ms
rejects a single-letter first name — "Hi J," is not a greeting0.2ms
briefForPrompt bounds the product brief · 3 tests
leaves a short brief untouched0.3ms
caps a long brief and MARKS the truncation so the model knows it is an excerpt0.4ms
handles null/undefined as an empty string, never the text "null"0.1ms
model-failure-surface.vitest.ts
15/15 15ms · 4 suites PASS
src/llm/model-failure-surface.vitest.ts
a failed attempt records its latency (defect 1) · 5 tests
catch block 0 emits a gen_ai span2.0ms
catch block 1 emits a gen_ai span0.7ms
records the span as a FAILURE, or it pollutes the success series0.4ms
carries the duration — the whole point0.5ms
separates timeouts from other errors in the task name0.3ms
the two producers are distinguishable (defect 2) · 2 tests
each throw site names itself0.4ms
keeps the marker OUTSIDE the phrase existing consumers match on1.2ms
the chat path translates model failures (defect 3) · 6 tests
maps the exact Sentry string to a sentence with no model slug and no milliseconds0.8ms
distinguishes a timeout from an unavailable chain0.4ms
covers the tools path's own exhaustion message, not just the timeout one0.3ms
returns null for anything that is NOT a model-chain failure0.7ms
is wired into BOTH chat exits — streaming and non-streaming1.2ms
still runs the message through scanOutbound0.6ms
Sentry suppression is unchanged by the refactor · 2 tests
the timeout sentence keeps the shape isExpectedToolOutcome matches1.9ms
and a genuine novel error still reports, so the check is not vacuous2.6ms
router-truncation.vitest.ts
15/15 28ms · 5 suites PASS
src/llm/router-truncation.vitest.ts
callOpenRouterFull — finish_reason reaches the caller · 4 tests
reports finish=stop / truncated=false on a clean completion5.5ms
reports truncated=true when a LONG completion hit the output ceiling5.2ms
reports finish='?' rather than 'stop' when the provider sent no finish_reason1.1ms
surfaces the WINNING attempt’s finish_reason after a failover, not the failed one1.3ms
callOpenRouter — the bare-string wrapper is unchanged · 1 test
still returns only the text of a truncated completion0.9ms
failOnTruncation is OFF by default · 1 test
does not fail over, throw, or alter text on a truncated-but-long completion0.7ms
failOnTruncation: a truncated generation is a FAILED one, not a short one · 5 tests
retries the next model in the chain instead of returning the partial2.0ms
throws when every model in the chain truncates3.3ms
describes the cause as truncation rather than empty/short content1.1ms
leaves a clean completion completely unaffected1.0ms
reaches the bare-string wrapper, which cannot otherwise see truncation0.9ms
hidden reasoning is disabled — failOnTruncation callers included (2026-08-29) · 4 tests
sends reasoning:{enabled:false} when the caller cannot tolerate truncation1.4ms
now covers prose callers too — the old predicate was the defect0.6ms
NEVER overrides a reasoning option the caller set deliberately0.4ms
applies on every model in the chain, not just the first1.2ms
router.vitest.ts
15/15 26ms · 4 suites PASS
src/llm/router.vitest.ts
resolveToolModelChain — Phase 0 eval override seam · 5 tests
is unaffected by default (no override set)6.9ms
leads with the mapped candidate model when a valid override is set1.1ms
falls through to the normal TOOL_MODELS chain after the candidate (never narrows reliability)1.4ms
rejects an unknown key — reqCtx stays null, no injection1.3ms
never includes the disqualified judge-family model as a candidate0.4ms
resolveRouterRollout — env flag parsing · 3 tests
returns null for unset/off/empty (no live change)0.5ms
maps a known candidate key to its model id0.4ms
returns null for an unknown key rather than injecting it as a model id1.1ms
resolveToolModelChain — live rollout flag · 4 tests
default 'off' leaves the chain exactly as it ships today0.6ms
leads with the rollout model and keeps the tier chain behind it as failover0.6ms
an unknown flag value falls back to the normal chain (fail-safe, not fail-open)0.3ms
the admin eval override outranks an active live rollout1.1ms
timeout errors name the model and the budget · 3 tests
callOpenRouterTools reports a timeout, not "The operation was aborted"5.8ms
callOpenRouterFull reports a timeout the same way2.0ms
a non-abort failure keeps its own message0.6ms
observation.vitest.ts
15/15 19ms · 4 suites PASS
src/runtime/observation.vitest.ts
observe · 5 tests
DE-DUPLICATES — 30 calls against one connector are one observation, not thirty4.7ms
keeps distinct resources distinct1.5ms
is bounded, so a pathological run cannot make one flush unbounded0.8ms
ignores empty refs rather than writing a row that says nothing0.3ms
truncates a long ref instead of storing an unbounded string0.3ms
flushObservations · 4 tests
writes one row per distinct resource, carrying the run correlation2.6ms
clears the buffer so a second request cannot inherit the first request one0.5ms
writes nothing when there is no tenant to attribute it to0.6ms
SWALLOWS a write failure — an audit gap must never break a turn1.5ms
retention · 3 tests
prunes past a stated window rather than accumulating forever1.2ms
a failed prune reports zero rather than throwing a cron down0.5ms
the retention window is a real limit, not effectively infinite0.3ms
what is never recorded · 3 tests
records the connector NAME, never a secret or a row of tenant data0.6ms
records the provider+operation, never the request payload1.9ms
records the egress HOST, never the full URL with its query string0.5ms
preflight-ask.vitest.ts
15/15 64ms · 4 suites PASS
src/tools/preflight-ask.vitest.ts
a call that cannot run is caught before the money question · 7 tests
asks who, for a people search with no audience at all4.1ms
speaks to the person, not to the schema0.8ms
says nothing when any one handle is present1.1ms
covers the local-business shapes a user can also answer0.6ms
never blocks a call the validator would have accepted0.5ms
returns null — not a thrown error — for a failure a user cannot answer4.3ms
is inert for tools with no schema0.6ms
the disambiguation chip reads as English · 1 test
strips the leading verb so the chip does not say "find" twice0.8ms
the chip prefix has ONE definition · 1 test
matches both the current and the legacy chip forms1.1ms
the saved audience is read before the user is asked · 6 tests
the lead path passes userId so it CAN load personas7.4ms
the lead path also passes the chip decision, or Tier −1 answers a question nobody asked7.0ms
structureLeadArgs loads __personas__ and the brief8.0ms
an explicit ask still wins over the stored persona9.2ms
refuses to invent an audience when nothing is saved10.7ms
names the persona on the approval card so a wrong target can be caught5.6ms
system-scope.vitest.ts
15/15 40ms · 5 suites PASS
src/tools/system-scope.vitest.ts
the scope map is exhaustive, both ways · 3 tests
every part has exactly one scope entry, and every entry matches exactly one part9.3ms
every scope names a family that exists, or one of the three conditions1.7ms
V2_SYSTEM is the parts joined — nothing that reads the full prompt changed0.6ms
what a call receives · 6 tests
with tiering off (and the two conditions on), the flat prompt: every part, no stubs1.6ms
a settled tenant on a CORE ask gets core only, plus one stub per unloaded family with a stub1.0ms
a loaded family brings its guidance and drops its stub1.0ms
a two-scope part rides with either family1.6ms
onboarding and the capabilities ask are conditions, not families5.7ms
order is the original order — a family loaded later appends nothing out of place1.0ms
the per-call budget (a ratchet — lower it with the work, never raise it silently) · 4 tests
the core-only prompt stays under its ceiling2.3ms
the CORE tool schemas stay under their ceiling0.7ms
a WHY question preloads the family the stub points at, so the guidance arrives on the first call4.2ms
a content-quality ask preloads seo, so seo_content_quality is visible and read_url is not the fallback (bklink 2026-09-18)5.8ms
the loop composes the prompt on every iteration · 1 test
sets messages[0] from composeSystemPrompt with the run's families, tiering, onboarding and the capabilities ask1.2ms
the Jev shortlist stub (2026-09-19) · 1 test
a shortlisted turn is told where the everyday tools went; a full-CORE turn is not0.9ms
competitor-joins.vitest.ts
15/15 15ms · 3 suites PASS
src/seo/competitor-joins.vitest.ts
join 3 — what find_competitors is allowed to persist · 6 tests
screens out the directories a "<brand> alternatives" SERP is full of3.9ms
keeps a real product domain1.0ms
rejects empty input rather than storing a blank competitor0.2ms
merging keeps user-confirmed entries ahead of auto-discovered ones0.6ms
merging dedupes, so re-running discovery cannot grow the set forever1.4ms
merging caps the set, so discovery cannot blow past the stored limit1.7ms
join 3 — the write is conditional in source · 3 tests
only persists for the tenant's own brand, never another company's rivals0.5ms
screens before storing rather than trusting the reader to re-screen0.5ms
reports what it saved instead of changing the set silently0.3ms
join 2 — the gap resolves the saved set before spending · 6 tests
competitor_domain is no longer a required argument0.4ms
the fallback resolves BEFORE the balance gate — never after a cost click1.0ms
asks when several are saved rather than guessing "top" from an unranked list0.2ms
the picker turn carries chips, so the question it asks is answerable by clicking0.3ms
BOTH competitor picks halt the loop verbatim, so the rivals are always offered as chips (2026-09-15)2.0ms
points at discovery when nothing is saved, instead of a bare refusal0.2ms
content-performance.vitest.ts
15/15 35ms · 4 suites PASS
src/seo/content-performance.vitest.ts
URLs match the way a human would compare them · 3 tests
ignores scheme, www, trailing slash, query and hash3.1ms
does not collapse two different pages1.4ms
sums variants of one page rather than letting the last one win2.2ms
the three absences are not one absence · 6 tests
earning: clicks measured0.9ms
seen_not_clicked: impressions but no clicks is a FINDING0.4ms
unmatched: no row at all is an absence of measurement, and clicks stay NULL not 00.6ms
too_new beats every other empty verdict inside the grace window0.4ms
but a young page that IS earning is reported as earning0.3ms
an unpublished piece is not in the list at all0.4ms
clicks are the outcome; impressions are the denominator · 5 tests
leads with clicks when anything is earning21.2ms
names the zero-click case as a titles problem, not a rankings one0.6ms
calls an unmatched page an absence of measurement, and names the causes0.3ms
says a young piece is too new to judge rather than failing0.2ms
says nothing when nothing was published0.2ms
ordering · 1 test
puts what works at the top and the unjudgeable at the bottom1.0ms
page-stake.vitest.ts
15/15 45ms · 5 suites PASS
src/seo/page-stake.vitest.ts
signal 1 — stake concentration · 3 tests
reports how many affected pages earn, and what share of the site they carry2.8ms
matches URLs across protocol/www/trailing-slash differences0.5ms
says nothing rather than 0% when no affected page earns anything0.3ms
the fix list is ordered by stake, not by volume · 3 tests
a smaller issue on pages that EARN outranks a bigger one on pages that do not0.4ms
falls back to volume when there is no performance data — unchanged behaviour0.2ms
an issue with stake outranks one with none at all0.2ms
signal 2 — CTR against the SITE'S OWN median (FR-031) · 3 tests
finds the page losing clicks it has already earned the ranking for18.0ms
says NOTHING when the band is too thin for a trustworthy median0.9ms
ignores pages with too few impressions for CTR to mean anything0.6ms
signal 3 — indexed and earning nothing · 3 tests
finds an indexed page that competes for no query0.5ms
frames it as a targeting gap, never as a fault0.2ms
emits nothing when performance could not be read — never guesses from absence0.2ms
the report stops apologising once it can see stake · 3 tests
stakeGap goes silent when per-page performance exists0.4ms
the "what we cannot see yet" panel disappears from the rendered report18.1ms
a CTR underperformer renders as a Fix, an idle indexed page as a Try1.0ms
rival-pressure.vitest.ts
15/15 13ms · 5 suites PASS
src/seo/rival-pressure.vitest.ts
an outranking claim needs BOTH positions · 4 tests
counts a rival above us as outranking4.5ms
does not count a rival BELOW us0.7ms
counts an unknown own position separately, never as a loss0.5ms
treats a null own position as unknown, not as zero0.7ms
a scan that has not run is not a finding · 2 tests
reports scanned:false with no rows0.4ms
says nothing at all rather than "you have no competitors"0.5ms
only the most recent scan is summarized · 4 tests
counts keywords and rivals from the latest run alone0.7ms
names a domain absent from the previous run as new0.5ms
claims no movement when there is no previous run0.6ms
suppresses movement entirely when the read was truncated0.8ms
ordering puts pressure first, not noise · 2 tests
ranks the domain that beats us above one merely present everywhere0.4ms
counts one standing per rival per keyword even with duplicate rows0.4ms
the note says which of the two problems this is · 3 tests
names the rival, the count and the basis0.3ms
distinguishes an arriving competitor from a page that got worse0.3ms
does not claim a defeat when our position is unknown0.5ms
corpus-relevance.vitest.ts
15/15 20ms · 6 suites PASS
src/leads/shared/corpus-relevance.vitest.ts
the Jev path (2026-09-17) · 4 tests
maps the three bands onto the rubric's scores through the probabilities4.3ms
asks one choice per candidate, criteria as a map, the state carrying the filtered-already rule1.4ms
uses Jev when rolled out and never calls the model; the reason is a template2.8ms
falls back to the model when Jev fails, and stays on the model when not rolled out2.8ms
decideRelevance · 2 tests
is unknown below 3 scored rows — a coin toss is not a verdict0.5ms
no_fit when the median is under 40, fit at or above it0.6ms
parseRelevanceScores · 2 tests
reads the JSON, clamps, and never returns more scores than candidates0.5ms
garbage is an empty sample, not a throw0.9ms
describeRelevanceAsk / describeCandidate · 1 test
states role, subject and place in the user's terms1.5ms
assessCorpusRelevance · 3 tests
a low-scoring sample is no_fit, with examples for the reply and its own ledger label0.8ms
FAILS OPEN: a model failure, a garbled answer or too few rows all leave the rung alone0.8ms
a matching sample is fit0.4ms
the judged ask is role and subject only · 3 tests
drops industry and place — the database already applied them and the rows cannot show them0.6ms
the prompt tells the model the filters already ran and scores a related role 50-790.6ms
an ask with only an industry has nothing to judge — the gate stays open0.3ms
sentry-identity.vitest.ts
14/14 19ms · 4 suites PASS
client/sentry-identity.vitest.ts
a signed-in user is identified the way the worker identifies them · 5 tests
sets the Sentry user block with id and email5.3ms
uses the WORKER'S tag names, so one query works across both projects1.0ms
trusts the SERVER for internal, never a client guess4.6ms
an internal account that is NOT an admin is still tagged internal1.1ms
an admin is tagged on both axes0.4ms
signing out is a state, not an absence · 2 tests
clears the user block0.5ms
writes false rather than leaving the tags unset0.4ms
the loader stub has no setUser — buffer, do not throw · 3 tests
defers through onLoad when only the stub is present0.8ms
the LAST identity wins when the SDK arrives after a sign-out0.5ms
does nothing at all when Sentry is absent (ad blocker, offline)1.1ms
the real call sites · 4 tests
BOTH /api/user/me readers identify — not just one0.7ms
each reader passes the SERVER'S internal flag, not a local guess0.7ms
signing out clears the Sentry identity0.9ms
the loader-stub guard is present at the real call site0.3ms
provider-balances.vitest.ts
14/14 42ms · 3 suites PASS
src/admin/provider-balances.vitest.ts
provider-balances — Composio usage (derived from logs) · 4 tests
counts executions in the 30d window and surfaces failures, stopping at the window edge29.5ms
populates quota_limit (for a usage %) when COMPOSIO_MONTHLY_QUOTA is set0.9ms
reports an error entry (never throws) when the logs endpoint fails2.1ms
omits Composio entirely when COMPOSIO_API_KEY is not configured0.5ms
provider-balances — OmegaIndexer usage (quota − ledger, no balance API) · 5 tests
shows remaining = quota − all-time usage when OMEGA_INDEXER_QUOTA is set1.0ms
shows credits-used only when OMEGA_INDEXER_QUOTA is not set0.6ms
treats a null ledger sum as 0 used (no submits yet)0.6ms
reports an error entry (never throws) when the ledger query fails1.2ms
omits Omega entirely when OMEGA_INDEXER_KEY is not configured1.3ms
provider-balances — GitHub Actions billing · 5 tests
reports an error (not a network call) when GITHUB_TOKEN is not set0.8ms
reports minutes used vs included, and sends the token as a Bearer header0.9ms
flags paid overage minutes distinctly from within-plan usage0.8ms
gives the exact scope fix on a 404 (default-scope token lacks billing access)0.5ms
reports a generic error entry (never throws) on other failures0.4ms
telemetry-tables.vitest.ts
14/14 22ms · 3 suites PASS
src/admin/telemetry-tables.vitest.ts
computeOperationsTable — quality score averaging · 3 tests
averages correctly when every score is a number6.0ms
does not corrupt the average when some scores are strings (Sentry NQZAI/admin advisory bug, 2026-07-23)1.3ms
ignores a non-numeric score instead of pushing NaN1.4ms
computeToolRunHealth · 5 tests
buckets every status and computes success_rate over EXECUTED runs only (excludes capped/rejected from the denominator)1.3ms
a tool with only capped/rejected runs (never actually executed) gets success_rate 0, not NaN or 10.5ms
an unlabeled tool falls back to its raw id, not a blank label0.5ms
sorts by volume descending and keeps the newest last_seen per tool1.4ms
defaults a missing meta.status to ok, matching the writer default (trackApiCall status defaults unset -> treated as success)0.3ms
computeDailySpendAndForecast — honours the requested window · 6 tests
defaults to 14 days when no window is passed (back-compat)4.0ms
returns 30 days when 30 is requested1.3ms
returns 90 days when 90 is requested — the case the hardcoded slice silently truncated1.1ms
keeps the NEWEST days, not the oldest0.7ms
never returns more days than exist, however wide the window0.7ms
clamps a nonsense window to at least one day rather than returning everything0.5ms
notion-blocks.vitest.ts
14/14 14ms · 4 suites PASS
src/connectors/notion-blocks.vitest.ts
markdownToNotionBlocks structure · 7 tests
maps heading levels, lists and quotes4.8ms
collapses h4-h6 onto heading_3 instead of emitting a block type Notion rejects1.3ms
joins a soft-wrapped paragraph into ONE block1.7ms
does not swallow the construct that ends a paragraph0.4ms
keeps a fenced code block intact, newlines and all0.5ms
terminates on an unterminated fence rather than looping0.4ms
emits a divider for a thematic break0.4ms
inlineRichText · 4 tests
marks bold, italic and code runs0.7ms
links http and mailto targets0.7ms
renders a javascript: link as plain text, never a clickable block0.5ms
SPLITS past the 2000-char API limit instead of truncating0.9ms
notionLanguage · 1 test
resolves aliases and falls back to plain text on anything unknown0.3ms
chunkBlocks · 2 tests
splits into request-sized batches so a long article is not silently cut at 100 blocks0.3ms
returns nothing for an empty document0.2ms
bounce-class.vitest.ts
14/14 11ms · 2 suites PASS
src/email/bounce-class.vitest.ts
classifyBounce · 7 tests
reads 5.1.1 (no such user) as a dead address3.2ms
reads 4.x.x as transient — the address is fine, the mailbox was not ready0.7ms
reads a full mailbox as transient even though 5.2.2 is a PERMANENT code0.3ms
reads "Action: delayed" as transient — the MTA has not given up yet0.3ms
falls back to the bare SMTP reply when there is no enhanced status0.4ms
does NOT read "undeliverable" in a subject as permanent0.6ms
classifies an unreadable bounce as unknown rather than guessing0.4ms
bounceBlocksSend · 7 tests
lets a contact through when there is no bounce history at all0.4ms
blocks permanently on a hard bounce2.2ms
lets a single soft bounce through — this is the lead the old gate threw away0.4ms
gives up once the softs reach the tolerance0.5ms
blocks on an UNKNOWN class — an unparseable bounce keeps the pre-fix behaviour0.5ms
blocks on a NULL class — rows that bounced before migration 058 must not be unblocked0.3ms
one hard bounce outweighs any number of softs0.1ms
evidence-gaps.vitest.ts
14/14 21ms · 3 suites PASS
src/chat/evidence-gaps.vitest.ts
what counts as a gap · 7 tests
records a page that would not load, with the status3.6ms
records our own site WITHOUT repeating the internal instruction1.1ms
counts a search that RAN and found nothing — status ok is not an answer0.4ms
says a REFUSAL is a refusal — the reader can act on that0.7ms
separates rate-limiting, timeout and size from a refusal0.9ms
is silent for a call that worked0.3ms
ignores tools whose failure is an ERROR, not a gap in evidence0.3ms
the footer · 6 tests
renders nothing when nothing failed — silence is the common answer0.3ms
names what it costs the answer, not just what failed0.4ms
deduplicates a retried target — three attempts are one gap2.2ms
caps the list and says how many are hidden1.0ms
drops a gap with no target rather than printing an empty bullet0.2ms
never names the tool that failed — the user cares what, not which internal tool0.3ms
the guardrail backstop (v2.346.2) · 1 test
strips the internal instruction if the model recites it anyway8.2ms
full-audit-scope-words.vitest.ts
14/14 62ms · 3 suites PASS
src/chat/full-audit-scope-words.vitest.ts
every scope word reaches full_seo_audit, not the picker · 8 tests
"run a full seo audit"33.4ms
"run a complete seo audit"4.1ms
"run a comprehensive seo audit"0.6ms
"run a thorough seo audit"0.6ms
"run a entire seo audit"0.6ms
"run a whole seo audit"0.4ms
the scope word survives an inserted "technical"1.3ms
the phrasings the pattern advertises actually reach it1.3ms
the two halves cannot drift again · 2 tests
the exclusion defers to the SAME regex the pattern uses0.7ms
adding a scope word to the constant is enough on its own1.1ms
what must NOT change — the picker is not the defect · 4 tests
a genuinely ambiguous ask still gets the disambiguation3.0ms
on-page and off-page keep their own intents2.9ms
the crawl-depth picker reply still routes9.2ms
"deep" is deliberately NOT a scope word1.7ms
scan-offer.vitest.ts
14/14 20ms · 3 suites PASS
src/chat/scan-offer.vitest.ts
the gate records what it offered · 3 tests
writes the pending offer before returning the chip11.6ms
the offer is scoped to the conversation that made it1.7ms
and the turn reports what it cost, like the branch thirty lines above it0.6ms
accepting the offer runs the offer, not a re-planned turn · 9 tests
the site comes from KV, never from re-reading the message0.4ms
runs scan_product and NOTHING else0.8ms
clears the offer, so it cannot be redeemed twice0.4ms
a FAILED scan falls through instead of reporting success0.6ms
uses the same composer as the onboarding scan1.1ms
offers the original ask back, now that it can be answered well0.6ms
an absent or expired offer falls through rather than guessing a domain0.3ms
only an ACCEPTANCE triggers it — a message that merely mentions scanning does not0.7ms
the loop consults that predicate rather than re-implementing it0.4ms
a compound ask is not an AEO ask · 2 tests
the shortcut stands down when the message also asks for something else0.3ms
asks the SAME registry the misroute guard reads0.3ms
sov-domain-helpers.vitest.ts
14/14 12ms · 5 suites PASS
src/middleware/sov-domain-helpers.vitest.ts
registrableCore — the token brand matching runs against · 4 tests
takes the SLD, not the full host3.4ms
handles two-part TLDs so the core is not the country suffix0.8ms
does not mistake a subdomain for the brand0.4ms
degrades without throwing on junk0.5ms
isNoiseHost / isGenericPlatform · 2 tests
matches the host itself and its subdomains, not arbitrary substrings0.8ms
keeps a genuine competitor domain out of both buckets0.4ms
isDefinitionQuery — the ambiguous-brand signal · 2 tests
catches dictionary and translation lookups0.9ms
does NOT catch a buyer-intent query that merely mentions a product0.4ms
domainFromChunk — unwrapping Gemini grounding · 4 tests
prefers a domain-shaped title over the vertex redirect URI1.0ms
falls back to the URI host when the title is prose, not a domain0.4ms
returns the title rather than throwing on an unparseable URI0.8ms
returns empty for an empty chunk instead of undefined0.3ms
normDomain · 2 tests
strips scheme, www and path to a bare host0.2ms
is idempotent0.2ms
tap-provisional-share.vitest.ts
14/14 48ms · 5 suites PASS
src/middleware/tap-provisional-share.vitest.ts
the provisional-share rule has ONE definition · 2 tests
lives with the code that produces the share, and both surfaces read it3.0ms
an ABSENT leaderboard is provisional, not permissive1.0ms
a provisional share cannot produce an impressions count · 6 tests
withholds the derived figure — null, not zero0.9ms
keeps the MEASURED half intact0.4ms
withholds it per query too0.3ms
states WHY, so the absence is never read as zero0.6ms
keeps stage 4 in the funnel, carrying its reason instead of a volume0.4ms
advises what would CLOSE it, and drops the advice written from the figure0.4ms
a measured share still produces the number · 2 tests
computes impressions when the comparator set supports it0.8ms
0% against a real field is a FINDING, not a withheld figure17.6ms
the artifact does not print a withheld figure as 0 · 2 tests
shows an em dash and says it was not estimated18.8ms
leads with the impressions figure when there IS one0.5ms
the forensic contract holds the rule even if the arithmetic drifts back · 2 tests
a provisional share carrying an impressions count is a violation0.7ms
the withheld shape passes, and so does a measured one0.6ms
full-audit-report.vitest.ts
14/14 13ms · 3 suites PASS
src/reports/full-audit-report.vitest.ts
full_seo_audit — §17 gold standard · 9 tests
leads with the constraint, not with how many sub-audits ran3.4ms
keeps the two half-scores as context cards with their fix prompts1.3ms
renders all FOUR directive groups, each stating its own absence (GS-011 / FR-030)0.5ms
an artifact predating the join says so instead of claiming nothing was found (GS-004)0.8ms
renders cross findings by directive with both sides of the evidence1.7ms
states what it could not check as a consequence, not as a step list0.6ms
overall score is the average of PRESENT scores and always <= 100 (was 119/100)0.7ms
a blocked half reports the consequence, never the vendor or the raw error0.4ms
has a feedback mount and no "Cluster N" headers0.3ms
full_seo_audit — the source ledger (spec §5.4) · 3 tests
names every source, its age, and whether it was on file at all0.7ms
drops the old "Audit evidence" grid — it restated the on-page report verbatim (FR-040)0.4ms
an Expect item renders with its method visible, not just its conclusion0.5ms
full_seo_audit — a single-source finding is never sold as a join · 2 tests
counts probes as faults but does NOT claim they came from the join0.6ms
says how many DID need the join when some genuinely did0.4ms
publish-controls.vitest.ts
14/14 14ms · 5 suites PASS
src/reports/publish-controls.vitest.ts
planPublishControls — live state decides, every time · 4 tests
nothing connected → connect routes for both, plus the coding-agent fallback3.7ms
WordPress connected later → publish appears (no regeneration)2.1ms
Notion connected later → send appears1.0ms
both connected → both, no fallback0.8ms
planPublishControls — the stack fingerprint cannot outrank live state · 3 tests
non-WordPress stack + WordPress NOT connected → no WordPress route (correct)0.5ms
non-WordPress stack + WordPress CONNECTED → publish shows anyway0.5ms
Notion is never suppressed by stack — a private workspace is not the site CMS0.5ms
planPublishControls — unknown state never guesses · 2 tests
unknown → no publish and no connect controls, fallback only0.4ms
unknown names the stack when it knows it0.8ms
planPublishControls — the subtitle always describes the buttons shown · 3 tests
connect-only state mentions connecting, not just copying0.4ms
live state names the live destinations0.7ms
suppressed WordPress + nothing live → mentions only Notion1.1ms
planPublishControls — surface differences are explicit, not accidental · 2 tests
panel leads with publish-live; chat leads with draft1.3ms
both surfaces offer the SAME destinations — only the order/emphasis differs0.5ms
legal-pages.vitest.ts
14/14 88ms · 3 suites PASS
src/routes/legal-pages.vitest.ts
Terms of Service · 6 tests
is a business-use contract under Pennsylvania law with individual arbitration47.9ms
caps liability at three months of fees and puts every obligation under the cap11.3ms
the customer indemnity names the outbound-email and contact-data risks and a procedure2.7ms
allocates outbound-email duties on both sides, and never claims to have no role1.9ms
AI outputs, third-party data and roadmap items are disclaimed; content is licensed, never trained on3.1ms
refunds are governed only by the Refund Policy, and the Terms no longer say "non-refundable"3.3ms
Privacy Policy · 6 tests
is a notice, not a contract, and separates the roles2.4ms
keeps the Google Limited Use disclosure and scopes verbatim2.6ms
the sub-processor schedule names the providers the code actually calls2.7ms
states the retention periods the infrastructure actually implements2.6ms
deletion says what remains, including undeleted contacts, and gives corpus removal1.7ms
regional rights sections exist and the transfer position names a DPA on request1.5ms
Refund Policy · 2 tests
is the operative refund text and settles the negative-balance and chargeback questions1.3ms
all three pages carry the same contact address and the same date2.2ms
egress.vitest.ts
14/14 63ms · 3 suites PASS
src/runtime/egress.vitest.ts
normalizeUserUrl · 4 tests
adds https to a bare host — existing product behaviour, kept4.4ms
refuses schemes that are not http(s)1.1ms
refuses hosts nobody should be able to name2.3ms
does NOT refuse public hosts that merely look similar0.8ms
fetchUserUrl · 9 tests
records every fetch, which is what makes an abuse report answerable41.4ms
records a DENIAL separately — that is the number worth watching1.1ms
re-validates EVERY redirect hop — a public host must not redirect us inward1.6ms
follows a legitimate redirect and returns the final response2.4ms
stops at the redirect cap instead of looping1.2ms
refuses an oversized response before reading it0.9ms
never leaks the target or an internal detail into user-facing copy1.5ms
a metrics outage never fails the fetch1.1ms
with no metrics binding it still fetches — the record is best-effort, the control is not0.8ms
readEgressBuckets · 1 test
returns the hours that exist, newest first, and tolerates gaps1.0ms
tool-caps.vitest.ts
14/14 298ms · 5 suites PASS
src/runtime/tool-caps.vitest.ts
EXTERNAL_TOOL_CAPS completeness · 3 tests
covers every tool with a provider cost estimate4.4ms
aliases resolve to existing canonical buckets1.6ms
cap labels never name a backend vendor (CLAUDE.md §2)2.2ms
resolveRunLimit for a test-cap tenant · 3 tests
search_leads is a DAILY allowance (default 3), not free's 3-per-lifetime0.8ms
a signed-up user is untouched by the test allowance0.6ms
every other tool still resolves to the FREE count for a test-cap tenant0.3ms
isTestCapUser · 2 tests
is false when TEST_CAP_USER_IDS unset0.4ms
matches ids case-insensitively in a comma list with spaces0.4ms
checkExternalToolCap scope · 3 tests
NEVER caps a signed-up user, even with an exhausted bucket0.6ms
caps a test tenant on an exhausted bucket283.1ms
ignores tools without a cap entry even for test tenants0.5ms
effectiveSendLimits · 3 tests
prod tenants keep the S3 constants0.5ms
test tenants get the testing defaults0.4ms
env overrides apply, junk values fall back0.4ms
aeo-visibility-schema.vitest.ts
14/14 16ms · 3 suites PASS
src/tools/aeo-visibility-schema.vitest.ts
the previously-undeclared arguments are now part of the contract · 4 tests
declares engines, which the dispatch has always read3.1ms
declares brand, which the dispatch has always read0.5ms
declares brand_aliases, which the dispatch has always read0.4ms
has no required fields — an omitted site resolves to the saved domain1.6ms
engines — the cost lever · 5 tests
accepts every engine we actually price, bare1.1ms
accepts the picker's real "engine:model" values, slashes and dots included0.4ms
rejects an engine we do not price — it would run with no cost estimate0.3ms
rejects a sentence where an engine belongs0.4ms
states in its own text that engines are the cost lever1.1ms
the remaining fields are bounded · 5 tests
accepts a realistic full call0.5ms
rejects a country that is not a two-letter code3.5ms
rejects a sentence in site rather than measuring the wrong brand0.3ms
rejects an off-schema field instead of silently ignoring it0.3ms
names no vendor and no USD price in the model-facing definition0.5ms
router-contradictions.vitest.ts
14/14 18ms · 5 suites PASS
src/tools/router-contradictions.vitest.ts
settled rulings are not re-opened elsewhere in the same prompt · 4 tests
never tells the model to ask which lead source to use (R3, owner 2026-07-30)3.8ms
still states the rule positively, so the absence is a decision and not an omission1.1ms
does not list the source question among the mandated gates either1.0ms
routes the shortfall to the paid source rather than to a question0.4ms
the prompt does not contradict itself about calling search_leads · 1 test
says both "never speculatively" and how the user asks for it, without a third rule0.6ms
a request that names WHO does not get the existing-or-new gate · 4 tests
does not carry the unconditional "list_contacts first" order any more0.8ms
states the specified-request branch positively, so its absence would be a failure1.0ms
keeps the check-existing branch for the ask it was actually written for0.5ms
agrees with the list_contacts description instead of contradicting it0.4ms
the judge is graded against the rules that actually ship · 4 tests
does not tell the judge the source question is mandatory0.5ms
tells the judge that a named audience goes straight to the search1.4ms
still tells the judge the ask is mandated when nobody was named0.3ms
splits on the SAME discriminators as the agent prompt3.3ms
the substitution above is only sound while these tools are always sent · 1 test
search_leads and list_contacts are CORE0.7ms
semantic-enum.vitest.ts
14/14 40ms · 3 suites PASS
src/tools/semantic-enum.vitest.ts
the SaaS failure, replayed · 5 tests
is the semantic-distance case: zero lexical candidates4.1ms
rejects — but now hands the model the full vocabulary to map into5.7ms
accepts the mapped retry0.4ms
does not ship the whole taxonomy when five candidates suffice2.4ms
gives a sentence no vocabulary1.8ms
droppable marking respects structure · 2 tests
marks the optional enum filter droppable1.8ms
never marks a cross-field rule droppable0.7ms
degrade, don't die — the shared implementation the seam actually runs · 7 tests
DEGRADES ON THE FIRST FAILURE — the retry it used to wait for does not happen3.4ms
degrades on the second failure of the same field, and says what it dropped3.2ms
the rescue does not depend on a counter the caller increments AFTERWARDS6.5ms
the AGENT path states the drop itself — it is not left to the model6.3ms
a REQUIRED field is still never dropped — degrading structure is not a rescue0.4ms
refuses to degrade when dropping would leave nothing to search on1.6ms
never drops a schema-required field0.4ms
aeo-presence.vitest.ts
14/14 14ms · 3 suites PASS
src/seo/aeo-presence.vitest.ts
readAeoPresence — the live "effectively absent" run · 6 tests
does not grade a cited site absent5.0ms
counts the field the citation sat in, and where in it1.0ms
keeps citation, mention and share of voice as three numbers (AEO-003)0.7ms
states the honest reading in answers RECEIVED (AEO-001)0.8ms
names absence as the reading this run does not support0.6ms
carries the vintage of the snapshot it read0.4ms
readAeoPresence — the states that are genuinely absent · 5 tests
grades a run with no citation and no mention as absent0.8ms
separates mentioned-but-not-cited from absent0.8ms
reports "not measured" rather than absent when no answer came back0.5ms
grades a top-of-field citation as present0.9ms
never claims a field size for a run that captured no sources (trap 14)0.7ms
readAeoPresence — a silent engine is not an answer (AEO-002) · 3 tests
counts only the engines that actually answered0.5ms
states the honest denominator rather than the execution count0.3ms
still prefers the stored count when the snapshot carries one0.4ms
ai-optimization.vitest.ts
14/14 21ms · 5 suites PASS
src/seo/ai-optimization.vitest.ts
aioReferencesFromItem · 2 tests
parses top-level and nested element references, dedupes, derives domain from url9.1ms
returns empty for an item without references2.0ms
DFS engine map · 1 test
covers the four consumer engines used by seo_geo_visibility0.6ms
distillSeedKeyword · 4 tests
strips question framing + stopwords down to keyword-shaped terms0.8ms
caps at maxWords0.3ms
falls back to raw words when everything is a stopword0.2ms
returns empty string for empty input0.3ms
AI Mode is a separate surface, not a fifth engine · 3 tests
stays out of the engine matrix, so a user cannot deselect it as if it were a model0.8ms
is priced per query and costs more than the AI-Overview check1.1ms
is inside the fan-out estimate the approval card quotes0.4ms
AI Overview references come only from known organic elements · 4 tests
counts the element types the live response actually returns1.0ms
keeps the block-level references alongside them0.6ms
does NOT count references from an element type it does not recognise1.5ms
reports an unknown type rather than skipping it quietly2.3ms
audit-freshness.vitest.ts
14/14 13ms · 4 suites PASS
src/seo/audit-freshness.vitest.ts
reading the last run · 4 tests
reads the snapshot store for the two audits, each under its own audit_type4.1ms
counts only COMPLETED spider runs0.9ms
scopes to the tenant and the site1.0ms
does not query at all without a site — nothing to compare against0.8ms
when it must say nothing · 3 tests
is silent when the site was never measured — the run is the whole point0.5ms
is silent in the grey zone rather than guessing0.8ms
treats a future timestamp as a clock problem, not a finding0.2ms
when it speaks · 5 tests
names the age and leaves the decision alone1.5ms
says "earlier today" rather than "0 days ago"0.4ms
pluralises, and uses each tool's own words0.7ms
never names a backend vendor (CLAUDE.md §2)1.2ms
fires right up to the threshold and stops exactly at it0.3ms
age arithmetic · 2 tests
is null for an unparseable or absent stamp, never 00.2ms
measures in whole and fractional days from the stamp0.3ms
full-audit-needs-audits.vitest.ts
14/14 35ms · 4 suites PASS
src/seo/full-audit-needs-audits.vitest.ts
the deliberate empty path is not an error · 6 tests
full_seo_audit returns guidance, never `error`10.4ms
aeo_full_audit returns guidance, never `error`1.0ms
full_seo_audit ships the executable chips FR-051 promised0.5ms
aeo_full_audit ships the executable chips FR-051 promised0.4ms
the SEO chips are the SAME strings the picker offers, not a paraphrase2.4ms
detectSoftFailure still tests `error` first — which is WHY the field name mattered0.6ms
the empty case renders as a report, not a bare sentence · 1 test
full_seo_audit has a needs_audits branch, like its AEO twin0.9ms
every author agrees the tool runs no crawl · 3 tests
the chip no longer promises a crawl it cannot do1.2ms
the registry no longer ROUTES THE MODEL to it at all — one author fewer1.2ms
the timeout comment no longer describes the removed nested runs1.9ms
the nothing-measured result survives every consumer · 4 tests
formatToolResult returns the guidance instead of throwing on result.onpage8.6ms
the crash is pinned: the old shape would have read .score off nothing2.3ms
the chips reach the user, and are the tool’s own2.4ms
a REAL error still formats as an error — the branch is not a catch-all0.5ms
geo-position-band.vitest.ts
14/14 38ms · 3 suites PASS
src/seo/geo-position-band.vitest.ts
the report states where you rank, and admits what it could not see · 5 tests
renders the band and its measured citation rate9.7ms
says plainly when the page is not in the top 10, rather than implying it does not rank1.0ms
discloses the top-10-vs-top-20 truncation through the generic *_note pass0.5ms
shows the user's own page signals beside the competitors'0.5ms
does not JSON-dump the result0.7ms
#57 changed the cost, so the estimate changed with it · 4 tests
prices the added SERP lookup0.4ms
carries a per-call estimator, because the plan narrows what it runs1.1ms
quotes a free-tier run at the ONE engine it actually bills, not at four0.5ms
falls back to the static ceiling when the plan is unknown1.1ms
the visibility check shows movement, because the history was already there · 5 tests
reports the lift against the previous run19.9ms
reports a drop just as plainly0.7ms
calls a FIRST check a baseline, not "no change"0.5ms
says no change only when the number genuinely did not move0.3ms
a basis change reports NO delta and says why — the case the old field could not express0.3ms
google-ads-keywords.vitest.ts
14/14 70ms · 4 suites PASS
src/seo/google-ads-keywords.vitest.ts
parsing what Google actually returns · 5 tests
reads volumes that arrive as strings5.7ms
an idea with no metrics is null volume, NOT zero0.8ms
keeps a genuine zero distinct from a missing one0.5ms
lowercases and de-duplicates, because two seeds converge on one idea0.4ms
survives a shape that is not a result list0.7ms
the seed is exactly one of three shapes · 3 tests
maps each to the field Google expects0.7ms
never sends more than 20 seeds — Google 400s rather than truncating1.5ms
sends exactly one seed field, never two0.8ms
it says why it cannot run · 2 tests
names the missing credential instead of returning a bare empty list1.1ms
makes no request when it is not configured1.9ms
the live call · 4 tests
authenticates with X-Treg-Token, not Authorization47.8ms
always bounds pageSize — the unbounded response is thousands of ideas1.7ms
degrades with a named reason rather than throwing3.5ms
distinguishes an access revocation from an ordinary failure1.2ms
revenue-attribution.vitest.ts
14/14 78ms · 2 suites PASS
src/seo/revenue-attribution.vitest.ts
revenue-attribution · 12 tests
every attribution query carries include_all_channels + skip_snapshot (organic-filter guard)3.4ms
classifies every AI engine referrer as AI Chat Engines1.1ms
classifies search/social/email/direct/referral/other buckets0.5ms
never classifies (direct)/(not set) as AI0.2ms
share-of-voice shares sum to ~100 and AI bucket survives low volume2.3ms
guards AOV/conversion division by zero and flags missing ecommerce0.4ms
degrades per-section on partial failure without tripping the advisory0.5ms
returns a single error when every query fails0.4ms
formats GA4 dates and sorts the daily trend10.8ms
fetchPropertyCurrency falls back to null on non-200 and returns the code on 20040.8ms
fetchRevenueAttribution requires Google connected0.7ms
fetchRevenueAttribution assembles stubbed query results end-to-end1.6ms
fetchSeoGoogleMerge regressions (attribution must never break the organic join) · 2 tests
REGRESSION: attribution failure leaves organic rows/insights intact13.8ms
REGRESSION: successful attribution attaches without altering the organic row shape1.0ms
stack-fingerprint.vitest.ts
14/14 62ms · 4 suites PASS
src/seo/stack-fingerprint.vitest.ts
detectStackFingerprint — frameworks · 3 tests
nextjs pages-router via __NEXT_DATA__49.1ms
nextjs app-router via /_next/ without __NEXT_DATA__1.0ms
nuxt / sveltekit / gatsby / astro / remix markers2.1ms
detectStackFingerprint — CMS and builders · 4 tests
wordpress + yoast plugin0.5ms
wordpress + rank math0.5ms
shopify / webflow / wix / squarespace / framer0.5ms
generator meta fallback is medium confidence1.0ms
detectStackFingerprint — hosting + unknowns · 3 tests
host detection from headers0.7ms
bare react is low confidence; empty page is null/low1.2ms
accepts a real Headers object2.2ms
stackGuidance — prompt placement snippets · 4 tests
nextjs app-router points at app/layout.tsx0.8ms
wordpress+yoast prefers the plugin UI over code0.3ms
no-code builders route to settings pages, static files flagged unsupported0.2ms
low-confidence and null fingerprints yield NO guidance (generic fallback)0.4ms
tag-provenance.vitest.mjs
14/14 13ms · 4 suites PASS
scripts/lib/tag-provenance.vitest.mjs
versionFromSource · 3 tests
reads APP_VERSION4.7ms
returns null rather than guessing when the constant is absent1.0ms
is not fooled by the file's own prose mentioning the name0.4ms
buildSetterMap — first appearance wins · 3 tests
maps a version to the FIRST commit that set it, not a later carrier1.0ms
omits versions nothing set — the absence is the finding0.4ms
skips commits whose version.ts cannot be read (shallow boundary)0.7ms
classifyTag — the four states, from real measurements · 5 tests
exact: on main, at the setter0.5ms
off_main: the squash-merge orphan this whole change exists for0.3ms
off_main wins even when no setter exists — unreachable is the stronger fact1.2ms
version_never_on_main: a tag naming a release that never shipped0.6ms
not_setter: on main, but the range would be wrong0.5ms
isRepairable — a repair needs somewhere to move the tag TO · 3 tests
off_main and not_setter have a setter to move onto0.4ms
a phantom is NOT repairable — inventing a target is the defect, not the fix0.3ms
an exact tag is not something to repair0.2ms
plan-open-telemetry.vitest.ts
13/13 17ms · 4 suites PASS
client/plan-open-telemetry.vitest.ts
the counts are transported, not recounted · 3 tests
reads all five numbers off the server-baked marker4.6ms
a report with no marker yields null — never a zero-filled object0.6ms
a malformed attribute degrades to 0 rather than NaN1.1ms
the server authors the counts · 3 tests
bakes a plan-open-marker carrying all five attributes2.6ms
executable uses the SAME condition that renders the Execute button0.7ms
the marker is inside the plan branch only0.9ms
both entry points emit, and only for plans · 5 tests
the live panel and the stored artifact each call trackPlanOpened0.5ms
each emission is GATED on the marker being present1.7ms
trackPlanOpened is imported, not shadowed by a local helper0.8ms
the event carries executable_count and the precomputed had_executable1.4ms
source distinguishes the live plan from a snapshot0.4ms
the event has a reader · 2 tests
plan_opened is in the admin panel TRACKED_EVENTS list0.4ms
the panel family that raised this question is also read now0.4ms
tool-costs.vitest.ts
13/13 14ms · 1 suite PASS
src/billing/tool-costs.vitest.ts
tool-costs — cost gate · 13 tests
has valid cost-estimate values3.8ms
formats token counts0.3ms
builds the cost-gate message0.5ms
builds the cost-approval card0.4ms
resolves cost approval by mode0.7ms
pins the cost-gate threshold0.3ms
write→consume happy path6.0ms
consume of a missing record is expired0.3ms
double consume is one-shot0.4ms
fails closed when KV is unbound0.3ms
recalibrated AEO fan-out costs scale with engines × prompts0.5ms
recalibrated aeo_visibility covers the 2026-07-13 incident's real max spend0.2ms
every tool implicated in the 2026-07-13 audit now has a declared estimate0.2ms
defect-class.vitest.ts
13/13 13ms · 3 suites PASS
src/admin/defect-class.vitest.ts
it never guesses · 3 tests
a session with no render manifest is UNCLASSIFIED, not a defect3.5ms
NEVER assigns a root cause3.3ms
carries the EVIDENCE, not a restatement of the label0.7ms
the classes, each pinned to a session that really happened · 8 tests
judge — scored 1.00 and delivered nothing0.5ms
presentation — 21 rows reached the user and it still scored 0.680.8ms
functional — tools ran, zero rows0.8ms
interface — a tool ran and reported an empty outcome0.5ms
usability — never got past being asked things0.3ms
performance — slow, and nothing to show for it0.5ms
none — delivered and judged acceptably0.5ms
an ARTIFACT counts as delivery even with no rows0.4ms
ordering is a decision, not an accident · 2 tests
a judge defect outranks everything it would otherwise hide0.3ms
a slow session that DID deliver is not a performance defect0.3ms
gsc-coverage-sampler.vitest.ts
13/13 70ms · 3 suites PASS
src/admin/gsc-coverage-sampler.vitest.ts
fetchAdminGscIndexCoverage — quota + failure disclosure · 6 tests
reserves the shared daily quota once per inspected URL (the Deep scan already did; this path did not)37.7ms
halts and flags quota_exhausted when the daily budget is spent, instead of hammering on1.4ms
counts partial failures and returns the reason (this was computed then silently discarded)1.2ms
treats a 429 as rate-limited and stops the run rather than burning the rest of the sample1.1ms
never opens more than the bounded number of concurrent inspections14.2ms
reports the real universe size so the card can show a denominator, not a bare count4.9ms
isCoverageStale — the gate that froze the page · 4 tests
treats a missing cache as stale (first run must always sample)0.4ms
holds a fresh snapshot inside the window — this is correct, and is why an explicit force is REQUIRED0.2ms
opens once past the window0.3ms
treats an unparseable timestamp as stale rather than trusting it0.3ms
admin GSC performance requests — the shape every number depends on · 3 tests
asks for fresh (preliminary) data, not only finalised days1.2ms
fetches property totals with NO dimensions, aggregated byProperty2.1ms
surfaces Google's own first_incomplete_date instead of guessing a fixed lag0.5ms
verify-readiness.vitest.ts
13/13 13ms · 3 suites PASS
src/campaigns/verify-readiness.vitest.ts
when it must say nothing · 6 tests
stays silent when the caller ALREADY asked to skip checked addresses3.2ms
stays silent when no target is named — that turn is a picker, not a purchase0.6ms
says nothing about a trivial number of re-checks0.4ms
says nothing about a trivial SHARE, even when the count is large0.4ms
says nothing when nothing has been checked — the ordinary, correct case0.3ms
says nothing about an empty target0.3ms
when it speaks, the number is the one that would happen · 3 tests
quotes the re-checked count and offers the one-click fix2.0ms
says ALL when it is all of them, rather than "100 of 100"0.4ms
adds the staleness clause only when it changes the advice0.7ms
the query mirrors the dispatcher, not a reasonable-sounding rule · 4 tests
counts a PROVIDER CLAIM as unchecked — migration 050, and it decides money1.0ms
scopes every count to the tenant2.2ms
counts stale as a subset of CHECKED, never of the whole target0.5ms
reads list_name as well as filter.in_lists — the gate runs UPSTREAM of the validator0.5ms
metrics.vitest.ts
13/13 17ms · 3 suites PASS
src/commerce/metrics.vitest.ts
computeOrderEconomics · 4 tests
matches the live drill orders exactly (#1001 + #1002)5.7ms
NetRevenue = GrossSales − Discounts − Refunds (invariant holds under refunds)1.0ms
excludes cancelled and test orders but counts them transparently1.7ms
tax and shipping stay out of merchandise revenue0.8ms
computeProfitRollup · 5 tests
known contribution margin covers only costed variants; coverage reported honestly1.6ms
excluded (cancelled/test) order lines contribute nothing0.5ms
estimated layer: uncovered lines assumed at 50% of selling price, clearly labelled1.4ms
estimated layer absent at 100% coverage0.4ms
empty window degrades to zeros, no division blowups0.6ms
rankProductMargins · 4 tests
ranks by margin RATE (margin_pct), not absolute margin0.8ms
products with no cost coverage are unrankable, never guessed into the ranking0.8ms
worst list is worst-first and disjoint ordering holds with >10 products1.0ms
empty and all-unrankable inputs degrade honestly0.5ms
start-sequence.vitest.ts
13/13 21ms · 4 suites PASS
src/drip/start-sequence.vitest.ts
the gate, not the appetite for risk · 3 tests
is declared external — starting is at least as consequential as enrolling3.0ms
carries a preflight assessor, so it cannot be an ungated external tool1.7ms
loads with the campaigns family, beside the two moves that precede it0.5ms
what the start is about to do, said first · 3 tests
leads with the blocker when the tenant cannot send at all0.7ms
states the volume when a start commits to real mail0.5ms
says nothing about an ordinary small start0.3ms
the reply distinguishes the three actions · 3 tests
a start says what leaves and when7.0ms
a pause is explicit that nobody was unenrolled1.0ms
an error reads as an error3.5ms
nobody enrolled is not a success · 4 tests
a start that flips zero enrolments is reported as an error, not as started0.6ms
refuses a sequence with no steps rather than starting an empty send0.4ms
reuses handleSequenceStatus instead of writing a second status mutation0.3ms
resolves the sequence with two scoped queries, never one _or0.3ms
aeo-brief-misroute.vitest.ts
13/13 32ms · 3 suites PASS
src/chat/aeo-brief-misroute.vitest.ts
the three turns that produced this fix · 4 tests
"dashboard" no longer opens the engine selector18.8ms
"cmo" no longer opens the engine selector4.4ms
"omnibus" no longer opens the engine selector0.4ms
each names well past the three-subject floor0.7ms
REAL visibility asks still route — the fix must not eat its own intent · 6 tests
still claims: "Check my AI visibility for detailingdevils.com"0.6ms
still claims: "Analyze how AI answer engines discover, describe and"0.5ms
still claims: "am I cited by chatgpt"1.8ms
still claims: "Run an AEO check"0.3ms
still claims: "is my brand visible in AI search"1.2ms
TWO subjects is still a question, not a brief0.6ms
the count is a SHAPE, not a keyword list · 3 tests
a phrasing nobody has written yet is still caught0.6ms
does not disturb the carve-outs that already existed1.0ms
a message naming no subject at all is not a brief0.2ms
cot.vitest.ts
13/13 10ms · 4 suites PASS
src/chat/cot.vitest.ts
classifyTurnComplexity (cost governor) · 5 tests
simple turns stay simple3.4ms
multi-step: two+ detected intents or explicit sequencing0.4ms
reasoning: numeric/multi-constraint or deliberative cues0.6ms
long deliberative message → reasoning0.3ms
multi_step dominates reasoning when both cues present0.3ms
cotRolloutAllows (default OFF — no prod change until flipped) · 3 tests
simple turns never get CoT regardless of flag0.8ms
off / unknown / undefined disables all classes0.3ms
per-class flags gate correctly1.1ms
cotBlockFor · 1 test
returns the class-appropriate block or null0.9ms
stripReasoning (Phase 1: reasoning improves the answer but is not exposed) · 4 tests
removes a closed scratchpad and keeps the answer0.8ms
drops an unterminated scratchpad entirely (never ships a truncated one)0.4ms
passes through text with no scratchpad unchanged0.3ms
is case-insensitive and handles multiple blocks0.2ms
site-resolution.vitest.ts
13/13 22ms · 4 suites PASS
src/chat/site-resolution.vitest.ts
the transcript that produced this file · 2 tests
recovers the domain the user typed earlier4.1ms
still resolves when the picker CHOICE is the message0.7ms
precedence, and why each rung is where it is · 4 tests
the CURRENT message overrides the stored site1.1ms
the stored site beats conversation history0.4ms
the LAST domain in a message wins — a correction is the live one0.4ms
no site anywhere returns null, so the caller ASKS0.3ms
what it must NOT resolve — the ways a naive scan goes wrong · 5 tests
NEVER reads the assistant's own turns0.6ms
placeholder hosts are not sites even when a USER types one0.5ms
files and endpoints are not hostnames0.6ms
a bare name with no TLD is not a domain0.3ms
survives junk input rather than throwing0.4ms
BOTH pickers use it — a fix on one of two is a fix on none · 2 tests
the SEO audit picker resolves the site BEFORE showing the menu6.2ms
the AEO picker uses the SAME resolver, not its own regex5.2ms
stream-parse.vitest.ts
13/13 60ms · 3 suites PASS
src/llm/stream-parse.vitest.ts
readToolStream — content · 5 tests
concatenates deltas and reports each one exactly once36.2ms
keeps the usage object, including cost — an unbilled turn is a revenue hole1.4ms
does not treat the usage chunk as content1.6ms
survives the payload being split at EVERY byte4.4ms
ignores keep-alive comments and the DONE sentinel0.6ms
readToolStream — tool calls · 5 tests
CONCATENATES argument fragments instead of parsing one1.8ms
reassembles correctly even split at every byte7.8ms
keeps parallel calls apart by index, in index order1.3ms
announces the first tool-call delta exactly once1.2ms
does not announce a tool call when the turn is pure text0.7ms
readToolStream — degrading · 3 tests
skips malformed JSON rather than failing the turn0.8ms
never lets a display callback break the model call0.7ms
returns an empty turn for a bodyless response instead of throwing0.4ms
report-emitters.vitest.ts
13/13 19ms · 1 suite PASS
src/planner/report-emitters.vitest.ts
planner report emitters · 13 tests
emits one row per not-indexed page plus canonical and sitemap findings5.2ms
caps not-indexed emission at 5 pages0.6ms
emits nothing on error or healthy report1.1ms
unverified canonical is not a finding0.3ms
turns top_priorities into ordered rows grounded in audit scores1.3ms
emits nothing without synthesis or on error0.5ms
anchors dedupe to the campaign id so generic advice stays per-campaign3.5ms
emits nothing without a next step0.5ms
fills source fields, hash, and recheck_at (default 14d, override honored)1.9ms
stamps campaign report rows with a replies measurement contract and baseline0.7ms
stamps full SEO audit rows with an on-page score contract when evidence exists0.5ms
keeps report rows without a numeric observable in the non-learning class0.9ms
same advice re-emitted hashes identically (rerun collapses into existing row)0.5ms
aeo-adjacent-report.vitest.ts
13/13 13ms · 5 suites PASS
src/reports/aeo-adjacent-report.vitest.ts
tap_volume — §17 gold standard · 3 tests
renders a signal bento with the model outputs + growth levers2.8ms
has plain headers and a feedback mount1.0ms
free-tier run shows the 3-prompt cap note + a token top-up section; paid shows neither0.9ms
aeo_visibility — free-tier cap upsell · 2 tests
free run: hero cap note + top-up section naming all four engines1.0ms
paid run: no cap note, no top-up section1.6ms
sov_trend — §17 gold standard · 2 tests
renders a signal bento — SoV, trend direction, tracking coverage0.6ms
has plain headers and a feedback mount0.5ms
aeo_full_audit — §17 gold standard (composite) · 3 tests
renders a top-level pillar signal bento0.5ms
carries exactly ONE feedback mount (nested sub-report mounts stripped)0.8ms
still stitches the three sub-reports0.4ms
seo_geo_visibility — a capped sample must not be coloured like a full run · 3 tests
colours a full run with confidence0.6ms
greys the score when the run was capped to one engine and a short prompt set0.6ms
still says a capped run happened, in the hero, without naming a vendor or a price0.5ms
google-merge-report.vitest.ts
13/13 8ms · 2 suites PASS
src/reports/google-merge-report.vitest.ts
seo_google_merge report — the merge finally gets a card · 10 tests
renders an HTML report, not null (the plain-text regression)3.1ms
aggregates date+page rows into page-level totals0.7ms
renders GSC and GA4 columns side by side (the merge is the point)0.6ms
shows all four opportunity lists0.3ms
renders compare deltas when compare was requested0.2ms
shows top queries and join diagnostics0.7ms
advice items are rule-built from the insight lists (copy buttons come from adviceSection)0.3ms
error and truly-empty results (no attribution either) fall back to plain text0.4ms
states cost explicitly in the footer — never silent about a free tool (CLAUDE.md §4)0.6ms
is a background (Tier-C) tool and produces an artifact for the async completion0.4ms
seo_google_merge report — empty organic join, populated attribution still gets a card · 3 tests
renders an HTML report — no organic rows is not "nothing to show"0.2ms
shows the share-of-voice breakdown, channels table, and AI-engine line0.2ms
still states cost even on the empty-organic path0.2ms
publish-affordance.vitest.ts
13/13 59ms · 3 suites PASS
src/reports/publish-affordance.vitest.ts
stored artifact — carries a slot, never frozen connector state · 6 tests
emits a publish slot49.0ms
bakes no live control — both connected1.5ms
bakes no live control — neither connected1.2ms
bakes no live control — flags absent entirely0.6ms
bakes no live control — non-WordPress stack0.8ms
the connector flags do not change the stored markup at all1.0ms
stored artifact — the fallback is true regardless of connectors · 2 tests
offers the coding-agent prompt, which needs no connector0.5ms
names a known stack in the fallback copy0.6ms
stored artifact — carries everything hydration needs · 5 tests
passes the stack fingerprint through as data0.7ms
empty stack when unknown, so the client reads null rather than a guess0.6ms
carries the article payload the publish handlers read0.5ms
carries the agent prompt for the fallback copy button0.3ms
subtitle is a hydration target, so it can never describe absent buttons0.4ms
pillar-simulator.vitest.ts
13/13 84ms · 2 suites PASS
src/routes/pillar-simulator.vitest.ts
the slot contract, across both pillar pages · 7 tests
.v-out is green and .v-sup is red — the colours the labels have to agree with8.4ms
/ai-visibility-answered: v-out always reads as a ruling-out, v-sup never does59.1ms
/ai-visibility-answered: renders the shared simulator, not a hand-rolled copy2.3ms
/ai-visibility-answered: exactly one chip starts active2.5ms
/seo-questions-answered: v-out always reads as a ruling-out, v-sup never does2.4ms
/seo-questions-answered: renders the shared simulator, not a hand-rolled copy1.8ms
/seo-questions-answered: exactly one chip starts active2.3ms
pillarSimulatorHtml · 6 tests
defaults to the SEO vocabulary1.3ms
a page may override the words0.9ms
the first example is rendered server-side — a reader with no JS still sees an answer0.6ms
inlines every example, so switching chips needs no request0.4ms
verdictsHtml pairs each tag with its own label0.4ms
an overridden head replaces the caption0.2ms
cost-gate-recovery.vitest.ts
13/13 15ms · 4 suites PASS
src/runtime/cost-gate-recovery.vitest.ts
the window · 3 tests
is 600s, the same as every sibling gate2.8ms
would have covered the click that was lost0.4ms
writes the authorisation short and the receipt long4.3ms
recovery after expiry · 3 tests
keeps the tools and the QUOTE so a re-armed card cannot change the price1.7ms
lets the receipt identify WHICH card expired0.5ms
re-arming mints a NEW id and a fresh authorisation0.8ms
the replay guard · 4 tests
destroys the receipt when the authorisation is SPENT0.5ms
a second click after a successful run recovers NOTHING0.4ms
destroys the receipt when the user says no0.7ms
clears the receipt on the legacy array-shaped record too0.4ms
degradation · 3 tests
is inert with no KV bound0.9ms
still returns a usable card id when only the RECEIPT write fails0.7ms
ignores a corrupt receipt rather than throwing0.3ms
leads-family-rules.vitest.ts
13/13 14ms · 2 suites PASS
src/tools/leads-family-rules.vitest.ts
every rule from the deleted LEADS block still reaches the model · 10 tests
(1) a request that NAMES who they want goes straight to search_leads6.0ms
(1b) and the measurement that motivated it survives1.1ms
(2) a request that names NOBODY calls list_contacts first and asks0.7ms
(3) the price, the worked example, and SCALE-DON'T-REFUSE0.5ms
(3b) never ask which lead source to use0.3ms
(3c) the shortfall goes to the paid card, not back to a question0.3ms
(3d) search_leads is never called speculatively0.4ms
(4) an in-flight search is reported as in-flight, never as empty — THE MOVED RULE0.8ms
(5) list_contacts reads what is already saved and cannot find anyone new0.6ms
(6) enrich_contacts adds a homepage summary and recent news0.7ms
and the duplication is actually gone · 3 tests
V2_SYSTEM no longer carries a LEADS FAMILY routing block0.5ms
the price is stated ONCE, on the tool that charges it0.5ms
V2_SYSTEM stays under its ratchet0.3ms
grounded-signals.vitest.ts
13/13 64ms · 2 suites PASS
src/seo/grounded-signals.vitest.ts
readGroundedSignals · 7 tests
reads every field the renderers were missing5.0ms
drops a claim with no source — the section promises "which source fed this"2.2ms
returns null when the leg errored or never ran, so nothing renders an empty shell1.3ms
an absent array and an empty array are the same READ but never invent rows (trap 14)0.5ms
fan-out 0 and fan-out null are different facts0.9ms
carries the per-engine coverage note and names the engine (AEO-014 / AEO-008)0.9ms
derives the reading ONCE so the artifact and the panel cannot word it differently1.0ms
aeo_visibility artifact renders the grounded payload (R-B) · 6 tests
shows fan-out, its reading, the vocabulary and the coverage note46.4ms
shows which source produced which claim — and only sourced ones1.5ms
leads with entity collision — it is the CAUSE of the tables under it0.9ms
a run whose grounded leg failed renders NO grounded sections, not empty ones0.6ms
an artifact stored before R-B has no share_of_model at all and still renders0.6ms
no collision on a healthy brand — the banner is a finding, not a header0.7ms
hosting-footprint.vitest.ts
13/13 16ms · 4 suites PASS
src/seo/hosting-footprint.vitest.ts
subnet24 · 2 tests
truncates dotted-quad to /243.8ms
rejects anything that is not a dotted quad, including IPv60.8ms
isSharedInfra · 2 tests
matches on ASN first0.5ms
falls back to the operator name when the ASN is unknown1.1ms
computeHostingFootprint — the bklink defects it must not reproduce · 3 tests
counts a repeated domain once, no matter how many links it carries3.0ms
does not call a Cloudflare cluster a footprint0.8ms
excludes unresolved domains from the denominator and reports them by reason1.5ms
computeHostingFootprint — verdicts · 6 tests
flags a real footprint when a non-CDN IP dominates1.1ms
calls a one-domain-per-server profile diverse0.9ms
refuses a verdict below the resolved-domain floor and says so0.7ms
states the basis — how many resolved, and how many did not — in the note0.6ms
prefers the announced prefix over an assumed /24 for prefix diversity0.2ms
counts an AAAA-only domain as a resolved distinct host0.3ms
sov-write.vitest.ts
13/13 17ms · 3 suites PASS
src/seo/sov-write.vitest.ts
persistSovPoint — one writer, one stamped basis · 5 tests
writes exactly one `sov` row and stamps what the share was measured on7.4ms
returns the SAME stamped object the row carries, so the caller embeds one share not two0.9ms
writes nothing when there is no share — an absent share is not a zero (AEO-002)0.5ms
stamps a matrix-only run as measured on no engine at all, not as ChatGPT0.9ms
the two live records now carry ONE basis where they carried two2.8ms
the count a run reports about itself · 5 tests
records answers separately from cells, because cells are prompts x engines0.7ms
stores an empty engine list rather than a default, so absence is not read as four engines0.9ms
never stores an engine key carrying a model slug — that is a vendor name (CLAUDE.md §4)0.7ms
drops a token that is not an engine at all rather than passing it through0.5ms
omits answers_received rather than guessing when the caller has no count0.5ms
storedSovBasis — snapshots of both shapes keep rendering · 3 tests
reads the stamp on a v2.491.0+ row0.3ms
recomputes from by_engine on a row written before the stamp existed0.3ms
does not invent a basis for a row with no share block at all0.2ms
org-country-presence.vitest.ts
13/13 11ms · 6 suites PASS
src/leads/shared/org-country-presence.vitest.ts
157 · organization.country_code is never written · 2 tests
no UPDATE sets organization.country_code3.1ms
the fix is a side table, not a rewrite0.6ms
157 · the presence table is derived from country DISAGREEMENT · 2 tests
only rows where the two country codes differ0.4ms
joins the person to its primary employer0.4ms
157 · the refresh is additive and only fills nulls · 2 tests
never deletes or truncates the presence table0.5ms
the conflict arm coalesces rather than overwrites0.7ms
157 · 153's two arms survive byte-for-byte · 3 tests
still scopes the organization arms by country0.5ms
adds exactly two new arms, both country-scoped1.3ms
passes country and region to all four arms0.6ms
157 · both new arms are index-served · 1 test
mirrors the organization trigram indexes onto the presence table0.6ms
157 · the self-verification is bounded, not merely positive · 3 tests
asserts the US is unchanged, both as row count and as predicate output0.4ms
bounds the India gain BELOW a measured figure, not just above the old value0.2ms
asserts both indexes exist0.5ms
query-embedding.vitest.ts
13/13 30ms · 3 suites PASS
src/leads/shared/query-embedding.vitest.ts
the model contract with the index · 3 tests
names the SAME model_id the corpus was embedded with and 085 indexed3.9ms
requests the pinned dimension count from the provider11.0ms
REJECTS a wrong-width vector here rather than letting Postgres reject it1.6ms
null is a first-class answer, never a throw · 5 tests
no API key configured — a deployment state, not a runtime failure0.8ms
provider returns a non-2xx1.5ms
provider throws or the request is aborted1.2ms
provider answers with a malformed payload1.2ms
empty query — nothing to embed0.9ms
caching · 5 tests
does not pay the provider twice for the same query1.8ms
normalizes so trivially different spellings share one entry1.1ms
a BROKEN cache degrades to a paid call, never to a failed search1.1ms
stores no raw search text in the key — it is hashed and model-scoped1.4ms
caches only what it would return — a rejected vector is never stored1.5ms
aeo-selector-quote.vitest.ts
12/12 19ms · 1 suite PASS
src/billing/aeo-selector-quote.vitest.ts
engine selector quote · 12 tests
perplexity × 18 avg: the selector's arithmetic equals the card's quote3.0ms
perplexity × 18 p90: the selector's arithmetic equals the card's quote0.3ms
perplexity × 12 avg: the selector's arithmetic equals the card's quote0.3ms
perplexity × 12 p90: the selector's arithmetic equals the card's quote0.4ms
chatgpt+claude+gemini+perplexity × 18 avg: the selector's arithmetic equals the card's quote0.3ms
chatgpt+claude+gemini+perplexity × 18 p90: the selector's arithmetic equals the card's quote0.4ms
chatgpt × 3 avg: the selector's arithmetic equals the card's quote0.3ms
chatgpt × 3 p90: the selector's arithmetic equals the card's quote0.2ms
gemini+claude × 7 avg: the selector's arithmetic equals the card's quote0.4ms
gemini+claude × 7 p90: the selector's arithmetic equals the card's quote0.4ms
the per-prompt legs are not a rounding term — they exceed a Perplexity call0.5ms
the picker carries the quote and the plan cap; the client reads both and no longer trusts its mirror first12.2ms
backlinks-pricing.vitest.ts
12/12 8ms · 3 suites PASS
src/billing/backlinks-pricing.vitest.ts
dfsBacklinksCostUsd — the published formula · 6 tests
reproduces DFS's own worked example exactly3.1ms
prices the task fee at 667x a single row — the fact that decides which dial to turn0.6ms
shows that 100x the rows costs only 2.46x the money0.5ms
charges for the extra REQUESTS when rows exceed one call, never pretending it is one task0.4ms
never bills below one task, because a request was still made0.6ms
honours an explicit task count above the row-derived minimum (summary + list)0.4ms
free-tier caps on the backlink family · 4 tests
caps the free gap by DOMAINS, and paid gets the provider row ceiling0.4ms
bounds seo_backlink_verify even though it spends NO provider money0.5ms
never widens beyond what the caller asked for0.4ms
shows the free domain cap saves cents while runs save the unavoidable floor0.5ms
GAP_DFS_TASKS — reproduces a real invoice · 2 tests
matches what DFS actually billed for the dock.io run0.3ms
is FOUR, because each of the two legs is a summary call plus a list call0.4ms
free-tier-visibility-quote.vitest.ts
12/12 38ms · 3 suites PASS
src/billing/free-tier-visibility-quote.vitest.ts
the free tier can now afford the capability the product is named for · 8 tests
aeo_visibility: a bare call fits inside the signup grant26.9ms
aeo_visibility: the free quote REPLACES the table ceiling rather than being floored by it0.9ms
aeo_visibility: PAID is untouched — an uncapped run still asks for the real ceiling0.5ms
aeo_visibility: the plan really does narrow it — the cap is not fiction1.4ms
seo_geo_visibility: a bare call fits inside the signup grant1.1ms
seo_geo_visibility: the free quote REPLACES the table ceiling rather than being floored by it0.4ms
seo_geo_visibility: PAID is untouched — an uncapped run still asks for the real ceiling0.3ms
seo_geo_visibility: the plan really does narrow it — the cap is not fiction0.5ms
membership requires BOTH halves · 2 tests
both tools are registered as tier-aware0.4ms
search_leads is deliberately NOT — its bare estimate invents a request shape1.4ms
one implementation, so the two halves of "AEO & GEO" cannot diverge · 2 tests
both estimators route through the shared plan-capped helper2.3ms
an uncapped plan returns null, never a fabricated discount1.2ms
stripe-refund.vitest.ts
12/12 73ms · 3 suites PASS
src/billing/stripe-refund.vitest.ts
charge.refunded — ownership · 4 tests
ignores a sibling product's refund without touching the ledger47.4ms
escalates when a charge CLAIMS our metadata but its session is not ours1.4ms
escalates — never silently drops — when the session IS ours and no credit row exists0.9ms
treats a charge with no Checkout Session as not ours0.8ms
charge.refunded — the reversal · 5 tests
debits the full grant on a full refund, as a POSITIVE row2.7ms
debits proportionally on a partial refund2.8ms
writes only the DIFFERENCE when a partial refund is followed by the rest1.2ms
writes nothing on a redelivered event1.4ms
lets the balance go negative when the tokens were already spent, and says so1.5ms
the reversal moves the balance under BOTH billing models · 3 tests
cost-based: a stripe_refund row is counted in TOKEN space, not as zero-cost spend0.5ms
legacy: unchanged, because it sums total_tokens over everything0.5ms
the reversal is excluded from the user-facing spend breakdown10.6ms
tool-repricing-2026-09.vitest.ts
12/12 8ms · 3 suites PASS
src/billing/tool-repricing-2026-09.vitest.ts
entity_audit is priced against what it actually costs to deliver · 3 tests
quotes the measured p90, not the median3.1ms
the pre-2026-09-12 value would under-quote the p900.5ms
does not cross the cost gate0.3ms
the structural trap: a table entry is not a quote · 3 tests
priceOfTool exceeds the raw table entry on the agent route, by exactly the floor0.6ms
costOfCall is still provider+llm only — the floor is added above it0.4ms
a shortcut dispatch pays no orchestration floor0.4ms
the repricing sweep lands on measured p90 · 6 tests
seo_content_brief quotes its p90 (n=61)0.4ms
generate_emails quotes its p90 (n=30)0.3ms
seo_content_ideas quotes its p90 (n=41)0.3ms
only tools with a tier-A sample were touched0.6ms
none of them crosses the gate — repricing must not smuggle in a confirm card0.3ms
seo_write_content is knowingly left under-quoted, and still does not gate0.4ms
usage-read-honesty.vitest.ts
12/12 54ms · 3 suites PASS
src/billing/usage-read-honesty.vitest.ts
/api/usage/me on a healthy read · 1 test
reports real consumption against granted credits41.7ms
/api/usage/me on a failed read · 4 tests
does NOT answer 200 — a failure must be legible as a failure1.4ms
invents no numbers at all1.2ms
does not leak the raw provider error to the browser1.2ms
reports the fault — an unreadable balance is not a nothing3.6ms
the client renders unknown, not zero · 7 tests
has actually found the code it claims to test0.7ms
routes every vitals tile through a fetch that throws on a bad status0.7ms
paints the rows through the renderer module, not by hand0.8ms
still hides the boot splash when the balance read fails0.4ms
shows one honest line when the vitals read fails, and reports the fault0.6ms
keeps the token bar on its own read, so one failure does not blank the other0.3ms
the Profile panel never claims a zero balance it did not read1.2ms
broadcast-segments.vitest.ts
12/12 11ms · 5 suites PASS
src/admin/broadcast-segments.vitest.ts
segment catalogue · 3 tests
ids are unique and every segment says who it is AND what to send them3.7ms
covers every scoring tier, including ones that are currently empty1.6ms
rejects unknown ids rather than falling through to everyone0.8ms
inSegment — tier matching · 2 tests
matches a user to exactly their own tier1.4ms
all_verified takes everyone, scored or not0.5ms
inSegment — unscored users are their own segment · 3 tests
a user with no score row is "unscored", not a drifter0.3ms
a scored user is never "unscored", whatever their tier0.3ms
treats an empty-string tier as unscored rather than as a tier0.3ms
segments partition the user base · 2 tests
every user lands in exactly one targeting segment0.4ms
an unknown tier value matches NO targeting segment0.4ms
exclusion counts are scoped to the segment · 2 tests
counts only members of the segment0.4ms
reconciles: in-segment = sendable + exclusions drawn from the segment0.2ms
eval-verdict.vitest.ts
12/12 11ms · 1 suite PASS
src/admin/eval-verdict.vitest.ts
classifyEvalRun · 12 tests
calls a case that was reliably passing a REGRESSION3.6ms
does NOT fail the build for a case that has been failing on and off0.8ms
a case with ONE scattered prior failure is still treated as newly broken0.7ms
THE BUG THE FIRST DRAFT HAD: a case failing EVERY run is broken, never "flaky"0.5ms
a case failing MORE often than it passes is degraded, not flaky — and stays red0.6ms
a case that mostly passes stays flaky and green — measured d-rank-track at 4/50.9ms
scattered failures with at least one pass ARE flaky0.5ms
separates a real regression from flakes in the SAME run0.6ms
is green and quiet when nothing failed1.1ms
refuses to judge with no history rather than calling day one a regression0.5ms
only looks back HISTORY_WINDOW runs, so ancient failures do not excuse a fresh break0.4ms
deduplicates a case reported twice in one run0.3ms
gsc-trend.vitest.ts
12/12 15ms · 4 suites PASS
src/admin/gsc-trend.vitest.ts
zeroFillDailySeries · 2 tests
inserts missing days as zeros — Search Console omits no-data days entirely5.9ms
leaves an already-dense series untouched1.6ms
sumPeriod · 3 tests
recomputes CTR from summed clicks and impressions, never averaging daily CTRs0.5ms
weights position by impressions and ignores zero-impression days0.4ms
reports null position when nothing was seen at all0.5ms
comparePeriods · 5 tests
compares equal-length halves and reports real improvement0.9ms
treats a falling position as an improvement (rank 8 -> 3 is better, not worse)0.4ms
returns null percent (not Infinity) when the previous period was zero0.4ms
does NOT call rising impressions with flat clicks an improvement0.5ms
returns null for a series too short to halve1.1ms
toWeeklyBuckets · 2 tests
buckets into 7-day groups tagged with the week start1.6ms
keeps a trailing partial week rather than dropping it0.8ms
infra-health.vitest.ts
12/12 23ms · 5 suites PASS
src/admin/infra-health.vitest.ts
it takes TWO failures to cry wolf · 2 tests
one bad probe is silent — a blip is not an outage5.1ms
the second consecutive failure alerts, and only once1.4ms
a throw is a detection, not an error to swallow · 2 tests
DNS/TLS failure counts as down1.2ms
a timeout counts as down and says so1.2ms
recovery · 1 test
reports how long we were down, as a LOG not a fault3.4ms
UNKNOWN must never read as DOWN · 4 tests
no stored record reads healthy0.5ms
an UNPARSEABLE record reads healthy, not broken0.6ms
VALID json of the WRONG SHAPE reads healthy too0.7ms
an unconfigured NHOST_URL does not invent an outage0.6ms
it shares nothing with what it watches · 3 tests
never reaches the database0.6ms
state lives in Cloudflare KV, and the alert goes to Sentry0.4ms
cannot take down the tick it rides on5.8ms
preflight-rate.vitest.ts
12/12 36ms · 3 suites PASS
src/admin/preflight-rate.vitest.ts
sample size is part of the answer · 3 tests
quotes NO rate below the floor — a percent sign on six events is an anecdote in costume11.4ms
null is not zero — a suppressed rate must not read as "never fires"2.2ms
quotes a rate once the floor is cleared1.1ms
what the number means · 4 tests
never calls the rate a false-alarm rate — that needs the proceeded-anyway signal3.0ms
keeps a tool that never spoke, because silent and unwired look identical without the denominator1.5ms
says so when nothing ran at all, and points at the wiring guard0.8ms
degrades to a posture, not an exception, when the service account is unset0.6ms
the override rate — the half that actually answers §9 · 5 tests
quotes it once enough findings reached a DECISION0.8ms
refuses it below the outcome floor, and says what it is waiting for2.1ms
never folds silence into either bucket1.9ms
never reports negative unobserved when outcomes straddle the window edge1.0ms
still refuses to call the SPOKE rate a false-alarm rate7.6ms
telemetry-tables-operations-exact.vitest.ts
12/12 11ms · 2 suites PASS
src/admin/telemetry-tables-operations-exact.vitest.ts
computeOperationsTable — exact api groups · 7 tests
without exactApiGroups, behaves exactly as before (existing callers/tests unaffected)3.4ms
replaces the sample total with the exact grouped total for a non-apify provider (direct OP_MAP route)0.6ms
routes an apify group through the SAME special-case (SEO_APIFY_OPS) as the row-level path0.4ms
routes an apify group NOT in SEO_APIFY_OPS to lead.search, matching row-level behaviour0.3ms
excludes provider="quality" from exact groups, matching the row-level exclusion0.3ms
combines llm (exact, from token_by_model) and api (exact, from the grouped total) into one row cost0.3ms
an empty exactApiGroups array is treated as "no exact data", not as "zero api cost"0.3ms
computeOperationsTable — unmapped-provider catch-all · 5 tests
an unmapped provider no longer vanishes — its cost lands in the platform cluster0.5ms
keys on provider:operation, not bare operation — two unmapped providers cannot merge under one label2.2ms
does not apply the catch-all to a provider that WAS matched by OP_MAP0.6ms
applies to the row-level (sample) path too, not only the exact-groups path0.5ms
still excludes provider="quality" before reaching the catch-all0.3ms
notion-token.vitest.ts
12/12 8ms · 5 suites PASS
src/connectors/notion-token.vitest.ts
the invented expiry is gone · 2 tests
the callback records an expiry only when Notion states one2.8ms
and so does the refresh path, so it cannot reintroduce the same lie0.6ms
nothing that is not a token leaves as one · 4 tests
the raw-secret fallback is gone0.4ms
a legacy plain-string secret is accepted only if it could be a bearer token0.4ms
a parsed document with no access_token returns null, which routes to the connector gate0.5ms
the token is trimmed — a trailing newline is the classic malformed-header cause0.5ms
a refresh failure does not lose a token that never expired · 2 tests
falls back to the stored token rather than null or a blob0.4ms
and reports the failure instead of swallowing it0.3ms
the header itself was never the problem · 1 test
notionApi has always sent Bearer, which is why the value was the suspect0.5ms
a failed refresh repairs the record that caused it · 3 tests
writes expires_at back to 0, so the refresh is tried once per connection, not once per publish0.4ms
reports it as a log, not a fault — the token is valid and the publish proceeds0.4ms
and a failed repair does not break the publish it was trying to help0.3ms
backlinks-presenter.vitest.ts
12/12 12ms · 3 suites PASS
src/chat/backlinks-presenter.vitest.ts
seo_backlinks now renders its own rows · 6 tests
produces a block instead of leaving the rows to the model3.6ms
reads the valuation body in BOTH shapes it arrives in0.6ms
does NOT price links when real_backlinks is false1.8ms
DOES price them once the provider confirmed the sample0.6ms
never prices a domain with no DR, verified or not0.4ms
returns null rather than an empty table when there are no links0.3ms
the model no longer holds the rows it was transcribing · 1 test
strips domains from the model copy, and keeps everything else1.8ms
modelResultView strips nested consumed paths · 5 tests
removes value.links — the rows the user is NOT looking at and cannot trust0.5ms
keeps the rest of value, because the synthesis needs the aggregates and the note1.2ms
still removes the top-level domains it always did0.4ms
leaves untouched siblings alone0.4ms
a flat-only consumed list still behaves exactly as before0.3ms
contextual-reply.vitest.ts
12/12 9ms · 3 suites PASS
src/chat/contextual-reply.vitest.ts
typed confirmations reach the approval the button would have · 6 tests
accepts the words users actually type3.5ms
maps to the exact protocol message the SPA posts0.4ms
recognises a refusal so it cancels instead of falling through0.5ms
REGRESSION: internal punctuation must not defeat the match0.7ms
REGRESSION: a punctuated refusal cancels — the more dangerous half0.6ms
keeps the apostrophe form matching after normalisation0.4ms
it must not approve spend on anything but a bare yes · 3 tests
ignores a reply that carries new instructions0.4ms
does nothing when no approval is pending — a stray "yes" cannot arm spend0.2ms
does not read an unrelated short message as consent0.4ms
a typed source choice routes like the picker chip · 3 tests
accepts the picker options as bare words0.6ms
stays silent when no lead search is pending0.3ms
prefers the named source over a generic confirmation0.2ms
delivered-summary.vitest.ts
12/12 14ms · 1 suite PASS
src/chat/delivered-summary.vitest.ts
delivered summary (feature 004) · 12 tests
keeps terminal steps only and the LAST state of a repeated step wins5.2ms
stays silent only when a lone step has nothing to say1.2ms
builds outcome lines from allow-listed numeric result fields only1.6ms
covers the report composites and falls back to the generic numeric allow-list1.2ms
covers create_marketing_plan / show_marketing_plan (nested plan_score, not top-level)0.7ms
renders counts and names failed steps honestly0.4ms
tolerates malformed trace input1.3ms
accepts a rephrase that reuses only fact numbers1.0ms
rejects any number absent from the facts0.5ms
allows the step-count number itself0.3ms
rejects empty, oversized, or scratchpad-carrying output0.3ms
accepts numbers sourced from the outcome line, rejects others0.4ms
diagnostic-recognition.vitest.ts
12/12 9ms · 3 suites PASS
src/chat/diagnostic-recognition.vitest.ts
the typo that cost a session · 4 tests
recognises the exact message the user sent5.2ms
and the correctly spelled one, as it always did0.8ms
handles the other three single-edit shapes, not just transposition0.7ms
TRANSPOSITION specifically — the case plain Levenshtein scores as 20.4ms
the imperative form — the same question without the question mark · 4 tests
"improve my seo"0.2ms
"fix my rankings"0.2ms
"increase my leads"0.2ms
"boost my traffic"0.2ms
what must NOT become a diagnosis — a false redirect is not free · 4 tests
sends a real spend request to a read-only tool if it fires wrongly0.8ms
explicit action requests still route to their tools0.4ms
fuzzy matching is length-gated — short words are too close together0.4ms
an exact verb is GROWTH_ASK_RE's job, not the fuzzy path0.2ms
public-activity.vitest.ts
12/12 15ms · 1 suite PASS
src/chat/public-activity.vitest.ts
public activity projection · 12 tests
maps explicitly approved Google tools without exposing their internal name6.3ms
omits unknown tools rather than inventing a generic progress step0.4ms
does not leak banned vendor names from tool identifiers or phase strings0.9ms
keeps trace DTOs to public fields only0.4ms
describes the content-quality workflow with a real user-facing operation0.3ms
covers deterministic audit result checks without provider disclosure0.5ms
has an explicit public activity policy for every LLM-callable tool0.7ms
assigns every mapped tool an operation-cluster group0.4ms
extracts counts only from allow-listed array fields2.0ms
attaches counts on terminal success only — never on running or failed steps0.5ms
carries the group through phase projections for sub-steps0.3ms
carries real elapsed + heartbeat details through phase projections, scanned0.7ms
turn-evidence.vitest.ts
12/12 11ms · 3 suites PASS
src/chat/turn-evidence.vitest.ts
call identity · 3 tests
collapses two reads of one URL that differ only by the extract HINT2.9ms
separates searches that differ by site scope1.3ms
is case- and whitespace-insensitive, so trivial variation is still a repeat0.3ms
what counts as new evidence · 3 tests
a repeat is never novel, however good the result0.7ms
a GAP is recorded but is not novelty0.6ms
a search that ran and found nothing is not novelty either0.7ms
when it stops — and when it must NOT · 6 tests
does not stop while anything new is still arriving0.5ms
stops when a turn made calls and learned nothing0.5ms
a turn made ENTIRELY of repeats counts as calls, and stops1.1ms
a turn of pure thinking is NOT stagnation0.3ms
the ledger spans the RUN, not one turn — a page fetched on turn 1 is known on turn 40.7ms
only evidence tools participate — a campaign write repeated is a different question0.2ms
turn-floor.vitest.ts
12/12 12ms · 4 suites PASS
src/chat/turn-floor.vitest.ts
the turn floor · 2 tests
is the cheapest turn ever measured, not a guess3.0ms
is larger than the estimate buffer — the buffer could never have done this job0.5ms
the check runs BEFORE the model, not at tool dispatch · 6 tests
gates on the balance and returns without calling a model1.0ms
the gating read is STRICT — a failed ledger must not read as a zero balance0.4ms
REUSES the existing turnBalance read instead of adding a second one0.6ms
an UNKNOWN balance proceeds — a failed read must not lock out a paying user0.5ms
does not report the refusal as a fault0.5ms
names the action that helps and stays token-denominated0.7ms
deterministic shortcuts stay reachable at zero balance · 1 test
the floor sits inside runChatV2, which runs only after tryChatShortcut1.5ms
the model chain leads with the one that caches · 3 tests
deepseek leads TOOL_MODELS1.2ms
every tier leads with deepseek1.0ms
llama is retained as the fallback — deleting it leaves no tool-capable backup0.4ms
audience-fit.vitest.ts
12/12 12ms · 3 suites PASS
src/leads/audience-fit.vitest.ts
when NOT to ask — every one of these is a wasted interruption · 4 tests
no product brief: there is nothing to judge the audience against2.8ms
a one-line brief cannot support a mismatch claim0.3ms
no audience: preflightAsk already owns that case and asks a better question0.3ms
asks in the one case that motivated it0.4ms
parsing the model answer — a bad response must not become a bad card · 4 tests
reads a well-formed mismatch1.1ms
DOWNGRADES a mismatch with no reason — a warning you cannot act on is noise0.5ms
treats anything unparseable as no finding, never as a mismatch1.0ms
bounds the strings so a runaway answer cannot flood the card0.5ms
what the user actually sees · 4 tests
renders nothing at all unless there is a real mismatch0.5ms
ALWAYS leaves the user a way through, without inventing a second protocol token0.5ms
offers the brief as a repair too, because a mismatch never says which side is wrong2.4ms
is phrased as an observation before spending, not as a refusal0.6ms
registry.vitest.ts
12/12 15ms · 3 suites PASS
src/metrics/registry.vitest.ts
metric registry — canonical formatting · 4 tests
a score/100 never renders as a bare percent (the 87%-vs-12/22 mislabel class)3.9ms
a non-finite value formats as an honest blank, never NaN%/undefined/1001.2ms
an unknown metric id defaults to percent formatting (safe fallback)0.5ms
metricLabel returns the canonical label, id as fallback0.5ms
metric stamping + cross-report consistency (Dim 5) · 6 tests
stampMetric emits canonical text + machine-readable data-* attributes1.8ms
a blank value stamps an empty data-value, not "NaN"0.5ms
extractMetricStamps round-trips what stampMetric emits2.1ms
checkMetricConsistency catches the same metric showing two values in one epoch1.9ms
the same metric across DIFFERENT epochs is not a violation (reports are frozen artifacts)0.4ms
consistent values across reports pass0.6ms
epochOf — stable data-epoch derivation · 2 tests
prefers a persisted snapshot identifier over anything time-based0.7ms
falls back to a deterministic per-result token for live one-shot data (never wall-clock)0.4ms
entity_audit.vitest.ts
12/12 49ms · 4 suites PASS
src/middleware/entity_audit.vitest.ts
nameCorresponds / normEntityName · 4 tests
strips legal suffixes and punctuation2.9ms
same entity across legal-form and short/long variants → corresponds0.5ms
a different same-word namesake → does NOT correspond0.3ms
cannot judge when a name is missing → corresponds (no invented collision)0.3ms
kgSearch verdicts · 5 tests
REGRESSION (glenindia.com): a same-type DIFFERENT company is an identity collision, not partial35.3ms
a thin but corresponding entity is still partial1.0ms
corresponding + detailedDescription → recognized0.8ms
wrong TYPE is still a (type) collision0.6ms
no result → invisible0.7ms
entityAudit summary · 1 test
glenindia.com run: 1 identity collision → collision=1, health=15 (not partial/45)1.5ms
entityAudit competitor comparison · 2 tests
audits competitors separately and NEVER folds them into the own-brand summary/health3.3ms
omits the comparison entirely when no competitors are passed1.0ms
godmode-indexing.vitest.ts
12/12 17ms · 4 suites PASS
src/reports/godmode-indexing.vitest.ts
god_mode indexing section — gold standard · 3 tests
sources coverage from the live index with no quota language3.8ms
shows the free-tier upsell0.5ms
adds a site: check link + copy-fix prompt + submit-for-indexing action1.1ms
god_mode signal overview — §17.1 EVERY dimension aggregated · 3 tests
renders one signal card per dimension (problems + healthy), fix buttons only on problems0.9ms
detail accordions are collapsed by default (bento is the primary surface)2.0ms
appends result.insights_html so async delivery matches the sync artifact (keep-both parity)1.1ms
god_mode telemetry — honest run-cost footer · 2 tests
reports the real URLs-checked count and the billed index checks on paid tier1.0ms
says "included" on the free tier and only "no extra token cost" when nothing was checked1.2ms
god_mode health score — a partial measurement must not read as a confident grade · 4 tests
renders a letter grade and a confident colour only when every component was measured1.2ms
withholds the grade, greys the score and NAMES the missing signals when components are absent1.0ms
treats a sampled index check as provisional even when every component IS present0.9ms
leaves artifacts saved before the field existed on their original confident rendering1.0ms
free-tool-cache.vitest.ts
12/12 43ms · 3 suites PASS
src/routes/free-tool-cache.vitest.ts
the key is bounded BY CONSTRUCTION, not by a remembered truncation · 4 tests
the URL that actually broke it produces a legal key3.0ms
any input length yields a key well under the 512-byte KV limit22.3ms
distinct URLs that share a long prefix do NOT collide3.6ms
the same URL always yields the same key — a shared link must show the shared score1.3ms
a cache can never fail the request · 6 tests
a THROWING get still returns the computed result — the live 4142.3ms
a THROWING put still returns the computed result1.5ms
unparseable cached JSON recomputes instead of throwing1.2ms
no KV binding at all still works0.9ms
a HIT is served from cache and does not recompute1.3ms
writes under the shared TTL so a shared link outlives the visit1.6ms
both free tools use the one implementation · 2 tests
src/routes/geo-scorecard.ts delegates instead of building its own key0.9ms
src/routes/ai-search-prompt-generator.ts delegates instead of building its own key1.6ms
public-endpoints.vitest.ts
12/12 69ms · 4 suites PASS
src/routes/public-endpoints.vitest.ts
robots.txt · 4 tests
serves 200 as text/plain50.0ms
never blanket-disallows the site2.6ms
points at the sitemap with an absolute URL1.3ms
keeps the AI-agent paths crawlable1.0ms
sitemaps · 4 tests
the index is well-formed XML with absolute child locs1.4ms
the pages sitemap lists absolute, deduplicated URLs on this origin4.1ms
escapes ampersands so the XML stays parseable1.0ms
serves XML content types1.1ms
ai-sitemap.json · 2 tests
is valid JSON with an absolute site and sitemap list1.2ms
carries a parseable generated_at0.5ms
llms.txt · 2 tests
serves plain text with the required H1 and summary blockquote1.0ms
links are absolute so an agent can follow them without a base URL1.5ms
guardrail-empty-message.vitest.ts
12/12 21ms · 4 suites PASS
src/runtime/guardrail-empty-message.vitest.ts
guard 1 — the rule substitutes, like its siblings · 5 tests
no longer empties a reply that is only the leaked directive7.5ms
still redacts it — the leak does not survive2.1ms
keeps the surrounding prose when the directive is embedded1.7ms
no REDACT rule may empty a non-empty string2.6ms
a real BLOCK still blocks and still returns the fallback0.6ms
guard 2 — an emptied message is treated as a block, not a deletion · 4 tests
assigns the fallback rather than deleting the message0.4ms
tears down the artifact, exactly as the BLOCK path does0.6ms
still deletes the four OPTIONAL fields when they empty0.3ms
reports it — an unreachable branch that fires is news1.1ms
guard 3 — the client never renders a raw payload · 2 tests
has no JSON.stringify fallback left anywhere0.6ms
falls back to honest copy instead0.3ms
one voice for "cannot show this" · 1 test
index.ts no longer carries its own copy of the sentence0.6ms
competitors-wave2.vitest.ts
12/12 9ms · 4 suites PASS
src/tools/competitors-wave2.vitest.ts
seo_competitor_gap · 3 tests
accepts the declared name3.5ms
rejects the retired domain alias0.8ms
accepts the empty call — the saved competitor set is the fallback (2026-08-11)0.4ms
find_competitors · 3 tests
accepts the empty call — "my competitors" is the common case0.4ms
accepts a named third-party domain0.4ms
rejects an invented argument0.3ms
the dispatch now matches what it declares · 4 tests
find_competitors reads the site it declares0.6ms
find_competitors skips the saved set when another brand is named0.3ms
seo_competitor_gap no longer coalesces domain0.4ms
leaves still-legacy tools their fallbacks0.4ms
the clarify gate asks about fields that exist · 2 tests
seo_competitor_gap probes the field the tool actually takes0.3ms
does not gate seo_enrich_keywords on its own default mode0.4ms
confirm-rescue.vitest.ts
12/12 40ms · 4 suites PASS
src/tools/confirm-rescue.vitest.ts
the disagreement that produced the dead turn · 3 tests
preflightAsk lets these arguments PAST the cost card8.5ms
but the schema rejects them, which is what ended the turn2.8ms
so the confirmed call rescues them instead of refusing2.8ms
the rescue keeps what the user got right · 3 tests
drops only the unaccepted member, not the whole filter13.8ms
falls back to dropping the field when NO member survives3.1ms
leaves a fully valid call alone0.7ms
what it refuses to rescue · 3 tests
never rescues by silence — the dropped part is always returned3.4ms
returns null when there is nothing droppable to rescue0.7ms
never rescues a cross-field conflict by dropping one half of it0.6ms
the note the user reads · 3 tests
says what could not be applied and that the rest was0.4ms
is empty when nothing was dropped, so a clean run stays clean0.2ms
does not repeat a value dropped from two fields1.4ms
poll-budget.vitest.ts
12/12 182ms · 5 suites PASS
src/tools/poll-budget.vitest.ts
pollDeadlineMs — a poll loop cannot outlive the harness timing it · 4 tests
is strictly inside the tool budget2.6ms
reserves headroom for the work AFTER the last poll0.3ms
never returns a nonsensically small deadline for a short-budget tool0.3ms
the graceful degradation is reachable — 240s never was, under a 120s budget0.4ms
the graceful outcome is not a defect · 2 tests
"taking longer than usual" is an EXPECTED outcome2.5ms
a real crash is still reported1.8ms
no tool polls past its own timeout budget · 1 test
every literal poll deadline is inside its enclosing tool budget171.7ms
composite budgets contain their nested children (owner 2026-07-28) · 2 tests
full_seo_audit outlives the on-page crawl it nests, with room to spare0.4ms
the on-page budget is the 3 minutes the crawl actually needs0.4ms
seo_serp_spider — the same inversion, found 2026-08-31 · 3 tests
the poll is derived from the budget, so the two-turn fallback is reachable0.5ms
the crawl budget matches its sibling crawl, and covers the measured 144s run0.3ms
seo_keyword_metrics is off the 30s default it kept overrunning by 4-5s0.2ms
corroboration-sources.vitest.ts
12/12 12ms · 3 suites PASS
src/seo/corroboration-sources.vitest.ts
the premise is measured, not assumed · 4 tests
rules the crowd premise OUT when the category does not read that way2.4ms
a rival site is NOT independent corroboration0.4ms
the decision refuses the spend, and carries the honest coverage line1.0ms
the situation names the actual diet rather than a category average0.2ms
when the crowd premise DOES hold · 3 tests
independent corroboration survives and leads0.7ms
forums-on-best-X is measured on the COMPARISON prompts, not the whole panel0.9ms
no comparison prompt is a different sentence from "forums are absent"0.7ms
what it will not do · 5 tests
local presence is untested BY DECISION, and says so0.8ms
thread influence needs comparable runs, and names the panel as the fix0.8ms
never tells the user to buy reviews1.4ms
an ungrounded run says the models answered from memory0.8ms
a high unclassified share is disclosed rather than absorbed0.5ms
crawler-access.vitest.ts
12/12 24ms · 2 suites PASS
src/seo/crawler-access.vitest.ts
the contradiction between our fetch and the crawler · 7 tests
is reported for the live case4.4ms
says NOTHING when the crawler got in — the ordinary case must stay silent0.8ms
says NOTHING when the site is genuinely down for us too0.4ms
treats an UNKNOWN probe as agreement, never as contradiction0.4ms
matches the root even when the crawl returned other pages first0.7ms
handles www and scheme differences in the stored url0.5ms
survives junk urls without throwing2.2ms
the rest of the report agrees with the disclosure · 5 tests
does NOT tell them to fix a page it just said is not broken8.4ms
does NOT present a hygiene grade over a crawl that was refused1.7ms
keeps the status code the guardrail would otherwise redact1.0ms
points at the issues in the direction they actually are1.0ms
an ordinary refused-free report is completely unchanged0.6ms
enrich-keywords-readiness.vitest.ts
12/12 12ms · 3 suites PASS
src/seo/enrich-keywords-readiness.vitest.ts
the denominator is what the RUN will process · 5 tests
uses the named set, not the stored set — a never-seen keyword still gets bought4.6ms
windows the stored set in the QUERY, the way the dispatcher windows it1.6ms
does not window a NAMED set — the user asked for exactly those0.4ms
never reports more fresh than total0.4ms
dedupes and lowercases the named set the way the dispatcher does1.2ms
what counts as fresh · 3 tests
requires a real number, not just a recent stamp0.4ms
scopes both counts to the tenant0.4ms
uses the same 30-day window the volume path already treats as fresh0.2ms
when it speaks · 4 tests
says nothing about a trivial number or share of re-buys0.5ms
says nothing when nothing is fresh — the ordinary, correct case0.5ms
names how many actually need refreshing, not just how many are wasted0.6ms
says ALL when there is nothing to gain at all0.3ms
gsc-property-scoping.vitest.ts
12/12 51ms · 3 suites PASS
src/seo/gsc-property-scoping.vitest.ts
a GSC property is never borrowed from another domain · 4 tests
uses the property that matches the audited host3.3ms
REFUSES the connector default when it is a different domain0.7ms
accepts the default only when the default IS the audited host0.5ms
returns nothing when there is no host to match0.4ms
the report names the CAUSE, so the remedy fits it · 5 tests
connected, but this domain is not a verified property — never says "connect Google"40.3ms
connected and matched, but the check returned nothing — never blamed on the site0.6ms
genuinely not connected — this is the only case that says so0.6ms
a reason is never rendered as coverage data0.8ms
real coverage still renders as coverage1.1ms
the full-audit gap list distinguishes the same four causes · 3 tests
names the property gap instead of blaming the connection0.9ms
does not name a CAUSE it never measured0.5ms
still says "not connected" when that is actually true0.3ms
onpage-resume.vitest.ts
12/12 8ms · 2 suites PASS
src/seo/onpage-resume.vitest.ts
on-page crawl — resume before restart · 8 tests
resumes the in-flight crawl for the same site at the same depth3.1ms
resumes a DEEPER in-flight crawl for a shallower request — it can answer it0.6ms
never serves a SHALLOWER crawl under a deeper request0.5ms
never crosses sites — the marker is keyed per user, not per site0.4ms
matches domains the way the rest of the SEO stack does (protocol/www/case)0.4ms
does not reuse a crawl older than the working session0.4ms
still reuses a recent one, and a marker with no timestamp0.5ms
falls back to a fresh crawl on a missing, unreadable or task-less marker0.5ms
on-page crawl — the deadline branch is observable and does not bill twice · 4 tests
reports the overrun to Sentry and to the feature table0.7ms
carries how far the crawl actually got, not just that it stopped0.3ms
no longer instructs the user to run it again — that meant paying twice0.4ms
only writes the marker on a fresh start, so a resume cannot shrink the stored ceiling0.2ms
revenue-attribution-classify.vitest.ts
12/12 10ms · 3 suites PASS
src/seo/revenue-attribution-classify.vitest.ts
aiEngineLabel · 4 tests
names the engines a user would recognise4.0ms
maps every alias of one engine to a single label0.7ms
matches subdomains but NOT lookalike domains0.7ms
returns null for a non-AI source rather than guessing0.5ms
isAiEngineSource · 3 tests
does not treat direct or unset traffic as AI0.6ms
normalizes scheme and www before matching0.3ms
is case-insensitive0.3ms
classifyMacroChannel · 5 tests
routes AI traffic to AI Chat Engines even though GA4 files it as Referral0.9ms
splits search into Google vs Other0.9ms
keeps DuckDuckGo in search, NOT in AI Chat Engines0.4ms
maps the remaining groups and falls back to Other0.5ms
is case-insensitive on the channel group0.2ms
sov-schedule.vitest.ts
12/12 16ms · 4 suites PASS
src/seo/sov-schedule.vitest.ts
the daily dispatcher is actually registered · 3 tests
every cron expression the worker branches on exists in wrangler.toml5.7ms
has no day-of-week field, so the platform DOW offset cannot apply0.4ms
the cron gates on the tenant day — a daily tick without it runs 7x a week0.5ms
isSovDueToday · 3 tests
defaults to Sunday, which is the day the old weekly tick fired0.5ms
fires on exactly one day per week for a given tenant0.4ms
an out-of-range or junk day falls back to Sunday rather than never running0.4ms
the weekly config round-trips all three fields independently · 5 tests
keeps a frozen panel, engines and day together1.2ms
a day-only config is still a config0.3ms
dedupes and bounds stored prompts0.4ms
caps a stored panel0.8ms
rejects a junk day instead of storing it0.2ms
setting one field does not wipe the others · 1 test
the dispatcher merges against the stored config4.1ms
sov-weekly-panel.vitest.ts
12/12 13ms · 3 suites PASS
src/seo/sov-weekly-panel.vitest.ts
weekly AI-visibility panel · 5 tests
runs 12 prompts on a paid plan — not silently clamped by the plan cap3.8ms
defaults to THREE engines, with Claude deliberately not in the recurring run4.3ms
still lets a paid user CHOOSE Claude — dropped from the default, not removed0.5ms
holds the free tier to one engine, so the cron cannot run for them0.4ms
the cron builds its rotation from the SHARED constant, never a literal0.8ms
the free-tier plan gate announces itself · 3 tests
does not silently continue past the sov_weekly plan gate0.4ms
offers the action that actually resolves it0.3ms
the balance gate still announces itself too — both gates, same standard0.2ms
a frozen panel is priced by what will run, not by what the plan allows · 4 tests
THE LIVE CASE: 8 frozen against a cap of 12 counts as 80.5ms
a rotating panel still fills the cap0.3ms
a frozen panel LARGER than the cap is bounded by the cap0.4ms
never returns zero — a zero would price a run at nothing0.3ms
video-decision.vitest.ts
12/12 15ms · 4 suites PASS
src/seo/video-decision.vitest.ts
the answer is do not commission, stated plainly · 5 tests
says it in as many words3.0ms
frames it as the rule applied, not as a gap0.8ms
the ask makes the refusal the deliverable0.6ms
confirms the stay-as-text default on the ABSENCE of the precondition1.1ms
flips once a video platform is connected1.1ms
what it will not claim · 3 tests
never says the results pages do or do not show video0.5ms
never rules the audience in or out — it reports that nothing can see0.6ms
keeps the two-indexes point as a rule rather than dressing it as a finding0.7ms
the free fix it does find · 3 tests
flags embeds that never reached a live address1.0ms
does not flag them once they are published2.3ms
always states the packaging rules for anything that does get made1.3ms
no internal vocabulary reaches the user (GS-005) · 1 test
keeps field and table names out of the prose0.6ms
visibility-signals.vitest.ts
12/12 16ms · 5 suites PASS
src/seo/visibility-signals.vitest.ts
problems sort to the front · 2 tests
orders problem → unmeasured → ok, so the broken card is read first6.0ms
a healthy panel still renders cards, marked ok, with no action button0.8ms
an engine we could not reach is never a zero · 3 tests
reports coverage as unmeasured and names the engine1.3ms
does not claim an engine spread while an engine is missing0.7ms
flags a real spread only when it is worth acting on0.9ms
invisible questions — the most actionable card · 2 tests
counts only prompts we could actually test0.7ms
says so plainly when there is no gap0.5ms
competitive card · 2 tests
names who is ahead and by how much0.6ms
is ok when we lead0.4ms
cards decline to appear rather than invent a number · 3 tests
emits nothing measurable when there is no visibility detail at all0.4ms
an unmeasured pillar says unmeasured, never 02.3ms
the weakest page names what is holding it back when the scorer said so0.4ms
csv-stream.vitest.mjs
12/12 213ms · 2 suites PASS
scripts/lib/csv-stream.vitest.mjs
csvRecords · 10 tests
parses a plain record9.8ms
KEEPS a newline inside a quoted field in the same record5.2ms
survives several newlines in one field5.7ms
handles an escaped quote inside a quoted field3.7ms
keeps commas inside quotes as data2.2ms
handles CRLF without leaving a stray carriage return in the last field2.3ms
emits a final record with no trailing newline3.1ms
does NOT emit a phantom empty record for a file ending in a newline15.3ms
preserves empty fields rather than collapsing them7.2ms
reads a quoted field spanning a 1 MB chunk boundary156.4ms
headerIndex · 2 tests
finds a column by any accepted spelling, case-insensitively0.8ms
returns -1 rather than 0 for an absent column0.6ms
consumer-mailbox-list.vitest.ts
12/12 13ms · 4 suites PASS
src/leads/shared/consumer-mailbox-list.vitest.ts
158 · it adds exactly the reviewed set · 3 tests
inserts 40 domains3.9ms
covers the highest-volume gap in each non-US country3.1ms
is idempotent0.4ms
158 · no real employer domain is ever classified consumer · 2 tests
none of them is in the insert list1.5ms
the migration asserts it at apply time too0.5ms
158 · the derivation requires BOTH signals · 3 tests
the candidates function tests self-domain rate and modal employer share0.4ms
and excludes what is already listed, so re-running proposes only new work0.4ms
the function PROPOSES and never writes0.4ms
159 · the tool warns the next person with a measured number · 4 tests
states the one-in-three false-positive rate and forbids bulk insertion0.3ms
names the employers the tool actually proposed, so the warning is checkable0.4ms
verifies its own comment landed0.3ms
changes nothing but the comment0.4ms
keep-warm.vitest.ts
12/12 73ms · 2 suites PASS
src/leads/shared/keep-warm.vitest.ts
corpusKeepWarm · 10 tests
does not touch the database when the rung is off for everyone11.0ms
runs when the flag names tenants, not just on the literal 110.4ms
pings with the INDEXED domain lookup, not the browse branch7.9ms
reports how long the ping took, because that IS the observation3.7ms
does not ping an IDLE corpus — no demand, no compute-hours5.1ms
pings while somebody is actually searching10.1ms
lets the compute go once the session is over3.0ms
fails OPEN — a KV outage must not stop keeping a BUSY corpus warm8.9ms
the ping never renews its own demand stamp0.9ms
swallows a failed ping — a missed one costs one slow search, not an incident10.0ms
the cron and the module agree · 2 tests
is actually scheduled in wrangler.toml0.7ms
fires with real margin against the suspend timeout, not a one-minute race0.3ms
affordability-gate.vitest.ts
11/11 102ms · 6 suites PASS
src/billing/affordability-gate.vitest.ts
the check lives on the shared path, not on one gate · 3 tests
getCostApprovalPlan reads the balance itself2.1ms
the agent loop no longer carries its own copy5.6ms
card and dispatch gate share ONE boundary0.5ms
an unreadable ledger is NOT a refusal · 1 test
the balance read FAILS OPEN4.1ms
what the user is told instead · 3 tests
names needed, held and short — and never a Confirm button0.5ms
the reply offers the one action that changes the outcome0.4ms
prices are TOKENS, never dollars (CLAUDE.md §4)0.4ms
coverage is enforced by a guard, not by memory · 2 tests
check-affordability-gates passes over the real tree68.3ms
it is wired into npm run check1.8ms
a gate that overrides the plan feeds its own numbers back in · 1 test
aeo_visibility passes the fan-out figures to getCostApprovalPlan11.0ms
the overrun alarm states an observation, not a conclusion · 1 test
no longer asserts "missing a leg"5.9ms
cost-gate-economics.vitest.ts
11/11 13ms · 5 suites PASS
src/billing/cost-gate-economics.vitest.ts
fixtures exist · 1 test
found a cheap, an expensive and an always-confirm tool to reason about2.8ms
the threshold applies in EVERY mode · 3 tests
does not gate a sub-threshold tool, even in confirm mode1.1ms
does not gate a sub-threshold tool in the default (unset) mode1.2ms
does not gate a sub-threshold tool in auto mode0.3ms
gates that are worth their cost still fire · 3 tests
gates an over-threshold tool in confirm and default modes0.4ms
ALWAYS gates an always-confirm tool, at any size and in any mode1.1ms
gates a mixed batch when any member qualifies1.5ms
the threshold is economically coherent · 1 test
is meaningful relative to what a gate itself costs0.5ms
the two tools marked for SAFETY, not economy (owner ruling 2026-08-09) · 3 tests
verify_contacts confirms by the threshold, not always (owner 2026-09-17) — the re-buy guard lives at dispatch1.6ms
aeo_visibility always confirms, because its WORST run was its cheapest0.6ms
marking a tool always-confirm never removes a gate — it can only add one0.3ms
quote-target.vitest.ts
11/11 12ms · 3 suites PASS
src/billing/quote-target.vitest.ts
the estimator prices a KNOWN target · 4 tests
quotes per address when the count is handed in, the ceiling when it is not3.4ms
a known EMPTY target is zero, not the ceiling — the dispatch answers it with a picker0.5ms
priceOfTool threads the count through, so a shortcut card reads the same number0.4ms
the count only speaks for target-sized tools1.5ms
resolving the target the dispatch will bill for · 4 tests
reads every spelling the gate may see before the validator folds them0.9ms
a list resolves to its member count; explicit ids win over a list1.0ms
the user's own limit cuts the count, as it cuts the run0.4ms
nothing named → undefined (picker, priced by the estimator at zero); a missing list → zero0.4ms
every gate that knows the tenant asks for the count · 3 tests
the agent loop gate1.5ms
the chip-dispatched (next_action) gate0.5ms
the plan-initiative Execute gate — it passed undefined, the catalogue ceiling, for every tool1.1ms
usage-labels.vitest.ts
11/11 13ms · 4 suites PASS
src/billing/usage-labels.vitest.ts
every context production writes has a name · 3 tests
labels all 64 of them3.4ms
and the old map would have lost 97.9% of the tokens1.0ms
puts 97.9% of all spend under Conversations, which is the true answer0.5ms
the two rules that keep the table small · 2 tests
:failover is a suffix, not a task0.9ms
the chat family matches by prefix, so a new variant is named the day it ships0.8ms
what must NOT get a bucket · 2 tests
credits and top-ups are not usage0.7ms
an unknown context returns null rather than "Other"0.6ms
provider rows are labelled by what the tenant asked for, never by vendor · 4 tests
maps the providers that actually appear in the ledger1.8ms
splits one provider by what it was used FOR0.5ms
drops zero-cost attribution rows instead of charting them0.4ms
never returns a vendor name as a label (CLAUDE.md §4)1.4ms
corpus-rung.vitest.ts
11/11 9ms · 2 suites PASS
src/admin/corpus-rung.vitest.ts
what an operator is told · 6 tests
says OFF FOR EVERYONE when the flag is unset — the case nobody could see3.3ms
distinguishes a tenant ON the list from one that is not0.6ms
reports the ROLLOUT WIDTH without naming who is on it0.5ms
answers NULL, not false, when no tenant was named0.3ms
separates "no database" from "flag off" — different failures, different fixes0.4ms
says armed-for-everyone when the flag is 10.3ms
reveal billing is reported separately from access · 5 tests
says billing is OFF when the flag is unset, whatever access says0.7ms
tracks the two flags independently0.9ms
does not claim a no-verdict reveal is free, because it is not1.0ms
still says a reveal prices from the verification behind it0.4ms
only a proven bounce and an unscopeable verdict are free0.3ms
feature-detail.vitest.ts
11/11 11ms · 3 suites PASS
src/admin/feature-detail.vitest.ts
sanitiseFeatureFields · 5 tests
accepts ordinary attribute names3.4ms
trims whitespace around names0.8ms
drops anything that is not an attribute name0.4ms
caps the list1.5ms
handles absent input0.5ms
the reader keeps null and empty distinguishable · 2 tests
the handler reports WHY there are no rows rather than only that there are none1.1ms
always requests the message column, which is the stable contract0.7ms
the numeric pass is separate on purpose · 4 tests
asks for the typed accessor for numeric attributes0.3ms
issues it as a SECOND request rather than one combined field list0.5ms
a failed numeric pass never costs the string attributes0.4ms
never overwrites a value the string pass already resolved0.2ms
gsc-scan.vitest.ts
11/11 120ms · 2 suites PASS
src/admin/gsc-scan.vitest.ts
startAdminGscScan · 7 tests
exact-probes the universe and buckets the not-indexed URL58.2ms
with a queue binding: enqueues chunk 1 and advances one chunk at a time (no inline drain)24.5ms
watchdog resurrects a stalled running scan (enqueues one chunk), leaves a fresh one alone1.9ms
records and reads submitted URLs (merge + de-dupe across calls)1.1ms
stop halts a running scan and preserves progress so far — owner kill switch (2026-08-01, no way to stop a paid scan mid-flight)1.1ms
stop is a no-op on a scan that already finished0.4ms
errors cleanly when there is no URL universe22.2ms
probeIndexed — GSC-first fallback logic · 4 tests
with no admin Google connection (gsc=null), goes straight to paid ValueSERP — unchanged prior behavior6.7ms
an admin connection + quota + an unambiguous Google verdict answers for FREE — ValueSERP never called0.9ms
an ambiguous Google verdict (NEUTRAL → null) falls through to paid ValueSERP for that URL1.0ms
quota exhausted for the day skips the free attempt entirely and goes straight to paid1.3ms
provider-usage-grouped.vitest.ts
11/11 15ms · 2 suites PASS
src/admin/provider-usage-grouped.vitest.ts
buildGroupedAggregateQuery · 7 tests
emits one aliased aggregate per pair, in order4.4ms
filters each alias on its OWN provider+operation, not a shared/loose match0.8ms
reuses ONE $since variable across every alias rather than declaring one per pair2.7ms
declares a distinct $provN/$opN pair per alias, matching the pair count1.1ms
requests count and cost sum — the two fields the caller reads0.4ms
produces a valid query for zero pairs (the caller short-circuits before this, but the builder must not throw)1.5ms
MAX_PAIRS leaves real headroom over the measured reality (49 pairs, 2026-08-15)0.6ms
buildRepresentativeMeta · 4 tests
keeps the FIRST row per (provider, operation) — the sample is created_at desc, so first = most recent1.3ms
keys strictly on provider AND operation — same operation under a different provider is a different group0.5ms
records null meta as null, not as "missing"0.4ms
is empty for an empty sample0.2ms
scope-note-voice.vitest.ts
11/11 20ms · 3 suites PASS
src/campaigns/scope-note-voice.vitest.ts
the note the USER reads · 4 tests
never addresses the model in the second person2.9ms
still makes the disclosure that the 2026-08-05 defect needs0.8ms
says nothing when the result is empty — both siblings now follow one rule0.3ms
offers the user the correction, not a function signature0.5ms
count_note got the same split, a day later, for the same reason · 4 tests
never addresses the model in the second person0.8ms
still states the denominator, which is the disclosure that must survive0.4ms
says nothing when there is nothing — "included below" under an empty set is false0.7ms
and the instruction survives, where only the model sees it0.5ms
the directive only the MODEL reads · 3 tests
carries the argument instruction1.3ms
is rendered NOWHERE — that is the whole point of the split10.4ms
is not named like a disclosure, so the rendering guard does not demand it0.6ms
bare-ack-probe-surface.vitest.ts
11/11 7ms · 2 suites PASS
src/chat/bare-ack-probe-surface.vitest.ts
the bare-ack probe reports as a log, not a fault · 3 tests
uses reportSentryLog at warn level2.5ms
does NOT raise an Error Issue0.8ms
still fires on the same condition — the signal is not being dropped0.3ms
the fields that discriminate are QUERYABLE, which was the point · 8 tests
carries model as a log attribute0.4ms
carries cot_on as a log attribute0.4ms
carries cot_complexity as a log attribute0.3ms
carries compound_intent_count as a log attribute0.3ms
carries history_turns as a log attribute0.3ms
keeps the identity needed to divide eval traffic from real0.3ms
flattens history_tail to a scalar, because attributes are not arrays0.4ms
never lets an absent model become an empty bucket again0.3ms
disclosure-live-path.vitest.ts
11/11 56ms · 2 suites PASS
src/chat/disclosure-live-path.vitest.ts
withDisclosures puts the tool-authored sentence into a model-authored message · 5 tests
appends a note the model did not mention6.0ms
does NOT duplicate a note the model already narrated1.4ms
is a no-op over formatToolResult output, so the shortcut path is unchanged6.2ms
leaves a message alone when the tool disclosed nothing0.3ms
carries EVERY *_note, not just the one that exposed the bug0.3ms
the disclosure survives the surfaces that REPLACE the composed message · 6 tests
rides the document handoff, which states the very count the note qualifies17.2ms
rides the report chip, as HTML, because that message ships is_html:true1.0ms
escapes note text into the chip rather than injecting markup0.5ms
omits the note block entirely when there is nothing to disclose0.5ms
appears on the SAVED ARTIFACT, which is the thing the user opens20.7ms
does not put a note block on a saved artifact that disclosed nothing0.8ms
honest-zero.vitest.ts
11/11 18ms · 3 suites PASS
src/chat/honest-zero.vitest.ts
buildOutcomeNote — an honest empty is not a failure · 4 tests
tells the judge the run COMPLETED and that zero is the verified outcome4.5ms
still asks the judge to grade how the empty was HANDLED1.0ms
leaves the failure note EXACTLY as it was for a genuine failure0.5ms
is unchanged when the flag is absent — no silent behaviour change for other callers0.8ms
the classifier that decides which note is used · 2 tests
treats the real zero-yield strings as expected outcomes2.8ms
does NOT treat a real provider fault as an expected outcome3.5ms
resultYield — a paid-boundary stop is not a provider zero · 5 tests
returns null when the turn stopped to offer a paid source0.9ms
still measures a real zero once the paid source HAS run0.4ms
a PARTIAL success still measures, so it can clear the streak0.5ms
counts a user intent ONCE across the escalation handshake, not twice0.5ms
leaves the existing exclusions and real counts untouched0.7ms
judge-pool.vitest.ts
11/11 106ms · 2 suites PASS
src/chat/judge-pool.vitest.ts
the judge pool · 7 tests
no longer carries grok-4.520.6ms
uses the luna id THAT ACTUALLY SERVES, not the batch one8.8ms
the pool comment records that a fallback chain hid the failure3.7ms
every pool model is priced EXACTLY — a suffixed id must not fall to the default11.0ms
prices the batch variant an order of magnitude below the synchronous one1.2ms
weights still sum to 1, so the cheap model keeps the bulk of the volume12.1ms
stays disjoint from the WRITER pool, which is the rule that actually matters8.5ms
judge sampling is subject-aware · 4 tests
a REAL user is always judged, whatever the secret says11.6ms
the rate is read ONLY on the internal branch8.3ms
uses the canonical internal predicate rather than a second list9.2ms
still defaults to 1.0, so deploying this changes nothing until the secret is set9.6ms
lead-shortfall.vitest.ts
11/11 21ms · 3 suites PASS
src/chat/lead-shortfall.vitest.ts
a short result explains itself · 4 tests
states the gap, where the leads came from, and what the rest would cost11.9ms
says NOTHING when the search was fully satisfied0.9ms
never names the vendor — the operation, not the provider0.5ms
prices in TOKENS, never dollars0.6ms
the yes is one click, and it costs what it says · 4 tests
offers the paid chip FIRST — it answers the question they are left with2.1ms
offers no such chip when nothing is short0.4ms
keeps verification ahead of drafting, as before0.9ms
works on the no-list path too0.3ms
a FULL 0-of-N miss with a real shortfall (live 2026-08-15, owner-reported) · 3 tests
offers the paid-source chip even when found is 0, not just on a partial fill0.9ms
never offers to verify or draft 0 contacts0.5ms
a true 0-of-N miss with NO shortfall still gets the old fallback chips, not the paid offer1.3ms
loop-waste.vitest.ts
11/11 24ms · 4 suites PASS
src/chat/loop-waste.vitest.ts
D7 — a memoized read is drawn once · 3 tests
the render seam is keyed on the memo key, not on the call3.7ms
the repeat is announced to the model, because silence is what let it loop0.5ms
the memo key travels with the result so the seam can see it2.4ms
D4 — the shortlist already answered what search_tools asks · 3 tests
search_tools is withheld on the first call behind a confident shortlist0.8ms
the model calls go out on the filtered set, not the unfiltered one1.2ms
a shortlisted family still carries its own tools, so withholding costs nothing2.0ms
D6 — a chip that cannot complete its own action · 2 tests
the create_sequence enrol chip populates instead of firing2.3ms
enroll_in_sequence still refuses without a list — the chip was wrong, not the guard2.5ms
D2b — a name we refuse is a name we mention · 3 tests
the presenter says which names were dropped and why4.4ms
says nothing when every name was accepted0.6ms
the dispatch records the rejection rather than discarding it2.2ms
visibility-state.vitest.ts
11/11 19ms · 5 suites PASS
src/chat/visibility-state.vitest.ts
the line states what actually ran · 3 tests
carries the date, the shape and the rate2.2ms
forbids the exact false sentence that was shipped0.3ms
tells the model to name the OPERATION, not a tool0.3ms
an UNGROUNDED run is a third state, not one of the other two · 1 test
says a check ran but refuses to call it a measurement of citations0.7ms
silence is the safe default, in BOTH absent cases · 2 tests
injects nothing when no run exists0.3ms
injects nothing when the READ FAILED — never "no check has run"0.2ms
the topical gate spends a query only when it might matter · 3 tests
fires on visibility questions0.8ms
does not fire on unrelated turns0.7ms
is a SPEND gate, not a routing predicate10.7ms
it is wired into the turn, not merely written · 2 tests
the line joins briefStatusText, which is what the model receives1.4ms
a failed read degrades to silence at the call site too0.3ms
derived-artifacts.vitest.ts
11/11 21ms · 3 suites PASS
src/leads/derived-artifacts.vitest.ts
every derivation the scan starts is registered to outlive the response · 4 tests
personas and brand kit are awaited together, not left dangling3.0ms
the pair is handed to waitUntil, with an await when there is no ctx1.0ms
the keyword seed is protected too — it writes the numbers the reply itemises0.5ms
no bare fire-and-forget remains on the settings writes0.8ms
the plan does not call a waiting candidate an absence · 4 tests
reads the candidate set before declaring there are no competitors0.6ms
keeps CONFIRMED as the only set the plan may name from0.8ms
points at the one click instead of telling them to go and search0.9ms
still gives the plain message when there is genuinely nothing0.8ms
the scan reply never claims work that did not survive · 3 tests
the personas line is gated, like every other line in that list4.4ms
reads whether personas EXIST, not whether generation was started6.6ms
a failed read-back reports personas false, never true0.5ms
geo-country.vitest.ts
11/11 9ms · 4 suites PASS
src/leads/geo-country.vitest.ts
resolveApifyCountry · 4 tests
maps the recurring failure — Chennai → India2.4ms
resolves known cities to their country0.6ms
resolves aliases and exact country names (case-insensitive)0.5ms
returns null for unrecognized geography (caller omits the filter, no 400)0.3ms
resolveCountryISO · 2 tests
resolves names, aliases and known cities to lowercase ISO-20.5ms
returns null for unrecognized geography0.2ms
enum coverage · 2 tests
every country we can emit is a country the provider accepts1.3ms
an unmapped country degrades to null, never to a wrong code0.3ms
findCountryCueISO · 3 tests
finds a cue anywhere in a query — name, alias, or city1.2ms
longest match wins — "new zealand" is not read as stray words0.4ms
null when no cue present0.2ms
follow-through.vitest.ts
11/11 25ms · 6 suites PASS
src/planner/follow-through.vitest.ts
nextOpenInitiative · 2 tests
asks for ONE proposed initiative of THIS tenant whose window is still open, in plan order5.3ms
nothing open, a blank action, or a failed read → null1.0ms
planNudgeChip · 2 tests
carries the id and the initiative in the plan's own words, bounded0.9ms
a long action is cut at a word boundary with an ellipsis, never mid-word0.4ms
shouldOfferPlanNudge · 2 tests
an ordinary turn gets the reminder0.6ms
gates, pickers, spend stand-downs, control tokens and plan turns do not0.8ms
once per session per day · 1 test
unset → not nudged; set → nudged; no KV → never blocks0.6ms
planFollowThroughSummary · 1 test
counts real tenants only and names the three states1.6ms
wiring (source pins) · 3 tests
the v2 handler appends the chip on ordinary turns and marks the session7.1ms
the client renders __execute: chips as a real button on the plan execute path4.7ms
the digest carries the follow-through line for real tenants1.3ms
lee-signals.vitest.ts
11/11 21ms · 2 suites PASS
src/middleware/lee-signals.vitest.ts
extractLeeSignals — each predictor · 7 tests
counts comparison language, and weights a comparison TABLE higher than the phrase4.2ms
measures query-term coverage against the topic's content words0.9ms
returns NULL coverage when no topic was given — not 00.5ms
ignores stopwords in the topic, so "the best CRM" is not 33% covered by "the"1.7ms
counts heading depth0.4ms
normalises stats and first-person per 1,000 words0.8ms
scores primary-source as own-numbers versus borrowed links0.3ms
a citable page outscores a first-person blog page · 4 tests
scores the comparison/structured page higher than the lived-experience one3.3ms
puts lee_signals on every successful report6.0ms
advises REDUCING first-person, and says why it contradicts the SEO tool1.0ms
does not penalise a page for a topic nobody supplied0.8ms
next_actions.vitest.ts
11/11 12ms · 2 suites PASS
src/middleware/next_actions.vitest.ts
next_actions · 10 tests
substitutes result params2.9ms
skips rules with missing result fields0.5ms
keeps static params0.3ms
suggests draft follow-ups0.3ms
never suggests reviewing contacts after drafting (they were already resolved)0.8ms
suggests post-send follow-ups0.3ms
suggests google-merge follow-ups0.2ms
lists next-action rules0.6ms
has no deprecated names in rules0.7ms
shows the WordPress-connect nudge only in the exact unconnected-WP state0.5ms
optional next-action parameters · 1 test
drops an absent optional key and keeps the chip; a missing required key still drops the chip3.6ms
contracts-inherited.vitest.ts
11/11 7ms · 2 suites PASS
src/reports/contracts-inherited.vitest.ts
aeo_visibility inherits the retired legs' invariants · 7 tests
a sound composite passes2.9ms
fires on an out-of-range share_of_model coverage — the leg nests one level deeper0.4ms
fires on the phantom-citation contradiction (coverage > 0, nobody "ours" in the leaderboard)0.3ms
fires when more prompts cite us than were answered0.4ms
fires on the AI-overview rate ordering (cited > present)0.4ms
stays SILENT when a leg errored — a failed leg is a reported absence, not a soundness bug0.3ms
stays silent when a leg is absent entirely (free tier runs fewer legs)0.3ms
aeo_page_check inherits the retired rag_readiness reconciliation · 4 tests
a sound page passes0.4ms
fires when the stated deductions do not account for the score0.4ms
a clamped-at-zero page may OVERSTATE deductions without failing0.3ms
stays silent on rows stored before score_breakdown existed0.2ms
onpage-report.vitest.ts
11/11 16ms · 1 suite PASS
src/reports/onpage-report.vitest.ts
seo_onpage_audit — §17 gold standard · 11 tests
aggregates issues into a findings bento with self-contained fix prompts3.7ms
high-affected issues render in alert red, keeps the distribution chart + feedback mount1.0ms
uses plain section headers, not "Cluster N" labels (§17 F)1.5ms
drops the legacy Agent-Readiness "copy fix" button that trailed the footer1.7ms
same case powers seo_onpage_results0.8ms
renders all three directive groups in fixed order, empty ones stating their absence1.3ms
an error-page count IS a measured defect — the Fix group can never contradict the KPI1.3ms
classifies EVERY issue before capping, so a class is never empty because of volume1.3ms
never tells the user to fix the highest-volume issue first0.4ms
states that it cannot rank by stake, rather than implying it can0.6ms
classifies EVERY issue type, not the top 8 the chart shows1.1ms
agent-kv.vitest.ts
11/11 11ms · 3 suites PASS
src/runtime/agent-kv.vitest.ts
listReportArtifacts · 3 tests
returns the newest report even when its key sorts LAST lexicographically3.1ms
scopes to the user prefix and sorts by createdAt desc0.9ms
returns [] when CHAT_HISTORY is absent0.3ms
pending standing-instruction gate · 4 tests
write → consume is one-shot (returns pinned text, then null)1.1ms
cancel drops the pending record so a later confirm finds nothing0.7ms
fails closed when KV is unbound (no durable confirmation channel)0.5ms
is tenant-scoped — one user cannot confirm another user's pending text0.5ms
lastDraftBatch · 4 tests
round-trips the ids the next turn needs0.8ms
is tenant-scoped — one tenant can never resolve another tenant's drafts0.3ms
returns null rather than an empty set when nothing was drafted0.3ms
survives an unbound KV without throwing — it is a convenience, never a dependency1.3ms
keyword-metrics-upsert.vitest.ts
11/11 14ms · 4 suites PASS
src/seo/keyword-metrics-upsert.vitest.ts
competition reaches the registry · 2 tests
is sent on the object, not silently dropped5.1ms
is overwritten on conflict when the batch actually knows it1.6ms
a null-carrying writer cannot clobber · 4 tests
omits competition entirely when no object in the batch has one0.6ms
omits volume and cpc too — the same hazard, not a competition special case0.6ms
one knowing row in a mixed batch is enough to update the column0.5ms
a filtered-out row cannot vouch for a column0.7ms
what is always written · 2 tests
stamps metrics_updated_at unconditionally0.8ms
writes nothing at all for an empty batch0.7ms
competition stays out of the opportunity score · 3 tests
computeKES does not read it — same inputs, same score0.5ms
high CPC RAISES the score — so competition as a divisor would fight it0.5ms
is consumed as a difficulty TIER instead, which is the honest use0.8ms
own-site-scoping.vitest.ts
11/11 13ms · 3 suites PASS
src/seo/own-site-scoping.vitest.ts
isTenantOwnHost — the one ownership predicate · 4 tests
accepts the site itself, however it was written down7.0ms
accepts a subdomain — a blog on blog.acme.com belongs to acme.com0.6ms
REFUSES a different domain, including the suffix trick0.3ms
treats unknown ownership as NOT owned0.4ms
the keyword registry only takes the tenant's own topics · 2 tests
gates the tracked_keywords write on site ownership0.4ms
never registers behind the user's back OR silently declines to0.3ms
the stored stack fingerprint is only claimed for the tenant's own site · 5 tests
entity_audit: no fingerprint on a foreign domain0.4ms
entity_audit: the local-presence leg only reads the tenant's OWN site0.5ms
aeo_page_check: the stored fallback takes the same test the save takes0.5ms
aeo_page_check: SAVING still demands an exact host match, not a subdomain0.4ms
seo_generate_llms_txt: deploy instructions never assume the tenant's stack for another domain0.6ms
rival-capture.vitest.ts
11/11 19ms · 2 suites PASS
src/seo/rival-capture.vitest.ts
captureRivalsFromSerp · 9 tests
stores the rivals for a keyword the tenant tracks8.7ms
NEVER stores a query the tenant does not track1.0ms
does nothing when the tenant has no site to be a rival relative to0.7ms
does not write the same keyword twice in a day1.2ms
records nothing, and says so, when the SERP held no other domains0.8ms
matches the registry case-insensitively — the caller's casing is not the registry's0.4ms
escapes LIKE wildcards — real keywords contain % and _0.4ms
never throws — a capture must not cost the caller the answer it went to fetch1.7ms
checks tracked-ness and same-day in ONE round trip, before any insert1.2ms
seoRoute wiring · 2 tests
captures on an open-web serp and NOT on a site:-restricted one0.6ms
is fire-and-forget so a capture cannot delay or fail the caller0.7ms
serp-rivals.vitest.ts
11/11 8ms · 1 suite PASS
src/seo/serp-rivals.vitest.ts
extractRivals · 11 tests
keeps the rival domains a SERP response already contains3.9ms
excludes the tenant themselves — a tenant is not their own competitor0.5ms
normalises www and scheme so the domain joins to __competitors__0.4ms
matches the tenant regardless of www/scheme form1.2ms
records TRUE SERP position, never the index into the filtered output0.4ms
counts a domain once even when it holds two positions0.3ms
drops non-domains rather than storing junk in a table joined on host0.3ms
accepts `url` as well as `link` — providers disagree on the field name0.2ms
honours the limit0.4ms
is safe on empty/absent input — the cron must never throw on a thin SERP0.4ms
still returns rivals when the tenant has no site set0.2ms
timeout-attribution.vitest.ts
11/11 15ms · 2 suites PASS
src/seo/timeout-attribution.vitest.ts
a timeout names the thing that actually timed out · 6 tests
blames the USER SITE when the crawl target was slow2.8ms
blames OUR OWN budget when the step ran out of its allotted time0.7ms
still blames the PROVIDER when the provider is what timed out0.6ms
still blames the MODEL, the attribution fixed on 2026-08-290.3ms
gives four DIFFERENT answers — the point is discrimination, not four branches0.6ms
leaks no internal token while reattributing1.9ms
Sentry volume is unchanged by the reattribution · 5 tests
site crawl stays suppressed as an expected outcome1.3ms
tool budget stays suppressed as an expected outcome0.7ms
provider stays suppressed as an expected outcome0.8ms
the model-timeout message keeps its own suppression too1.2ms
and a genuine novel error is still REPORTED, so the rule has not been widened2.8ms
visibility-derived.vitest.ts
11/11 34ms · 4 suites PASS
src/seo/visibility-derived.vitest.ts
citedPages — which of OUR pages AI actually cited · 6 tests
counts a URL once per PROMPT, not once per engine4.8ms
excludes competitor domains — this table is about our own pages0.9ms
collapses www, trailing slash and hash to one page1.4ms
keeps the query string — ?v=2 can be genuinely different content14.3ms
ignores errored cells and malformed URLs instead of throwing0.9ms
returns nothing rather than guessing when no run captured citations0.7ms
classifyPromptTopic · 1 test
prefers the more specific and more actionable shape3.8ms
topicPerformance · 2 tests
excludes prompts no engine could answer, rather than scoring them as losses1.5ms
sorts worst first — the table says where to write next0.6ms
citationConcentration · 2 tests
translates HHI into what it means for the reader2.4ms
returns null on runs stored without a share-of-voice block1.1ms
visibility-tldr.vitest.ts
11/11 15ms · 4 suites PASS
src/seo/visibility-tldr.vitest.ts
the score is never presented as more certain than it is · 3 tests
calls a 15-cell sample directional and refuses to read a +33 as progress4.5ms
states movement plainly once the sample is big enough to carry it1.2ms
says the number describes the past when nothing has been measured for weeks0.8ms
it distinguishes the three ways a pillar can be absent · 3 tests
an unmeasured pillar is excluded, not scored zero0.5ms
an unreachable engine is named, not silently read as poor visibility0.5ms
names the weakest pillar against the strongest0.5ms
the competitive read · 2 tests
says the engines are answering without you when citations are zero0.5ms
compares shares when you do hold some0.7ms
it declines to speak when it has nothing to say · 3 tests
returns null when nothing has ever been measured1.5ms
survives a missing visibility detail without inventing a sample size1.7ms
always ends with exactly one next action when it can name one0.6ms
country-filter.vitest.ts
11/11 7ms · 2 suites PASS
src/leads/shared/country-filter.vitest.ts
the country backstop asserts what was REQUESTED, not a constant · 7 tests
keeps a GB row on a GB request — the case that returned nothing2.5ms
keeps a CA row on a CA request0.5ms
still keeps a US row on a US request — the path that already worked0.3ms
STILL DROPS a row from the wrong country — this is a backstop, not a no-op0.5ms
treats an unspecified country as US, because that is what the RPC does0.3ms
reads the default from ONE place shared with the sender0.4ms
is case- and whitespace-insensitive on both sides0.4ms
every other compliance check is untouched · 4 tests
still drops a non-active entity0.4ms
still drops a source that is disabled or not distributable0.4ms
still drops a source not licensed for this purpose0.3ms
still drops a candidate carrying an invalid identifier0.4ms
normalization.vitest.ts
11/11 12ms · 1 suite PASS
src/leads/shared/normalization.vitest.ts
shared lead normalization · 11 tests
normalizes domains without retaining URL transport details2.8ms
normalizes valid email and rejects malformed input0.6ms
converts US phone formats to E.164 and rejects extensions/non-US prefixes0.6ms
normalizes a GB number to +44 instead of mangling it into a US number0.6ms
normalises AU numbers, and refuses the shapes the export is full of0.6ms
normalises NZ numbers, and rejects the foreign numbers that dominate that column1.5ms
normalises IN numbers, keeping the Delhi landlines a NANP-shaped rule would discard0.6ms
keeps the two PHONE_RULES copies from drifting on which countries exist1.3ms
REFUSES rather than guesses when the region is unsupported or missing0.9ms
keeps normalizeUsPhone byte-compatible so US call sites are unchanged0.4ms
creates a deterministic person-name key without punctuation or accents0.7ms
run-attribution.vitest.ts
10/10 12ms · 2 suites PASS
src/billing/run-attribution.vitest.ts
LLM cost resolution + provenance · 6 tests
prefers the provider-reported cost and labels it measured3.5ms
falls back to the rate table when no cost is reported, labelled estimated1.1ms
a reported cost of exactly 0 does NOT bypass the free-model floor0.6ms
a floored free-model turn still bills something, at the same ×10 as everything else0.6ms
a merely-cheap model keeps its real rate — the floor is only for $0 on BOTH sides0.7ms
a negative or non-finite reported cost never becomes a bill0.8ms
platform overhead buckets · 4 tests
covers every background spender found in the Phase 0 audit, plus unrecovered spend0.5ms
the render marker can never become a charge1.9ms
unrecovered spend is excluded from what users are billed1.2ms
the judge is platform overhead — reverting would re-charge users for our own QA0.4ms
tier-aware-ceiling.vitest.ts
10/10 12ms · 3 suites PASS
src/billing/tier-aware-ceiling.vitest.ts
seo_serp_spider prices at the tier that asked · 5 tests
the free ceiling is what a free run can actually cost2.8ms
the paid ceiling is EXACTLY unchanged from the static row0.7ms
THE CHURN CASE: a free user can now afford it, and could not before0.4ms
an explicit page count is also capped to the tier0.3ms
NO tier given → the paid shape, which over-reserves rather than under0.2ms
NO tool outside the opt-in set may have its reservation lowered · 2 tests
every static ceiling is still reserved for non-members2.4ms
membership requires an estimator that actually reads the plan4.0ms
both authors price at the tier — the gate AND the card · 3 tests
requireSufficientBalance resolves the plan when given no override0.6ms
getCostApprovalPlan prices the card at the same tier0.4ms
a per-call override is never overwritten by the tier shape0.4ms
gsc-sortable.vitest.ts
10/10 18ms · 2 suites PASS
src/admin/gsc-sortable.vitest.ts
compareValues — missing values sink · 7 tests
sorts null LAST when ascending, not first as position zero3.3ms
sorts null LAST when descending too — absent is not an extreme0.5ms
treats empty string as missing, so a blank label does not sort as "before A"11.1ms
ties between two missing values are stable (0), not arbitrary0.6ms
compares numbers numerically, not lexically (10 is not less than 9)0.4ms
compares strings with localeCompare0.5ms
does not treat 0 as missing — a real zero is a real value0.4ms
nextSort — first click lands on the useful end · 3 tests
starts a metric column descending (biggest first)0.5ms
starts position ASCENDING — rank 1 beats rank 90, so best-first is the useful end0.3ms
toggles direction when the same column is clicked again0.4ms
account-deletion.vitest.ts
10/10 33ms · 3 suites PASS
src/auth/account-deletion.vitest.ts
softDeleteAccount · 4 tests
sets the lock marker, blanks PII, and revokes credentials — caller-scoped8.9ms
reports disable_auth_user as a FAILED step when the mutation throws1.2ms
marker step failure → ok:false, but later failures are isolated (best-effort)2.5ms
accountDeletedKey is per-user and namespaced0.8ms
account deletion confirmation · 4 tests
sends the confirmation, and does it LAST — after the lock, scrub and revocations3.2ms
actually uses the email parameter it has always accepted0.8ms
uses the exact kind the consent gate exempts1.5ms
dedupes on the user, with no time window0.6ms
PII_SETTING_KEYS coverage · 2 tests
every PII key is a real settings key, so erasure targets something that exists11.7ms
scrubs the business address — the most identifying field we store about the user0.6ms
jwt-reads.vitest.ts
10/10 35ms · 1 suite PASS
src/db/jwt-reads.vitest.ts
6c: request-context reads carry the JWT · 10 tests
handleActivityPoll takes userToken and passes it to the dual seam3.9ms
handleGetKeywords takes userToken and passes it to the dual seam9.5ms
handleSerpSpiderTasks takes userToken and passes it to the dual seam1.1ms
listConnections takes userToken and passes it to the dual seam0.7ms
the routes hand the request token to the three handlers11.2ms
listConnections receives the token from the tool paths that have one5.0ms
getChatFeedbackRecord states why it stays on the admin seam0.6ms
handleSerpSpiderResults states why it stays on the admin seam0.4ms
fetchSitePerformance states why it stays on the admin seam1.2ms
handleCommerceAuthedRoute states why it stays on the admin seam0.8ms
render.vitest.ts
10/10 7ms · 1 suite PASS
src/email/render.vitest.ts
email render · 10 tests
keeps paragraphs as <p> blocks2.3ms
preserves paragraph breaks in the plain-text alternative0.5ms
preserves inline anchors and escapes the rest0.4ms
keeps paragraphs in both parts when composing0.7ms
does not click-wrap the unsubscribe link0.3ms
click-wraps body links0.5ms
adds UTM to body links1.3ms
does not overwrite existing UTM0.4ms
keeps the unsubscribe link UTM-free0.3ms
injects the open pixel and signature0.3ms
transactional.vitest.ts
10/10 6ms · 2 suites PASS
src/email/transactional.vitest.ts
transactional templates · 4 tests
renders seo rank digest with movement-first copy2.3ms
renders seo rank digest highlights-only check-in copy0.3ms
renders inactivity reminder with progress highlights0.3ms
renders setup nudge with concrete blockers0.4ms
isPermanentEmailFailure · 6 tests
treats a provider suppression as permanent0.4ms
treats a hard bounce as permanent0.3ms
does NOT treat a quota failure as permanent0.3ms
does NOT treat a rate-limit failure as permanent0.2ms
handles the real multi-provider error string from NQZAI-7W0.2ms
is false for empty/missing errors0.2ms
aeo-routing.vitest.ts
10/10 43ms · 3 suites PASS
src/chat/aeo-routing.vitest.ts
a named page is never answered with a site-wide sweep · 2 tests
the live regression: one URL + one query does not open the engine selector11.9ms
any URL with a PATH is page-level, whatever else the sentence says4.6ms
site-level asks still reach the engine selector · 5 tests
"am I cited by AI for kakunin.ai" still routes to the selector20.7ms
"check my AI visibility" still routes to the selector0.6ms
"am I visible in AI search?" still routes to the selector0.5ms
"is https://kakunin.ai ai-visible" still routes to the selector0.9ms
"run an AEO check" still routes to the selector0.5ms
the older carve-outs still hold · 3 tests
a full AEO audit is the composite, not the visibility-only selector0.6ms
"write X in AEO mode" is the writer, not the selector1.1ms
page-readiness phrasing stays with the page checker0.6ms
concealed-failure-disclosure.vitest.ts
10/10 22ms · 4 suites PASS
src/chat/concealed-failure-disclosure.vitest.ts
it asks the judge's question one step earlier · 2 tests
reuses the SAME two functions the judge uses2.6ms
fires only when the failure is CONCEALED, never merely present0.9ms
the acknowledgement check is what keeps this quiet on honest turns · 3 tests
a reply that owns the failure needs no note1.0ms
no failure at all needs no note0.4ms
a confident answer over a failed run is NOT acknowledged — the case that churns users0.9ms
one note per turn, and the specific one wins · 3 tests
rejected-and-nothing-ran takes precedence over concealed-failure0.5ms
neither fires on an approval card0.4ms
the stand-down names the tool, so it is countable per tool0.5ms
the matched intent is now recorded · 2 tests
reqCtx carries it and the judge writes it13.4ms
is captured SYNCHRONOUSLY, next to the other reqCtx reads0.4ms
filter-notes.vitest.ts
10/10 18ms · 5 suites PASS
src/chat/filter-notes.vitest.ts
adjusted filters · 2 tests
never reports an applied filter as "Not applied"8.0ms
says what actually happened0.6ms
dropped filters — unchanged · 1 test
still reports a removed filter as not applied0.4ms
both at once — the case neither of us had covered · 3 tests
shows BOTH notes, not just the first0.4ms
leads with what ran before what did not0.7ms
does not leak the raw marker fields into the body0.5ms
neither · 2 tests
adds no note at all on a clean run0.4ms
treats empty arrays as absent0.5ms
the live two-note turn, built from the real filter helper · 2 tests
renders the widened band AND every unfilterable owned-tier attribute4.5ms
says nothing about those three when the paid provider ran1.3ms
growth-ask.vitest.ts
10/10 31ms · 2 suites PASS
src/chat/growth-ask.vitest.ts
a growth ask is a why-question in different clothes · 7 tests
recognises the exact phrasing that failed live4.2ms
recognises the family, not just that one string1.7ms
still requires a subject the platform actually measures0.9ms
does not match across a sentence boundary0.3ms
leaves product questions and plain how-tos alone1.2ms
stands down when the user asked for an action outright16.5ms
treats the paid indexation audit as evidence-first gated0.6ms
diagnose owns its next steps · 3 tests
surfaces the tool's own horizon chips first2.7ms
offers to MEASURE when stored evidence could not settle the question1.0ms
never returns more than four0.5ms
honest-zero-judged.vitest.ts
10/10 6ms · 3 suites PASS
src/chat/honest-zero-judged.vitest.ts
an empty list is a MEASURED zero, not an unreadable shape · 3 tests
list_campaigns reads as yield 02.4ms
list_sequences reads as yield 00.5ms
and a populated one still reads as its count0.3ms
so the judge is told it is an honest empty, not a non-delivery · 4 tests
classifies as an honest empty0.3ms
the DELIVERY note is suppressed — this is the note the judge obeyed0.4ms
and the OUTCOME note tells it a stated zero is the verified answer0.4ms
without the fix, the delivery note fires — the exact 0.1 turn0.5ms
the widening did not arm the wrong signal · 3 tests
an empty `failures` array is NOT a zero yield0.4ms
an empty `applied` array is NOT a zero yield either0.3ms
an unknown shape still reads as NO MEASUREMENT, never as zero0.3ms
paid-escalation.vitest.ts
10/10 10ms · 4 suites PASS
src/chat/paid-escalation.vitest.ts
a total miss with a paid option · 4 tests
QUOTES the wider search — the number that was computed and never shown2.9ms
offers to run it0.8ms
does NOT blame the query for our coverage gap0.5ms
still offers the one-click paid chip beside the prose2.4ms
a total miss WITHOUT a paid option keeps the original advice · 2 tests
retains the retry guidance0.4ms
invents no price0.4ms
a structural note is preserved AND priced · 3 tests
keeps the structural explanation0.4ms
appends the price rather than dropping it0.4ms
sentence-cases the note at the seam0.8ms
a partial fill is unchanged · 1 test
still states the shortfall and its price0.5ms
query-classifier.vitest.ts
10/10 48ms · 4 suites PASS
src/chat/query-classifier.vitest.ts
tokenize · 2 tests
lowercases, strips punctuation, drops stopwords and 1-char tokens, stems plural/tense3.6ms
stems common verb/noun suffixes so tense/plural line up with the base form0.7ms
fuzzyOverlapScore · 3 tests
scores 0 with no overlap1.2ms
scores 1 when the message covers every intent keyword0.3ms
is recall against the intent vocabulary, not punished by message length/filler0.3ms
keywordsForIntent · 1 test
derives from the label and bonusKeywords already authored on the intent2.7ms
PatternClassifier — fuzzy fallback catches honest paraphrases · 4 tests
classifies a paraphrase the exact pattern misses on word proximity31.7ms
still prefers an exact pattern match over fuzzy when one exists0.5ms
returns null on a short, generic message with no real signal4.4ms
never fuzzy-matches shortcut (chatRouter) intents — only LLM-owned intents1.2ms
send-report-format.vitest.ts
10/10 15ms · 4 suites PASS
src/chat/send-report-format.vitest.ts
send_emails failure reporting collapses by reason · 4 tests
25 recipients failing for ONE reason prints that reason ONCE — the incident case8.0ms
tells the user their drafts survived — the first thing they want to know0.6ms
distinct reasons stay distinct — collapsing must not hide a second problem0.5ms
a fully successful send says nothing about failures0.6ms
send_emails confirm preview renders a table · 2 tests
emits a markdown pipe table, not bullets0.7ms
names the remainder when the preview is shorter than the total0.3ms
mdCell · 3 tests
escapes a pipe so a subject cannot break the row0.4ms
collapses newlines, which would end the row outright0.3ms
renders an em dash for empty values rather than an empty cell1.2ms
generate_emails relays a partial run · 1 test
surfaces the deferred note — a field the formatter does not read never reaches the user0.6ms
xml-tool-call-salvage.vitest.ts
10/10 12ms · 3 suites PASS
src/chat/xml-tool-call-salvage.vitest.ts
the call is RECOVERED, not apologised for · 4 tests
parses the exact message siriiusblack0 received4.8ms
honours string="false" — a typed value is not passed through as text0.6ms
is PREFIX-AGNOSTIC — the dialect family, not one vendor token1.0ms
the JSON dialect it always handled still works0.5ms
it never guesses · 3 tests
an UNREGISTERED name is not salvaged0.4ms
the REDACTED name cannot resurrect a call0.3ms
ordinary prose containing angle brackets is untouched0.4ms
the backstop, for when salvage cannot recover it · 3 tests
the detector now SEES the XML dialect it was written for0.7ms
still gated on delivery — a turn that delivered is never rewritten1.0ms
report HTML is not mistaken for a typed tool call0.9ms
dropleads.vitest.ts
10/10 56ms · 4 suites PASS
src/leads/dropleads.vitest.ts
filters: only keys measured to narrow are sent · 3 tests
maps role, country, city, domain, technology and keyword; never the ignored location scopes5.2ms
a search with no role, domain or keyword has no target and never pages the whole database0.4ms
spells the industry the way the provider does0.4ms
parsing the provider shapes seen live · 2 tests
reads a search page1.8ms
builds a lead from an enrich, carrying the provider's verdict as the provider's claim0.7ms
the rung: free search, paid enrich to the shortfall, misses counted · 4 tests
validates the industry with a free count, drops and declares it when the provider holds no such label43.3ms
keeps the industry when the count could not be read — an unreadable count is not evidence1.4ms
a search failure is said and charged nothing; no enrich is attempted1.0ms
is absent without a treg token1.1ms
the price is in the table and matches the measured credit rate · 1 test
dropleads_enrich = 0.2 credit at the account rate; search is free and visible0.4ms
us-states.vitest.ts
10/10 11ms · 3 suites PASS
src/leads/us-states.vitest.ts
US_STATE_MAP · 2 tests
has exactly 50 entries4.0ms
every value is a 2-letter uppercase code2.4ms
US_STATE_ENUM · 2 tests
is title-cased, matching the schema enum this repo's other lists use1.0ms
has one entry per state0.6ms
resolveUsStateCode · 6 tests
resolves a title-cased name0.5ms
resolves a lowercase name0.3ms
resolves an uppercase abbreviation0.2ms
resolves a multi-word state name0.3ms
returns undefined for garbage input0.8ms
returns undefined for an empty string0.3ms
verify-email.vitest.ts
10/10 44ms · 2 suites PASS
src/leads/verify-email.vitest.ts
verifyEmail MillionVerifier mapping · 5 tests
treats catch_all as valid, not risky36.9ms
treats unknown as valid, not risky1.3ms
still treats disposable as risky0.6ms
still treats invalid mailboxes as invalid0.7ms
still treats ok as valid0.7ms
verifyEmail strict mode (guessed permutations) · 5 tests
downgrades catch_all to risky so a guessed address is discarded0.7ms
downgrades unknown to risky for the same reason1.1ms
still accepts an affirmatively confirmed mailbox0.5ms
still rejects a dead mailbox0.6ms
leaves disposable risky0.8ms
publish-capture.vitest.ts
10/10 10ms · 4 suites PASS
src/reports/publish-capture.vitest.ts
the artifact card carries the row a publish should close · 2 tests
passes content_piece_id through when the tool recorded one2.6ms
omits it rather than sending an empty string when no piece was recorded0.6ms
a DRAFT is not a publication · 3 tests
the WordPress branch records only on status=publish0.7ms
records only when the connector actually returned a URL0.3ms
the capture never gates the publish response0.3ms
a PARTIAL Notion page is not the article · 2 tests
is excluded from capture, matching what the client already refuses to celebrate0.4ms
records with connector provenance when the page is whole0.5ms
the client echoes the row id back on both paths · 3 tests
the document card carries it as a data attribute0.4ms
both publish calls send it, and neither sends an empty one1.7ms
reads the id off the card even when title/content were passed in0.5ms
about-schema.vitest.ts
10/10 70ms · 2 suites PASS
src/routes/about-schema.vitest.ts
/about structured data · 6 tests
emits parseable JSON-LD41.5ms
describes the page as an AboutPage whose mainEntity is the organization3.3ms
reuses the homepage organization @id so the two nodes merge into one entity1.5ms
names the operating company and its postal address10.6ms
carries a sameAs for the nqzai entity itself, not just its parent company3.9ms
never breaks out of the script tag1.9ms
/quality-test crawlability · 4 tests
links to both reports with real anchors, not buttons1.4ms
carries a noscript fallback for agents that do not run JavaScript0.6ms
emits a CollectionPage listing both reports1.9ms
lists the quality gates in the page sitemap2.4ms
pillar-page-css.vitest.ts
10/10 78ms · 1 suite PASS
src/routes/pillar-page-css.vitest.ts
the pillar stylesheet is defined once and shared · 10 tests
is substantial — an emptied export would silently unstyle both pages7.6ms
/ai-visibility-answered emits the shared block, not a copy54.9ms
/ai-visibility-answered sets data-theme, or every token resolves to nothing2.7ms
/ai-visibility-answered emits robots as a META TAG, never a bare string2.2ms
/ai-visibility-answered carries the three JSON-LD blocks the template requires1.7ms
/seo-questions-answered emits the shared block, not a copy1.9ms
/seo-questions-answered sets data-theme, or every token resolves to nothing1.8ms
/seo-questions-answered emits robots as a META TAG, never a bare string1.6ms
/seo-questions-answered carries the three JSON-LD blocks the template requires1.6ms
neither page carries its own copy of the pillar rules1.5ms
llm-json.vitest.ts
10/10 12ms · 3 suites PASS
src/runtime/llm-json.vitest.ts
parseLlmJsonArray · 7 tests
parses clean output unchanged3.9ms
survives a real newline inside an email body1.5ms
handles tabs and carriage returns too0.4ms
finds the array inside surrounding prose0.5ms
still fails loudly on a truncated array0.5ms
still fails on genuinely broken structure0.4ms
rejects a JSON value that is not an array0.7ms
parseLlmJsonObject · 1 test
parses the personalized-opener shape1.0ms
escapeControlCharsInStrings · 2 tests
leaves structural whitespace alone0.6ms
does not double-escape an already-escaped sequence0.9ms
backlink-routing.vitest.ts
10/10 7ms · 2 suites PASS
src/tools/backlink-routing.vitest.ts
the backlink family is DISCOVERABLE from the always-on pointer · 4 tests
names backlinks in the words a user would use2.2ms
names each distinct backlink question the family answers0.7ms
warns that two pairs sound alike, which is the whole failure mode0.3ms
says several are paid, so cost is not inferred from the family name0.3ms
and DISTINGUISHED once the family is loaded · 6 tests
seo_backlink_value states its own routing rule0.5ms
seo_backlink_deep_scan states its own routing rule0.3ms
seo_backlink_verify states its own routing rule0.2ms
seo_backlink_gap states its own routing rule0.3ms
tells the model that WORTH is not the off-page audit — from BOTH sides1.2ms
marks the free ones free, so cost is not inferred from the family name0.4ms
redirect-consequence-gate.vitest.ts
10/10 29ms · 3 suites PASS
src/tools/redirect-consequence-gate.vitest.ts
connector actions are on the side-effect axis · 4 tests
the four that write on a tenant-owned third-party account are external5.1ms
the route-backed connector actions are declared, and are actually reachable6.5ms
an outward publish that FAILS reports to Sentry — not only to the button12.6ms
the Google connector reads stay reads — url_inspection is a query, not a submission1.1ms
the fix_redirect shortcut cannot write without a confirmation · 4 tests
detects read-only, then stops — it does not write on this turn0.5ms
fails CLOSED when the confirmation store is unavailable0.3ms
reports detection failures rather than swallowing them0.5ms
presents CONSEQUENCE, not cost — the action is free and must not read as a price0.3ms
the detect+write function is gone, not merely bypassed · 2 tests
fixRedirectAuto no longer exists anywhere1.3ms
detectRedirectFixTool returns a tool NAME and performs no write0.5ms
seo-family-rules.vitest.ts
10/10 7ms · 3 suites PASS
src/tools/seo-family-rules.vitest.ts
the three rules that were NOT already on their tool · 4 tests
MOVE 1 — seo_offpage_audit says it covers ranking keywords, not just links2.2ms
MOVE 2 — seo_google_merge says compare=true is mandatory for a change question0.6ms
MOVE 3 — seo_backlink_value says a WORTH question is not the off-page audit0.5ms
and the mirror of MOVE 3 is on the tool that was wrongly chosen0.3ms
the dead instruction is gone and must not come back · 2 tests
nothing model-facing routes to full_seo_audit0.4ms
and "audit my site" is answered with tools the model CAN call0.5ms
the duplication is actually gone · 4 tests
the per-bullet routing table is not in V2_SYSTEM any more0.2ms
the never-say-free rule is stated ONCE, not once per family0.8ms
the pointer is an order of magnitude smaller than the block it replaced0.7ms
V2_SYSTEM stays under its ratchet0.3ms
ai-visibility-score.vitest.ts
10/10 9ms · 3 suites PASS
src/seo/ai-visibility-score.vitest.ts
computeAiVisibilityScore · 1 test
is a weighted composite, normalized over PRESENT pillars only3.6ms
aiVisibilityFromAudits — Dim-5 unification with the dashboard · 4 tests
maps the three sub-audit results to the four pillars and matches computeAiVisibilityScore1.0ms
falls back to nested rag_readiness.rag_score when the top-level field is absent0.5ms
a failed sub-audit drops its pillar (normalized over the rest), not scored 00.3ms
all sub-audits failed → null (caller surfaces an error, never a fake 0)0.3ms
the two Google surfaces share one slice · 5 tests
a score with no AI Mode computes EXACTLY as it did before AI Mode existed0.4ms
with both present, Google still carries 0.25 in total — not 0.500.6ms
AI Mode alone carries the full Google slice when no Overview was measured0.4ms
averages the two surfaces when they disagree1.3ms
aiVisibilityFromAudits threads ai_mode_pct through0.4ms
backlink-store.vitest.ts
10/10 38ms · 2 suites PASS
src/seo/backlink-store.vitest.ts
toStoredRows · 4 tests
derives the referring domain and keeps the provider fields5.6ms
dedupes within a batch — a live index can return the same URL twice mid-scan3.0ms
drops rows with no usable URL rather than storing a blank referrer0.9ms
keeps missing numeric fields NULL instead of coercing them to zero0.9ms
deepScanNote — warns about paying twice, never predicts what it cannot know · 6 tests
says nothing when nothing is on file — an all-new scan needs no caution0.4ms
warns when the last scan was recent, and gives BOTH measured numbers22.6ms
stays quiet once enough time has passed for links to accrue0.6ms
warns that pages past the end of a profile cost a lookup and return nothing0.8ms
never forecasts how many new links exist — that number is the purchase itself1.2ms
asks for a site before anything else1.3ms
content-ideas-knows-what-we-wrote.vitest.ts
10/10 7ms · 3 suites PASS
src/seo/content-ideas-knows-what-we-wrote.vitest.ts
the dedup reads what we WROTE, not only what is published · 3 tests
loads the tenant's content pieces for this site2.7ms
a failed read does not take the tool down0.5ms
written topics join the same dedup set the sitemap feeds0.4ms
and the RESULT says so, so the model cannot deny it · 5 tests
carries a count and the pieces themselves0.5ms
names the state, because published and drafted lead to different actions0.3ms
forbids the exact sentence that was wrong0.2ms
and tells the model publishing beats drafting when pieces are unpublished0.3ms
the note is omitted entirely when nothing has been written0.3ms
the coverage note steers to publishing, not to another draft · 2 tests
has a branch for "everything overlaps AND drafts are unpublished"0.3ms
which is checked BEFORE the generic overlap message0.6ms
content-verify.vitest.ts
10/10 27ms · 4 suites PASS
src/seo/content-verify.vitest.ts
extraction (code) · 4 tests
lists linked sentences first, then numeric sentences without a link; headings and list counts never count5.7ms
reads outline sections from headings, H2: lines and numbered items, skipping boilerplate1.4ms
picks the source window that carries the claim's numbers1.2ms
page text drops scripts, styles and tags0.9ms
judgments · 1 test
one choice per claim with a contradicted outcome, one noul per section0.7ms
presentation · 4 tests
the note states counts the pass measured and names the missing sections0.7ms
the block lists the contradicted claim with its source and the unreachable source; nothing when there was nothing to check1.5ms
findings carry provenance judged so the report shows them as verdicts, not counts1.6ms
a skipped pass is said, never rendered as clean0.4ms
wiring · 1 test
the write tool verifies inline when its clock allows, otherwise in the background as an activity line; the chat renderer shows the block12.1ms
keyword-vs-prompt.vitest.ts
10/10 18ms · 2 suites PASS
src/seo/keyword-vs-prompt.vitest.ts
the two worlds are measured, not asserted · 6 tests
counts word length and question-form on both sides2.5ms
different_languages survives, quoting both sides1.1ms
names the two SYSTEMS as its sources, not two fields of one1.5ms
no_shared_terms is a SEPARATE finding from the language gap1.8ms
an OVERLAPPING term flips it and is named1.0ms
comparison prompts are read for third-party share0.3ms
what it refuses · 4 tests
the rank-versus-prompt join is untested, and says WHY it cannot be approximated0.6ms
where the prompt language comes from is not ours to see0.6ms
no Search Console terms is a different sentence from no panel5.5ms
claims no trend — the keyword table keeps no history1.9ms
onpage-js-rendering.vitest.ts
10/10 17ms · 3 suites PASS
src/seo/onpage-js-rendering.vitest.ts
detecting a site whose content is behind JavaScript · 4 tests
the SPA shell that measured 1 page is detected4.5ms
a server-rendered site is NOT charged the 10x rate1.4ms
DEGRADES TO NO RENDERING when the probe fails0.7ms
markup alone is not content — script and style bodies do not count0.5ms
the rendered crawl is priced as its own leg · 3 tests
the JS rate is what the provider actually billed0.7ms
a rendered crawl costs an order of magnitude more, so it can never be the default0.4ms
the base rate is KNOWN-LOW and left alone on purpose0.4ms
a zero-page crawl states what was observed, not a cause · 3 tests
no longer tells the owner their site blocks crawlers3.6ms
says the crawl reached the site, and that nothing was charged2.4ms
distinguishes the case where rendering was ALREADY on1.9ms
request-indexing.vitest.ts
10/10 38ms · 1 suite PASS
src/seo/request-indexing.vitest.ts
runRequestIndexing · 10 tests
with no URLs and no Serpdex gaps: asks for URLs, does not submit5.7ms
free (no top-up): returns an upsell with the count, never submits or charges1.1ms
paid: submits, charges per URL, and confirms4.8ms
paid but insufficient balance: refuses with a top-up message, never submits or charges19.8ms
paid but provider unconfigured: clean error, no submit0.7ms
pulls the latest Serpdex gaps when no URLs are passed1.5ms
never submits a competitor run: skips other-domain tasks and picks the tenant's own0.7ms
only competitor runs on file: refuses, submits nothing, flags it to telemetry1.8ms
no saved site: refuses to guess which run was the tenant's own0.8ms
drops off-site URLs found inside the tenant's own task0.8ms
sov-weekly.vitest.ts
10/10 8ms · 4 suites PASS
src/seo/sov-weekly.vitest.ts
weekly config round-trip · 3 tests
survives serialize → parse3.3ms
drops engines that are not real engines rather than passing them to the fan-out0.6ms
treats malformed stored config as absent, never throws0.3ms
the quote is the same arithmetic the run will be billed · 2 tests
prices through aeoVisibilityFanoutCostUsd, not a second estimate0.5ms
one engine costs materially less than four — the lever the copy tells them to pull0.3ms
the offer states the cost · 4 tests
an OFF state that offers the run must carry the number0.9ms
an ON state says which engines and where the choice came from0.5ms
quotes nothing rather than something vague when the resolver failed0.4ms
a plan that does not include the run says so instead of offering it0.4ms
fmtTok · 1 test
formats the ranges the copy actually uses0.4ms
honesty.vitest.mjs
10/10 12ms · 4 suites PASS
scripts/lib/honesty.vitest.mjs
honesty — "free" is a claim, not a word · 3 tests
flags a claim, and quotes the text around it3.5ms
does not flag a negation — that is the product saying what §4 requires2.6ms
does not flag product nouns or idioms0.9ms
honesty — universal rules judge our prose, not the tenant's rows · 2 tests
a "free" inside a rendered block is data when the caller passes prose0.4ms
per-row assertions still see the whole reply, table included0.4ms
honesty — a negated work word is not a claim of work · 4 tests
does not flag "haven't been verified yet" on a tool-backed reply with no number0.5ms
does not flag an adjective — "four saved competitors on file" ([8.2.1], 2026-09-15)0.7ms
does not flag a zero stated before the noun — "No contacts found for that query" ([3.1.3], 2026-09-16)0.5ms
still flags an unnumbered positive claim1.0ms
honesty — the tenant's own money stays in currency · 1 test
a tenantMoney row may quote the scanned site's price and its free plan0.8ms
embed-candidate-predicate.vitest.ts
10/10 9ms · 3 suites PASS
src/leads/shared/embed-candidate-predicate.vitest.ts
the candidate query requires something to embed · 3 tests
demands at least one of the four fields composeContent uses3.1ms
ORs them — requiring all four would exclude almost everybody1.9ms
uses the SAME location expression the SELECT list does0.4ms
the predicate is deliberately LOOSER than composeContent · 3 tests
does not re-implement LOCATION_TITLE_RE in SQL0.5ms
does not re-implement GENERIC_BARE_TITLES in SQL0.3ms
composeContent still exists and still decides0.3ms
161 · search_candidates pins hnsw.ef_search · 4 tests
sets it on the function, not on a session or a role0.3ms
loads the vector library first, or the ALTER fails as an unrecognized parameter0.3ms
does not regenerate the function body0.9ms
asserts the three pre-existing pins survive0.4ms
reveal-billing.vitest.ts
10/10 17ms · 2 suites PASS
src/leads/shared/reveal-billing.vitest.ts
what the customer is actually charged · 5 tests
charges the FULL-price key for a confirmed mailbox5.6ms
charges the HALF-price key for a domain-derived catch-all1.2ms
charges NOTHING at all for an address we proved bounces1.2ms
charges nothing when the corpus cannot say what it checked0.8ms
keeps the ledger amount and the charged key in step1.2ms
the rollout gate — SHARED_LEADS_BILLING · 5 tests
charges NOTHING when the flag is unset — the state every deploy lands in0.8ms
still records the reveal in the ledger at zero, not skipping the row1.7ms
charges when armed for everyone0.8ms
charges the NAMED tenant and nobody else2.1ms
fails closed on every ambiguous input0.8ms
vitals-auth.vitest.ts
9/9 19ms · 3 suites PASS
client/vitals-auth.vitest.ts
a vitals request carries the session, like every other data fetch · 3 tests
sends the bearer token5.9ms
sends it on BOTH endpoints — the token bar is a separate fetch2.4ms
omits the header rather than sending "Bearer undefined" when signed out2.8ms
a 401 is classified, not lumped · 3 tests
a tokenless 401 reads as "no session", not as a fault0.9ms
a 401 WITH a token is still a real failure — the token is bad or expired0.8ms
still fails on non-401 errors, so the classification did not widen1.3ms
the real call site still passes headers · 3 tests
vitalsFetch calls fetch with an options object carrying authHeaderMap()1.2ms
the real call site still classifies a tokenless 4011.3ms
authHeaderMap is the shared helper, not a second copy1.2ms
run-cap-before-card.vitest.ts
9/9 6ms · 2 suites PASS
src/billing/run-cap-before-card.vitest.ts
one resolver, so the card and the gate cannot disagree · 4 tests
free search_leads is a LIFETIME allowance, not a resetting one2.5ms
a paid plan sets no run count — the balance is the bound0.3ms
a TEST tenant is held to FREE counts whatever its plan says1.0ms
window widths cover all three periods with one mechanism0.3ms
the wiring, which no unit test can reach (getCostApprovalPlan is not exported) · 5 tests
the card path peeks the cap WITHOUT consuming an allowance0.5ms
DISPATCH uses the same resolver, so the two cannot drift0.4ms
every gate treats "no runs left" exactly like "cannot pay"0.5ms
the cap OUTRANKS the price in the message0.2ms
it FAILS OPEN — an unreadable counter shows the card0.3ms
advisory.vitest.ts
9/9 14ms · 2 suites PASS
src/admin/advisory.vitest.ts
directionFor · 3 tests
reports up when current exceeds previous beyond epsilon5.0ms
reports down when current is below previous beyond epsilon0.4ms
reports flat when the difference is within epsilon (avoids noise from float rounding)0.2ms
computeChangesSinceLast · 6 tests
returns null when there is no previous brief (first-ever run)2.8ms
returns null when the previous brief predates the economics/mandate schema (old shape guard)0.6ms
computes real economics deltas with direction, skipping fields that were null in either snapshot1.5ms
computes mandate revenue-impact deltas keyed by the exact mandate title, using the midpoint of low/high1.1ms
always returns all three mandate titles even if the previous brief is missing one (defensive against schema drift)0.4ms
reports days_since_previous as a non-negative number derived from generated_at1.1ms
defect-class-routing.vitest.ts
9/9 9ms · 2 suites PASS
src/admin/defect-class-routing.vitest.ts
the routing class · 6 tests
is registered on both axes4.4ms
catches the live session the console called clean1.0ms
does NOT fire when the tenant has a saved site1.2ms
does NOT fire when no site-scoped tool ran0.3ms
treats an UNREADABLE saved-site as unknown, never as absent0.3ms
is not reached when the signals are absent entirely (old callers)0.3ms
ordering against the classes it sits between · 3 tests
functional still wins — work that reached NOTHING outranks work missing a piece0.3ms
routing wins over presentation — a missing side effect outlives a badly-read reply0.3ms
judge still wins — a wrong instrument hides every class behind it0.3ms
gsc-query-page.vitest.ts
9/9 11ms · 1 suite PASS
src/admin/gsc-query-page.vitest.ts
buildQueryPageMap · 9 tests
picks the best-RANKING page, not the one with the most impressions3.0ms
breaks a position tie by impressions0.5ms
totals clicks and impressions across ALL pages, and weights position by impressions0.6ms
flags cannibalization when two of our pages both have real exposure1.8ms
does NOT call a single stray impression on a second URL cannibalization0.5ms
does not flag a query where every page is marginal0.3ms
handles a single page per query without inventing competitors1.8ms
ranks the map by impressions so unmet demand is at the top0.9ms
skips rows missing either dimension rather than creating an empty grouping1.3ms
internal-accounts.vitest.ts
9/9 7ms · 2 suites PASS
src/admin/internal-accounts.vitest.ts
internalAccounts · 4 tests
reads emails and ids from env, merged and lower-cased2.9ms
tolerates whitespace, empty entries and trailing commas0.5ms
falls back to the known internal list when NOTHING is configured0.8ms
does NOT add the fallback on top of a configured list0.4ms
isInternalAccount · 5 tests
matches on email regardless of case or padding0.3ms
matches on user id too — a caller may hold only one identifier0.3ms
does not match a real customer0.2ms
handles missing identifiers without throwing0.3ms
does not treat a substring as a match0.3ms
mixpanel-time.vitest.ts
9/9 26ms · 3 suites PASS
src/admin/mixpanel-time.vitest.ts
tzOffsetMs · 3 tests
returns 0 for UTC20.1ms
handles DST on both sides of a transition (US/Pacific)0.8ms
handles eastern offsets (Asia/Kolkata, +5:30 year-round)0.5ms
naiveProjectTimeToUtcIso · 4 tests
converts a naive project-tz wall clock to true UTC1.1ms
accepts space-separated datetimes and missing seconds0.5ms
passes through values that already carry a zone0.3ms
returns null for junk0.3ms
exportEpochToUtcIso · 2 tests
undoes the project-timezone shift Mixpanel bakes into export epochs0.8ms
returns null for non-finite input0.3ms
quality-rows.vitest.ts
9/9 10ms · 2 suites PASS
src/admin/quality-rows.vitest.ts
judgeScore · 4 tests
reads the score the live judge actually writes — a string3.3ms
reads a numeric score, and the older quality_score spelling0.8ms
keeps a score of zero0.3ms
returns null rather than NaN for junk, so callers cannot compare against it0.4ms
isJudgeScoreRow · 5 tests
accepts a judged capability, including a variant key0.5ms
rejects every declared counter, shaped the way its writer shapes it2.0ms
rejects an undeclared future counter on shape alone0.4ms
accepts a scored row under a name nobody recognises — that alarm must still fire0.3ms
rejects a declared counter even if it starts carrying a score field0.7ms
release-ledger.vitest.ts
9/9 11ms · 1 suite PASS
src/admin/release-ledger.vitest.ts
computeMetrics · 9 tests
empty ledger → zeroed metrics, no alert3.4ms
lead time = deployed_at − build_time, median over builds that have a build_time1.8ms
deploy frequency counts only the 30-day window0.6ms
change-failure rate counts rollback OR incident; alerts only above 15% with ≥5 samples0.8ms
does not alert on a high rate built from too few samples0.3ms
MTTR = closed − opened, median over resolved incidents0.4ms
quality-at-release inherits the last judge digest on/before deploy day0.8ms
negative lead time (clock skew: build_time after deploy) is dropped, not negative0.9ms
digest line renders once something shipped1.5ms
synthetic-session.vitest.ts
9/9 10ms · 4 suites PASS
src/admin/synthetic-session.vitest.ts
the real ids, which have exactly one producer · 3 tests
keeps client-generated sessions3.3ms
KEEPS `default` — it is the server's own fallback, not a probe0.4ms
an unknown/empty id is NOT synthetic — never invent an exclusion0.2ms
the probe ids, taken verbatim from the contaminated window · 2 tests
excludes every harness session actually observed1.0ms
catches a probe run under a REAL user's account0.2ms
it is an ALLOWLIST, and that is the point · 2 tests
a probe name nobody has invented yet is still excluded0.2ms
does not reject a real id for containing probe-ish words0.4ms
the sessions view states what it could actually read · 2 tests
reads newest-first, so truncation costs the OLDEST rows1.4ms
reports coverage rather than presenting a partial window as whole1.6ms
telemetry-truncation.vitest.ts
9/9 8ms · 2 suites PASS
src/admin/telemetry-truncation.vitest.ts
truncationFlag · 7 tests
is not truncated below the cap2.5ms
IS truncated at exactly the cap — over-warning by one exact-fit window is the correct direction to be wrong in0.5ms
treats an empty list as complete, not as truncated0.3ms
reports true coverage when the unbounded total is known — "at least 25,000" read as nearly-complete when it was 9.7% of 257,5230.8ms
omits coverage when no total is available rather than inventing one0.3ms
never reports coverage above 1 when the sample outruns a stale aggregate0.3ms
treats a missing list as complete rather than throwing — a panel that never loaded is the error surface’s job, not this one’s0.3ms
TELEMETRY_CAPS · 2 tests
is one ceiling per list, derived from the page size and page count0.6ms
caps every list the console derives a displayed number from1.6ms
consequential-confirm.vitest.ts
9/9 9ms · 1 suite PASS
src/campaigns/consequential-confirm.vitest.ts
gated consequential tools · 9 tests
resume_campaign stages a pending action and refuses to act unconfirmed3.4ms
cloudflare_fix_email_dns stages a pending action and refuses to act unconfirmed0.6ms
resume_campaign declares confirm in its schema0.7ms
cloudflare_fix_email_dns declares confirm in its schema0.9ms
stages the SUBJECT, not a bare flag, so one confirm cannot authorise another action0.6ms
does NOT gate pause_campaign — stopping mail is the safe direction0.4ms
does NOT gate enroll_in_sequence — arming is not sending0.6ms
does NOT gate set_campaign_sequence — arming is not sending0.4ms
keeps all four classified as external regardless of where the human check sits0.9ms
list-unsubscribe.vitest.ts
9/9 75ms · 3 suites PASS
src/email/list-unsubscribe.vitest.ts
listUnsubscribeHeaders · 1 test
produces the RFC 8058 pair3.6ms
every provider forwards the headers · 5 tests
resend: headers reach the request body52.6ms
sendgrid: headers reach the request body1.1ms
mailtrap: headers reach the request body1.1ms
gmail: headers are lines in the raw MIME message2.4ms
smtp: the raw-message builder emits the header lines1.5ms
the callers · 3 tests
a contact send always carries the one-click headers for that contact1.4ms
our own transactional mail carries them only when the kind is marketing1.4ms
the one-click POST lands on a handler that exists8.8ms
sender-missing-sweep.vitest.ts
9/9 20ms · 3 suites PASS
src/email/sender-missing-sweep.vitest.ts
the bulk transport check is stricter than the one-off check · 2 tests
hasBulkSendingTransport ignores Gmail; hasSendingTransport still accepts it3.0ms
the drip still forbids Gmail, which is why the stricter check exists0.4ms
enrolment says whether the sequence can send · 4 tests
the dispatch asks before answering0.7ms
the reply leads with the blocking fact, not with "paused until you start it"7.5ms
and offers the connector, not "Start the sequence"3.0ms
a tenant that CAN send is unchanged0.6ms
a failed scheduled send reaches the user · 3 tests
the cron narrates the failure once per tenant per day0.7ms
the provider case gets the connect chip; other causes state their own reason0.9ms
the reason is passed out of the send, not only logged1.9ms
answer-chips.vitest.ts
9/9 22ms · 3 suites PASS
src/chat/answer-chips.vitest.ts
options the model wrote become options you can click · 3 tests
the Hinglish reply that listed report types5.8ms
the bundled ask that quoted a price it could not afford0.7ms
numbered options too0.5ms
a statement is not an offer, and a button that does nothing is worse than none · 5 tests
rejects findings and metrics written in the same bullet shape0.5ms
rejects a label that is too short, too long, or a URL0.7ms
ignores a rendered artifact — its bold headings are not options0.6ms
returns EMPTY for prose with no options, so the caller keeps its fallback0.7ms
dedupes and caps, so one answer cannot flood the chip row1.8ms
the wiring · 1 test
is consulted before the generic triple, and the triple survives as the fallback9.2ms
doc-lane.vitest.ts
9/9 20ms · 3 suites PASS
src/chat/doc-lane.vitest.ts
doc lane — negative sample: every live cmd string · 2 tests
has a real catalog to test against (not an emptied glob)3.4ms
never matches ANY live capability cmd phrasing5.7ms
doc lane — positive cases (synthetic fixture, isolated from the negative-set caution) · 4 tests
matches a "what does X do" question built from the entry's own label1.5ms
matches a "how does X work" explainer phrasing1.3ms
does not match when the fixture registry has no vocabulary overlap at all0.9ms
formats the answer as the tldr plus a link, not a raw field dump1.6ms
doc lane — refuses non-meta phrasing regardless of vocabulary overlap · 3 tests
does not match a direct task request even if it shares words with a doc-route label0.8ms
does not match a bare short message0.5ms
does not match an unrelated meta-shaped question with no real registry overlap2.8ms
pending-picker.vitest.ts
9/9 11ms · 2 suites PASS
src/chat/pending-picker.vitest.ts
the questions the platform can leave open · 4 tests
every registered picker is keyed by the id its route already reports2.8ms
carries the REAL chips, not a copy that can drift0.5ms
the depth chips are the strings the intent can actually match0.6ms
states the real tiers — 50 / 150 / 5000.4ms
the wiring — a picker is recorded, re-offered, and closed · 5 tests
every picker route records that it is now open0.5ms
answering it CLOSES it, so it is not re-offered afterwards2.7ms
re-offers it on the agent path when the turn wandered off0.8ms
A GATE STILL OUTRANKS IT — a Confirm chip is never replaced0.4ms
fails open — an unreadable KV must not break the reply0.8ms
persona-claim.vitest.ts
9/9 6ms · 2 suites PASS
src/chat/persona-claim.vitest.ts
does the user name an audience · 7 tests
Find me 10 new business owners in the US -> named=true2.4ms
find founders -> named=true0.4ms
find 10 managers -> named=true0.2ms
find me some CEOs -> named=true0.3ms
find leads for my business -> named=false0.2ms
get me 50 contacts -> named=false0.1ms
the shipped regex tolerates plurals0.3ms
naming a persona is a claim, not a guess · 2 tests
never claims the first persona by position0.5ms
claims one only when exactly one persona matches the targeting produced0.6ms
render-manifest-artifact.vitest.ts
9/9 23ms · 3 suites PASS
src/chat/render-manifest-artifact.vitest.ts
what the live producers actually set · 4 tests
a saved report chip counts — report_id is the field buildCapabilityChatPayload writes6.2ms
an artifact card counts0.8ms
inline artifact html counts0.6ms
a plain prose answer does not0.4ms
the predicate must not read better than reality · 3 tests
`is_html` alone is NOT an artifact — the capabilities MENU sets it0.4ms
`artifact_id` is NOT resurrected — nothing in the repo sets it9.9ms
an empty artifact field does not count1.0ms
rows and chips keep working — the fix must not narrow the meter elsewhere · 2 tests
rows are still summed across blocks0.5ms
chips still read suggestions0.5ms
scan-writes-identity.vitest.ts
9/9 16ms · 3 suites PASS
src/chat/scan-writes-identity.vitest.ts
the ownership question keys on the WRITE, not on the message · 3 tests
fires when a scan claimed a site that is not the one on file10.9ms
keeps the original first-turn condition rather than replacing it0.9ms
does NOT fire when the scan confirmed the site already on file0.6ms
the host is captured from the RESULT, not from the arguments · 2 tests
reads the scan result0.6ms
a FAILED scan claims nothing, so it must not trigger the question0.4ms
and the write is announced · 4 tests
a turn that scanned says what was saved0.6ms
it names the HOST, so a third party is visibly a third party0.3ms
and offers the correction in the same breath0.3ms
is deterministic, not a prompt instruction0.4ms
contacts.vitest.ts
9/9 10ms · 3 suites PASS
src/leads/contacts.vitest.ts
sanitizeEmail · 5 tests
strips a leading JSON-escaped ">" fragment (live bug: u003emakena_kelly@wired.com)3.4ms
peels other encoded/markup wrappers0.8ms
leaves clean addresses untouched (idempotent)0.5ms
rejects unrecoverable / non-emails as null0.5ms
does not corrupt a local part that merely contains hex-like runs mid-string0.3ms
autoListName · 3 tests
slugs significant words from a one-shot ask0.6ms
drops stopwords and numbers, caps length at 401.7ms
falls back to lead-search on empty/stopword-only input0.4ms
extractListName (still wins over auto-name when a list phrase is present) · 1 test
parses "…to <name> list"1.3ms
icp-surface.vitest.ts
9/9 35ms · 3 suites PASS
src/leads/icp-surface.vitest.ts
the schema is the fabrication guard, one layer down · 3 tests
takes NO arguments at all4.1ms
tells the model it is a proposal, not a verdict about the user’s market1.5ms
routes the question and states that it costs no provider spend2.3ms
what the user reads · 4 tests
keeps the quote beside the reading7.3ms
says nothing about sourcing when everything is matchable0.5ms
offers to fix the INPUT when the brief is too thin, not to try again1.4ms
offers the search only AFTER the profile has been shown0.6ms
the profile is rendered, never re-written by the model · 2 tests
marks its result render_verbatim so the agent loop does not paraphrase it3.3ms
the loop honours the flag with a general check, not a tool-name list13.5ms
plan-score.vitest.ts
9/9 6ms · 2 suites PASS
src/planner/plan-score.vitest.ts
rankPositionToScore100 · 3 tests
position 1 is a perfect score2.5ms
position 20 (the striking-distance floor) is zero0.3ms
clamps beyond the 1-20 window instead of going negative or over 1000.5ms
computeMarketingPlanScore (Finding 1 — honest composite, no fabricated legs) · 6 tests
averages onpage_score and aeo_score directly, rank via normalization0.5ms
drops unmeasurable (none) legs and campaign_replies — never scores them as 00.3ms
is null (an honest "—", not 0) when nothing is scorable0.4ms
current uses observed_value when present, falls back to baseline otherwise; delta only when both are known0.7ms
counts a shared measurement key ONCE, not once per initiative referencing it0.3ms
two DIFFERENT keys on the same adapter are two distinct votes, not deduped together0.3ms
lead-search-honesty.vitest.ts
9/9 12ms · 4 suites PASS
src/reports/lead-search-honesty.vitest.ts
a provider claim is never rendered as a verification · 3 tests
the artifact counts only rows we checked3.0ms
the chat summary counts only rows we checked0.7ms
an unchecked batch is told to verify, and told why0.5ms
found is not saved · 1 test
the artifact reports what actually landed, and accounts for the gap0.4ms
verify emails from the artifact · 3 tests
offers the verify action on the verified-rate card0.6ms
scopes the request to THIS batch's list0.4ms
routes through chat, so the spend still meets a gate1.0ms
user text inside an onclick handler · 2 tests
escapes for JavaScript, not just for HTML0.5ms
leaves no server-interpolated nqzSend argument using the HTML-only escaper4.3ms
shared-leads.vitest.ts
9/9 65ms · 1 suite PASS
src/routes/shared-leads.vitest.ts
shared-leads route boundary · 9 tests
requires the authenticated user input before it calls a service45.1ms
rejects malformed or unknown JSON fields strictly3.3ms
propagates vector degradation as a curated search response without raw source data4.9ms
turns source-rights denial into a 403, and unavailable retrieval into a 5032.4ms
writes a selection with the caller uid as a GraphQL variable, never interpolated1.4ms
scopes list ownership and the list-item mutation to the caller uid1.5ms
returns 404 when the requested list is not owned by the tenant1.1ms
queues identifier actions with tenant context and returns no private identifier payload2.4ms
requires the injected admin callback and rejects source-rights-prohibited ingestion3.1ms
degraded-banner.vitest.ts
9/9 14ms · 4 suites PASS
src/ui/degraded-banner.vitest.ts
the signal travels on a channel that survives the outage · 2 tests
rides /api/version — public, unauthenticated, and NO database2.5ms
the endpoint READS cached health, it does not probe per request0.8ms
absent means healthy — a bug here must not invent an outage · 2 tests
the server omits the field entirely unless degraded past the threshold0.7ms
a failed poll does not raise the bar0.3ms
the bar itself · 4 tests
says NOTHING IS LOST — the sentence that actually matters0.5ms
offers no button, because there is nothing the user can do0.4ms
clears itself when the dependency recovers0.4ms
is amber and wraps — impaired, not broken, and readable on a phone0.8ms
the deploy banner no longer kills the poll · 1 test
stopPolling is gone from showBanner7.1ms
free-plan-cap.vitest.ts
9/9 7ms · 2 suites PASS
src/tools/free-plan-cap.vitest.ts
the requested count survives validation · 4 tests
carries the number the user asked for3.0ms
defaults to 25 when the user named no number0.4ms
migrates a legacy `limit` rather than dropping it0.5ms
clamps an over-large ask to the fetch ceiling0.3ms
the free plan caps DELIVERY, on the number the user actually asked for · 5 tests
caps a free user at 10 however many they requested0.6ms
does not inflate a free user who asked for fewer than the cap0.3ms
gives a paid user the number they asked for0.3ms
the cap is visible: a free user asking for 50 is demonstrably narrowed0.4ms
the fetch floor is identical on both plans — the cap costs nothing to lift0.2ms
geo-schema.vitest.ts
9/9 13ms · 4 suites PASS
src/tools/geo-schema.vitest.ts
both tools are on the schema contract · 3 tests
seo_geo_research is schematised and dispatch-enforced4.2ms
seo_geo_research declares topic — the field this epic exists to add0.6ms
seo_geo_research rejects an off-schema field instead of ignoring it1.7ms
the deleted alias chains can no longer fire · 2 tests
seo_geo_research rejects the old keyword/query spellings0.7ms
requires the field the dispatch actually needs0.6ms
rag_readiness is RETIRED — the requirement moved, it did not vanish · 2 tests
is gone from every advertising surface, so it cannot creep back one at a time0.8ms
aeo_page_check still declares topic — THE requirement this epic existed for0.7ms
model-facing copy · 2 tests
separates topic research from brand measurement0.5ms
names no vendor and no USD price2.3ms
aeo-sample.vitest.ts
9/9 7ms · 2 suites PASS
src/seo/aeo-sample.vitest.ts
counting the run the way the estimator prices it · 4 tests
is silent before the picker resolves engines and prompts2.4ms
multiplies engines by prompts1.1ms
counts two models of one engine as two calls, because they are two calls0.4ms
drops exact duplicates and blanks rather than inflating the sample0.2ms
what it says, and what it refuses to say · 5 tests
says nothing about a run big enough to read0.5ms
warns about the TREND, never that the check is wrong0.6ms
pluralises, because "1 answers" reads as a bug and undermines the sentence0.3ms
fires right up to the threshold and stops exactly at it0.4ms
stays silent on a null sample rather than inventing a zero0.3ms
brand-answer-position.vitest.ts
9/9 5ms · 1 suite PASS
src/seo/brand-answer-position.vitest.ts
brandAnswerPosition · 9 tests
ranks by first mention, not by mention count2.6ms
reports #1 when we are named before every rival0.5ms
returns null when no rival was named — never a flattering #10.3ms
returns null when we are absent — never a default last place0.3ms
returns null on an empty answer rather than 00.2ms
matches a rival by bare name when the answer never writes the domain0.3ms
falls back to the domain when the brand name itself is absent0.3ms
is word-bounded, so a rival inside a longer word does not count0.4ms
is repeatable — the shared regexes carry no lastIndex between calls0.3ms
brief-kit-decision.vitest.ts
9/9 10ms · 3 suites PASS
src/seo/brief-kit-decision.vitest.ts
decisionFor — zero survivors · 4 tests
MIXED: leads with what was ruled out, and does not say STOP4.3ms
NOTHING TESTABLE: still stops, and still says why1.0ms
ALL RULED OUT: stops, and points outside the frame rather than at a next pull0.4ms
the ruled-out count is DERIVED, never a fourth number to keep in step0.5ms
decisionFor — the survivor paths are untouched · 2 tests
one survivor still names the act-first instruction0.3ms
onNone still receives the honest sentence for policy briefs0.3ms
decisionFor — the mixed line agrees with its own counts · 3 tests
uses singular agreement for exactly one ruled-out cause1.3ms
uses plural agreement for more than one0.4ms
writes "the other one" rather than "the other 1"0.8ms
brief-word-count.vitest.ts
9/9 9ms · 2 suites PASS
src/seo/brief-word-count.vitest.ts
the content brief is told the article length · 5 tests
seo_write_content forwards word_count when it auto-generates the brief3.2ms
the brief prompt renders the supplied number instead of asking the model to pick0.5ms
falls back to the model choosing when no word_count was supplied0.5ms
clamps to the same 400-3000 range the writer uses, so the two cannot disagree0.6ms
word_count is DECLARED on seo_content_brief, or the model can never pass it0.8ms
the length target is stated as a requirement, not just a slot · 4 tests
emits a LENGTH REQUIREMENT line naming the number when one was supplied0.4ms
forbids substituting an estimate, in as many words0.3ms
emits NOTHING when no target was supplied0.5ms
places the rule BEFORE the output template, not inside it0.6ms
entity-plumbing.vitest.ts
9/9 11ms · 3 suites PASS
src/seo/entity-plumbing.vitest.ts
a clean entity reading is an ANSWER · 2 tests
rules out every testable cause when the brand resolves and is anchored4.2ms
does not report a clean reading as untestable1.5ms
the findings it exists to produce · 4 tests
names an unresolved brand — the engines have nothing to cite0.6ms
names a missing structured anchor — resolved to Google, invisible to the rest0.5ms
names a namesake collision, and puts it FIRST — content cannot fix the graph0.5ms
compares rivals only when a rival set was actually read0.6ms
what it refuses to claim · 3 tests
never marks sameAs as checked — nothing reads the schema or the profiles1.2ms
says NEVER LOOKED, not "nothing found", when no entity audit exists0.7ms
carries a headline and a complete one-pager1.2ms
full-audit-synthesis.vitest.ts
9/9 7ms · 2 suites PASS
src/seo/full-audit-synthesis.vitest.ts
full_seo_audit runs no provider calls (spec FR-001) · 6 tests
never dispatches the two sub-audits it used to nest2.9ms
reads the six sources the spec names, not two0.6ms
takes no balance gate — nothing is spent, so nothing is reserved0.3ms
degrades to guidance rather than an error when nothing has been measured (FR-051)0.5ms
demotes findings from a stale source instead of dropping them (FR-003)0.3ms
names each absent source as a CONSEQUENCE, never as a table name0.5ms
the crawl summary is captured, not discarded (spec FR-030) · 3 tests
parses site-level checks from the poll we already pay for0.6ms
only reads them from the FINISHED poll — a partial crawl has partial metrics0.3ms
a missing probe stays null, never false — an unknown must not read as a failure0.3ms
gsc-page-moves.vitest.ts
9/9 16ms · 5 suites PASS
src/seo/gsc-page-moves.vitest.ts
comparePagePeriods · 3 tests
joins by page, absent-in-one-window counts as zero there, losers ranked by the fall3.7ms
site-wide direction as a rounded percentage0.6ms
a first window with no clicks has no percentage, not a fake 00.4ms
thin windows claim no direction (live 2026-09-15: 9 → 7 clicks read as -22%) · 1 test
is marked thin, yields no evidence point, and the note states the numbers without a percentage0.8ms
gscPointFromMoves · 1 test
is a Google-traffic point whose delta is a percent, tagged as such1.4ms
pageMovesNote · 2 tests
names the move, the windows and the pages that carried it, as paths0.5ms
says so when nothing fell0.6ms
wiring · 2 tests
the diagnose dispatch gathers the moves for a search/movement question and hands them to the brief2.6ms
the presenter and the card both render the losers table4.5ms
onpage-directives.vitest.ts
9/9 10ms · 3 suites PASS
src/seo/onpage-directives.vitest.ts
classifyOnPageIssue · 6 tests
calls broken-by-construction issues Corrective — no traffic data needed2.4ms
does NOT promote a numerous hygiene issue to Corrective — FR-0310.5ms
escalates a canonical oddity ONLY when Search Console confirms pages are missing0.4ms
files structural signals as Advisory0.5ms
never returns anything outside the vocabulary, including for unknown codes2.3ms
an unknown code degrades to Suggestive, never to Corrective0.3ms
stakeGap — the report says what it cannot rank by · 2 tests
raises the gap while no per-page performance is connected0.7ms
goes silent once the data exists — a resolved gap is not a finding0.3ms
ONPAGE_EMPTY_COPY · 1 test
gives every directive a real sentence — an empty group must state its absence (FR-030)0.8ms
rank-snapshot-attribution.vitest.ts
9/9 19ms · 3 suites PASS
src/seo/rank-snapshot-attribution.vitest.ts
rank_snapshots is scoped by property · 3 tests
every read of rank_snapshots filters on site10.1ms
the writer stamps the property it measured against0.3ms
the writer records NULL, never an empty string, when no site is resolved0.2ms
no retroactive attribution · 3 tests
the migration adds the column WITHOUT backfilling it0.6ms
the column is nullable, so unattributed history stays visibly unattributed0.3ms
no reader treats a NULL site as matching the current property0.6ms
one site resolver, not five · 3 tests
tenantSiteHost is exported from settings and is the shared predicate0.4ms
returns empty string for an unset site — callers must decline, not widen0.5ms
tool-dispatch no longer keeps its own private copy6.0ms
rivals-note.vitest.ts
9/9 6ms · 3 suites PASS
src/seo/rivals-note.vitest.ts
the note only speaks when the scan has run · 2 tests
is omitted when nothing has been scanned2.3ms
and it dates the measurement0.4ms
when someone DOES outrank us, it names them · 2 tests
lists the domains and how many keywords each takes0.4ms
and forbids calling the picture unknown0.5ms
when NOBODY outranks us, it forbids the sentence that was actually wrong · 5 tests
states the measured negative rather than staying silent0.4ms
forbids claiming a competitor took positions0.3ms
forbids offering to BUY the answer it already has0.4ms
and says where the evidence actually points0.3ms
reports the unknown-position pairs rather than hiding them0.4ms
prefilter-set-cap.vitest.mjs
9/9 160ms · 2 suites PASS
scripts/lib/prefilter-set-cap.vitest.mjs
createShardedHashSet — within-file dedup above the Set cap · 4 tests
behaves like a Set for add/has3.9ms
counts distinct keys, and re-adding one does not grow it4.1ms
SPREADS across every shard, which is the whole point — capacity is shards x the cap145.6ms
defaults to many shards rather than one1.5ms
seenStoreMerge — cross-file merge above the Set cap · 5 tests
merges, dedupes, and returns a BigUint64Array1.3ms
KEEPS THE OUTPUT SORTED — seenStoreFind does a binary search on it0.7ms
handles either side being empty, and duplicates within one side0.4ms
does not over-allocate — the returned array is trimmed, not a view on the scratch buffer0.3ms
NEVER builds an intermediate Set — the structural property that removes the cap0.6ms
free-tier-caps.vitest.ts
8/8 7ms · 3 suites PASS
src/billing/free-tier-caps.vitest.ts
free-tier cap constants · 3 tests
every cap is a positive integer — a 0 cap would be a wall, not a taste2.5ms
the AEO free tier still runs a real engine, not an empty set1.3ms
backlink prospects are capped BELOW the hard 12-domain ceiling, or the cap does nothing0.3ms
paid tools carry a cost estimate (the gate feeds off it) · 1 test
share_of_model and backlink_outreach_search are priced, so the gate can quote them0.4ms
cap provenance — the plan table is the whole free-tier policy · 4 tests
declares BOTH of seo_serp_spider's billed dimensions, not just the crawl0.3ms
lets the paid tier through to the full exact-verify pass0.3ms
never inflates a request that is already under the cap0.7ms
keeps every copy-only cap constant equal to the entitlement it quotes to the user0.5ms
plan-budget.vitest.ts
8/8 16ms · 2 suites PASS
src/billing/plan-budget.vitest.ts
checkAgainstApprovedPlan · 6 tests
allows an approved tool inside budget7.4ms
halts a tool the user never approved — the "model wants a 4th tool" case1.1ms
halts when an approved tool would breach the approved total0.5ms
never tells the user to approve again — a halt must not read like a second gate0.8ms
fails OPEN with no plan — ordinary single-tool turns must not be blocked0.6ms
allows spend exactly AT the approved ceiling1.6ms
planBudgetEnforced gate · 2 tests
defaults to observe-only — a misfiring rule must not block real work3.0ms
enforces only on an explicit on value0.4ms
plan-runtime.vitest.ts
8/8 12ms · 5 suites PASS
src/billing/plan-runtime.vitest.ts
plan-runtime records without changing the answer · 4 tests
returns exactly what the pure resolver returns5.0ms
records a trim, and stays silent when nothing was trimmed0.6ms
does not record a same-count substitution — there is no shortfall to offer0.7ms
records a multi-engine request narrowed to one0.5ms
takeCapEvents isolates runs · 1 test
clears, so a second run in the same turn starts clean2.0ms
cron trims are recorded but attributed to the cron · 1 test
namespaces the cron so it can never be mistaken for a tool0.4ms
ledger encoding · 1 test
round-trips into a compact marker the admin panel can parse0.4ms
defaultDepthFor resolves the plan ceiling for a number nobody asked for · 1 test
returns the plan depth and records NO cap event0.7ms
stripe-dispute.vitest.ts
8/8 66ms · 3 suites PASS
src/billing/stripe-dispute.vitest.ts
charge.dispute.created · 4 tests
reverses the disputed share as a POSITIVE row, pages a human, and tells the tenant55.0ms
reverses proportionally on a partial dispute2.1ms
does NOT double-debit when a refund already reversed the same charge1.8ms
ignores a sibling product's dispute without touching the ledger or paging1.0ms
charge.dispute.closed · 3 tests
restores the disputed share as a NEGATIVE row when won, keyed by dispute id, and tells the tenant2.0ms
writes nothing on a redelivered win0.9ms
changes nothing when lost — the reversal already happened at open0.4ms
the refund path tells the tenant too · 1 test
sends the balance-reversed notice after a refund reversal1.6ms
gsc-coverage.vitest.ts
8/8 10ms · 2 suites PASS
src/admin/gsc-coverage.vitest.ts
gsc-coverage · 5 tests
classifies indexed vs not-indexed from verdict/coverageState3.8ms
aggregates counts and groups not-indexed by reason, most first2.4ms
builds a fix prompt with a hint per reason0.9ms
all-indexed → no-fixes prompt0.7ms
unknown reason falls back to a generic hint0.5ms
resolveSitemapTotal · 3 tests
prefers total_urls_known (the real sitemap+seed union) over the smaller pre-merge seed length0.4ms
falls back to seed length only when total_urls_known is genuinely absent (degenerate empty case)0.6ms
does not silently regress if total_urls_known is ever smaller than the seed (still trusts the real union)0.4ms
gsc-scan-free.vitest.ts
8/8 125ms · 2 suites PASS
src/admin/gsc-scan-free.vitest.ts
probeIndexedFree — no paid path exists · 4 tests
returns a real verdict when Google answers3.1ms
reports unresolved — NOT not_indexed — when Google gives no usable verdict0.4ms
reports quota_blocked without spending, when the daily budget is gone0.8ms
reports quota_blocked rather than falling back when there is no Google connection at all0.7ms
GSC-only scan — pauses on quota, never skips unprobed URLs, never pays · 4 tests
completes a full free scan without ever calling the paid provider49.1ms
pauses when quota runs out mid-scan and parks the cursor at the unprobed URL2.8ms
resumes a paused scan from its cursor instead of restarting and re-spending a day of quota4.1ms
leaves the paid free-first path untouched when gsc_only is not requested64.0ms
indexer.vitest.ts
8/8 47ms · 3 suites PASS
src/admin/indexer.vitest.ts
cleanSubmitUrls · 2 tests
drops non-http, trims, and de-dupes (case/trailing-slash insensitive)3.4ms
caps the batch1.3ms
provider selection · 2 tests
defaults to omega and falls back on an unknown provider0.6ms
reports configured only when the key is present0.5ms
submitUrlsForIndexing · 4 tests
returns not-configured cleanly (no fetch) when the key is missing2.0ms
rejects an empty/invalid batch before calling the provider0.5ms
posts pipe-delimited urls + clamped dripfeed and reports success on "done"36.7ms
surfaces the provider error verbatim on rejection1.2ms
judge-model-authored-alert.vitest.ts
8/8 6ms · 2 suites PASS
src/admin/judge-model-authored-alert.vitest.ts
the model-authored judge alert · 6 tests
exists in the daily digest rather than as a new cron2.1ms
derives its population from PRESENTED_TOOLS, never a hand-kept list0.5ms
fires below 0.5 and not at or above it0.3ms
routes to Sentry LOGS, not Issues — it is a counter, not a fault0.4ms
carries the fields needed to act, not just a count0.4ms
splits a tool:leg operation so a leg row is attributed to its tool0.2ms
the population it actually covers today · 2 tests
seo_backlinks has LEFT the alert, because it now has a presenter1.6ms
still covers the tools that remain model-authored0.5ms
rca-judge-source.vitest.ts
8/8 10ms · 2 suites PASS
src/admin/rca-judge-source.vitest.ts
RCA evidence source (h) — low judge scores · 5 tests
is collected, tenant-tagged and capped like every other observation list3.2ms
carries the fields needed to cross-reference and to discount internal traffic1.9ms
selects on the documented ceiling rather than a bare literal0.4ms
is named in the prompt, with the count of sources updated to match0.7ms
tells the analyst to treat it as a lead and cross-reference it, not to believe it0.6ms
judge verdicts can be traced to the turn they judged · 3 tests
captures the turn id synchronously, before the judge detaches0.8ms
passes it at BOTH judge write sites — single and ensemble0.8ms
raises the reasoning cap off the 200-char literal that cut verdicts mid-word0.6ms
telemetry-tables-exact-overlay.vitest.ts
8/8 12ms · 2 suites PASS
src/admin/telemetry-tables-exact-overlay.vitest.ts
computeModelAndProviderTables — exact overlay end to end · 3 tests
replaces a sample-derived (approximate) total with the exact grouped total5.9ms
without exactApiGroups, behaves exactly as before (existing callers/tests unaffected)1.0ms
still excludes provider="quality" — the exact fetch filters it at the query level too0.7ms
overlayExactApiUsage · 5 tests
keeps actor and recent_runs from the sample — an aggregate cannot supply either1.6ms
recomputes the provider-level total as the SUM of its (now-exact) actor rows, not the old sample sum0.3ms
adds a synthetic entry for an exact group entirely absent from the sample, with actor null and no recent runs0.7ms
leaves a sample-derived group untouched if no exact data was fetched for it0.3ms
keeps providers sorted by cost descending after the overlay changes the ranking0.4ms
tenant-economics.vitest.ts
8/8 7ms · 1 suite PASS
src/admin/tenant-economics.vitest.ts
foldTenantEconomics · 8 tests
sums model and provider cost separately and together2.6ms
prices revenue from what was BOUGHT, not from usage — a trial is cost with zero revenue0.6ms
reads top-ups as a magnitude — they are stored as a negative credit0.4ms
leaves coverage null when cost is zero — that is not infinite margin0.3ms
buckets rows with no user_id as unattributed rather than dropping them1.4ms
sorts most expensive first — the page answers "who is costing us money"1.0ms
computes coverage as revenue over cost0.3ms
coerces string numerics from Hasura instead of concatenating them0.5ms
contact-page-scope.vitest.ts
8/8 12ms · 2 suites PASS
src/campaigns/contact-page-scope.vitest.ts
list_contacts answers describe themselves · 5 tests
every exit goes through the page wrapper5.4ms
states the denominator in prose on EVERY answer, not only when truncated1.4ms
puts the facts BEFORE the rows they describe1.0ms
marks an unscoped result as unscoped — for BOTH audiences, separately1.1ms
carries the verification arithmetic rather than leaving it to be inferred0.3ms
the schema tells the model what the payload means · 3 tests
requires the list filter when the user names a list1.0ms
says this is a page and that shown is not total1.1ms
points at the computed verification block instead of the raw column0.4ms
draft-count.vitest.ts
8/8 15ms · 2 suites PASS
src/campaigns/draft-count.vitest.ts
the schema can say how many · 4 tests
accepts a limit at all — this is the whole defect2.9ms
bounds it to the batch ceiling, so limit can never promise more than the tool delivers1.0ms
tells the model to pass it when the user names a number0.7ms
still refuses unknown properties, so a stray arg cannot ride along0.3ms
the answer says WHICH ceiling applied · 4 tests
restates the user's number when they capped it6.8ms
reports OUR ceiling when the user set none — this case said nothing at all before0.5ms
stays quiet when the ask was fully satisfied0.6ms
does not call the user's own remainder a deferral0.6ms
google-fetch.vitest.ts
8/8 47ms · 2 suites PASS
src/connectors/google-fetch.vitest.ts
googleFetch — Composio routing · 5 tests
routes a GSC search-analytics query to the GSC connection, verbatim39.9ms
routes a GA4 runReport to the GA4 connection0.9ms
returns 501 for Tag Manager (no Composio toolkit) without calling the proxy0.9ms
reports the connections only when composio_active is true0.4ms
native mode (flag off) passes through to fetch unchanged1.8ms
googleFetch — a proxy timeout is a counter, not a fault · 3 tests
answers 504 with a named code and does not throw1.0ms
a non-timeout transport failure still answers 502 — that path is unchanged0.8ms
the source routes TimeoutError to the counter before the Issue reporter1.0ms
failure-reason.vitest.ts
8/8 11ms · 3 suites PASS
src/email/failure-reason.vitest.ts
the column exists and is reachable · 2 tests
the migration is additive and nullable — an old failed row has no known reason3.3ms
the apply script grants the user role write access to it1.1ms
the reason is written, and cleared · 3 tests
updateEmailStatus carries the reason and bounds it0.5ms
a successful retry clears the previous failure's reason0.7ms
both failing send paths pass it0.5ms
the user can read it back · 3 tests
a failed-sends view leads with why, not with six empty columns2.6ms
the normal sent view is unchanged1.5ms
the row is selected from the database, not invented in the presenter0.5ms
product-update-copy.vitest.ts
8/8 8ms · 2 suites PASS
src/email/product-update-copy.vitest.ts
product announcement — scope · 2 tests
makes no cost, pricing or token claim4.2ms
does not claim anything got faster or cheaper without a measurement behind it0.7ms
product announcement — the promises it makes · 6 tests
leads with the ask-why capability and shows real prompts0.9ms
says the report still exists — users came for the artifact0.3ms
describes verification the way it actually behaves0.4ms
greets by name when there is one, and never with an email address0.4ms
carries a working dashboard CTA0.3ms
is registered so the admin console can list and preview it0.7ms
chip-price-honesty.vitest.ts
8/8 33ms · 3 suites PASS
src/chat/chip-price-honesty.vitest.ts
every route chip states the ceiling when there is one · 3 tests
NO chip advertises a number below what the gate will demand2.8ms
the two that measured wrong now carry both ends0.7ms
a tool with no ceiling above its typical still shows ONE number0.4ms
costRange is ONE rule with two renderings · 4 tests
compact is for chips, full prose is for the card0.8ms
collapses when there is no range, in both renderings0.4ms
a maxTokens BELOW tokens is not a range0.2ms
the gate message reads it too, so the two surfaces cannot drift1.9ms
no surface is left formatting a bare typical · 1 test
nothing renders TOOL_COST_ESTIMATE[...].tokens through fmtTokens directly24.4ms
history-persists.vitest.ts
8/8 21ms · 2 suites PASS
src/chat/history-persists.vitest.ts
the write is AWAITED, not deferred · 4 tests
no ctx.waitUntil on the history write2.8ms
the promise is returned so the caller can await it0.5ms
the same-isolate fast path is kept0.3ms
the caller still awaits it9.7ms
a lost write can no longer be silent · 4 tests
saveChatHistory reports instead of swallowing1.2ms
the reporter is INJECTED, not imported0.4ms
index wires the reporter and names the consequence4.7ms
reporting still cannot break the response0.5ms
history-trim.vitest.ts
8/8 9ms · 1 suite PASS
src/chat/history-trim.vitest.ts
trimPriorTurns · 8 tests
leaves the last KEEP_FULL messages untouched whatever their size3.2ms
still stubs an old rendered report — R6 is not undone0.8ms
gives an old USER message far more room than an assistant one at the same position0.4ms
preserves a typical ask verbatim, with no truncation marker0.4ms
marks a cut user message without claiming it was shown to the user0.6ms
bounds total preserved user text by the budget, newest first0.8ms
never leaves a user turn worse off than an assistant turn2.2ms
is a pure function — inputs are not mutated0.3ms
judge-delivery.vitest.ts
8/8 27ms · 3 suites PASS
src/chat/judge-delivery.vitest.ts
the delivery note fires on real non-delivery · 3 tests
a record tool ran and nothing reached the screen2.2ms
and NOT when the turn produced an artifact, a gate, or rows0.5ms
and NOT on an HONEST EMPTY — the defect this file already paid for once0.2ms
the clamp is reachable from BOTH judge branches · 3 tests
caps a nothing-delivered turn at 0.40.4ms
the REPORT branch applies it too — it did not, and that is the 1.00 row7.6ms
leaves a genuinely good turn alone0.4ms
the gate reads every tool the turn ran, not the last one · 2 tests
a four-tool turn is not judged on whichever finished last11.7ms
and the turn actually passes its tool list to the judge4.3ms
placeholder-leak.vitest.ts
8/8 24ms · 2 suites PASS
src/chat/placeholder-leak.vitest.ts
formatter placeholders · 5 tests
create_sequence names the step count and never a [list-name]11.7ms
seo_content_ideas points at the user's own top idea0.9ms
find_competitors names the competitor it just surfaced0.6ms
the empty branches instruct in plain words, not brackets0.9ms
no user-facing template in tool-format.ts ships a bracketed placeholder4.9ms
list_sequences enrolment counts · 3 tests
renders the counts the handler actually returns1.3ms
distinguishes zero active from zero enrolled0.5ms
the dispatch reads the fields handleListSequences returns2.2ms
rejected-tool-no-fabrication.vitest.ts
8/8 14ms · 3 suites PASS
src/chat/rejected-tool-no-fabrication.vitest.ts
the condition is structural, not a guess about the prose · 4 tests
fires only when a tool was REJECTED and NOT ONE ran2.5ms
reads the rejection ledger the validator already writes0.5ms
does not fire on an approval card — a question is not an answer0.3ms
does not fire on an empty response0.2ms
it DISCLOSES rather than blocks · 3 tests
appends to the answer instead of replacing it0.4ms
tells the user what the text IS, and what to do0.4ms
records the stand-down so it is countable, not just cosmetic0.3ms
the metadata that caught this stays wired · 1 test
hidden_failure and tool_call_count are still on the judge row8.6ms
render-manifest.vitest.ts
8/8 50ms · 2 suites PASS
src/chat/render-manifest.vitest.ts
it counts what was sent, not what was intended · 7 tests
a rendered-but-EMPTY table is visible as such3.1ms
counts rows across every block0.7ms
an approval card is recorded as a decision asked, not an answer given0.4ms
a prose-only turn shows no blocks and no chips — the picker shape0.5ms
an artifact is detected however it was attached0.4ms
records WHICH router produced the turn0.5ms
survives a malformed payload rather than throwing3.6ms
once per turn, whichever author gets there first · 1 test
a second call for the same turn does not write again40.9ms
silent-turn-text.vitest.ts
8/8 15ms · 1 suite PASS
src/chat/silent-turn-text.vitest.ts
resolveSilentTurnText · 8 tests
relays the tool error when every tool call errored (unchanged behavior)2.7ms
defaults to "Done." for an ordinary silent turn0.9ms
recovers a drafting ask that dead-ended on list_contacts1.5ms
does NOT use the DRAFTING recovery copy when the ask was not a drafting request7.7ms
renders the successful tool result instead of swallowing it as "Done."0.6ms
still says "Done." when there is no tool result to render0.5ms
does NOT recover when the last tool was not a read-only lookup0.5ms
an errored drafting turn still relays the error, not the recovery copy0.4ms
icp-cache.vitest.ts
8/8 29ms · 2 suites PASS
src/leads/icp-cache.vitest.ts
one extraction per brief · 6 tests
the second call for the same brief reads the stored profile and skips the model16.4ms
a changed brief misses by construction2.7ms
whitespace around the brief does not defeat the cache1.9ms
a thin brief never reaches the model or the store0.8ms
an empty extraction is not pinned — it could be an outage2.6ms
a malformed stored row reads as no cache0.5ms
the row is tenant data · 2 tests
is a declared setting and is erased with the account0.5ms
both producers go through the cache — the scan turn and define_icp3.3ms
local-business-locality.vitest.ts
8/8 11ms · 2 suites PASS
src/leads/local-business-locality.vitest.ts
local_business: postal_code and locality can coexist · 5 tests
accepts both and keeps locality — the regression: this used to be silently deleted5.9ms
still accepts postal_code alone (paired with country_code)0.8ms
still accepts locality alone0.7ms
still requires country_code alongside a bare postal_code0.7ms
still requires at least one of postal_code/locality0.5ms
resolveSearchLeadsNote: a bare zip discloses instead of silently broadening · 3 tests
discloses when postal_code was the only geography given0.7ms
says nothing about it when locality was also supplied0.5ms
outranks the free-tier cap note — even a delivered, capped batch matched the wrong geography0.3ms
one-paid-rung.vitest.ts
8/8 28ms · 2 suites PASS
src/leads/one-paid-rung.vitest.ts
the paid leg of leadSearch is DropLeads and nothing else · 4 tests
no retired rung remains in search.ts7.0ms
the paid leg calls the DropLeads rung, declares dropped filters, and reports a missing token as a fault1.8ms
the Product Hunt job is gone from every surface9.4ms
kept on purpose: the backlink harvester, the role-inbox rule, verifyEmail and the Apollo path0.6ms
the cost protocol sees the rung · 4 tests
the price is classified, reported, and quoted6.0ms
the paid quote is delivered x the enrich price — a miss is free, so that IS the ceiling1.0ms
the enrich row is billed on the charge treg made, with the fallbacks in order0.8ms
the admin cost roll-up files provider=dropleads under the lead search0.6ms
platform-profile-site.vitest.ts
8/8 12ms · 4 suites PASS
src/leads/platform-profile-site.vitest.ts
the URLs that actually caused it · 2 tests
the exact Facebook album URL from the incident3.3ms
the LinkedIn case from the same sweep0.7ms
subdomain-aware, never substring · 4 tests
a platform subdomain still counts0.4ms
a REAL business whose domain merely contains the word is scanned normally0.6ms
an ordinary business site is untouched0.6ms
bare domains and malformed input do not throw0.5ms
what is deliberately NOT listed · 1 test
publishing platforms where the page genuinely IS the product presence1.3ms
the guard is wired into the scan, before anything is saved · 1 test
returns PLATFORM_PROFILE and never reaches saveSiteOnly4.2ms
profile-shadowing.vitest.ts
8/8 12ms · 3 suites PASS
src/leads/profile-shadowing.vitest.ts
a name-only profile · 4 tests
does not shadow a rich brief8.4ms
still respects the character cap when the brief wins0.6ms
keeps the name when the brief says LESS — never downgrade0.6ms
keeps the name when there is no brief at all0.3ms
a real profile still wins — the preference is intact · 2 tests
prefers the structured profile over an equally rich brief0.6ms
prefers the profile even when a rambling brief is longer0.3ms
no profile at all · 2 tests
falls back to the brief, unchanged behaviour0.2ms
returns empty rather than throwing when both are missing0.2ms
scan-nothing-learned.vitest.ts
8/8 52ms · 2 suites PASS
src/leads/scan-nothing-learned.vitest.ts
a page that declares nothing · 5 tests
is below the substance floor — the premise of every case below2.7ms
never calls the model40.7ms
writes no product brief1.4ms
still saves the site — the domain resolved and site-scoped tools need a subject1.3ms
says what happened, and flags the source as thin1.1ms
any one real signal keeps the normal path · 3 tests
a declared brand is enough — the model still runs2.0ms
the user's own description is enough, even on an empty page0.7ms
real page copy is enough0.7ms
jev-adoptions.vitest.ts
8/8 72ms · 3 suites PASS
src/llm/jev-adoptions.vitest.ts
audience_fit: Jev decides, the writer only writes · 3 tests
a fit verdict never reaches the writer6.5ms
a mismatch verdict hands the sentence to the writer, whose own verdict stands1.3ms
a Jev failure, or the flag off, is the old path3.1ms
page scores: five levels → 0–100 · 4 tests
maps the score position onto the scale the findings read, using the legend size0.9ms
every axis is a five-level score question, levels concrete and ordered low → high5.3ms
jevPageScores throws when any axis comes back unscored — a half-scored page is not a scorecard50.7ms
the notes sentence names the weakest axis0.8ms
wiring · 1 test
both scorecards try Jev first and keep the writer as the fallback; the rollout names all four contexts3.3ms
router-reasoning-default.vitest.ts
8/8 22ms · 2 suites PASS
src/llm/router-reasoning-default.vitest.ts
callOpenRouterFull disables reasoning unless asked · 6 tests
sends reasoning:{enabled:false} for a caller that passes NOTHING10.0ms
sends it for a prose caller with a ceiling and no failOnTruncation2.0ms
still sends it for a failOnTruncation caller — unchanged from v2.496.11.7ms
LETS A CALLER OPT BACK IN with reasoning:{enabled:true}1.0ms
still honours an explicit effort level1.0ms
an opted-in caller is NOT translated to effort:minimal on a mandatory-reasoning model0.7ms
the agent loop is OUTSIDE this default, on purpose · 2 tests
callOpenRouterTools sends NO reasoning key when the caller sets none3.2ms
callOpenRouterTools still forwards a reasoning option it is given0.9ms
router-reasoning-models.vitest.ts
8/8 19ms · 2 suites PASS
src/llm/router-reasoning-models.vitest.ts
reasoning is disabled in the form each endpoint accepts · 6 tests
sends effort:minimal to gpt-5-mini, which rejects enabled:false outright7.2ms
does NOT downgrade a gpt-5 sibling that accepts enabled:false1.1ms
still sends enabled:false to models that accept it1.2ms
translates a CALLER-supplied enabled:false too — same choice, spelled for the endpoint0.9ms
leaves a caller reasoning that is not "off" alone0.9ms
keeps the copy chain on its first model instead of demoting the draft1.4ms
a model that refuses enabled:false is retried, not dropped · 2 tests
retries the SAME model with effort:minimal on the 400 that says so3.9ms
does NOT retry an unrelated 400 — that one is a real failover2.4ms
router-tools-reasoning.vitest.ts
8/8 16ms · 3 suites PASS
src/llm/router-tools-reasoning.vitest.ts
callOpenRouterTools is outside the reasoning default · 3 tests
sends NO reasoning key at all when the caller sets none6.0ms
forwards a caller-supplied effort verbatim — the live CoT path2.1ms
does NOT apply the REASONING_MANDATORY rewrite — that helper is scoped to callOpenRouterFull1.2ms
callOpenRouterTools labels its ledger rows · 2 tests
defaults to chat:v2, so existing callers and existing history keep their meaning1.3ms
writes a caller-supplied context instead0.7ms
agent-loop backend preference · 3 tests
sends provider.order StreamLake with fallbacks ON0.7ms
keeps fallbacks enabled — a hard pin makes one backend outage a failed turn1.3ms
does NOT pin callOpenRouterFull — single-shot callers were not measured2.0ms
bet-count-channel.vitest.ts
8/8 11ms · 2 suites PASS
src/planner/bet-count-channel.vitest.ts
the validator still distinguishes its three outcomes · 4 tests
a below-range count is reported as such3.2ms
a padded bet is a different message — and it is OUR bug0.5ms
zero bets on a plan with initiatives is also ours0.3ms
and a healthy plan still passes1.1ms
only the below-range case leaves the fault channel · 4 tests
a below-range count goes to Logs as a counter1.7ms
the other two still reach reportError1.8ms
the branch is chosen by the MESSAGE, which is the validator's own output1.0ms
the plan is still built either way — this was never user-facing1.2ms
middleware.vitest.ts
8/8 7ms · 1 suite PASS
src/middleware/middleware.vitest.ts
middleware — dispatchToolCallFromText · 8 tests
dispatches a JSON tool call2.8ms
falls back to an XML tool call0.9ms
emits structured next actions0.5ms
bypasses special-tool execution0.4ms
rejects an unknown tool before execution0.4ms
returns a top-up before execution0.5ms
ignores plain text0.4ms
survives formatter failures on a successful tool run0.6ms
share-of-model.vitest.ts
8/8 11ms · 2 suites PASS
src/middleware/share-of-model.vitest.ts
registrableCore · 2 tests
returns the SLD, ignoring subdomains4.6ms
handles two-part TLDs1.0ms
isOurs — phantom-citation guard · 6 tests
does NOT match a generic brand token inside an unrelated subdomain0.8ms
still matches our own domain and subdomains of it0.8ms
matches a distinctive brand token in another registrable domain (real alias hit)0.6ms
does not match a distinctive token buried in a subdomain of a rival0.6ms
short (3-4 char) brands match the core label, not arbitrary subdomains0.6ms
empty/garbage inputs are safe0.3ms
aeo-rivals-render.vitest.ts
8/8 8ms · 2 suites PASS
src/reports/aeo-rivals-render.vitest.ts
R-D acceptance — durability beats volume in the rendered artifact · 6 tests
the domain three engines agree on outranks the one with equal volume on one engine2.7ms
names the PAGE, its rank and its shape — a hostname is not actionable0.5ms
states the ordering rule so the reader can disagree with it0.3ms
shows what KIND of page wins, excluding our own0.4ms
groups the work by who has to do it, biggest group first (AEO-009)0.4ms
the next step is an outreach brief when the market sits on other people pages0.9ms
R-D — a run stored before source capture says so (trap 14) · 2 tests
falls back to the old chips and explains WHY the detail is missing1.3ms
a captured run with genuinely no rivals says THAT instead1.3ms
contracts.property.vitest.ts
8/8 1049ms · 2 suites PASS
src/reports/contracts.property.vitest.ts
forensic contracts — totality under adversarial fuzz · 2 tests
no predicate throws across 2000 random hostile results × every contracted type1037.4ms
prose-only types always return no violations regardless of input9.6ms
forensic contracts — nasty-tenant fixtures · 6 tests
phantom SOV: 80% coverage with zero real citations (the isOurs subdomain collision)0.6ms
single-competitor thin denominator is still bounded (no crash, no false pass on OOB)0.3ms
all-error engine legs → empty leaderboard, zero coverage → no false phantom flag0.3ms
a leg that ERRORED is not a soundness violation — it is a reported absence0.3ms
campaign with more opens than sends (tracking double-count) is caught0.2ms
onpage score computed out of [0,100] is caught0.2ms
outbound-report.vitest.ts
8/8 8ms · 4 suites PASS
src/reports/outbound-report.vitest.ts
generate_emails — draft artifact persists (regression) · 2 tests
returns a real artifact (not null) with the drafts rendered3.4ms
returns null (plain-text path) for a zero-draft or error result — no empty artifact0.5ms
search_leads — §17 gold standard · 2 tests
renders a batch-quality bento (verified rate, leads, sources) + a draft-emails action0.7ms
keeps the contact preview, plain headers, feedback mount0.5ms
campaign_stats/dashboard — §17 gold standard · 2 tests
renders a performance bento (open/reply/audience/state), no fix buttons (advice in copy)0.5ms
plain headers + feedback mount0.4ms
domain_email_readiness_audit — §17 gold standard · 2 tests
renders a deliverability bento (issues/auto-fixable/blacklist) with a one-click fix action0.5ms
keeps the per-issue drill-down, plain headers, feedback mount0.4ms
playbook-tail.vitest.ts
8/8 7ms · 3 suites PASS
src/reports/playbook-tail.vitest.ts
a playbook answer drops the site-diagnostic tail · 5 tests
the playbook itself is fully rendered2.4ms
drops the answerProse block1.2ms
drops the movement block0.2ms
drops the keywords block0.2ms
drops the outbound and traffic prose specifically0.3ms
a BRIEF keeps the tail — its verdicts rest on those readings · 2 tests
renders the brief0.4ms
still carries the diagnostic prose the verdicts were read from0.2ms
provenance survives the trim — a shorter answer must not be a less honest one · 1 test
the reading dates and the never-measured list are kept on a playbook0.7ms
render-integrity.vitest.ts
8/8 63ms · 2 suites PASS
src/reports/render-integrity.vitest.ts
assertReportRenderSound · 7 tests
passes a clean report3.4ms
flags an empty table body (the blank citation-matrix defect)1.8ms
flags [object Object]0.6ms
flags undefined / NaN leaking into a rendered value1.2ms
does NOT flag the words in legitimate prose0.5ms
collects multiple distinct defects0.8ms
is safe on empty/garbage input0.6ms
aeo_visibility citation matrix — zero test prompts (Sentry NQZAI-5G) · 1 test
renders an explicit empty state instead of an empty tbody53.8ms
guardrail-user-money.vitest.ts
8/8 21ms · 4 suites PASS
src/runtime/guardrail-user-money.vitest.ts
the two live failures · 2 tests
a liability figure the scan read off the tenant OWN site survives6.2ms
an ICP band typed an EARLIER turn survives on later turns3.1ms
what must STILL be redacted — the rule this protects · 3 tests
our own price is not theirs, even inside a rich corpus0.8ms
a reply mixing both keeps theirs and hides ours0.8ms
with NO corpus at all, everything is still redacted0.4ms
matching stays formatting-insensitive, not fuzzy · 2 tests
case and spacing are formatting, not a different figure0.4ms
a figure the tenant never wrote is NOT rescued by a near miss0.4ms
the corpus is built from what the turn already holds · 1 test
every user turn, the brief and the profile — and it is never rendered6.7ms
advisory-routing.vitest.ts
8/8 7ms · 4 suites PASS
src/tools/advisory-routing.vitest.ts
the prohibition is gone · 3 tests
no longer declares open questions to be non-plan asks2.7ms
names the question classes that must reach the planner0.7ms
the WHY is recorded in the FILE, not spent on every turn0.6ms
the split is advisory vs imperative, not a keyword list · 2 tests
imperatives keep going straight to their tool0.5ms
the doubt case prefers the planner, and says why0.4ms
it does not contradict rule (5) · 2 tests
a single-scope audit routes to a tool the model CAN call0.5ms
THREE OR MORE dimensions is a brief — the same counting rule the AEO picker uses0.4ms
the planner is still barred from paid fan-outs · 1 test
neither planner tool may route to a paid fan-out0.3ms
arg-key-shape.vitest.ts
8/8 9ms · 3 suites PASS
src/tools/arg-key-shape.vitest.ts
the two turns that died today · 2 tests
generate_emails accepts contactIds as contact_ids4.6ms
seo_keywords accepts query as its seed topic0.4ms
a SHAPE transform, not a vocabulary · 4 tests
handles the conventions a model actually emits0.9ms
needs no list to maintain — it only re-spells onto a DECLARED field0.5ms
never overwrites a value the model already put in the right field0.3ms
leaves dispatch metadata alone0.9ms
what it must NOT do · 2 tests
a bad VALUE is still a rejection — this fixes keys, not contents0.4ms
search_leads keeps its own query -> topic migration0.5ms
enroll-sequence-identifier.vitest.ts
8/8 10ms · 2 suites PASS
src/tools/enroll-sequence-identifier.vitest.ts
enroll_in_sequence accepts either identifier · 6 tests
accepts sequence_id — the exact shape that was rejected 4 times5.5ms
still accepts sequence_name0.4ms
accepts both together0.3ms
lets a call with NEITHER identifier through the schema on purpose0.3ms
still rejects a malformed sequence_id rather than passing it to the DB0.3ms
still rejects an invented argument — additionalProperties:false is intact0.3ms
the vocabulary the model is handed matches what the tool accepts · 2 tests
list_sequences returns an id, so the enrol schema must accept one1.3ms
resolves an id and a name with SEPARATE user-scoped queries, never one _or0.6ms
lead-routing.vitest.ts
8/8 6ms · 3 suites PASS
src/tools/lead-routing.vitest.ts
list_contacts says what it is NOT for · 3 tests
states it cannot find anyone new2.5ms
names search_leads as the tool for finding people0.6ms
tells the model to prefer the paid tool when the ask is to find0.3ms
search_leads claims the find intent · 2 tests
names the trigger verbs and the NEW/FRESH qualifiers0.4ms
points away from list_contacts explicitly0.3ms
a contact-list quality check is verification, not enrichment · 3 tests
verify_contacts claims the quality-check phrasing0.4ms
enrich_contacts sends the question away rather than absorbing it0.5ms
each tool names the other as the wrong choice for the other job0.4ms
producer-contract.vitest.ts
8/8 10ms · 3 suites PASS
src/tools/producer-contract.vitest.ts
skills that call a schematised tool build valid arguments · 3 tests
finds the skill steps to check (a silent zero here would prove nothing)2.6ms
quick_list_build → search_leads1.7ms
cold_launch → search_leads0.5ms
the clarify gate reads the arguments the schema actually produces · 2 tests
asks when the request names nothing to target on2.0ms
does not interrogate a well-specified request0.3ms
legacy `query` producers keep working through the migration shim · 3 tests
maps a legacy query onto topic instead of rejecting the call0.9ms
never lets a legacy query become a provider filter0.5ms
the chat-shortcut adapter produces the same shape0.8ms
retry-directive.vitest.ts
8/8 13ms · 2 suites PASS
src/tools/retry-directive.vitest.ts
retryDirective · 7 tests
tells the model to retry from the vocabulary, and not to answer yet3.1ms
treats an unknown ARGUMENT as a rename, not a bad value1.1ms
pluralises and de-duplicates when several argument names are wrong0.3ms
points at `accepted` when lexical matching did find candidates0.4ms
prefers the vocabulary directive when both are present0.2ms
names each affected field once0.2ms
stays silent when there is nothing to retry FROM0.4ms
the live 2026-08-23 shape produces a directive · 1 test
a real unmatched industry rejects WITH a vocabulary to retry from6.1ms
dfs-cost.vitest.ts
8/8 6ms · 2 suites PASS
src/seo/dfs-cost.vitest.ts
readCost · 4 tests
reads a reported charge2.8ms
keeps a genuine zero — free and cached endpoints really do charge nothing0.4ms
returns null when the field is absent, so the caller falls back to the constant0.5ms
rejects non-finite and non-numeric values rather than billing NaN0.4ms
multi-call cost aggregation · 4 tests
sums when every call reported a cost0.5ms
reports NULL when ANY call went unmeasured — never a partial sum0.3ms
treats an all-zero run as measured zero, not unmeasured0.4ms
is zero for a run that made no calls0.3ms
gtm-scope-removal.vitest.ts
8/8 8ms · 1 suite PASS
src/seo/gtm-scope-removal.vitest.ts
dropping the Tag Manager scope · 8 tests
is not requested at consent, and the two that remain are2.4ms
an absent Tag Manager leaves the DENOMINATOR, never scores 0 against weight 100.6ms
and its absence does not make every score provisional either0.3ms
a connected container with zero tags is still a real 0.6 finding0.2ms
never tells the user to reconnect Google to regain it — that is what removes it0.5ms
the god-mode capability no longer promises a check we do not request0.3ms
the public scope disclosures match what we actually request2.3ms
the CONNECT CARD does not promise a Tag Manager audit0.7ms
keyword-router-free-rung.vitest.ts
8/8 15ms · 2 suites PASS
src/seo/keyword-router-free-rung.vitest.ts
the free rung runs even when spend is denied · 3 tests
resolves volumes with allowPaid:false, and never calls the paid rung6.0ms
runs BEFORE the paid rung, not after it0.7ms
leaves the paid rung exactly what Google could not answer2.0ms
what the free rung refuses to claim · 5 tests
does not resolve a keyword Google returned with no volume1.3ms
never sets cpc0.8ms
ignores the adjacent ideas Google volunteers for a keyword seed0.6ms
chunks past Google's 20-seed cap instead of dropping the 21st keyword2.5ms
a failing free rung still lets the paid rung run0.7ms
keyword-site-attribution.vitest.ts
8/8 14ms · 3 suites PASS
src/seo/keyword-site-attribution.vitest.ts
upsertGscPerformance — the writer records which property the pull came from · 4 tests
writes the normalised host and the raw property onto every behavioural row6.0ms
normalises a URL-prefix property to the same host as its domain property1.2ms
never writes behaviour that carries no property0.4ms
registers the keyword without clobbering its volume/CPC side2.1ms
fetchSitePerformance — the reader sees one property and no legacy rows · 3 tests
returns only the requested property, not the tenant-wide merge1.1ms
scopes the query by site and never falls back to tracked_keywords behaviour0.6ms
returns nothing rather than guessing when no site is given0.4ms
the volume ladder stays per-keyword, not per-property · 1 test
reads the volume cache WITHOUT a site filter, so a second property costs nothing1.9ms
link-health.vitest.ts
8/8 9ms · 1 suite PASS
src/seo/link-health.vitest.ts
computeLinkHealth · 8 tests
counts dead targets and groups them by the page, not the link4.9ms
treats a redirect as alive — the link still lands somewhere0.3ms
excludes rows with no status instead of assuming they are healthy0.4ms
says "unknown, not healthy" when nothing carried a status0.4ms
honours broken:true even when the status is missing, without inventing one0.3ms
lets an explicit status win over a bare broken flag for the same target0.5ms
never claims first-hand verification — the source rides with the result0.9ms
handles an empty profile without dividing by zero1.0ms
onpage-sweep.vitest.ts
8/8 12ms · 1 suite PASS
src/seo/onpage-sweep.vitest.ts
sweepAbandonedOnpageCrawls · 8 tests
delivers a finished crawl against its ORIGINAL job row5.5ms
the fetch is what records the spend — the sweep never bills separately1.0ms
leaves a still-running crawl alone0.6ms
skips a young crawl — the inline poll still owns it0.5ms
records unrecovered spend when the owning job cannot be identified1.0ms
a dead task is recorded and dropped, never re-fetched forever1.3ms
one broken marker does not stop the next tenant being delivered1.0ms
no-ops without the provider credentials or a session store0.4ms
write-content-length.vitest.ts
8/8 53ms · 3 suites PASS
src/seo/write-content-length.vitest.ts
the article prompt states its length as a requirement · 3 tests
names a hard floor at 90% of the target instead of a ~approximate hint38.2ms
scales the floor with the caller-supplied target1.9ms
tells the model the requirement has a consequence and how to plan for it2.1ms
the generation ceiling can physically hold the article it demands · 2 tests
leaves the default request at exactly the historic 3500 tokens1.5ms
raises the ceiling for a long article the caller explicitly asked for1.9ms
an article that still lands short says so · 3 tests
discloses the shortfall with both numbers2.2ms
stays quiet when the article met the floor2.7ms
defers to truncation_note when the draft was cut off2.0ms
chunk-subject-index.vitest.ts
8/8 5ms · 3 suites PASS
src/leads/shared/chunk-subject-index.vitest.ts
160 · the index exists and covers the join · 4 tests
indexes subject_id2.0ms
carries chunk_id so the chunk_embedding join stays index-only0.4ms
is NOT partial on subject_type0.7ms
is idempotent, because the index was built live before the migration was written0.3ms
160 · the self-check proves the index is USED, not merely present · 2 tests
collects every plan line, not just the first0.4ms
fails on a Seq Scan and on the index not being chosen0.3ms
160 · the reader this was built for still looks up by subject_id · 2 tests
the embed candidate query still anti-joins document_chunk on subject_id0.4ms
and still joins chunk_embedding by chunk_id, which is why chunk_id is in the index0.2ms
google_analytics_helpers.vitest.ts
7/7 22ms · 1 suite PASS
src/google_analytics_helpers.vitest.ts
google_analytics_helpers · 7 tests
builds the metadata cache key2.2ms
compatibility cache key ignores input ordering0.7ms
normalizes join paths15.4ms
computes metric deltas0.7ms
attaches row deltas1.0ms
summarizes merge snapshots without prior0.9ms
summarizes merge snapshots with prior1.0ms
mixpanel.vitest.ts
7/7 56ms · 2 suites PASS
src/mixpanel.vitest.ts
mixpanel request geo · 4 tests
binds request.cf geo for the async chain and is empty outside it41.4ms
stamps mp_country_code/$city/$region onto tracked events7.3ms
events fired outside a request carry no geo (and still send)1.2ms
people $set carries $country_code/$city and never a real $ip1.5ms
mixpanel Unicode-safe payload encoding (NQZAI-4A) · 3 tests
setServerMixpanelPeople round-trips a non-Latin1 $name/$email without throwing1.2ms
captureServerMixpanelEvent round-trips a non-Latin1 error_message (judge/feedback path)1.2ms
sanity: the original btoa(JSON.stringify(...)) path would have thrown on this input1.7ms
free-tier-aeo.vitest.ts
7/7 8ms · 2 suites PASS
src/billing/free-tier-aeo.vitest.ts
the free entitlement is affordable on the signup grant · 3 tests
caps 16 prompts to 3 and 4 engines to chatgpt3.0ms
the capped run clears the affordability gate; the uncapped one never could0.6ms
and leaves the user enough to do something else afterwards0.3ms
the picker and the card are capped, not just the dispatch · 4 tests
the quote prices the plan depth, not the raw selection1.5ms
what gets DISPATCHED is what was priced0.3ms
the narrowing is DISCLOSED, never silent1.0ms
a PAID plan is not narrowed1.1ms
run-cap-honesty.vitest.ts
7/7 13ms · 2 suites PASS
src/billing/run-cap-honesty.vitest.ts
a refusal states the entitlement that bound · 5 tests
names the limit and the tool instead of "your current plan"3.4ms
says a LIFETIME cap does not reset — the fact that decides what to do next0.7ms
says WHEN a windowed cap resets, rather than only offering money0.4ms
still promises a top-up ONLY because the paid plan really has no run cap1.2ms
degrades to a true sentence when the caller passes no entitlement0.4ms
the refusal is not printed twice · 2 tests
suppresses the append when the body already IS the offer5.6ms
still appends for a tool that DELIVERED something and had the rest trimmed0.6ms
telemetry-pagination.vitest.ts
7/7 15ms · 1 suite PASS
src/admin/telemetry-pagination.vitest.ts
drainList · 7 tests
does not query at all when the first page came back short — the only path taken at current volume5.6ms
keeps paging while pages come back full, and stops on the first short page1.8ms
advances the offset by a full page each time rather than re-reading page one2.2ms
stops at the ceiling instead of looping forever on an endlessly-full list1.6ms
keeps the rows already collected when a later page fails, rather than throwing the lot away1.8ms
leaves an unknown list untouched rather than guessing a query for it0.7ms
passes the caller’s window through, so a continuation cannot widen the range0.8ms
telemetry-payload.vitest.ts
7/7 18ms · 1 suite PASS
src/admin/telemetry-payload.vitest.ts
trimClientPayload · 7 tests
drops the arrays with no client reader7.2ms
trims the two arrays the client DOES read to a display window4.3ms
keeps the NEWEST rows — the arrays arrive created_at desc and both readers show recent items1.7ms
leaves short arrays alone1.1ms
declares the window it applied, so the client cannot mistake it for the working set0.5ms
does not touch the computed aggregates it sits beside1.5ms
survives a payload missing those keys entirely1.7ms
testomat-freshness.vitest.ts
7/7 11ms · 1 suite PASS
src/admin/testomat-freshness.vitest.ts
computeStalenessBreaches · 7 tests
flags a behavior suite (SLA 8d) run 10 days ago, passes one run 3 days ago5.6ms
uses the newest run in a suite, not the oldest0.4ms
suite 17 has a tight 2-day SLA (posted daily by the judge cron)0.6ms
suites 13-16 and 18-24 breach like any other: they have eval rows and ride the weekly rotation (2026-09-15)3.5ms
a suite that has NEVER run is a breach (age null)0.4ms
rows without a bracketed suite code are ignored0.3ms
all-fresh catalog yields no breaches0.3ms
draft-grounding.vitest.ts
7/7 8ms · 2 suites PASS
src/campaigns/draft-grounding.vitest.ts
generate_emails is told what it does NOT know · 5 tests
states that a company name is not knowledge of the company2.5ms
names the only recipient facts that exist, so "invent nothing" is actionable0.6ms
closes the specific extrapolations that were observed0.8ms
offers the honest fallback instead of only prohibiting0.3ms
grounds claims about OUR product in the sender context too0.5ms
both drafting prompts carry a grounding rule · 2 tests
the backlink path still has the one it always had0.3ms
neither prompt block is empty — the slices still find their anchors0.5ms
generate-emails-zero.vitest.ts
7/7 7ms · 2 suites PASS
src/campaigns/generate-emails-zero.vitest.ts
generate_emails zero outcomes are distinguishable · 4 tests
no contacts resolved → a targeting problem, not a drafting one2.8ms
contacts resolved but the writer produced nothing → OUR failure, must not read as empty success0.6ms
drafts produced but none saved → the existing dropped-contacts warning, not a writer failure0.4ms
partial save is still a success with a shortfall, not a zero0.5ms
zero-draft message · 3 tests
states the drafting step failed and explicitly clears the list0.7ms
never tells the user to add or fix contacts0.4ms
reads correctly for a single contact0.8ms
merchandising.vitest.ts
7/7 12ms · 2 suites PASS
src/commerce/merchandising.vitest.ts
computeCoPurchase · 4 tests
counts a pair once per order and gates below MIN_PAIR_ORDERS6.4ms
too few multi-item orders → insufficient with an honest note, no fabricated pairs1.8ms
excluded (cancelled/test) orders contribute nothing0.5ms
constants sanity0.5ms
rankPromotable · 3 tests
ranks by margin per day — throughput beats rate1.0ms
no recorded cost → unrankable, never guessed into the ranking0.6ms
window floor prevents division blowups0.3ms
gmail-send.vitest.ts
7/7 11ms · 2 suites PASS
src/email/gmail-send.vitest.ts
Gmail send transport · 4 tests
posts to the Gmail API with a bearer token and a base64url-encoded RFC2822 message5.8ms
strips header-injection attempts from subject/to/reply-to1.2ms
classifies a 403 dailyLimitExceeded distinctly from a generic failure1.0ms
classifies a 401/invalid_grant as a reconnect prompt, not a generic failure0.9ms
Gmail daily send cap · 3 tests
fails open when CHAT_HISTORY is unbound (matches every other rate limit in this codebase)0.5ms
blocks once the default cap is reached and reports the correct limit1.2ms
resets in a new day bucket0.4ms
link-guard.vitest.ts
7/7 5ms · 1 suite PASS
src/email/link-guard.vitest.ts
stripUnapprovedLinks · 7 tests
removes the exact hallucinated booking link from the incident2.8ms
keeps a link we actually supplied0.5ms
keeps the tenant product URL and drops an invented one in the same body0.5ms
strips every URL when nothing is approved0.4ms
tidies the dangling punctuation the removed link left behind0.3ms
leaves a link-free body untouched0.4ms
is not fooled by trailing punctuation on an approved link0.2ms
recipient.vitest.ts
7/7 7ms · 2 suites PASS
src/email/recipient.vitest.ts
extractRecipientAddress · 5 tests
finds the address in the prompts that mis-targeted2.8ms
returns '' for the send-my-drafts phrasings that must keep falling back1.0ms
lowercases so the refusal echoes a canonical address0.3ms
takes the first address when several are named0.2ms
handles plus-addressing and dotted local parts0.2ms
adHocRecipientRefusal · 2 tests
names the address and states nothing was queued0.5ms
never implies emails were sent0.5ms
send-ceiling-parity.vitest.ts
7/7 5ms · 1 suite PASS
src/email/send-ceiling-parity.vitest.ts
send paths share one hourly ceiling · 7 tests
campaign tool (the original) calls checkSendRateLimit2.7ms
REST bulk send (handleEmailSend) calls checkSendRateLimit0.4ms
single draft send (handleSendSingleEmail) calls checkSendRateLimit0.6ms
REST bulk send sizes the ceiling from effectiveSendLimits, not a local constant0.3ms
single draft send sizes the ceiling from effectiveSendLimits, not a local constant0.5ms
the single-send path bills platform_send0.5ms
the single-send path opens a billing window so the fee lands on this user0.5ms
brief-without-site.vitest.ts
7/7 4ms · 3 suites PASS
src/chat/brief-without-site.vitest.ts
briefStatusText: brief with a site · 2 tests
still tells the model not to re-ask — the original behaviour is untouched1.9ms
gates that instruction on a site actually being known0.3ms
briefStatusText: brief WITHOUT a site · 4 tests
says the site is missing rather than implying everything is set up0.2ms
tells the model to proceed with outbound instead of asking for a domain0.4ms
still allows the ask when a site-scoped tool is the actual request0.2ms
forbids it as a precondition0.2ms
the premise that made this reachable · 1 test
the chat shortcut writes a brief without touching __site_url__0.7ms
call-legs.vitest.ts
7/7 12ms · 2 suites PASS
src/chat/call-legs.vitest.ts
promptLegs classifies a wire prompt by leg · 4 tests
first system is the system prompt, later systems are context, last user is the message3.1ms
tool results and this turn's own tool-call message are counted where they belong0.8ms
never throws on odd content1.9ms
the datapoint order is fixed and documented1.1ms
every agent-loop call site names its reason · 3 tests
first / first_cot / tool_round on the main call, and the three follow-ups1.2ms
the router writes the reason to the ledger row and the legs to the metric2.4ms
the reader exists1.4ms
competitor-pick.vitest.ts
7/7 14ms · 3 suites PASS
src/chat/competitor-pick.vitest.ts
isCompetitorGapAsk · 2 tests
recognises the shapes the gap tools answer3.2ms
leaves everything else alone0.7ms
competitorPickBackstop · 3 tests
the live [8.2.1] turn: no tool, own site named, four+ saved → four keyword chips1.7ms
a backlink ask gets the backlink chips0.4ms
stands down when a tool ran, chips exist, fewer than two are saved, or a rival is named0.4ms
the chips are the tools' own strings · 2 tests
matches seo/tool-dispatch.ts verbatim2.6ms
is wired at the end of runChatV2 on no-tool turns5.1ms
diagnose-question-bound.vitest.ts
7/7 9ms · 2 suites PASS
src/chat/diagnose-question-bound.vitest.ts
the bound itself · 4 tests
matches the maxLength the schema actually declares4.2ms
truncates over-long input and leaves short input untouched1.3ms
never returns a non-string, whatever it is handed0.4ms
the real 710-character message that broke it now fits0.3ms
BOTH producers use the shared bound — neither carries its own literal · 3 tests
the answer-shape shortcut bounds the message0.8ms
the misroute redirect bounds the message0.3ms
no producer bounds the QUESTION by its own literal1.3ms
empty-tenant-route.vitest.ts
7/7 15ms · 2 suites PASS
src/chat/empty-tenant-route.vitest.ts
D1 — the menu shown to an empty tenant · 4 tests
does NOT offer the synthesis chip when nothing has been measured2.8ms
still offers the two audits that CAN run — the menu is narrowed, not emptied0.8ms
offers it again the moment there is something to read1.8ms
does not mutate the shared array — a filtered menu must not shrink the real one0.5ms
D2 — a deliberate stop is not a use of the allowance · 3 tests
recognises the empty-tenant synthesis return as BLOCKED0.5ms
still treats a real delivery as a use of the allowance0.4ms
the refund condition covers blocked runs, not just errors7.4ms
judge-in-loop.vitest.ts
7/7 13ms · 4 suites PASS
src/chat/judge-in-loop.vitest.ts
latestLowJudgeVerdict · 2 tests
reads ONE recent low verdict on THIS tenant + session, bounded by score and age5.5ms
no row, no session, or a failed read all prime nothing1.1ms
priorVerdictLine · 1 test
states the score and reasoning as ground truth and forbids the repeat0.7ms
primedTurnsSummary · 1 test
splits judged rows by the ledger mark and averages each side0.8ms
the loop is wired (source pins) · 3 tests
the v2 handler reads the verdict before composing and passes it into runChatV20.9ms
the judged turn is marked in the ledger and on the payload1.0ms
the digest names today AND the window on the no-renderer line, and reports the loop1.7ms
loop-halts.vitest.ts
7/7 16ms · 3 suites PASS
src/chat/loop-halts.vitest.ts
a blocked outcome ends the turn without going back to the model · 2 tests
halts on isBlockedOutcome after the verbatim halt, rendering the tool's own sentence2.7ms
blockedCall is the shared classifier, not a copy of it0.3ms
a single rendered tool ends the turn without a narration call · 3 tests
exists, after the per-tool loop and before the halt is applied0.3ms
fires only for one real, non-errored tool with a renderer of its own0.5ms
evidence tools keep their narration turn — their output is input to the answer9.2ms
the shortcut defers to the request, not just the tool · 2 tests
a write request over a lookup keeps the loop alive0.7ms
the stand-down is instrumented, because its absence is what hid the defect0.7ms
search-insight.vitest.ts
7/7 19ms · 2 suites PASS
src/chat/search-insight.vitest.ts
the two readers agree · 4 tests
a caution produces both a block and an insight, carrying the same sentence3.8ms
a rationale does the same, and folds whyNow into the body4.7ms
a rationale WINS over a concern, in both readers0.5ms
both return nothing when the model said nothing1.0ms
the subtraction is exact · 3 tests
the block appears verbatim in the formatted message6.4ms
removing it leaves the rest of the message intact2.0ms
a message with no note is unchanged by the subtraction0.6ms
shortfall-balance-consistency.vitest.ts
7/7 12ms · 3 suites PASS
src/chat/shortfall-balance-consistency.vitest.ts
the exact event · 2 tests
the message that fired NQZAI-93 still reads as a contradiction2.8ms
the same sentence built from the TURN balance does not0.6ms
one balance per turn, for everything the user reads · 4 tests
runChatV2 pins the turn balance exactly once5.0ms
the shortfall message prefers it over its own read0.4ms
but the DECISION still uses the freshest read0.4ms
falls back to its own read outside a chat turn0.3ms
the guard itself is untouched · 1 test
tolerance was not widened to make the symptom go away1.0ms
stage5-movement.vitest.ts
7/7 7ms · 2 suites PASS
src/chat/stage5-movement.vitest.ts
a fresh measurement answers the question instead of re-buying it · 5 tests
the snapshot path exists and runs BEFORE the prompt library is built2.4ms
is bounded to a week — past that, re-measuring is the honest default0.5ms
an explicit ask for a fresh run skips it entirely1.0ms
says plainly that nothing was spent, and offers the paid path0.3ms
reuses the dashboard TL;DR rather than writing a second narration0.3ms
what the cached answer actually says · 2 tests
leads with movement, which is the whole point of stage 50.7ms
still states the sample honestly — a cached number is not a better number0.6ms
starter-chips.vitest.ts
7/7 41ms · 3 suites PASS
src/chat/starter-chips.vitest.ts
the home-screen starter chips are intents, not prompts · 3 tests
the outbound starter routes to define_icp without the model4.7ms
the scan starter scans the saved site or asks for one, without the model1.6ms
the other starters still reach the model — they are open-ended27.1ms
"Find these leads" carries the ICP's own arguments · 3 tests
is a next_action with the executable arguments, optional axes dropped when silent2.9ms
does not render on a thin brief or a brief with no buyer0.5ms
the plain-text twin is gone from the formatter chips1.7ms
a chip-dispatched tool is quoted from its own arguments · 1 test
next_action_exec passes the arguments to the estimator instead of the catalogue ceiling0.9ms
brief-derivation-gate.vitest.ts
7/7 15ms · 1 suite PASS
src/leads/brief-derivation-gate.vitest.ts
saveProductBrief derivation gate · 7 tests
agrees with isSubstantiveBrief about the two fixtures3.1ms
derives NOTHING from the one-line brief the incident produced3.2ms
still saves the brief and the site — a name is real information0.7ms
still populates onboarding keywords — those read the SITE, not the brief1.6ms
derives normally from a real brief2.6ms
honours an explicit allowDerived:false even on a substantive brief1.7ms
defaults to deriving when no option is passed — the Product panel is unaffected1.9ms
corpus-coverage.vitest.ts
7/7 8ms · 1 suite PASS
src/leads/corpus-coverage.vitest.ts
corpus coverage vs a dropped filter · 7 tests
reaches the user when the corpus returned nothing and nothing outranks it3.3ms
does NOT claim the filter was ignored0.8ms
still says nothing about industry or company size as UNAPPLIED filters0.6ms
yields to a more specific explanation rather than stacking on it0.4ms
does not promise results the user cannot see0.4ms
reads as a sentence when appended after a full stop0.8ms
stays silent once results were actually delivered1.2ms
lead-count-honesty.vitest.ts
7/7 6ms · 4 suites PASS
src/leads/lead-count-honesty.vitest.ts
the requested count is honoured on delivery · 2 tests
the people-search branch slices to meta.limit, like the gmaps branch2.1ms
the gmaps branch still slices too0.3ms
one run reports one set of numbers · 1 test
the poll path forwards saved instead of dropping it0.5ms
a green tick means we checked it · 3 tests
the API sends verified_source so the client can tell a claim from a verdict0.5ms
the list UI ticks only an api-sourced valid, never a provider claim0.4ms
a provider claim still renders, just not as a verdict0.4ms
outbound verification is strict · 1 test
every verifyEmail call that stamps verified_source api is strict0.8ms
research.vitest.ts
7/7 10ms · 2 suites PASS
src/leads/research.vitest.ts
extractSocials — homepage-published profiles only · 3 tests
picks company/profile links, ignores share + intent widgets5.1ms
does NOT invent links when the homepage has none1.1ms
excludes non-profile twitter routes (home/search/hashtag)0.5ms
timezoneHintFromDomain — known ccTLDs only · 4 tests
maps known country TLDs0.9ms
returns null for generic TLDs (never guess a location)0.7ms
treats ambiguous .co as unknown (Colombia vs startup TLD)0.5ms
handles null domain0.4ms
next-action-chips.vitest.ts
7/7 11ms · 2 suites PASS
src/middleware/next-action-chips.vitest.ts
search_leads chips · 5 tests
offers drafting when leads were actually saved5.5ms
does NOT point at existing contacts when the search found nothing0.9ms
offers nothing at all on a zero-result turn rather than something backward-pointing0.4ms
still offers review once there is something to review0.5ms
drops drafting when ids are absent even though leads were found0.5ms
campaign_stats chips · 2 tests
resolves the campaign id from the flat sibling, not by walking the name0.5ms
regression: walking into the name yields nothing0.5ms
tool_adapter.vitest.ts
7/7 5ms · 1 suite PASS
src/middleware/tool_adapter.vitest.ts
tool_adapter — extractToolCallFromText · 7 tests
extracts a JSON tool call2.2ms
extracts the first valid JSON tool call0.5ms
trims the JSON tool name0.4ms
extracts an XML tool call0.9ms
extracts a longcat tool call without a closing tag0.3ms
rejects an unsafe tool name0.2ms
ignores plain text0.3ms
offpage-report.vitest.ts
7/7 11ms · 1 suite PASS
src/reports/offpage-report.vitest.ts
seo_offpage_audit — §17 gold standard · 7 tests
renders a signal bento with fix-prompt cards, grouped by directive2.7ms
has plain section headers (no "Cluster N"), a feedback mount, and no fabricated sparklines0.7ms
healthy authority renders green cards with no fix buttons0.7ms
classifies anchor risk as Fix, authority as Understand — never the other way round0.9ms
does not repeat one referring domain ten times in a "top links" table0.7ms
rounds an estimated traffic figure — no thousandths of a visit2.1ms
never calls a wide, shallow link profile "solid"1.9ms
campaign-stats-coverage.vitest.ts
7/7 9ms · 1 suite PASS
src/routes/campaign-stats-coverage.vitest.ts
coverageForCampaignStats · 7 tests
empty audience → names the missing-audience reason, not "underperformed"3.2ms
has audience but not launched → explains the 0s are pre-launch1.1ms
launched but nothing sent → points at cadence/window, not recipient behavior0.7ms
live with replies → grounds insight in the actual rates0.7ms
a structural zero gets a structural next step, never a copy rewrite1.6ms
never reports sending activity for a campaign that has sent nothing0.5ms
opens but no replies → advises body/CTA, not subject success0.6ms
meta-budget.vitest.ts
7/7 3609ms · 1 suite PASS
src/routes/meta-budget.vitest.ts
public page meta budgets · 7 tests
every serve* export either renders or is a declared machine format3270.4ms
every page has a non-empty title and description51.2ms
no title exceeds 60 characters74.1ms
no description exceeds 160 characters50.2ms
every robots directive is the shared constant, never a hand-written copy57.1ms
no page leaks a robots directive into its visible text58.5ms
no page ships another page's title — a copied template that was never retitled45.5ms
tool-call-limit.vitest.ts
7/7 10ms · 4 suites PASS
src/runtime/tool-call-limit.vitest.ts
checkToolCallLimit · 3 tests
counts up to the cap, then refuses3.8ms
scopes per user and per tool — one user cannot spend another's allowance0.6ms
is inactive when KV is unbound0.4ms
refundToolCallLimit · 2 tests
gives back exactly one allowance0.8ms
never drives the counter below zero0.8ms
peekToolCallLimit · 1 test
reads without incrementing0.6ms
checkToolCallLimit when KV throws · 1 test
refuses, and says the limiter is unavailable rather than that the cap was hit2.2ms
keyword-rivals.vitest.ts
7/7 11ms · 1 suite PASS
src/seo/keyword-rivals.vitest.ts
fetchKeywordRivals · 7 tests
scopes to ONE of the tenant's properties5.1ms
normalises the site, so a stored URL or sc-domain: property still matches0.8ms
orders rivals by real SERP position, not by whatever the DB returned0.8ms
keys case-insensitively so the panel join cannot miss on casing drift1.9ms
collapses the time series to the latest observation per rival0.6ms
returns an empty map for a tenant with no site rather than querying0.5ms
degrades to no-rivals on a failed read, and reports it1.0ms
provider-authority.vitest.ts
7/7 7ms · 2 suites PASS
src/seo/provider-authority.vitest.ts
the provider never rescales its internal rank into an authority score · 4 tests
does not divide rank by ten anywhere3.1ms
passes the rank through under a name that says what it is0.5ms
leaves site authority NULL rather than filling it from the rank0.4ms
reads the free rating service for the off-page audit0.5ms
an unmeasured authority is dropped from the off-page score, not scored as zero · 3 tests
gates the authority component on the value being measured0.6ms
rescales the remaining components so a data gap is not a penalty0.4ms
applies penalties AFTER the rescale, so they are not inflated with it0.8ms
email-pattern.vitest.ts
7/7 17ms · 1 suite PASS
src/leads/shared/email-pattern.vitest.ts
email pattern inference · 7 tests
recognises each of the seven measured shapes7.5ms
reproduces the two domains measured on the real corpus3.7ms
returns null rather than a weak pattern — the common, correct outcome1.0ms
excludes role accounts from BOTH numerator and denominator1.0ms
refuses names that cannot support a first/last pattern0.9ms
is deterministic on ties so re-running does not rewrite rows0.9ms
builds a candidate from a stored recipe, and refuses an unknown one2.1ms
ingest.vitest.ts
7/7 38ms · 1 suite PASS
src/leads/shared/ingest.vitest.ts
shared-leads ingest contracts · 7 tests
admits policy-approved US rows and normalizes them28.3ms
blocks disabled/disallowed sources before payload admission1.9ms
rejects rows whose country contradicts their own batch, and invalid timestamps0.9ms
admits a non-US row when the SOURCE is scoped to that country1.9ms
still refuses a country the source is NOT scoped for0.6ms
defaults a row with no country to its batch country, rather than to US1.4ms
hashes canonical content deterministically and has a known SHA-256 output1.8ms
per-tool-llm.vitest.ts
6/6 7ms · 1 suite PASS
src/billing/per-tool-llm.vitest.ts
per-tool LLM term · 6 tests
is declared on the tools that have a measured sample, and nowhere else3.4ms
is folded into costOfCall, which is what makes it reach BOTH surfaces1.2ms
comes from the TABLE even for tools that have a dynamic estimator0.4ms
NO TOOL CROSSES THE GATE because of its LLM term — the claim, proved not asserted1.2ms
search_leads does not double-count its floor, and no longer carries a hand-typed guess0.5ms
a tool with no measured sample is left alone, not estimated0.3ms
stripe-foreign-session.vitest.ts
6/6 6ms · 1 suite PASS
src/billing/stripe-foreign-session.vitest.ts
a paid session that is not ours · 6 tests
is classified before any money moves3.0ms
does not decide ownership from metadata a sibling product also sets0.7ms
sends a foreign session to LOGS, never to the Issues stream0.4ms
does not go silent on a foreign session0.6ms
still pages when a session IS ours and cannot be credited0.5ms
the error a human reads carries what reconciliation needs0.5ms
gsc-staleness.vitest.ts
6/6 3ms · 1 suite PASS
src/admin/gsc-staleness.vitest.ts
isCoverageStale · 6 tests
is stale when there is no cached timestamp at all (first load ever)2.0ms
is stale when the cached timestamp is unparseable0.3ms
is NOT stale within the 24h window — reproduces the exact live bug value would be false either way, this asserts the window is honored0.5ms
IS stale past the 24h window — this is the live production case: cache sat at 2026-07-19 while today is 2026-08-16, ~28 days old0.3ms
flips exactly at the boundary0.3ms
respects a custom staleMs override0.2ms
issues-are-faults-only.vitest.ts
6/6 7ms · 2 suites PASS
src/admin/issues-are-faults-only.vitest.ts
the admin panel does not report itself to the system it is reading · 3 tests
every Sentry-API read failure goes to Logs, not Issues3.7ms
they stay VISIBLE — suppressed is not the same as silenced0.5ms
real faults elsewhere in this file still use reportError0.2ms
an expected boot state is not filed at all · 3 tests
the no-session 401 is MARKED, not merely named1.2ms
the reporter skips it, and only it0.4ms
the tile still degrades honestly for the user0.3ms
judge-turn-provenance.vitest.ts
6/6 6ms · 2 suites PASS
src/admin/judge-turn-provenance.vitest.ts
the judge-provenance column is actually requested · 4 tests
finds both quality_scores queries — primary and fallback3.6ms
selection 0 requests turn_id0.4ms
selection 1 requests turn_id0.2ms
every field feedback-rca reads off a quality row is in BOTH selections1.0ms
the write side that was wrongly blamed · 2 tests
captures the turn id SYNCHRONOUSLY, before the detached judge runs0.5ms
passes it in logApiUsage's turnIdOverride slot, not a meta field0.3ms
prompt-cache.vitest.ts
6/6 67ms · 2 suites PASS
src/admin/prompt-cache.vitest.ts
handleAdminPromptCache · 5 tests
refuses without the admin secret54.0ms
says WHY it cannot answer when the CF credential is absent, instead of returning zero4.3ms
computes per-row and total hit rates from the datapoints3.7ms
sends the window as a bounded day count1.4ms
surfaces an upstream rejection verbatim rather than reporting zero cache hits1.6ms
describeVerdict — an empty dataset is not a cold cache · 1 test
separates no_data from not_caching0.6ms
provision-verified.vitest.ts
6/6 6ms · 3 suites PASS
src/admin/provision-verified.vitest.ts
it calls the real function, not a copy · 2 tests
delegates to ensureVerifiedUserProvisioning2.8ms
does NOT re-implement the grant or the email0.8ms
the verification gate survives · 3 tests
passes the user REAL emailVerified, never a hardcoded true0.3ms
refuses an unknown user or one with no email0.5ms
the gate it relies on still checks app-verification independently0.5ms
safe to run twice · 1 test
the grant and the welcome are both idempotent upstream0.4ms
user-verified.vitest.ts
6/6 6ms · 1 suite PASS
src/admin/user-verified.vitest.ts
user-verified · 6 tests
treats a __app_email_verified__ row with a JSON instruction as verified3.5ms
builds the app-verified id set, ignoring empty-instruction rows0.9ms
is verified when the app flag is set even though Nhost emailVerified is false (the bug)0.4ms
is verified when Nhost emailVerified is true even without the app flag0.4ms
is unverified only when neither source says so0.5ms
card and list agree for the same inputs (no drift)0.4ms
signup-server-events.vitest.ts
6/6 59ms · 1 suite PASS
src/auth/signup-server-events.vitest.ts
captureSignupAttribution + ensureVerifiedUserProvisioning — server-side signup events · 6 tests
captures at signup, fires GA4 MP and the DataFast goal exactly once at grant time46.7ms
fires correctly even when the grant is triggered by a DIFFERENT request than signup (the actual bug)2.8ms
does not fire twice — a second grant after the bonus is already granted is a no-op2.0ms
skips silently when the secrets are unset1.9ms
skips silently when neither cookie was present at signup1.6ms
never throws when GA4/DataFast fetches fail — signup provisioning must not depend on either3.6ms
verify-provisioning.vitest.ts
6/6 4ms · 3 suites PASS
src/auth/verify-provisioning.vitest.ts
the verify endpoint provisions · 2 tests
calls ensureVerifiedUserProvisioning after writing the verified flag2.5ms
a provisioning failure cannot break a SUCCESSFUL verification0.3ms
calling it from BOTH paths stays safe · 3 tests
the grant checks for an existing row before inserting0.3ms
the welcome email is deduped per user0.3ms
the bootstrap path is untouched — it simply finds the work done0.4ms
the grant is still gated on actually being verified · 1 test
an unverified user provisions nothing0.2ms
commerce-availability.vitest.ts
6/6 8ms · 2 suites PASS
src/commerce/commerce-availability.vitest.ts
one switch, every surface · 3 tests
the UI banner is present exactly when the module is under review3.3ms
the model is told not to offer store features while it is under review1.0ms
the banner copy and the chat copy say the same thing1.2ms
the tool result is a soft outcome, not an error · 3 tests
carries a message rather than an error0.4ms
names the tool that was asked for, so the turn can be traced0.3ms
does not blame the store platform or promise a date1.4ms
sync.vitest.ts
6/6 7ms · 2 suites PASS
src/commerce/sync.vitest.ts
(top level) · 1 test
order queries request no customer-identifying fields2.9ms
sync GraphQL documents are structurally balanced · 5 tests
BULK_PRODUCTS_QUERY0.8ms
RECON_PRODUCTS_QUERY0.8ms
PRODUCT_REFETCH_QUERY0.4ms
BULK_ORDERS_QUERY0.3ms
RECON_ORDERS_QUERY0.6ms
nhost-error-attribution.vitest.ts
6/6 9ms · 1 suite PASS
src/db/nhost-error-attribution.vitest.ts
every Nhost failure knows which query it was · 6 tests
names the operation for the real query shapes this repo sends4.2ms
degrades to "anonymous" rather than throwing on an unnamed query0.6ms
the HTTP failure carries it0.6ms
the GraphQL-errors failure carries it too0.7ms
the MESSAGE is unchanged, so one outage stays one issue0.6ms
reportError promotes it to a tag0.5ms
domain-readiness.vitest.ts
6/6 4ms · 1 suite PASS
src/email/domain-readiness.vitest.ts
parseDkimSelectorResult · 6 tests
finds a standard v=DKIM1 record2.8ms
finds a record with no v= tag — RFC 6376 makes it optional, p= alone still counts0.6ms
treats an empty p= as a REVOKED key, not a missing one — a different, intentional state0.3ms
reports not-found on an empty TXT set (the common case — selector unused)0.2ms
does not mistake an unrelated TXT record for a DKIM key0.3ms
has a real selector list to probe, not an emptied one0.4ms
gmail-one-at-a-time.vitest.ts
6/6 4ms · 4 suites PASS
src/email/gmail-one-at-a-time.vitest.ts
gmail-send disabled — feature flag · 1 test
isGmailSendEnabled() is false (scope removed from verification)1.7ms
gmail-send disabled — resolvesToGmail never resolves to Gmail · 2 tests
is false even when the caller explicitly asks for channel:gmail0.7ms
is false even when a Gmail identity is connected and no SMTP is configured0.2ms
gmail-send disabled — sendEmail never hits the Gmail API · 2 tests
with channel:gmail but no SMTP: no Gmail network call, returns a BYOK-required error0.9ms
automated/drip send (disallowGmail) with a stale Gmail connection makes no Gmail call0.3ms
SMTP provider resolution still works · 1 test
getConfiguredSmtpProvider returns null when no __smtp__ row exists0.4ms
finalising-step.vitest.ts
6/6 7ms · 1 suite PASS
src/chat/finalising-step.vitest.ts
the closing step · 6 tests
carries the finalising flag so settle completes it instead of staling it4.4ms
survives the guardrail scan with its label intact — the flag keys off that label0.3ms
never flags an ordinary step0.6ms
names the work rather than the wait — no fake-progress copy0.6ms
is user-facing copy: no vendor name, no USD figure (CLAUDE.md §4)0.4ms
keeps ordinary phase projection unchanged — group, elapsed and details still ride1.5ms
find-ask.vitest.ts
6/6 10ms · 2 suites PASS
src/chat/find-ask.vitest.ts
the shape of a find-new-people ask · 5 tests
a find verb with a count or a place4.8ms
owned-inventory wording is never a find ask1.0ms
the platform's own artefacts are not people0.5ms
a bare find with no count and no place stays with the intent router0.4ms
only fires when list_contacts was chosen alone1.2ms
the loop redirects at the chokepoint, before the cost gate · 1 test
rewrites the call to search_leads with arguments from the same builder the lead chip uses1.1ms
picker-turns-not-failures.vitest.ts
6/6 7ms · 2 suites PASS
src/chat/picker-turns-not-failures.vitest.ts
arming a picker marks the turn · 3 tests
setPendingPicker records it — the one call every picker makes3.3ms
and it is recorded BEFORE the KV write, which can be skipped0.4ms
the flag resets per request0.3ms
and a picker turn is not judged as a failed answer · 3 tests
isGateTurn includes pickerArmed alongside the three typed fields1.1ms
the three original terms survive — this is an addition, not a swap0.6ms
a gate turn that PRODUCED something is still judged0.2ms
verify-contacts-format.vitest.ts
6/6 23ms · 2 suites PASS
src/chat/verify-contacts-format.vitest.ts
verify_contacts selection step · 2 tests
asks which list instead of picking one15.6ms
offers each list as a one-click chip3.4ms
verify_contacts results · 4 tests
reports verified and deliverable counts0.6ms
says unchecked addresses were NOT marked bad when the provider gave no verdict0.5ms
surfaces the free-tier cap note and a top-up chip0.9ms
relays an error without pretending anything was verified0.6ms
apify-poll-ownership.vitest.ts
6/6 28ms · 1 suite PASS
src/leads/apify-poll-ownership.vitest.ts
apifyCheckAndStore ownership · 6 tests
refuses a poll for a run owned by a different tenant10.0ms
refuses when the run meta carries no owner at all1.3ms
refuses when there is no run meta at all1.3ms
refuses an anonymous caller even when the run has an owner0.8ms
lets the real owner through to the run lookup12.2ms
lets the trusted queue job through without an owner in meta2.5ms
async-spend-visibility.vitest.ts
6/6 5ms · 3 suites PASS
src/leads/async-spend-visibility.vitest.ts
the async spend reaches the window it happened in · 2 tests
the Apify leads_scrape charge is accrued, not only ledgered2.7ms
accrueApiCost is actually imported0.5ms
the user is told what the background half cost · 3 tests
the completion push carries a total0.4ms
both completion shapes render it — artifact and plain bubble0.5ms
states it in tokens, never currency0.5ms
a failed turn still accounts for what it spent · 1 test
the error branch emits a cumulative footer0.4ms
lead-fit.vitest.ts
6/6 30ms · 1 suite PASS
src/leads/lead-fit.vitest.ts
lead fit · 6 tests
weights sum to one and every axis has five concrete levels6.6ms
one score question per axis, over the axis levels2.2ms
the composite is code arithmetic: all top levels = 100, all bottom = 0, weights visible in between1.9ms
the why-line names each axis and its level; empty when there is no breakdown0.5ms
returns null — the model's score stands — when the flag is off or there is no brief1.0ms
research runs the fit beside the generative call, every writeback persists the breakdown, the card shows why17.1ms
list-name.vitest.ts
6/6 9ms · 4 suites PASS
src/leads/list-name.vitest.ts
matching ignores case, because users do · 2 tests
uses _ilike, not _eq or _in4.5ms
matches any of several names1.4ms
it does NOT guess between near-miss names · 2 tests
leaves separators alone — two lists differing by separator are two lists0.6ms
escapes LIKE wildcards so a literal name matches only itself0.3ms
tenant scoping is structural, not remembered · 1 test
always emits user_id — the parameter is required, so a caller cannot omit it0.4ms
no usable name is NOT "match everything" · 1 test
returns null for empty, blank and whitespace-only input0.5ms
role-inbox.vitest.ts
6/6 6ms · 1 suite PASS
src/leads/role-inbox.vitest.ts
role inboxes on a role-constrained people search · 6 tests
drops the shared mailbox when a department was asked for3.3ms
drops it when only a seniority was asked for0.4ms
KEEPS it when no role was requested — then it is a weak but honest lead0.4ms
returns nothing rather than a role inbox when that is all the domain has0.3ms
treats a missing flag as "not a role inbox" rather than guessing from the title0.3ms
does not mutate the caller’s array1.4ms
jev.vitest.ts
6/6 53ms · 4 suites PASS
src/llm/jev.vitest.ts
rollout · 1 test
needs the key AND the context in JEV_ROLLOUT (or "1")3.5ms
an evaluation · 3 tests
posts state + questions with the bearer key, returns the answers, bills the usage35.6ms
throws on a non-2xx and on a body with no answers — the caller owns the fallback4.3ms
is priced at the gateway rate, never a promotional zero0.4ms
sub-group narrowing is gated separately (2026-09-20) · 1 test
the hook applies a sub-group only when tool_shortlist_subgroup is rolled out6.6ms
tool shortlist question (2026-09-19) · 1 test
offers every family plus general_or_core, off the same hints the model reads in search_tools1.8ms
cost-estimate.vitest.ts
6/6 5ms · 3 suites PASS
src/outbound-run/cost-estimate.vitest.ts
cost estimate ordering — low <= expected <= high, always · 2 tests
holds at the default daily target2.3ms
scales linearly with daily target0.7ms
7-day high estimate at the default 10 leads/day, pinned to a real computed value · 2 tests
is in the low single-digit millions, not the doc's original uncomputed claim0.5ms
the default budget covers the full 7-day high estimate with margin, derived not hardcoded0.3ms
run cost aggregates correctly from per-day · 2 tests
run total equals per-day * days0.2ms
per-lead is per-day / daily target0.4ms
function_registry.vitest.ts
6/6 4ms · 1 suite PASS
src/middleware/function_registry.vitest.ts
FunctionRegistry · 6 tests
lists only public tools1.9ms
invokes with valid args0.7ms
rejects a missing required arg0.5ms
rejects an unknown property0.4ms
rejects a non-public function0.3ms
lists all registered functions0.3ms
rag-score-conformity.vitest.ts
6/6 32ms · 1 suite PASS
src/reports/rag-score-conformity.vitest.ts
aeo_page_check — the composite carries the attribution too · 6 tests
lifts score_breakdown to the top level so every reader finds it2.3ms
the composite artifact renders the same breakdown section21.4ms
the composite artifact names the biggest loss, not just a finding count0.7ms
the chat formatter reads the NESTED rag leg instead of rendering zeros5.4ms
the TL;DR states the score rather than falling through to the generic lead1.1ms
still renders when the rag leg errored — a failed leg is not a blank report0.5ms
resolve-report-href.vitest.ts
6/6 4ms · 1 suite PASS
src/reports/resolve-report-href.vitest.ts
resolveReportHref · 6 tests
joins a site-relative path onto the report domain2.4ms
strips protocol/trailing slash from the base before joining0.6ms
leaves absolute URLs untouched (base ignored)0.3ms
prefixes https:// on a bare host0.2ms
never emits the malformed triple-slash when base is missing0.4ms
returns empty string for empty input0.3ms
capability-groups.vitest.ts
6/6 11ms · 1 suite PASS
src/routes/capability-groups.vitest.ts
capability grouping · 6 tests
renders EVERY section exactly once — none lost, none duplicated5.4ms
keeps every capability — the card count is preserved end to end1.1ms
has no section falling through to "More"1.2ms
still loses nothing when a section is unknown to the group list1.4ms
preserves the section anchors the rest of the site links to0.9ms
puts the decision-shaped sections together, which is the line pricing mostly draws0.6ms
me-internal-flag.vitest.ts
6/6 110ms · 1 suite PASS
src/routes/me-internal-flag.vitest.ts
the identity payload carries an internal flag · 6 tests
is true for an account listed in SCORE_EXCLUDE_USERS57.3ms
matches case-insensitively — the list is lower-cased, the account is not32.2ms
is true when only the USER ID matches, with no email overlap5.2ms
is FALSE for a real customer — the flag has to be able to say no4.0ms
is present on every 200, never conditionally omitted6.6ms
an unauthenticated call still 401s — the new field did not widen the gate3.6ms
open-tracking.vitest.ts
6/6 49ms · 1 suite PASS
src/routes/open-tracking.vitest.ts
machine-open suppression · 6 tests
suppresses an explicit prefetch/preview fetch42.4ms
suppresses unambiguous security gateways and scripted clients2.9ms
does NOT suppress mail proxies that also carry genuine human opens1.6ms
does not suppress an ordinary browser fetch0.4ms
treats a missing request or empty UA as human — never invent a suppression0.4ms
the time window is long enough to exclude delivery-time proxying, short enough to keep real opens0.6ms
api-ratelimit.vitest.ts
6/6 8ms · 1 suite PASS
src/runtime/api-ratelimit.vitest.ts
checkApiRateLimit · 6 tests
allows and counts a normal request under both keys3.6ms
refuses the user once their hourly ceiling is reached, naming the scope0.8ms
refuses an IP once its ceiling is reached even for a fresh user0.4ms
the per-IP ceiling is looser than the per-user one (an office is one IP)0.2ms
fails OPEN when KV throws2.9ms
is inactive when KV is unbound0.2ms
eval-escape-not-forgeable.vitest.ts
6/6 5ms · 1 suite PASS
src/runtime/eval-escape-not-forgeable.vitest.ts
eval escapes are not forgeable from a request · 6 tests
never reads X-Smoke-Bypass from request headers3.4ms
never reads X-Approval-Mode from request headers0.6ms
never reads X-CoT-Override from request headers0.5ms
never sets the escape headers on an outgoing/synthetic request either0.6ms
gates the balance skip on the injected argument, not the request0.3ms
keeps the balance gate wired to that flag0.4ms
preflight-warned.vitest.ts
6/6 11ms · 2 suites PASS
src/runtime/preflight-warned.vitest.ts
the slot is ONE-SHOT · 3 tests
a second read returns nothing — one warning can produce at most one outcome4.5ms
stores the TOOL, not a bare flag, so a confirm can only be credited to its own warning0.6ms
is per-tenant0.5ms
it can never break the turn it measures · 3 tests
no KV bound → silent no-op, never a throw2.0ms
a KV that throws is swallowed on both sides1.7ms
does not delete when there was nothing to read0.8ms
substitute-spend.vitest.ts
6/6 5ms · 1 suite PASS
src/runtime/substitute-spend.vitest.ts
blocksSubstituteSpend — a cap binds the turn, not one tool name · 6 tests
blocks a DIFFERENT paid tool after a cap refusal3.0ms
never blocks a FREE tool — the turn must still be able to explain itself0.5ms
does not block the capped tool itself — its own cap check owns that message0.3ms
does nothing when no cap has bitten0.3ms
blocks after ANY of several caps, not only the most recent0.4ms
replays the live incident end to end0.3ms
server-error-not-data.vitest.ts
6/6 8ms · 3 suites PASS
src/ui/server-error-not-data.vitest.ts
the seam, not 41 call sites · 3 tests
throws on a 5xx so the catch blocks that already exist finally fire2.2ms
only for API calls, and only for 5xx0.8ms
the error carries its status, so a caller can tell what happened0.4ms
the outage channel must not throw · 1 test
the version poll uses _nativeFetch and so bypasses this entirely0.3ms
the failure this prevents, stated as the thing a user would see · 2 tests
contacts still renders an ERROR path, not just an empty state0.8ms
the assignment that turned a failure into an empty list is still the fallback3.3ms
aeo-grounding.vitest.ts
6/6 5ms · 1 suite PASS
src/seo/aeo-grounding.vitest.ts
AEO engines answer from retrieval, not memory · 6 tests
chatgpt is grounded with the web plugin2.8ms
claude is grounded with the web plugin0.3ms
gemini is grounded with the web plugin0.2ms
perplexity is NOT double-grounded — sonar already retrieves0.5ms
EVERY engine in the map is a deliberate choice, grounded or native0.4ms
an unknown engine yields no model id rather than a plausible-looking one0.3ms
ai-visibility-answers-library.vitest.ts
6/6 66ms · 1 suite PASS
src/seo/ai-visibility-answers-library.vitest.ts
AI Visibility Answers is registered and reachable · 6 tests
exists in the prompt library, which is also the public capability catalog9.0ms
every published prompt routes to EXACTLY ONE brief39.2ms
no two published prompts land on the SAME brief6.4ms
every prompt is priced at the diagnostic floor with NO provider term4.2ms
every item carries a label and a description a browser can act on5.0ms
does not duplicate a prompt already published in another section2.1ms
competitor-store-single-source.vitest.ts
6/6 5ms · 2 suites PASS
src/seo/competitor-store-single-source.vitest.ts
the competitors snapshot read is gone and stays gone · 3 tests
no module reads audit_type "competitors" — it has no writer2.6ms
sov.ts resolveCompetitorSet no longer touches seo_snapshots at all0.3ms
sov.ts dropped the now-orphaned nhostAdminGraphQL import0.2ms
the competitor prompt class is wired to the live store · 3 tests
aeo.ts builds "alternatives to" prompts from the saved competitor set0.5ms
the alternatives prompt reads the saved set in its OWN block0.8ms
still tags those prompts as `competitor` so the library can report provenance0.2ms
content-ideas-filter.vitest.ts
6/6 7ms · 1 suite PASS
src/seo/content-ideas-filter.vitest.ts
content-ideas-filter · 6 tests
drops brand-navigational self-references despite spacing differences2.8ms
keeps genuine topic questions that merely share a common word0.3ms
does not gate on very short brand tokens (<4 collapsed chars)0.2ms
still drops JS-wall / anti-bot interstitial noise0.8ms
cleanContentIdeas partitions and counts both drop classes1.0ms
empty brand skips the navigational rule1.0ms
content-ideas-sitemap-probe.vitest.ts
6/6 8ms · 2 suites PASS
src/seo/content-ideas-sitemap-probe.vitest.ts
seo_content_ideas sitemap probe · 4 tests
probes the candidates in parallel, not one 5s wait after another2.7ms
catches each candidate independently, so one throw cannot skip the rest0.8ms
still lets the first candidate that yields topics win0.4ms
keeps all three candidates — the fix must not have quietly dropped one0.9ms
probe semantics — a throwing first candidate must not mask the rest · 2 tests
reaches sitemap_index.xml when sitemap.xml throws1.8ms
the OLD shared-catch form stopped at the first throw — the regression this locks out0.6ms
friendly-seo-error.vitest.ts
6/6 14ms · 2 suites PASS
src/seo/friendly-seo-error.vitest.ts
friendlySeoError names the subsystem that actually failed · 4 tests
attributes an exhausted model chain to the writing model, not the SEO provider4.8ms
still attributes a provider timeout to the SEO provider0.9ms
keeps the non-timeout model failure distinct from the timeout one1.0ms
leaves an unrecognised error untouched for the raw path1.4ms
the corrected message keeps its Sentry classification · 2 tests
a model TIMEOUT stays an expected outcome, exactly as before1.6ms
a model UNAVAILABLE is still not suppressed3.5ms
geo-cold-start.vitest.ts
6/6 6ms · 2 suites PASS
src/seo/geo-cold-start.vitest.ts
the new-account entry states the whole path, not one step of it · 3 tests
names what happens after the domain is given2.3ms
says WHY the scan matters, in the user's terms0.3ms
offers a chip rather than ending on a bare instruction0.2ms
every prompt origin is explainable to the user · 3 tests
labels all five origins0.6ms
distinguishes a MEASURED origin from a GUESSED one1.1ms
never uses wording that implies a guess was measured1.0ms
keyword-input.vitest.ts
6/6 6ms · 2 suites PASS
src/seo/keyword-input.vitest.ts
isEchoedEmptyKeyword · 3 tests
catches the echoed-empty shapes that reached a paid lookup2.6ms
leaves genuine keywords alone, including quoted ones0.5ms
reports a genuinely empty string as NOT echoed — the plain guard owns that case0.3ms
normalizeKeywordInput · 3 tests
collapses both empty and echoed-empty to '' so one guard covers both0.4ms
passes a real keyword through, trimmed0.3ms
coerces non-string arguments the model can emit0.9ms
seo-fail-channel.vitest.ts
6/6 36ms · 2 suites PASS
src/seo/seo-fail-channel.vitest.ts
seoFail routes by what the message SAYS, not by how it travelled · 4 tests
consults the same predicate the returned-{error} path uses7.3ms
an expected outcome is counted on Logs, not filed as an Issue4.8ms
reportError is reachable only on the NOT-expected branch9.6ms
the NQZAI-96 body classifies as an outcome, and a real crash does not4.8ms
a resumed crawl keeps the rendering mode it was started with · 2 tests
seo_onpage_results passes renderJs from the stored marker4.3ms
the marker type declares renderJs, so a future reader cannot silently drop it again4.3ms
serpdex-degrade-surface.vitest.ts
6/6 11ms · 2 suites PASS
src/seo/serpdex-degrade-surface.vitest.ts
a recovered serpdex degradation is a log, not an Issue · 4 tests
reports it on the Logs surface at warn level2.5ms
does NOT raise it as an Error Issue0.8ms
keeps the diagnostic context that made the Issue useful0.8ms
still names the fallback that ran, so the log says what happened next0.3ms
why an EXPECTED_OUTCOME pattern would NOT have worked here · 2 tests
the degraded string is not on the soft-failure path, so the matcher never sees it3.2ms
the matcher DOES already cover the read_url 403 pair from the same sweep1.6ms
write-content-budget.vitest.ts
6/6 7ms · 1 suite PASS
src/seo/write-content-budget.vitest.ts
seo_write_content fits its generation inside its tool budget · 6 tests
derives the remaining time from the declared tool budget, not a constant3.4ms
leaves the cron/inbound path on its historic budget0.8ms
never lets the derived deadline EXCEED the historic 240s0.4ms
refuses to start a generation that cannot finish, instead of guaranteeing a timeout0.5ms
the reserve leaves room for the work that happens AFTER the generation returns0.4ms
the arithmetic actually closes: reserve + minimum generation < the tool budget0.8ms
probe-outcome.vitest.mjs
6/6 7ms · 1 suite PASS
scripts/lib/probe-outcome.vitest.mjs
isUnproductiveCode · 6 tests
treats no response at all as unproductive2.9ms
treats 4xx deferrals as unproductive — the defect this file exists for0.6ms
treats definitive answers as productive, whether valid or invalid0.5ms
accepts numbers and strings identically0.3ms
matches migration 122 SQL exactly across every code that can reach the column1.8ms
normalizeCode keeps the empty string out of provider_code entirely0.4ms
branch-telemetry.vitest.ts
6/6 5ms · 1 suite PASS
src/leads/shared/branch-telemetry.vitest.ts
every corpus search records the branch it took · 6 tests
emits on EVERY search, not only when a stage degrades2.9ms
classifies all four branches the way the SQL function does1.0ms
records the ARGUMENT that chose the branch, so a zero traces to its turn0.6ms
records the OUTCOME, not just the path — the half the deadline eats0.3ms
cannot break the search it counts0.3ms
is a Sentry LOG, never an Issue — a counter is not a fault0.5ms
corpus-gate.vitest.ts
6/6 23ms · 2 suites PASS
src/leads/shared/corpus-gate.vitest.ts
the corpus is the only database the shared-leads path talks to · 2 tests
no retired shared_leads_* root field survives anywhere in src15.2ms
every corpus root field the path needs is actually wired2.2ms
one gate, and its refusal still reaches the caller as a refusal · 4 tests
the Worker no longer computes its own rights verdict1.5ms
a database-side rejection is mapped back to the route's 403 contract1.3ms
an unreadable policy is NOT reported as a rights refusal0.7ms
the policy is still fetched — it is not only a gate0.5ms
reachability-ranking.vitest.ts
6/6 16ms · 2 suites PASS
src/leads/shared/reachability-ranking.vitest.ts
isReachable · 3 tests
is true only for an active email on a non-consumer domain2.8ms
is false when the flag is absent — never assume reachable0.4ms
ignores a reachable flag on a non-email identifier0.3ms
rankCatalogCandidates · 3 tests
puts a reachable candidate above an unreachable one with a BETTER score0.4ms
still ranks by score within the same reachability class1.3ms
is deterministic on a tie10.1ms
region-branch.vitest.ts
6/6 5ms · 1 suite PASS
src/leads/shared/region-branch.vitest.ts
the region filter branches on AXIS, not on existence (migration 155) · 6 tests
reads the migration that actually shipped the fix2.4ms
does not choose the branch with a bare EXISTS over person.locality0.7ms
probes BOTH organization axes and compares them0.7ms
keeps the caps that make the comparison meaningful0.5ms
still requires the person half to be able to FILL the request0.4ms
carries a self-verification that plants the failing condition rather than counting rows0.3ms
chip-renderer-parity.vitest.ts
5/5 4ms · 1 suite PASS
client/chip-renderer-parity.vitest.ts
chip renderer parity · 5 tests
both renderers branch on __topup__2.1ms
both renderers branch on __execute:0.7ms
both renderers branch on __panel:0.5ms
both renderers branch on __populate__:0.5ms
the label normaliser strips every prefix the renderers know0.3ms
balance-cache.vitest.ts
5/5 447ms · 1 suite PASS
src/billing/balance-cache.vitest.ts
balance cache invalidation on spend · 5 tests
invalidateBalanceCache deletes the cached balance3.4ms
logTokenUsage drops the stale cached balance so the next read recomputes259.3ms
logApiUsage drops the stale cached balance178.1ms
tokensRemaining noCache bypasses a stale cache and reads the ledger fresh1.0ms
tokensRemaining (cached) still serves the cache when present and not bypassed1.2ms
reservation-accumulates.vitest.ts
5/5 26ms · 2 suites PASS
src/billing/reservation-accumulates.vitest.ts
a fan-out cannot spend the same balance twice · 3 tests
refuses the leg that the balance can no longer cover23.6ms
accumulates across legs that all fit0.5ms
pins the baseline at the FIRST gate, not the last0.4ms
the NQZAI-92 arithmetic, replayed · 2 tests
the spend the alarm reported is real and is three legs, not one0.3ms
three reservations cover it; one does not0.7ms
gsc-site-pick.vitest.ts
5/5 6ms · 1 suite PASS
src/admin/gsc-site-pick.vitest.ts
pickAdminSite · 5 tests
ignores a non-nqz requested site (the connector default) and picks nqz.ai3.2ms
honors a requested site only when it is itself an nqz.ai property0.8ms
prefers the sc-domain nqz property over url variants0.4ms
falls back to any nqz.ai host variant not in the preferred list0.4ms
returns null-ish only when the connector truly has no nqz.ai property0.7ms
perf-drift.vitest.ts
5/5 8ms · 1 suite PASS
src/admin/perf-drift.vitest.ts
computePerfDrift · 5 tests
flags a tool whose recent p95 is >1.5× its baseline5.0ms
a stable tool (recent ≈ baseline) is not flagged0.6ms
a tool that got FASTER is not flagged0.5ms
too few samples in either window → skipped (noise guard)0.3ms
ignores non-finite / negative durations without crashing0.8ms
signup-digest.vitest.ts
5/5 10ms · 1 suite PASS
src/admin/signup-digest.vitest.ts
signupDigestLines · 5 tests
counts real signups only, the silent ones, and the flagged ones; flagged lines first4.8ms
the 09-15 defect is detected structurally: a confirm card ran 3 tools and the reply showed 11.4ms
names the tools with counts, the not-ok runs, the low judge scores, spend, purchases and audits0.7ms
renders as a section with the three counts0.7ms
is wired into the daily judge report0.9ms
users-query.vitest.ts
5/5 6ms · 1 suite PASS
src/admin/users-query.vitest.ts
AdminUsers query · 5 tests
does not select users_aggregate — Hasura has no aggregate root for the auth users table3.4ms
still selects the token_usage aggregate, which DOES exist0.8ms
uses # for comments, never // — a JS comment inside a GraphQL document is a syntax error0.4ms
still selects every field the handler reads, so the hoist did not drop one0.6ms
declares both variables it uses0.4ms
tool-dispatch.vitest.ts
5/5 6ms · 2 suites PASS
src/campaigns/tool-dispatch.vitest.ts
statusTagFromName · 3 tests
maps bare status names3.2ms
normalises case, "list"/"contacts" suffixes, and spaces/hyphens0.6ms
rejects real list names and junk0.5ms
statusTagWhere · 2 tests
treats NULL lead_status as 'new' (UI default)1.2ms
matches other tags exactly0.5ms
pricing.vitest.ts
5/5 6ms · 1 suite PASS
src/commerce/pricing.vitest.ts
computePriceScenario · 5 tests
price rise: margin math + break-even fall boundary4.0ms
price cut: break-even rise boundary0.5ms
scenario at or below cost → no break-even, negative margin stated0.5ms
missing cost → labelled 50%-of-current-price assumption, never silent0.4ms
invalid inputs rejected0.9ms
composio-proxy-failure.vitest.ts
5/5 53ms · 1 suite PASS
src/connectors/composio-proxy-failure.vitest.ts
composioProxy: successful:false is a failure, not empty data · 5 tests
reports a failed execution as non-2xx even though the HTTP status was 20047.2ms
carries the upstream reason so it is not just a bare number1.3ms
prefers a real upstream status over the generic 5021.6ms
leaves a genuine success completely alone2.1ms
is additive — an envelope with no `successful` field behaves exactly as before1.1ms
google-proxy-hosts.vitest.ts
5/5 5ms · 1 suite PASS
src/connectors/google-proxy-hosts.vitest.ts
composio proxy endpoint — which Google hosts survive path-only · 5 tests
GSC URL Inspection goes ABSOLUTE — Sentry NQZAI-7M2.4ms
an unknown Google endpoint defaults to ABSOLUTE, not to a guess about routing0.7ms
GSC Search Analytics stays path-only — /webmasters/v3 is served by the base host0.4ms
GA4 Data API stays path-only — it IS the ga4 base host0.5ms
GA4 ADMIN API goes absolute — a different host on the same route0.3ms
sequences.vitest.ts
5/5 49ms · 1 suite PASS
src/drip/sequences.vitest.ts
handleCreateSequence delay_days coercion · 5 tests
accepts numeric-string delay_days43.0ms
accepts synonym keys (delay / days / wait_days)1.5ms
defaults a missing delay: first step 0, later steps 31.9ms
still rejects garbage delay values0.9ms
still rejects steps missing subject/body1.5ms
domain-readiness-prompts.vitest.ts
5/5 6ms · 2 suites PASS
src/email/domain-readiness-prompts.vitest.ts
buildDnsFixPrompt — provider-aware routing · 3 tests
names the detected provider and its dashboard path3.0ms
route53 and godaddy get their own paths0.4ms
unknown or null provider → neutral phrasing0.3ms
buildDnsFixPrompt with a derived fix · 2 tests
leads with the concrete change and asks only to apply and verify0.7ms
recommendedDmarcRecord upgrades in place and keeps the tenant's tags0.6ms
confirm-sections.vitest.ts
5/5 6ms · 4 suites PASS
src/chat/confirm-sections.vitest.ts
firstProseLine · 1 test
skips table rows, rules, notes and next-moves, strips bold and bullets, bounds the length2.8ms
toolLabelFor · 1 test
prefers the cost table label, falls back to the name in words0.5ms
composeConfirmPreface · 2 tests
markdown: one bold line per tool, report note where one was saved, blank line before the reply0.3ms
html: the same lines as paragraphs, escaped0.3ms
the cost_confirm handler keeps every executed tool · 1 test
collects each run and prefixes the reply with the earlier ones0.8ms
response-contract.vitest.ts
5/5 31ms · 1 suite PASS
src/chat/response-contract.vitest.ts
response contract registry · 5 tests
every intent applies only known invariants and declares path/cost/role28.7ms
no duplicate intents0.6ms
paid/expensive intents that answer require the approval gate0.5ms
expensive heavy tasks require reasoning-before-handoff (except pure gate turns)0.7ms
lookup helpers resolve0.4ms
shortcut-domain.vitest.ts
5/5 15ms · 1 suite PASS
src/chat/shortcut-domain.vitest.ts
shortcut paths never drop a domain the user named · 5 tests
full_seo_audit extracts it, and carries it through the approval turn6.1ms
serpdex extracts it — its own catalog prompt names a competitor domain4.6ms
the on-page DEPTH turn reads the request, not the chip1.3ms
the SEO route picker carries the domain into whichever audit it selects0.9ms
every domain-scoped SEO shortcut passes a site — guards the shape, not today instances1.6ms
tables-only.vitest.ts
5/5 14ms · 2 suites PASS
src/chat/tables-only.vitest.ts
isTablesOnly · 3 tests
the [1.5.1] reply — tables and captions, no sentence3.5ms
one real sentence anywhere means it is not tables-only0.7ms
empty is not tables-only (that is the silent-turn path)0.4ms
diagnoseAnswerText · 2 tests
mirrors the presenter: bold headline, then the answer0.7ms
is applied on the agent path after the loop8.1ms
topup-intent.vitest.ts
5/5 8ms · 3 suites PASS
src/chat/topup-intent.vitest.ts
top-up intent → deterministic route · 2 tests
catches the phrasing the broken button used to send3.1ms
catches the ways a person actually asks1.3ms
questions ABOUT spend still reach the model · 2 tests
does not hijack a usage or cost question0.5ms
does not fire on unrelated messages that merely mention tokens0.3ms
chip contract · 1 test
the top-up chip carries the __topup__ prefix the client routes on0.7ms
uplift-wiring.vitest.ts
5/5 13ms · 2 suites PASS
src/chat/uplift-wiring.vitest.ts
the offer becomes a chip, for every tool · 3 tests
leads with the top-up chip on tools that have no upsell code of their own4.4ms
keeps the tool's own next steps behind the offer1.2ms
changes nothing when the run was not capped0.6ms
the offer is appended to the result, never substituted for it · 2 tests
keeps the whole tool output and adds the offer after it6.3ms
never quotes a dollar figure0.5ms
poor-fit-note.vitest.ts
5/5 5ms · 1 suite PASS
src/leads/poor-fit-note.vitest.ts
a poor-fit batch announces itself in the note, not in a column · 5 tests
poorFit is a branch of the note chain, not just a result field2.7ms
states how many of how many, so the user can check the claim0.7ms
delivers rather than withholds — they asked for these and they get them0.7ms
offers BOTH repairs, matching the pre-spend card0.3ms
only fires on a real sample — three scored leads, majority under 400.3ms
product-brief-read.vitest.ts
5/5 10ms · 1 suite PASS
src/leads/product-brief-read.vitest.ts
getProductBriefStatus — absence and failure are different facts · 5 tests
a tenant with no brief reads as absent, NOT as a failure5.3ms
a THROWN read is reported as a failure, not as an empty tenant0.9ms
a failed read is never silent — it reaches Sentry0.6ms
returns the brief text when one is on file1.3ms
getProductBrief keeps its old shape for the 10 callers that only want the text0.7ms
search-query.vitest.ts
5/5 7ms · 1 suite PASS
src/leads/search-query.vitest.ts
normalizeLeadQuery · 5 tests
returns '' for the shapes that crashed extractPersona3.3ms
treats whitespace-only as absent — ' '.toLowerCase() would not throw, but it is not a query0.6ms
coerces non-string args the LLM can emit rather than passing them through0.6ms
preserves a real query verbatim, trimming only the edges0.7ms
keeps inner casing and punctuation — downstream persona extraction lowercases its own copy1.1ms
adoption-context.vitest.ts
5/5 5ms · 1 suite PASS
src/planner/adoption-context.vitest.ts
adaptPlannerAdoption (ADAPTER over the adoption module) · 5 tests
derives readiness from the module rows — blocked wins when a prerequisite is false3.9ms
joins the planner recommendation history onto families0.5ms
collects suppressed actions from dismissed + not_run rows only0.6ms
reuses the adoption module profile summary verbatim0.4ms
summary lines carry history + missing prerequisites0.4ms
article-copy.vitest.ts
5/5 5ms · 1 suite PASS
src/reports/article-copy.vitest.ts
the article artifact offers a copy button · 5 tests
renders one, labelled for the article rather than a prompt2.3ms
copies the markdown source, title included0.9ms
is not the rendered HTML0.6ms
needs no client wiring — the artifact is served back from KV byte-for-byte0.4ms
renders nothing when there is no article to copy0.9ms
godmode-truncation.vitest.ts
5/5 7ms · 1 suite PASS
src/reports/godmode-truncation.vitest.ts
buildGodModeInsights — truncated analysis · 5 tests
renders every recommendation and no disclosure when the model finished4.2ms
drops the cut-off final recommendation instead of rendering half a sentence0.6ms
discloses that the list is incomplete0.6ms
never claims truncation when the model produced nothing and the fallback rendered0.7ms
keeps a lone truncated recommendation rather than emptying the section0.5ms
guardrail-scan.vitest.ts
5/5 42ms · 1 suite PASS
src/reports/guardrail-scan.vitest.ts
publishReportChip — guardrail scan · 5 tests
redacts a vendor name embedded in report HTML before saving36.8ms
redacts a USD figure for a non-tenant-currency tool1.6ms
does NOT redact USD for a tenant-currency tool (the user's own revenue)1.6ms
BLOCKs and replaces the artifact on a secret leak, and reports it loudly1.3ms
leaves clean HTML untouched1.0ms
sov-render.vitest.ts
5/5 8ms · 2 suites PASS
src/reports/sov-render.vitest.ts
composite render probe · 1 test
composite shape renders SOV without undefined leakage4.3ms
aeo_visibility — prompt transparency + next steps (feedback 2026-07-16) · 4 tests
surfaces the exact prompts sent (methodology transparency)1.0ms
renders a Recommended next steps section with Copy-Fix-Prompt buttons0.7ms
§17: signal-overview bento (every dimension), plain headers, feedback mount1.2ms
empty-signal run still yields at least one concrete next step0.9ms
live-stage.vitest.ts
5/5 9ms · 1 suite PASS
src/runtime/live-stage.vitest.ts
stageSequence (every stage reaches a terminal state) · 5 tests
completes the previous stage before starting the next4.8ms
closes out the LAST stage on done() — the exact gap the incident exposed0.7ms
done() is a no-op when no stage is active1.6ms
supports closing the last stage as failed0.6ms
is a no-op entirely when reqCtx.progress is null (outside interactive chat turns)0.5ms
push-channel.vitest.ts
5/5 45ms · 1 suite PASS
src/runtime/push-channel.vitest.ts
pushToUser — guardrail scan on event.title · 5 tests
redacts a vendor name in the title38.6ms
leaves a clean, user-derived title untouched1.6ms
BLOCKs and replaces the title on a secret leak, never sends the raw value1.7ms
does not redact USD for a tenant-currency tool0.8ms
passes through events with no title unchanged (job_failed has none)1.6ms
add-contacts-surface.vitest.ts
5/5 12ms · 1 suite PASS
src/tools/add-contacts-surface.vitest.ts
add_contacts surface · 5 tests
is schematised, requires contacts[], and tells the model not to substitute a search5.3ms
list_contacts points at it, so a model reading either schema knows where saving lives0.6ms
is classified as a tenant write, and is dispatched through the ONE contact writer with source manual3.4ms
presents the saved rows as a table whose lead states added vs already there1.7ms
returns nothing to present when nothing was saved, so the error text is what the user reads0.5ms
list-sent-emails-surface.vitest.ts
5/5 17ms · 1 suite PASS
src/tools/list-sent-emails-surface.vitest.ts
list_sent_emails surface · 5 tests
is schematised, bounded, a read, in the campaigns family, and preloaded by "sent emails"4.7ms
campaign_stats and list_campaigns point at it, so the aggregate tools know where the log lives0.4ms
is dispatched as a tenant-scoped read of emails_sent, newest first, with a count aggregate3.4ms
presents one row per email with sent/opened/replied/bounced times and an honest page lead1.2ms
the formatter says "nothing sent" plainly and names the outcome per row6.8ms
honest-stop-brief.vitest.ts
5/5 5ms · 1 suite PASS
src/seo/honest-stop-brief.vitest.ts
the honest stop hands back the research it already charged for · 5 tests
returns the brief when one was generated2.7ms
does not tell the user to supply a brief it is already handing them0.7ms
keeps the original advice when there is genuinely no brief to return0.5ms
still reads as transient in both shapes, because it is0.6ms
names no backend vendor in either shape (CLAUDE.md §4)0.5ms
onpage-coverage-note.vitest.ts
5/5 4ms · 1 suite PASS
src/seo/onpage-coverage-note.vitest.ts
the on-page report footer · 5 tests
does not claim coverage for the live case — a tenant who never connected Google3.0ms
claims coverage ONLY when coverage was actually read0.5ms
a thrown/absent read still asks for the connection rather than claiming one0.3ms
never tells a CONNECTED tenant to connect Google0.5ms
an unrecognised reason still refuses to claim coverage0.2ms
seo-answers-library.vitest.ts
5/5 65ms · 1 suite PASS
src/seo/seo-answers-library.vitest.ts
SEO Answers is registered and reachable · 5 tests
exists in the prompt library, which is also the public capability catalog4.8ms
every published prompt routes to EXACTLY ONE brief33.8ms
no two prompts land on the same brief4.9ms
is priced at the diagnostic floor with NO provider term2.6ms
does not duplicate a question already listed elsewhere17.9ms
serp-spider-poll.vitest.ts
5/5 7ms · 1 suite PASS
src/seo/serp-spider-poll.vitest.ts
seo_serp_spider inline poll · 5 tests
declares a bounded inline poll rather than using the full deadline3.9ms
is a small fraction of the tool budget — the user learns quickly, not eventually0.7ms
the unbounded deadline it replaced really was near-budget0.4ms
leaves seo_onpage_audit on the full deadline, because its poll works1.0ms
still falls through to the durable two-turn contract0.7ms
corpus-filter-honesty.vitest.ts
5/5 8ms · 2 suites PASS
src/leads/shared/corpus-filter-honesty.vitest.ts
a row with no person cannot satisfy a filter on job title · 3 tests
drops the organization row that has nobody in it2.0ms
keeps the person0.4ms
does NOT drop organizations when no title was asked for0.3ms
the filter names we send must be the ones the RPC reads · 2 tests
translates employeeMin/employeeMax into the keys migration 136 actually parses1.7ms
leaves every other filter name exactly as it was3.3ms
legacy-margin.vitest.ts
4/4 6ms · 1 suite PASS
src/billing/legacy-margin.vitest.ts
legacy billing margin · 4 tests
retail is $2.00 per million platform tokens2.6ms
every model whose INPUT rate alone meets retail is an ACCEPTED loss-maker1.2ms
the accepted list is not padded with models that actually make money1.8ms
free models are not counted as loss-makers — they have their own floor0.5ms
quote-on-run-row.vitest.ts
4/4 6ms · 1 suite PASS
src/billing/quote-on-run-row.vitest.ts
the quote of record · 4 tests
is a request-context field, reset per request3.4ms
is set by the approval plan, by the agent gate for ungated tools, and by the confirm replay1.3ms
lands on the tool_run ledger row as quoted_tokens0.6ms
is what the calibration ratchet judges an args-priced tool by, once enough runs carry it0.4ms
signup-bonus.vitest.ts
4/4 102ms · 1 suite PASS
src/billing/signup-bonus.vitest.ts
the signup bonus is defined exactly once · 4 tests
has ONE definition across src/**98.4ms
is 1,000,000 — what the public pricing pages promise1.2ms
the waitlist 5,000,000 is gone, not merely unused1.2ms
a free-tier lead search fits inside it0.8ms
token-math.vitest.ts
4/4 4ms · 2 suites PASS
src/billing/token-math.vitest.ts
apiCostToTokens — provider cost → token conversion · 3 tests
uses the canonical cost basis2.4ms
converts a per-unit provider cost to whole tokens0.7ms
rounds to whole tokens and handles zero0.3ms
fmtTokens — human-readable token counts · 1 test
formats thousands and millions, raw below 1K0.3ms
ai-discovery.vitest.ts
4/4 4ms · 1 suite PASS
src/admin/ai-discovery.vitest.ts
mergeProviderOutcomes · 4 tests
keeps every provider result when all three settle, regardless of arrival order2.8ms
all three can independently fail without any of them being lost0.5ms
a truly unexpected rejection (not the inner try/catch) still produces a failed entry, not a silent gap0.4ms
the entire provider set is always present in the output, one write, no partial merges needed0.3ms
geo-scorecard-bulk.vitest.ts
4/4 59ms · 1 suite PASS
src/admin/geo-scorecard-bulk.vitest.ts
handleAdminGeoScorecardBulk · 4 tests
rejects requests without a valid admin secret51.8ms
calls runGeoScorecard directly for each URL — no rate limiter applied5.3ms
rejects more than the per-request URL cap1.1ms
rejects an empty urls array1.3ms
session-key.vitest.ts
4/4 6ms · 1 suite PASS
src/admin/session-key.vitest.ts
no control characters in the join · 4 tests
sessions.ts contains no NUL byte2.7ms
and no other non-printable control character2.9ms
the key is built by ONE named helper, not three inline templates0.8ms
the separator is visible and cannot occur inside an id0.3ms
telemetry-sentry-issues.vitest.ts
4/4 9ms · 1 suite PASS
src/admin/telemetry-sentry-issues.vitest.ts
fetchSentryIssues — F2 window scaling · 4 tests
scales statsPeriod to the requested days, not a fixed 14d5.0ms
caps statsPeriod at Sentry's 90d ceiling for a wider window1.1ms
requests the raised ceiling, not the old hardcoded 101.0ms
defaults to 14d when no days argument is given (back-compat for other callers)0.6ms
telemetry-trim-placement.vitest.ts
4/4 104ms · 1 suite PASS
src/admin/telemetry-trim-placement.vitest.ts
buildAdminTelemetryData / trimClientPayload placement · 4 tests
buildAdminTelemetryData returns the FULL quality_scores array, untrimmed32.6ms
the RCA analyst's own read pattern (filter quality_scores for ensemble rows) sees ALL matches, not a 500-row slice14.7ms
the HTTP handler (the only actual browser-facing caller) DOES trim, to 50054.9ms
trimClientPayload itself is unchanged — still trims when called directly1.4ms
waitlist-paging.vitest.ts
4/4 5ms · 1 suite PASS
src/admin/waitlist-paging.vitest.ts
waitlist list paging · 4 tests
reports has_more when the over-fetched row comes back3.8ms
does NOT report has_more on an exactly-full final page — the off-by-one that would strand a Next button on an empty page0.7ms
never leaks the over-fetched row into the page0.9ms
handles a short page and an empty page0.5ms
send-recovery.vitest.ts
4/4 17ms · 1 suite PASS
src/campaigns/send-recovery.vitest.ts
the turn still offers a way forward · 4 tests
offers drafting when there is nothing to send6.1ms
offers the drafts panel when the ids were wrong1.4ms
never offers a confirm chip on a failed send — there is nothing to confirm0.5ms
states the failure rather than describing a send8.7ms
sequence-intent.vitest.ts
4/4 13ms · 2 suites PASS
src/campaigns/sequence-intent.vitest.ts
isFullySpecifiedSequenceAsk · 3 tests
a named or spaced ask is the tool's, not the picker flow's3.4ms
a bare ask still goes through the pickers0.6ms
is consulted by the state machine gate6.0ms
create_sequence tells the model what delay_days means · 1 test
names the PREVIOUS step and gives the 1/3/5 example ([6.2.1] 2026-09-15: 0/1/3)1.9ms
account-export.vitest.ts
4/4 68ms · 1 suite PASS
src/auth/account-export.vitest.ts
handleAccountExport · 4 tests
reads every table scoped by the caller and returns a download62.6ms
never selects the columns the user role is denied1.4ms
a table that cannot be read is reported in place, not dropped silently2.0ms
is rate-limited: over the cap is 429, limiter down is 5031.5ms
signup.vitest.ts
4/4 53ms · 1 suite PASS
src/auth/signup.vitest.ts
auth signup helpers · 4 tests
prefers CF-Connecting-IP for signup requests52.3ms
normalizes gmail aliases before signup validation0.5ms
keeps the auth credential email intact (no dot/plus stripping)0.3ms
never lets an email pass as a display name0.5ms
revenue-reconciliation.vitest.ts
4/4 12ms · 1 suite PASS
src/commerce/revenue-reconciliation.vitest.ts
buildRevenueReconciliation · 4 tests
aligned verdict within 10%, comparable figure includes tax+shipping8.2ms
GA4 far below store = ga4_undercount (measurement gap, per the google_merge lesson)1.3ms
refunds and non-storefront channels appear as quantified explanation factors0.8ms
degrades gracefully when GA4 errors: store numbers still returned0.8ms
shopify-client.vitest.ts
4/4 6ms · 2 suites PASS
src/commerce/shopify-client.vitest.ts
parseGid · 2 tests
extracts numeric ids from admin gids2.5ms
returns null for junk0.5ms
parseInventoryLevelId · 2 tests
decodes location + inventory item from the InventoryLevel gid (the read_locations dodge)0.9ms
degrades to nulls on unexpected shapes0.7ms
core.vitest.ts
4/4 5ms · 1 suite PASS
src/connectors/core.vitest.ts
Apollo partner connector · 4 tests
is flagged as a partner with a CTA and default link3.3ms
resolveSignupUrl falls back to the committed default0.5ms
env.APOLLO_PARTNER_URL overrides the default (rotatable link)0.3ms
non-partner connectors have no signup link0.3ms
mark-sent.vitest.ts
4/4 39ms · 2 suites PASS
src/drip/mark-sent.vitest.ts
SentDrip records the send · 3 tests
never passes null for channel4.5ms
omits the field entirely on the platform transport, and names it on Gmail1.1ms
the mutation no longer declares a $channel variable it cannot fill0.5ms
the class, not just the instance · 1 test
no _set anywhere writes a literal null into emails_sent.channel31.9ms
dictated-draft.vitest.ts
4/4 7ms · 1 suite PASS
src/email/dictated-draft.vitest.ts
dictated email — transcribed, not generated · 4 tests
parses the [2.1.2] request exactly: subject, body, values4.0ms
fills placeholders from the user first, then the contact, and leaves the rest visible1.4ms
needs BOTH a subject and a body — a subject alone is a brief for the generator0.5ms
accepts unquoted and curly-quoted forms1.0ms
backlink-value-two-lenses.vitest.ts
4/4 44ms · 1 suite PASS
src/chat/backlink-value-two-lenses.vitest.ts
seo_backlink_value: two lenses · 4 tests
leads with both readings, each named, and the table carries earned beside cost to buy30.4ms
the live case: visits but no revenue on the property → says so for the earned reading, keeps the cost reading1.2ms
no Analytics → the earned reading is "not measured" with the fix, never a number1.0ms
the report hero leads with the earned reading too11.1ms
connector-chip-labels.vitest.ts
4/4 10ms · 1 suite PASS
src/chat/connector-chip-labels.vitest.ts
hand-wired connector chips name the connector (spec §5.4) · 4 tests
the email readiness audit on a Cloudflare-hosted, unconnected zone offers Connect Cloudflare5.0ms
every email-deliverability branch that points at Cloudflare says so3.1ms
Slack and Vercel failures name their own connector1.2ms
the two tools whose whole point is the panel keep "Open Connectors"0.5ms
feedback.vitest.ts
4/4 3ms · 1 suite PASS
src/chat/feedback.vitest.ts
chat feedback · 4 tests
normalizeFeedbackOptions dedupes + trims1.8ms
normalizeFeedbackOptions falls back on bad input0.2ms
buildFeedbackSummary aggregates totals, leaderboard, and mismatches1.3ms
judge_human_agreement is null when no row has both a vote and a judge score0.2ms
jev-judge.vitest.ts
4/4 14ms · 1 suite PASS
src/chat/jev-judge.vitest.ts
the shadow verdict · 4 tests
asks a five-band score and one failure mode from the product's own taxonomy5.5ms
maps the band position onto the judge's 0–1 scale and keeps the taxonomy honest1.8ms
decides nothing: not rolled out or failed → null → no columns2.1ms
rides beside the real verdict on both judge paths, conversation turns only4.9ms
judge-fixtures.vitest.ts
4/4 45ms · 1 suite PASS
src/chat/judge-fixtures.vitest.ts
judge-fail fixture pipeline — oracle cross-check · 4 tests
non-forensic failures are ignored (no write)4.2ms
CONFIRMED: judge forensic-fail AND contracts also flag → clean regression fixture38.3ms
CONTRACT_GAP: judge forensic-fail but contracts PASS → the judge caught what code missed1.4ms
captured fixtures are enumerable newest-first for triage1.0ms
narration-degrade.vitest.ts
4/4 5ms · 1 suite PASS
src/chat/narration-degrade.vitest.ts
a failed narration does not throw away the tool results · 4 tests
degrades instead of rethrowing when tools already ran3.0ms
still throws when NOTHING ran, because there is nothing to degrade to0.6ms
tells the user the summary is machine-rendered0.9ms
is distinguishable from an ordinary silent turn in telemetry0.4ms
pair-completion.vitest.ts
4/4 4ms · 1 suite PASS
src/chat/pair-completion.vitest.ts
pair completion — two nouns are two calls · 4 tests
supplies the half the model skipped, in either direction2.4ms
does nothing when both ran, or when neither ran0.5ms
needs BOTH nouns in the message — one listing alone is a complete answer0.5ms
is not fooled by unrelated tools having run0.3ms
silent-turn-diagnosis.vitest.ts
4/4 10ms · 1 suite PASS
src/chat/silent-turn-diagnosis.vitest.ts
a diagnosis outranks whatever ran last · 4 tests
returns the diagnosis, not the incidental closing tool2.8ms
falls back to the last tool when no diagnosis ran7.0ms
ignores a diagnose result that errored or said nothing0.6ms
does not shadow a genuine tool error0.3ms
skill-approval-gate.vitest.ts
4/4 13ms · 2 suites PASS
src/chat/skill-approval-gate.vitest.ts
the gate exists at all · 2 tests
search_leads is always-confirm, so a skill step naming it must never self-execute2.6ms
the threshold used by the halt is the same one the chat gate uses0.6ms
runSkill halts on a step that needs approval · 2 tests
does not call executeTool for an always-confirm step1.9ms
still runs a step that costs nothing8.1ms
list-attach.vitest.ts
4/4 9ms · 2 suites PASS
src/leads/list-attach.vitest.ts
upsertContactList survives the DO-NOTHING null · 3 tests
returns the id on first creation3.5ms
falls back to SELECT when the list already exists — the incident case0.8ms
returns null only when the list genuinely does not exist either way0.9ms
the full save attaches memberships on a SECOND save into the same list · 1 test
replays the incident: existing list, 3 contacts, memberships must be written4.5ms
verify-changes.vitest.ts
4/4 7ms · 1 suite PASS
src/leads/verify-changes.vitest.ts
verifyChangeRows · 4 tests
keeps only the addresses whose verdict actually moved4.2ms
NEVER reports a move to unverified0.5ms
drops rows missing either side of the comparison0.5ms
is empty for an empty or absent result set1.2ms
initiative-tenant-scope.vitest.ts
4/4 7ms · 1 suite PASS
src/planner/initiative-tenant-scope.vitest.ts
updateInitiativeStatus tenant scope · 4 tests
refuses to transition an initiative owned by someone else3.4ms
allows the real owner1.1ms
carries user_id into the mutation predicate, not just the read check1.4ms
selects user_id in the read so ownership is checkable at all0.6ms
markdown-negotiation.vitest.ts
4/4 61ms · 2 suites PASS
src/routes/markdown-negotiation.vitest.ts
requestWantsMarkdown · 2 tests
detects an Accept header requesting text/markdown46.8ms
is false for a normal browser Accept header0.8ms
maybeServeMarkdown · 2 tests
passes through untouched when the client did not ask for markdown3.3ms
renders a <summary> as a heading, distinct from its answer paragraph9.8ms
website-health-check.vitest.ts
4/4 61ms · 3 suites PASS
src/routes/website-health-check.vitest.ts
serveWebsiteHealthCheck SEO metadata · 1 test
renders all required SEO, Open Graph, and Twitter metadata47.4ms
website health check rate limiting · 2 tests
limits to 2 usage per IP per day and blocks the 3rd2.8ms
handleWebsiteHealthApi returns 429 when IP exceeds 2 checks3.4ms
runWebsiteHealth AEO and DEO signals · 1 test
extracts schema, social cards, tables, pricing, policy, and cta signals7.5ms
async-jobs.vitest.ts
4/4 3ms · 2 suites PASS
src/runtime/async-jobs.vitest.ts
async-jobs tier declaration · 2 tests
enrich_contacts runs INLINE, never backgrounded (renders the card + gets judged)1.9ms
still backgrounds the genuine long report tools0.4ms
isBackgroundAckMessage · 2 tests
matches the real background-ack copy0.4ms
does NOT match ordinary answers or empty input0.3ms
map-limit.vitest.ts
4/4 46ms · 1 suite PASS
src/runtime/map-limit.vitest.ts
mapLimit · 4 tests
never runs more than `limit` at once11.0ms
preserves input order regardless of completion order32.5ms
handles an empty list and a limit above the item count1.1ms
runs every item — the batch is bounded, not truncated0.8ms
vendor-parity.vitest.ts
4/4 20ms · 1 suite PASS
src/runtime/vendor-parity.vitest.ts
the eval vendor check is derived, not retyped · 4 tests
parses every name the product declares17.0ms
detects the escaped names, which a raw parse silently misses1.0ms
does NOT flag the tenant's own mail provider in an SPF fix0.5ms
honours the product's own allow-list0.8ms
zero-action-probe.vitest.ts
4/4 10ms · 1 suite PASS
src/runtime/zero-action-probe.vitest.ts
zero-action probe: expected refusals are not non-answers · 4 tests
skips the exact NQZAI-6R payload — the balance gate refusing3.6ms
skips other economic gates and prerequisite asks2.6ms
does NOT skip a real non-answer — the case the probe exists to catch2.4ms
does NOT skip an EMPTY reply0.6ms
bug-report-wiring.vitest.ts
4/4 11ms · 2 suites PASS
src/ui/bug-report-wiring.vitest.ts
bug reporter — every global it reads is actually written · 1 test
has no read-only window.__GLOBAL__ in the report payload8.5ms
bug reporter — the capture cannot silently stop working · 3 tests
normalises modern colour functions in onclone0.7ms
captures the viewport, not the whole scroll height0.5ms
never blocks the report on the screenshot0.5ms
aeo-gap-ceiling.vitest.ts
4/4 3ms · 2 suites PASS
src/seo/aeo-gap-ceiling.vitest.ts
aeo_gap generation ceiling · 3 tests
asks for 900 tokens, not the 400 that truncated ~88% of real generations1.8ms
does NOT pass a reasoning option — it inherits the router default deliberately0.3ms
still routes on the seo chain at the same temperature0.4ms
the truncation disclosure the ceiling exists to stop firing · 1 test
still discloses when a gap analysis is cut off0.4ms
backlink-gap-snapshot-shape.vitest.ts
4/4 9ms · 1 suite PASS
src/seo/backlink-gap-snapshot-shape.vitest.ts
the gap reads the shape the database actually stores · 4 tests
finds our referring domains under backlink_profile, not at the top level3.6ms
produces a MEASURED gap from a real snapshot — not no_baseline1.9ms
still reads a FLAT snapshot, so older rows are not orphaned0.4ms
the dispatcher actually performs the nested read2.8ms
content-brief-reasoning.vitest.ts
4/4 5ms · 2 suites PASS
src/seo/content-brief-reasoning.vitest.ts
seo_content_brief disables hidden reasoning · 3 tests
passes reasoning:{enabled:false} on the brief generation call2.0ms
keeps the 600-token ceiling — the budget was never the problem0.4ms
still routes on the seo chain at the same temperature0.4ms
the brief feeds a second paid call, which is why an empty one is not a local failure · 1 test
seo_write_content still auto-runs the brief and injects it as briefContext0.6ms
cron-fair-ordering.vitest.ts
4/4 17ms · 1 suite PASS
src/seo/cron-fair-ordering.vitest.ts
rank cron: least-recently-scanned goes first · 4 tests
sorts `eligible` before phase 2 spends anything2.5ms
reads the last scan per user in ONE query, not per user1.9ms
a never-scanned tenant sorts ahead of every scanned one12.3ms
falls back to the old order rather than skipping the run when the read fails0.7ms
geo-scorecard-fabrication-check.vitest.ts
4/4 106ms · 1 suite PASS
src/seo/geo-scorecard-fabrication-check.vitest.ts
geo_scorecard — Unverified NQZAI feature claims (self-audit only) · 4 tests
flags a fabricated named module attributed to nqzai74.1ms
does not flag a real, allowlisted nqzai capability10.6ms
does not flag a third-party term like Knowledge Graph mentioned alongside nqzai8.7ms
does not run this check at all for a third-party URL11.6ms
geo-scorecard-self-fetch.vitest.ts
4/4 88ms · 1 suite PASS
src/seo/geo-scorecard-self-fetch.vitest.ts
geo_scorecard self-zone fetch (SELF service binding) · 4 tests
fetches an nqz.ai URL through env.SELF, never the public edge69.4ms
falls back to a plain fetch for an nqz.ai URL when SELF is not bound (e.g. local wrangler dev)8.6ms
routes an nqz.ai/blog/* URL through BLOG_WORKER, not SELF6.1ms
does not route a third-party URL through SELF3.6ms
google-merge-totals.vitest.ts
4/4 31ms · 1 suite PASS
src/seo/google-merge-totals.vitest.ts
the merged report states its totals once, each named for its population · 4 tests
sums the joined rows and reads the all-channel figure from attribution27.1ms
falls back to the channel rows when attribution carries no total0.9ms
an attribution leg that failed yields NULL for the all-channel figure — never a zero that reads as no traffic0.6ms
the totals sit at the head of the payload, before the rows1.7ms
index-coverage.vitest.ts
4/4 69ms · 1 suite PASS
src/seo/index-coverage.vitest.ts
computeIndexCoverage · 4 tests
buckets indexed vs not-indexed from ValueSERP probes (free tier)45.1ms
caps the free tier at 50 URLs11.1ms
paid tier lifts the cap10.7ms
enriches the not-indexed subset with a reason when GSC access is provided1.7ms
keyword-rung-degrade.vitest.ts
4/4 6ms · 2 suites PASS
src/seo/keyword-rung-degrade.vitest.ts
the ladder falls through when its last rung fails · 2 tests
fromApify catches instead of propagating2.7ms
degrades to an EMPTY map, so an unresolved keyword carries no volume rather than zero1.3ms
the provider gives up before the tool budget does · 2 tests
bounds the keyword-volume call below the 30s tool wall-clock0.7ms
leaves the client default alone — this is a per-call ceiling, not a global one1.0ms
prompt-fit.vitest.ts
4/4 22ms · 1 suite PASS
src/seo/prompt-fit.vitest.ts
prompt fit · 4 tests
one noul per prompt, naming the brief and the prompt by path, with the false cases spelled out3.9ms
returns null — every prompt ticked — when the flag is off or the brief is empty0.9ms
the threshold and the cap are the ones the client and the server share8.4ms
the picker carries prompt_fit and the selector unticks off-brief prompts without dropping them9.1ms
query-helpers.vitest.ts
4/4 6ms · 1 suite PASS
src/seo/query-helpers.vitest.ts
pickGscPropertyForHost · 4 tests
resolves the property matching the audited host, not the default3.6ms
matches URL-form properties too0.4ms
returns null when the host has no verified property (caller falls back to site_url)0.7ms
extractGoogleSiteHost handles sc-domain + url forms0.7ms
ingest-errors.vitest.mjs
4/4 4ms · 1 suite PASS
scripts/lib/ingest-errors.vitest.mjs
ingest error classification · 4 tests
treats a manifest_sha256 collision as already-ingested, not as a failure2.6ms
does NOT swallow a different uniqueness violation that means real data loss0.5ms
does not fire on unrelated failures0.5ms
exports the constraint name so the script and the test cannot drift apart0.3ms
ingestion-service.vitest.ts
4/4 43ms · 1 suite PASS
src/leads/shared/ingestion-service.vitest.ts
shared lead ingestion service · 4 tests
persists an approved batch using parameterized values and a transactional outbox37.7ms
rejects unauthorized, disallowed, and invalid batches before any client call2.1ms
uses the curated ingest mutation as the only database boundary and exposes an exact outbox command when atomic outbox is unavailable2.0ms
writes invalid verification, tombstone, and identifier suppression as one idempotent command1.3ms
multitenant.vitest.ts
3/3 4ms · 1 suite PASS
src/multitenant.vitest.ts
multitenant scoping · 3 tests
assertUserScope fails closed on missing userId2.5ms
blocks cross-tenant reads0.4ms
blocks cross-tenant deletes0.5ms
observability-cron-monitor.vitest.ts
3/3 5ms · 1 suite PASS
src/observability-cron-monitor.vitest.ts
cron monitors track the schedule the worker actually runs · 3 tests
reads both sides (a test that finds nothing is green for the wrong reason)2.5ms
every monitored schedule matches a worker cron, shifted by the measured day-of-week offset1.2ms
the check-in payload carries monitor_config, so the schedule is upserted from code0.6ms
adoption-email.vitest.ts
3/3 5ms · 1 suite PASS
src/admin/adoption-email.vitest.ts
finalizeAdoptionBody · 3 tests
appends a styled CTA button, not a bare URL3.3ms
strips a raw link the model wrote anyway despite the instruction not to0.8ms
keeps the greeting and body as separate paragraphs (blank-line preserved)0.3ms
feedback-fixlog.vitest.ts
3/3 67ms · 1 suite PASS
src/admin/feedback-fixlog.vitest.ts
feedback fixlog · 3 tests
every entry is well-formed51.3ms
entries are in chronological order (append-at-bottom discipline)2.1ms
prompt section carries every entry and the verification instruction14.4ms
feedback-rca-reasoning.vitest.ts
3/3 6ms · 1 suite PASS
src/admin/feedback-rca-reasoning.vitest.ts
feedback_rca opts back into reasoning · 3 tests
passes reasoning:{enabled:true} explicitly3.4ms
keeps the large ceiling that was sized to hold the reasoning1.0ms
the prompt still contains the derive-before-answering instruction the opt-in rests on0.6ms
digest.vitest.ts
3/3 22ms · 2 suites PASS
src/commerce/digest.vitest.ts
isoWeek · 1 test
stable ISO-8601 week ids across year boundaries2.3ms
commerce_weekly_digest template · 2 tests
renders revenue delta, margin+coverage, and attention items in the store currency18.0ms
no prior-week revenue → no fabricated delta; clean stores get no attention section0.6ms
composio.vitest.ts
3/3 62ms · 1 suite PASS
src/connectors/composio.vitest.ts
composioFetch — 429 retry · 3 tests
retries a 429 with backoff and succeeds once the rate limit clears55.3ms
gives up after exhausting retries if still rate-limited4.7ms
does not retry a non-429 error — fails on the first attempt1.5ms
send-failure-reason.vitest.ts
3/3 8ms · 1 suite PASS
src/email/send-failure-reason.vitest.ts
humanizeSendFailure · 3 tests
the live [2.4.2] body: provider JSON becomes a sentence with no vendor voice3.9ms
known shapes map; unknown shapes keep the message minus URLs and asides1.9ms
is the reason the send path records1.6ms
transactional-fallback.vitest.ts
3/3 15ms · 1 suite PASS
src/email/transactional-fallback.vitest.ts
transactional fallback chain · 3 tests
falls back to Cloudflare when Mailtrap declines, and marks it sent via cloudflare11.3ms
accumulates BOTH providers errors in last_error when all fail2.1ms
does not include the dead XSMTP provider in the chain1.4ms
agent-context-lines.vitest.ts
3/3 6ms · 2 suites PASS
src/chat/agent-context-lines.vitest.ts
agent ground-truth context lines · 2 tests
the saved competitor set is injected by name and joined into the brief status text3.8ms
the admin judge accepts caller-supplied stored context and puts it before the prompt1.3ms
a spend stand-down turn carries a structural marker to the client (2026-09-15) · 1 test
runChatV2 returns spendBlocked and the v2 payload surfaces spend_blocked0.7ms
present-aeo-page-check.vitest.ts
3/3 7ms · 1 suite PASS
src/chat/present-aeo-page-check.vitest.ts
aeo_page_check presenter · 3 tests
three scores with one name each, six predictors, and what cost points6.2ms
no target query → coverage is "not measured", never 0%0.9ms
an errored or scoreless result presents nothing0.4ms
reminders.vitest.ts
3/3 9ms · 1 suite PASS
src/lifecycle/reminders.vitest.ts
touchUserActivity presence write · 3 tests
first activity writes a user_presence upsert with geo6.1ms
activity within the 10-min window skips the DB write1.1ms
activity past the window writes again0.9ms
token_hook.vitest.ts
3/3 3ms · 1 suite PASS
src/middleware/token_hook.vitest.ts
token_hook · 3 tests
parses model usage payload2.3ms
merges usage accumulators0.4ms
suggests a cheaper model on large completions0.3ms
merge-report.vitest.ts
3/3 3ms · 1 suite PASS
src/reports/merge-report.vitest.ts
seo_google_merge — §17 gold standard · 3 tests
aggregates opportunities into a findings bento with fix prompts2.4ms
restores the REAL daily-sales trend (regression from the fabricated-sparkline sweep)0.4ms
has a feedback mount and no "Cluster N" section headers0.4ms
titles.vitest.ts
3/3 3ms · 1 suite PASS
src/reports/titles.vitest.ts
report titles · 3 tests
every report-class tool has a human title (no snake_case, no bare tool name)2.4ms
appends the site and prefers an article title for written content0.6ms
humanizes unmapped tools instead of leaking snake_case0.4ms
health.vitest.ts
3/3 7ms · 1 suite PASS
src/routes/health.vitest.ts
healthVerdict · 3 tests
is 200 ok while the infra cron reports healthy4.9ms
is 503 degraded once the cron has seen the database down, and says since when1.0ms
reports null checked_at when the cron has never written (unknown is not down)0.6ms
nap-consistency-checker.vitest.ts
3/3 69ms · 1 suite PASS
src/routes/nap-consistency-checker.vitest.ts
nap-consistency-checker demo normalizers stay in sync with src/seo/nap.ts · 3 tests
normAddress: demo transcription matches the real function on every known pair61.1ms
normPhone: demo transcription matches the real function on every known pair4.6ms
still reports a genuine conflict as a conflict, not a false match3.3ms
async-jobs-guardrail.vitest.ts
3/3 11ms · 1 suite PASS
src/runtime/async-jobs-guardrail.vitest.ts
completeAsyncJob — guardrail scan on summarizeResult() · 3 tests
redacts a vendor name in result.message before it reaches the completion email7.3ms
leaves ordinary result.message untouched2.8ms
BLOCKs a secret embedded in the result before it reaches the completion email1.1ms
send-rate-limit.vitest.ts
3/3 6ms · 1 suite PASS
src/runtime/send-rate-limit.vitest.ts
checkSendRateLimit · 3 tests
allows under the ceiling and refuses over it, naming the reason2.6ms
fails CLOSED when KV throws, and the refusal says the limiter is unavailable — not that the limit was hit2.6ms
is inactive when KV is unbound (local dev, tests)0.5ms
tenant-figures-on-scan-turn.vitest.ts
3/3 14ms · 1 suite PASS
src/runtime/tenant-figures-on-scan-turn.vitest.ts
the tenant's own figures survive the rail when the brief is in the owned text · 3 tests
without the brief the prices are rewritten; with it they stand7.1ms
nqzai's own price is still never a dollar figure, brief or no brief0.5ms
every path that produces a brief owns it before the reply is scanned6.4ms
backlink-gap-readiness.vitest.ts
3/3 4ms · 1 suite PASS
src/seo/backlink-gap-readiness.vitest.ts
backlinkBaselineNote — only claims what it can still know before spending · 3 tests
warns when no site is set — the one case the user can fix before paying3.4ms
stays SILENT when a site is set, instead of predicting a number it is about to measure0.3ms
never tells the user to run an off-page audit to fix the gap basis0.4ms
common-crawl-fallback.vitest.ts
3/3 48ms · 1 suite PASS
src/seo/common-crawl-fallback.vitest.ts
Common Crawl fallback · 3 tests
reports a not-yet-indexed site as NOT INDEXED, not as an outage44.4ms
still reports a REAL outage as an outage1.9ms
parses a live-shaped success response2.4ms
content-brief-grounding.vitest.ts
3/3 3ms · 1 suite PASS
src/seo/content-brief-grounding.vitest.ts
seo_content_brief tenant grounding · 3 tests
reads the product brief and site from settings inside the case2.6ms
the prompt carries the TENANT CONTEXT block with the grounding instruction0.4ms
an absent brief is disclosed on the result, never silently written for the keyword alone0.2ms
diagnose-headline-producers.vitest.ts
3/3 7ms · 1 suite PASS
src/seo/diagnose-headline-producers.vitest.ts
diagnose: the headline consults every brief producer · 3 tests
has exactly one headline-selection expression to reason about2.6ms
consults the traffic-incident brief, which is built outside the dgAnswer chain0.9ms
every dg* brief is reachable from the headline: either in the chain or named explicitly3.6ms
diagnose-payload-parity.vitest.ts
3/3 7ms · 1 suite PASS
src/seo/diagnose-payload-parity.vitest.ts
both diagnose return paths carry the same briefs · 3 tests
finds exactly the two payload lists3.6ms
neither list is missing a brief the other has1.5ms
includes the newest brief, so the guard is demonstrably live1.8ms
geo-scorecard-reasoning.vitest.ts
3/3 54ms · 1 suite PASS
src/seo/geo-scorecard-reasoning.vitest.ts
geo_scorecard LLM call — reasoning disabled (2026-08-29) · 3 tests
sends reasoning:{enabled:false} to the provider48.0ms
asks for a ceiling that only has to cover the visible JSON2.8ms
still scores all 7 LLM-backed findings from the reasoning-free answer3.1ms
index-classify.vitest.ts
3/3 6ms · 1 suite PASS
src/seo/index-classify.vitest.ts
isRowIndexed — the one canonical classifier · 3 tests
treats a PASS verdict as indexed regardless of coverage_state3.3ms
reads real GSC coverageState values correctly1.2ms
is safe on empty / null rows0.8ms
leg-timeout.vitest.ts
3/3 10ms · 1 suite PASS
src/seo/leg-timeout.vitest.ts
withLegTimeout · 3 tests
returns the real result when the promise settles before the timeout4.6ms
degrades to an {error} object — never rejects — when the leg is slower than its budget4.1ms
does not let a slow leg block a fast sibling — Promise.all resolves with mixed results1.3ms
serp-crawl.vitest.ts
3/3 56ms · 1 suite PASS
src/seo/serp-crawl.vitest.ts
crawlViaValueSerp — page-1 resilience (NQZAI-64) · 3 tests
falls back to a count-only query when the broad num=10 page-1 times out47.9ms
DEGRADES (never throws) when both the broad and count-only queries fail4.3ms
normal path: page 1 enumerates + carries the count3.6ms
orchestration-floor.vitest.ts
2/2 2ms · 1 suite PASS
src/billing/orchestration-floor.vitest.ts
the orchestration floor tracks the measured agent turn · 2 tests
is one full-prompt call after the 2026-09-17 loop fixes, not the old two2.0ms
asking still costs less than the smallest thing worth asking about0.4ms
analytics-funnel.vitest.ts
2/2 4ms · 1 suite PASS
src/admin/analytics-funnel.vitest.ts
analytics golden-path funnels · 2 tests
every stage op is a registry tool or a known non-tool operation3.6ms
covers all four product sections0.9ms
gsc-disconnect.vitest.ts
2/2 38ms · 1 suite PASS
src/admin/gsc-disconnect.vitest.ts
handleAdminGscDisconnect · 2 tests
rejects without the admin secret33.1ms
deletes the token, meta, and coverage cache KV entries, leaving the deep scan state untouched4.9ms
gsc-no-fallback.vitest.ts
2/2 77ms · 1 suite PASS
src/admin/gsc-no-fallback.vitest.ts
getAdminGscClicksTotal — no tenant-connector fallback · 2 tests
returns null when there is no admin-kv token, without ever querying tenant connections3.8ms
still resolves clicks normally when a real admin-kv token exists (not a total removal of the read path)72.7ms
mixpanel-activity.vitest.ts
2/2 85ms · 1 suite PASS
src/admin/mixpanel-activity.vitest.ts
fetchMixpanelUserActivity — fallback fail-soft · 2 tests
returns the primary (empty) result instead of throwing when the unfiltered fallback times out79.3ms
still returns fallback results when the fallback succeeds4.7ms
mixpanel-adoption.vitest.ts
2/2 4ms · 1 suite PASS
src/admin/mixpanel-adoption.vitest.ts
computeFunnels · 2 tests
counts sequential first-touch survivors, not mere co-occurrence3.4ms
handles empty data without dividing by zero0.6ms
signup-verify-mirror.vitest.ts
2/2 10ms · 1 suite PASS
src/auth/signup-verify-mirror.vitest.ts
verifySignupEmailToken mirrors the verdict into Nhost · 2 tests
writes __app_email_verified__ AND updateUser(emailVerified: true) for the same user6.5ms
a failed mirror write does not turn a successful verification into a failure4.1ms
byok-required.vitest.ts
2/2 4ms · 1 suite PASS
src/email/byok-required.vitest.ts
sendEmail — no platform fallback · 2 tests
fails with a clear message when the user has no configured sending provider3.5ms
never calls fetch when there is no configured provider (no silent platform-credential send)0.8ms
lead-no-fit-chips.vitest.ts
2/2 12ms · 1 suite PASS
src/chat/lead-no-fit-chips.vitest.ts
search_leads: the relevance gate refused a delivery · 2 tests
offers the closest matches anyway, first5.2ms
does not open with "No contacts found for that query" over a refusal6.8ms
onboarding-ask-chips.vitest.ts
2/2 5ms · 1 suite PASS
src/chat/onboarding-ask-chips.vitest.ts
the first-turn website ask carries chips · 2 tests
attaches the populate chip and the skip chip when no brief, no URL, no tool and no gate3.6ms
the populate chip prefills the composer; the skip chip is wording the onboarding gate already reads as a skip2.0ms
present-backlink-gap.vitest.ts
2/2 7ms · 1 suite PASS
src/chat/present-backlink-gap.vitest.ts
seo_backlink_gap renders the prospects the tool actually returns · 2 tests
emits one records block with a row per prospect5.0ms
an empty gap presents nothing — the formatter says so in words0.6ms
commerce-report.vitest.ts
2/2 40ms · 1 suite PASS
src/reports/commerce-report.vitest.ts
commerce_profitability — §17 gold standard · 2 tests
overview: cost-coverage-first bento + missing-cost action + plain headers + feedback37.4ms
products focus: best/worst-margin bento (worst-first when losing money) + plain headers1.8ms
content-quality-report.vitest.ts
2/2 5ms · 1 suite PASS
src/reports/content-quality-report.vitest.ts
seo_content_quality — §17 gold standard · 2 tests
turns failing E-E-A-T categories into fix-prompt findings cards (healthy categories excluded)3.4ms
has a feedback mount, honest score, and no "Cluster N" headers0.8ms
metric-stamps.vitest.ts
2/2 44ms · 1 suite PASS
src/reports/metric-stamps.vitest.ts
entity_audit renderer — health_score stamp (cross-surface with the dashboard entity card) · 2 tests
stamps health_score with a machine-readable value and stays render-sound42.2ms
all stamps share one epoch and the consistency check passes1.1ms
page-check-topic-wiring.vitest.ts
2/2 124ms · 1 suite PASS
src/reports/page-check-topic-wiring.vitest.ts
the composite forwards what the sub-tool needs · 2 tests
aeo_page_check passes `topic` to rag_readiness4.8ms
aeo_page_check declares `topic` so the model can set it118.6ms
usage-inline.vitest.ts
2/2 4ms · 1 suite PASS
src/reports/usage-inline.vitest.ts
get_usage_breakdown — inline chat chart, not a report surface · 2 tests
is flagged inline (no artifact panel)3.3ms
renders a donut + plan bar + breakdown, but NOT a full report shell1.3ms
keyword-landing-runtime.vitest.ts
2/2 53ms · 1 suite PASS
src/routes/keyword-landing-runtime.vitest.ts
keyword landing runtime · 2 tests
emits a parseable inline form script with an escaped website regex48.3ms
classifies the supported website inputs without accepting malformed URL-like input4.8ms
graphql-callsite-coverage.vitest.ts
2/2 4ms · 1 suite PASS
src/tools/graphql-callsite-coverage.vitest.ts
the mutation-name guard can see a generic call site · 2 tests
the extractor tolerates a generic between the name and the paren2.7ms
the optional generic cannot leap a statement boundary into the query literal0.6ms
domain-validity.vitest.ts
2/2 4ms · 1 suite PASS
src/seo/domain-validity.vitest.ts
isValidDomain · 2 tests
rejects the red-team probe and other garbage3.7ms
accepts real domains (post-normalization)0.5ms
stripe-checkout-params.vitest.ts
1/1 55ms · 1 suite PASS
src/billing/stripe-checkout-params.vitest.ts
createTopUpCheckout · 1 test
tags the session, the PaymentIntent and the card statement as nqzai, and still owns itself by success_url55.3ms
export-css-coverage.vitest.ts
1/1 9ms · 1 suite PASS
src/reports/export-css-coverage.vitest.ts
report export CSS coverage · 1 test
every report-* class used in html.ts report bodies is defined in REPORT_EXPORT_CSS9.0ms
pillar-conversion-hero.vitest.ts
1/1 30ms · 1 suite PASS
src/routes/pillar-conversion-hero.vitest.ts
pillar conversion hero · 1 test
emits a parseable handoff script without exposing submitted values in a URL30.1ms
rate-limited-marker.vitest.ts
1/1 3ms · 1 suite PASS
src/runtime/rate-limited-marker.vitest.ts
rate_limited reaches the v2 payload · 1 test
is tracked off the executed result, returned by runChatV2, and spread onto the payload2.8ms
serp-notify.vitest.ts
1/1 4ms · 1 suite PASS
src/seo/serp-notify.vitest.ts
publishSerpdexArtifactAndNotify — live chat confirmation · 1 test
emits a job_completed push carrying the report chip3.9ms
Deterministic unit layer — every *.vitest.ts suite run by vitest 4.1.10 at commit 40fa7eb. Green = passed, red = failed. Durations are per-test wall time. These tests gate every push via npm run check (tsc + static guards + this suite). This is the pure-logic complement to the LLM quality eval, which judges response prose. Auto-generated by scripts/unit-test-report.mjs — no numbers are hand-entered.