Skip to content

spike: isolate the Play runtime inside a shadow root - #35

Merged
k08200 merged 5 commits into
mainfrom
spike/play-shadow-runtime
Jul 23, 2026
Merged

spike: isolate the Play runtime inside a shadow root#35
k08200 merged 5 commits into
mainfrom
spike/play-shadow-runtime

Conversation

@k08200

@k08200 k08200 commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Spike for #30's shell/runtime separation (the shadow-root option).

The app's page and surface hosts, the primitive base styles, and the authored application stylesheet now live inside a shadow root on #uh-app. Shell chrome stays document-level; the application stylesheet is appended after the runtime bases inside the shadow, so it still wins the cascade the shell comments promise.

What this buys: the machine-owned trees and the shell cannot collide in either direction, and surface layering gets a defined stacking root owned by the runtime — the containment half of #30. The placement/backdrop/focus contract itself stays open as the spec half.

Verification: all 283 web tests green; against a live spock start, the instagram feed renders identically and the comments sheet opens contained inside the prototype frame (screenshots below). Four regressions found and fixed during the spike, all of the same family — authored styles that legitimately assume the document: (1) the stylesheet initially stayed document-level and never reached the shadowed tree; (2) :root/body/#uh-app selector heads matched nothing inside the shadow, so the design-token block silently died (found on the desktop frame) — they now retarget to :host; (3) frame-keyed rules (#uh-frame[data-frame=…]) cannot see across the boundary — the frame state is mirrored onto the host and those heads rewrite to :host(#uh-app[…]), preserving id-level specificity so mobile-first base rules do not win ties they used to lose; (4) comments before a selector shielded it from retargeting. Desktop now renders identically to pre-spike main; the retargeting is covered by unit tests.

Feed unchanged (no regression):
image

Comments sheet contained inside the prototype frame:
image

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 21 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ae64f7d1-bc82-42f1-a598-9da2d27daefb

📥 Commits

Reviewing files that changed from the base of the PR and between 4171866 and 0e16f65.

📒 Files selected for processing (2)
  • web/src/play/shell.ts
  • web/src/play/tests/shell.test.ts

Walkthrough

Play now mounts the application inside an open shadow root, injects runtime styles there, retargets authored stylesheet selectors, moves Play-specific CSS into runtime styles, and propagates selected frame state to the app host.

Changes

Play shadow-root runtime

Layer / File(s) Summary
Shadow shell and selector retargeting
web/src/play/shell.ts, web/src/play/tests/shell.test.ts
#uh-app now owns an open shadow root containing page and surface hosts; authored selectors are retargeted for shadow-root usage and covered by tests.
Runtime styles and mount placement
web/src/play/runtime.css, web/src/play/shell.css, web/src/play/mount.ts
Play layout, projection errors, interaction affordances, overlays, and focus styles are defined in runtime CSS, while authored styles mount inside the runtime shadow root.
Boot stylesheet application and frame state
web/src/play/main.ts, web/src/play/chrome.ts, web/src/play/tests/chrome.test.ts
Fetched application CSS is retargeted before insertion, and frame selection updates the app host dataset with matching test harness support.

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

Suggested reviewers: softmarshmallow

Sequence Diagram(s)

sequenceDiagram
  participant PlayBoot
  participant retargetApplicationStyles
  participant applicationStyle
  participant runtimeRoot
  participant appHost
  PlayBoot->>retargetApplicationStyles: transform fetched application CSS
  retargetApplicationStyles-->>PlayBoot: return retargeted CSS
  PlayBoot->>applicationStyle: assign transformed CSS
  PlayBoot->>runtimeRoot: mount application stylesheet
  appHost->>appHost: update data-frame on frame selection
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: isolating the Play runtime inside a shadow root.
Description check ✅ Passed The description is directly about the shadow-root Play runtime isolation and selector retargeting changes.
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
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch spike/play-shadow-runtime

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.

@k08200
k08200 force-pushed the spike/play-shadow-runtime branch from d89238d to 644a538 Compare July 23, 2026 11:40
k08200 added 3 commits July 23, 2026 21:11
The app's page and surface hosts, the primitive base styles, and the
authored application stylesheet now live inside a shadow root on
#uh-app (#30): shell chrome and machine-owned trees cannot
collide, and surface layering has a defined stacking root owned by the
runtime. Shell chrome styles stay document-level; the application
stylesheet is appended after the runtime bases inside the shadow so it
still wins the cascade. All 283 web tests stay green, and the
instagram comments sheet renders contained inside the prototype frame
against a live spock start.
Authored stylesheets legitimately use :root, html, body, and #uh-app;
inside the shadow those match nothing and the app's design tokens
silently die (found on the desktop frame). Selector heads are rewritten
to :host at injection, with class-name lookalikes (.post-body) proven
untouched.
…adow

Three follow-up holes found on the desktop frame and closed:
comments before a selector shielded it from retargeting (the token
:root block silently died), frame-keyed rules (#uh-frame[data-frame])
could not see across the boundary (the frame state is now mirrored
onto the host and those heads rewrite to :host(#uh-app[...])), and the
rewrites preserve id-level specificity so mobile-first base rules do
not win ties they used to lose. Desktop now renders identically to
pre-spike main; covered by three retargeting unit tests.
@k08200
k08200 force-pushed the spike/play-shadow-runtime branch from 644a538 to 3aed9bb Compare July 23, 2026 12:12
Comment thread web/src/play/shell.ts Outdated
* the app's design tokens and base rules silently die (#30 spike).
*/
export function retargetApplicationStyles(styleText: string): string {
// 주석은 셀렉터 앵커를 가리므로 런타임 사본에서는 제거한다.

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.

🇰🇷

@k08200
k08200 marked this pull request as ready for review July 23, 2026 13:08

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
web/src/play/tests/chrome.test.ts (1)

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

Exercise the app-host frame contract.

appHost is not returned and frameButtons is empty, so removing the production assignment would still pass. Expose appHost and assert dataset.frame for initial selection and a frame change.

🤖 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 `@web/src/play/tests/chrome.test.ts` at line 204, Update the test setup and
assertions around appHost and frameButtons to return appHost, then assert that
appHost.dataset.frame reflects the initially selected frame and updates after a
frame change. Ensure the test would fail if the production app-host frame
assignment were removed.
🤖 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 `@web/src/play/shell.ts`:
- Around line 230-250: Update retargetApplicationStyles in web/src/play/shell.ts
to collapse selectors combining document ancestry (:root, html, or body) or
`#uh-frame` with `#uh-app` into a single :host(`#uh-app`) selector, preserving any
attributes and remaining descendants. Extend regression coverage in
web/src/play/tests/shell.test.ts for these compound patterns, including body
`#uh-app` and :root `#uh-app`, and verify they do not retain `#uh-app` as a shadow
descendant.

---

Nitpick comments:
In `@web/src/play/tests/chrome.test.ts`:
- Line 204: Update the test setup and assertions around appHost and frameButtons
to return appHost, then assert that appHost.dataset.frame reflects the initially
selected frame and updates after a frame change. Ensure the test would fail if
the production app-host frame assignment were removed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fe9851f7-9faa-4be1-855a-4875da654c2f

📥 Commits

Reviewing files that changed from the base of the PR and between c1d420b and 4171866.

📒 Files selected for processing (8)
  • web/src/play/chrome.ts
  • web/src/play/main.ts
  • web/src/play/mount.ts
  • web/src/play/runtime.css
  • web/src/play/shell.css
  • web/src/play/shell.ts
  • web/src/play/tests/chrome.test.ts
  • web/src/play/tests/shell.test.ts
💤 Files with no reviewable changes (1)
  • web/src/play/shell.css

Comment thread web/src/play/shell.ts Outdated
@k08200
k08200 merged commit 1625da0 into main Jul 23, 2026
2 checks passed
@k08200
k08200 deleted the spike/play-shadow-runtime branch July 23, 2026 14:06
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.

2 participants