A snippet sequence · after the eighteen steps

The News Voice, Deepened

A snippet sequence in four steps: making the site properly Google-newsworthy — which turned out to mean listening before growing.

The wish, as stated: a news sitemap for Google News, built from published entries marked by a checkbox on the edit page, default off — the two things Google Sites cannot do (a news XML sitemap, and per-article NewsArticle structured data).

A snippet sequence obeys the three rules of the original, plus three of its own. It must attach at points in the existing structure you can name — if it has to be smeared across the system, it belongs to a different sequence. It must leave a live datastore untouched, or touch it only by addition — existing entities never learn that anything happened. And it must be the least of all sufficient changes: where the organism already has an organ for the job, the graft uses that organ rather than growing its own.

And this snippet begins with something neither of the first two snippets needed: a discovery about the wish itself. Which makes it a third species of graft — where snippet A added an organ and snippet B added a pair of doors, snippet C deepens a voice the organism already has, until it speaks the current protocol to the letter.

Step C1

Listening

Before growing an organ, listen for it: the wish may name a voice the organism already has.

The first act writes no code; it re-reads the eighteen steps against the wish. And there it is: the checkbox asked for has existed since step eight — Article (RSS + Google News), default off — and step thirteen already speaks a news sitemap at /news-sitemap.xml with the required tags (publication name and language, publication date, title), already enforces the two-day window in code, and already embeds NewsArticle structured data in every article's head.

So the step becomes an audit against the protocol as it stands today — the Guardian's living sitemap on one side, Google's current specification on the other. Four gaps emerge. The articles show no face: no image signal anywhere, though Google chooses thumbnails from Schema.org and og:image. The structured data names no publisher. The sitemap does not honor the thousand-URL ceiling. And discovery is unaided — robots.txt names the general sitemap but not the news one. One non-gap, noted deliberately: the Guardian's news:keywords are legacy; Google removed keywords, genres and stock tickers from the specification, so the deepening will not imitate them.

$ curl -s /news-sitemap.xml     # already, since step 13:
<urlset xmlns:news="http://www.google.com/schemas/sitemap-news/0.9">
 <url><loc>https://site/news/story</loc><news:news>
  <news:publication><news:name>An Unfolding Site</news:name>
   <news:language>en</news:language></news:publication>
  <news:publication_date>2026-07-07T06:38:02Z</news:publication_date>
  <news:title>A Story</news:title>
 </news:news></url></urlset>

The voice that was already speaking. The audit found four gaps.

Step C2

The article's face

The article's face is the first picture it shows — derived, never stored.

Google chooses an article's thumbnail from the signals in its head, so an article needs a face. The obvious move is a new image field on the Page, with a picker in the editor. The snippet refuses it, on both of its own rules: it is not the least sufficient change, and it plants an opinion in the datastore that must forever be kept in sync with the body. Instead, the face is derived: the first <img> in the body, made absolute. Writers already place a lead image first because readers see pages top-down; the derivation rides a habit instead of policing a field.

The face then appears wherever a face is looked for: og:image in the head, and image in the NewsArticle record. An article with no picture simply has no face, and every consumer of the signal degrades to its own default — no placeholder, no empty tag.

_IMG = re.compile(r"<img[^>]+src=[\"']([^\"']+)[\"']", re.I)

def lead_image(page):
    m = _IMG.search(page.body or "")
    if not m:
        return ""
    src = m.group(1)
    if src.startswith(("http://", "https://")):
        return src
    return base_url() + (src if src.startswith("/") else "/" + src)

No field, no migration, no opinion to keep in sync.

Step C3

The fuller record

A record that names its publisher, its times, and its face is a record a machine can trust.

The article's head deepens in three small ways. The NewsArticle record gains a publisher — an Organization whose name comes from PUBLICATION_NAME, a new knob that defaults to the site title, because the protocol matches the publisher's name against Google's Publisher Center exactly, and a site's title and its registered publication name are allowed to differ. The head gains article:published_time and article:modified_time — restatements, in Open Graph's dialect, of the timestamps that have been facts since step eight. And it gains max-image-preview:large, the one-line permission slip for the large thumbnails the news surfaces prefer.

Notice what all three have in common: not one required new information. Step eight's metadata and step C2's derivation were already sufficient; the record only needed to be spoken more fully.

publisher = {"@type": "Organization", "name": PUBLICATION_NAME}
if SITE_LOGO:
    publisher["logo"] = {"@type": "ImageObject", "url": SITE_LOGO}
data["publisher"] = publisher

<meta name="robots" content="max-image-preview:large">
<meta property="article:published_time" content="{{ page.published }}">

Nothing new learned; everything known, spoken.

Step C4

The letter of the protocol

A protocol honored approximately is a protocol not honored; the ceiling, the face, and the announcement go in as written.

Three finishing strokes on the sitemap itself. The thousand-URL ceiling from the specification becomes a cap in the code — invisible today, and exactly the kind of limit that must be adopted while it is invisible, because the day it matters is the day the site is too busy to think about it. Each entry gains the article's face through the image extension namespace, the same derivation from C2 speaking in a third dialect. And robots.txt now announces both sitemaps, so discovery no longer depends on memory.

The verification that closes the snippet: an article with a picture carries its face in all three places — head, record, sitemap entry — with one URL, absolute in each; an article without one is simply plain in all three; and a synthetic flock of articles confirms the ceiling holds.

img = lead_image(p)
image_el = (f"<image:image><image:loc>{_e(img)}</image:loc>"
            "</image:image>" if img else "")
...
if len(urls) >= cap:          # the specification's ceiling: 1,000
    break

The same face, spoken in the sitemap's dialect; the ceiling, adopted early.

What it cost, and what it proved

What this snippet changed, in total: one derivation function, one cap, one namespace, three head tags, one publisher record, and one line of robots.txt. What it did not change: any model field, any write pathway, any template a reader sees, any byte in a live datastore. The entire snippet lives in projections — the site's outward speech — which is the mildest tissue a graft can join.

What remains is not code, and no sequence should pretend otherwise: Google News is entered through the door marked Publisher Center, and the sitemap is announced in Search Console. Set PUBLICATION_NAME in app.yaml to the name exactly as it appears there — the protocol matches names literally, not approximately. The program can make the site worthy of the news index; only the publisher can ask to be in it.

And the observation this snippet leaves behind, for the method itself: the most dangerous moment in receiving a feature request is the moment before checking whether the organism already half-satisfies it. Building the news sitemap a second time, beside the first, would have been easy, fast, demonstrable — and would have left two organs disagreeing about what an article is. Listening is a step. It deserves a drawing.