Skip to content

feat: WordPress authority bridge — the snapshot is the contract#10

Open
Eth-Interchained wants to merge 2 commits into
mainfrom
hyperagent/wordpress-blog-source
Open

feat: WordPress authority bridge — the snapshot is the contract#10
Eth-Interchained wants to merge 2 commits into
mainfrom
hyperagent/wordpress-blog-source

Conversation

@Eth-Interchained

@Eth-Interchained Eth-Interchained commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Summary

(Reworked mid-review after a course correction — the first commit on this branch wedged WordPress in as a slug-cherry-picking sync inventing /blog/:slug URLs. That was a content importer, not the authority bridge the spec defines. The second commit makes the snapshot the contract.)

WordPress (mintontheavenue.com, plugin installed) becomes a full content source for this storefront: the bridge snapshot — every published route at its EARNED path, the internal link graph, menus, one contentHash — is consumed via WordPressBridgeSource and persisted in NEDB through the bridge's own NedbSnapshotStore (hash-chained, caused_by provenance). A WordPress post at /botanical-repair-guide/ renders at /botanical-repair-guide/ on this domain, in Park Avenue Warm, and every internal link in the content resolves because every published route resolves.

Decisions encoded (Mark, 2026-07-08)

  • Entity surfaces win collisions: pages.ts mounts first; /services, /stylists, /book, cities, home always belong to the platform. WordPress fills everything else. Verified with a deliberate trap: a 'services' page created IN WordPress never renders.
  • Canonical posture, built for migration scenarios: PORTAL_BRIDGE_CANONICAL=source (default) — canonicals point at the origin WordPress site, which keeps the authority while this domain serves the content; WordPress routes stay OUT of this domain's sitemap (sitemaps list canonical URLs). Flip to =self when a migration lands: canonicals become origin+path and WP routes join the sitemap. One env var.
  • canonicalUrl on PageMeta is the ONE documented exception to the origin+path canonical discipline (head.ts doctrine comment updated accordingly).

Resilience

Stored snapshot in NEDB boots the bridge with WordPress down (that's snapshot-first). Nothing stored + WordPress down = loud warning + lazy retry with cooldown — the storefront never dies over the bridge.

Tests run

  • 36/36 unit tests (canonical override, posture config validation, journal ordering + earned paths, sitemap honesty per posture)
  • TypeScript clean
  • Live, real WordPress + real nedbd: collision trap passed, earned-path rendering with source canonical, internal link graph resolving on-storefront, sitemap honesty in both postures, self-posture flip verified, boot log showing snapshot adopted from the NEDB store then refreshed from the tunnel

Verification scope (honest)

Verified against local sandboxes (WP on SQLite + nedbd 2.6.1), not yet against the production Mint WordPress or live salon nedbd. First production connection should re-run this battery — especially the collision list (Mint's real WP has more pages than the sandbox).

Follow-up work

Webhook-triggered snapshot refresh · redirect map for migration completion (source paths → new domain) · Mint production connection + battery re-run

🤖 Generated with Claude Code

Vex (Hyperagent) and others added 2 commits July 8, 2026 16:56
campaign.ts already named 'blog' as a valid surface and orlando's
contentPlan already listed 'articles' as a collection — neither was ever
built. This ships the first writer for it: WordPress posts, pulled through
the wp-portal-bridge signed tunnel, become salon-platform articles.

No new render pipeline, no new styling. WordPress content lands in the
SAME articles collection pages read from, rendered through the SAME
pageShell()/Park Avenue Warm CSS every other page uses. By the time it hits
the renderer it's indistinguishable from hand-seeded content. The tightly
zod-modeled salon/stylist/service data (working well) is untouched.

- src/server/wordpress-sync.ts: syncWordPressArticles() — same provenance
  discipline as seed.ts (content-addressed idempotency keyed on the
  bridge's own contentHash, a seed_runs provenance root every article's
  caused_by chains to, real writes measured via engine seq delta so
  idempotency is reported honestly, not just claimed)
- scripts/sync-wordpress.ts: thin CLI wrapper matching seed.ts's exact
  shape (npm run sync:wordpress), verify()-gated
- src/server/entities.ts: articles collection + typed reads (listArticles,
  getArticleBySlug, articleId — same deterministic-id pattern as
  identityId)
- src/server/articles.ts: GET /blog + /blog/:slug, same esc()/pageShell()/
  CSS-class vocabulary as pages.ts
- src/server/render.ts: sitemap coverage for articles, computed independent
  of anchorSalonHandle (a directory/national campaign with no anchor salon
  can still have a journal)
- src/server/config.ts: optional PORTAL_BRIDGE_BASE_URL/PORTAL_TMK, must be
  set together or not at all (validateConfig rejects half-configured)
- @interchained/portal-source-wordpress added for BridgeClient + the
  sanitizer (WordPress HTML is sanitized before it ever reaches the
  articles collection, verified in the unit suite with a live <script>
  payload)

Verified end to end against a real WordPress (wp-portal-bridge sandbox) and
a real nedbd: 2 published posts synced, second run correctly reports 0
writes (hash-chain head unchanged, confirmed independent of the counter),
engine verify:true both times. Booted the real Express app against the
synced db: /blog and /blog/:slug render with correct SEO title (slotted
into orlando's titleTemplate), Park Avenue Warm CSS applied, zero
unsanitized script survives from a deliberately hostile test payload.

35/35 unit tests green (5 new). TypeScript clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t importer

Course correction (Mark, 2026-07-08): the previous commit on this branch
wedged WordPress in as a slug-cherry-picking sync that invented /blog/:slug
URLs and dropped the link graph — a content importer. The spec's deal was
an AUTHORITY bridge: all links in the snapshot, every route at its earned
path. This makes it so.

- WordPressBridgeSource consumes the FULL snapshot (all routes, exact
  paths, link graph, menus, one contentHash), persisted in NEDB via the
  bridge's own NedbSnapshotStore — hash-chained, caused_by provenance,
  verify-able. No per-post copying; the articles collection is gone.
- WordPress routes render at their EARNED paths in the campaign theme:
  /botanical-repair-guide/ stays /botanical-repair-guide/. Internal links
  in content resolve because every published route resolves.
- Precedence (decided): entity surfaces win — pages.ts mounts first,
  WordPress fills everything the platform didn't claim, SPA shell last.
- Canonical posture (decided, built for migration scenarios):
  PORTAL_BRIDGE_CANONICAL=source (default) keeps authority at the origin
  WordPress site (canonicalUrl override in buildHead — the ONE deliberate
  exception to origin+path discipline, documented in head.ts); =self flips
  when a migration lands. Sitemap honesty follows the posture: WP routes
  join the sitemap only in self posture (sitemaps list canonical URLs).
- Boot resilience: stored snapshot boots with WordPress down; nothing
  stored + WordPress down = loud warning + lazy retry with cooldown; the
  storefront never dies over the bridge.
- /blog journal index remains, linking to REAL paths only.

Verified live (real WordPress + real nedbd + this server):
- COLLISION: a 'services' page created in WordPress; storefront /services
  still serves the entity price tables — WP trap page never rendered
- /botanical-repair-guide/ at its earned path, Park Avenue theme,
  canonical -> the SOURCE WordPress origin (source posture)
- internal link inside the post content resolves on the storefront (200)
- sitemap in source posture: entity surfaces only, zero WP routes
- PORTAL_BRIDGE_CANONICAL=self flip: canonical becomes self-origin, WP
  routes join the sitemap at earned paths
- boot log: snapshot adopted from NEDB store, then refreshed from tunnel

36/36 unit tests. TypeScript clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Eth-Interchained Eth-Interchained changed the title feat: WordPress as the editorial engine for the blog surface feat: WordPress authority bridge — the snapshot is the contract Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant