[object Object]

← back to Thesetdecorator

TSD SEO: add self-referencing canonical to 9 evergreen pages (DTD verdict A; skip param-driven/redirect/admin)

91c0e4ad7b8ab2acad0c50be0c261a0534873f2c · 2026-06-03 08:56:24 -0700 · Steve

Files touched

Diff

commit 91c0e4ad7b8ab2acad0c50be0c261a0534873f2c
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Jun 3 08:56:24 2026 -0700

    TSD SEO: add self-referencing canonical to 9 evergreen pages (DTD verdict A; skip param-driven/redirect/admin)
---
 YOLO_BACKLOG.md                  | 11 ++++++++
 public/cleared-artwork-sale.html |  1 +
 public/collections-by-steve.html |  1 +
 public/job-board.html            |  1 +
 public/license-terms.html        |  1 +
 public/stories.html              |  1 +
 public/submit.html               |  1 +
 public/trades.html               |  1 +
 public/unions.html               |  1 +
 public/wall-treatments.html      |  1 +
 scripts/canonical-backfill.py    | 59 ++++++++++++++++++++++++++++++++++++++++
 11 files changed, 79 insertions(+)

diff --git a/YOLO_BACKLOG.md b/YOLO_BACKLOG.md
index 1ffbc4b..7125902 100644
--- a/YOLO_BACKLOG.md
+++ b/YOLO_BACKLOG.md
@@ -38,9 +38,20 @@ reversible local commit** marked `[x]` w/ hash, then ScheduleWakeup ~1800s.
   cleared-artwork-sale card imgs already `loading="lazy"`; catalog uses CSS bg-image by
   design; films/roster have no image cards. Nothing to change.
 
+## Done (cont.) — 2026-06-03 loop #2 (fresh-dimension audit: head/meta tags)
+- [x] **favicon backfill** — `b7ea9fa` · only index.html linked a favicon; added a
+  consistent svg+png+apple-touch icon block to the other 43 public pages via idempotent
+  `scripts/favicon-backfill.py` (inserts after viewport meta, skips pages already
+  linking). 44/44 pages now link favicon; all 3 assets verified on disk. CNCP win logged.
+- [ ] **canonical backfill** — 16 pages lack `<link rel=canonical>`. DTD in flight on
+  scope (evergreen-only vs all-16 vs skip). Param-driven (union-talk, popout) + prod-
+  redirecting (decorator.html→roster) + admin/utility pages are the contested set.
+
 ### Audited clean (no action) — 2026-06-03
 - Broken internal links: **0**. · Grid empty-states: all present. · Focus styles:
   main.css covered. · OG/social, alt-text, decorator meta: done (above).
+- Per-page head audit: every page has lang, viewport, title. theme-color present on a
+  subset (cosmetic, not pursued). Favicon → fixed above. Canonical → in flight.
 
 ## Notes / queued-for-Steve (gated — do NOT auto-do)
 - (none yet)
diff --git a/public/cleared-artwork-sale.html b/public/cleared-artwork-sale.html
index 888afa1..e731c6c 100644
--- a/public/cleared-artwork-sale.html
+++ b/public/cleared-artwork-sale.html
@@ -8,6 +8,7 @@
     <link rel="apple-touch-icon" href="/apple-touch-icon.png">
 <meta name="theme-color" content="#0e0e0e">
 <title>Cleared Artwork · For License — The Set Decorator</title>
+    <link rel="canonical" href="https://thesetdecorator.com/cleared-artwork-sale.html">
 <meta name="description" content="Original cleared artwork by Steve Abrams — film/TV use rights, royalty-free per show. Photography, painted scenes, period composites.">
 <link rel="preconnect" href="https://fonts.googleapis.com">
 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
diff --git a/public/collections-by-steve.html b/public/collections-by-steve.html
index de4664c..6760d70 100644
--- a/public/collections-by-steve.html
+++ b/public/collections-by-steve.html
@@ -8,6 +8,7 @@
     <link rel="apple-touch-icon" href="/apple-touch-icon.png">
 <meta name="theme-color" content="#0e0e0e">
 <title>Collections by Steve · Cleared Artwork — The Set Decorator</title>
+    <link rel="canonical" href="https://thesetdecorator.com/collections-by-steve.html">
 <meta name="description" content="Original photography by Steve Abrams, grouped by city and month. Royalty-free film/TV use rights — license direct, instant download, no model releases needed.">
 <link rel="preconnect" href="https://fonts.googleapis.com">
 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
diff --git a/public/job-board.html b/public/job-board.html
index 62b0f37..78129f7 100644
--- a/public/job-board.html
+++ b/public/job-board.html
@@ -7,6 +7,7 @@
     <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png">
     <link rel="apple-touch-icon" href="/apple-touch-icon.png">
   <title>Job Board — The Set Decorator</title>
+    <link rel="canonical" href="https://thesetdecorator.com/job-board.html">
   <meta name="description" content="Open set-decoration positions submitted by working production designers and decorators, plus auto-extracted production signals from the trade-news corpus.">
   <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;1,400&family=Inter:wght@400;500;600&display=swap">
   <link rel="stylesheet" href="/css/main.css">
diff --git a/public/license-terms.html b/public/license-terms.html
index 565511b..79a7f48 100644
--- a/public/license-terms.html
+++ b/public/license-terms.html
@@ -8,6 +8,7 @@
     <link rel="apple-touch-icon" href="/apple-touch-icon.png">
 <meta name="theme-color" content="#0e0e0e">
 <title>License Terms — The Set Decorator</title>
+    <link rel="canonical" href="https://thesetdecorator.com/license-terms.html">
 <meta name="description" content="Plain-English license terms for cleared artwork licensed through thesetdecorator.com. Royalty-Free · Film/TV Use Rights · per-production.">
 <link rel="preconnect" href="https://fonts.googleapis.com">
 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
diff --git a/public/stories.html b/public/stories.html
index ecca610..7460d56 100644
--- a/public/stories.html
+++ b/public/stories.html
@@ -7,6 +7,7 @@
     <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png">
     <link rel="apple-touch-icon" href="/apple-touch-icon.png">
   <title>Stories — The Set Decorator</title>
+    <link rel="canonical" href="https://thesetdecorator.com/stories.html">
   <meta name="description" content="Written stories about set decoration. Continuity craft, period research, sourcing, and the working life of the department.">
   <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;1,400&family=Inter:wght@400;500;600&display=swap">
   <link rel="stylesheet" href="/css/main.css">
diff --git a/public/submit.html b/public/submit.html
index 89b95b6..f597a76 100644
--- a/public/submit.html
+++ b/public/submit.html
@@ -8,6 +8,7 @@
     <link rel="apple-touch-icon" href="/apple-touch-icon.png">
 <meta name="theme-color" content="#0e0e0e">
 <title>Submit your photos — The Set Decorator</title>
+    <link rel="canonical" href="https://thesetdecorator.com/submit.html">
 <meta name="description" content="Share a Google Drive folder with TSD. We license your face-free photography to film/TV productions. You keep 70%. No upload limits, no exclusivity.">
 <link rel="preconnect" href="https://fonts.googleapis.com">
 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
diff --git a/public/trades.html b/public/trades.html
index 5017943..4a8fd9e 100644
--- a/public/trades.html
+++ b/public/trades.html
@@ -7,6 +7,7 @@
     <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png">
     <link rel="apple-touch-icon" href="/apple-touch-icon.png">
   <title>Industry News — The Set Decorator</title>
+    <link rel="canonical" href="https://thesetdecorator.com/trades.html">
   <meta name="description" content="32 trade, celebrity, craft, design, and prestige outlets in one feed — Variety, THR, California Post, Perez Hilton, AD, Vanity Fair, the lot. Filterable, searchable, LLM-ready.">
   <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;1,400&family=Inter:wght@400;500;600&display=swap">
   <link rel="stylesheet" href="/css/main.css">
diff --git a/public/unions.html b/public/unions.html
index ff87b72..286a27b 100644
--- a/public/unions.html
+++ b/public/unions.html
@@ -7,6 +7,7 @@
     <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png">
     <link rel="apple-touch-icon" href="/apple-touch-icon.png">
   <title>Unions & Guilds — The Set Decorator</title>
+    <link rel="canonical" href="https://thesetdecorator.com/unions.html">
   <meta name="description" content="Every IATSE local and adjacent guild representing set decorators across the US and Canada. LA Local 44, NY Local 52, Atlanta Local 479, plus every regional studio-mechanics local.">
   <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;1,400&family=Inter:wght@400;500;600&display=swap">
   <link rel="stylesheet" href="/css/main.css">
diff --git a/public/wall-treatments.html b/public/wall-treatments.html
index 421014f..7979470 100644
--- a/public/wall-treatments.html
+++ b/public/wall-treatments.html
@@ -7,6 +7,7 @@
     <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png">
     <link rel="apple-touch-icon" href="/apple-touch-icon.png">
   <title>Wall Treatments — Ombres — The Set Decorator</title>
+    <link rel="canonical" href="https://thesetdecorator.com/wall-treatments.html">
   <meta name="description" content="Hand-pulled gradient ombres in nine traditional palettes — left to right and right to left. Print at any size for set-wall washes and backdrops.">
   <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;1,400&family=Inter:wght@400;500;600&display=swap">
   <link rel="stylesheet" href="/css/main.css">
diff --git a/scripts/canonical-backfill.py b/scripts/canonical-backfill.py
new file mode 100644
index 0000000..a42010e
--- /dev/null
+++ b/scripts/canonical-backfill.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+"""Idempotent canonical-tag backfill for TSD public/*.html.
+
+16 pages lacked <link rel="canonical">. Default scope (DTD verdict A) = the
+evergreen, indexable content pages only. --all applies to every page lacking
+one (verdict B). Canonical form: https://thesetdecorator.com/<file>.html
+(index.html uses the bare domain, but index already has one so it's untouched).
+
+SKIPPED by default (and why):
+  param-driven  : union-talk.html, popout.html  (self-canonical collapses ?query variants)
+  prod-redirect : decorator.html                (302s to roster.html server-side)
+  admin/utility : art-review.html, logo-agent.html, logo-variants.html, sent-to-exa.html
+
+Idempotent (skips pages already carrying rel="canonical"); reversible (single
+<link> inserted after </title>). Usage:
+    python3 scripts/canonical-backfill.py          # evergreen only (verdict A)
+    python3 scripts/canonical-backfill.py --all     # every page lacking one (verdict B)
+"""
+import os, re, glob, sys
+
+PUB = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "public")
+BASE = "https://thesetdecorator.com/"
+
+EVERGREEN = {
+    "cleared-artwork-sale.html", "collections-by-steve.html", "license-terms.html",
+    "stories.html", "submit.html", "trades.html", "unions.html", "wall-treatments.html",
+    "job-board.html",
+}
+
+ALL_MODE = "--all" in sys.argv
+HASCANON_RE = re.compile(r'rel=["\']?canonical', re.IGNORECASE)
+TITLE_RE = re.compile(r'</title>[ \t]*\n', re.IGNORECASE)
+
+changed, skipped_have, skipped_scope, noanchor = [], [], [], []
+for path in sorted(glob.glob(os.path.join(PUB, "*.html"))):
+    name = os.path.basename(path)
+    html = open(path, encoding="utf-8").read()
+    if HASCANON_RE.search(html):
+        skipped_have.append(name)
+        continue
+    if not ALL_MODE and name not in EVERGREEN:
+        skipped_scope.append(name)
+        continue
+    m = TITLE_RE.search(html)
+    if not m:
+        noanchor.append(name)
+        continue
+    link = f'    <link rel="canonical" href="{BASE}{name}">\n'
+    new = html[:m.end()] + link + html[m.end():]
+    open(path, "w", encoding="utf-8").write(new)
+    changed.append(name)
+
+print(f"mode: {'ALL' if ALL_MODE else 'evergreen-only'}")
+print(f"changed ({len(changed)}): {', '.join(changed) or '-'}")
+print(f"already had canonical: {len(skipped_have)}")
+if not ALL_MODE:
+    print(f"skipped (out of scope): {', '.join(skipped_scope) or '-'}")
+if noanchor:
+    print(f"NO </title> anchor ({len(noanchor)}): {', '.join(noanchor)}")

← b7ea9fa TSD: favicon backfill — add svg+png+apple-touch icon links t  ·  back to Thesetdecorator  ·  backlog: mark loop #2 items done (favicon, canonical) 1b62c7a →