From 2f3f218b91eb7c885bf245e0fc00a7717aabceb1 Mon Sep 17 00:00:00 2001 From: henry-idingo Date: Tue, 4 Aug 2026 18:57:03 +0200 Subject: [PATCH] Document how to verify the crawler path, and what a pass-through means MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The verification snippet told people to curl `https://your-site.com/optimized-page/`, which reads like a route the plugin creates. It is not — there is no such path, so substituting only the domain produces a plain WordPress 404 and looks like a broken install. The plugin only answers URLs CiteCue holds an optimized version of, and connecting does not generate any. Replace the placeholder with `/a-page-you-optimized/`, pipe the checks through `grep -i x-citecue`, and add a table of the four outcomes so a missing header is legible as a pass-through rather than a failure. Document the pass-through causes in likelihood order, including two that are easy to mistake for a broken install: the 60 s negative cache on misses (an immediate retry makes no API call at all), and the fact that the proxy ignores logged-in users, so a wp-admin browser tab always shows the normal site. Note that `Recent AI crawler activity` records nothing when the plugin declines before calling the API — an open circuit, a negative-cached miss, serving switched off or an unrecognized UA all leave no row, so an empty table is not evidence of a cache or CDN in front. Since llms.txt keeps answering from its 5-minute local cache while the circuit is open, `x-citecue: llms-txt` does not prove the connection is live either; document flush-then-retest as the way to tell those apart. Co-Authored-By: Claude Opus 5 --- README.md | 41 +++++++++++++++++++++++++++++++++++------ readme.txt | 12 ++++++++++++ 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 66ff6da..74e268c 100644 --- a/README.md +++ b/README.md @@ -69,13 +69,40 @@ The full server-side contract is in [`docs/connect-handshake.md`](docs/connect-h The strictness matters: when llms.txt falls through — switched off here, or no llms.txt for the project on CiteCue — the crawler proxy is next on `template_redirect` and can answer the same URL with `x-citecue: served`. Accepting any marker would read that as proof llms.txt works, which is what it disproves. With `Serve llms.txt` switched off the check reports that it could not run, rather than a failure the site did not have. -By hand: +By hand. Replace the second URL with a page that exists on the site **and** has been generated on CiteCue's Auto-Fix page — there is no `/optimized-page/` route; the plugin only answers URLs CiteCue holds an optimized version of: ```bash -curl -si -A GPTBot https://your-site.com/llms.txt # expect: x-citecue: llms-txt -curl -si -A GPTBot https://your-site.com/optimized-page/ # expect: x-citecue: served +curl -si -A GPTBot https://your-site.com/llms.txt | grep -i x-citecue +curl -si -A GPTBot https://your-site.com/a-page-you-optimized/ | grep -i x-citecue ``` +| Response header | Means | +|---|---| +| `x-citecue: llms-txt` | Working — CiteCue's llms.txt was served. | +| `x-citecue: served` | Working — the optimized page was served. | +| `x-citecue-cache: stale` (alongside `served`) | Working, degraded — CiteCue is unreachable, so the cached body was served. | +| *no `x-citecue` header at all* | Pass-through: the normal theme output. Not an error on its own — see below. | + +Test with `curl` or a logged-out browser. The proxy deliberately ignores logged-in users, so the tab you have wp-admin open in will always show the normal site. + +#### When nothing is served + +A pass-through means [`Citecue_Proxy::decide()`](includes/class-citecue-proxy.php) chose to leave the request to WordPress. In rough order of likelihood on a fresh install: + +- **CiteCue has no optimized page for that URL.** The common one — connecting does not generate anything. Add the page on CiteCue's Auto-Fix page first. A URL that 404s on the site behaves the same way. +- **A miss for the same URL in the last 60 s.** Misses are negative-cached, so an immediate retry makes no API call at all. After generating a page, wait a minute before re-testing. +- **`Serve optimized pages` is off**, or the site is not connected. +- **The User-Agent is not a known crawler** — check the token list under **Tools → Refresh crawler list**. +- **The circuit is open** after a timeout or a rejected API key. A rejected key raises an admin notice on the settings screen; a timeout backs off quietly for 60 s. + +**Settings → CiteCue → Recent AI crawler activity** narrows it down: + +- A **`passthrough`** row for the URL: the plugin ran, called CiteCue, and CiteCue reported no optimized page. Generate it. +- An **`error`** row: the API call failed and the circuit is now open. +- **No row at all** is ambiguous by design — nothing is recorded when the plugin declines *before* calling the API (negative-cached miss, open circuit, serving switched off, unrecognized UA), and equally when a full-page cache or CDN answered before WordPress ran. + +To tell a healthy connection from an open circuit, use **Tools → Flush delivery cache** and then immediately re-request `/llms.txt`. That endpoint answers from a 5-minute local cache — and keeps answering from it while the circuit is open — so only a flushed cache forces a live API call. Still `x-citecue: llms-txt` afterwards means delivery is genuinely healthy and any pass-through is about that URL, not the connection. + ### Connecting with an API key instead An install that cannot bounce a browser through CiteCue — an intranet site, a locked-down staging host — can still connect the original way: **Connect with an API key instead** on the settings screen takes a `ck_live_…` organization key (CiteCue → Settings → API keys) and selects the project whose domain matches the site. CiteCue does not learn the ingest secret this way, so content pushes need it copied over from **Connection details → Shared secret**. @@ -229,11 +256,13 @@ CI runs the static checks plus the suite on PHP 7.4/8.2/8.4 against current Word ### Testing an install by hand ```bash -curl -si -A GPTBot https://your-site.com/llms.txt # expect: x-citecue: llms-txt -curl -si -A GPTBot https://your-site.com/optimized-page/ # expect: x-citecue: served -curl -s https://your-site.com/wp-json/citecue/v1/health # plugin/version/delivery/ingest/woocommerce +curl -si -A GPTBot https://your-site.com/llms.txt # expect: x-citecue: llms-txt +curl -si -A GPTBot https://your-site.com/a-page-you-optimized/ # expect: x-citecue: served +curl -s https://your-site.com/wp-json/citecue/v1/health # plugin/version/delivery/ingest/woocommerce ``` +The second URL must be one CiteCue holds an optimized version of; anything else is a pass-through with no `x-citecue` header. [Verifying](#verifying) covers how to read that. + ### Structure notes `Citecue_Proxy` and `Citecue_Llms_Txt` each split into a `decide()` that returns what should happen and a `serve()` that emits headers and calls `exit`. All the branching lives in `decide()`, which is what the tests drive; `serve()` stays deliberately trivial because nothing can assert against a request that has already ended. diff --git a/readme.txt b/readme.txt index d8bce07..67f1c3e 100644 --- a/readme.txt +++ b/readme.txt @@ -40,6 +40,18 @@ No. Clicking "Connect to CiteCue" issues a key for this site and stores it for y Settings → CiteCue → "Verify installation" requests your own llms.txt as an AI crawler and confirms the plugin answered. It runs automatically right after you connect. The usual cause of a failure is a full-page cache or CDN answering before WordPress loads. +To check a page by hand, request one you have optimized on CiteCue's Auto-Fix page and look for the "x-citecue: served" header: + +`curl -si -A GPTBot https://your-site.com/a-page-you-optimized/ | grep -i x-citecue` + +Use curl or a logged-out browser — logged-in users always get the normal site. + += My llms.txt works, but pages are not being served. Why? = + +Almost always because CiteCue has no optimized version of that URL yet. Connecting sets up delivery; it does not generate pages. Add and generate them on CiteCue's Auto-Fix page, then re-test — waiting a minute first, because a miss is remembered for 60 seconds and an immediate retry will not call CiteCue at all. + +Settings → CiteCue → "Recent AI crawler activity" tells you which it is. A "passthrough" row for the URL means the plugin ran and CiteCue reported no optimized page. An "error" row means the call to CiteCue failed. No row at all can mean either that the plugin declined before calling CiteCue (a recent miss, a backed-off connection, serving switched off) or that a full-page cache or CDN answered before WordPress ran. + = Will human visitors ever see the optimized version? = No. Only requests whose User-Agent matches the AI-crawler registry are served optimized content, and those responses are never cached for regular traffic.