TinyStudio — The Desk
+
diff --git a/public/audit.html b/public/audit.html
index 504a425c..b90ac77c 100644
--- a/public/audit.html
+++ b/public/audit.html
@@ -5,6 +5,7 @@
TinyStudio — The Website Appraisal
+
diff --git a/public/index.html b/public/index.html
index 25bd282a..2e62c8c9 100644
--- a/public/index.html
+++ b/public/index.html
@@ -5,6 +5,7 @@
TinyStudio — The Website Appraisal
+
diff --git a/public/pricing.html b/public/pricing.html
index 6a4c7594..76384f52 100644
--- a/public/pricing.html
+++ b/public/pricing.html
@@ -5,6 +5,7 @@
Pricing & terms — The Tiny Studio
+
diff --git a/public/specimen.html b/public/specimen.html
index d4817f27..445de018 100644
--- a/public/specimen.html
+++ b/public/specimen.html
@@ -5,6 +5,7 @@
TinyStudio — The Website Appraisal — specimen
+
diff --git a/scripts/check-site.mjs b/scripts/check-site.mjs
index 16b2ffb8..53668368 100644
--- a/scripts/check-site.mjs
+++ b/scripts/check-site.mjs
@@ -934,6 +934,47 @@ for (const [pageName, pageHtml] of metaDescriptionPages) {
}
}
+// ---- Canonical URLs (dogfood) ----------------------------------------------
+// The leak audit this site sells also flags a homepage whose served HTML
+// carries no canonical URL, leaving search engines to guess which address is
+// the page (finding 6631c0ab0454, "Missing canonical URL on home"), so the
+// site's own five public pages must not carry that fault either. Each page
+// keeps exactly one inside its head, pointing at the
+// absolute https://tinystudio.io address the page is served under (the .html
+// form; the worker also serves extensionless twins).
+const canonicalPages = [
+ ["homepage", siteHome, "https://tinystudio.io/"],
+ ["audit page", siteAudit, "https://tinystudio.io/audit.html"],
+ ["desk page", read("public/agents.html"), "https://tinystudio.io/agents.html"],
+ ["pricing page", read("public/pricing.html"), "https://tinystudio.io/pricing.html"],
+ ["specimen page", read("public/specimen.html"), "https://tinystudio.io/specimen.html"]
+];
+
+const seenCanonicals = new Map();
+for (const [pageName, pageHtml, expected] of canonicalPages) {
+ const head = pageHtml.match(/]*>([\s\S]*?)<\/head>/i)?.[1] ?? "";
+ const links = [...head.matchAll(/]*\brel="canonical"[^>]*>/gi)].map((match) => match[0]);
+ if (links.length !== 1) {
+ failures.push(`Canonical link must appear exactly once in the head of ${pageName} (found ${links.length}).`);
+ continue;
+ }
+ const href = links[0].match(/\bhref="([^"]*)"/i)?.[1] ?? "";
+ const trimmed = href.trim();
+ if (!trimmed) {
+ failures.push(`Canonical link on ${pageName} must not have an empty href.`);
+ continue;
+ }
+ if (trimmed !== expected) {
+ failures.push(`Canonical link on ${pageName} must point at ${expected} (found ${trimmed}).`);
+ }
+ const prior = seenCanonicals.get(trimmed);
+ if (prior) {
+ failures.push(`Canonical URL on ${pageName} must be unique; it duplicates ${prior}.`);
+ } else {
+ seenCanonicals.set(trimmed, pageName);
+ }
+}
+
for (const migration of ["migrations/0002_agent_runs.sql", "migrations/0003_agent_usage_limits.sql"]) { if (!existsSync(new URL(`../${migration}`, import.meta.url))) {
failures.push(`Missing migration: ${migration}`);
continue;
From 1f65f268b9fd0e6ca925891b5eb99052a5f4f831 Mon Sep 17 00:00:00 2001
From: nish3451 <257724087+nish3451@users.noreply.github.com>
Date: Sun, 9 Aug 2026 08:36:31 +0530
Subject: [PATCH 2/2] ci: harden canonical guard against body duplicates,
comments, and quote variants
---
scripts/check-site.mjs | 26 +++++++++++++++++++-------
1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/scripts/check-site.mjs b/scripts/check-site.mjs
index 53668368..7cbed638 100644
--- a/scripts/check-site.mjs
+++ b/scripts/check-site.mjs
@@ -939,9 +939,11 @@ for (const [pageName, pageHtml] of metaDescriptionPages) {
// carries no canonical URL, leaving search engines to guess which address is
// the page (finding 6631c0ab0454, "Missing canonical URL on home"), so the
// site's own five public pages must not carry that fault either. Each page
-// keeps exactly one inside its head, pointing at the
-// absolute https://tinystudio.io address the page is served under (the .html
-// form; the worker also serves extensionless twins).
+// keeps exactly one — parsed across the whole document,
+// ignoring commented-out markup and accepting single or double quotes — that
+// link sits inside the head and points at the absolute https://tinystudio.io
+// address the page is served under (the .html form; the worker also serves
+// extensionless twins).
const canonicalPages = [
["homepage", siteHome, "https://tinystudio.io/"],
["audit page", siteAudit, "https://tinystudio.io/audit.html"],
@@ -950,15 +952,25 @@ const canonicalPages = [
["specimen page", read("public/specimen.html"), "https://tinystudio.io/specimen.html"]
];
+const canonicalLinkPattern = /]*\brel\s*=\s*["']canonical["'][^>]*>/gi;
+const canonicalHrefPattern = /\bhref\s*=\s*["']([^"']*)["']/i;
+
const seenCanonicals = new Map();
for (const [pageName, pageHtml, expected] of canonicalPages) {
- const head = pageHtml.match(/]*>([\s\S]*?)<\/head>/i)?.[1] ?? "";
- const links = [...head.matchAll(/]*\brel="canonical"[^>]*>/gi)].map((match) => match[0]);
+ // A commented-out canonical is inert markup; it must neither satisfy the
+ // guarantee nor trip the duplicate check.
+ const liveHtml = pageHtml.replace(//g, "");
+ const links = [...liveHtml.matchAll(canonicalLinkPattern)].map((match) => match[0]);
if (links.length !== 1) {
- failures.push(`Canonical link must appear exactly once in the head of ${pageName} (found ${links.length}).`);
+ failures.push(`Canonical link must appear exactly once across ${pageName} (found ${links.length}).`);
continue;
}
- const href = links[0].match(/\bhref="([^"]*)"/i)?.[1] ?? "";
+ const head = liveHtml.match(/]*>([\s\S]*?)<\/head>/i)?.[1] ?? "";
+ const headLinks = [...head.matchAll(canonicalLinkPattern)].length;
+ if (headLinks !== 1) {
+ failures.push(`Canonical link on ${pageName} must sit inside the head (found ${headLinks} in head, ${links.length} total).`);
+ }
+ const href = links[0].match(canonicalHrefPattern)?.[1] ?? "";
const trimmed = href.trim();
if (!trimmed) {
failures.push(`Canonical link on ${pageName} must not have an empty href.`);