Skip to content

feat(plugin): send object payload in onPageRendered with front matter (#1095)#1118

Merged
zeroedin merged 8 commits into
mainfrom
feature/page-rendered-payload
Jul 24, 2026
Merged

feat(plugin): send object payload in onPageRendered with front matter (#1095)#1118
zeroedin merged 8 commits into
mainfrom
feature/page-rendered-payload

Conversation

@zeroedin

@zeroedin zeroedin commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Summary

  • Changes onPageRendered hook payload from a raw HTML string to { html, frontMatter, url, path } — plugins can now conditionally process pages based on front matter without parsing HTML markers
  • Only the html field in the return is applied back; frontMatter, url, and path are read-only context
  • Updated both Build() and BuildIncremental() paths identically, using a shared extractPageRenderedHTML helper that handles both map[string]interface{} and *ordered.Map return types from the QuickJS bridge

Test plan

  • All 6 new spec tests pass (onPageRendered object payload with front matter)
  • Incremental build hook test passes (onPageRendered fires during BuildIncremental)
  • Full test suite passes with race detector: go test -race ./...
  • Crosscutting tests pass (hook registry-level contract tests)

Closes #1095

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Enhanced the onPageRendered plugin hook to receive a per-page object containing html, frontMatter, url, and path (instead of raw HTML).
    • Plugins can conditionally transform or skip output; only the returned { html } is applied back (other fields are read-only).
  • Documentation
    • Updated lifecycle hook specs and examples for both Build and BuildIncremental, including edge-case return behavior.
  • Tests
    • Expanded integration and pipeline test coverage to verify payload shape, { html }-only application, and robustness against malformed/legacy hook results.

zeroedin and others added 4 commits July 23, 2026 21:53
Change onPageRendered payload contract from raw HTML string to page
object with html, frontMatter, url, path fields. Only html in the
return is applied back — other fields are read-only context for
conditional processing.

7 failing pipeline tests + 3 registry-level contract tests covering:
payload shape validation, conditional transforms via front matter,
read-only frontMatter/url/path, correct url and path values, empty
frontMatter handling, html apply-back through the pipeline, and
incremental rebuild with new callback signature.

Refs #1095

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix stale section heading "Per-page hooks (HTML string)" → "Per-page
  hooks (page object)" in PLAN.md
- Replace unchecked type assertion on frontMatter with checked assertion
  using Expect(fmOk).To(BeTrue()) in crosscutting_test.go
- Rename misleading test "page with no custom front matter has empty
  frontMatter object" → "page with minimal front matter receives
  frontMatter as an object" in hooks_test.go

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…spec

test: onPageRendered object payload with front matter (issue #1095)
…#1095)

Change onPageRendered from sending a raw HTML string to sending
{html, frontMatter, url, path}. Only the html field in the return
is applied back — other fields are read-only context for conditional
processing. Updated both Build() and BuildIncremental() paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@netlify

netlify Bot commented Jul 24, 2026

Copy link
Copy Markdown

Deploy Preview for alloyssg ready!

Name Link
🔨 Latest commit 6762534
🔍 Latest deploy log https://app.netlify.com/projects/alloyssg/deploys/6a62d2c95faee7000809dfa2
😎 Deploy Preview https://deploy-preview-1118--alloyssg.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.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

onPageRendered now receives structured page data containing HTML, front matter, URL, and path. Hook results apply only their returned html field. Full builds, incremental builds, tests, specifications, and release documentation reflect the new callback contract.

Changes

onPageRendered contract

Layer / File(s) Summary
Structured payload and HTML extraction
internal/plugin/payload.go, internal/pipeline/hooks.go, internal/pipeline/build.go, internal/pipeline/incremental.go
Both rendering paths pass structured page payloads and apply only HTML extracted from returned maps.
Hook behavior validation
internal/pipeline/hooks_test.go, internal/pipeline/incremental_hooks_test.go, test/integration/crosscutting_test.go
Tests validate payload fields, front-matter conditionals, HTML transformations, ignored metadata mutations, invalid returns, and nested front matter.
Specification, examples, and release note
plans/IMPLEMENTATION.md, plans/PLAN.md, .changeset/page-rendered-payload.md
Documentation defines the page-object payload, { html } return shape, read-only fields, ABI handling, migration behavior, and breaking callback update.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BuildPipeline
  participant OnPageRendered
  participant RenderedPage
  BuildPipeline->>OnPageRendered: Send html, frontMatter, url, path
  OnPageRendered-->>BuildPipeline: Return object containing html
  BuildPipeline->>RenderedPage: Apply returned html
Loading

Possibly related issues

  • #1120 — The PR implements and tests invalid return handling, HTML extraction, and nested front matter conversion.

Poem

A bunny sees pages hop in a row,
With front matter helping transforms know.
HTML returns, metadata stays,
Paths and URLs guide the ways.
“Only html!” the rabbit sings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: switching onPageRendered to an object payload with front matter.
Linked Issues check ✅ Passed The PR matches #1095 by passing page metadata in onPageRendered and applying only the returned html back to output.
Out of Scope Changes check ✅ Passed The changes stay focused on the onPageRendered payload contract and related tests/docs; no unrelated edits stand out.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/page-rendered-payload

Comment @coderabbitai help to get the list of available commands.

zeroedin and others added 2 commits July 23, 2026 22:24
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
internal/pipeline/build.go (2)

918-921: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Legacy string-return hooks silently no-op with no diagnostic.

Since the onPageRendered contract changed from (html) => string to (page) => object (issue #1095), a plugin still returning a bare string will fail toGoMap and be silently dropped here — the transform simply stops applying with no log message to explain why. Given this is a documented breaking change, a warning on extraction failure would help plugin authors diagnose the migration.

🩹 Proposed diagnostic
 		for i, result := range results {
-			if html, ok := extractPageRenderedHTML(result); ok {
-				pages[i].SetRenderedBody([]byte(html))
+			if html, ok := extractPageRenderedHTML(result); ok {
+				pages[i].SetRenderedBody([]byte(html))
+			} else if result != nil {
+				log.Printf("warning: onPageRendered result for %s did not match the expected page-object shape; ignoring", pages[i].RelPath)
 			}
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/pipeline/build.go` around lines 918 - 921, Update the results loop
around extractPageRenderedHTML to emit a warning when extraction fails,
including enough context to identify the invalid legacy string-return hook
result. Preserve the existing SetRenderedBody behavior when extraction succeeds,
and do not alter page processing otherwise.

888-899: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Payload construction duplicated with incremental.go; consider a shared helper.

This exact html/frontMatter/url/path map literal (with the nil-frontMatter guard) is repeated verbatim in internal/pipeline/incremental.go. Since extractPageRenderedHTML was already factored into a shared helper for the result side, a symmetrical helper for payload construction would prevent the two call sites from drifting when the payload shape changes again.

♻️ Proposed helper (in hooks.go)
// buildPageRenderedPayload builds the onPageRendered hook payload for a page.
func buildPageRenderedPayload(page *content.Page) map[string]interface{} {
	fm := page.FrontMatter
	if fm == nil {
		fm = map[string]interface{}{}
	}
	return map[string]interface{}{
		"html":        page.HTML(),
		"frontMatter": fm,
		"url":         page.URL,
		"path":        page.RelPath,
	}
}
-		for i, page := range pages {
-			fm := page.FrontMatter
-			if fm == nil {
-				fm = map[string]interface{}{}
-			}
-			payloads[i] = map[string]interface{}{
-				"html":        page.HTML(),
-				"frontMatter": fm,
-				"url":         page.URL,
-				"path":        page.RelPath,
-			}
-		}
+		for i, page := range pages {
+			payloads[i] = buildPageRenderedPayload(page)
+		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/pipeline/build.go` around lines 888 - 899, The payload map
construction is duplicated between the build and incremental flows. Add a shared
buildPageRenderedPayload helper in hooks.go containing the nil-frontMatter guard
and html/frontMatter/url/path fields, then replace both call sites with this
helper so payload behavior remains identical and shape changes stay centralized.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plans/IMPLEMENTATION.md`:
- Line 642: The implementation guide must match the current payload contract in
internal pipeline Build. Update the documented onPageRendered instructions
around HookRenderedPayload and the payload construction/apply-back behavior to
reflect the actual map[string]interface{} implementation, or change the
implementation to construct plugin.HookRenderedPayload if that is the intended
contract; keep the guide and Build behavior consistent.

In `@plans/PLAN.md`:
- Line 2472: Clarify the onPageRendered return contract in the hook table to
state whether both the full page object and an object containing only html are
supported; explicitly note that, regardless of shape, only the returned object's
html field is applied back to the rendered page.
- Line 2355: Update hook request construction in the WASM hook dispatch logic
around the code sending requests from internal/plugin/wasm.go so map/object
payloads merge their fields alongside the event key, while primitive payloads
remain wrapped under payload. Preserve the documented event name and ensure
returned payload handling still works for both envelope forms.

---

Nitpick comments:
In `@internal/pipeline/build.go`:
- Around line 918-921: Update the results loop around extractPageRenderedHTML to
emit a warning when extraction fails, including enough context to identify the
invalid legacy string-return hook result. Preserve the existing SetRenderedBody
behavior when extraction succeeds, and do not alter page processing otherwise.
- Around line 888-899: The payload map construction is duplicated between the
build and incremental flows. Add a shared buildPageRenderedPayload helper in
hooks.go containing the nil-frontMatter guard and html/frontMatter/url/path
fields, then replace both call sites with this helper so payload behavior
remains identical and shape changes stay centralized.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2201d2b8-6a73-482c-8459-f7e1b2a2a7bc

📥 Commits

Reviewing files that changed from the base of the PR and between d67af39 and f326377.

📒 Files selected for processing (8)
  • internal/pipeline/build.go
  • internal/pipeline/hooks.go
  • internal/pipeline/hooks_test.go
  • internal/pipeline/incremental.go
  • internal/pipeline/incremental_hooks_test.go
  • plans/IMPLEMENTATION.md
  • plans/PLAN.md
  • test/integration/crosscutting_test.go

Comment thread plans/IMPLEMENTATION.md Outdated
Comment thread plans/PLAN.md
Comment thread plans/PLAN.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/page-rendered-payload.md:
- Line 5: Update the onPageRendered hook chaining described in the change note
so frontMatter, url, and path cannot be mutated and propagated between hooks.
Preserve html as the only mutable value applied from hook results, either by
stripping metadata before passing each result onward or deep-freezing the
metadata before dispatch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4e3c92cd-87c9-46f2-9db0-4034f2f80d3a

📥 Commits

Reviewing files that changed from the base of the PR and between f326377 and ffc9a25.

📒 Files selected for processing (1)
  • .changeset/page-rendered-payload.md

Comment thread .changeset/page-rendered-payload.md

@zeroedin zeroedin left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Results

Scope: internal/pipeline/build.go, hooks.go, incremental.go, hooks_test.go, incremental_hooks_test.go, test/integration/crosscutting_test.go, plans/PLAN.md, plans/IMPLEMENTATION.md, .changeset/page-rendered-payload.md
Intent: Change onPageRendered hook payload from raw HTML string to { html, frontMatter, url, path } object. Only html in the return is applied back. Both Build() and BuildIncremental() paths updated identically.

P1 -- High

# File Issue Reviewer Confidence Route
1 build.go:890, incremental.go:497 Missing convertOrderedMaps() on FrontMatter. fm := page.FrontMatter passes the raw map which can contain *ordered.Map values from JSON data files via cascade. Every other hook that sends FrontMatter (onContentTransformed at hooks.go:283, onPagesReady at hooks.go:486, onContentLoaded at hooks.go:201) calls convertOrderedMaps(). IMPLEMENTATION.md explicitly requires it (issue #463): "ordered.Map.MarshalJSON is expensive (13.7% of CPU in profiling)." Fix: fm := convertOrderedMaps(page.FrontMatter) in both files. correctness, performance, maintainability, adversarial 100 safe_auto
2 internal/plugin/payload.go (missing), build.go:894, incremental.go:501 Missing HookRenderedPayload typed struct. IMPLEMENTATION.md line 642: "The developer must create a HookRenderedPayload struct in internal/plugin/payload.go with JSON tags html, frontMatter, url, path." Implementation uses raw map[string]interface{} instead. All 6 other hooks use typed structs (HookTransformPayload, HookPagesReadyPayload, etc.). This also eliminates the DRY violation — the same 11-line payload map literal is duplicated verbatim in build.go and incremental.go. Fix: Create the struct, use plugin.HookRenderedPayload{HTML: page.HTML(), FrontMatter: convertOrderedMaps(page.FrontMatter), URL: page.URL, Path: page.RelPath} in both call sites. correctness, maintainability 100 manual

P2 -- Moderate

# File Issue Reviewer Confidence Route
3 hooks.go:516-522 Old-format plugins fail silently. A plugin written for the old API returns a string. extractPageRenderedHTMLtoGoMap() fails the type assertion → returns ("", false)SetRenderedBody is skipped. The plugin's transformation is silently dropped with no warning or error. For a documented breaking change, the failure mode should be visible. Consider logging a warning when toGoMap fails (the result is non-nil but not a map), e.g., "onPageRendered: plugin returned %T instead of map — ignoring (old string API was removed in v0.X)". adversarial 90 manual

Pre-existing Issues (not blocking)

# File Issue Reviewer Confidence
4 internal/plugin/wasm.go:827-830 WASM CallHook always wraps as {event, payload} instead of merging event key into map payloads per spec. PLAN.md says map payloads should get {"event": "onPageRendered", "html": "...", ...} but the code wraps all payloads as {"event": name, "payload": payload}. Pre-existing — affects all map-payload hooks (onContentTransformed, etc.), not specific to this PR. adversarial 85
5 incremental.go:509 BuildIncremental logs onPageRendered errors as warnings while Build treats them as fatal. Pre-existing divergence, but the new object payload makes malformed returns more likely. adversarial 90

Agent-Native Gaps

Docs and example plugins still show the old string API:

  • /docs/content/hooks/index.md:326-332 — old (html) => html.replace(...) signature
  • /docs/content/plugins/index.md:70 — old inline example
  • /docs/plugins/shiki.js:25, /docs/plugins/prettify.js:6 — live example plugins use old format

Per project rules, docs are separate PRs. Open an issue for the architect to update these before release.

Learnings & Past Solutions

The convertOrderedMaps requirement is a known pattern established in issue #571 (ordered map type preservation through hook serialization). The performance cost was profiled in issue #463 at 13.7% CPU. Every FrontMatter-sending hook follows this pattern — this PR is the only exception.

Coverage

  • Testing gaps (for architect — open issue): extractPageRenderedHTML edge cases are untested (nil return, missing html key, non-string html, old string format return). BuildIncremental object payload shape is not verified at field level. No test exercises FrontMatter containing *ordered.Map values through onPageRendered.
  • Residual risks: FrontMatter reference sharing — fm := page.FrontMatter passes a reference, not a copy. Go-registered hooks could mutate page FrontMatter in-place. convertOrderedMaps creates a copy when conversion is needed, but passes through the original reference when no *ordered.Map values exist. Low risk since onPageRendered is post-render.
  • Suppressed: 0 findings below anchor 75.

Verdict: Not ready

Reasoning: Two P1 findings must be fixed before merge. Finding #1 (convertOrderedMaps) is a performance regression confirmed by 4 independent reviewers at 100% confidence — the spec explicitly requires it, all other hooks follow the pattern, and profiling data shows 13.7% CPU overhead without it. Finding #2 (missing HookRenderedPayload struct) is a direct spec deviation — IMPLEMENTATION.md explicitly says to create it, and the raw map approach blocks sonic JIT optimization while duplicating code across two files. Both fixes are straightforward and follow established patterns already present in the codebase. Fix order: #2 first (struct creation naturally resolves the DRY issue), then #1 (add convertOrderedMaps inside the struct construction).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@zeroedin

Copy link
Copy Markdown
Owner Author

Review response

All fixes pushed in cf57ec4. Full test suite passes with -race.

P1 #1 — Missing convertOrderedMaps() on FrontMatter

Fixed in cf57ec4. buildPageRenderedPayload calls convertOrderedMaps(page.FrontMatter), matching the pattern used by onContentTransformed (hooks.go:283), onPagesReady (hooks.go:486), and onContentLoaded (hooks.go:201).

P1 #2 — Missing HookRenderedPayload typed struct

Fixed in cf57ec4. Added HookRenderedPayload in internal/plugin/payload.go with JSON tags html, frontMatter, url, path. Both build.go and incremental.go now call buildPageRenderedPayload(page) — the DRY violation is eliminated.

P2 #3 — Old-format plugins fail silently

Fixed in cf57ec4. Both Build() and BuildIncremental() now log a warning when extractPageRenderedHTML fails on a non-nil result: "warning: onPageRendered result for %s: expected page object with html key, got %T — plugin may need migration to the object API".

Pre-existing #4 — WASM CallHook envelope

Skipped. Pre-existing — affects all map-payload hooks, not this PR's scope.

Pre-existing #5BuildIncremental error handling divergence

Skipped. Pre-existing — BuildIncremental logs as warning while Build returns fatal. Existing pattern predates this change.

Agent-Native Gaps — Docs showing old string API

Issue opened: #1119. Docs are separate PRs per project rules.

Testing gaps (advisory)

Issue opened: #1120. extractPageRenderedHTML edge cases, BuildIncremental field-level payload verification, and *ordered.Map FrontMatter through onPageRendered — all flagged for the architect.

CodeRabbit threads

  • IMPLEMENTATION.md (line 642) — Already resolved. Plan docs are architect territory; implementation now matches the spec's intent (typed struct + convertOrderedMaps).
  • PLAN.md (line 2355, WASM envelope) — Replied and resolved. Pre-existing, affects all hooks.
  • PLAN.md (line 2472, return contract) — Replied and resolved. The { html } column describes what's applied, not what's returned.
  • Changeset (metadata freeze) — Already resolved. Go→JS bridge copies the payload; only html is extracted from results on the Go side.

…ndered payload (#1120)

- extractPageRenderedHTML edge cases: nil result, missing html key,
  non-string html, old string format return — all preserve original HTML
- BuildIncremental field-level payload verification: validates all 4
  fields (html, frontMatter, url, path) across initial and cached rebuilds
- Nested YAML front matter with *ordered.Map values through onPageRendered:
  proves convertOrderedMaps flattens deeply nested maps for plugins
- Updated PLAN.md and IMPLEMENTATION.md with edge-case documentation

Refs #1120

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@zeroedin

Copy link
Copy Markdown
Owner Author

Edge-case coverage added (issue #1120)

Pushed 6762534 with 6 new tests covering the gaps identified in human review:

extractPageRenderedHTML edge cases (4 tests)

Test Plugin returns Expected behavior
nil return undefined (no return) Original HTML preserved, no warning
Missing html key { transformed: true } Original HTML preserved, migration warning
Non-string html { html: 42 } Original HTML preserved, migration warning
Old string format return page.html + '...' Original HTML preserved, migration warning

BuildIncremental field-level payload (1 test)

Validates all 4 fields (html, frontMatter, url, path) with correct values across both nil-cache and cached incremental rebuilds. Verifies updated front matter is reflected in the payload after content changes.

*ordered.Map front matter (1 test)

3-level nested YAML front matter (metadata.author.social.twitter) proves convertOrderedMaps recursively flattens *ordered.Map values for plugin access.

PLAN.md and IMPLEMENTATION.md updated with edge-case documentation.

Full suite: 404 pipeline + 60 integration = 464 passing with -race.

@zeroedin zeroedin left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review after commits cf57ec4 and 6762534

Previous review was against ffc9a25. Two commits have landed since.

Finding resolution

# Original finding Status
1 Missing convertOrderedMaps() on FrontMatter FixedbuildPageRenderedPayload calls convertOrderedMaps(page.FrontMatter) at hooks.go:516
2 Missing HookRenderedPayload typed struct + DRY Fixed — Struct added to payload.go:27-32 with correct JSON tags. Shared buildPageRenderedPayload helper eliminates DRY violation. Both build.go:890 and incremental.go:496 use the helper.
3 Old-format plugins fail silently Fixed — Migration warning logged at build.go:912 and incremental.go:506 when result is non-nil but not a map with an html key

New code quality check

The fix commits are clean:

  • buildPageRenderedPayload (hooks.go:514-526): Returns plugin.HookRenderedPayload (typed struct). Calls convertOrderedMaps before nil guard — correct order (convert first, then fall back to empty map if result is nil). Matches the pattern used by onContentTransformed.
  • Migration warning (build.go:911-912, incremental.go:505-506): Guards with result != nil so nil/undefined returns don't trigger false warnings. Warning text includes %T and mentions "object API" — actionable for plugin authors.
  • HookRenderedPayload (payload.go:24-32): JSON tags match spec (html, frontMatter, url, path). Consistent with HookTransformPayload structure.
  • Edge-case tests (hooks_test.go, incremental_hooks_test.go): 6 new tests covering nil return, missing html key, non-string html, old string format, nested ordered.Map front matter, and BuildIncremental field-level verification. All gaps flagged in the previous review are now covered.
  • Spec updates: IMPLEMENTATION.md updated with edge-case handling docs (issue #1120). PLAN.md updated with return value edge cases section and BuildIncremental parity note.

Remaining pre-existing issues (not blocking)

  • WASM CallHook always wraps as {event, payload} instead of merging — pre-existing, affects all hooks.
  • BuildIncremental logs hook errors as warnings vs Build treating them as fatal — pre-existing divergence.

Verdict: Ready to merge

Reasoning: All three findings from the initial review are resolved. The typed HookRenderedPayload struct matches the spec. convertOrderedMaps is called correctly. Migration warnings surface old-format plugin returns. Edge-case test coverage is comprehensive. Specs are updated. The two pre-existing issues are unrelated to this PR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/pipeline/hooks_test.go (1)

3009-3106: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the migration warning is actually logged.

Each of these three tests (missing html key, non-string html, old string-format return) documents in its failure message that "the pipeline logs a migration warning," but none of them capture/assert on the actual log output — they only verify HTML preservation. Since logging the migration warning is an explicit implementation goal for this PR (per the PR objectives), consider capturing log output (e.g., redirect via log.SetOutput to a buffer) and asserting the warning text appears, to close the gap between the documented behavior and what's actually verified.

🧪 Example: capturing log output
var buf bytes.Buffer
log.SetOutput(&buf)
defer log.SetOutput(os.Stderr)

result, err := pipeline.BuildWithContent(cfg, contentMap)
Expect(err).NotTo(HaveOccurred())
Expect(buf.String()).To(ContainSubstring("onPageRendered"))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/pipeline/hooks_test.go` around lines 3009 - 3106, Update the three
tests for missing HTML, non-string HTML, and old string-format returns to
capture logger output around pipeline.BuildWithContent, restore the logger
destination afterward, and assert the captured warning contains the
onPageRendered migration-warning text. Keep the existing HTML preservation
assertions unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/pipeline/hooks_test.go`:
- Around line 3009-3106: Update the three tests for missing HTML, non-string
HTML, and old string-format returns to capture logger output around
pipeline.BuildWithContent, restore the logger destination afterward, and assert
the captured warning contains the onPageRendered migration-warning text. Keep
the existing HTML preservation assertions unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6ff7d329-4f55-4a14-8524-0874bc812fba

📥 Commits

Reviewing files that changed from the base of the PR and between cf57ec4 and 6762534.

📒 Files selected for processing (4)
  • internal/pipeline/hooks_test.go
  • internal/pipeline/incremental_hooks_test.go
  • plans/IMPLEMENTATION.md
  • plans/PLAN.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • plans/IMPLEMENTATION.md

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.

onPageRendered: include front matter in hook payload

1 participant