Skip to content

Expose page render source metadata for a11y agent#36264

Open
fmontes wants to merge 6 commits into
mainfrom
fmontes/a11y-agent-backend
Open

Expose page render source metadata for a11y agent#36264
fmontes wants to merge 6 commits into
mainfrom
fmontes/a11y-agent-backend

Conversation

@fmontes

@fmontes fmontes commented Jun 22, 2026

Copy link
Copy Markdown
Member

Summary

This is the backend slice of the a11y-agent work. It gives the agent enough render-source metadata to understand which theme/template files participate in a page render, and enough stylesheet source information to trace compiled CSS back to editable source files.

What changed

  • Expanded GET /api/v1/page/_render-sources so theme sources are returned as a generic files[] list instead of only VTL-oriented refs. Each file now includes its path, identifier, and lowercased extension, so consumers can decide which files are relevant.
  • Replaced the VTL-specific file ref view with a generic FileRefView, since the same shape is now used for VTL, CSS, SCSS/SASS/LESS, JS, and widget refs.
  • Added opt-in inline Sass source maps using ?sourcemap=true for compiled stylesheet requests. This keeps normal stylesheet responses unchanged, while allowing the agent to map CSS rules back to editable SCSS sources when needed.
  • Fixed preview/live mode handling for theme stylesheet and template file requests so backend users scanning a page in LIVE, PREVIEW, or edit-related modes get assets that match the requested page mode instead of accidentally mixing in working content.
    • Before: when you do ?mode=LIVE it leaks vtl and or css files in working state.
  • Updated OpenAPI metadata, language entries, and integration tests around render-source and source-map behavior.

Why

The a11y agent needs to propose or apply fixes against the files editors can actually change. For contrast fixes and template attribution, looking only at rendered HTML/CSS is not enough: the agent needs to know which theme files exist, which ones are editable stylesheet/template sources, and how compiled CSS maps back to source SCSS.

This PR keeps that backend/API support separate from the frontend and agent runtime changes so it is easier to review and QA independently.

Verification

  • git diff --check before commit
  • Not run locally: full dotcms-integration suite; it uses the heavier Maven/Failsafe integration setup.

This PR fixes: #36265

@github-actions github-actions Bot added the Area : Backend PR changes Java/Maven backend code label Jun 22, 2026
@claude

claude Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @fmontes's task in 5m 40s —— View job


Rollback Safety Analysis

  • Read rollback-unsafe categories reference
  • Get full PR diff
  • Analyze diff against all categories
  • Post results and apply labels

Result: Pull Request Unsafe to Rollback — two categories matched.

M-3 — REST / GraphQL / Headless API Contract Change 🟡 MEDIUM

ThemeSourceView response shape changed breakingly: the vtls array (VtlFileRefView with path + identifier) is removed and replaced with a files array (FileRefView with path + identifier + extension). Consumers reading theme.vtls get null on N, and after rollback N-1 returns vtls again, breaking any consumer deployed against N's contract.

Files: ThemeSourceView.java, VtlFileRefView.java (deleted), openapi.yaml.

Safer alternative: Keep vtls as a deprecated alias in N (delegate to files filtered by .vtl), remove in N+1.

H-8 — VTL Viewtool Contract Change 🟠 HIGH

DotTemplateTool.themeLayout() now adds templatePathIsFileAsset to its returned map and changes the templatePath value from a physical inode disk path to a logical host-qualified path. PageLoader's generated Velocity now branches on $dotTheme.templatePathIsFileAsset. On rollback to N-1, that key is absent, so file-asset themes fall into the #else #parse (static-fallback disk) path. Risk is partially mitigated since PageLoader regenerates the Velocity fragment on first render, but there is a brief window after rollback where file-asset-themed pages may serve wrong content.

Files: DotTemplateTool.java, PageLoader.java.

The label AI: Not Safe To Rollback has been applied to the PR.

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — deepseek.v3.2

[🟠 High] dotCMS/src/main/java/com/dotcms/csspreproc/CSSPreProcessServlet.java:188 — Missing null check on user before calling user.getUserId() in log statement; could cause NPE if user is null (e.g., anonymous request).
[🟠 High] dotCMS/src/main/java/com/dotcms/csspreproc/CSSPreProcessServlet.java:176 — Permission check for source map uses fileAsset variable which may be null if actualUri resolution fails earlier; could cause NPE.
[🟡 Medium] dotCMS/src/main/java/com/dotcms/csspreproc/CSSPreProcessServlet.java:180 — Log statement uses user.getUserId() without null guard; if user is null, logs "null" but may still throw NPE depending on Logger implementation.
[🟡 Medium] dotCMS/src/main/java/com/dotcms/csspreproc/CSSPreProcessServlet.java:400fileName substring logic assumes fileUri is not null; if fileUri is null, lastIndexOf throws NPE.
[🟡 Medium] dotCMS/src/main/java/com/dotcms/csspreproc/dartsass/CompilerOptions.java:156this.sourceMap() method call inside generate() could be moved outside condition for clarity, but no bug.
[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/util/PageMode.java:134modeFromReferer does not URL-decode the mode value; if the Referer contains encoded characters (e.g., %20), the mode may not match.
[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/servlets/SpeedyAssetServlet.java:119 — Lambda debug log references request.getParameter("mode") directly; if parameter is missing, logs null which is fine but could be misleading.


Run: #27989102206 · tokens: in: 17067 · out: 429 · total: 17496

@claude

claude Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Pull Request Unsafe to Rollback!!!


  • Category: M-3 — REST / GraphQL / Headless API Contract Change
  • Risk Level: 🟡 MEDIUM
  • Why it's unsafe: The ThemeSourceView REST response shape changed in a breaking way. The vtls array (of type VtlFileRefView, with fields path + identifier) is removed and replaced with a files array (of type FileRefView, with fields path + identifier + extension). Any consumer (headless front-end, integration, or the a11y agent itself on N-1) that calls the /api/v1/page/render-sources endpoint and reads theme.vtls will get null after rollback to N-1, because N drops vtls entirely; and after rollback N-1 will return vtls (not files), breaking consumers co-deployed against N's contract.
  • Code that makes it unsafe:
    • dotCMS/src/main/java/com/dotcms/rest/api/v1/page/ThemeSourceView.javagetVtls() replaced by getFiles(), constructor signature changed.
    • dotCMS/src/main/java/com/dotcms/rest/api/v1/page/VtlFileRefView.java — deleted entirely (N-1 still emits this type under the vtls key).
    • dotCMS/src/main/webapp/WEB-INF/openapi/openapi.yamlVtlFileRefView schema removed, ThemeSourceView.vtls property removed, FileRefView schema and ThemeSourceView.files property added.
  • Alternative (if possible): Follow the two-phase contract change (M-3 guidance): in Release N, keep vtls in the response (as a deprecated, read-only alias that delegates to the new files list filtered to .vtl extension) alongside the new files array. Remove the vtls alias in Release N+1 after N-1 is out of rotation. This gives both old and new consumers a consistent view across a rollback.

  • Category: H-8 — VTL Viewtool Contract Change
  • Risk Level: 🟠 HIGH
  • Why it's unsafe: DotTemplateTool (registered as $templatetool in toolbox.xml) now adds a new key (templatePathIsFileAsset) to the map it returns from themeLayout(). This is a vector (2b) change: the viewtool method signature is unchanged, but the returned object (a Map) gains a new key that PageLoader immediately consumed in the same PR via $dotTheme.templatePathIsFileAsset. After rollback to N-1, any theme whose VTL uses $dotTheme.templatePathIsFileAsset will silently resolve it to null/falsy and always fall into the #parse branch — serving the wrong (static-fallback) template.vtl instead of the file-asset one. While the key is currently consumed only in PageLoader's generated Velocity string, the pattern leaks into the rendered VTL context and could be adopted by customer theme templates.
  • Code that makes it unsafe:
    • dotCMS/src/main/java/com/dotcms/rendering/velocity/viewtools/DotTemplateTool.java lines added: themeMap.put("templatePathIsFileAsset", themeTemplateIsFileAsset);
    • dotCMS/src/main/java/com/dotcms/rendering/velocity/services/PageLoader.java — the generated Velocity fragment now branches on $dotTheme.templatePathIsFileAsset; on N-1, this key is absent, so the branch always takes the #parse (static-fallback) path, silently serving the wrong template.
  • Alternative (if possible): Apply the two-phase warm-the-contract pattern (H-8 / safer alternative): ship templatePathIsFileAsset on the returned map in Release N but do not consume it in the generated VTL until Release N+1, when N-1 already knows the key. Alternatively, guard the VTL with a null-check fallback: #if($dotTheme.templatePathIsFileAsset)…#else…#end still works safely on N-1 as long as the #else branch is correct for file-asset themes when the key is absent (which it is, since N-1 always uses #parse). In this specific case the regression on rollback is silent wrong-template rendering, not a hard error, which reduces blast radius — but it is still a behavioral regression for all file-asset themed pages.

@fmontes fmontes linked an issue Jun 22, 2026 that may be closed by this pull request
12 tasks
fileUri.substring(fileUri.lastIndexOf('/')) throws
StringIndexOutOfBoundsException when the URI has no slash. Use
lastIndexOf('/') + 1 so it returns the bare file name and safely
falls back to the full URI when no slash is present.

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

fmontes commented Jun 22, 2026

Copy link
Copy Markdown
Member Author

Review triage — addressed in 8e647ff

Went through the DeepSeek code review and the Claude rollback analysis. Summary of what was fixed vs. intentionally left.

✅ Fixed

  • CSSPreProcessServlet.java:389Content-Disposition filename (DeepSeek 🟡 Medium). fileUri.substring(fileUri.lastIndexOf('/')) throws StringIndexOutOfBoundsException if the URI has no slash. Changed to lastIndexOf('/') + 1, which returns the bare file name and safely falls back to the full URI when there's no slash. (In practice CSS asset URIs are always rooted, so this was latent — but cheap to harden.)

⏭️ Not changed — and why

DeepSeek 🟠 High / 🔴 Critical — all self-resolved in the same comment, verified against HEAD:

  • :129 (Critical) old DotLibSassCompiler ctor — confirmed it delegates this(host, uri, live, false, req). No issue.
  • :80 sourceMap null check — equalsIgnoreCase is null-safe; no functional bug.
  • :172 compiler source-map config — ctor passes the sourceMap flag; correct at the call site.
  • DotParse.java:173 flag cleanup without try-finally — dontShowThemeTemplateIcon is a one-shot flag; the exception window is negligible and the flag is re-set per render. Not worth a try-finally here.
  • SpeedyAssetServlet.java:114 Referer mode spoofing — getWithReferer only honors the referer when user.isBackendUser(), so a spoofed Referer from an anon user can't expose working content. Secure as written.

Claude rollback — H-8 templatePathIsFileAsset VTL key (🟠 High): the generated fragment is a guarded #if($dotTheme.templatePathIsFileAsset) #dotParse #else #parse #end. On rollback to N-1 the key resolves falsy and takes the #else #parse branch — which is exactly N-1's original behavior — so the generated VTL degrades safely. PageLoader also regenerates the velocity files itself, so N-1 rewrites the fragment regardless. No customer theme consumes the new key. Leaving as-is.

Claude rollback — M-3 vtlsfiles REST shape (🟡 Medium): this is the intentional, documented breaking change in the PR description (generic FileRefView replacing the VTL-specific view). Deliberate, not a regression.

Remaining DeepSeek 🟡 Medium items (389 rethrow, 1254/1460 file-ref handling, PageMode 147/164, FileRefView:18 lowercasing, CompilerOptions:156): cosmetic or behaviorally correct as-is; not blocking.

Copilot AI 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.

Pull request overview

This PR adds backend support for the a11y agent to (1) identify which theme/template assets participate in a page render and (2) optionally trace compiled CSS back to editable preprocessor sources via inline source maps, while also fixing page-mode consistency for backend-user asset requests.

Changes:

  • Expanded page render-sources output to return all theme files as a generic files[] list with (path, identifier, extension) via the new FileRefView.
  • Made sub-resource page-mode resolution referer-aware for backend users to avoid mixing working assets into ?mode=LIVE renders; updated theme template inclusion to be version-aware.
  • Added ?sourcemap=true support to emit inline Sass source maps (uncached), plus OpenAPI and integration test updates.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
dotCMS/src/main/webapp/WEB-INF/openapi/openapi.yaml Adds FileRefView schema and updates theme source schema from VTL-only to full file list.
dotCMS/src/main/webapp/WEB-INF/messages/Language.properties Adds i18n strings for the Accessibility Studio portlet UI.
dotCMS/src/main/java/com/dotmarketing/util/PageMode.java Adds getWithReferer() to resolve page mode for sub-resource requests using referer for backend users.
dotCMS/src/main/java/com/dotmarketing/servlets/SpeedyAssetServlet.java Switches asset serving mode resolution to PageMode.getWithReferer() and updates debug logging.
dotCMS/src/main/java/com/dotcms/rest/api/v1/page/VtlFileRefView.java Removes VTL-specific file-ref DTO in favor of a generic one.
dotCMS/src/main/java/com/dotcms/rest/api/v1/page/ThemeSourceView.java Replaces vtls with generic files list of FileRefView.
dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResourceHelper.java Builds full theme file list (all extensions) and reuses FileRefView for widget file refs.
dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageRenderSourcesView.java Updates schema description to reflect broader theme file coverage.
dotCMS/src/main/java/com/dotcms/rest/api/v1/page/FileRefView.java Introduces generic file reference DTO including extension metadata.
dotCMS/src/main/java/com/dotcms/rendering/velocity/viewtools/DotTemplateTool.java Emits logical theme template path plus a flag indicating whether it’s a file-asset theme template.
dotCMS/src/main/java/com/dotcms/rendering/velocity/services/PageLoader.java Uses #dotParse for file-asset theme templates (version-aware) and preserves #parse for static fallbacks.
dotCMS/src/main/java/com/dotcms/rendering/velocity/directive/DotParse.java Adds one-shot suppression flag to avoid injecting an edit-control icon for the theme shell include.
dotCMS/src/main/java/com/dotcms/csspreproc/DotLibSassCompiler.java Adds optional inline source-map compilation option passed into Dart Sass compiler.
dotCMS/src/main/java/com/dotcms/csspreproc/dartsass/CompilerOptions.java Adds sourceMap flag and command generation for inline source maps vs explicit --no-source-map.
dotCMS/src/main/java/com/dotcms/csspreproc/CSSPreProcessServlet.java Derives live from page mode (referer-aware) and adds ?sourcemap=true uncached serving path.
dotcms-integration/src/test/java/com/dotcms/rest/api/v1/page/PageRenderSourcesResourceTest.java Extends coverage to assert multiple theme file types are returned with correct extensions.
dotcms-integration/src/test/java/com/dotcms/csspreproc/CSSPreProcessServletTest.java Adds integration coverage asserting inline source map marker is present when requested.

Comment thread dotCMS/src/main/java/com/dotmarketing/servlets/SpeedyAssetServlet.java Outdated
Copilot review findings:

- CSSPreProcessServlet: ?sourcemap=true uses --embed-sources, inlining
  the raw SCSS (including @imported partials never served directly) into
  the response. Previously any caller able to fetch the compiled CSS
  could also retrieve the source by adding the flag. Require EDIT
  permission on the asset before embedding sources; return 403 otherwise.
  Edit perms are resolved for LIVE requests too, since the a11y agent
  scans pages in ?mode=LIVE.

- SpeedyAssetServlet: drop the raw Referer header from the debug log; it
  can carry sensitive query-string data and the resolved PageMode already
  captures what the log needs.

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

fmontes commented Jun 22, 2026

Copy link
Copy Markdown
Member Author

Copilot review — both findings addressed in ba5abdf

Both inline comments were valid and are now fixed.

CSSPreProcessServlet.java — source-map exposure (the important one)

Correct catch. ?sourcemap=true enables --embed-sources, which inlines the raw SCSS — including @imported partials that are never served on their own — into the response. As written, any caller who could fetch the compiled CSS (including anonymous users for a published stylesheet) could append the flag and pull back editable theme source.

Fixed by gating the source-map path behind EDIT permission on the asset; otherwise it returns 403. userHasEditPerms was only computed for !live requests, so for LIVE requests (the a11y agent scans pages in ?mode=LIVE) edit permission is now resolved explicitly before embedding sources.

SpeedyAssetServlet.java — Referer in debug log

Fair point. Dropped the raw Referer header from the Logger.debug line. The resolved PageMode already captures what the log needs for diagnostics, and the explicit ?mode= param is kept; the header (which can carry sensitive query-string data) is no longer logged.

Both verified with ./mvnw compile -pl :dotcms-core. No OpenAPI/annotation changes, so openapi.yaml is unaffected.

Comment thread dotCMS/src/main/java/com/dotcms/rendering/velocity/viewtools/DotTemplateTool.java Outdated
* The {@code extension} lets consumers filter by type (e.g. vtl, css, scss, sass, js)
* without the API having to whitelist which extensions a theme may contain.
*/
public class FileRefView {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Prefer records now that we are in Java 25

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done in 000a59bFileRefView is now a record. Regenerated openapi.yaml is byte-identical, so the REST contract is unchanged.

.map(f -> toFileRef(f, host))
.collect(Collectors.toList());

return new ThemeSourceView(theme.getIdentifier(), theme.getName(), folderPath, vtls);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A test would be nice here

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Tracked in #36269 (test for the toFileRef/widget file-ref path).

* @param request the current sub-resource request
* @return the resolved {@link PageMode}
*/
public static PageMode getWithReferer(final HttpServletRequest request) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add cobertura to PageModeTest

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done in d24909c — added getWithReferer coverage to PageModeTest: backend honors referer mode, anon/front-end ignore it (forced LIVE), explicit ?mode= wins, no-referer falls back to PREVIEW.

@fabrizzio-dotCMS fabrizzio-dotCMS left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Two notes on the theme-template #parse#dotParse switch. The change is good and actually improves content invalidation (it moves template.vtl onto the same version-aware file-asset velocity path that header.vtl/footer.vtl/html_head.vtl already use, and removes the old up-to-5s stale-binary pin). But it's a sensitive, high-blast-radius path, so flagging one concrete cross-host cache-key risk and one test gap inline.

themeTemplatePath = themeTemplate.getFileAsset().getPath();
// Logical, host-relative file-asset path (NOT the physical working-inode disk path) so the
// page render can include it with #dotParse and resolve live vs working from the PageMode.
themeTemplatePath = themePath + Template.THEME_TEMPLATE; // themePath already ends with "/"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cache-key scoping risk with the new host-relative templatePath 🟠

Going from themeTemplate.getFileAsset().getPath() (an absolute, host-independent path to the working binary on disk) to themePath + THEME_TEMPLATE is correct for version-aware live/working resolution via #dotParse, and it improves invalidation. But it interacts badly with the themeMap cache key.

The cache (DotTemplateTool.java:55 — 5s expireAfterWrite, no event invalidation) is keyed by theme folder identifier only ("themeMap" + themeFolder.getIdentifier(), line 336), while themePath is host-relative whenever themeFolder.getHostId().equals(hostId). And #dotParse resolves a relative path against params.currentHost (DotParse.resolveFileAsset:295) — the rendering page's host, not the theme's.

So for a theme shared across hosts (the //host branch right above exists precisely because that's supported):

  • A page on the theme's own host renders first → relative templatePath gets cached.
  • Within the 5s window, a page on a different host whose template points at this same theme reads the cached relative path → #dotParse resolves /application/themes/<x>/template.vtl against the wrong host → ResourceNotFound, or worse, a different theme that happens to sit at that path.

The old absolute disk path didn't have this failure mode for the include. Two clean fixes:

  1. Always store the host-qualified (//<themeHost>/...) path for file-asset themes so it stays host-independent like before (the theme host is already resolved in the else branch), or
  2. Add the rendering host to the cache key ("themeMap" + folderId + hostId).

I'd lean toward (1) — it keeps #dotParse resolution unambiguous regardless of which host renders.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch — fixed in 7f27ee8. The file-asset templatePath is now always host-qualified (//<themeHost>/..., your option 1), so #dotParse resolves it against the theme's own host regardless of the rendering host. $dotTheme.path stays host-relative so existing consumers are unaffected.

// (html_head/header/footer) inside the theme still emit their icons.
sb.append("#if($dotTheme.templatePathIsFileAsset)")
.append("#set($dontShowThemeTemplateIcon = true)")
.append("#dotParse(\"$dotTheme.templatePath\")")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

High-blast-radius behavior change with no direct render test 🟠

This swaps the theme-shell include from #parse (filesystem, always the working binary) to #dotParse (version-aware) for every page that uses a file-asset theme. Net effect: a LIVE render now uses the published template.vtl instead of the latest working copy — that's the intended fix, but it's a semantic change on the hottest render path and nothing in the PR exercises it end-to-end.

Worth an integration test that renders a file-asset-theme page:

  • EDIT mode: assert no stray data-dot-object='vtl-file' control is emitted for the shell (the one-shot dontShowThemeTemplateIcon suppression), while nested #dotParse includes (html_head/header/footer) still emit theirs.
  • LIVE vs WORKING: publish template.vtl, then edit the working copy, and assert LIVE renders the published version while PREVIEW/EDIT renders the working one.

Static-fallback themes correctly stay on #parse — good. See also the cross-host themeMap cache-key concern on DotTemplateTool line 369, which this include now depends on.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Tracked in #36269 — the full publish/edit/render IT (EDIT-icon suppression + LIVE-vs-WORKING + cross-host) is too heavy for this PR. The cache-key concern it depends on is fixed here in 7f27ee8.

The themeMap cache is keyed by theme folder identifier only, but the
file-asset template path was host-relative when the rendering host
matched the theme host. For a theme shared across hosts, a relative
path cached by one host's render would be resolved by #dotParse against
a different rendering host within the cache window, causing
ResourceNotFound or the wrong theme. Always store the host-qualified
("//<themeHost>/...") path for the file-asset template include so
#dotParse resolves it against the theme's own host regardless of which
host renders the page.

Reported by @fabrizzio-dotCMS in review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses review feedback from @fabrizzio-dotCMS:

- Rename CSSPreProcessServletTest -> CSSPreProcessServletIT to match the
  integration-test naming convention; update the MainSuite2b registration.
- Document the templatePathIsFileAsset map entry and the host-qualified
  file-asset template path on DotTemplateTool.setThemeData.
- Add PageModeTest coverage for the new PageMode.getWithReferer: backend
  user honors referer mode, anonymous and front-end users ignore it
  (forced LIVE), explicit ?mode= wins over referer, and no-referer-mode
  falls back to PREVIEW.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per review (@fabrizzio-dotCMS) — prefer records on Java 25. Updated the
record-accessor call sites (PageResourceHelper widget refs, the
PageRenderSourcesResourceTest assertions). Regenerated openapi.yaml is
byte-identical, so the REST contract is unchanged.

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

claude Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Pull Request Unsafe to Rollback!!!


  • Category: M-3 — REST / GraphQL / Headless API Contract Change
  • Risk Level: 🟡 MEDIUM
  • Why it's unsafe: The ThemeSourceView REST response shape changed in a breaking way. The vtls array (of type VtlFileRefView, with fields path + identifier) is removed and replaced with a files array (of type FileRefView, with fields path + identifier + extension). Any consumer that calls GET /api/v1/page/_render-sources and reads theme.vtls will get null on N, because N drops vtls entirely. After rollback N-1 returns vtls (not files), breaking consumers deployed against N's contract.
  • Code that makes it unsafe:
    • dotCMS/src/main/java/com/dotcms/rest/api/v1/page/ThemeSourceView.javagetVtls() replaced by getFiles(), constructor signature changed from List<VtlFileRefView> to List<FileRefView>.
    • dotCMS/src/main/java/com/dotcms/rest/api/v1/page/VtlFileRefView.java — deleted entirely (N-1 still emits this type under the vtls key).
    • dotCMS/src/main/webapp/WEB-INF/openapi/openapi.yamlVtlFileRefView schema removed, ThemeSourceView.vtls property removed, FileRefView schema and ThemeSourceView.files property added.
  • Alternative (if possible): Follow the two-phase contract change (M-3 guidance): in Release N, keep vtls in the response as a deprecated alias that delegates to the new files list filtered to .vtl extension, alongside the new files array. Remove the vtls alias in Release N+1 after N-1 is out of rotation.

  • Category: H-8 — VTL Viewtool Contract Change
  • Risk Level: 🟠 HIGH
  • Why it's unsafe: DotTemplateTool (registered as $templatetool in toolbox.xml) now adds a new key (templatePathIsFileAsset) to the map returned by themeLayout(). The new key is consumed immediately in PageLoader's generated Velocity fragment via $dotTheme.templatePathIsFileAsset. Additionally, the templatePath value changes from the physical working-inode disk path (themeTemplate.getFileAsset().getPath()) to a logical host-qualified path (//<themeHost>/application/themes/<x>/template.vtl). Both are Vector (2b) changes: the viewtool method signature is unchanged, but the returned map gains a new key and a changed value. On rollback to N-1, the generated Velocity in the cache (if still warm from N) contains the #if($dotTheme.templatePathIsFileAsset) branch; N-1's themeLayout() does not return that key, so the branch evaluates falsy and always takes the #else #parse path — serving the correct static-fallback behavior but potentially wrong for file-asset themes still cached by N.
  • Code that makes it unsafe:
    • dotCMS/src/main/java/com/dotcms/rendering/velocity/viewtools/DotTemplateTool.javathemeMap.put("templatePathIsFileAsset", themeTemplateIsFileAsset) added; themeTemplatePath changed from physical inode disk path to host-qualified logical path.
    • dotCMS/src/main/java/com/dotcms/rendering/velocity/services/PageLoader.java — generated VTL fragment now branches on $dotTheme.templatePathIsFileAsset; on N-1 this key is absent, so file-asset themes silently fall into the #else #parse (static-fallback disk) path. Because PageLoader regenerates the VTL file, N-1 will rewrite the fragment on first render without the branch, restoring N-1 behavior.
  • Note: The risk is mitigated in practice since PageLoader regenerates the Velocity fragment on deploy (so N-1 overwrites the N-generated fragment), and the #else branch is the correct N-1 behavior. However, the window between N rollout and N-1 Velocity file regeneration could briefly serve wrong content for file-asset-themed pages. The rollback comment thread acknowledges this and the PR author has confirmed it degrades safely. Risk is lower than the standard H-8 HIGH given these mitigations.

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

Labels

AI: Not Safe To Rollback Area : Backend PR changes Java/Maven backend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Add backend render-source metadata for a11y agent

3 participants