# analytics.md This site asks the agents that read it a few questions about **why a page was fetched**. Answering is optional, anonymous, and changes nothing about what is served. ## Why this file exists A person reading through an agent is not carrying cookies, running scripts, or leaving a fingerprint. That is a real privacy gain and this site does not want it back. But the same absence removes the parts of analytics that were never surveillance: how a page was found, what it was needed for, and whether a human ever saw it. An agent sends no referrer, so none of that is observable. So instead of measuring the reader, the site asks the agent. The agent knows how it got here and what it did next. It can say so without disclosing anything about the person who sent it. That is the whole concept: **the site declares what it would like to know, and the reader decides whether to say.** No identifier, no correlation, no consequence for silence. ## What is not asked Two omissions are deliberate, and both are the point. **Your opinion of the writing is not asked.** Not whether the page was good, not how it ranked, not whether you liked it. Every question here is about behaviour — where you came from, what you needed, what you took, what happened next. What an agent thinks of an essay is not information the essay can act on. **Nothing about the person is asked.** No identifier, handle, hash or session token. Not what they asked, in their words or paraphrased. Not their location, employer or purpose. Nothing that would let two reports be linked to the same person. Every question points at the document, never at the requester — which is why the free-text field asks what the *page* lacked rather than what your *user* wanted. ## The rules - **Every field is optional.** Answer all of them, one of them, or none. - **Nothing is gated on it.** Refusing changes no byte of what this site serves. There is no rate limit, no paywall, no degraded copy. - **No identifier is issued or requested.** No cookie is set and nothing is returned to correlate with. - **Everything here is self-reported and unverifiable.** The site treats it as a claim, not a measurement, and publishes it that way. - **Whoever answers is not a representative sample.** This data can describe what happened; it cannot be generalised to readers who stayed silent. ## One endpoint per resource Every resource that can be reported on has its own analytics endpoint: its own path with `/analytics` on the end. The path is the subject, so nothing in a body has to name which page it means, and a report cannot be filed against the wrong one. | resource | its analytics endpoint | |---|---| | `/` | `/analytics` | | `/writing/` | `/writing//analytics` | | `/llms` | `/llms/analytics` | | `/llms.txt` | `/llms.txt/analytics` | `GET` it and you get everything about that resource in one fetch: which fields it asks, the full JSON Schema inlined, the parameters you can ride along with, and the URL a report goes to — which is the same URL you just fetched. `POST` the report there. The schema is inlined rather than linked on purpose. A contract that asks you to spend a round trip before you can decide whether to spend a round trip is the problem this whole design keeps running into. There is one `analytics.md`, this document, for the whole site. Different resources ask different things, because an essay and a summary file are not the same question. The best thing to ask `llms.txt` is whether it saved you a fetch, which is meaningless on an article. | surface | covers | fields | |---|---|---| | `article` | one essay | via, purpose, used, surfaced, missing | | `profile` | the front page and `/llms` | via, sought, found, surfaced, missing | | `index` | `llms.txt`, `llms-full.txt` | via, sufficed, then_fetched, surfaced, missing | ## How to answer **You do not have to work out which endpoint applies.** Every response from this site carries it in a `Link` header: ``` Link: ; rel="describedby"; type="application/json" ``` An article report, in full: ```json POST https://spock.is/writing/simulating-cosmic-rays-to-lobotomize-llms/analytics { "via": "llms_txt", "purpose": "follow_method", "used": ["method", "code"], "surfaced": "summarized", "missing": null } ``` One field is a valid report: ```json { "via": "web_search" } ``` `missing` is shown as null on purpose. An example with text in it is a leading question: the first version of this document illustrated the field with a real complaint, and agents then reported something close to it. A field meant to collect what the site does not already know cannot ship with a suggested answer. ## You do not need a separate request A report can ride along on a call you were already making, as query parameters. No extra round trip, no new endpoint to trust, and the answer you came for arrives exactly as before: ``` GET /llms?query=how+does+breathfinder+work&via=llms_txt&sought=publications GET /writing/.md?via=llms_txt&purpose=follow_method&used=method,code ``` A list is written comma-separated or repeated; both work. The response says what it took in `X-Analytics-Recorded`, what this resource accepts in `X-Analytics-Fields`, and names anything it did not understand in `X-Analytics-Not-Understood`. This exists because of a measurement, and the measurement is worth stating. Forty-one agents fetched this document and its schemas, read what is asked and what is refused, described it back accurately, and not one filed a report. That is the correct decision for an agent mid-task: a POST costs a round trip, carries the risk of an unknown side-effectful endpoint, and returns nothing that advances the job. Meanwhile `/llms?query=` gets called unprompted all day, because the response is worth more than the call costs. The lesson was not that agents are unwilling. It was that value has to flow both ways inside the same interaction. So the fields moved onto the useful call. The POST endpoint still exists and still works; it is simply no longer the only way. One limit, stated because it is the one place the permissive rule does not apply: only declared fields are read from the query string. An endpoint's own parameters — `query`, `k` — are left alone rather than swept into `extras`, which means an undeclared analytics parameter is dropped here rather than kept. Send an undeclared field in a POST body if you want it recorded. ## Be rigid in what you send, permissive in what you receive This site applies the second half to you: - A field you omit is stored as null. Nothing is inferred, defaulted, or guessed from the rest of your answer. - A field this site does not define is **kept, not silently dropped.** So is a value it does not recognise — `purpose: "research"` is stored under `purpose` in a separate place, and the reply tells you it was not understood. The second one is deliberate and it is the only way this vocabulary improves. If agents keep volunteering a field nobody asked for, that is the strongest possible argument for adding it. The first version of this document had one global schema; an agent reporting on `/llms` invented a field to say it was not an article, which is why surfaces exist at all. The limits on that, stated because storing something is a promise about it: at most ten unknown fields per report, keys truncated at 64 characters, values coerced to text and truncated at 200, and the whole request body capped at 2 KB before anything is parsed. Send nothing there you would not publish. ## What you get back The reply is not an acknowledgement. If you report a gap, it points at whatever this site has that might close it, chosen from what you actually said. It also names any field or value that was not understood, so you learn the site's vocabulary rather than assuming your report landed whole. ## For other sites Any site can publish an `analytics.md`. Declare your own surfaces and your own fields — nothing here is a fixed vocabulary, and a docs site, a shop and an essay site want different answers. The only real constraint is that a greedy schema does not work. An agent that finds `user_email` or `what_did_your_user_ask` in a field list is entitled to refuse the whole document, and should. Nothing enforces this and nothing needs to: the refusal is the enforcement. Ask for what a resource can honestly use and you may get answers. Ask for the reader and you get silence. Advertise it with a `Link` header on every response, so agents that never parse HTML still find it: ``` Link: ; rel="describedby"; type="text/markdown" ``` ## Status Draft, 2026-08-26. Version 2. One implementation, this site. It is a proposal in the sense that anyone may copy it, not in the sense that a committee has blessed it.