From 3b557d3e51870bfc92007d765700c7a9b1ff24fd Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Apr 2026 22:23:40 +0000 Subject: [PATCH] test: add render-regression checks to prevent #7079-class bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds four layers of coverage against the class of rendering bug reported in #7079: whitespace leaks in Hugo render hooks or wrapper shortcode templates that cause Goldmark to HTML-escape highlighted code blocks. Each layer catches the bug at a different point in the lifecycle — together they mean this specific regression can't land again without somebody actively defeating every layer. Layer 1 — Site-wide HTML grep after Hugo build .ci/scripts/check-render-artifacts.sh Scans every .html file under `public/` for three literal patterns that can only appear when a render hook leaked whitespace into its output: `<div class="highlight"`, `<pre tabindex="0"`, and `<span class="line"><span class="cl"`. Runs in ~1 second on the full built site. Catches regressions on every page automatically, including pages no test lists explicitly. Verified against a rebuild with the pre-fix render-codeblock.html from commit d80eb2f — correctly reports 54 affected files — and against the current clean build — reports 0. Layer 2 — Pre-commit lint for render hooks .ci/scripts/check-render-hook-whitespace.sh lefthook.yml: new pre-commit command `check-render-hook-whitespace` Enforces the invariant that every action tag inside `layouts/_default/_markup/render-*.html` uses `{{- ... -}}` whitespace trimming. Bare `{{ ... }}` actions leak their surrounding indent and newline into the render-hook output; the lint catches this at commit time, before a PR is even opened. Scoped narrowly to render hooks so it doesn't nag on normal Hugo templates where bare actions are fine. Also hardens render-codeblock.html lines 1 and 19, which had bare `{{ ... }}` actions that happened to work correctly because surrounding `{{- ... -}}` operators trimmed their whitespace by accident. The lint flagged them as fragile; rewriting them as `{{- ... -}}` makes the file unconditionally correct and lets the lint ship with no exceptions. Layer 3 — Cypress render-regression spec cypress/e2e/content/render-regression.cy.js package.json: `test:render-regression`, `test:render-artifacts` DOM-level assertion that no `pre > code` element on a curated page list contains the escaped chroma fragments. Two sections: * Shortcode examples page (/example/) — a single exhaustive shortcode fixture that covers every render-hook combination documented in the site. * Representative product pages — one page per InfluxDB 3 edition, hand-picked for the wrapper/attribute combination it exercises: - core/reference/sample-data (placeholders + custom-timestamps + code-tab-content) - enterprise/admin/backup-restore (placeholders inside tab-content — the page that regressed worst in #7079) - cloud-dedicated/reference/sample-data (placeholders with regex grouping inside custom-timestamps) - clustered/admin/users/add (placeholders + callouts in nested expand wrappers) - cloud-serverless/reference/sample-data (post-migration fence-attribute syntax) Layer 4 — Example page coverage for the render-hook attributes content/example.md: new "Render-regression fixtures" section Adds fence-attribute and wrapper fixtures that exercise every code path implicated in #7079: `placeholders=` with and without regex grouping, `callout=` with default and explicit color, combined `placeholders=` + `callout=`, placeholder fences inside `influxdb/custom-timestamps`, and the worst-case nested shape (expand > code-tabs > code-tab-content > custom-timestamps > placeholder fence) that every sample-data page uses. Before this commit `content/example.md` had zero uses of either attribute, which meant the shortcode-examples smoke test was blind to #7079-class bugs. Verified after rebuild: 11 placeholder wrappers, 3 callouts, and 3 custom-timestamps wrappers render on /example/. CI workflow .github/workflows/pr-render-check.yml Runs on every pull_request. Two jobs: * check-artifacts: Unconditional site-wide grep on every PR. * cypress-render: Cypress spec, gated on a path filter that only triggers when the PR touches layouts/, assets/, the render-regression spec itself, the check-artifacts script, or this workflow file. The unconditional grep is the backstop — it runs even on content-only PRs and catches any regression anywhere in the site. Cypress runs on the subset of PRs that can actually cause this class of bug. Refs influxdata/docs-v2#7079 --- .ci/scripts/check-render-artifacts.sh | 108 +++++++++++++++ .ci/scripts/check-render-hook-whitespace.sh | 77 +++++++++++ .github/workflows/pr-render-check.yml | 101 ++++++++++++++ content/example.md | 105 +++++++++++++++ cypress/e2e/content/render-regression.cy.js | 127 ++++++++++++++++++ .../_default/_markup/render-codeblock.html | 4 +- lefthook.yml | 5 + package.json | 2 + 8 files changed, 527 insertions(+), 2 deletions(-) create mode 100755 .ci/scripts/check-render-artifacts.sh create mode 100755 .ci/scripts/check-render-hook-whitespace.sh create mode 100644 .github/workflows/pr-render-check.yml create mode 100644 cypress/e2e/content/render-regression.cy.js diff --git a/.ci/scripts/check-render-artifacts.sh b/.ci/scripts/check-render-artifacts.sh new file mode 100755 index 0000000000..79ab798a0b --- /dev/null +++ b/.ci/scripts/check-render-artifacts.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env bash +# +# check-render-artifacts.sh +# +# Scan built Hugo HTML for forbidden render artifacts — patterns that +# should be impossible on a correctly built page. Their presence signals +# a rendering bug (whitespace leak in a render hook, broken shortcode +# template, etc.) that would otherwise ship to production as visibly +# broken content. +# +# The canonical example is influxdata/docs-v2#7079, where a whitespace +# leak in the `placeholders`/`callout` branch of render-codeblock.html +# caused Goldmark to HTML-escape highlighted code blocks into literal +# `
<div class="highlight"…` fragments on every
+# page that used either attribute. Every pattern in this script is a
+# fingerprint of that class of bug.
+#
+# Usage:
+#   .ci/scripts/check-render-artifacts.sh [target]
+#
+# Arguments:
+#   target   Directory or file to scan. Defaults to `public`.
+#
+# Exit codes:
+#   0  No forbidden patterns found.
+#   1  At least one forbidden pattern found. The script prints every
+#      offending file and the pattern that matched.
+#   2  Target directory/file does not exist.
+#
+# Run this immediately after `npx hugo --quiet` in CI so that any
+# regression fails the build before downstream jobs (Cypress, Vale,
+# link-check) waste time.
+#
+# Known gaps / possible future additions:
+#
+#   - `{{<` / `{{%`   : Indicates an unrendered Hugo shortcode. Currently
+#                      excluded because legitimate docs contain Helm and
+#                      Go template examples that use `{{` syntax inside
+#                      code fences, producing unavoidable false positives.
+#                      Could be re-added as a scoped regex that requires
+#                      the delimiters to appear outside `` / `
`.
+#
+#   - `ZgotmplZ`      : Hugo's context-escape sentinel. Currently excluded
+#                      because a pre-existing bug emits it into
+#                      `data-influxdb-urls="#ZgotmplZ"` on ~4600 pages.
+#                      Re-add after that bug is fixed.
+
+set -euo pipefail
+
+TARGET="${1:-public}"
+
+if [[ ! -e "$TARGET" ]]; then
+  echo "::error::check-render-artifacts: target '$TARGET' does not exist. Did Hugo build succeed?"
+  exit 2
+fi
+
+# Forbidden patterns, format: "PATTERN|DESCRIPTION"
+#
+# Every pattern below is HTML-escaped chroma output (`
`, +# `
`, ``). Goldmark only produces those
+# escaped forms when it has interpreted legitimate chroma HTML as plain-text
+# content — i.e. when a render hook or wrapper shortcode leaked whitespace and
+# Goldmark re-wrapped the highlighted output as an indented code block.
+PATTERNS=(
+  "<div class="highlight"|Goldmark re-wrapped highlighted code as an indented code block (see #7079). Likely cause: whitespace leak in a render hook (layouts/_default/_markup/render-*.html) or a wrapper shortcode template."
+  "<pre tabindex="0"|Escaped chroma 
 wrapper — same class of bug as #7079."
+  "<span class="line"><span class="cl"|Escaped chroma line span — same class of bug as #7079."
+)
+
+failed=0
+total_hits=0
+
+for entry in "${PATTERNS[@]}"; do
+  pattern="${entry%%|*}"
+  description="${entry#*|}"
+
+  if [[ -d "$TARGET" ]]; then
+    hits=$(grep -rlF --include='*.html' "$pattern" "$TARGET" 2>/dev/null || true)
+  else
+    hits=$(grep -lF "$pattern" "$TARGET" 2>/dev/null || true)
+  fi
+
+  if [[ -n "$hits" ]]; then
+    count=$(printf '%s\n' "$hits" | wc -l | tr -d ' ')
+    total_hits=$((total_hits + count))
+    failed=1
+
+    echo "::error::Found forbidden render artifact in $count file(s): '$pattern'"
+    echo "   Cause: $description"
+    echo "   First 10 affected files:"
+    printf '%s\n' "$hits" | head -10 | sed 's/^/     /'
+    echo ""
+  fi
+done
+
+if [[ $failed -eq 0 ]]; then
+  echo "✅ check-render-artifacts: no forbidden patterns found in '$TARGET'."
+  exit 0
+fi
+
+echo "❌ check-render-artifacts: found $total_hits rendering artifact(s) across the built site."
+echo ""
+echo "These signatures appear only when a render hook or wrapper shortcode leaks"
+echo "whitespace into its output and Goldmark re-wraps highlighted code as an"
+echo "indented code block. The fix is almost always to add {{- ... -}} whitespace"
+echo "trimming to every action tag in the offending template. See"
+echo "influxdata/docs-v2#7079 for the canonical case and fix pattern."
+exit 1
diff --git a/.ci/scripts/check-render-hook-whitespace.sh b/.ci/scripts/check-render-hook-whitespace.sh
new file mode 100755
index 0000000000..8e85fee3de
--- /dev/null
+++ b/.ci/scripts/check-render-hook-whitespace.sh
@@ -0,0 +1,77 @@
+#!/usr/bin/env bash
+#
+# check-render-hook-whitespace.sh
+#
+# Pre-commit lint for Hugo render hooks (layouts/_default/_markup/render-*.html).
+#
+# Enforces the invariant that every action tag inside a render hook must
+# use `{{- ... -}}` whitespace trimming. Bare `{{ ... }}` actions leak
+# their surrounding indent and trailing newline into the rendered output,
+# which causes Goldmark to interpret the result as an indented code block
+# and HTML-escape any leading HTML (see influxdata/docs-v2#7079 for the
+# canonical failure mode).
+#
+# The only exception is template comments (`{{/* ... */}}`), which
+# produce no output regardless of trimming.
+#
+# Usage:
+#   .ci/scripts/check-render-hook-whitespace.sh [file...]
+#
+# Typical invocation from lefthook pre-commit:
+#   glob: "layouts/_default/_markup/render-*.html"
+#   run: .ci/scripts/check-render-hook-whitespace.sh {staged_files}
+#
+# Exit codes:
+#   0  All action tags in the provided files are whitespace-trimmed.
+#   1  At least one bare `{{ ... }}` action was found.
+
+set -euo pipefail
+
+if [[ $# -eq 0 ]]; then
+  exit 0
+fi
+
+failed=0
+
+for file in "$@"; do
+  [[ -f "$file" ]] || continue
+
+  # Match any line whose first non-whitespace `{{` is not followed by `-`
+  # (i.e. a bare opening action), AND whose matching `}}` is not preceded
+  # by `-` (bare closing action).
+  #
+  # Exclude template comments `{{/* ... */}}` since they produce no
+  # output and are unaffected by whitespace.
+  #
+  # Exclude lines where `{{` appears only inside a string literal (e.g.
+  # `print "...{{..."`), detected by requiring the action to start the
+  # line (after optional whitespace).
+  offenders=$(grep -nE '^\s*\{\{[^-/]' "$file" | grep -vE '\{\{/\*' || true)
+
+  # Also catch bare closing actions: `... }}` without the leading `-}}`.
+  # Only warn on lines that also start with an action (avoid false
+  # positives from string literals spanning multiple lines).
+  closing_offenders=$(grep -nE '[^-]\}\}\s*$' "$file" | grep -E '^\s*\{\{' | grep -vE '\{\{-' || true)
+
+  all_offenders=$(printf '%s\n%s\n' "$offenders" "$closing_offenders" | grep -v '^$' | sort -u || true)
+
+  if [[ -n "$all_offenders" ]]; then
+    failed=1
+    echo "❌ $file: found bare {{ ... }} action tag(s) in a render hook."
+    echo "   Every action inside layouts/_default/_markup/render-*.html must"
+    echo "   use whitespace-trimming delimiters ({{- ... -}}) to prevent"
+    echo "   leaked whitespace from breaking Goldmark's HTML-block detection."
+    echo "   See influxdata/docs-v2#7079 for the canonical failure."
+    echo ""
+    printf '%s\n' "$all_offenders" | sed 's/^/     /'
+    echo ""
+  fi
+done
+
+if [[ $failed -eq 1 ]]; then
+  echo "Fix: rewrite each offending action with {{- ... -}} trimming."
+  echo "Example: '{{ \$x := foo }}' → '{{- \$x := foo -}}'"
+  exit 1
+fi
+
+exit 0
diff --git a/.github/workflows/pr-render-check.yml b/.github/workflows/pr-render-check.yml
new file mode 100644
index 0000000000..40dbd91736
--- /dev/null
+++ b/.github/workflows/pr-render-check.yml
@@ -0,0 +1,101 @@
+name: Render Regression Check
+
+# Guards against the class of rendering bug reported in
+# influxdata/docs-v2#7079: whitespace leaks in Hugo render hooks or
+# wrapper shortcode templates that cause Goldmark to HTML-escape
+# highlighted code blocks on every affected page.
+#
+# Two layers:
+#
+#   1. check-artifacts: Site-wide grep of built HTML. Cheap, runs on
+#      every PR, catches regressions anywhere in the site even on pages
+#      nobody is thinking about. This is the backstop.
+#
+#   2. cypress-render: Cypress spec against content/example.md and a
+#      curated set of representative product pages. Runs only when a
+#      PR touches `layouts/` or `assets/` (i.e. the code paths that can
+#      cause this class of bug). Provides DOM-level verification that
+#      the pages actually look right, not just that they lack a string.
+
+on:
+  pull_request:
+    types: [opened, synchronize, reopened]
+
+jobs:
+  check-artifacts:
+    name: Scan built HTML for forbidden render artifacts
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v6
+
+      - name: Setup Node.js
+        uses: actions/setup-node@v4
+        with:
+          node-version: '20'
+          cache: 'yarn'
+
+      - name: Install dependencies
+        env:
+          CYPRESS_INSTALL_BINARY: 0
+        run: yarn install --frozen-lockfile
+
+      - name: Build Hugo site
+        run: npx hugo --quiet
+
+      - name: Scan built HTML for render artifacts
+        run: .ci/scripts/check-render-artifacts.sh public
+
+  cypress-render:
+    name: Cypress render-regression spec
+    runs-on: ubuntu-latest
+    # Only run when a PR touches code that could regress rendering.
+    # Content-only PRs don't need this layer — the site-wide grep above
+    # already catches any page-level impact they could have.
+    if: |
+      contains(github.event.pull_request.labels.*.name, 'render-regression') ||
+      github.event.pull_request.draft == false
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v6
+        with:
+          fetch-depth: 0
+
+      - name: Detect layout/asset changes
+        id: detect
+        run: |
+          # Only run Cypress when files under layouts/, assets/, or
+          # the render-regression spec itself have changed. Content-only
+          # PRs rely on the site-wide grep job above for coverage.
+          CHANGED=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
+            "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.number }}/files" \
+            | jq -r '.[].filename')
+
+          echo "Changed files:"
+          echo "$CHANGED"
+
+          if echo "$CHANGED" | grep -qE '^(layouts/|assets/|cypress/e2e/content/render-regression\.cy\.js$|cypress/support/|\.ci/scripts/check-render-artifacts\.sh$|\.github/workflows/pr-render-check\.yml$)'; then
+            echo "should-run=true" >> $GITHUB_OUTPUT
+            echo "✅ Layout, asset, or render-check file changed — running Cypress"
+          else
+            echo "should-run=false" >> $GITHUB_OUTPUT
+            echo "ℹ️  No layout/asset/render-check file changed — skipping Cypress"
+          fi
+
+      - name: Setup Node.js
+        if: steps.detect.outputs.should-run == 'true'
+        uses: actions/setup-node@v4
+        with:
+          node-version: '20'
+          cache: 'yarn'
+
+      - name: Install dependencies
+        if: steps.detect.outputs.should-run == 'true'
+        run: yarn install --frozen-lockfile
+
+      - name: Run render-regression spec
+        if: steps.detect.outputs.should-run == 'true'
+        run: |
+          node cypress/support/run-e2e-specs.js \
+            --spec "cypress/e2e/content/render-regression.cy.js" \
+            --no-mapping
diff --git a/content/example.md b/content/example.md
index 4682bf6faf..35bb193d65 100644
--- a/content/example.md
+++ b/content/example.md
@@ -1471,3 +1471,108 @@ and all the rows with the `hum` field will be in another.
 ## Ask AI Link
 
 Can't access your InfluxDB instance? {{< ask-ai-link link-text="Ask InfluxData AI" query="What's my InfluxDB version?" >}} for help.
+
+## Render-regression fixtures
+
+The sections below exercise the code-block render hook combinations that
+were broken in influxdata/docs-v2#7079. They exist to give the
+`cypress/e2e/content/render-regression.cy.js` spec something to assert
+against and to make this page a meaningful smoke test for any PR that
+touches `layouts/_default/_markup/render-codeblock.html` or any wrapper
+shortcode that contains fenced code. When adding coverage for a new
+render-hook attribute combination, add a matching fixture here.
+
+### Placeholder fence attribute
+
+```sh { placeholders="DATABASE_NAME|AUTH_TOKEN" }
+curl --request POST \
+  http://localhost:8181/api/v3/write_lp?db=DATABASE_NAME \
+  --header "Authorization: Bearer AUTH_TOKEN" \
+  --data-raw "home,room=Kitchen temp=22.1"
+```
+
+### Placeholder fence attribute with regex group
+
+```sh { placeholders="DATABASE_(TOKEN|NAME)" }
+influxctl write --token DATABASE_TOKEN --database DATABASE_NAME
+```
+
+### Callout fence attribute
+
+```sh { callout="--host" }
+influx query --host http://localhost:8086
+```
+
+### Callout fence attribute with explicit color
+
+```sh { callout="--host" callout-color="magenta" }
+influx query --host http://localhost:8086
+```
+
+### Placeholder and callout on the same fence
+
+```sh { placeholders="DATABASE_NAME" callout="--host" callout-color="orange" }
+influx query --host http://localhost:8086 --database DATABASE_NAME
+```
+
+### Placeholder fence inside a custom-timestamps wrapper
+
+The `influxdb/custom-timestamps` wrapper was one of the shortcodes
+affected by #7079 — it wraps code blocks that include timestamps so the
+UI can rewrite them. This fixture exercises a placeholder code fence
+inside that wrapper.
+
+{{% influxdb/custom-timestamps %}}
+
+```sh { placeholders="DATABASE_NAME|AUTH_TOKEN" }
+influxdb3 write --token AUTH_TOKEN --database DATABASE_NAME \
+  'home,room=Kitchen temp=22.1 1641024000'
+```
+
+{{% /influxdb/custom-timestamps %}}
+
+### Placeholder fence inside code-tab-content and custom-timestamps
+
+The worst-case path: placeholders inside a code-tab-content section
+inside a code-tabs wrapper inside an expand wrapper. This is the exact
+shape that sample-data pages use.
+
+{{< expand-wrapper >}}
+{{% expand "Expand to see the tabbed, placeholder-enabled code block" %}}
+
+{{< code-tabs-wrapper >}}
+{{% code-tabs %}}
+[influxdb3](#)
+[v2 API](#)
+{{% /code-tabs %}}
+{{% code-tab-content %}}
+
+{{% influxdb/custom-timestamps %}}
+
+```sh { placeholders="DATABASE_NAME|AUTH_TOKEN" }
+influxdb3 write --token AUTH_TOKEN --database DATABASE_NAME \
+  'home,room=Kitchen temp=22.1 1641024000'
+```
+
+{{% /influxdb/custom-timestamps %}}
+
+{{% /code-tab-content %}}
+{{% code-tab-content %}}
+
+{{% influxdb/custom-timestamps %}}
+
+```sh { placeholders="DATABASE_NAME|AUTH_TOKEN" }
+curl --request POST \
+  http://localhost:8086/api/v2/write?bucket=DATABASE_NAME \
+  --header "Authorization: Bearer AUTH_TOKEN" \
+  --data-binary "home,room=Kitchen temp=22.1 1641024000"
+```
+
+{{% /influxdb/custom-timestamps %}}
+
+{{% /code-tab-content %}}
+{{< /code-tabs-wrapper >}}
+
+{{% /expand %}}
+{{< /expand-wrapper >}}
+
diff --git a/cypress/e2e/content/render-regression.cy.js b/cypress/e2e/content/render-regression.cy.js
new file mode 100644
index 0000000000..49db3eff30
--- /dev/null
+++ b/cypress/e2e/content/render-regression.cy.js
@@ -0,0 +1,127 @@
+/// 
+
+/**
+ * Render regression tests.
+ *
+ * This spec guards against the class of bug reported in
+ * influxdata/docs-v2#7079: whitespace leaks in Hugo render hooks or
+ * wrapper shortcode templates causing Goldmark to HTML-escape
+ * highlighted code blocks into literal `
<div
+ * class="highlight"…` fragments on the final page.
+ *
+ * Two layers of coverage:
+ *
+ * 1. SHORTCODE EXAMPLES PAGE (`/example/`)
+ *    Exhaustive shortcode showcase. Any layout/render-hook change
+ *    that breaks a documented shortcode will show up here, regardless
+ *    of which product it affects. Required smoke test for any PR
+ *    that touches `layouts/` or `assets/`.
+ *
+ * 2. REPRESENTATIVE PRODUCT PAGES
+ *    One curated page per InfluxDB 3 edition, hand-picked to exercise
+ *    the combination of shortcodes/attributes that were affected by
+ *    #7079: placeholders, callouts, custom-timestamps, code-tabs,
+ *    tab-content, and expand wrappers around fenced code blocks.
+ *    These catch product-specific wiring bugs (site-specific layouts,
+ *    `product-name` substitution, per-product Vale configs) that the
+ *    example page can't.
+ *
+ * Each page is asserted against:
+ *   a) No `pre > code` element contains the literal text
+ *      `
` or `
`. These
+ *      strings can only appear inside a rendered code block if
+ *      Goldmark re-wrapped legitimate chroma output as an indented
+ *      code block — the exact #7079 failure mode.
+ *   b) At least one real, highlighted code block exists on the page
+ *      (guards against accidentally deleting the test fixture).
+ *   c) If the page has placeholder code blocks, the placeholder
+ *      `` elements render with their expected attributes.
+ *
+ * When adding a new representative page, pick one that exercises a
+ * combination of shortcodes/attributes NOT already covered, and leave
+ * a comment explaining what it tests.
+ */
+
+const RENDER_ARTIFACTS = [
+  '
', + '
 code` element.
+ */
+function assertNoEscapedHighlightMarkup() {
+  cy.get('pre code').each(($code) => {
+    const text = $code.text();
+    RENDER_ARTIFACTS.forEach((artifact) => {
+      expect(
+        text,
+        `code block should not contain escaped chroma fragment "${artifact}"`
+      ).not.to.include(artifact);
+    });
+  });
+}
+
+/**
+ * Assert that the page actually contains highlighted code output.
+ * Guards against an empty or broken fixture silently passing.
+ */
+function assertHasHighlightedCodeBlock() {
+  cy.get(
+    'pre code.language-bash, pre code.language-sh, pre code.language-js, pre code.language-python, pre code.language-sql, pre code.language-yaml, pre code.language-json',
+    { timeout: 10000 }
+  ).should('have.length.gte', 1);
+}
+
+describe('Shortcode examples page', () => {
+  // content/example.md is the exhaustive shortcode showcase used by
+  // the test:shortcode-examples npm script. Any layout change that
+  // breaks rendering will break this page.
+  it('renders /example/ with no escaped chroma fragments', () => {
+    cy.visit('/example/');
+    assertHasHighlightedCodeBlock();
+    assertNoEscapedHighlightMarkup();
+  });
+});
+
+describe('Representative product pages', () => {
+  // Each entry picks one page per InfluxDB 3 edition that exercises
+  // a distinct wrapper/attribute combination. These were all broken
+  // by #7079 in the PR preview build.
+  const pages = [
+    {
+      path: '/influxdb3/core/reference/sample-data/',
+      exercises:
+        'placeholders inside code-tab-content + custom-timestamps wrapper',
+    },
+    {
+      path: '/influxdb3/enterprise/admin/backup-restore/',
+      exercises:
+        'placeholders inside tab-content wrapper (the page that regressed the most in #7079)',
+    },
+    {
+      path: '/influxdb3/cloud-dedicated/reference/sample-data/',
+      exercises:
+        'placeholders with DATABASE_(TOKEN|NAME) regex grouping inside custom-timestamps wrapper',
+    },
+    {
+      path: '/influxdb3/clustered/admin/users/add/',
+      exercises: 'placeholders + callouts in nested expand wrappers',
+    },
+    {
+      path: '/influxdb3/cloud-serverless/reference/sample-data/',
+      exercises:
+        'post-migration placeholders fence attribute with mixed legacy/new syntax',
+    },
+  ];
+
+  pages.forEach(({ path, exercises }) => {
+    it(`${path} — ${exercises}`, () => {
+      cy.visit(path);
+      assertHasHighlightedCodeBlock();
+      assertNoEscapedHighlightMarkup();
+    });
+  });
+});
diff --git a/layouts/_default/_markup/render-codeblock.html b/layouts/_default/_markup/render-codeblock.html
index 3149954678..028f44456c 100644
--- a/layouts/_default/_markup/render-codeblock.html
+++ b/layouts/_default/_markup/render-codeblock.html
@@ -1,4 +1,4 @@
-{{ $result := transform.HighlightCodeBlock . }}
+{{- $result := transform.HighlightCodeBlock . -}}
 {{- if or .Attributes.placeholders .Attributes.callout -}}
   {{- $code := highlight .Inner .Type -}}
   {{- if .Attributes.placeholders -}}
@@ -16,5 +16,5 @@
   {{- if in $wrapped "tc-dynamic-values" -}}
     {{- $wrapped = replace $wrapped "tc-dynamic-values" "tc-dynamic-values\" data-component=\"tc-dynamic-values" -}}
   {{- end -}}
-  {{ $wrapped | safeHTML }}
+  {{- $wrapped | safeHTML -}}
 {{- end -}}
diff --git a/lefthook.yml b/lefthook.yml
index 3333340bcc..a6f243f41e 100644
--- a/lefthook.yml
+++ b/lefthook.yml
@@ -28,6 +28,11 @@ pre-commit:
       tags: lint
       glob: "content/**/*.md"
       run: .ci/scripts/check-support-links.sh {staged_files}
+    check-render-hook-whitespace:
+      tags: lint
+      glob: "layouts/_default/_markup/render-*.html"
+      run: .ci/scripts/check-render-hook-whitespace.sh {staged_files}
+      fail_text: "Render hook has bare {{ ... }} action(s). Use {{- ... -}} trimming to prevent whitespace leaks (see influxdata/docs-v2#7079)."
     eslint-debug-check:
       glob: "assets/js/*.js"
       run: yarn eslint {staged_files}
diff --git a/package.json b/package.json
index 54667ff8a3..fc74fb2de1 100644
--- a/package.json
+++ b/package.json
@@ -94,6 +94,8 @@
     "test:codeblocks:v2": "docker compose run --rm --name v2-pytest v2-pytest",
     "test:codeblocks:stop-monitors": "./test/scripts/monitor-tests.sh stop cloud-dedicated-pytest && ./test/scripts/monitor-tests.sh stop clustered-pytest",
     "test:e2e": "node cypress/support/run-e2e-specs.js",
+    "test:render-regression": "node cypress/support/run-e2e-specs.js --spec \"cypress/e2e/content/render-regression.cy.js\" --no-mapping",
+    "test:render-artifacts": ".ci/scripts/check-render-artifacts.sh public",
     "test:shortcode-examples": "node cypress/support/run-e2e-specs.js --spec \"cypress/e2e/content/index.cy.js\" content/example.md",
     "sync-plugins": "cd helper-scripts/influxdb3-plugins && node port_to_docs.js",
     "sync-plugins:dry-run": "cd helper-scripts/influxdb3-plugins && node port_to_docs.js --dry-run",