From 1a47d6ea59b79d12d84bd5d5f4d05aefc93c4333 Mon Sep 17 00:00:00 2001 From: lane1 agent Date: Fri, 14 Aug 2026 11:20:51 +0530 Subject: [PATCH 1/2] fix(worker): redirect the duplicate www host to the canonical apex wrangler.jsonc routes www.tinystudio.io/* at this worker, but nothing sent that host anywhere: it answered 200 with a byte-identical copy of the public site, on a second hostname and over plain http. Google therefore holds www.tinystudio.io as a site of its own, and that entity still carries the retired "TinyStudio Agent Desk" site name from when the self-serve desk owned the root - which is why the retired product name still surfaces for this site even though the apex host's title, description and site name are correct. robots.txt, sitemap.xml and every canonical and og:url already name https://tinystudio.io as the single address, so the duplicate host now 301s there, preserving path and query and upgrading plain http. A page-level canonical is only a hint; the 301 is what retires the duplicate site entity along with its stale name. The apex host and the retired app/api hosts are untouched. --- scripts/test-agent-worker.mjs | 58 +++++++++++++++++++++++++++++++++++ src/worker.js | 28 +++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/scripts/test-agent-worker.mjs b/scripts/test-agent-worker.mjs index 509b530e..6d37abcf 100644 --- a/scripts/test-agent-worker.mjs +++ b/scripts/test-agent-worker.mjs @@ -1174,6 +1174,64 @@ test("worker does not serve unlisted asset-like paths outside the public allow-l assert.equal(res.status, 404); }); +// --- Canonical host redirect (dogfood: the retired "TinyStudio Agent Desk" +// title/snippet kept surfacing for tinystudio.io) --- +// +// www.tinystudio.io is routed at this worker by wrangler.jsonc and used to +// answer 200 with a duplicate of the public site, so Google held it as its own +// site with its own, stale site name. It must now redirect permanently to the +// apex host that robots.txt, sitemap.xml and every canonical already name. + +test("www host redirects permanently to the canonical apex host", async () => { + const res = await worker.fetch(new Request("https://www.tinystudio.io/"), {}); + assert.equal(res.status, 301, "the duplicate www host must answer a permanent redirect, not 200"); + assert.equal(res.headers.get("Location"), "https://tinystudio.io/"); +}); + +test("www host redirect preserves path and query, and upgrades plain http", async () => { + const res = await worker.fetch( + new Request("http://www.tinystudio.io/pricing?utm_source=x&utm_medium=y"), + {} + ); + assert.equal(res.status, 301); + assert.equal( + res.headers.get("Location"), + "https://tinystudio.io/pricing?utm_source=x&utm_medium=y", + "the redirect must keep the visitor's destination and campaign parameters intact" + ); +}); + +test("www host never serves a second copy of the public site", async () => { + const env = { + ASSETS: { fetch: async () => new Response("duplicate site", { status: 200 }) } + }; + for (const target of [ + "https://www.tinystudio.io/", + "https://www.tinystudio.io/audit", + "https://www.tinystudio.io/agents", + "https://www.tinystudio.io/agent-desk" + ]) { + const res = await worker.fetch(new Request(target), env); + assert.equal(res.status, 301, `www must not serve ${target}`); + const body = await res.text(); + assert.doesNotMatch(body, /duplicate site/, "www must not return public site HTML"); + } +}); + +test("canonical host redirect leaves the apex and the retired hosts alone", async () => { + const env = { + ASSETS: { fetch: async () => new Response("apex site", { status: 200 }) } + }; + const apex = await worker.fetch(new Request("https://tinystudio.io/"), env); + assert.notEqual(apex.status, 301, "the apex host must keep serving the site, not redirect"); + + const app = await worker.fetch(new Request("https://app.tinystudio.io/"), {}); + assert.equal(app.status, 410, "the retired app host keeps its own retired response"); + + const api = await worker.fetch(new Request("https://api.tinystudio.io/"), {}); + assert.equal(api.status, 410, "the retired API host keeps its own retired response"); +}); + test("retired app host frames the current offer as The Website Appraisal, not the Agent Desk", async () => { const res = await worker.fetch(new Request("https://app.tinystudio.io/"), {}); assert.equal(res.status, 410); diff --git a/src/worker.js b/src/worker.js index e0969d4e..a847ab71 100644 --- a/src/worker.js +++ b/src/worker.js @@ -1387,6 +1387,34 @@ export default { const url = new URL(request.url); const host = url.hostname.toLowerCase(); + // ---- Canonical host redirect (dogfood: Google still presents the retired + // self-serve "TinyStudio Agent Desk" title/snippet for tinystudio.io) ---- + // wrangler.jsonc routes www.tinystudio.io/* at this worker, but nothing + // ever sent that host anywhere: it answered 200 with a byte-identical copy + // of the public site, on a second hostname and over plain http. Google + // therefore holds www.tinystudio.io as its own site, with its own site + // name, and that entity still carries the retired "TinyStudio Agent Desk" + // name from when the self-serve desk owned the root — which is why the + // retired product name keeps surfacing for this site even though the apex + // host's own title, description and site name are long since correct. + // robots.txt, sitemap.xml and every canonical and og:url on the site name + // https://tinystudio.io as the single address, so the duplicate host is + // sent there permanently and drops out of the index. A page-level + // canonical is only a hint; a 301 is the directive that retires the + // duplicate site entity along with its stale name. + if (host === "www.tinystudio.io") { + const canonical = new URL(url); + canonical.protocol = "https:"; + canonical.hostname = "tinystudio.io"; + canonical.port = ""; + return withSecurityHeaders( + new Response(null, { + status: 301, + headers: { Location: canonical.toString() } + }) + ); + } + if (host === "app.tinystudio.io") { return retiredAppResponse(); } From ee363d3b7987552a99e385dbbee5abc377729108 Mon Sep 17 00:00:00 2001 From: lane1 agent Date: Fri, 14 Aug 2026 11:23:19 +0530 Subject: [PATCH 2/2] docs(evidence): re-verify the retired Agent Desk title finding and receipt the www-host fix Re-verifies the shipped noindex/retired-framing fix on head and live, records the 2026-08-14 Google site: measurement showing the apex result is now clean, and documents the residual duplicate www host that kept handing Google the retired site name, with root cause, regression proof and the deliberate non-overlap with open PR #91. --- ...gent-desk-index-reverify-lane1-20260814.md | 57 ++++++++ .../agent-desk-index-reverify-2026-08-14.md | 131 ++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 .lane/reports/docs/agent-desk-index-reverify-lane1-20260814.md create mode 100644 docs/evidence/agent-desk-index-reverify-2026-08-14.md diff --git a/.lane/reports/docs/agent-desk-index-reverify-lane1-20260814.md b/.lane/reports/docs/agent-desk-index-reverify-lane1-20260814.md new file mode 100644 index 00000000..7e61ac6e --- /dev/null +++ b/.lane/reports/docs/agent-desk-index-reverify-lane1-20260814.md @@ -0,0 +1,57 @@ +# Lane 1 — dogfood f41c8af0f8 (retired "TinyStudio Agent Desk" title in Google) + +Date: 2026-08-14 +Branch: `docs/agent-desk-index-reverify-lane1-20260814` +Base: fresh `origin/main` @ `5770bf3` + +## Verdict + +The finding was **half closed and half live**. The apex homepage — the exact +target named in the finding — is fixed and now verified fixed against a real +Google SERP. But the retired name still reaches Google through +`www.tinystudio.io`, a byte-identical duplicate of the site that no PR had +touched. This lane closed that host. + +## What this lane did + +1. Re-verified the shipped `noindex` + retired-framing fix on head and live. +2. Measured the live Google SERP (`site:tinystudio.io`, real browser) instead of + trusting the repo state — which is what surfaced the residual. +3. Traced the residual to `wrangler.jsonc` routing `www.tinystudio.io/*` at a + worker whose host dispatch only handled `app.`/`api.`. +4. Shipped a 301 canonical-host redirect plus four behavioural tests. + +Full measurements, tables and root cause in +`docs/evidence/agent-desk-index-reverify-2026-08-14.md`. + +## Key evidence + +- Live SERP apex result: "TinyStudio — The Website Appraisal" ✅ (finding's + original symptom gone). +- Live SERP www results still carry Google's cached site name + "**- TinyStudio Agent Desk**" and the stale "The Tiny Studio" titles. +- `https://www.tinystudio.io/` served bytes `sha256 b37b0bbe…934e`, identical to + the apex; `http://www.tinystudio.io/` answered 200 with no HTTPS upgrade. + +## Files touched + +- `src/worker.js` — canonical-host 301 for `www.tinystudio.io`. +- `scripts/test-agent-worker.mjs` — 4 new guards. +- `docs/evidence/agent-desk-index-reverify-2026-08-14.md` — evidence receipt. +- `.lane/reports/docs/agent-desk-index-reverify-lane1-20260814.md` — this file. + +No shared report file was written. + +## Tests + +`check-site` pass · worker 80/0 (was 76) · headings 6 · sitemap 7 · UI 16 · +contract 8 · viewport 4 · narrow-pages pass · render-blocking pass. +Regression proof: disabling the host check yields `# pass 77 / # fail 3`. + +## Overlap avoided + +Open PR **#91** already fixes the `/agent-desk` canonical (the other +consolidation path for this same finding); it is `MERGEABLE` but `BLOCKED` on +review. This branch deliberately does not touch `public/agent-desk.html` or the +`check-site.mjs` desk guard, so it does not conflict and the two compose. +Open PRs #142/#95 cover the `/agents` canonical. Not duplicated here. diff --git a/docs/evidence/agent-desk-index-reverify-2026-08-14.md b/docs/evidence/agent-desk-index-reverify-2026-08-14.md new file mode 100644 index 00000000..b85a1b05 --- /dev/null +++ b/docs/evidence/agent-desk-index-reverify-2026-08-14.md @@ -0,0 +1,131 @@ +# Retired "TinyStudio Agent Desk" title/snippet in Google — re-verification and residual fix + +Date: 2026-08-14 +Scope: backlog item `f41c8af0f8` — "[unreviewed-by-opus] Google still presents +the retired self-serve 'TinyStudio Agent Desk' title/snippet for tinystudio.io". + +Verdict in one line: **the finding's original target — the apex homepage — is +fixed and verified fixed on live; the retired name still reaches Google through +a duplicate host nobody had closed, and this change closes it.** + +## Environment + +- Source baseline: fresh `origin/main` at `5770bf3`. +- Live target: `https://tinystudio.io/`, `https://www.tinystudio.io/`, + `http://www.tinystudio.io/`, and the served public pages. +- SERP measurement: Google `site:tinystudio.io`, 2026-08-14, real browser. + +## Part 1 — what was already fixed (re-verified, no change needed) + +The 2026-08-09 de-indexing pass (PR #46) put `noindex, nofollow` and retired +framing on the legacy desk surface. Re-verified on this head and on live: + +- `public/agent-desk.html` head carries ``, + titles itself `TinyStudio — the retired Agent Desk`, and its description opens + "The self-serve Agent Desk is retired and is not the current offer." +- Live `https://tinystudio.io/agent-desk` → 200 serving exactly that head. +- Live `https://tinystudio.io/agent-desk.html` → 307 → `/agent-desk`, same head. +- The `check-site.mjs` dogfood guard ("Retired Agent Desk index guard") still + fails the build if the robots meta or the retired framing is removed. +- The legacy surface stays absent from `sitemap.xml`, and `llms.txt`/`offer.md` + keep the demotion statement. + +**Live SERP, 2026-08-14 — the apex result is clean.** `site:tinystudio.io` +returns `https://tinystudio.io/` titled **"TinyStudio — The Website Appraisal"** +with the current leak-audit snippet. The title the finding reported +("tinystudio.io - TinyStudio Agent Desk", q5/google 2026-08-06) is **gone from +the apex homepage result**. That part of the finding is genuinely closed. + +## Part 2 — the residual the finding is still right about + +The same SERP still shows the retired product name, on a host nobody had +addressed: + +| Indexed URL | Title Google presents | +|---|---| +| `https://tinystudio.io/` | TinyStudio — The Website Appraisal ✅ | +| `http://www.tinystudio.io/agents` | TinyStudio — The Desk **- TinyStudio Agent Desk** ❌ | +| `http://www.tinystudio.io/pricing.html` | Pricing & terms — **The Tiny Studio** ❌ | +| `http://www.tinystudio.io/specimen.html` | The Website Appraisal — specimen — **The Tiny Studio** ❌ | + +The trailing "- TinyStudio Agent Desk" is Google's **site name** for the host, +not a page title. Google derives and caches a site name per host. `www.tinystudio.io` +is a *separate host entity* in the index, and its cached site name is still the +retired product's — from when the self-serve desk owned the root. The stale +"The Tiny Studio" titles fixed by PR #98 also survive on this host. + +### Root cause + +`wrangler.jsonc` routes `www.tinystudio.io/*` at this worker, but the worker's +host dispatch only special-cased `app.` and `api.`. `www` fell through to the +normal public-site handler, so the site answered **200** on a second hostname — +and over plain `http`, un-upgraded. + +Measured on live before the fix: + +- `http://www.tinystudio.io/` → `HTTP/1.1 200 OK` (no redirect, no HTTPS upgrade) +- `https://www.tinystudio.io/` → `200`, body **byte-identical** to the apex + (`sha256 b37b0bbe…934e` on both) +- `http://tinystudio.io/` → `200` (apex over plain http; see "Not fixed here") + +Every page's `canonical`, every `og:url`, `robots.txt` and all seven +`sitemap.xml` entries already name `https://tinystudio.io`. The duplicate host +contradicted all of them. A page-level canonical is only a *hint*; Google had +plainly not taken it, since it kept indexing www URLs and kept a separate, +stale site name for them. + +## The change + +`src/worker.js` — canonical-host redirect ahead of all other dispatch: + +```js +if (host === "www.tinystudio.io") { + const canonical = new URL(url); + canonical.protocol = "https:"; + canonical.hostname = "tinystudio.io"; + canonical.port = ""; + return withSecurityHeaders(new Response(null, { status: 301, headers: { Location: canonical.toString() } })); +} +``` + +301 (not 302/307) is the point: it is the directive that collapses the +duplicate site entity — and its cached "TinyStudio Agent Desk" site name — into +the apex host. Path and query are preserved so campaign links keep working, and +plain `http` is upgraded to `https` in the same hop. + +`scripts/test-agent-worker.mjs` — four guards: + +1. www → `301` with `Location: https://tinystudio.io/`. +2. Path and query preserved; `http://www…/pricing?utm_source=x&utm_medium=y` + → `https://tinystudio.io/pricing?utm_source=x&utm_medium=y`. +3. www never serves public-site HTML for `/`, `/audit`, `/agents`, `/agent-desk` + (asserted against a stub `ASSETS` binding that would return page HTML). +4. The apex host and the retired `app.`/`api.` 410 hosts are unaffected. + +## Verification + +- `node scripts/check-site.mjs` → "TinyStudio.io checks passed." +- `node --test scripts/test-agent-worker.mjs` → 80 pass, 0 fail (76 before). +- Full suite green: headings 6, sitemap 7, UI 16, contract 8, viewport 4, + narrow-pages pass, render-blocking pass. +- **Regression proof:** disabling the host check (`if (false)`) makes exactly + the three behavioural tests fail (`# pass 77 / # fail 3`); restoring it + returns 80/0. Guard 4 passes either way by design — it asserts the hosts this + change must *not* touch. + +## Not fixed here (deliberate, separate items) + +- **`/agent-desk` canonical still names the apex root.** That is the other + consolidation path for this same finding, and it is already fixed and waiting + in **open PR #91** ("stop the retired Agent Desk from claiming the apex root + as its canonical"), currently `MERGEABLE` but `BLOCKED` on review. Not + duplicated here on purpose — this branch does not touch + `public/agent-desk.html` or the `check-site.mjs` desk guard, so it does not + conflict with #91 and the two compose. +- **`http://tinystudio.io/` (apex over plain http) answers 200.** Same class of + duplicate-URL problem, different host; it is a Cloudflare "Always Use HTTPS" / + HSTS concern rather than this finding's mechanism. Left for its own item. +- **`/agents` canonical points at `/agents.html`.** Covered by open PRs #142/#95. +- No SERP change is claimed as immediate: recrawl and site-name refresh are + Google's timetable. What is claimed and proven is that the duplicate host — + the last surface still handing Google the retired name — no longer exists.