=== Atelier ===
Contributors: aquarelleai
Tags: mcp, ai, agent, geo, seo
Requires at least: 6.9
Tested up to: 7.0
Requires PHP: 8.1
Stable tag: 0.23.2
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Turns a WordPress site into something an external AI agent can build with, over MCP, without the site's contents ever passing through that agent.

== Description ==

Atelier is the production end of an agent. An orchestrating agent decides what a
site should look like; Atelier does the work inside WordPress. The two speak
MCP, so any client that speaks that protocol can drive it.

The point of the split is that bytes never cross the agent's context window. An
image is generated by a model this site calls, written straight into the media
library, and referenced by id. The agent asks for it and hears that it exists;
it never carries the file.

The plugin is itself the MCP server. There is no separate process, service or
daemon: the endpoint is a REST route on the site, served by the same PHP as
everything else.

= What it can do =

* Read what the install actually supports before planning anything, and the
  design vocabulary the theme defines, so new markup is written in the site's
  own scale rather than in literals that drift from it.
* Read and change the site title, tagline and logo.
* Read and change colours, gradients and the palette, through global styles.
* Install a webfont from Google Fonts, served from this site rather than linked,
  so no visitor request reaches a third party.
* Read the block structure of any page or template — as an index, or as the
  markup itself — and edit it a block at a time against a hash of what was read,
  reaching a block nested inside a column without rewriting the section holding
  it.
* Generate images and video, read images that already exist, and edit, crop,
  resize and convert what comes back.
* Write and install CSS, browser JavaScript, block markup, and complete PHP
  modules — each behind its own switch, all of them off by default.

= One account, a model per kind of work =

Choose a provider — OpenAI, Google Gemini, Anthropic Claude, xAI Grok, Moonshot
Kimi, or any endpoint that speaks the same API — and give it one key. Its address
is filled in for you and read-only unless you ask to change it.

Then pick a model for each kind of work: text, images, reading images, video, and
markup. Those lists come from your own account, not from a hard-coded table, so
every name offered is one your credential just proved it can use. Typing model
names is how the previous version worked and it was the main source of wasted
time: a name that is merely wrong looks exactly like one your plan does not
include, and both surface as a 404 much later, from inside a queued job.

Video is the exception and says so. No service offers video generation through
this API, so that category speaks each provider's own protocol and works only
where that protocol is implemented — currently Google, for Veo.

= Nothing is on until you turn it on =

Activating this plugin opens nothing. The master switch is off, every code kind
is off, and no host is fetchable until one is named. Each switch says what it
grants rather than what it is called, because "PHP modules" does not tell an
operator that it means an external agent may write code the server executes.

== Installation ==

1. Upload the plugin folder to `/wp-content/plugins/`, or install the zip from
   Plugins > Add New > Upload.
2. Activate it. Nothing is enabled yet; this is deliberate.
3. Go to Atelier in the admin menu. Choose a provider, paste its API key, then
   press "Reload models" and pick one for each kind of work you intend to use.
   Test each one — the result is what the provider actually answered.
4. Turn on the master switch, and only the code kinds you want.
5. Connect a client. Most will do this themselves: point them at
   `https://your-site/wp-json/atelier/v1/mcp`, and they will send you here to
   sign in and approve. For a connector that asks for a Client ID and Secret,
   create an application on the Atelier screen and paste in what it gives you.
   For one that takes a token directly, create a token there instead.

The site must be served over HTTPS with a certificate the agent's machine
trusts. A self-signed or private-CA certificate is the most common reason a
client refuses to connect.

== Frequently Asked Questions ==

= Does this need a separate server? =

No. The plugin is the MCP server. The endpoint is a REST route on the site.

= What redirect URL do I put in? =

Usually none. Most clients, Claude among them, register themselves: give them
`https://your-site/wp-json/atelier/v1/mcp` and they negotiate the rest, bringing
their own address with them. You only enter one for a connector that asks for a
Client ID and Secret, and that connector prints the address on its own setup
screen. If you cannot find it, try connecting anyway — the attempt will be turned
away, and the address it wanted appears on the Atelier screen ready to add.

= How does a client that cannot hold a token connect? =

Turn on the authorisation flow in the settings. The client then registers itself,
sends whoever is setting it up to this site to log in and approve, and keeps a
credential it refreshes on its own. Registration by itself grants nothing: every
grant needs somebody signed in to press Approve, and what they approve is an
account acting as them.

= What can a leaked credential do? =

Whatever its scope allows, acting as the WordPress user it was issued for.
Revoke it from the Atelier screen. Prefer the `Authorization` header over the query
string: a credential in a URL is written to the log of every proxy on the path.

= What happens if a generated PHP module breaks the site? =

It is caught and quarantined, and the site recovers on the next request. For the
failures that cannot be caught — memory exhausted, execution limit reached — the
loader records that a module was mid-load and quarantines it on the following
request. If everything else fails, creating an empty file at
`wp-content/atelier-modules/.disabled`, or defining `ATELIER_DISABLE_MODULES` as
true in `wp-config.php`, stops every module without needing the admin.

= Why can't I see the JavaScript it generated? =

Because you can edit posts. A generated script runs with the session of whoever
loads the page, so it is never served to an account worth impersonating, nor on
any admin screen. Check it in a private window.

== Changelog ==

= 0.23.2 =
* A person sent by an assistant who lands through a redirect — the bare
  domain to www, a missing trailing slash — counts once, where they land,
  instead of once for the redirect and once for the page. When the redirect
  loses what marked them as sent by an assistant, the redirect is kept as
  the only trace of the arrival.
* People from AI, and the geo-referrals tool, say which clicks cannot be
  counted: one opened from an assistant's app, or from a link it did not tag,
  carries neither a referrer nor a utm_source and cannot be told from a
  direct visit.

= 0.23.1 =
* Home opens on the figures and charts: machine requests, AI search reads,
  reads for an answer, people sent by AI, pages read and crawlers that can
  read — over the period on screen, with the comparison — then requests by
  day and class beside who reads. What needs attention follows as one list,
  most serious first and with nothing repeated; then the modules by area,
  and last the site's census, its checks in order and its latest changes.
* An assistant reading a page to answer someone (ChatGPT-User, Claude-User,
  Perplexity-User) is no longer labelled as if it were a person: its class is
  "Reading for an answer", and "People from AI" shows those reads beside the
  people who followed a link, by operator, counted apart and never matched.
  The agent tools' descriptions say the same.
* Home and AI traffic take their headline figures from the same calculation,
  so they always agree.

= 0.23.0 =
* A new layout for the admin screen: five zones across the top — Home,
  Observe, Improve, Site and Settings — with the screens of each in a second
  bar, and no sidebar of Atelier's own, so the whole width goes to the data.
  WordPress's menu lists the same zones. Every screen from before is in its
  place, and addresses saved before the change still open it.
* One period for every screen that observes over time, kept in the address
  and remembered between screens: 24 hours, 7 or 28 days, or any range of
  whole days within what is kept. "Compare" sets it against the period of the
  same length just before: each figure says how it moved, the daily chart
  draws the period before as a dashed line, and a period before that was
  only partly recorded is said to be.
* "Go to…" (Ctrl K, or ⌘K on a Mac) finds a screen, or a page by its address
  or title, and opens it.
* An activity indicator shows the jobs waiting and running, each described
  in words.
* Sheets keep the keyboard inside while open, close with Escape and give the
  focus back to what opened them. Charts can be read as tables, are walked
  with the arrow keys, and carry a sentence with their totals for screen
  readers; the weekday-and-hour chart is now reachable by keyboard.
* Home brings together the state of the site and the GEO summary.
* For agents: `geo-traffic`, `geo-traffic-agent`, `geo-traffic-page`,
  `geo-coverage`, `geo-referrals` and `geo-traffic-log` take a range of whole
  days (`from`, `to`); `geo-traffic` reports the period before with
  `compare`; `census-resources` searches addresses and titles with `search`.

= 0.22.0 =
* Atelier Free and Atelier Pro. Everything that observes stays free: AI and
  search traffic with verification, the census and coverage, access and
  robots.txt for AI agents, missing addresses, freshness and IndexNow, the
  machine view, the MCP server's reading tools, and the change history with
  undo. Atelier Pro, one license per site, adds AI models, reversible
  changes to the site, engine-metric imports and data streams.
* A license is a PASETO v4.public token signed by Aquarelle AI's license
  service and bound to one site by the SHA-256 of its canonical address.
  Activating it creates a key pair inside the site; the service checks back
  at the site's own address before accepting the key, and every later
  request is signed with it, so a copied license is useless elsewhere.
* A lease, signed by the service and bound to the site and its key, says
  whether the subscription is paid up. It is renewed daily and lasts a
  week, so an outage of the license service never switches a paying site
  off; when a subscription ends, Pro stops at the end of the period paid.
* New License screen: activate, check now, manage billing, move the license
  to another site, deactivate. The Overview says what the site has, and the
  Models screen says what Pro adds.
* Tools that need Pro stay listed for agents and say so; they refuse to
  change anything without it, from agents and from the admin screen alike.
  Reading and removing installed CSS, scripts and modules, and undoing any
  change, are never locked.
* Deleting the plugin gives its license back, so it can be activated on
  another site straight away.

= 0.21.0 =
* AI traffic can count what a page cache answers. A new switch, "Record
  what a page cache answers", off by default, adds a marked block to
  wp-config.php — the one place that runs before a cache's drop-in, and
  belongs to no other plugin — so Atelier sees every request that reaches
  PHP by itself, without relying on the cache. The block only reads each
  request: the cache, the request and the response are left as they are.
  Requests are screened against rules WordPress compiles, as signed JSON
  read as data, and only those the sensor would have recorded are queued:
  machines, and people sent by AI assistants without address, full
  referrer or query. They are classified by the sensor's own classifier
  when the queue is drained, and a request WordPress goes on to answer is
  the sensor's, so nothing is counted twice. Measured on WP-Optimize: every
  cached crawler hit recorded, about 0.04 ms per cached page.
* The block is removed when the switch is turned off or the plugin is
  deactivated or deleted, leaving wp-config.php as it was to the byte. A
  WordPress update does not touch it; another plugin editing its own lines
  keeps it; paths are relative to wp-config.php, and a check daily and on
  every admin screen rewrites a block whose paths no longer match. Where
  wp-config.php is not writable, or DISALLOW_FILE_MODS is set, nothing is
  forced: the switch says so and shows the lines to add by hand. Not
  available on multisite.
* The page cache check tells a cache the observer sees (status
  `observed`, figures not lowered) from one that answers without PHP, and
  offers the switch where a cache answers in PHP.
* Output a clean discarded is no longer counted in a machine visit's size.

= 0.20.1 =
* The model's reading of a page is accepted when it writes a single block
  as [n]. The first reading on a real site was right and was refused over
  that form alone, so the landmarks decided instead. A flat pair such as
  [9, 35], which could mean one range or two blocks, is still refused
  rather than guessed, and the model is now told to write a single block
  as [n, n]. Readings made under the previous instructions are not reused.
* AI traffic names the page cache that hides visits from it. A cache that
  answers before WordPress runs keeps those visits from the sensor, and the
  counts fall as the cache fills with no error anywhere. Atelier reads what
  is installed and confirms it with a two-fetch probe that never counts as
  a visit, and geo-traffic, geo-traffic-agent, geo-traffic-page,
  geo-coverage and geo-referrals report it under confidence.page_cache: the
  cache named, what is and is not counted, and the evidence.

= 0.20.0 =
* The census reads links from the pages the site actually serves, not only
  from stored content. Page builders, themes and plugins print links storage
  does not hold — a grid of the latest posts, related posts under an
  article, a menu an add-on draws — and on a site built that way the census
  used to report most pages as unreachable. A background pass now fetches
  each page as an anonymous visitor would and adds the `<a href>` links it
  receives to the link graph, as their own source, whatever built the page.
* The pass is written for slow shared hosting: one page at a time with a
  pause between fetches, short background slices, no cache bypass (a page
  cache answers as it answers anybody), a back-off when the site answers
  429 or 503 or keeps failing, and readings kept for a day so a rebuild only
  fetches what is new or has aged. Every setting is under `census_served_*`,
  and `census_served_links` switches the pass off.
* Links the front page carries are taken as the site's chrome and recorded
  once, from the front page, as stored chrome already was.
* The confidence block reports both sources: how many pages were read,
  failed, cut short or are stale; how many links exist only in served HTML,
  with examples; and how many pages only served links reach.
* It also names what storage could not show: Elementor widgets that print
  links at render time (Posts, Loop Grid and Carousel, Post Info, Essential
  Addons grids and menus and others), with where they were met, and the
  displayed Elementor templates other than header and footer.
* Orphans are pages no link of any source reaches; outbound links count
  distinct destinations; links from pages no longer published no longer
  count. `wp atelier census served` runs the pass in the foreground.
* The machine view reads a served page as numbered Markdown blocks built
  from what HTML itself defines, and the model in the text slot decides
  which blocks are the page's own content; code assembles the text, so it
  cannot add a word the page does not hold. Without a working slot the
  page's landmarks decide, and the reading says which. seo-serve-check and
  geo-content-diagnose return that content as Markdown, beside what the
  landmarks alone would have taken.
* Migrating from per-slot providers no longer points text and code calls
  at a media endpoint that does not speak chat completions.
* New identity: the glass-ribbon mark in the admin sidebar and the
  WordPress menu, Atelier's indigo across the screen, and Aquarelle AI as
  author.

= 0.19.0 =
* A GEO & AI workspace gathers everything about how search engines and AI
  systems find, read and cite the site: Traffic, Access, Missing addresses
  and Freshness now sit under it in the sidebar, with room for what comes
  next. Navigation is two levels — workspaces and pages in the sidebar,
  views as tabs inside a page — and each page names its workspace in a
  breadcrumb.
* The workspace opens on a summary: what needs attention across every area,
  most serious first, and one module per area — access by kind of agent,
  machine traffic and people sent by AI with their trend, coverage of
  indexable pages, who reads, missing addresses, freshness and IndexNow,
  and engine reports — each loading on its own and opening its page.
* Every screen uses the full width of the window instead of a centred
  column, and the sidebar folds to icons, remembered per browser, to give
  data and charts even more room.

= 0.18.3 =
* The AI access screen is redesigned so the posture is read at the top,
  not found by scrolling: a verdict for the whole site (how many crawlers
  can read it, how many were seen in 28 days, addresses evaluated) and one
  tile per kind of agent with how many can read and a bar of allowed,
  partly closed, blocked and opted out; a tile filters the table.
* Quick policies are shown side by side with what each blocks, by kind of
  agent, and the one in force is marked.
* Every agent sits in one table grouped by kind, with its access now, the
  change drafted for it, when the site last saw it and the decision, plus
  allow-all and block-all for each group, search, and filters by access and
  by whether it was seen. Allowed and blocked decisions are coloured.
* An agent's details open in a side panel: what it does, what blocking it
  costs, the robots.txt group and rule that apply, the addresses closed to
  it and its traffic on the site, with a link to AI traffic.
* robots.txt is shown with numbered lines and Atelier's rules highlighted.

= 0.18.2 =
* Genuine crawlers were reported as false claims when their operator lists
  the same address block for several of its crawlers: OpenAI publishes
  blocks in both OAI-SearchBot's and GPTBot's lists, and a claim was checked
  against only one of them. A claim is now verified when the address is in
  any list of the claimed crawler's operator; another operator's crawler
  name from that block is still a false claim. On upgrade, visits already
  recorded as false claims from a network their operator publishes become
  unverifiable, since only the network was kept.
* A program that names itself without a browser signature — any user agent
  without "Mozilla/" — is recorded as a machine even when its name carries
  none of the usual words such as bot or crawler.

= 0.18.1 =
* Two crawlers seen on a live site join the AI agents catalogue:
  Meta-WebIndexer, which builds the index Meta AI answers and cites from,
  and Seltz, a web search API for AI agents, which reads robots.txt and
  sitemaps as SeltzSitemapFetcher. Both are AI search now, not "other".
* An operator that documents nothing about robots.txt is marked as such in
  AI access, and blocking it is reported as uncertain, like one that may
  ignore the file.
* Redirects read correctly in the log: a redirect is recorded under the path
  as requested (`/about` → `/about/`), and one that only changes the host or
  the scheme (bare domain → www, http → https) keeps the whole target
  address instead of looking like a redirect to itself.

= 0.18.0 =
* The AI traffic screen is rebuilt as an analytics workspace. Seven views
  replace the single long page: Overview, Agents, Pages, People from AI,
  Engine reports, Log and Data export. The period, the view, every filter
  and any open detail live in the address, so a state can be reloaded,
  shared and left with the back button.
* Overview: six headline figures with their daily trend, what needs
  attention (server errors met by crawlers, false claims, pages no index
  has read, undeclared crawlers, missing addresses) each opening the view
  that explains it, requests per day by agent class with a legend that
  switches classes, who reads the site by purpose, requests by weekday and
  hour of the site's clock, and how the site answers machines (response
  time, p95, data served, status and kind of address).
* Agents: a sortable table with search, purpose and identity filters, and
  a bar of verified, false and uncheckable requests per agent. A detail
  panel shows its requests per day, status mix, pages read and how often it
  returns to each, networks and user agents.
* Pages: coverage of indexable pages by each kind of agent, and a matrix of
  when each one last read each page, shaded by recency; the pages machines
  read most, and the pages assistants fetched live for people. A detail
  panel per page lists every agent that read it and the people sent to it.
* People from AI: arrivals per day by assistant, landing pages, devices,
  languages, countries, campaign tags and time on page.
* Log: every visit, newest first, with combinable filters (kind, status,
  identity, purpose, agent, assistant, text in the address), details on
  demand, older pages on request and an optional live mode.
* New MCP tool `geo-traffic-log` reads the same log with the same filters;
  `geo-traffic` now reports requests by weekday and hour per class, and
  `geo-coverage` lists every indexable page with its reads per class.
* Charts are drawn by the plugin in SVG — no charting library — and each
  has a table behind it for screen readers; the arrow keys read a chart day
  by day.

= 0.17.1 =
* A client connecting to the MCP server is no longer counted as traffic: the
  OAuth discovery documents and the authorise, token and register endpoints
  are skipped like the MCP endpoint itself. Reconnecting a connector from
  claude.ai was recorded as a script visit and as a person sent by Claude.

= 0.17.0 =
* Machine and AI traffic. Every visit of a crawler or other automated client
  is recorded after the response is sent — agent, operator and purpose, page,
  status, bytes, server time, redirect — and every person arriving from an AI
  assistant's answer (ChatGPT, Perplexity, Gemini, Copilot, Claude, DeepSeek,
  Grok, Meta AI and others) is counted by assistant, landing page, device
  class and language, without address, user agent or identifier. Recording
  never delays or breaks a page, and skips Atelier's own MCP calls.
* Crawlers are verified against the address ranges Google, Microsoft, OpenAI
  and Perplexity publish, fetched daily and compiled into a compact index
  searched on every request: each claim is verified, false, or unverifiable,
  and requests from those ranges that do not name the crawler are recorded as
  undeclared. Behind a proxy, the client address header is believed only from
  declared or private proxies.
* Reports as MCP tools and a new AI traffic screen: `geo-traffic` (agents,
  classes, days, pages, errors, response time, last robots.txt and sitemap
  read), `geo-traffic-agent` (pages read and how often it returns),
  `geo-traffic-page`, `geo-coverage` (indexable pages no search or AI search
  engine has read, from the census) and `geo-referrals`.
* External analytics: visits, the change journal and census snapshots are
  offered as ordered streams with cursors — pulled with `stream-read` or
  pushed to a collector in gzip batches signed with HMAC-SHA256 over a
  timestamp, acknowledged per batch and retried with backoff. The collector is
  set only from the Atelier screen, its secret stored encrypted. The contract
  is in docs/collector-protocol.md.
* New credential preset, Analytics collector, limited to reading streams and
  reports. The SEO and GEO preset now includes redirects and IndexNow.
* Visits are kept 30 days and daily totals 400, both configurable, with a row
  limit; totals are recomputed hourly and old visits pruned daily.
* What the engines report: Search Console's generative AI report and Bing
  Webmaster Tools' AI Performance have no API, so their CSV exports are
  imported from the AI traffic screen — headers in any of their languages,
  UTF-8 or UTF-16, locale number formats — previewed first, and a new import
  of the same report and period replaces the old. Figures sit beside the
  site's own crawler visits and arrivals per page, with Bing's grounding
  queries ranked; `geo-engine-metrics` reads them and a fourth stream,
  `engines`, carries them to the collector.
* Optional time on page for people sent by AI assistants: a small script,
  off by default and acting only on those arrivals, reports once how long the
  page stayed visible — no cookie, nothing stored, no identifier — to a
  public, validated and rate-limited route. Reports show average time and the
  share of engaged visits per assistant and page, and are never counted as
  arrivals.
* The overview names crawler trouble the sensor saw: server errors served to
  crawlers, impostors, no AI search crawler all week, failing deliveries to
  the collector. Each page's sheet shows who read it.

= 0.16.5 =
* Taking a page with real content out of search now names it in the
  preview, with its address and word count, as a critical note; the
  threshold is the `seo_noindex_warn_words` setting (300 by default). The
  search settings panel states which page a change applies to.

= 0.16.4 =
* The change history no longer lists previews. A preview changes nothing,
  but it was listed among the changes as one that could not be undone. The
  journal still records every call, and `history` returns previews when asked.

= 0.16.3 =
* On sites with no SEO plugin, posts and terms marked noindex through
  Atelier now leave WordPress's sitemaps. Listing a page that also asks not
  to be indexed is a contradiction search engines report as an error.

= 0.16.2 =
* The census reads the links a block theme's post and page templates add to
  every post: its categories and tags (Post Terms) and the previous and next
  post (Post Navigation Link), following template parts and patterns, with
  the template chosen as core chooses it. On a blog these reach most posts
  and every category archive, which the census reported unreachable. They
  are counted as navigation, not as links the author wrote.

= 0.16.1 =
* Fixes allowing an agent in robots.txt freeing it from the rules the site
  writes for every crawler. An agent named in a group of its own obeys only
  that group, so a bare "Allow: /" let it into /wp-admin/ and anywhere else
  closed to all. An allow group now repeats those rules and drops only the
  ones that close the whole site. Sites that already allowed agents get the
  corrected rules without doing anything.
* Fixes the census treating links written as /?page_id=N, /?p=N, /?cat=N or
  /?tag_id=N as addresses of their own, which made pages linked that way from
  the menu look unreachable. They now count as links to the page, and the
  overview reports them: each one costs every crawler a redirect.

= 0.16.0 =
* Missing addresses: the paths AI and search agents ask for that this site
  answers with 404 — usually old addresses of pages that moved, still in their
  indexes and in the answers they give. Found in the access logs when the site
  may read them, and recorded as they happen everywhere else: only requests
  whose user agent names a catalogued agent, and only the path, the agent and
  the time. Probes for software the site does not run, and missing images or
  scripts, are left out.
* A proposed destination for each, among the pages open to indexing and never
  the front page, with its score and what it rests on: words the addresses
  share and words in the title, weighted by how rare they are, with spelling
  variants and inflections counted, and the letters shared by the last
  segment. A proposal another page fits almost as well is marked ambiguous.
* Redirect rules (301) and gone rules (410) that only ever answer a 404, so
  they cannot hide a page and stop applying if one is published at that
  address. New abilities `phantom-urls` and `redirect-write` (previews by
  default, checked against what the site serves, undoable), a Missing
  addresses screen, and overview checks for rules whose target went away.
* Deleting the plugin now also removes what versions 0.11 to 0.15 stored:
  the robots.txt policy, IndexNow state, freshness records, Atelier's own
  search settings and their scheduled tasks.

= 0.15.0 =
* Substantive changes told from cosmetic ones: every save of a published page,
  including Elementor's, is compared with the text it replaced. An edit is
  substantive when at least a fifth of its five-word runs differ across 25
  words or more, or 150 words were added or removed; the thresholds are data.
  Each page keeps a change log and the date of its last substantive change.
* Honest dates, off by default: the modified date themes display and the
  last-modified of WordPress's, Yoast's and Rank Math's sitemaps follow the
  last substantive change instead of the last save. Pages with no recorded
  change keep WordPress's date — Atelier does not invent dates.
* IndexNow, off by default: new, substantively changed, moved and removed
  addresses are queued and sent together after a short delay, with the key
  served at the site root only while it is on. Pages closed to indexing are
  left out. Every answer from the endpoint is explained.
* New abilities `freshness-report` and `indexnow-submit` (previews by default),
  and a Freshness screen with recent edits, pages possibly outdated, and the
  IndexNow queue and history.
* Fixes dialogs on the admin screen showing without their colours and with a
  transparent footer: they render outside the application's root and did not
  receive its design tokens.

= 0.14.0 =
* Content diagnosis for AI answers: `geo-content-diagnose` reads a page as a
  crawler that runs no JavaScript does and reports what it offers a machine
  answering a question — where the substance starts and what the page opens
  with; each section under its heading, whether the heading is a question and
  the sentence that follows it; concrete figures, cited sources, lists and
  tables; who is said to have written it and when, and whether the declared
  update date is honest.
* Finds text addressed to AI models where people cannot read it — hidden
  elements, HTML comments, attributes and meta tags — which search engines
  treat as spam and which can attack visitors' assistants.
* Every finding states how strong the evidence behind it is: strong (documented
  by the engines or measured at scale), moderate (observational studies or
  experiments that did not fully carry over) or good practice with no measured
  effect. No score, no prediction of citation, and nothing is rewritten: the
  agent gets each section's first sentence to decide what to rewrite.
* Thresholds, question words in six languages and the model-instruction
  patterns are data, each extendable by filter.
* Each address's sheet in the census gains a content diagnosis panel.
* Fixes robots.txt being read before other plugins and core had added their
  rules, which since 0.13.0 could leave the sitemap line out and make
  crawlability follow an incomplete file. It is now read when first needed.

= 0.13.0 =
* Search settings through the SEO plugin the site runs: `seo-meta-read`,
  `seo-meta-write` and `seo-meta-audit` read and change a post's or term's
  title, description, canonical and indexing in the storage of Yoast SEO, Rank
  Math, SEOPress, Slim SEO or All in One SEO — the same keys their own editors
  write. An unrecognised SEO plugin makes them read-only rather than guessed at.
* With no SEO plugin, Atelier keeps these settings itself and prints them
  through core's hooks — description, custom title, canonical for archives,
  noindex — and never runs alongside an SEO plugin. The census honours the
  noindex it sets.
* Every write is previewed first, with what it means (a noindex removes the
  page from search and the AI answers built on it; a canonical elsewhere asks
  engines to credit another address) and how a result might read. Applied, the
  served page is read back and each field reported as served, not served, or
  left to a template. Undoable with `change-undo`, down to settings that did not
  exist before.
* The audit reports pages with no description of their own and descriptions
  shared by several pages.
* New `seo_write_enabled` switch for agents; administrators apply from the
  address sheet in the census, which now shows a search result preview.

= 0.12.0 =
* AI access matrix: `geo-access-matrix` evaluates robots.txt for every search
  and AI agent that matters — Googlebot, Bingbot, OAI-SearchBot, GPTBot,
  ChatGPT-User, Claude-SearchBot, ClaudeBot, Claude-User, PerplexityBot,
  Perplexity-User, Google-Extended, Applebot-Extended, CCBot and more — against
  every address in the census. Each agent states what it is for (search index,
  AI answers, training, fetching for a person, or a usage control), whether its
  operator says it honours robots.txt, and what blocking it actually changes.
* `geo-robots-write` allows or blocks AI agents in the robots.txt WordPress
  serves, by preset or agent by agent. Previews by default, with the consequence
  for each agent and the file before and after; presets never touch search
  engines; an agent already named by other rules is never given a second group.
  Applying can be undone, and needs the new `robots_write_enabled` switch for
  agents — administrators can always apply from the screen.
* New AI access screen in wp-admin, and health checks for search engines or AI
  search agents shut out by robots.txt.
* The census account of what it could read is now replaced only when a rebuild
  completes, so it never disappears while one runs.

= 0.11.0 =
* Machine view: `seo-serve-check` fetches a page the way a crawler that runs no
  JavaScript does — no session, an honest user agent, only this site — and
  reports status and redirects, robots and snippet directives from meta tags and
  X-Robots-Tag, canonical, title, description, headings, the text actually
  served and how the page opens, structured data types and links. Most AI
  crawlers never run scripts, so this is what they read.
* Findings where the served page disagrees with what WordPress stores: a
  noindex added by a filter, a canonical pointing elsewhere, a 404 for a
  published page, content drawn only by JavaScript, text hidden from people.
  The served verdict is written back to the census.
* Sitemap membership is now real. The census build reads the sitemaps the site
  serves — found through robots.txt, as crawlers find them — whichever plugin
  generates them. Absence is reported only when every sitemap was read.
* New census filters and health checks: not in sitemap, served differently,
  canonical elsewhere. Each address's sheet in the admin screen can read the
  page as a crawler would.
* New settings: `machine_fetch_base_url`, for sites whose public name does not
  resolve from inside the server, plus fetch timeout, size and cache limits.

= 0.10.0 =
* A new admin screen, inside wp-admin under the same Atelier menu. It opens on a
  health check that says what is wrong, how bad it is and what fixes it; every
  figure opens the list behind it. Census explorer with filters, sections and a
  sheet per address showing the pages linking in and out. A history of every
  change with undo. Connections with scoped tokens chosen from presets. Models
  with live tests. Safety switches that say what each one grants.
* Changes can be undone. Before a write, the prior state is journalled; after
  it, a fingerprint of the result. `change-undo` restores the prior state only
  while nothing has touched the object since, and undoing an undo reinstates the
  change. `change-history` lists what was changed and whether it can be undone.
* Tools that both read and wrote are split: `site-identity`, `design-colors` and
  `design-typography` now only read, and `-write` tools change. A credential can
  finally be granted reading without writing.
* The census link graph reads what block themes render: `wp_navigation` menus,
  template parts, the front page template, patterns, synced patterns, page
  lists and core's navigation fallback (read without creating anything), plus
  classic-theme widgets and Elementor Pro headers and footers. Sites whose
  navigation lived in any of those were reported almost entirely unreachable.
* Blocks whose links only exist at render time (query loops, latest posts…) are
  counted and reported rather than guessed at.
* Crawlable and indexable are now separate answers. robots.txt is parsed to RFC
  9309 — groups, Allow, longest match, wildcards — for a configurable reference
  crawler. An empty per-post SEO setting now inherits the type's default in Yoast,
  Rank Math, SEOPress, AIOSEO and Slim SEO instead of being reported indexable.
* Builder template post types (Elementor, Divi, Beaver, Oxygen, Bricks…) are no
  longer counted as addresses.
* Structural changes — templates, navigation, menus, widgets, theme — rebuild the
  link graph on their own.
* Fixes a queued census build losing its stage on every slice, which kept large
  sites from ever finishing the link graph.

= 0.9.15 =
* Adds the `page-duplicate` MCP action. It creates a new page from `post:id`,
  copies stored content, its assigned template, featured image and Elementor
  presentation metadata, and defaults to a draft with its own identity and slug.

= 0.9.14 =
* Includes links stored in Elementor's `_elementor_data` documents when
  building the census graph. Elementor pages and templates no longer appear
  unreachable merely because their links are not present in `post_content`.

= 0.9.13 =
* The provider test now uses the selected model, endpoint, credential and
  temperature directly from the unsaved settings form. It no longer tests the
  previously saved model or forces temperature to zero.
* Recognises providers that report `invalid temperature` and retries without
  that optional parameter, remembering the model-specific requirement.
* Removes the completed OAuth/MCP diagnostics panel from the settings screen.

= 0.9.12 =
* Discovers account-local cPanel, LiteSpeed and Plesk access-log locations in
  addition to the system paths. cPanel Raw Access `.gz` archives are read with
  fixed compressed and expanded-size limits rather than being returned as bytes.
* Adds an administrator-only field for additional local log locations. Clients
  still select only discovered source slugs, never filesystem paths.

= 0.9.11 =
* Fixes the `census-resources` MCP action. Its row formatter could access no
  reporter instance, causing a PHP exception instead of returning the census
  rows. Summary and resource listing now use the same live census data.

= 0.9.10 =
* Every built-in MCP tool now publishes an output schema. The schemas describe
  stable read, change and queued-work fields without treating mode-specific
  fields as mandatory, so clients can reason about results while calls remain
  valid.

= 0.9.9 =
* Implements the MCP 2026-07-28 discovery result used by ChatGPT. The response
  now publishes `supportedVersions`, moves the server identity to the standard
  `_meta` key, and includes the required private cache policy.
* When a client selects MCP 2026-07-28, tool listings and tool-call results now
  include that revision's mandatory result discriminator and cache fields.
  Older protocol revisions retain their previous response shape.

= 0.9.8 =
* Adds a bounded OAuth and MCP diagnostic trace to the Atelier settings screen.
  It records protocol stages, status codes, content types, protocol revisions,
  response sizes and tool counts so client-specific setup failures can be
  diagnosed on hosts whose web-server logs are unavailable.
* The trace retains only the newest 60 exchanges and deliberately excludes
  tokens, authorization codes, client IDs, secrets, cookies, tool arguments and
  IP addresses. Administrators can clear it at any time.

= 0.9.7 =
* Tools now use concise names such as `census-summary`; calls using either of the
  two earlier Atelier-prefixed forms remain accepted during upgrades.
* Every tool explicitly declares `openWorldHint`, completing the safety
  annotations required by ChatGPT's action importer. Tools that may contact a
  model provider, Google Fonts or an allowlisted media host are marked open;
  operations confined to this WordPress installation are marked closed.
* `tools/list` now returns only the standard catalogue fields. Removed private
  cache metadata that was redundant with the endpoint's non-cacheable HTTP
  response and offered strict importers an unnecessary compatibility edge.

= 0.9.6 =
* MCP tool names now use the portable `atelier_` namespace prefix. OpenAI's
  function-tool importer rejects dots in names, so the former `atelier.` prefix
  let OAuth finish but made ChatGPT reject the complete `tools/list` catalogue.
  Calls using names cached from earlier Atelier versions remain accepted.

= 0.9.5 =
* Browser-hosted MCP clients can now complete the CORS preflight: Atelier allows
  the MCP protocol headers on its REST route and exposes the OAuth challenge and
  negotiated protocol headers. Previously the browser blocked the request before
  OAuth or `tools/list` reached Atelier, while server-side clients appeared fine.

= 0.9.4 =
* OAuth discovery may now begin from any client origin, and a verified Atelier
  bearer token may return from that origin without site-specific configuration.
  Foreign origins still cannot use WordPress cookies or Application Passwords,
  preserving the DNS-rebinding protection for ambient credentials.
* OAuth metadata now advertises `offline_access`, matching the refresh tokens
  the server already issues so clients can keep a connection alive reliably.

= 0.9.3 =
* A dynamically registered confidential OAuth client now receives its secret
  exactly once, as the registration protocol requires, and is told to use
  `client_secret_post`. Previously it received neither; its secret was stored
  only as a digest, so it could never authenticate when exchanging a code.

= 0.9.2 =
* The census tables were never created, so every build ran to completion and
  recorded nothing. The census registered its listener for the storage-migration
  hook *after* the migration had already run, and that hook fires once per version
  change while the recorded version advances regardless of who was listening — so
  the one moment it could have acted was missed, permanently. First-party storage
  is now installed directly by the upgrader, like the journal and the queue, and
  listeners are registered before the migration rather than after it.

= 0.9.1 =
* Permalinks were built from an incomplete post, because the census selected only
  the columns it read and `get_permalink()` needs two it does not. A WP_Post
  missing them keeps the class defaults — an empty slug and a zero date — so
  addresses came back pointing at 1970 with no slug in them. Nothing errored.
* A refused database write is no longer swallowed. It used to turn every
  storage-level problem into one symptom — a build that runs to completion and
  records nothing — which is also what a site with no content looks like. The
  census now reports how many resources it found alongside how many it stored,
  and shows the database's own message when those differ.
* The census screen states the host and install path it measures addresses
  against, and shows one real permalink with the address it was recorded as. A
  site whose permalinks carry a different host than `home_url()` has every
  address rejected as external, and the only symptom was an empty census.

= 0.9.0 =
* Adds the site census: an inventory of everything on this site that has a public
  address, including the pages nothing links to. A crawler discovers a site by
  following links, so what it produces is not an inventory but the reachable
  subset of one — which excludes precisely the pages worth finding. Reading the
  database instead gives the whole set, and it is the half of every coverage
  question that no external tool holds.
* Maps internal links, so a page nothing has read comes with the reason attached:
  orphaned, a dead end, or unreachable from the front page. Click depth is
  measured breadth-first over rows rather than in memory, because on shared
  hosting the memory limit binds before the clock does.
* Four read tools for an agent: `census-summary` first because it is a fixed size
  whatever the site's, then `census-section`, then `census-resources`. Every list
  is capped and reports whether it was truncated, so a partial answer cannot be
  mistaken for a complete one.
* Indexability is a three-valued answer, not a boolean. Where an SEO plugin is
  active that Atelier cannot read, the census reports undetermined rather than
  guessing — and says how many rows that covers, so the figures above it can be
  judged instead of taken on trust.
* Builds without a console. Content hooks keep the census current one row at a
  time; a full pass runs through the job queue's loopback, through WP-Cron, or
  from the admin screen, which is the path that always works because somebody is
  already there.

= 0.8.1 =
* Image and video generation reappears in clients that check a tool's schema
  before offering it. PHP cannot tell an empty map from an empty list and encodes
  both as `[]`, and a slot with nothing configured produced exactly that where
  JSON Schema requires an object — so the schema was invalid and the tool was
  refused. Refusing it is correct, and is why this took finding: a client does
  not report a broken tool, it drops that one and offers the rest. The site looks
  connected, the catalogue looks complete, and one capability has quietly stopped
  existing. Every schema is now checked on the way out, not just the one place
  this was found, because any empty property set fails the same way and is as
  invisible.

= 0.8.0 =
* The design vocabulary a theme defines can now be read. A block theme does not
  express size, spacing or colour as literal values: it defines a palette, a type
  scale and a spacing scale in theme.json, and blocks refer to entries by slug.
  Colours and font families were readable; the type scale, the spacing scale and
  the content widths were readable nowhere, so an agent writing new markup had to
  invent them. Markup written with invented literals looks right on its own and
  drifts from everything around it, and drifts further with every theme change,
  because the literal is the one value on the page that stops following.
* Each entry carries both forms markup refers to it by — `var(--wp--preset--…)`
  for a stylesheet and `var:preset|…|…` for a block attribute. They are not
  interchangeable and the difference is not guessable from either one.
* This vocabulary already existed, assembled for the model that composes block
  markup inside the plugin, and was never offered to a caller composing markup
  from outside — the agent this plugin exists to serve. Both now read one source,
  so neither can be told something the other is not. The internal one gains the
  content and wide widths in the exchange, which it had also been missing.

= 0.7.0 =
* Reading a layout no longer stops at a summary. The index names blocks and
  previews their text, which is what makes it cheap, and there was nothing else:
  an agent that had chosen what to change could not see what it was changing.
  Rewriting a section then meant reconstructing it from a preview, and a
  reconstruction that is merely close destroys precisely what a summary leaves
  out. Three things were missing and all three are here.
  * `layout-read` returns block markup exactly as stored, for a document or for
    one block in it. What comes back can be edited and handed straight back to
    `layout-edit`. A document too large to return is refused with the instruction
    to narrow it by path, rather than truncated — truncated block markup is
    invalid markup and reads as complete.
  * `layout-outline` takes a path and a depth, so a caller can index inside one
    block instead of the whole page. The default stays two levels, because a
    deeper default would return the document rather than an index of it.
  * `layout-edit` takes a `parent_path`. Positions then count the children of the
    block that path names, so a block nested in a column can be changed on its
    own. Before this the only address was a top-level index, which meant every
    nested change was a rewrite of the whole section around it.
* The index reports the addresses each block links to. Previews run through
  `wp_strip_all_tags`, which is what makes an index an index and which took every
  href with it — so a footer's legal links could be seen to exist, and read, at
  no depth whatsoever. They were the one detail a caller cannot invent and their
  loss was silent.

= 0.6.2 =
* A connected client is now told when Atelier is switched off, instead of being
  handed an empty list of tools. An empty list is a truthful answer to a
  credential that may use none of them, so it could not also be how the master
  switch reported itself — and a client shown one says there are no tools and
  stops, which reads as a broken plugin. The switch also lives on a screen the
  person setting up the client may never see. It now refuses by name, and the
  client repeats the reason.
* The warning on the settings screen said that with the switch off every call is
  refused. It was not: the client connected and signed in normally and only then
  found nothing, so anyone who had just watched a client connect successfully
  concluded the warning did not apply to them. It now describes what actually
  happens.
* Deleting the plugin now removes the provider account and the registered
  clients too. The account holds the encrypted API key, directly under a comment
  saying credentials are the first thing removed, so deleting the plugin left
  behind exactly what a person deleting it would most expect to be gone.

= 0.6.1 =
* Connects to clients that previously refused the server outright. Four
  departures from the protocol had crept in, and each of them fails in the same
  unhelpful way: the client reports that the address is not an MCP server rather
  than naming what it disliked.
  * A revision this server does not implement was refused with an error.
    Version selection is a negotiation — a server answers with a revision it does
    speak and lets the client decide — and refusing is something a conformant
    server cannot do, so the refusal reads as "not an MCP server". The oldest
    revision still in wide use is now also answered directly.
  * Every response named the newest revision implemented rather than the one the
    exchange had agreed on, which reads to a client as the server changing
    protocol underneath it.
  * Tool listings and tool results carried extra properties beside the ones the
    protocol defines. A client validating strictly rejects the whole listing,
    so the server appears to offer no tools.
  * A notification, which is defined to be acknowledged with no body, was
    answered with an empty JSON array.
* Signing in is on by default, as the previous release said it was. The setting
  was declared twice and the stale copy won, so the flow was off on every new
  install and there was no way to tell from the screen that this had happened.
  Sites that never turned it on by hand had no discovery documents at all, which
  is why clients fell back to asking for a Client ID and Secret that should never
  have been needed.

= 0.6.0 =
* You no longer have to find out a client's redirect address. When one is turned
  away for asking to be sent somewhere it has not registered, the address it
  asked for is shown on the settings screen with a button to add it. Adding is
  still a decision made while looking at the address, because an address that
  arrives in a link is exactly how a code gets delivered somewhere it should not.

= 0.5.0 =
* Everything needed to connect a client can now be done from the admin screen.
  Nothing requires a command line, which most people running WordPress do not
  have on the machine their site runs on.
* Create an application and get a Client ID and Client Secret to paste into a
  connector that asks for them, alongside the two addresses it wants.
* Create and revoke access tokens from the screen, for clients that take one
  directly.
* Signing in is on by default. Registering grants nothing on its own; every
  grant still needs somebody signed in to approve it.

= 0.4.0 =
* Clients can now be authorised by a person instead of being handed a token.
  Some MCP clients, ChatGPT among them, offer no field in which to type one, so
  their only choices were this or no authentication at all. Off by default.
* The 401 now says where to authenticate rather than only that something was
  missing, so a client can offer a sign-in button instead of reporting failure.
* An authorised client acts as the WordPress user who approved it, with that
  user's capabilities, and the grant appears in the ordinary token list — so it
  expires, is scoped, and is revoked like any other.

= 0.3.0 =
* Adapts to models that renamed or withdrew a parameter. Newer models reject
  `max_tokens` in favour of `max_completion_tokens`, and several accept only
  their default temperature. Atelier reads the refusal, corrects the request,
  retries once, and remembers the correction for that model. There is no table
  of model names to keep up to date.
* A temperature control for text, markup and vision, on the settings screen.
* A panel showing everything a client needs to connect — the endpoint, the
  header, how to mint a token — and what is currently in the way of it.
* Saving no longer requires resubmitting the endpoint address. A partial save
  used to be refused, or could clear it.

= 0.2.0 =
* One provider account replaces the four independent provider slots. Pick a
  service, give it one key, and choose a model per category from the list your
  own account returns rather than typing names from memory.
* Added a category for reading images, which is a different job from making them
  and usually a different model.
* Existing per-slot settings are adopted automatically on upgrade. Where the
  slots pointed at more than one service only one can survive, and the screen
  says which and what it could not keep.
* The address for each service is filled in and read-only unless you ask to
  change it.
* Reasoning controls are no longer sent unless asked for. Sent by default they
  made strict endpoints reject every request.
* The image connection test now reaches the network. It previously reported
  success without contacting the provider at all.

= 0.1.0 =
* First release.
