Skip to content

fix(rendering): stabilize Phase 4 parity and editor lifecycle - #18

Merged
andycamara merged 4 commits into
mainfrom
fix/render-v2-phase-4-review
Aug 3, 2026
Merged

andycamara merged 4 commits into
mainfrom
fix/render-v2-phase-4-review

Conversation

@andycamara

@andycamara andycamara commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • port the verified Render v2 Phase 4 review and editor-lifecycle corrections from aria-demo
  • stabilize guarded save, draft, publish, snapshot, and Cloudflare delivery behavior
  • preserve list and description-list semantics across import, Stage, public HTML, Node, and workerd
  • fix component detach/convert ownership for shared layout slots
  • include the selection overlay resizing, sizing, and toolbar visibility controls required for editor parity
  • strengthen direct-writer, push-guard, and strict type-safety ratchets

The resulting OSS source tree is byte-identical to the verified aria-demo correction checkpoint.

Verification

  • Full Vitest: 860 files passed, 1 skipped; 4,985 tests passed, 1 skipped
  • Rendering foundation: Node 40 passed plus 5 tracked expected failures; workerd 39 passed
  • Runtime stability workerd: 3 passed
  • Canvas parity: 18 passed
  • npm run check: 0 errors
  • Node and Cloudflare production builds passed
  • Bundle integrity and Cloudflare Free limits passed
  • Rendering boundary, writer boundary, parity-route, type-safety, and push-guard checks passed
  • Node and Cloudflare runtime startup smokes passed
  • Pack smoke passed: 2,522 files, 4.4 MB tarball, 19.1 MB unpacked
  • git diff --check passed

Release

No release, package publish, version bump, tag, or deployment is included. The release remains deferred until the full Render v2 implementation is complete.

Summary by CodeRabbit

  • New Features

    • Resize selected canvas elements with handles, keyboard controls, size previews, and accessibility announcements.
    • Show or hide the selection toolbar and sizing controls from Composer preferences; settings persist.
    • Improved support for unordered, ordered, and description lists during editing, import, and rendering.
  • Bug Fixes

    • Saves and publishes now respond faster, with background updates and safer retry handling.
    • Prevented duplicate history entries when retrying unchanged content.
    • Improved global stylesheet loading and caching.
  • Style

    • Refined selection overlays, color-picker hover feedback, and quick-picker controls.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@andycamara, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 13 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1e278dca-ccd8-4310-8692-e4417d1f47c4

📥 Commits

Reviewing files that changed from the base of the PR and between 82a64cc and be2bef0.

📒 Files selected for processing (21)
  • .github/workflows/publish-npm.yml
  • aria/actions/_shared.ts
  • aria/actions/library.ts
  • aria/actions/nodes.ts
  • aria/admin/features/Nodes/events/useNodeComponentHandlers.ts
  • aria/admin/features/Stage/components/CanvasOverlayLayer.vue
  • aria/admin/i18n/messages/composer/core.ts
  • aria/lib/blocks/htmlToNodes.ts
  • aria/lib/cloudflare/waitUntil.ts
  • aria/lib/storage/internal/domains/dslAssets.ts
  • aria/lib/storage/internal/domains/pageLifecycle.ts
  • aria/tests/actions/nodes-render-contract.test.ts
  • aria/tests/actions/save-resource-lifecycle.test.ts
  • aria/tests/core/useSavePublish.test.ts
  • aria/tests/features/nodes/nodeEventHandlers.extracted.test.ts
  • aria/tests/integration/global-css-route.test.ts
  • aria/tests/lib/cloudflare/waitUntil.test.ts
  • aria/tests/lib/htmlToNodes.test.ts
  • aria/tests/storage/cloudflare-storage.test.ts
  • aria/tests/storage/sqlite-storage.test.ts
  • src/pages/styles/global.css.ts
📝 Walkthrough

Walkthrough

The PR adds deferred revision delivery, persisted-artifact publishing, canonical render validation, canvas selection resizing, list and description-list semantics, editable-tree mutations, save retries, and expanded validation coverage.

Changes

Rendering and persistence

Layer / File(s) Summary
Revision delivery and publishing
aria/actions/_shared.ts, aria/actions/publishing.ts, aria/lib/content-sync/*, src/pages/styles/global.css.ts
Saves and publishing commit revisions and defer side effects when supported. Publishing and global CSS loading use persisted artifacts.
Render contracts and validation
aria/actions/nodes.ts, aria/lib/rendering/canonical/*, aria/scripts/*
Node writes use strict render validation and shared surface mapping. Writer-boundary checks inspect normalizer ordering.
Save retry and storage reconciliation
aria/admin/features/Core/composables/useSavePublish.ts, aria/lib/storage/internal/domains/*
Ambiguous saves retry once. Unchanged stale saves reconcile by content hash without duplicate history entries.

Canvas and editor

Layer / File(s) Summary
Canvas selection controls
aria/admin/features/Stage/*, aria/admin/features/Composer/*
Selection sizing and toolbar visibility persist locally and propagate through the stage. Resize interactions support pointer, keyboard, preview, cancellation, and persistence.
Editable tree mutations
aria/admin/features/Nodes/events/useNodeComponentHandlers.ts
Detach and replacement operations use registry-backed editable trees and cloned undo/redo snapshots.

List rendering

Layer / File(s) Summary
List and description-list semantics
aria/lib/blocks/*, aria/admin/features/Inspector/*, aria/tests/lib/*
Imported lists preserve ul, ol, dl, dt, and dd semantics. Unordered lists default to no markers and hug sizing. Rendering selectors follow canonical tags and link context.

Supporting validation

Layer / File(s) Summary
Workflow and regression coverage
.github/workflows/*, aria/tests/*, vitest.*.config.ts
CI runs dependency installation and rendering-writer validation. Tests cover publishing, deferred delivery, render styles, save retries, lists, canvas controls, and storage reconciliation.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • ariabuilder/aria#15: This PR reuses its canonical render-contract normalization, surface mapping, writer checks, and persistence validation.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description includes a detailed summary and verification results but omits the required Checklist, Architecture, Security Notes, and Screenshots sections. Add the missing template sections and document checklist results, architecture compliance, security impact, and screenshots or state why screenshots are not applicable.
Docstring Coverage ⚠️ Warning Docstring coverage is 10.59% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the PR's main rendering parity and editor lifecycle corrections.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/render-v2-phase-4-review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
aria/lib/storage/internal/domains/pageLifecycle.ts (1)

394-415: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

The unchanged-content early return omits linked-layout media sync.

This branch runs when preparedLinkedLayout exists and is unchanged. It calls only syncPageUsage. The reconciliation branch at Lines 350-363 also calls syncMediaUsageBestEffort("layout", ...) for the same unchanged-layout state. Make the two branches perform the same post-commit work.

🐛 Proposed fix
           await settlePostCommit([
             () => context.syncPageUsage(id, normalizedDSL),
+            ...(preparedLinkedLayout
+              ? [
+                  () =>
+                    context.syncMediaUsageBestEffort(
+                      "layout",
+                      preparedLinkedLayout.id,
+                      preparedLinkedLayout.dsl,
+                    ),
+                ]
+              : []),
           ]);
🤖 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 `@aria/lib/storage/internal/domains/pageLifecycle.ts` around lines 394 - 415,
The unchanged-content early-return branch in the page lifecycle flow omits
linked-layout media synchronization. Update the settlePostCommit callbacks in
the branch using shouldSkipIfContentUnchanged and preparedLinkedLayout so it
also invokes syncMediaUsageBestEffort("layout", ...) with the same arguments as
the reconciliation branch, while preserving the existing syncPageUsage call and
return behavior.
🧹 Nitpick comments (10)
aria/tests/features/nodes/nodeEventHandlers.extracted.test.ts (2)

1324-1392: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the unresolved-node branch.

The new tests cover missing content and fetch failure. No test calls handleDetachComponent or handleReplaceBlockWithComponent with a nodeId that is absent from the editable tree. That branch returns early from resolveEditableComponentTree and emits the error toast. Add a case that asserts no mutation, no history entry, and the expected message.

🤖 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 `@aria/tests/features/nodes/nodeEventHandlers.extracted.test.ts` around lines
1324 - 1392, Add test coverage for the unresolved-node branch by invoking
handleDetachComponent (or handleReplaceBlockWithComponent) with a nodeId absent
from the editable tree. Assert the tree remains unchanged,
executeNodeEventOperation is not called, and the error toast receives the
expected unresolved-node message.

1497-1502: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert that the component fields are removed after undo.

toMatchObject checks only the listed keys. If a future change restored the node with a stale componentRef or reference, this assertion would still pass and the block would still render as a component instance. Add explicit checks for the removed fields.

♻️ Proposed stronger undo assertion
     await historyCallbacks?.undo();
-    expect(currentLayout.value?.slots?.[0]?.defaultContent?.[0]).toMatchObject({
-      id: "header-heading",
-      type: "Heading",
-    });
+    const restored = currentLayout.value?.slots?.[0]?.defaultContent?.[0];
+    expect(restored).toMatchObject({
+      id: "header-heading",
+      type: "Heading",
+    });
+    expect(restored?.componentRef).toBeUndefined();
+    expect(restored?.reference).toBeUndefined();
🤖 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 `@aria/tests/features/nodes/nodeEventHandlers.extracted.test.ts` around lines
1497 - 1502, Strengthen the undo assertion after historyCallbacks?.undo() by
explicitly verifying that the restored header-heading node does not contain the
removed component fields, including componentRef and reference. Keep the
existing id and type checks while asserting those fields are absent rather than
relying solely on toMatchObject.
aria/admin/features/Stage/components/CanvasOverlayLayer.vue (2)

1006-1008: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Repeated identical announcements are not read out.

resizeAnnouncement keeps its previous value after a commit. If the next resize produces the same size text, the value does not change and assistive technology announces nothing. Clear the value before you set the new text, or append a changing suffix.

♿ Proposed fix in `commitResizeSession`
+  resizeAnnouncement.value = "";
   resizeAnnouncement.value = `Resized to ${formatCanvasSelectionSize(finalSize)} pixels`;
🤖 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 `@aria/admin/features/Stage/components/CanvasOverlayLayer.vue` around lines
1006 - 1008, Update commitResizeSession so resizeAnnouncement is cleared before
assigning the newly computed resize message, ensuring identical consecutive
resize announcements are exposed as value changes while preserving the existing
announcement text.

488-521: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

The 2px dead zone freezes the preview near the start point.

The handler computes nextSize first, then returns when both movement components are below 2px. After the drag passes the threshold, a move back toward the start point re-enters the dead zone and stops updating currentSize. The committed size then does not match the pointer position.

Evaluate the threshold once, at drag start, instead of on every move.

♻️ Proposed change
-  const movementX = Math.abs(
-    event.clientX - (session.startClientX ?? event.clientX),
-  );
-  const movementY = Math.abs(
-    event.clientY - (session.startClientY ?? event.clientY),
-  );
-  if (movementX < 2 && movementY < 2) {
-    return;
-  }
-  session.hasChanged = true;
+  if (!session.hasChanged) {
+    const movementX = Math.abs(
+      event.clientX - (session.startClientX ?? event.clientX),
+    );
+    const movementY = Math.abs(
+      event.clientY - (session.startClientY ?? event.clientY),
+    );
+    if (movementX < 2 && movementY < 2) {
+      return;
+    }
+    session.hasChanged = true;
+  }
🤖 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 `@aria/admin/features/Stage/components/CanvasOverlayLayer.vue` around lines 488
- 521, Update handleResizePointerMove so the 2px movement threshold is evaluated
only once before marking the resize session as changed, such as by guarding the
initial movement in the session state. After the threshold is crossed, continue
updating currentSize and queueResizePreview for every subsequent pointer move,
including movement back toward the start point.
aria/lib/cloudflare/waitUntil.ts (1)

23-29: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Handle a throwing waitUntil call.

context.waitUntil throws when the Cloudflare execution context is already finalized. The exception then propagates into the caller, and the task is neither deferred nor awaited. Return false on failure so the caller awaits the same work.

♻️ Proposed refactor
   const context = locals?.cfContext;
   if (!isWaitUntilContext(context)) {
     return false;
   }
 
-  context.waitUntil(task);
-  return true;
+  try {
+    context.waitUntil(task);
+    return true;
+  } catch {
+    return false;
+  }
 }
🤖 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 `@aria/lib/cloudflare/waitUntil.ts` around lines 23 - 29, Update the waitUntil
handling in the visible context-checking function to catch exceptions thrown by
context.waitUntil(task). Return true only when scheduling succeeds; on failure,
return false so the caller awaits the task, while preserving the existing
invalid-context behavior.
aria/admin/features/Stage/composables/useIframeSetup.ts (1)

111-113: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Share one global stylesheet URL resolver between the iframe bootstrap and the live head sync. Both sites now implement the same contract: skip the link when inline globalCSS exists, otherwise build /styles/global.css and append ?v=<encoded cacheKey> when a cache key is available. The route in src/pages/styles/global.css.ts derives its Cache-Control value from that v parameter, so any drift between the two producers changes caching behavior.

  • aria/admin/features/Stage/composables/useIframeSetup.ts#L111-L113: export resolveStageGlobalCssHref as the single source of truth for this URL.
  • aria/admin/features/Stage/components/StageFrame.vue#L680-L684: import that helper in syncGlobalCssLink and remove the inline cacheKey and href computation.
🤖 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 `@aria/admin/features/Stage/composables/useIframeSetup.ts` around lines 111 -
113, The global stylesheet URL logic is duplicated between the iframe setup and
live head sync. In
aria/admin/features/Stage/composables/useIframeSetup.ts:111-113, export
resolveStageGlobalCssHref as the shared resolver; in
aria/admin/features/Stage/components/StageFrame.vue:680-684, import and use it
within syncGlobalCssLink, removing the local cacheKey and href computation.
aria/actions/publishing.ts (2)

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

Mark skipCSSRegeneration as ignored in the publish input contract.

The single-page publish path now discards skipCSSRegeneration, but PublishPageInputSchema still accepts it. Batch publish still honors the same field name. A caller can reasonably expect the flag to control CSS regeneration in both paths. Document the field as deprecated on the schema, or remove it and keep the batch-only flag distinct.

🤖 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 `@aria/actions/publishing.ts` at line 194, Update PublishPageInputSchema to
reflect that skipCSSRegeneration is ignored by the single-page publish path:
either mark the field deprecated with appropriate schema documentation while
preserving batch compatibility, or remove it from the single-page schema and use
a distinct batch-only field name. Keep the existing batch publish behavior
intact.

205-205: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

htmlSize is now always zero in the publish response.

Publishing no longer renders HTML, so htmlSize is a constant 0 in both the scheduled and published responses. useSavePublish.ts still logs it as ${(publishedData.htmlSize / 1024).toFixed(2)}KB, which always prints 0.00KB. Remove the field from the response payload, or replace it with the stored snapshot size.

🤖 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 `@aria/actions/publishing.ts` at line 205, Update the publishing response
construction around htmlSize so it no longer returns a misleading constant zero;
remove htmlSize from both scheduled and published payloads, or populate it from
the stored snapshot size. Ensure useSavePublish.ts no longer expects or logs the
obsolete zero-valued field.
aria/tests/core/useSavePublish.test.ts (1)

750-765: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the returned-error retry branch.

Both new tests reject the promise, which exercises only the catch path of runAmbiguousSaveRetry. The branch at useSavePublish.ts line 311 handles a resolved result whose error is ambiguous, for example { error: { code: "SERVICE_UNAVAILABLE" } } or { error: { status: 503 } }. Add one test that returns such a result and asserts a second attempt.

🤖 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 `@aria/tests/core/useSavePublish.test.ts` around lines 750 - 765, Add a test
alongside the existing ambiguous component save retry test that makes the first
save attempt resolve with an ambiguous error object, such as SERVICE_UNAVAILABLE
or status 503, instead of rejecting. Assert that runAmbiguousSaveRetry performs
a second save attempt and preserves the same payload, covering the
resolved-error branch in useSavePublish.
aria/actions/library.ts (1)

217-225: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Deduplicate the packId fallback logic.

Line 218 and Line 848 both compute ARIA_FREE_BUTTON_COMPONENT.packId ?? "aria-free-pack" independently. If a future change updates one occurrence and not the other, resolveStarterButtonPackVersion can resolve against a different pack manifest entry than the one recorded on the installed component. Extract a single helper or constant for the starter button pack ID and reuse it in both places.

♻️ Proposed refactor
+const STARTER_BUTTON_PACK_ID =
+  ARIA_FREE_BUTTON_COMPONENT.packId ?? "aria-free-pack";
+
 export function resolveStarterButtonPackVersion(): string {
-  const packId = ARIA_FREE_BUTTON_COMPONENT.packId ?? "aria-free-pack";
   return (
-    SEEDED_REGISTRY_MANIFEST.packs.find((pack) => pack.id === packId)
+    SEEDED_REGISTRY_MANIFEST.packs.find(
+      (pack) => pack.id === STARTER_BUTTON_PACK_ID,
+    )
       ?.version ??
     ARIA_FREE_BUTTON_COMPONENT.version ??
     "1.0.0"
   );
 }
       const normalized = {
         ...ARIA_FREE_BUTTON_COMPONENT,
         source: "aria" as const,
-        packId: ARIA_FREE_BUTTON_COMPONENT.packId ?? "aria-free-pack",
+        packId: STARTER_BUTTON_PACK_ID,
         packVersion: resolveStarterButtonPackVersion(),

Also applies to: 845-853

🤖 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 `@aria/actions/library.ts` around lines 217 - 225, Deduplicate the starter pack
ID fallback used by resolveStarterButtonPackVersion and the installed-component
logic around the second occurrence. Extract a shared helper or constant for
ARIA_FREE_BUTTON_COMPONENT.packId ?? "aria-free-pack", then reuse it in both
locations so manifest lookup and component recording always use the same pack
ID.
🤖 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 @.github/workflows/publish-npm.yml:
- Line 38: Add npm run check:type-safety to the publish workflow before the
existing test/check commands, alongside check:push-guards. Keep the command
ordering consistent with ci.yml so type-safety validation runs before tests.

In `@aria/actions/_shared.ts`:
- Around line 191-196: Update all three save paths that call
touchContentRevision so failures after the resource write commits are handled as
best-effort: catch and log revision errors without propagating them from
saveResource or reporting the already-persisted save as failed. Preserve the
successful revision logging and include appropriate diagnostics in the failure
log.

In `@aria/actions/nodes.ts`:
- Around line 77-127: Update withGeneratedNodeId to recursively process each
node’s children, generating an ID for every descendant while preserving existing
valid IDs and non-node values. Ensure parsePreflightedBuilderNode with
generateMissingId: true can successfully validate complete BuilderNode subtrees
through BuilderNodeSchema.safeParse.

In `@aria/admin/features/Core/composables/useSavePublish.ts`:
- Around line 305-320: The retry flow in runAmbiguousSaveRetry must reconcile
the expected version before re-sending ambiguous saves. Ensure retries of
actions.saveComponent and actions.updateItem for layouts refresh or otherwise
synchronize their stale expected-version state after a failed-to-fetch response,
while preserving the existing two-attempt and unknown-status behavior.

In `@aria/admin/features/Nodes/events/useNodeComponentHandlers.ts`:
- Around line 167-172: Update the missing-tree branch in handleDetachComponent
when resolveEditableComponentTree(nodeId) returns null to display a distinct
“node not found” error message instead of “Not a component instance”; leave the
component-instance handling unchanged.

In `@aria/admin/features/Stage/components/CanvasOverlayLayer.vue`:
- Around line 260-262: Update getResizeHandleLabel to use the component’s
existing useStudioI18n t() helper with a new localized message key, preserving
the handle-specific label. Also replace the hardcoded resize announcement around
the existing line-406 logic with t() and add the corresponding message key(s),
following the established composer.toolbar.* or common.* localization pattern.

In `@aria/lib/blocks/htmlToNodes.ts`:
- Around line 569-574: Update IMPORTED_LIST_STYLE_UTILITY_PATTERN to accept
list-circle and list-square, and update IMPORTED_PADDING_UTILITY_PATTERN to
accept py, pt, and pb utilities alongside the existing variants. Add regression
coverage for classes such as list-square py-6, verifying the fallback preserves
the list marker and vertical padding instead of applying listStyleType: none or
padding: 0.

In `@aria/lib/storage/internal/domains/dslAssets.ts`:
- Around line 158-180: Revalidate the version before unchanged-content early
returns in both dslAssets.ts sites: the layout path around lines 158-180 must
call context.resolveLayoutVersionState(id), and the component path around lines
456-486 must call context.resolveComponentVersionState(id). Compare the
re-resolved currentVersion with the expected version and raise
VersionConflictError if it changed; only then return the latest version after
syncing usage.

In `@aria/tests/storage/cloudflare-storage.test.ts`:
- Around line 468-483: Add an explicit expect(atomicLayoutVersion).toBeDefined()
immediately after the atomicLayoutVersion assignment in
aria/tests/storage/cloudflare-storage.test.ts lines 468-483 and
aria/tests/storage/sqlite-storage.test.ts lines 685-700, so the subsequent
layout-version comparisons cannot pass when getLayoutDSL returns null.

In `@src/pages/styles/global.css.ts`:
- Around line 56-60: Update the 304 response branch before the main
Cache-Control logic to include the same Cache-Control value derived from
isPreviewRequest and hasMatchingVersion, alongside ETag. Ensure matching preview
requests receive no-store, must-revalidate and requests without a matching v
receive no-cache, must-revalidate.

---

Outside diff comments:
In `@aria/lib/storage/internal/domains/pageLifecycle.ts`:
- Around line 394-415: The unchanged-content early-return branch in the page
lifecycle flow omits linked-layout media synchronization. Update the
settlePostCommit callbacks in the branch using shouldSkipIfContentUnchanged and
preparedLinkedLayout so it also invokes syncMediaUsageBestEffort("layout", ...)
with the same arguments as the reconciliation branch, while preserving the
existing syncPageUsage call and return behavior.

---

Nitpick comments:
In `@aria/actions/library.ts`:
- Around line 217-225: Deduplicate the starter pack ID fallback used by
resolveStarterButtonPackVersion and the installed-component logic around the
second occurrence. Extract a shared helper or constant for
ARIA_FREE_BUTTON_COMPONENT.packId ?? "aria-free-pack", then reuse it in both
locations so manifest lookup and component recording always use the same pack
ID.

In `@aria/actions/publishing.ts`:
- Line 194: Update PublishPageInputSchema to reflect that skipCSSRegeneration is
ignored by the single-page publish path: either mark the field deprecated with
appropriate schema documentation while preserving batch compatibility, or remove
it from the single-page schema and use a distinct batch-only field name. Keep
the existing batch publish behavior intact.
- Line 205: Update the publishing response construction around htmlSize so it no
longer returns a misleading constant zero; remove htmlSize from both scheduled
and published payloads, or populate it from the stored snapshot size. Ensure
useSavePublish.ts no longer expects or logs the obsolete zero-valued field.

In `@aria/admin/features/Stage/components/CanvasOverlayLayer.vue`:
- Around line 1006-1008: Update commitResizeSession so resizeAnnouncement is
cleared before assigning the newly computed resize message, ensuring identical
consecutive resize announcements are exposed as value changes while preserving
the existing announcement text.
- Around line 488-521: Update handleResizePointerMove so the 2px movement
threshold is evaluated only once before marking the resize session as changed,
such as by guarding the initial movement in the session state. After the
threshold is crossed, continue updating currentSize and queueResizePreview for
every subsequent pointer move, including movement back toward the start point.

In `@aria/admin/features/Stage/composables/useIframeSetup.ts`:
- Around line 111-113: The global stylesheet URL logic is duplicated between the
iframe setup and live head sync. In
aria/admin/features/Stage/composables/useIframeSetup.ts:111-113, export
resolveStageGlobalCssHref as the shared resolver; in
aria/admin/features/Stage/components/StageFrame.vue:680-684, import and use it
within syncGlobalCssLink, removing the local cacheKey and href computation.

In `@aria/lib/cloudflare/waitUntil.ts`:
- Around line 23-29: Update the waitUntil handling in the visible
context-checking function to catch exceptions thrown by context.waitUntil(task).
Return true only when scheduling succeeds; on failure, return false so the
caller awaits the task, while preserving the existing invalid-context behavior.

In `@aria/tests/core/useSavePublish.test.ts`:
- Around line 750-765: Add a test alongside the existing ambiguous component
save retry test that makes the first save attempt resolve with an ambiguous
error object, such as SERVICE_UNAVAILABLE or status 503, instead of rejecting.
Assert that runAmbiguousSaveRetry performs a second save attempt and preserves
the same payload, covering the resolved-error branch in useSavePublish.

In `@aria/tests/features/nodes/nodeEventHandlers.extracted.test.ts`:
- Around line 1324-1392: Add test coverage for the unresolved-node branch by
invoking handleDetachComponent (or handleReplaceBlockWithComponent) with a
nodeId absent from the editable tree. Assert the tree remains unchanged,
executeNodeEventOperation is not called, and the error toast receives the
expected unresolved-node message.
- Around line 1497-1502: Strengthen the undo assertion after
historyCallbacks?.undo() by explicitly verifying that the restored
header-heading node does not contain the removed component fields, including
componentRef and reference. Keep the existing id and type checks while asserting
those fields are absent rather than relying solely on toMatchObject.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d57d05c-e4f8-43a1-9503-2468b925cf72

📥 Commits

Reviewing files that changed from the base of the PR and between b87e5dc and 82a64cc.

📒 Files selected for processing (92)
  • .github/workflows/ci.yml
  • .github/workflows/publish-npm.yml
  • aria/actions/_shared.ts
  • aria/actions/library.ts
  • aria/actions/nodes.ts
  • aria/actions/publishing.ts
  • aria/actions/save.ts
  • aria/actions/styles/renderStyles.ts
  • aria/admin/components/ui/color-picker/ColorField.vue
  • aria/admin/composables/useCanvasOverlays.ts
  • aria/admin/features/Agent/client/composables/useAgentClientTools.ts
  • aria/admin/features/Agent/lib/tools/content/nodeWriteTools.ts
  • aria/admin/features/Composer/components/ComposerCanvasOptionsMenu.vue
  • aria/admin/features/Composer/components/ComposerSidebar.vue
  • aria/admin/features/Composer/components/ComposerStage.vue
  • aria/admin/features/Core/components/AppCanvasShell.vue
  • aria/admin/features/Core/composables/useSavePublish.ts
  • aria/admin/features/Core/types/shell.ts
  • aria/admin/features/Inspector/inputs/ListProperty.vue
  • aria/admin/features/Inspector/schemas/list.schema.ts
  • aria/admin/features/Nodes/events/useNodeComponentHandlers.ts
  • aria/admin/features/Stage/StageApp.vue
  • aria/admin/features/Stage/components/CanvasOverlayLayer.vue
  • aria/admin/features/Stage/components/CmsQuickPicker.vue
  • aria/admin/features/Stage/components/MotionQuickPicker.vue
  • aria/admin/features/Stage/components/SelectionToolbarCmsControls.vue
  • aria/admin/features/Stage/components/SelectionToolbarMotionControl.vue
  • aria/admin/features/Stage/components/StageCanvasRuntime.vue
  • aria/admin/features/Stage/components/StageFrame.vue
  • aria/admin/features/Stage/components/StageSidebar.vue
  • aria/admin/features/Stage/composables/useIframeSetup.ts
  • aria/admin/features/Stage/composables/useStageAppBindings.ts
  • aria/admin/features/Stage/composables/useStageDisplayState.ts
  • aria/admin/features/Stage/composables/useStageShellBindings.ts
  • aria/admin/features/Stage/composables/useStageWorkspaceBindings.ts
  • aria/admin/features/Stage/types/index.ts
  • aria/admin/features/Stage/utils/canvasRenderAttributes.ts
  • aria/admin/features/Stage/utils/canvasSelectionResize.ts
  • aria/admin/features/Studio/pages/PageDetailView.vue
  • aria/admin/features/Studio/pages/composables/usePageActions.ts
  • aria/admin/i18n/messages/composer/core.ts
  • aria/admin/lib/actions/actionResult.ts
  • aria/admin/types/app.ts
  • aria/lib/blocks/htmlToNodes.ts
  • aria/lib/blocks/listNodes.ts
  • aria/lib/blocks/nodesToHtml.ts
  • aria/lib/blocks/renderSemantics.ts
  • aria/lib/cloudflare/waitUntil.ts
  • aria/lib/content-sync/mutations.ts
  • aria/lib/migrations/propMigrations.ts
  • aria/lib/rendering/canonical/contract.ts
  • aria/lib/rendering/canonical/index.ts
  • aria/lib/schemas/nodes.ts
  • aria/lib/storage/internal/domains/dslAssets.ts
  • aria/lib/storage/internal/domains/pageLifecycle.ts
  • aria/lib/storage/internal/domains/surfaceNormalization.ts
  • aria/scripts/check-rendering-writer-boundaries.ts
  • aria/scripts/project-command.ts
  • aria/tests/actions/library-pack-version.test.ts
  • aria/tests/actions/nodes-render-contract.test.ts
  • aria/tests/actions/render-styles-action.test.ts
  • aria/tests/actions/save-action-nonce.test.ts
  • aria/tests/actions/save-resource-lifecycle.test.ts
  • aria/tests/cache/public-page-cache.test.ts
  • aria/tests/composables/actionResult.test.ts
  • aria/tests/composables/useCanvasOverlays.test.ts
  • aria/tests/composables/useIframeSetup.test.ts
  • aria/tests/core/useSavePublish.test.ts
  • aria/tests/features/composer/ComposerCanvasOptionsMenu.test.ts
  • aria/tests/features/nodes/nodeEventHandlers.extracted.test.ts
  • aria/tests/features/stage/canvasRenderAttributes.test.ts
  • aria/tests/features/stage/canvasSelectionResize.test.ts
  • aria/tests/features/stage/stageCanvasRuntimeLoading.test.ts
  • aria/tests/features/stage/useStageDisplayState.test.ts
  • aria/tests/fixtures/renderingDescriptionListUtilityPaste.ts
  • aria/tests/inputs/ListProperty.test.ts
  • aria/tests/integration/global-css-route.test.ts
  • aria/tests/integration/publishing-snapshot-injection.test.ts
  • aria/tests/lib/descriptionListPasteParity.test.ts
  • aria/tests/lib/htmlToNodes.test.ts
  • aria/tests/lib/listNodes.test.ts
  • aria/tests/lib/nodesToHtml.test.ts
  • aria/tests/mocks/astro-actions.ts
  • aria/tests/rendering-foundation/description-list-semantics.parity.test.ts
  • aria/tests/rendering-foundation/normalization.parity.test.ts
  • aria/tests/scripts/rendering-writer-boundaries.test.ts
  • aria/tests/storage/cloudflare-storage.test.ts
  • aria/tests/storage/sqlite-storage.test.ts
  • scripts/check-repository-push-guards.mjs
  • src/pages/styles/global.css.ts
  • vitest.rendering-foundation.node.config.ts
  • vitest.rendering-foundation.workerd.config.ts

Comment thread .github/workflows/publish-npm.yml
Comment thread aria/actions/_shared.ts Outdated
Comment thread aria/actions/nodes.ts
Comment thread aria/admin/features/Core/composables/useSavePublish.ts
Comment thread aria/admin/features/Nodes/events/useNodeComponentHandlers.ts
Comment thread aria/admin/features/Stage/components/CanvasOverlayLayer.vue
Comment thread aria/lib/blocks/htmlToNodes.ts
Comment thread aria/lib/storage/internal/domains/dslAssets.ts
Comment thread aria/tests/storage/cloudflare-storage.test.ts
Comment thread src/pages/styles/global.css.ts Outdated
@andycamara
andycamara merged commit 3367794 into main Aug 3, 2026
4 checks passed
@andycamara
andycamara deleted the fix/render-v2-phase-4-review branch August 3, 2026 07:13
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.

1 participant