Skip to content

Instantly share code, notes, and snippets.

@aaronschachter
Last active May 13, 2026 17:31
Show Gist options
  • Select an option

  • Save aaronschachter/ee48f771659808e2445f6dac1483e0ba to your computer and use it in GitHub Desktop.

Select an option

Save aaronschachter/ee48f771659808e2445f6dac1483e0ba to your computer and use it in GitHub Desktop.

Single Page Publishing (SPP) and CMS Publication ID Analysis

Summary

Single Page Publishing reuses existing publication records via Publication.findLatestForDatabase(), which is a simple "newest by timestamp" query with no filtering by publish type, domain, or strategy. Combined with the fact that multiple publications can be "live" simultaneously across different domains, this creates a class of bugs where SPP can cross-pollinate publication IDs between domains and cause CMS content inconsistencies.

The Core Problem

  1. Each custom domain has its own lastPublicationId field
  2. Full site publishes only update lastPublicationId on domains included in customDomainsTargets (the domains selected for that publish)
  3. SPP calls findLatestForDatabase(databaseId) which returns the most recent publication by timestamp, regardless of which domains it was originally published to
  4. SPP then stamps that publication ID onto its target domains via markPublished()
  5. CMS single-item publish (SIP) writes to "live" target directly without creating new publication records, and publishes to ALL live publications (no domain selection)

This means:

  • A staging-only publish creates Publication B while production is still on Publication A
  • SPP fires, grabs Publication B (newest), and stamps it onto whatever domains it targets
  • The production CustomDomain model's lastPublicationId now references a publication whose published database was created during a staging publish

Why This Causes 404s

When SPP overwrites a domain's lastPublicationId with the wrong publication, the domain's manifest.json (which maps page slugs to S3 asset paths) no longer matches the publication ID that the hosting layer uses to resolve CMS content. The hosting layer calls resolvePublicationIdForDomain(), gets back the wrong publication, and looks up CMS data in a database target that doesn't correspond to what's actually live on that domain.

From the customer's perspective, publishes appear to stop working. They publish a CMS item via single-item publish, see it succeed in the UI, but the live site returns 404s for those items. The domain's manifest.json publicationVersion no longer matches its customDomain.lastPublicationId, so the hosting layer resolves CMS content from a published database that doesn't correspond to what's actually live on that domain.

Steps to Reproduce

  1. Set up a site with two custom domains (e.g., production.example.com and staging.example.com)
  2. Full publish targeting only production.example.com (creates Publication A)
  3. Full publish targeting only staging.example.com (creates Publication B, newer timestamp)
  4. At this point the two domains have divergent lastPublicationId values
  5. Do a Single Page Publish targeting production.example.com
  6. SPP calls findLatestForDatabase, which returns Publication B (newest by timestamp, even though it was deployed to staging)
  7. markPublished() overwrites production.example.com's lastPublicationId to Publication B
  8. The production domain's manifest.json publicationVersion still references Publication A's published database
  9. CMS single-item publishes now write to a different published database than the one the production domain's manifest expects, resulting in 404s

SPP vs CMS SIP: Domain Targeting Differences

Behavior SPP CMS Single-Item Publish (SIP)
Domain selection User selects which domains to publish to No selection; writes to ALL live publications
Publication records Reuses latest via findLatestForDatabase Does not create or reference publication records
What changes Page static assets (HTML/CSS/JS on S3) CMS item data in the "live" target
Updates lastPublicationId? Yes (this is the bug) No

The fundamental issue: SPP lets you target specific domains but assumes a single globally-latest publication ID is correct for all of them. CMS SIP doesn't have this problem because it doesn't touch publication IDs at all; it writes directly to the live data layer.

Why Does SPP Update lastPublicationId At All?

It shouldn't need to. SPP only changes a page's static assets on S3. It doesn't touch CMS data. The domain's existing lastPublicationId is still the correct pointer to its published database.

SPP updates lastPublicationId because it reuses the same markPublished() code path as full site publish (site.ts lines 3009-3025). After compile + upload, site.publish() calls cd.markPublished(lastPublicationId, ...) on every domain in customDomainsTargets. This code was written for full publish, where a new publication IS the correct published database. SPP rides the same path without considering that reusing a publication doesn't warrant overwriting domain metadata.

lastPublicationId on a domain is used downstream by:

  1. resolvePublicationIdForDomain() at request time to determine which published database to serve
  2. The publication pruner, to know which publications are still "live" and shouldn't be deleted

Proposed fix: Skip writing lastPublicationId in markPublished() when isSinglePagePublish is true. SPP could still update lastPublished (timestamp) and lastPublishedBy (user) without touching the publication pointer.

Key Code Locations

What File
findLatestForDatabase (the problematic lookup) entrypoints/server/models/publication.ts
SPP skip-database-publishing logic entrypoints/server/models/site.ts
Per-domain markPublished entrypoints/server/models/site.ts, entrypoints/server/models/customDomain.ts
Domain publication resolution at request time entrypoints/server/lib/logic/sites/resolvePublicationIdForDomain.ts
CMS single-item publish (separate path) entrypoints/server/lib/logic/cms/items/publishCMSItems.ts

Origin of the Pattern

PR Title Merged What it did
#63121 [ENT-2086] Check all publications for branch publishes 2023-12-05 Introduced findLatestForDatabase into the site publish flow for branch publishes
#96880 Work 1501 be skip cms database publishing for spp 2026-01-15 Added isSinglePagePublish to the skipDatabasePublishing condition, piggybacking on the branch-publish reuse path
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment