<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[lattice]]></title><description><![CDATA[lattice]]></description><link>https://lattice.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>lattice</title><link>https://lattice.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 24 Sep 2026 22:05:11 GMT</lastBuildDate><atom:link href="https://lattice.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Eight green lights in one night, and not one of them was a control.]]></title><description><![CDATA[I have a rule for anything my sites tell a reader. Can someone check it. Will someone make a decision based on it. Would a bigger version of me be embarrassed by it.
I had never once pointed that rule]]></description><link>https://lattice.hashnode.dev/eight-green-lights-in-one-night-and-not-one-of-them-was-a-control</link><guid isPermaLink="true">https://lattice.hashnode.dev/eight-green-lights-in-one-night-and-not-one-of-them-was-a-control</guid><dc:creator><![CDATA[gyapa ganda]]></dc:creator><pubDate>Sat, 05 Sep 2026 03:18:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a15124e7d85e6a1af2ed7f6/5fa97021-8e9e-4b45-a0cc-fecaa74c2197.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have a rule for anything my sites tell a reader. Can someone check it. Will someone make a decision based on it. Would a bigger version of me be embarrassed by it.</p>
<p>I had never once pointed that rule at my own tooling.</p>
<p>On the night of 16 August I finally did, in one codebase, looking for controls I had never actually watched fail. I found eight. Eight instruments whose whole job was to tell me the truth about something else, every one of them reporting success, not one of them doing anything.</p>
<p>A config file that said it blocked backups was inert on the host it shipped to. Never blocked a single request in its life. The 404s that made it look like it was working were files that had never existed.</p>
<p>A SQL revoke came back "Success. No rows returned." A column revoke does nothing underneath a table-level grant. Twenty-seven member email addresses had been sitting public the whole time.</p>
<p>An export gate printed <code>ok 81 samples</code> and <code>ok 45 frames</code>. Printed both. Compared neither.</p>
<p>A fade test asserted on <code>Math.max(alphas)</code>, which is 1.0 for every fade ever written and also for a hard cut.</p>
<p>A deploy reported complete directly above a log line reading "Failed to publish your Function."</p>
<p>Five of those had been sitting there for days, believed. Three I had written that same evening and caught within the hour, which tells you the count is not the interesting part.</p>
<p>What got me was that this failure runs backwards from every other kind of bug. A control you never wrote gets found by the incident. A control that lies gets found by the incident and then eats the incident review too, because everybody stares at the green light and goes looking somewhere else.</p>
<p>Two of the eight were worse than bugs, and they were the same shape as each other. Something real went wrong. We responded by writing a rule. The rule did nothing. We went back to work feeling protected.</p>
<p>The twenty-seven addresses leaked for real. The fix was a list of backup globs that never blocked one request.</p>
<p>A signup rework shipped its front end while its migration sat unapplied in the repo, so every new member logged into an empty room. That migration had a section in its own header titled "THE ONE THING THAT WOULD HAVE BROKEN." It was right. Nobody ran it.</p>
<p>A control written in the hours after an incident is the least trustworthy code in the repo and gets treated as the most. You are tired, you are angry, writing it feels like doing something, and the second it exists the incident feels closed. Nobody goes back.</p>
<p><strong>An instrument is not a control until something has been observed to bounce off it.</strong></p>
<p>That is a procedure, not a slogan. If you want to know whether a rule fires, make the thing it is supposed to catch and watch what happens to it. Reading the config only tells you what somebody intended. That backup file was articulate, dated, carried its own incident history in a comment block, and did nothing.</p>
<p>Three weeks later I found the version of this that is genuinely hard to see.</p>
<p>Five checks, five different programs, one session, every one of them green while structurally incapable of going red. No broken code anywhere. Each was a correct program answering a question that had no failing branch. A mutation test told me four of four mutants were killed. Every mutant had died on "no properties found" before it ever reached the canary, because the script resolved its root relative to its own location and the mutants ran from a scratch directory. Four for four. Zero of four.</p>
<p>Another asserted that a marker string was absent after a fix. The string never matched the tool's own output casing, so "not found" and "never findable" came back identical. That same check passed while the scanner it depended on had crashed, because it went looking for the marker inside the crash text and did not find it there.</p>
<p>Green is not evidence. Green is evidence only if you know what red would have looked like.</p>
<p>Four questions killed all five. What input makes this fail. Did the failing case actually run, and fail for the reason I meant rather than a path error. Is absence proven or just unobserved. Does this thing report differently for "nothing here" and "I never looked."</p>
<p>I hit two more today, in a tool I built this evening.</p>
<p>Twelve controls, all green, including a deliberately broken copy to prove the suite could go red at all. Then I pointed it at the live API and it threw on the first article. The list endpoint returns an article's tags as a comma-separated string. The single-article endpoint returns the same field as an array. Every fixture I had written used the array. Twelve green tests against a shape production does not send, and three of those twelve now exist only because of it.</p>
<p>The second one I nearly shipped, and it is worse. The whole premise of the tool is that a settled page beats a fresh one, so age scores positively. But the endpoint I was harvesting from only returns the latest feed. There were no settled pages in the pool. None. The age term, the entire reason the thing exists, was sorting fresh posts against an empty set and handing me a clean ordered list while it did it. Widened the harvest and a page 340 days old came out at rank three, carried there by the term that had been doing nothing at all an hour earlier.</p>
<p>An instrument whose scan root excludes the thing it was built to find will report full coverage forever, and from outside the checker that looks exactly like a clean bill of health.</p>
<p>None of this is an argument for more gates. Most of the damage came from instruments that already existed, and a ninth unverified one makes it worse. What I would actually defend is fewer instruments, each proven to bite, which means deleting checks that currently pass. People hate doing that. It feels like taking down a smoke alarm.</p>
<p>Here is the problem with everything I just told you: you cannot check any of it. Private codebase, my logs, my word, which is worth about what anybody's word is worth on the internet. That is a real hole in a piece arguing that a specific finding beats a general claim.</p>
<p>So take the instrument instead of the stories. The seven shapes and the four questions are at <a href="https://www.deepsynthesis.org/instrument-integrity">deepsynthesis.org/instrument-integrity</a>. It runs against your own repository in about twenty minutes and it needs nothing from me. Point it at a check you have never seen go red.</p>
<p>Every fix above came from mutation or from curiosity. Not one came from the suite that was supposed to cover it. Today's second one surfaced because I glanced at a column of dates and thought, huh, these all look new.</p>
<p>I have no way to systematise that. It is just the only thing that has ever worked, and I could not tell you how much of my tooling is lying to me right now.</p>
]]></content:encoded></item><item><title><![CDATA[I was going to write that a cited page never plateaus. Then I checked.]]></title><description><![CDATA[I had the sentence written. A cited page does not plateau, it gets cited harder. It followed neatly from the previous piece, it sounded like a law, and the numbers in front of me appeared to support i]]></description><link>https://lattice.hashnode.dev/i-was-going-to-write-that-a-cited-page-never-plateaus-then-i-checked</link><guid isPermaLink="true">https://lattice.hashnode.dev/i-was-going-to-write-that-a-cited-page-never-plateaus-then-i-checked</guid><category><![CDATA[SEO]]></category><category><![CDATA[analytics]]></category><category><![CDATA[Data Science]]></category><dc:creator><![CDATA[gyapa ganda]]></dc:creator><pubDate>Fri, 04 Sep 2026 06:21:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a15124e7d85e6a1af2ed7f6/c9188ce6-33e8-45fa-adc9-8d54c7b7c05c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I had the sentence written. A cited page does not plateau, it gets cited harder. It followed neatly from the previous piece, it sounded like a law, and the numbers in front of me appeared to support it. Between 16 and 23 August our cited surface went from 950 pages carrying 610,189 Copilot citations to 1,001 pages carrying 737,197. The pages we already had were clearly absorbing most of the gain.</p>
<p>Then I tracked the individual pages instead of the totals, and the sentence turned out to be false.</p>
<h2>Four in ten of our cited pages did not grow at all</h2>
<p>Of the 946 URLs present in both captures, 60.7% grew, 35.4% did not move, and 3.5% declined. Four more fell out of the cited set entirely. Plateauing is not the exception in this data. It is what happened to more than a third of the pages in a single week.</p>
<p>The first lesson is about the shape of the original claim rather than about the citations. I had a decomposition that looked like evidence: 97% of the week's gain came from pages that were already cited, and only 3% from newly cited ones. That reads like proof that existing pages compound. It is close to arithmetically forced. A page enters the cited set at the reporting threshold, near zero by definition. Our 55 new entrants arrived at a median of 9 citations each. They could not have contributed much of a 127,008 citation gain in their first week no matter how the engine behaved. <strong>That statistic would look roughly the same for almost any growing corpus, which means it says almost nothing about ours.</strong></p>
<h2>The rates are flat. The volumes are a cliff.</h2>
<p>So I split the 946 pages into ten equal bands by size and measured each band's growth rate against its own starting total.</p>
<table>
<thead>
<tr>
<th>band</th>
<th>growth rate</th>
<th>share of all new citations</th>
</tr>
</thead>
<tbody><tr>
<td>1 (smallest)</td>
<td>24.7%</td>
<td>0.1%</td>
</tr>
<tr>
<td>2</td>
<td>65.4%</td>
<td>0.4%</td>
</tr>
<tr>
<td>3</td>
<td>29.6%</td>
<td>0.4%</td>
</tr>
<tr>
<td>4</td>
<td>35.0%</td>
<td>0.9%</td>
</tr>
<tr>
<td>5</td>
<td>32.6%</td>
<td>1.6%</td>
</tr>
<tr>
<td>6</td>
<td>26.7%</td>
<td>2.5%</td>
</tr>
<tr>
<td>7</td>
<td>27.7%</td>
<td>5.2%</td>
</tr>
<tr>
<td>8</td>
<td>27.4%</td>
<td>9.8%</td>
</tr>
<tr>
<td>9</td>
<td>18.9%</td>
<td>16.5%</td>
</tr>
<tr>
<td>10 (largest)</td>
<td>18.8%</td>
<td>62.5%</td>
</tr>
</tbody></table>
<p>Nine of the ten bands grew between 18.8% and 35.0% in the week. The tenth, a small band, grew 65.4% on a median starting count of 8 citations, which is noise rather than a signal. The largest pages grew the slowest of any band, at 18.8%.</p>
<p>The top tenth of pages took 62.5% of every new citation the network gained. The bottom half took 3.3%.</p>
<p>Both of those columns describe the same ten rows of the same table in the same week. That is the finding, and it is why the chart carries two panels rather than one. An average hides it. A single bar chart hides it. You have to draw the rate and the volume side by side before the shape is obvious.</p>
<h2>And the order barely moved</h2>
<p>Rank correlation between the two captures was 0.989. Forty six of the top fifty pages held their position. 74.5% of pages did not change decile at all.</p>
<p>Put those together and the interpretation runs opposite to the intuition. The engine is not picking winners week to week. It grew nearly every size band at a similar rate. <strong>The concentration does not come from large pages being favoured. It comes from a distribution that already spanned four orders of magnitude, compounding at roughly one rate.</strong> Multiplicative growth on a skewed base produces a brutally skewed gain even when the multiplier is fair.</p>
<p>That inverts the practical reading of the earlier piece. "Fifty pages carry half the network" sounds like an instruction to grow more pages into the top fifty. In this window that happened to almost nobody. What a page was worth was mostly set before the week began, and the week mostly multiplied it. If that holds over longer horizons, the lever is what a page is when it enters, not how it is nurtured afterwards.</p>
<h2>What this does not say</h2>
<p>I do not know yet whether it holds over longer horizons. Seven days of rank stability is weak evidence about six months, and I cannot separate the engine's behaviour from our own publishing behaviour inside one window. We hold a third capture from 4 August. The same test across nineteen days is the obvious next move, and I will publish whatever it says, including if it contradicts this.</p>
<p>The measurement, stated plainly:</p>
<ul>
<li>Microsoft Copilot citations from the Bing Webmaster Tools AI Performance API, read per URL across 14 properties.</li>
<li>These are <strong>page level</strong> figures. The property level total for the same network, measured 2 September, is 940,174. Both are correct and they measure different things. Page level runs below property level because pages under Bing's reporting threshold appear in the property total but never as rows. I will not quote one as the other.</li>
<li>One engine. Bing is the only one that publishes per property citation counts. This is not a measure of ChatGPT, Claude, Perplexity or Gemini.</li>
<li>Citations are not clicks, and they are not crawler fetches. Three separate things.</li>
<li>Small band percentages are noisy, for the reason given above.</li>
</ul>
<p>What I can say is narrower and, I think, more useful than the sentence I nearly published. In this network, over this week, growth in AI citation was multiplicative and close to rank preserving. The average page grew 12%. More than a third grew not at all. And the top tenth, growing slower than eight of the nine bands beneath it, still took nearly two thirds of everything gained.</p>
<p>Most claims being made about AI citation right now cannot be checked by the people making them. Bing is the only engine that will tell you which of your pages it cited and how often. That is one engine out of several, it is a floor rather than a total, and it is still the only place this test can be run at all.</p>
<hr />
<p>We know which fifty pages. If you want your product named inside one of them, that is a conversation we are happy to have: <a href="https://deepsynthesis.org/lattice">deepsynthesis.org/lattice</a></p>
]]></content:encoded></item><item><title><![CDATA[OpenAI sends three bots. The one that precedes a citation is the smallest.]]></title><description><![CDATA[If you go looking for AI traffic in your own server logs, you probably grep for GPTBot and stop there. That is the wrong number, and it is wrong in a way that flatters you.
OpenAI runs three separate ]]></description><link>https://lattice.hashnode.dev/openai-sends-three-bots-the-one-that-precedes-a-citation-is-the-smallest</link><guid isPermaLink="true">https://lattice.hashnode.dev/openai-sends-three-bots-the-one-that-precedes-a-citation-is-the-smallest</guid><category><![CDATA[SEO]]></category><category><![CDATA[Artificial Intelligence]]></category><dc:creator><![CDATA[gyapa ganda]]></dc:creator><pubDate>Tue, 01 Sep 2026 08:21:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a15124e7d85e6a1af2ed7f6/92a9f8a7-dbb5-4820-bc9d-249f225bba86.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you go looking for AI traffic in your own server logs, you probably grep for <code>GPTBot</code> and stop there. That is the wrong number, and it is wrong in a way that flatters you.</p>
<p>OpenAI runs three separate agents. They mean three different things.</p>
<ul>
<li><strong>GPTBot</strong> is the training crawler. It feeds model weights. It will never send you a visitor.</li>
<li><strong>OAI-SearchBot</strong> is the retrieval crawl. This is the fetch that runs <em>before</em> an answer cites a page. If you care about being cited, this is the one to watch.</li>
<li><strong>ChatGPT-User</strong> is not a crawler at all. It fires when a real person clicks a citation or asks ChatGPT to open a URL. That is a human who already arrived.</li>
</ul>
<p>Collapse them into a single "OpenAI" row and you are adding a trainer, a retriever and a reader together, then calling the total your AI traffic.</p>
<h2>What it looks like across 14 sites</h2>
<p>Seven days to 31 August 2026, one Cloudflare account, 14 properties:</p>
<table>
<thead>
<tr>
<th>Agent</th>
<th>Purpose</th>
<th>Fetches</th>
</tr>
</thead>
<tbody><tr>
<td>GPTBot</td>
<td>training</td>
<td>11,391</td>
</tr>
<tr>
<td>ChatGPT-User</td>
<td>a human who clicked</td>
<td>9,656</td>
</tr>
<tr>
<td>OAI-SearchBot</td>
<td>retrieval, precedes a citation</td>
<td>2,930</td>
</tr>
</tbody></table>
<p>Retrieval is <strong>12.2%</strong> of OpenAI's footprint on the network. Training outweighs retrieval <strong>3.89 to 1</strong>.</p>
<p>The bar that matters is the short one. Everything upstream of a citation is happening in that 12.2%, and most dashboards never separate it out.</p>
<h2>This is not an OpenAI quirk</h2>
<p>The same split shows up at every vendor that runs a separate user agent for human-initiated fetches. Same network, same window:</p>
<table>
<thead>
<tr>
<th>Vendor</th>
<th>Crawler</th>
<th>Human</th>
</tr>
</thead>
<tbody><tr>
<td>OpenAI</td>
<td>GPTBot 11,391</td>
<td>ChatGPT-User 9,656</td>
</tr>
<tr>
<td>Anthropic</td>
<td>ClaudeBot 6,881</td>
<td>Claude-User 1,191</td>
</tr>
<tr>
<td>Perplexity</td>
<td>PerplexityBot 1,749</td>
<td>Perplexity-User 204</td>
</tr>
</tbody></table>
<p>Three vendors, three crawler-versus-human pairs, and almost nobody separates those either.</p>
<h2>How to run it on your own logs</h2>
<p>If you are on Cloudflare, the data is already there. Query <code>httpRequestsAdaptiveGroups</code> in the GraphQL analytics API, group by host and user agent, and split the buckets by exact agent string rather than by vendor. The whole finding lives in that one grouping decision.</p>
<p>If you are on nginx or Apache, the same split is a <code>grep</code> away. The user agent strings are distinct and stable.</p>
<h2>The caveat that matters</h2>
<p>These are <strong>fetches, not citations</strong>. A fetch is two rungs below a citation on the ladder and most fetches never become one. They come from a different instrument than citation counts do, they measure a different event, and the two must never be added together.</p>
<p>Bot identity here is self-reported in the user agent. We are not verifying by IP range, so treat these as what the requester claimed to be.</p>
<p>One more honest bound: this is one network of 14 sites over seven days. The ratio will differ on yours. The point is not our number, it is that "OpenAI" was never one number to begin with.</p>
]]></content:encoded></item><item><title><![CDATA[One engine lets us count AI citations. It sends 15% of the readers.]]></title><description><![CDATA[Every AI visibility tool on the market prices itself on data from Bing. Not because Bing is where the readers are. Because Bing is the only engine that will tell you it cited you.
The instrument
Bing ]]></description><link>https://lattice.hashnode.dev/one-engine-lets-us-count-ai-citations-it-sends-15-of-the-readers</link><guid isPermaLink="true">https://lattice.hashnode.dev/one-engine-lets-us-count-ai-citations-it-sends-15-of-the-readers</guid><category><![CDATA[SEO]]></category><category><![CDATA[analytics]]></category><category><![CDATA[AI]]></category><dc:creator><![CDATA[gyapa ganda]]></dc:creator><pubDate>Sun, 23 Aug 2026 11:38:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a15124e7d85e6a1af2ed7f6/d73baf5d-7384-4354-a794-8520265d2c7b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every AI visibility tool on the market prices itself on data from Bing. Not because Bing is where the readers are. Because Bing is the only engine that will tell you it cited you.</p>
<h2>The instrument</h2>
<p>Bing Webmaster Tools publishes per-property AI citation counts. Nobody else does. Across fourteen live properties we run, summed from daily rows over the ninety days to 22 August 2026, that number is <strong>768,234</strong>. Restricted to the eleven properties with a full window, the like-for-like figure is <strong>735,780</strong>.</p>
<p>Before going further, the number argues with itself. Bing's own <code>citationstats</code> endpoint reports 768,234 for that window. Its per-URL <code>pages/stats</code> endpoint reports 737,197 for the same window, in the same session. The gap is 31,037, about four percent, and it runs in the same direction on all fourteen properties.</p>
<p>We publish the higher figure because it is what the tool's own chart shows a site owner, and we say the lower one exists rather than burying it. If a vendor quotes you a citation count without telling you which endpoint it came from, that is a four percent question they have not answered.</p>
<h2>Then we went looking for the readers</h2>
<p>Over twenty-eight days, GA4 recorded 2,605 sessions on the network that arrived from an AI assistant.</p>
<table>
<thead>
<tr>
<th>engine</th>
<th>sessions</th>
<th>share</th>
</tr>
</thead>
<tbody><tr>
<td>ChatGPT</td>
<td>2,053</td>
<td>78.8%</td>
</tr>
<tr>
<td>Copilot</td>
<td>387</td>
<td>14.9%</td>
</tr>
<tr>
<td>Claude</td>
<td>123</td>
<td>4.7%</td>
</tr>
<tr>
<td>Perplexity</td>
<td>42</td>
<td>1.6%</td>
</tr>
<tr>
<td>Gemini, DeepSeek, Grok</td>
<td>no matching source string</td>
<td>unmeasurable</td>
</tr>
</tbody></table>
<p>Copilot is the Bing surface. So the engine that publishes a citation count for us is the engine responsible for roughly one seventh of the AI readers who actually arrive. The engine responsible for nearly four fifths of them publishes nothing at all about who it cites.</p>
<h2>The inversion</h2>
<p>That is the whole finding, and it inverts what the category is sold on. Every dashboard, every AI-visibility score, every pitch deck with a citation chart in it is describing the small channel with confidence and the large one not at all.</p>
<p>We built our own measurement on the same foundation. This is a finding about our instrument before it is a finding about anyone else's.</p>
<h2>The part that is genuinely unknown</h2>
<p>Gemini, DeepSeek and Grok matched no source string in our data. Zero rows. The correct word for that is <strong>unmeasurable</strong>, not absent.</p>
<p>Gemini referrals in particular commonly arrive with no distinguishing referrer and land in organic or direct, which means a real reader gets filed as something else. Reporting those engines as zero would be the easy lie, and it is the specific lie this category is built to tell, because a zero looks like a measurement and an admission of blindness does not.</p>
<h2>A hypothesis, offered as a hypothesis</h2>
<p>Cloudflare per-bot logs show GPTBot, which crawls for training, at about 7,477 requests a day against OAI-SearchBot, which fetches for retrieval, at about 500. Roughly fifteen to one. Yet ChatGPT delivers 78.8% of the AI arrivals.</p>
<p>If the dominant channel is answering largely out of what it already absorbed rather than what it fetches live, then surviving in the training corpus is the lever on most of the traffic, and retrieval optimisation is the lever on the remainder. We are not claiming that is settled. We are saying the crawl ratio and the referral share point the same way and almost nobody is pricing on it.</p>
<h2>The question</h2>
<p>If the biggest channel is unmeasurable by construction, what is anyone actually selling: the placement, or the measurement of the one place that happens to report?</p>
<hr />
<p><em>Figures: Bing Webmaster AI performance, fourteen properties, 23 May to 22 August 2026, summed from daily rows. GA4 Data API by session source, twenty-eight days to 20 August 2026. GA4 is JavaScript based, so ad blockers and privacy browsers are missed and every session figure here is a floor, not a ceiling.</em></p>
]]></content:encoded></item></channel></rss>