{"title":"The instrument, published before the data","what_this_document_is":"The measurement, written down before there is anything to measure. It says what counts as use, what gets logged, how long it is kept, and what will be published — so that a report six months from now cannot be accused of choosing its measure after seeing the numbers. If you connect an agent to this endpoint, this is what happens to that call.","question":"Does anyone actually connect an agent to a personal career site, and what do they ask it?","document_status":{"state":"running","last_updated":"2026-10-18","meaning_of_pre_launch":"The endpoint is live but unannounced, and measurement_start is not set. Everything here is being settled BEFORE the question is asked of anyone.","commitment":"Once measurement_start is set, every subsequent change to this document is added to `revisions` below, with the date and the reason. A pre-declared measure that can be quietly edited afterwards is not a pre-declared measure. Changes to the DEFINITIONS of what counts will not be made after that point at all; only clarifications, corrections of fact, and additions.","revisions":["2026-08-06 — threshold, demand classes and reporting commitment set (Rafe).","2026-08-07 — traffic type and acquisition separated into two dimensions; they had been collapsed into one mutually exclusive class, which lost information (an invited ChatGPT user is both human-invoked and invited, and only the invitation was recorded).","2026-08-07 — raw query retention raised from 30 to 180 days; see kept_365_days.","2026-08-29 — raw query retention raised to 365 days, and the key renamed from kept_180_days. The running container had been pruning at 30 days throughout: the compose default was never raised with the code, so this document and the landing page both disclosed a window that was not being kept. No file had yet reached the cutoff.","2026-08-07 — the permanent term ledger narrowed to counted traffic only.","2026-08-07 — generic client libraries reclassified from human_invoked to external.","2026-08-07 — rewritten for a reader rather than a maintainer; three internal inconsistencies fixed, listed under corrections.","2026-08-07 — landing-page analytics added (separate Umami site ID, cookieless, page views excluded from the threshold); WebMCP tools noted as present on rafeblandford.com behind an experimental browser API.","2026-08-08 — corpus revision added to every response and every logged call (follow-up brief §4.2, brought forward from post-launch). The corpus is regenerated daily, so without it no trace could say which evidence answered it. Brought forward rather than deferred because a trace written without the revision can never gain one afterwards.","2026-08-08 — acquisition split from two kinds into three: invited, channel, unknown. A `via` token had meant only 'Rafe invited this person', so a token embedded in a public listing would have been reported as an invitation and subtracted from organic discovery — the exact opposite of what it evidences. Made before any tagged link was published and before measurement_start, which is the only point at which a definition here can still change.","2026-08-08 — connections.ndjson added: handshakes are now recorded, in their own file, and remain excluded from the threshold. This ADDS a signal rather than changing what counts, and it is being made before measurement_start is set. The reason is that a signed-in ChatGPT session on 8 Aug connected and enumerated all four tools while producing zero log lines, which would have left a six-month null unable to say whether anyone had arrived at all. See connection_counter.","2026-08-13 — directory conformance auditors reclassified from external to monitor. `SaSame-MCP-Audit/0.1` ran once a day from 9 Aug, calling the same two tools and then probing a method that does not exist, and fell through to `external` because an unrecognised agent lands in the class that counts. Eight calls, 89% of everything classed external at that point. It arrived `via=registry`, so it was also being reported as organic discovery — the finding this whole measurement exists to produce. Traffic type is decided before `via` is read, so classifying it correctly is what stops an automated liveness check from manufacturing the result. The eight rows already written are NOT edited; both readers re-derive the class from the recorded user agent, one-way (counted to excluded only), and a provenance marker records this in calls.ndjson.","2026-08-13 — no running total is published before measurement_start is set. The weekly reading had carried 'Running total against the pre-declared 20: 99' for six days while the window was still shut, and 90 of those 99 were Rafe driving agents at his own endpoint to test it. Counting pre-launch traffic towards a threshold defined over the six months after the announcement is the flattering error this document exists to rule out; it arrived by accident rather than by choice, which is why it went unnoticed.","2026-10-16 — four corrections to what is recorded, made after the endpoint had real use, so any analysis splits at this date. (1) `server/discover`, the 2026-07-28 discovery method, is now logged as a connection; a successful discover had left no trace. (2) A request the protocol refused is logged as a failure even when it arrives as HTTP 200, which older protocol revisions do, a tool call refused for bad arguments, which had left no trace, is logged as `tool_error` (a category, never the caller's input); and only an answered call counts: four refused reads had been counted, mostly a duplicate line for a not_found the handler had already logged. (3) What a request declares about itself (`_meta` clientInfo, or `initialize` on older revisions) is recorded in new fields beside `client`, which keeps its old meaning so no series splits; the declaration remains a claim and never changes the traffic type. (4) MCP directories, scorers, liveness probes and research sweeps are classed `monitor`, extending the 13 Aug decision: 163 counted calls came from agents that describe themselves that way, and 12 from Rafe's own preflight agent sent without its token, now `self_test`. Rows already written are not edited; both are re-derived on read, one-way. Counted calls on the logs to date: 622 before, 443 after. A bare `node` agent (230 of the 443) reads all four resources in lockstep and is probably a sweep, but `node` is also what a real client sends, so it stays counted pending a decision.","2026-10-16 (later) — a failed call records a fixed error category (`unknown_slug`, `unknown_topic`, `unknown_section`) rather than the error message, which quoted what the caller had typed: 89 rows had carried caller text into a file whose rule is that it holds none. A read of a writing slug that does not exist is logged under the template `rafe://writing/{slug}`, not the URI asked for. A slug that resolved to nothing is treated as a question about writing that does not exist: its words go to the bounded, redacted query file and its terms to the backlog, as a search does. Neither changes what is counted. The same deploy moved the server to SDK 2.3.0 with subscriptions switched off; that changes what the server offers, not what is recorded.","2026-10-16 (W3) — three additions, none changing what counts. `completion/complete` (slug autocompletion) is logged as a connection, like discovery, and the partial value the caller typed is never logged. A read of the new `rafe://writing` catalogue is a call, like any resource read. Search and evidence results now carry resource links to the posts: a read that follows one is an ordinary read, and whether clients follow links is read from the sequence (a search, then a read of a slug it returned, on the same identity), because the protocol gives a client no way to mark a read as having come from a link.","2026-10-16 (W5) — a fifth tool, map_role_requirements, which checks up to eight subjects from a role in one call. It adds nothing check_experience could not say: each row is that tool's result, unchanged. One call is ONE line in calls.ndjson, however many requirements it carries, so a role-fit question that took 9 to 15 check_experience calls now counts once. Any analysis of call volume splits at this date. Each requirement enters the topic ledger and the query file under its own kind, as a check_experience topic does; the role title is kept only as a hash.","2026-10-16 (W5, App) — map_role_requirements now carries an MCP App (`ui://rafeblandford/role-evidence-matrix`), a table a host that supports MCP Apps can show instead of text. It adds no tool and no data: the tool's result is unchanged, and the App shows only what the text already carries. A read of the template is logged as a CONNECTION with method `ui/template`, never as a call, because it measures which hosts render Apps rather than use. The App makes no calls back to the server; links it opens and the questions its gaps button asks the host's model to write never reach this endpoint, so they are not recorded.","2026-10-16 (aliases) — a subject with a generic qualifier at either end (“CTO leadership”, “MACH architecture”) now reaches the alias or declared term inside it, and says which word was set aside. Only when the subject as typed has nothing to cite, and only to reach an alias, a declared term or the site's own alias wording. Found in the first Claude web trace. Phrasings like these were not_found before this date and may be supported after it, so the topic backlog splits here for them.","2026-10-16 (host behaviour) — two corrections applied when the log is read, never by editing it, and able only to remove rows from the count. (1) Link prefetch: two or more reads of rafe://writing/{slug} by the same client within 10 seconds of one of its own tool calls are the host fetching every link in a result, not anyone choosing to read; ChatGPT did exactly this with all 12 links on its first map_role_requirements call. One read after a call still counts. (2) Repeats: a tool call identical in every logged field to one by the same client within 15 seconds is counted once; ChatGPT made the same call twice, seven seconds apart. Known limit: a host that reports two separate conversations identically can have them merged. The report states how many rows it set aside, and why.","2026-10-16 (awards, speaking) — the profile's awards and speaking-and-media entries are now indexed as evidence. Both were published and rendered on /career/ but were never built into check_experience's index, so 'awards', 'speaking' and 'podcast' were not_found. Queries on those subjects split at this date.","2026-10-16 (shown in the work) — a partial or not_found answer on a subject Rafe has declared (prototyping, consulting, mentoring and similar) now carries `shown_in_the_work`: published work that demonstrates the subject without naming it, which word matching cannot see. The reader is asked to read it and weigh it; the status still records only the word match and never changes. The profile section lists the same groups. Its links are a separate channel (priority 0.8). A read that follows one is an ordinary read.","2026-10-17 (shown in the work, widened) — eleven groups instead of three: making; product strategy and advisory; senior client relationships; shipping products; technology transformation and modernisation; leading organisational change; leading people at scale; communication; applied AI and agentic systems; innovation and emerging technology; agency innovation (workshops, discovery and co-creation). Machine learning is deliberately not pointed. New aliases change answers from this date: 'transformation', 'business transformation' and 'technology transformation' answer as Digital transformation; 'public speaking', 'presenting' and 'keynotes' as the speaking entries; 'org design' as operating model.","2026-10-17 (role title) — the role title passed to map_role_requirements is now kept in words in the year-long, redacted query file (kind map_role_requirements.role), Rafe's decision. It was kept only as a hash from 16 Oct, on the argument that a title can name a search the caller has not made public; titles logged before this date stay hashed. The call line in calls.ndjson still carries only the hash, and the title never enters the permanent topic ledger.","2026-10-18 — two changes. (1) `plugin` added to the declared channel tokens: the using-rafeblandford-com agent plugin will carry the endpoint with `?via=plugin`, declared here before anything carrying it ships. Adds a route; changes nothing counted. (2) A call to a tool that does not exist is now answered as a JSON-RPC -32602 protocol error, as the MCP specification (2025-11-25) requires, instead of a tool result marked isError; bad arguments are unchanged. It is still logged once, as `tool_error` / `unknown_tool`, and was never counted as use.","2026-10-18 (Rafe's own link) — calls carrying `?via=rafe`, the token on Rafe's own Claude, ChatGPT and Codex connectors, are his own use and are classed `self_test`: never counted towards the threshold, still logged with the token so his use stays visible. They had been read as invitations. Rafe: \"my own tagged calls should not count towards the 20\". Decided before measurement_start was set; applied to earlier rows on read, one-way. Since 1 Sep this moves 68 calls out of the counted figure, leaving 33.","2026-10-18 (threshold) — the pre-declared threshold raised from 20 to 100 counted calls in six months, by Rafe, after seeing the use so far and before measurement_start was set. Between 1 Sep and 18 Oct, with no post published, 33 calls counted (an upper bound: some untagged Claude and Codex calls may be Rafe's), on the order of 125 over six months at that pace, so 20 would have been crossed by use that had already happened. Raised, never lowered: a harder bar, set with the data in view, and said so here.","2026-10-17 (behaviour) — two machine behaviours are excluded on read from the external class, one way, whatever the user agent: a resource sweep (4+ distinct rafe:// resources within 10 seconds with no tool call within 10 minutes) is a crawler, and a burst of 3+ tool calls within 2 seconds that are all not_found is a monitor. A client naming itself glama or a health checker in the protocol is a monitor. Prompted by the generic `node` user agent: 230 lockstep reads and a 5-call probe since August. The report states how many rows it reclassified; nothing in the log is edited.","2026-10-18 — measurement_start set to 2026-09-01T00:00:00Z, BACKDATED: Rafe's call on 18 Oct 2026: \"treat the mcp as live now - its public. posts to follow\", then \"I would say it's been live since 1st September\". Every series was split at that instant in one operation; earlier lines are kept aside, not deleted. From here, changes to this document are additions and corrections only; the definitions of what counts are closed.","2026-10-18 (directories) — additions only. `smithery` and `mcp-so` declared as channel tokens before either listing exists, so calls through them are attributed to the listing rather than read as invitations. `SmitheryBot` (Smithery's scanner) classed `monitor`; people connecting through Smithery's gateway are not affected by that rule. Nothing already counted changes."]},"pre_declared_threshold":{"number":100,"definition":"Genuine externally initiated MCP calls in six months — the aggregate of the counted traffic types (human_invoked + external), invited or not — excluding Rafe's own tests, uptime monitors and automated crawlers. The aggregate decides whether the threshold was crossed; every published figure also shows the split.","if_below":"The honest answer is 'essentially nobody used it', and that gets published.","if_above":"The counts get published anyway, split by class.","set_by":"Rafe, 6 Aug 2026, before launch. Raised to 100 by Rafe on 18 Oct 2026, before measurement_start was set."},"demand_hierarchy":{"note":"Two dimensions, not one list. What kind of caller it was, and how they found it. Kept separate because an invited ChatGPT user is both, and collapsing them loses the caller type.","traffic_type":{"note":"What kind of caller. Recorded as `demand_class`.","self_test":"Rafe's own probes — probe token, tailnet, or a rafe-smoke agent. Excluded.","monitor":"Uptime checks, and the directories, scorers and liveness probes that sweep MCP servers. Excluded.","crawler":"Automated collection — *Bot and *-SearchBot agents building a training corpus or a retrieval index. Excluded.","human_invoked":"COUNTS. A person driving an agent, identified by an agent string in which the vendor asserts a human — Claude-User, ChatGPT-User and the like. Anthropic's own MCP connector presents as Claude-User.","external":"COUNTS. Everything else, including generic client libraries such as openai-mcp and modelcontextprotocol. Those name software, not a person: the same library serves a signed-in chat session, a developer tool, an API integration and an automated evaluation. Attribution stays conservative on purpose, and it costs the measurement nothing, because external counts too."},"acquisition":{"note":"How the caller found it. Recorded as `via` — a token on the connection link. Orthogonal to traffic type: an invited ChatGPT user is both human-invoked and invited. THREE kinds, because a token means one of two opposite things and its absence means a third.","invited":"A token Rafe handed to a named person. Closer to a functional test with feedback attached than to demand, and every published figure separates it out, because the total will be primed by invitations.","channel":"A token embedded in something Rafe published rather than sent to anyone — the MCP Registry entry, the launch article, the LinkedIn post. THE PERSON FOUND IT THEMSELVES; the token only records which route they took. This is organic discovery and it is the finding the experiment is looking for, so it is never reported as an invitation.","unknown":"No token. Also organic, with nothing to say about the route — and this is expected to be the LARGEST group rather than a residue, because the canonical address is published untagged on the landing page and that is the route the endpoint is designed around: someone reads the site, decides to connect, and copies the address. Acquisition can only distinguish routes that publish their own distinct link. It should not be read as 'found it with no help'.","declared_channel_tokens":["registry","plugins-directory","article","linkedin","card","plugin","smithery","mcp-so"],"why_the_channel_list_is_published":"So that 'which of these is a channel rather than an invitation' is auditable rather than a naming convention. Invitation tokens are deliberately NOT published: they are people's names.","acquisition_is_asserted_not_verified":"Acquisition is asserted, not verified — like client identity. Channel tokens are public by construction — printed in a registry entry and in an article — so anyone can append one, and a forged channel token would inflate the ORGANIC figure. That is the flattering direction, where a forged invitation token could only ever deflate it. It is reported as a claim rather than treated as authenticated, because there is no version of a public listing whose token is also a secret.","corrected_2026_08_08":"Until this date the report counted ANY token as an invitation, and printed 'of which invited N, the rest found it themselves'. A registry pickup would have been subtracted from the number it is strongest evidence for. Found before any tagged link was published and before the clock started."},"how_the_threshold_uses_them":"The threshold is crossed on the aggregate of the counted traffic types. That aggregate exists only to decide whether the line was crossed. Every published figure also shows the split — by traffic type, and by all three acquisition kinds — because the total will be primed by direct invitations, and 'someone asked Claude about Rafe', 'something connected to this on its own' and 'Rafe asked a person to try it' are different findings.","what_is_not_demand":"A request that never reached a tool. A crawler issuing GET on the endpoint gets a 405, a bad Origin gets a 403, and an over-rate caller gets a 429. All are logged, because failures should be visible, and none is counted as use. Only tools/call and resources/read count.","connecting_is_not_demand_either":"A client that connects and asks what is here — tools/list and the other enumeration methods — has not used anything. Those handshakes are recorded, in their own file, and are never counted towards the threshold. See connection_counter under what_gets_logged for why they are recorded at all.","identity_is_asserted_not_verified":"Client identity is asserted, not verified. There is no authentication here, so anything can claim to be any client, and every client name in this measurement is a claim rather than a fact. It is recorded as a claim and will be reported as one."},"what_gets_logged":{"per_request":"Timestamp, the method and tool or resource used, latency, how many results came back, the error type if it failed, which page URLs were returned as evidence, the traffic type, the acquisition token if the link carried one, the client name and protocol version the caller asserts, and which version of the corpus answered (see corpus_revision).","corpus_revision":"Every response carries a `corpus` block — a contract version, the dossier version, the generation timestamp and a SHA-256 digest of the public corpus — and the digest is recorded against every logged call. The reason is that the published corpus is regenerated daily, so six months is on the order of 180 distinct versions of it: without this, a report could say that a question was asked and nothing about what the answer was drawn from. It also means anyone who records an answer from this endpoint can prove later which corpus produced it. The digest deliberately EXCLUDES the generation timestamp, so a regeneration that changed nothing produces the same digest; identical content always gives an identical digest, and any changed published claim changes it. Added 8 Aug 2026, before the measurement window opened, because it cannot be applied to traces after the fact.","no_ip_address":"The caller's IP address is NOT stored. It is used in memory to apply rate limiting and to recognise Rafe's own network, and is not written to any file.","about_query_text":"For search_writing, check_experience and each requirement passed to map_role_requirements, the long-lived file records a hash of the query and the NUMBER of terms in it — not the words. The words go to a separate, shorter-lived file described below. The ROLE title given to map_role_requirements is recorded in calls.ndjson only as a hash and a term count; since 17 Oct 2026 its words also go to the shorter-lived query file described below, under its own kind, and never to the permanent topic ledger.","kept_indefinitely":"Three files. calls.ndjson, one line per request as described above, with no free text at all — this is the six-month instrument. topics.ndjson, the normalised terms of queries from counted traffic only (see permanent_topic_ledger), which is the record of what people looked for and did not find. And connections.ndjson, one line per handshake (see connection_counter).","connection_counter":"When a client connects and enumerates what is here — tools/list and the other listing methods — that is recorded in connections.ndjson: timestamp, which method, latency, the traffic type, and the client name and protocol version the caller asserts. Same fields as a call, no query text, no IP address. It is NOT demand and is never counted towards the pre-declared 20; it is kept in a separate file so that it cannot be added to the count by accident. WHY IT IS RECORDED AT ALL: without it, a client could connect, read the full tool list and call nothing, leaving no trace whatsoever — so a six-month result of zero could not distinguish 'nobody connected' from 'people connected, looked, and found nothing worth calling'. Those are different answers to the question this endpoint exists to ask, and the second is the more interesting one. WHAT IT IS NOT: it counts handshakes, not people. Sessions here are stateless, so one client reconnecting five times writes five lines, and a client that re-establishes per conversation writes one per conversation. It is an upper bound on curiosity, and any published figure will say so. Added 8 Aug 2026, before the measurement window opened.","kept_365_days":"queries-YYYY-MM-DD.ndjson — the query words themselves, with contact details stripped: email addresses, phone numbers, URLs, long digit strings and @handles. Raised from 30 days to 180 on 7 Aug 2026, because the six-month report is written at around 180 days and a 30-day file would have expired its own evidence; raised to 365 days (a year) on 29 Aug 2026 so the report can look back across its window rather than at exactly it. Still bounded rather than indefinite, and never published without consent.","what_redaction_is_not":"Pattern matching, not entity recognition, and not anonymisation. It will not remove a person's name, a company name, or a job description that identifies someone by role plus employer. Please do not paste anything into a query that you would not want kept.","permanent_topic_ledger":"The normalised terms of each query are kept indefinitely, and ONLY for counted traffic types (human_invoked and external). The argument for keeping them forever is that a stranger's unanswered question is a content backlog worth having; that argument does not cover Rafe's own probes, an uptime monitor or a crawler, so those are never written to it. For a one-word query the normalised term is the word that was typed, so treat it as retained rather than discarded.","never":"Anything in the private source dossier that is not marked public. The server process does not hold that file at all; it reads only generated public projections of it.","rate_limit":"60 requests per 60 seconds per caller and method, as a backstop. Exceeding it returns 429 and is logged as a rejection, not as use.","rotation_at_announcement":"calls.ndjson, connections.ndjson, topics.ndjson and every pre-launch query file are rotated together, in one operation, at the moment measurement_start is set. Otherwise the permanent backlog would open with Rafe's own test vocabulary while the traffic number opened clean.","landing_page_analytics":"The human landing page at / carries Umami — self-hosted, cookieless — on its own site ID, separate from rafeblandford.com's. It is NOT on /mcp, and page views are NOT part of this measurement: they never count towards the pre-declared threshold, which counts MCP calls only. The reason for a separate ID is that the people who read the page and the agents that call the endpoint are different populations, and merging them would lose both. Added 7 Aug 2026 and disclosed on the page in the same change."},"reporting":{"checks":["48 hours — technical failures","one month — early behaviour","monthly thereafter"],"report":"A field report at six months, not a benchmark and not a study: descriptive counts, privacy-safe item-level outcomes, a failure taxonomy with concrete examples, changes made because of not_found, and an explicit null if no demand appeared.","commitment":"Published either way.","uptime":"An uptime monitor checks the endpoint independently, so that a quiet outage cannot be mistaken for an absence of demand. Its calls classify as monitor and are excluded."},"constraints":{"maintenance_ceiling":"Under one hour a month after the first month. If it exceeds that, it fails its own test.","reversibility":"One Caddy block and one container. Removing it removes it.","human_site":"Unaffected. Nothing in this experiment changes how rafeblandford.com behaves for a person."},"what_this_is_not":["Evidence that machine demand exists. That is the hypothesis, not the premise.","A controlled study. Model versions, routing and client behaviour all move underneath it.","A complete record of Rafe's work. It serves what the site publishes, which is a selection.","A verified identity check on callers. See identity_is_asserted_not_verified.","A statement about Rafe's experience. check_experience answers only from what this site publishes, which is a selection of his work; not_found means the corpus does not evidence a topic, never that he lacks it."],"corrections":{"note":"Things this instrument got wrong before launch, kept rather than quietly fixed. Each would have biased the result, and all were found and corrected before the measurement window opened.","2026-08-07":["Claude-User was classified as a crawler and excluded, because the crawler list was copied wholesale from the website's own analytics. That is right for a web page and wrong for this endpoint: on an MCP endpoint a Claude-User call IS a person driving an agent. Left uncorrected, the six-month number would have been structurally zero regardless of genuine use.","Rafe's own model-driven evaluations reach the endpoint through the same connector, and so were indistinguishable from real demand. They now tag themselves with a probe token in the URL and classify as self_test.","Transport-level rejections were counted as demand. Six phantom calls had accrued against the threshold before it was caught.","Six calls from a generic OpenAI client library were briefly classified as human-invoked. The client string names software, not a person, and the calls did not support the stronger reading. Reverted to external, which still counts.","This document said the permanent term ledger held the terms of EVERY query while another section correctly said counted traffic only. The narrower statement was the true one; the contradiction is fixed here."]},"what_is_served":{"tools":["get_evidence","get_post","search_writing","check_experience","map_role_requirements"],"resources":["rafe://profile","rafe://career","rafe://case-studies","rafe://provenance","rafe://writing"],"resource_templates":["rafe://writing/{slug}"],"prompts":["assess_role_fit","brief_me","what_does_he_think_about","interrogate_the_evidence"],"mutating_tools":0},"how_to_challenge_this":"If something here is wrong, or you want the record of your own queries removed, the contact route is https://machines.rafeblandford.com/.well-known/security.txt — it is a person, not a form, and there is no automated route to Rafe from this endpoint by design.","measurement_start":{"value":"2026-09-01T00:00:00Z","note":"Set this to the exact ISO timestamp of the ANNOUNCEMENT, not the deployment. The endpoint went live 7 Aug 2026 and sat unadvertised; 'ships' means the launch article and the invitations, because that is when the question 'does anyone use this' starts being asked. Every series is rotated in the same operation and under the same timestamp — see rotation_at_announcement. This said 'rotate calls.ndjson' until 8 Aug 2026, which contradicted that section and would have left the permanent term ledger opening with pre-launch test vocabulary while the traffic number opened clean."}}