Skip to content

Changelog

What actually shipped

Dated, specific, and started on the first day of work rather than backfilled to look busy.

The dispute clause pointed at a service the EU switched off

  • The terms sent consumers to the European Commission’s online dispute resolution platform. That platform stopped accepting complaints in March 2025 and was shut down on 20 July 2025, when Regulation (EU) 2024/3228 repealed the regulation behind it, and traders were required to remove the link by that date. Ours was still there, and the URL now redirects to a notice that the service has moved, so a consumer following it was told a route existed and then shown a relocation page.
  • What replaced it is the thing the law still asks for: whether this trader is subject to an out-of-court dispute resolution body, stated either way. We are not committed to one and not obliged to use one, so the terms say exactly that, and name the two routes that do exist, which are writing to us and the courts already named in the same section. If that ever changes, one field in the company record changes and the clause follows it.
  • The legal gate had a required check called “the ODR route” that failed the build if the sentence was taken out. So the gate was enforcing an obligation that no longer exists and demanding a link that no longer resolves. It now checks for the out-of-court position instead, and a second check fails the build if anything in the site ever links to the discontinued platform again.
  • Found while planning the translated versions of these pages, which is the reason it was worth finding now: a defect in an English legal page becomes the same defect in six languages and five more jurisdictions the moment it is translated.

The demo widget was still selling the old product

  • The site stopped being a WordPress chatbot on 10 September and became a support plugin per CMS. The widget on the demo page never followed: asked what DFYe is, it answered with the old positioning, and its answers about installing, licensing and where transcripts live were all written as though WordPress were the only platform.
  • All six are rewritten, and there is a new one for the question the repositioning was actually about: which CMS is this built for. It answers with the states from the platform table, and with the rule that no platform is called available until a real build has answered a real question on it.
  • The answer about languages promised to reply in whatever language a visitor writes in. It says what is true instead: it reads every language you publish in and answers out of those pages, and it will not invent a language you have not published in, because there would be nothing of yours behind the answer.
  • The widget is 9.4 kB gzipped after the rewrite, against the 20 kB budget the build enforces.

A support plugin for your CMS, not a WordPress chatbot

  • Every text on the site now says what the product is: one plugin, built once per content management system, automating the customer support questions a site’s own pages already answer. The tagline, the description, the hero, the feature groups, the FAQ, the docs, the terms and llms.txt all follow from the same two strings.
  • The handover is now part of the product rather than a footnote to refusal. Three outcomes and no fourth: it answers and cites the page, or it says the site does not cover that, and either way what it will not answer reaches a person with the question and the pages it looked at attached.
  • A new page, Works with your CMS, carries the honest half of “any CMS”. Twelve platforms, each with a state, and the word available appears nowhere: WordPress and WooCommerce are being built, eight are planned, two were asked for. No row may say available until a real build has answered a real question on that platform.
  • The affiliation disclaimers said “not affiliated with WordPress”, which was a complete sentence while WordPress was the only platform named here. They are now generated from the platform table, so a thirteenth platform cannot be added without the legal pages following it, and the legal gate fails the build if a disclaimer names fewer platforms than the site does.
  • The licence clauses in the terms and the EULA stated the GPL as though WordPress were the only platform. Each build now carries whatever licence its own platform requires, and which one applies is named on the key.
  • Two findings from the pass itself: the new page was orphaned because the header hardcodes its links rather than reading the nav table, and a contrast failure I introduced on it was caught by the a11y gate. The three platform states differ by their edge now, never by opacity.

Indigo, not magenta

  • The site’s colour is now electric indigo, #6C4DFF, with trust blue #3B82F6 as the other end of every gradient and a strong violet #8257FF for live states. It replaces Signal Magenta, which shipped for one day.
  • The reasoning: purple alone reads as “AI startup”, and blue is what the research associates with competence and trust, which is what a product asking to answer your customers has to earn. Indigo sits between the two.
  • Gradients run blue to indigo. Never pink to purple: that gesture is the most worn-out one in the category, to the point that Anthropic’s own frontend guidance names it as a cliché to avoid.
  • The ratio matters as much as the hexes and is written down where the next person will look: roughly 75% off-white, 18% near-black, 5% indigo and violet, 2% blue. Not 50% purple.
  • Everything is generated from eight pinned values in OKLCH, and nothing is written unless all 83 contrast pairs pass. Four failed on the first run and were fixed rather than waived: white on the strong violet is 4.42:1, just under the body floor, so anything carrying white text uses the deeper step.
  • The widget was repainted from the same ramps. Its tokens file claimed to be generated and was not, so it had been keeping its magenta while the site changed underneath it; the token build writes it now.
  • Full documentation, including which token names now lie about their colour: docs/design/PALETTE.md.

A new codebase underneath the same product

  • The site was rebuilt on a codebase copied from another of the owner’s properties, for its content machines and its gate suite rather than its content. The plan, the three decisions behind it and the fate of all 591 inherited routes are written down in docs/plan/.
  • Every trace of the other business is gone from anything this site ships, and scripts/qa/check-provenance.mjs fails the build if any comes back.
  • The inherited blog machine required every post to name the product as "the number-one recommendation". That gate is gone: the plugin is not for sale, so no post may recommend it. What replaced it asks for a figure a reader can check instead.
  • Both gate suites merged. The build runs thirteen checks and npm run gate adds seven more in real Chrome: axe on every page in both colour schemes, perf on a throttled phone, reduced motion, 30 interactions, the widget in three hostile host themes, the headers, and 32 checks on the homepage.
  • The homepage film was still promising a fixed price, mapped redirects and thirty days of monitoring, for a service this site does not sell. All five claims are gone and each is now a regex the legal gate fails on.
  • Three contrast failures found by the a11y gate and fixed: the display accordion at 2.21:1, a chip at 4.31:1, and code comments at 3.05:1 in the default syntax theme.
  • The Content-Security-Policy came back. The copied server sent none; the build now emits a sha256 for every inline script so script-src never needs unsafe-inline.
  • Terms rewritten from a service contract to a software licence, including the withdrawal waiver, which was citing the rule for services rather than the one for digital content.
  • The homepage and the pricing page were emitting priced schema.org Offer nodes with availability PreOrder. PreOrder says a buyer can order now, and nobody can, so there is no Offer node at all until there is something to buy.
  • Every URL the site was serving before the rebuild still resolves, and a gate proves it on every build. The blog moved from /journal/ to /blog/ and both posts kept their slugs; the changelog, the two other comparison pages and the internal dashboard were rebuilt at their own URLs rather than redirected somewhere approximate.
  • Three visuals were rendering empty boxes, on the biggest elements of the homepage, because a content pass renamed the data keys without renaming the branches that draw them. Fixed, and now a gate fails on any drawn mock with nothing drawn in it.
  • The widget was going dark inside a light page for anyone whose operating system is in dark mode, on the two pages the product is demonstrated on. The page states its own theme now, which is exactly what the documentation tells a customer with a single-look site to do.

The redesign, and the name

  • DFYe now stands for Done For You Employee, and is written with a lowercase e.
  • Light-first. Signal Magenta, anchored on a measured #cb0088, with the whole palette generated in OKLCH and every text pairing gate-checked at 4.5:1.
  • The widget takes its colour defaults from the same generator as the site, defaults to the visitor’s system font, and is 9.7kB gzipped.
  • Navigation morphs between menus the way stripe.com does it: instant on first open, 240ms after.
  • Scroll-driven progress, a CSS-only overflow detector, and a registered hover property, all without a library.

The site, the widget and the retrieval engine

  • Widget shipped at 7.1kB gzipped against a 20kB budget, with the budget enforced as a build gate rather than a target.
  • BM25-lite retrieval with a confidence floor. Below the floor it declines instead of guessing.
  • GSAP measured at 31kB gzipped for one headline effect and dropped for a 30-line native word split.
  • Design system: dark-first tokens, three variable typefaces self-hosted, full light theme.
  • Market teardown built on the WordPress.org plugin API after Semrush ran out of units.

Nothing before this date, because nothing existed before this date.

Put a real answer on every page.

Ask this site's own widget a question and watch it cite the page, or refuse. It is the same plugin, running on the same content you are reading.