Skip to content

fix(diffs): share SSR preloads and repair language-pack hydration#100487

Merged
steipete merged 6 commits into
mainfrom
feat/diffs-render-pipeline
Jul 5, 2026
Merged

fix(diffs): share SSR preloads and repair language-pack hydration#100487
steipete merged 6 commits into
mainfrom
feat/diffs-render-pipeline

Conversation

@steipete

@steipete steipete commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Deep review of the diffs extension surfaced one real bug, one config-precedence bug, and a 2x render cost in the default mode. This PR fixes the verified findings.

Render pipeline: stop rendering every diff twice

mode=both (the default) ran the full SSR pipeline — diff computation plus shiki highlighting — twice per file, because the viewer and file variants differ only in an inert unsafeCSS string (the file variant bumps --diffs-font-size to >=16px). The expensive asyncRender never reads that CSS; SSR just appends it as a <style> element.

Now the preload runs once per file and both documents share it; the file-mode document applies the font bump as a document-level override on the shadow host (outer host rules beat :host custom properties), and hydration payloads keep their per-variant options.

Measured on an 8-file / 240-changed-line patch (5-run average after warmup, M-series):

target before after
viewer 344ms 352ms
image 285ms 313ms
both (default) 651ms 303ms

Language pack: highlighting survived SSR but died at hydration

The language-pack runtime re-normalized payload languages without languagePackAvailable, so pack-only languages (abap, haskell, zig, ...) were rewritten to text and the boot-time rerender replaced the correctly server-highlighted HTML with plain text. The 10MB pack bundle was functionally dead in the interactive viewer. The build now defines a per-target flag (__OPENCLAW_DIFFS_LANGUAGE_PACK__, same pattern as __OPENCLAW_VERSION__) and hydration forwards it.

Also fixed

  • Case-insensitive language hintslang: "Python" / "TypeScript" silently degraded to plain text; hints are lowercased before lookup.
  • Config alias precedence — an explicit defaults.fileFormat: "png" was overridden by a stale deprecated imageFormat: "pdf" because "equal to schema default" was misread as "not set". Explicit canonical values now always win; schema defaults on the two alias-carrying fields moved to runtime normalization so alias-only configs keep working (covered by manifest-validated tests).
  • Identical before/after short-circuit — identical inputs no longer burn SSR plus a Chromium screenshot to produce an empty diff; the tool returns details.changed: false with a clear message, and rendered results now carry changed: true (documented in README, docs, skill, and prompt guidance).
  • Patch input errors are input errors — unparseable/oversized patches now surface as ToolInputError instead of internal tool failures.
  • Cacheable viewer runtime — the 4.9MB (base) / 10MB (pack) viewer-runtime.js is content-hash-pinned by the loader but was served no-store, re-downloading on every page view. It is now public, max-age=31536000, immutable; the loader and HTML stay no-store.
  • Truthful file-render errors — post-launch failures (hydration timeout, navigation timeout) no longer masquerade as "install Chrome/Chromium"; only browser acquisition/new-page failures get that guidance.
  • Timing-safe artifact token compare in the store (safeEqualSecret), relevant with security.allowRemoteViewer: true.
  • Idle browser-close timer unref'd so short-lived embedders exit promptly.

Proof

  • vitest run --config test/vitest/vitest.extension-diffs.config.ts — 145/145 passing
  • tsc --noEmit -p extensions/diffs/tsconfig.json — clean; scoped oxlint — clean
  • New regression tests: single-preload call counts (before/after + per patch file), alias precedence through manifest validation, case-insensitive hints, changed flag + no-artifact short-circuit, launch-vs-render error split, cache-control split, token-mismatch nulls.

Viewer runtime assets are untracked build outputs; the next packaging build picks up the hydration fix via scripts/build-diffs-viewer-runtime.mjs.

Render viewer and file documents from a single @pierre/diffs SSR preload
per file (mode=both previously ran the full diff+highlight pipeline
twice; 651ms -> 303ms on an 8-file patch), apply the file-mode font bump
as a document-level override, and keep hydration payloads
variant-faithful.

Fix the language-pack runtime downgrading pack-only languages to plain
text at hydration by defining a per-target build flag and forwarding it
to payload normalization.

Also: case-insensitive language hints, identical before/after
short-circuit with details.changed, patch input failures classified as
tool input errors, canonical config values now win over deprecated
aliases, hash-pinned viewer runtime served immutable, truthful
browser-vs-render errors, timing-safe artifact token compare, unref
idle browser timer.
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation scripts Repository scripts extensions: diffs size: L maintainer Maintainer-authored PR labels Jul 5, 2026
@clawsweeper

clawsweeper Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 5, 2026, 7:18 PM ET / 23:18 UTC.

Summary
The branch updates the diffs plugin render pipeline, language-pack hydration flag, config alias/default normalization, artifact security/cache headers, browser error handling, docs, and regression tests.

PR surface: Source +52, Tests +235, Docs +8, Other +3. Total +298 across 24 files.

Reproducibility: yes. Source inspection of the base shows mode=both performed separate viewer/image SSR preloads and hydration normalized payload languages without a language-pack flag; I did not run a live viewer scenario in this read-only review.

Review metrics: 1 noteworthy metric.

  • Config/default surfaces: 2 schema defaults removed, 0 runtime defaults removed. The diff changes how defaults.fileFormat and defaults.fileQuality are applied during config validation, which is compatibility-sensitive before merge.

Stored data model
Persistent data-model change detected: persistent cache schema: CHANGELOG.md, persistent cache schema: extensions/diffs-language-pack/src/plugin.ts, persistent cache schema: extensions/diffs/src/http.ts, persistent cache schema: extensions/diffs/src/store.test.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🌊 off-meta tidepool
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Maintainer should confirm the two removed schema defaults are the intended compatibility path before merge.

Risk before merge

  • [P1] The PR intentionally removes two config schema defaults while preserving runtime defaults; maintainers should accept that config metadata tradeoff before merge.
  • [P1] The language-pack hydration and image-typography paths are browser-visible; without a linked live viewer artifact or recording, review relies on code inspection, tests, CI, and the benchmark table.

Maintainer options:

  1. Accept Runtime-Default Normalization (recommended)
    Merge with runtime defaults plus the new manifest-validation tests as the intended compatibility path for alias-only and canonical configs.
  2. Add Metadata-Safe Defaults
    Before merge, expose the default values through docs or a non-applying metadata surface so config UIs do not depend on schema default injection.

Next step before merge

  • [P1] No automated repair is indicated; the remaining action is maintainer review of the protected PR and the config/default compatibility tradeoff.

Maintainer decision needed

  • Question: Should the maintainer-labeled diffs PR proceed with runtime-owned defaults for fileFormat and fileQuality instead of manifest-applied schema defaults?
  • Rationale: The change is a sensible bug fix, but config/default surfaces are compatibility-sensitive and the protected maintainer label requires explicit maintainer handling rather than cleanup automation.
  • Likely owner: Peter Steinberger — Peter is the current PR author and also has substantial prior diffs security/docs/test history, making him the closest routed owner for this compatibility choice.
  • Options:
    • Accept Runtime Defaults (recommended): Merge with runtime normalization and the added alias-precedence tests as the compatibility path for the removed schema defaults.
    • Preserve Default Metadata Elsewhere: Ask for a metadata-only way to expose fileFormat and fileQuality defaults without reintroducing manifest-applied defaults that shadow aliases.
    • Pause For Browser Proof: Wait for a linked live viewer artifact or recording if maintainers want direct visual proof for the hydration and image typography behavior.

Security
Cleared: No concrete security or supply-chain regression was found; the remote viewer token comparison is hardened and CSP/route boundaries remain constrained.

Review details

Best possible solution:

Land the diffs fixes with runtime defaults as the canonical compatibility path, after maintainer acceptance of the config-schema change and exact-head CI/proof gates.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection of the base shows mode=both performed separate viewer/image SSR preloads and hydration normalized payload languages without a language-pack flag; I did not run a live viewer scenario in this read-only review.

Is this the best way to solve the issue?

Yes, with a compatibility caveat. Sharing the preload, injecting a per-target language-pack flag, and moving alias-sensitive defaults to runtime normalization are maintainable fixes; the schema-default change needs maintainer acceptance before merge.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 2525ea41fd09.

Label changes

Label changes:

  • add P2: This is a normal-priority diffs plugin bug/performance/security-hardening PR with limited blast radius.
  • add merge-risk: 🚨 compatibility: The PR intentionally changes plugin config/default application for fileFormat and fileQuality, which can affect existing config validation and metadata consumers.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🌊 off-meta tidepool and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: The PR is labeled maintainer, so the external contributor proof gate is not applied; the body still includes benchmark data and focused validation commands.

Label justifications:

  • P2: This is a normal-priority diffs plugin bug/performance/security-hardening PR with limited blast radius.
  • merge-risk: 🚨 compatibility: The PR intentionally changes plugin config/default application for fileFormat and fileQuality, which can affect existing config validation and metadata consumers.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🌊 off-meta tidepool and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: The PR is labeled maintainer, so the external contributor proof gate is not applied; the body still includes benchmark data and focused validation commands.
Evidence reviewed

PR surface:

Source +52, Tests +235, Docs +8, Other +3. Total +298 across 24 files.

View PR surface stats
Area Files Added Removed Net
Source 11 199 147 +52
Tests 8 259 24 +235
Docs 4 8 0 +8
Config 0 0 0 0
Generated 0 0 0 0
Other 1 3 0 +3
Total 24 469 171 +298

What I checked:

  • Protected live PR state: Live GitHub state shows this PR is open, mergeable, and labeled maintainer, which is a protected cleanup label in this review workflow.
  • Not implemented on current main: The main checkout does not contain the core PR fix commit; git merge-base --is-ancestor d932a9ca86a9567515b6515f683ef2aa154433f6 HEAD returned 1. (d932a9ca86a9)
  • Shared preload implementation: The PR computes one preload result and derives viewer/image payload variants from it, which addresses the duplicate SSR preload path for mode=both. (extensions/diffs/src/render.ts:395, 2480bb7955a4)
  • Language-pack hydration fix: Hydration now passes the injected language-pack availability flag into payload language normalization, preserving pack-only language hints in the browser runtime. (extensions/diffs/src/viewer-client.ts:310, 2480bb7955a4)
  • Config alias/default normalization: The PR removes manifest-applied defaults from fileFormat and fileQuality and makes canonical configured values win over deprecated aliases in runtime normalization. (extensions/diffs/src/config.ts:147, 2480bb7955a4)
  • Focused regression coverage: New tests cover single-preload behavior, alias precedence through manifest validation, identical-input short-circuiting, and language-pack flag resolution. (extensions/diffs/src/render.preload.test.ts:23, 2480bb7955a4)

Likely related people:

  • Peter Steinberger: Shortlog shows substantial recent diffs-area work, including viewer security/docs and test refactors, and this PR author also has prior merged history in the same plugin area. (role: recent area contributor; confidence: high; commits: 4a1be9825421, d977af58534b, e510132f3c96; files: extensions/diffs, docs/tools/diffs.md)
  • Gustavo Madeira Santana: History shows the original diffs plugin and major render/config/language-payload work came from this author, including the language-normalization path this PR touches. (role: feature owner by history; confidence: high; commits: 612ed5b3e1ea, f96e5bec3983, 5f49a5da3c6e; files: extensions/diffs/src/render.ts, extensions/diffs/src/config.ts, extensions/diffs/src/viewer-client.ts)
  • Vincent Koc: Blame on the current base for the touched render, config, and viewer-client lines points to recent commits carrying the diffs implementation into the current main history. (role: recent area contributor; confidence: medium; commits: 20626d45807d, e085fa1a3ffd; files: extensions/diffs/src/render.ts, extensions/diffs/src/config.ts, extensions/diffs/src/viewer-client.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2480bb7955

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CHANGELOG.md

### Fixes

- **Diffs rendering:** render viewer and image output from one SSR preload, preserve language-pack highlighting through hydration, normalize language hints case-insensitively, skip identical before/after inputs with an explicit `changed` result, report truthful file-render and input errors, cache hash-pinned viewer runtimes, and prefer canonical file settings over stale aliases. (#100487)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Remove the normal PR changelog entry

This commit is a normal diffs fix, but AGENTS.md:238 says CHANGELOG.md is release-only and must not be edited for normal PRs or direct main fixes because release generation owns it. Keeping this hand-added Unreleased line makes the release notes non-canonical and can duplicate generated entries when the release tooling derives notes from merged PRs/direct commits, so remove the changelog edit and keep the release-note context in the PR or commit message instead.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jul 5, 2026
@steipete
steipete merged commit 2ded26a into main Jul 5, 2026
26 checks passed
@steipete
steipete deleted the feat/diffs-render-pipeline branch July 5, 2026 23:34
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 6, 2026
…enclaw#100487)

* fix(diffs): share SSR preloads and repair language-pack hydration

Render viewer and file documents from a single @pierre/diffs SSR preload
per file (mode=both previously ran the full diff+highlight pipeline
twice; 651ms -> 303ms on an 8-file patch), apply the file-mode font bump
as a document-level override, and keep hydration payloads
variant-faithful.

Fix the language-pack runtime downgrading pack-only languages to plain
text at hydration by defining a per-target build flag and forwarding it
to payload normalization.

Also: case-insensitive language hints, identical before/after
short-circuit with details.changed, patch input failures classified as
tool input errors, canonical config values now win over deprecated
aliases, hash-pinned viewer runtime served immutable, truthful
browser-vs-render errors, timing-safe artifact token compare, unref
idle browser timer.

* docs(changelog): link diffs rendering entry to PR

* test(diffs): narrow manifest validation results before value access

* test(tooling): allowlist diffs viewer-client define suppression
giodl73-repo pushed a commit to giodl73-repo/openclaw that referenced this pull request Jul 8, 2026
…enclaw#100487)

* fix(diffs): share SSR preloads and repair language-pack hydration

Render viewer and file documents from a single @pierre/diffs SSR preload
per file (mode=both previously ran the full diff+highlight pipeline
twice; 651ms -> 303ms on an 8-file patch), apply the file-mode font bump
as a document-level override, and keep hydration payloads
variant-faithful.

Fix the language-pack runtime downgrading pack-only languages to plain
text at hydration by defining a per-target build flag and forwarding it
to payload normalization.

Also: case-insensitive language hints, identical before/after
short-circuit with details.changed, patch input failures classified as
tool input errors, canonical config values now win over deprecated
aliases, hash-pinned viewer runtime served immutable, truthful
browser-vs-render errors, timing-safe artifact token compare, unref
idle browser timer.

* docs(changelog): link diffs rendering entry to PR

* test(diffs): narrow manifest validation results before value access

* test(tooling): allowlist diffs viewer-client define suppression
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to documentation extensions: diffs maintainer Maintainer-authored PR merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. scripts Repository scripts size: L status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant