Skip to content
Writing

One file, three paths: Shopify's llms.txt

Shopify auto-serves llms.txt, llms-full.txt and agents.md on every store - the same document, one sentence apart. Measured on ten live stores, 10 Aug 2026.

2026-08-10

Shopify has served llms.txt, llms-full.txt and agents.md at every store root since May 2026. That part is well known. What we checked today is what is actually in them, on ten real stores of very different sizes - and whether Sextantscan's own checks, which grade a store on exactly this, see through the template or get fooled by it like a length check would.

So we fetched. Ten live Shopify stores, three paths each, thirty requests, today.

The requests, and what came back

GET https://colourpop.com/llms.txt        HTTP/1.1 200 OK  Content-Type: text/markdown
GET https://colourpop.com/llms-full.txt   HTTP/1.1 200 OK  Content-Type: text/markdown
GET https://colourpop.com/agents.md       HTTP/1.1 200 OK  Content-Type: text/markdown

Same three requests against nine more stores, split deliberately into two groups so the finding cannot be dismissed as a big-brand artefact:

  • Five recognizable brands: colourpop.com, kyliecosmetics.com, allbirds.com, gymshark.com, brooklinen.com.
  • Five small merchants, found via real feedback threads on the Shopify community forum and verified by product count before including them: indiecoffeeroasters.com (58 products), roasttoordercoffee.com (62), tubbscoffeeroasters.com (8), temeculacoffeeroasters.com (158), gigawattcoffeeroasters.com (27). Product counts are from each store's own /products.json on the day of the fetch.

Thirty requests, thirty 200s, real content every time - no store had disabled or replaced the default, small or large.

Confirmed genuinely Shopify first, not assumed: each store's homepage references cdn.shopify.com and exposes a Shopify.theme object, checked before any discovery file.

It's one document, not three

Here is the entire diff between ColourPop's llms.txt and its llms-full.txt, fetched 10 Aug 2026:

- Agent discovery: the canonical agent-facing description of the store is at `/agents.md`.
  You're reading `/llms.txt`, which mirrors that content.
+ Agent discovery: the canonical agent-facing description of the store is at `/agents.md`.
  You're reading `/llms-full.txt`, which mirrors that content.

One line. /agents.md differs from both by the same single sentence, naming itself instead. And the same llms.txt-to-llms-full.txt diff holds on the smallest store in the sample - Tubbs Coffee Roasters, 8 products:

- Agent discovery: the canonical agent-facing description of the store is at `/agents.md`.
  You're reading `/llms.txt`, which mirrors that content.
+ Agent discovery: the canonical agent-facing description of the store is at `/agents.md`.
  You're reading `/llms-full.txt`, which mirrors that content.

Same sentence, same shape, 8 products or several hundred SKUs, made no difference. That is the entire difference across all three files, on all ten stores measured. We did not summarize this - it is the literal output of diff run against today's fetches.

What the file actually says

It is not a stub. Each file runs 4.0-4.5 KB across all ten stores measured, small and large alike

  • catalogue size does not move this number, because the byte count is mostly the fixed template, not anything about the store. It covers, in order: how a personal shopping agent should install Shopify's Shop skill and route checkout through Shop Pay; the store's Universal Commerce Protocol endpoints for agent-driven checkout, including /.well-known/ucp for discovery and /api/ucp/mcp for an MCP tool interface, with the exact tools/list flow to call search_catalog, create_cart and create_checkout; a read-only browsing section giving the generic Shopify catalogue endpoint shapes (/products/{handle}.json, /collections/{handle}/products.json, /search); links to whichever policy pages the store has published; and a closing paragraph naming Shopify as the platform and linking shopify.com/start and shop.app/SKILL.md.

What it does not do, on any of the ten stores: name a single product, collection, or store-specific page. Here is the complete list of same-site paths Indie Coffee Roasters' file references - a 58-product store, picked for this table instead of a recognizable brand precisely because the objection worth pre-empting is "sure, but that's just how big stores get treated":

| Reference in the file | Generic to every Shopify store? | |---|---| | /collections/all | yes - explicit platform default | | /products/{handle}, /products/{handle}.json | yes - unfilled placeholder | | /collections/{handle}, /collections/{handle}/products.json | yes - unfilled placeholder | | /search?q={query}&type=product | yes - /search is a platform default | | /sitemap.xml, /agents.md | yes - platform defaults | | /.well-known/ucp, /api/ucp/mcp | yes - platform defaults |

Zero rows reference anything specific to this store's own catalogue - no product name a customer would recognize, no real collection handle. The same is true, checked individually, on all ten stores, coffee roaster and cosmetics brand alike.

The two checks, and what they measure

Sextantscan runs two checks against exactly this shape of file. Both are implemented in packages/engine/src/parse/, both are pure functions over text with no knowledge of who served it or how big the store is, and both are run below against today's fetches using the checks' own thresholds, not an approximation of them.

discovery.llms_txt asks whether the file describes this store, not merely whether one exists. It counts same-site links and paths in the file, excluding a fixed list of paths every store on the platform serves without a merchant creating anything (/collections/all, /search, /.well-known/ucp, the standard policy pages, and more) and excluding unfilled placeholders like /products/{handle}. A file needs at least 2 references that survive that filter, or a product name the scan independently found on the store, to be credited as store-specific. Run against today's ten files, ordered by catalogue size:

The zero in every row below is Shopify's unedited default template, not a gap in what any of these merchants built - none of the five small stores here has done anything wrong; the file simply hasn't been touched since Shopify started serving it.

| Store | Products | Same-site references, generic paths and placeholders excluded | Result | |---|---|---|---| | tubbscoffeeroasters.com | 8 | 0 | below threshold (need 2) | | gigawattcoffeeroasters.com | 27 | 0 | below threshold | | indiecoffeeroasters.com | 58 | 0 | below threshold | | roasttoordercoffee.com | 62 | 0 | below threshold | | temeculacoffeeroasters.com | 158 | 0 | below threshold | | colourpop.com, kyliecosmetics.com, allbirds.com, gymshark.com, brooklinen.com | hundreds-thousands | 0 each | below threshold |

Zero on all ten, from an 8-product roaster to five-figure-catalogue brands. The check's status for this is warn, not fail - the file is present and substantial, which is real and is credited; it is the store-specific content that is absent, at every scale measured.

discovery.llms_full_txt asks whether the file expands llms.txt or repeats it, measured as the share of three-word sequences (trigrams) in llms-full.txt that already appear in llms.txt. A file is called a duplicate only when both conditions hold: 85% or more of its trigrams are already in llms.txt, and it contributes 20 or fewer trigrams that are not. Both conditions, because overlap alone convicts a genuinely short expansion of a small catalogue too easily - our own generator's one-product output overlaps its own llms.txt at 0.725 for exactly that reason, and that file is real. Run against today's fetches:

| Store | Trigram overlap with llms.txt | New trigrams | Result | |---|---|---|---| | tubbscoffeeroasters.com | 99.44% | 3 | duplicate | | gigawattcoffeeroasters.com | 99.44% | 3 | duplicate | | indiecoffeeroasters.com | 99.42% | 3 | duplicate | | roasttoordercoffee.com | 99.44% | 3 | duplicate | | temeculacoffeeroasters.com | 99.43% | 3 | duplicate | | colourpop.com, kyliecosmetics.com, allbirds.com, gymshark.com, brooklinen.com | 99.44% each | 3 each | duplicate |

Within a few hundredths of a percent of each other across ten unrelated stores spanning three orders of magnitude in catalogue size - consistent with what the byte diff already showed: store name and domain get substituted into one fixed document, and the trigram math comes out the same regardless of who is serving it.

The obvious objection, addressed

Five of these ten stores are not brands anyone would recognize. Tubbs Coffee Roasters lists eight products. The numbers above do not soften for them: zero specific references and a duplicate llms-full.txt, same as a store with a catalogue two orders of magnitude larger. Whatever is driving this result, it is not something that only happens to merchants big enough to have outsourced their theme work - the small stores here are running the same unedited default as the large ones, because both groups are running the same Liquid template Shopify ships.

The file that's already scored differently, and has been for a reason

agents.md is not gated on store-specificity at all, by design: its job is instructions - canonical endpoints, checkout rules, who to contact - not a catalogue map, and a genuinely good one can legitimately reference only platform endpoints. The check only asks whether it is present and past a length floor. On all ten stores it is - so agents.md passes, on the identical text that makes llms.txt warn one path over. That is not an inconsistency; the two checks measure different things on purpose.

The UCP manifest at /.well-known/ucp is a different case again, and one Sextantscan's own scoring already accounts for rather than something this measurement discovered today. Fetched fresh from all ten stores, it is real per-store data on every one - two examples:

// https://colourpop.com/.well-known/ucp
{"ucp":{"version":"2026-04-08","services":{"dev.ucp.shopping":[{
  "endpoint":"https://colourpop-prd.myshopify.com/api/ucp/mcp", ...

// https://indiecoffeeroasters.com/.well-known/ucp (58 products)
{"ucp":{"version":"2026-04-08","services":{"dev.ucp.shopping":[{
  "endpoint":"https://indie-coffee-roasters.myshopify.com/api/ucp/mcp", ...

All ten stores, small and large, return a distinct backend hostname matching their own Shopify identity - not a template. Sextantscan's ceiling calculation already treats this file on its own terms: it is the one root-path discovery check excluded from what a Fix implementation can close on standard Shopify, specifically because Shopify generates it from store configuration with no Liquid template, Admin API surface, or app extension point to override it. In a thread on community.shopify.dev a Shopify staff member states plainly that "there isn't currently a supported way to edit or replace that profile", and offers no route to one. The other three files lost that same exclusion in May, when the theme templates shipped and made them ordinary, editable files like any other. UCP kept it, on purpose, for the reason above. Whatever a UCP manifest says on any given store, nobody but Shopify can change it - which is a different, and narrower, claim than anything this piece measures about llms.txt.

Method, so this is checkable

Fetched 10 Aug 2026, direct HTTPS requests, no JavaScript, curl with a standard user agent. Ten stores in two groups of five - colourpop.com, kyliecosmetics.com, allbirds.com, gymshark.com, brooklinen.com for recognizability, and indiecoffeeroasters.com, roasttoordercoffee.com, tubbscoffeeroasters.com, temeculacoffeeroasters.com, gigawattcoffeeroasters.com specifically because they are not - found via real store-feedback threads and product searches rather than a top-brands list, and product counts pulled from each store's own /products.json before inclusion, not estimated. Confirmed Shopify by inspecting each homepage for cdn.shopify.com references and a Shopify.theme object before fetching any discovery file.

The two check algorithms above are quoted from packages/engine/src/parse/store-specificity.ts and packages/engine/src/parse/expansion.ts in this repository and re-implemented independently against today's raw fetches to produce the tables - not read off a report, so the numbers here are one hop closer to the wire than a scan result would be. Thresholds (storeSpecificRefsMin: 2, llmsFullDuplicationMax: 0.85, llmsFullMinUniqueShingles: 20) are quoted from packages/engine/src/thresholds.ts as shipped. The 2026-05-29 date for template customization is Shopify's own developer changelog entry, "Customize /llms.txt, /llms-full.txt and /agents.md", which lists that as the effective date (the entry itself was posted 2026-05-28).

What this does not tell you

Ten stores is still ten stores, not a random sample of Shopify's full merchant base, and every one of them is on the default, unedited theme template for these files - Shopify's changelog lists the Liquid templates as effective 2026-05-29, so any store with theme access can replace the default at any time, and this piece says nothing about how many already have. What it measures is the default as currently served, on ten real stores spanning three orders of magnitude in catalogue size, today.

We are not measuring or claiming anything about Shopify's own agentic-readiness tooling - we have not run it and have nothing to say about what it checks.

Why this is worth publishing rather than assuming

The two checks that produced most of the tables above did not exist a few months ago. Discovery checks used to test presence and byte count, which the default template clears easily on a store of any size. A presence-and-length check would have scored llms.txt and llms-full.txt as passing on a file no merchant wrote and that names nothing about what they sell, whether the store has eight products or eighty thousand. Building a check that measures the file's relationship to the actual catalogue, rather than whether a file exists, is what turns "present" into the more specific and more useful "present and about this store, or not" - and the reason it had to be a relationship to the catalogue rather than a fingerprint of this particular template is the same reason it still works after Shopify revises the wording: it would keep working on a WooCommerce store's thin default too, and it starts passing the day any platform's default genuinely starts naming what a store sells.

Related: the schema fields AI agents actually read covers the product-level data this piece does not, and llms.txt for Shopify is the practical guide to replacing the default with one that names your own catalogue.

Run a free scan and see what your own llms.txt measures as.