Skip to content

feat: render page content once — serve bodies from cached .Content (render-once D8)#2042

Merged
markdumay merged 5 commits into
mainfrom
feat/render-once-d8-content-flip
Jul 16, 2026
Merged

feat: render page content once — serve bodies from cached .Content (render-once D8)#2042
markdumay merged 5 commits into
mainfrom
feat/render-once-d8-content-flip

Conversation

@markdumay

Copy link
Copy Markdown
Collaborator

Render-once program — THE FLIP (HELD; stacked — see merge order below)

Part of the Hinode render-once implementation program (design:
gethinode.com docs/superpowers/specs/2026-07-15-content-render-once-design.md; full gate
evidence and diff classifications in the program register + D8 evidence file).

⚠️ STACKED PR: this branch integrates #2037 (D3), #2038 (D4), #2040 (D5), #2041 (D6) —
merge those first and this diff reduces to the D8 commits alone:

  • 2f554e0b D8a — mod-fontawesome v6.1.1 + owning page threaded through 52/53 hinode icon sites
  • 167e8daa D8c — plain-text RSS descriptions (.Description short-circuit, else plainified
    summary) — kills the RSS .Content race AND most per-output-format variant renders
  • dfcf0fa6 D8b — owning page threaded through shortcode UniqueID calls
  • 6e6b36d5 the flipbody/docs/body/minimal/body/list serve .Content
    (form/single keeps the fragment path); head dependencies read gains a .Content guard +
    Scratch∪Store union; exampleSite purgeHTMLCommentsignoreLogs; RSS allowlist deleted
    from scripts/check-build-determinism.sh
  • 0134ef59 — mod-blocks v2.2.2 + mod-mermaid v5.0.2 (page-threaded releases)

Results (exampleSite, hugo v0.164.0, M1 Pro)

  • Two consecutive builds are FULLY byte-identical — zero tolerated diffs; the determinism
    allowlist is gone.
  • CPU −33% / wall −23% vs this program's Wave-0 baseline (33.68→22.55 s user, 14.19→10.92 s
    wall, medians of 3 warm-stable runs). Cumulative vs the 2026-07-15 pre-program baseline:
    −41.8% CPU.
  • 0 ERROR, 98/26/24 pages, WARN set byte-identical; d--none count 0; no alt-text leakage.

Expected diffs vs pre-flip base (exhaustively classified, nothing unclassified)

  1. UID renumbering on 2 files (bijective, internally consistent).
  2. Three team pages gain <p> wrappers; three privacy pages whitespace residue
    (RenderString-inline vs .Content semantics — flagged and accepted 2026-07-16).
  3. RSS feeds carry plain-text descriptions (D8c, deliberate).

Known residual — DO NOT MERGE until resolved

4 dangling sprite refs on 4 docs/blocks demo pages (cta/about bi-chevron-right,
contact-form fa-paper-plane, featured bi-download): mod-blocks components call hinode
partials (button/download/links) without a page argument, and the RELEASED hinode schemas
cannot accept one yet. Completion runbook:

  1. Merge fix: read breakpoint via cached GetBreakpoint partial instead of page Scratch (render-once D3) #2037fix: thread owning page and embed flag into bookshop content blocks (render-once D4) #2038fix: thread the intrinsic page through content-reachable partials (render-once D5) #2040fix: make the footer script dependency read contract-based (render-once D6) #2041 → rebase this PR (diff shrinks to D8 commits).
  2. Release hinode (minor) so the new partial arg schemas are published.
  3. mod-blocks follow-up: thread page into the remaining indirect call sites
    (incl. magazine.html:85) + finish embed threading; release.
  4. Bump mod-blocks here; re-run scripts/check-build-determinism.sh + the sprite audit
    (expected: 0 dangling, fully byte-identical); merge this PR; re-baseline.
  5. Post-merge cleanup (design D9): drop baseof breakpoint/baseURL seeds, retire the paginator
    Store plumbing (Q1 disproven), drop Scratch dual-writes in module majors, remove
    blocks_embed fallback, remove the transitional warn-missing-page-argument suppression.

🤖 Generated with Claude Code

@netlify

netlify Bot commented Jul 16, 2026

Copy link
Copy Markdown

Deploy Preview for gethinode-demo ready!

Name Link
🔨 Latest commit f272b89
🔍 Latest deploy log https://app.netlify.com/projects/gethinode-demo/deploys/6a59043cabe1b100084150bb
😎 Deploy Preview https://deploy-preview-2042--gethinode-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@markdumay

Copy link
Copy Markdown
Collaborator Author

Program register (live, with all gate evidence pointers and the wrap-up table): docs/superpowers/specs/2026-07-16-render-once-implementation-register.md on branch program/render-once-implementation of the private gethinode.com repo. Recommended merge order for the held PRs: #2037#2038#2040#2041 → this (#2042, see runbook in the description) → rebase #2039gethinode/mod-flexsearch#308.

@markdumay

Copy link
Copy Markdown
Collaborator Author

Runbook progress: #2037/#2038/#2040 merged; #2041 and #2039 rebased onto main (D6 reduced to its single footer-consumer commit; E invariants re-verified — 1 nav hash / 120 instances / 0 server-emitted active). This PR gets its stack-reducing rebase after #2041 merges.

markdumay and others added 5 commits July 16, 2026 18:16
…icon call sites

Bump mod-fontawesome to v6.1.1, whose icon partial accepts a 'page' argument and
dual-writes symbol registrations to the page Scratch and Store (render-once D2).
The Font-Awesome upstream snapshot rides along (webfonts change bytes).

Thread the owning page through hinode's assets/icon.html call sites so sprite
symbols land on the page that emits them, whichever context triggers the render:
shortcodes pass .Page, partials their in-scope page. The button and card-icon
argument structures gain an optional 'page' argument (Q6 posture: omitted pages
fall back to the global page context) so their callers can thread it through.
utilities/GetIllustration.html has no rendering page in scope and stays on the
global fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Override Hugo's embedded RSS template (v0.164.0) with a byte-compatible copy
that changes only the item description payload: the frontmatter description
when set, else a plainified (entity-decoded, XML-escaped) summary.

Embedding rendered HTML forced a per-output-format content render of every
feed-listed page in the feed page's ambient context, making feed bytes depend
on render order: three feeds differed between otherwise identical builds. With
plain-text descriptions the feeds are deterministic (run-vs-run byte-identical,
former allowlist included) and most RSS-triggered content variant renders
disappear; only feed-listed pages without a frontmatter description still
render content for their summary ('with' short-circuits the fallback).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Threads 'page' into the 9 hinode shortcode UniqueID call sites that omitted it (table.html
already threads it). Anchors id counters to the owning page regardless of which rendering
context materializes the content, so body ids stay page-local (1..n) under render-once.
Folded into the flip slice: the one-time UID renumbering relative to the double-render
baseline is this PR's expected diff (see the D8 evidence file).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Body layouts (body, docs/body, minimal/body, list) render .Content instead of re-rendering
.RawContent through utilities/ProcessContent, removing one full shortcode/Goldmark pass per
page; form/single.html keeps ProcessContent as it renders a fragment. The head stylesheet
consumer materializes .Content explicitly and reads the module dependencies from the page
Scratch and Store, mirroring footer/scripts.html. The exampleSite drops the deprecated
purgeHTMLComments parameter in favour of Hugo's ignoreLogs, and the determinism script no
longer tolerates any diff between two builds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mod-blocks v2.2.2 and mod-mermaid v5.0.2 register icons and UIDs on the
owning page; required for sprite integrity under render-once bodies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@markdumay
markdumay force-pushed the feat/render-once-d8-content-flip branch from 0134ef5 to f272b89 Compare July 16, 2026 16:18
@markdumay

Copy link
Copy Markdown
Collaborator Author

Runbook step 1 complete: rebased onto main after the #2041 merge — the stack is gone; this PR is now exactly the 5 D8 commits (D8a icon adoption, D8c plain-text RSS, D8b UniqueID threading, the flip, module bumps). Full gate re-verified on merged main: two builds FULLY byte-identical, check-build-determinism.sh (allowlist-deleted) PASS, WARN parity, d--none 0, sprite residual = exactly the 4 documented deferred-indirect refs (runbook steps 2-4 clear them). Reminder: DO NOT merge until the runbook's mod-blocks second wave lands and the sprite audit reads 0.

@markdumay
markdumay enabled auto-merge July 16, 2026 16:20
@markdumay
markdumay merged commit 1e75b5d into main Jul 16, 2026
16 checks passed
@markdumay
markdumay deleted the feat/render-once-d8-content-flip branch July 16, 2026 16:26
@markdumay

Copy link
Copy Markdown
Collaborator Author

🎉 This PR is included in version 3.3.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant