Skip to content

feat(panels): subject-keyed panels (render-all, predicate-gated) - #94

Merged
kibertoad merged 5 commits into
mainfrom
claude/triaged-subject-keyed-initiative-lb0q1p
Jul 19, 2026
Merged

feat(panels): subject-keyed panels (render-all, predicate-gated)#94
kibertoad merged 5 commits into
mainfrom
claude/triaged-subject-keyed-initiative-lb0q1p

Conversation

@kibertoad

@kibertoad kibertoad commented Jul 19, 2026

Copy link
Copy Markdown
Owner

What & why

Implements the counter-proposal from the cat-factory slice-4 triage (#93, docs/subject-keyed-zones-triage.md): a render-all, subject-keyed, open-contribution panel surface — the sibling of the pick-one component-pairing helpers. A named region whose module-contributed panels are selected at render time by a runtime subject, gated by per-panel when(subject) predicates, ordered, and rendered all-matching. Any module (first-party or a consumer's) contributes through the existing module.slots path.

Landed the way the triage directed — engine-first, both bindings in the same train, under the name panels (the triage rejects a third "zones" meaning with prejudice). The three open maintainer decisions all resolved to the recommended option: name panels; React host in the same train; duplicate-id throw by default with a first-wins/last-wins escape hatch.

Engine (@modular-frontend/core)

  • PanelEntry<TSubject>, PanelGroupHandle<TSubject>, definePanelGroup(slotKey)
  • resolvePanels(entries, subject, opts?) — pure resolver: null subject → empty; when(subject) filter; stable order sort (ties keep contribution order); duplicate-id throw, mirroring resolveComponentRegistry
  • Re-exported by @modular-react/core (export *) and @modular-vue/core

Vue (@modular-vue/vue, re-exported from @modular-vue/core)

  • usePanels (computed over the slots source + subject; prefers the reactive slots source), <PanelsOutlet> (render-all; subject injected as a prop and via provide; per-panel ModuleErrorBoundary; #empty / #wrap slots), usePanelSubject

React (@modular-react/react)

  • usePanels, <PanelsOutlet>, usePanelSubject / PanelSubjectContext — the same surface with useMemo + context

No descriptor change, no new package, no registry plugin, no /testing subpath, no Nuxt manifest threading — all subsumed by the module → slot path, per the triage. Also widens the Vue family peer ranges (incl. compositions and testing) to admit @modular-frontend/core@^0.3.0 (the triage's Gap-D housekeeping).

How it was verified

  • New tests: engine panels.test.ts + panels.test-d.ts; Vue panels.test.ts (mounts <PanelsOutlet> — ordering, subject injection, #empty/#wrap, error-boundary containment, usePanelSubject, reactive re-resolution); React panels.test.tsx (parallel coverage).
  • pnpm turbo run test — 79/79 tasks pass; pnpm turbo run typecheck — 152/152 pass.
  • pnpm lint:fix (oxfmt --write . + oxlint --fix .) run over the repo; the new files produce no lint warnings.

Checklist

  • pnpm lint passes (typecheck + oxfmt + oxlint).
  • Tests cover the change and pass (pnpm test).
  • Docs / READMEs / tracker updated in the same PR: new docs/subject-panels.md; compositions README "pick by problem shape" table extended 3 → 4 primitives; cross-links from reactive-slots-vue.md and remote-capability-manifests.md; triage doc marked resolved.
  • Vue impact stated. The framework-neutral logic lands in @modular-frontend/core, and both bindings ship their hosts in this same PR (Vue @modular-vue/vue + React @modular-react/react), keeping the engine-first, both-bindings rule intact. Angular gets injectPanels when its gate opens — the engine part is already done for it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QULkEZq9fop9povfXdMecL


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added subject-keyed panels that render all matching contributions based on a runtime subject, optional predicates, and deterministic ordering.
    • Added panel support for React and Vue, including outlet components, subject access helpers, empty states, remount controls, and isolated error handling.
    • Added customizable labels for module error boundaries.
    • Added a React Router inspector-panels example demonstrating dynamic panel selection.
  • Documentation

    • Added comprehensive subject-keyed panels guidance and updated related guides and examples.
  • Tests

    • Added engine, React, Vue, and end-to-end coverage for filtering, ordering, reactivity, errors, duplicates, and remounting.

…icate-gated)

Implements the counter-proposal from the cat-factory slice-4 triage (#93):
a render-all, subject-keyed, open-contribution panel surface — the sibling
of the pick-one component-pairing helpers — landed engine-first with both
bindings in the same train, under the name "panels" (not "zones").

Engine (@modular-frontend/core):
- PanelEntry<TSubject>, PanelGroupHandle<TSubject>, definePanelGroup
- resolvePanels: pure resolver (null subject -> empty; when(subject) filter;
  stable order sort; duplicate-id throw with first-wins/last-wins escape
  hatch mirroring resolveComponentRegistry)
- Re-exported by @modular-react/core (export *) and @modular-vue/core

Vue (@modular-vue/vue, re-exported from @modular-vue/core):
- usePanels (computed over the slots source + subject; prefers the reactive
  slots source), <PanelsOutlet> (render-all, subject injected as prop +
  provide, per-panel ModuleErrorBoundary, #empty / #wrap slots),
  usePanelSubject

React (@modular-react/react):
- usePanels, <PanelsOutlet>, usePanelSubject / PanelSubjectContext — same
  surface with useMemo + context

Contributions ride the existing module -> slot path; no descriptor change,
no new package, no registry plugin, no /testing subpath, no Nuxt manifest
threading — all subsumed per the triage.

Docs:
- docs/subject-panels.md guide (entry shape, resolver, both hosts, the
  inspector recipe, the Vue reactivity caveat)
- compositions README "pick by problem shape" table extended 3 -> 4
  primitives; cross-links from reactive-slots-vue.md and
  remote-capability-manifests.md; triage doc marked resolved

Housekeeping (Gap D): widen the Vue family peer ranges (incl. compositions
and testing) to admit @modular-frontend/core@^0.3.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QULkEZq9fop9povfXdMecL
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 34 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

Run ID: 61b47f8a-feb2-4e67-a5c9-7432ae3eb5cd

📥 Commits

Reviewing files that changed from the base of the PR and between 18d8398 and 565a0d8.

📒 Files selected for processing (4)
  • docs/subject-panels.md
  • packages/react/src/panels.tsx
  • packages/vue/README.md
  • packages/vue/src/panels.ts
📝 Walkthrough

Walkthrough

Introduces subject-keyed panels with typed core resolution, React and Vue hosts, subject injection, predicate filtering, ordering, duplicate handling, remount semantics, documentation, and a React Router inspector example with end-to-end tests.

Changes

Subject-keyed panels

Layer / File(s) Summary
Core panel contracts and resolver
packages/frontend-core/src/panels.ts, packages/frontend-core/src/component-registry.ts, packages/frontend-core/src/panels.test*, packages/frontend-core/src/index.ts
Adds typed panel groups and entries, deterministic resolution, duplicate-ID policies, subject predicates, ordering, and type/runtime tests.
React panel host and lifecycle behavior
packages/react/src/panels.tsx, packages/react/src/error-boundary.tsx, packages/react/src/index.ts, packages/react/src/panels.test.tsx
Adds usePanels, PanelsOutlet, subject context access, wrapping, empty rendering, error isolation, duplicate handling, and subjectKey remounting.
Vue panel host and reactive sources
packages/vue/src/panels.ts, packages/vue/src/error-boundary.ts, packages/vue/src/index.ts, packages/vue/src/panels.test.ts, packages/vue-*/package.json
Adds reactive Vue panel resolution and rendering, subject injection, slot-source tracking, lifecycle keys, error labeling, tests, and core peer-range updates.
React Router inspector-panels example
examples/react-router/inspector-panels/*, examples/README.md
Adds shared board data, core and consumer panel modules, a shell application, Playwright configuration, and tests covering selection, ordering, visibility, clearing, and runtime errors.
Documentation and release references
docs/subject-panels.md, docs/*, README.md, CHANGELOG.md, packages/*/README.md
Documents the panel APIs, resolver semantics, framework hosts, reactivity behavior, comparisons with pairing and zones, examples, and release notes.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant InspectorShell
  participant PanelsOutlet
  participant resolvePanels
  participant PanelModules
  User->>InspectorShell: select a board subject
  InspectorShell->>PanelsOutlet: provide subject and panel group
  PanelsOutlet->>resolvePanels: resolve contributed entries
  resolvePanels->>PanelModules: evaluate when(subject)
  PanelModules-->>resolvePanels: return matching panels
  resolvePanels-->>PanelsOutlet: return ordered entries
  PanelsOutlet-->>InspectorShell: render all matching panels
Loading

Possibly related PRs

Suggested labels: minor

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding subject-keyed panels with render-all, predicate-gated behavior.
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 claude/triaged-subject-keyed-initiative-lb0q1p

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.

claude added 3 commits July 19, 2026 10:09
…ots source, shared dedup, boundary labels

Code-review + architecture follow-ups on the subject-keyed panels PR:

- resolvePanels validates duplicate ids BEFORE the null-subject guard, so a
  registration bug surfaces deterministically on first resolve — including the
  usual initial state where nothing is selected yet. New engine tests lock it.
- The duplicate-id collapse is now one shared implementation
  (collapseEntriesById in component-registry.ts) behind both
  resolveComponentRegistry and resolvePanels, so the two reductions' duplicate
  stance cannot drift; the redundant post-filter copy in resolvePanels is gone.
- Vue usePanels tracks BOTH runtime slot sources — the reactive computed and
  the recalculateSlots() signal Ref — serving whichever produced the more
  recent evaluation. Panels contributed via dynamicSlots now update on either
  path, matching the React host (whose single context carries both). Covered
  by a both-directions test.
- Vue usePanels accepts MaybeRefOrGetter for group and onDuplicate, resolved
  inside the computed; <PanelsOutlet> passes getters, so its props are live
  after mount instead of frozen at setup (tested via a post-mount onDuplicate
  flip).
- ModuleErrorBoundary (both bindings) gains an optional `label` prop (default
  "Module"); <PanelsOutlet> passes "Panel" so a crashed panel isn't mislabeled
  a module.
- React usePanels uses a module-level empty-entries constant so the memo stays
  stable for uncontributed groups.
- New host tests in both bindings: subjectKey remount semantics (state kept
  without it, remounted with it) and onDuplicate forwarding.
- Docs: resolver semantics reordered; new "when vs dynamicSlots" section;
  props.subject override documented; Vue caveat now covers the contribution
  side; CHANGELOG gains the missing Unreleased entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W5fr1q1oVcmjg9s1zibMXP
Close the doc-discoverability and API-reference gaps around the panels
feature:

- README.md: add a "Subject-keyed panels" row to the documentation index so
  the guide is discoverable alongside the sibling primitives.
- packages/frontend-core/README.md: document the engine exports
  (definePanelGroup, resolvePanels, PanelEntry, PanelGroupHandle).
- packages/react/README.md: document the React host (usePanels, PanelsOutlet,
  usePanelSubject, PanelSubjectContext).
- packages/vue/README.md: document the Vue host (usePanels, PanelsOutlet,
  usePanelSubject, panelSubjectKey), with the reactivity-caveat pointer.
- docs/subject-panels.md: add a concrete end-to-end walkthrough tying the
  scattered fragments into one inspector-rail scenario — subject type, group
  handle, first-party + consumer contributions, and a trace of exactly which
  panels render (ordered, filtered) across selections, including the
  open-contribution case where a consumer panel appears with no host edit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NV2ACUkMCvCgquryU3P5Cb
…uter)

Add examples/react-router/inspector-panels — a self-contained pnpm workspace
demonstrating the panels feature end to end, matching the sibling examples'
conventions (app-shared / modules / shell + Playwright e2e):

- app-shared: the BoardBlock subject type, a sample board, and the shared
  definePanelGroup handle.
- modules/inspector-core (first-party): an always-on `identity` panel plus a
  `frontend-config` panel gated to frame-level frontend blocks; demonstrates
  both the injected `subject` prop and the `usePanelSubject` context reader.
- modules/acme-extras (consumer): a namespaced `acme:security-report` panel for
  its own `acme-secure` block type, added with no edit to the host — the
  open-contribution property.
- shell: registry wiring plus the board host and the <PanelsOutlet> inspector
  rail (subjectKey / empty / wrap), and a smoke.spec.ts that asserts the exact
  filtered/ordered render across selections, the empty state, and re-resolution.

Verified: typecheck, vite build, and the Playwright e2e (5/5) all pass. Listed
in examples/README.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NV2ACUkMCvCgquryU3P5Cb
@kibertoad kibertoad added minor and removed minor labels Jul 19, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (4)
packages/vue/src/panels.test.ts (1)

110-131: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Ordering assertion doesn't actually verify order.

Both twoPanels entries render Probe, whose text is subject.type, which is identical for both panels here ("frontend"). The toEqual(["frontend", "frontend"]) assertion would pass even if the resolver stopped sorting by order entirely, so the "order 10 before order 20" claim in the comment is unverified.

♻️ Proposed fix using a per-entry data attribute
-  it("renders every matching panel, ordered, with the subject as a prop", () => {
-    seen.length = 0;
-    const subject: Block = { level: "frame", type: "frontend" };
-    const wrapper = mountOutlet(subject);
-    // order 10 (always) before order 20 (frontend)
-    expect(wrapper.findAll(".panel").map((n) => n.text())).toEqual(["frontend", "frontend"]);
+  it("renders every matching panel, ordered, with the subject as a prop", () => {
+    seen.length = 0;
+    const subject: Block = { level: "frame", type: "frontend" };
+    const wrapper = mountOutlet(subject);
+    // order 10 (always) before order 20 (frontend)
+    expect(wrapper.findAll(".panel").map((n) => n.attributes("data-id"))).toEqual([
+      "always",
+      "frontend",
+    ]);
(Requires `Probe` to render a `data-id` derived from the entry, e.g. via a prop threaded through.)
🤖 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 `@packages/vue/src/panels.test.ts` around lines 110 - 131, Update the ordering
test around mountOutlet and the twoPanels entries so rendered output identifies
which panel entry produced each node, rather than relying on identical
subject.type text. Thread each entry’s id into Probe or otherwise expose it as a
data-id, then assert the rendered panel ids are ["always", "frontend"] to verify
order 10 precedes order 20 while preserving the subject-prop assertions.
examples/react-router/inspector-panels/shell/package.json (1)

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

@react-router-modules/* dependencies use workspace:* instead of concrete version ranges. Both manifests declare the published router-module packages with workspace:*, contradicting this monorepo's established convention (per retrieved learnings) of reserving workspace:* for app-internal packages and using concrete ranges (e.g., ^2.0.0) for react-router-modules/*/tanstack-react-modules/*; local linking still works via .npmrc's link-workspace-packages/prefer-workspace-packages settings.

  • examples/react-router/inspector-panels/shell/package.json#L13-L24: change "@react-router-modules/core": "workspace:*" and "@react-router-modules/runtime": "workspace:*" to concrete version ranges.
  • examples/react-router/inspector-panels/modules/inspector-core/package.json#L17-L21: change "@react-router-modules/core": "workspace:*" to a concrete version range.
🤖 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 `@examples/react-router/inspector-panels/shell/package.json` around lines 13 -
24, Replace the `@react-router-modules/core` and `@react-router-modules/runtime`
workspace:* specifications in
examples/react-router/inspector-panels/shell/package.json (lines 13-24) with
concrete version ranges, and replace `@react-router-modules/core` workspace:* in
examples/react-router/inspector-panels/modules/inspector-core/package.json
(lines 17-21) likewise. Preserve workspace:* for app-internal packages and align
the concrete ranges with the published router-module package versions.

Source: Learnings

examples/react-router/inspector-panels/shell/e2e/smoke.spec.ts (1)

33-39: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

renderedPanels snapshot lacks Playwright's auto-retry, unlike neighboring assertions.

Every other assertion in this file (toHaveText, toBeVisible, toHaveCount) auto-retries; renderedPanels is a one-shot evaluateAll invoked immediately after a click (lines 55-59, 71-73, 84-88, 99-109), so a render that lags a tick behind the click could produce a stale/flaky read.

♻️ Proposed fix using `expect.poll`
-async function renderedPanels(page: Page): Promise<string[]> {
-  return page
-    .getByTestId("inspector")
-    .locator("[data-panel]")
-    .evaluateAll((els) => els.map((el) => el.getAttribute("data-panel") ?? ""));
-}
+async function renderedPanels(page: Page): Promise<string[]> {
+  return page
+    .getByTestId("inspector")
+    .locator("[data-panel]")
+    .evaluateAll((els) => els.map((el) => el.getAttribute("data-panel") ?? ""));
+}
+
+async function expectRenderedPanels(page: Page, expected: string[]) {
+  await expect.poll(() => renderedPanels(page)).toEqual(expected);
+}

Then replace expect(await renderedPanels(page)).toEqual([...]) call sites with await expectRenderedPanels(page, [...]).

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

In `@examples/react-router/inspector-panels/shell/e2e/smoke.spec.ts` around lines
33 - 39, Update the renderedPanels-based assertions to use Playwright
auto-retry: add an expectRenderedPanels helper that polls renderedPanels(page)
and asserts the expected panel IDs, then replace every direct expect(await
renderedPanels(page)).toEqual(...) call site with await
expectRenderedPanels(page, [...]).
packages/react/src/panels.test.tsx (1)

56-71: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Ordering assertion doesn't actually verify order.

Both panels render subject.type, which is identical for both entries here, so ["frontend", "frontend"] would pass regardless of whether always (order 10) or frontend (order 20) rendered first. The comment on line 66 claims this proves ordering, but the assertion can't distinguish entry position.

♻️ Proposed fix using the `wrap` render-prop to surface entry id
   it("renders every matching panel, ordered, with the subject as a prop", () => {
     const { container } = render(
-      <PanelsOutlet group={group} subject={{ level: "frame", type: "frontend" }} />,
+      <PanelsOutlet
+        group={group}
+        subject={{ level: "frame", type: "frontend" }}
+        wrap={({ entry, children }) => (
+          <div data-id={entry.id}>{children}</div>
+        )}
+      />,
       { wrapper: withSlots(twoPanels) },
     );
-    // order 10 (always) before order 20 (frontend); both get subject.type.
-    expect([...container.querySelectorAll(".panel")].map((n) => n.textContent)).toEqual([
-      "frontend",
-      "frontend",
-    ]);
+    // order 10 (always) before order 20 (frontend).
+    expect([...container.querySelectorAll("[data-id]")].map((n) => n.getAttribute("data-id"))).toEqual([
+      "always",
+      "frontend",
+    ]);
   });
🤖 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 `@packages/react/src/panels.test.tsx` around lines 56 - 71, Update the “renders
every matching panel, ordered...” test to make panel output identify the slot
entry, using the available wrap render-prop or equivalent entry-id marker. Keep
the expected sequence asserting the order-10 “always” panel before the order-20
“frontend” panel, while still verifying both receive the subject.
🤖 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 `@docs/subject-panels.md`:
- Around line 137-149: Update the React import in the definePanelGroup example
to use `@modular-react/core` instead of `@modular-react/react`; leave the Vue
import example unchanged.

In `@packages/vue/README.md`:
- Around line 25-34: Update the subject-keyed panels bullet in the panels
documentation so the phrase beginning “The Vue host over the framework-neutral
engine” includes an appropriate verb and reads as a complete sentence, while
preserving the existing links and technical details.

In `@packages/vue/src/panels.ts`:
- Around line 165-172: Clarify the `subjectKey` contract used by `keyFor`:
narrow the prop type to the function form if remount behavior is intended to
depend on the subject, or document the static string form’s separate purpose if
it must remain supported. Ensure the type and documentation do not imply that a
static string changes with `subject`.

---

Nitpick comments:
In `@examples/react-router/inspector-panels/shell/e2e/smoke.spec.ts`:
- Around line 33-39: Update the renderedPanels-based assertions to use
Playwright auto-retry: add an expectRenderedPanels helper that polls
renderedPanels(page) and asserts the expected panel IDs, then replace every
direct expect(await renderedPanels(page)).toEqual(...) call site with await
expectRenderedPanels(page, [...]).

In `@examples/react-router/inspector-panels/shell/package.json`:
- Around line 13-24: Replace the `@react-router-modules/core` and
`@react-router-modules/runtime` workspace:* specifications in
examples/react-router/inspector-panels/shell/package.json (lines 13-24) with
concrete version ranges, and replace `@react-router-modules/core` workspace:* in
examples/react-router/inspector-panels/modules/inspector-core/package.json
(lines 17-21) likewise. Preserve workspace:* for app-internal packages and align
the concrete ranges with the published router-module package versions.

In `@packages/react/src/panels.test.tsx`:
- Around line 56-71: Update the “renders every matching panel, ordered...” test
to make panel output identify the slot entry, using the available wrap
render-prop or equivalent entry-id marker. Keep the expected sequence asserting
the order-10 “always” panel before the order-20 “frontend” panel, while still
verifying both receive the subject.

In `@packages/vue/src/panels.test.ts`:
- Around line 110-131: Update the ordering test around mountOutlet and the
twoPanels entries so rendered output identifies which panel entry produced each
node, rather than relying on identical subject.type text. Thread each entry’s id
into Probe or otherwise expose it as a data-id, then assert the rendered panel
ids are ["always", "frontend"] to verify order 10 precedes order 20 while
preserving the subject-prop assertions.
🪄 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

Run ID: d9b527e6-0d1b-47b5-b0ff-6e5696161059

📥 Commits

Reviewing files that changed from the base of the PR and between c359af6 and 18d8398.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (53)
  • CHANGELOG.md
  • README.md
  • docs/reactive-slots-vue.md
  • docs/remote-capability-manifests.md
  • docs/subject-keyed-zones-triage.md
  • docs/subject-panels.md
  • examples/README.md
  • examples/react-router/inspector-panels/README.md
  • examples/react-router/inspector-panels/app-shared/package.json
  • examples/react-router/inspector-panels/app-shared/src/app-types.ts
  • examples/react-router/inspector-panels/app-shared/src/board.ts
  • examples/react-router/inspector-panels/app-shared/src/index.ts
  • examples/react-router/inspector-panels/app-shared/tsconfig.json
  • examples/react-router/inspector-panels/modules/acme-extras/package.json
  • examples/react-router/inspector-panels/modules/acme-extras/src/index.tsx
  • examples/react-router/inspector-panels/modules/acme-extras/tsconfig.json
  • examples/react-router/inspector-panels/modules/inspector-core/package.json
  • examples/react-router/inspector-panels/modules/inspector-core/src/index.tsx
  • examples/react-router/inspector-panels/modules/inspector-core/tsconfig.json
  • examples/react-router/inspector-panels/shell/e2e/smoke.spec.ts
  • examples/react-router/inspector-panels/shell/index.html
  • examples/react-router/inspector-panels/shell/package.json
  • examples/react-router/inspector-panels/shell/playwright.config.ts
  • examples/react-router/inspector-panels/shell/src/components/Home.tsx
  • examples/react-router/inspector-panels/shell/src/components/Layout.tsx
  • examples/react-router/inspector-panels/shell/src/main.tsx
  • examples/react-router/inspector-panels/shell/tsconfig.json
  • examples/react-router/inspector-panels/shell/vite.config.ts
  • packages/compositions/README.md
  • packages/frontend-core/README.md
  • packages/frontend-core/src/component-registry.ts
  • packages/frontend-core/src/index.ts
  • packages/frontend-core/src/panels.test-d.ts
  • packages/frontend-core/src/panels.test.ts
  • packages/frontend-core/src/panels.ts
  • packages/react/README.md
  • packages/react/src/error-boundary.tsx
  • packages/react/src/index.ts
  • packages/react/src/panels.test.tsx
  • packages/react/src/panels.tsx
  • packages/vue-compositions/package.json
  • packages/vue-core/package.json
  • packages/vue-core/src/index.ts
  • packages/vue-journeys/package.json
  • packages/vue-nuxt/package.json
  • packages/vue-runtime/package.json
  • packages/vue-testing/package.json
  • packages/vue/README.md
  • packages/vue/package.json
  • packages/vue/src/error-boundary.ts
  • packages/vue/src/index.ts
  • packages/vue/src/panels.test.ts
  • packages/vue/src/panels.ts

Comment thread docs/subject-panels.md
Comment thread packages/vue/README.md
Comment thread packages/vue/src/panels.ts
… fix README fragment, clarify subjectKey string form

- docs/subject-panels.md: import definePanelGroup from @modular-react/core
  (the /react host layer does not re-export it); both examples
- packages/vue/README.md: complete the sentence fragment in the panels bullet
- panels docstrings (React + Vue) and docs: clarify that subjectKey is re-read
  each render, so the string form is a caller-computed discriminator (a literal
  constant never drives remounts), while the function form derives it from the
  subject

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SDhW7GZMkb4pSCwKVch4tC
@kibertoad
kibertoad merged commit 029905c into main Jul 19, 2026
36 checks passed
@kibertoad
kibertoad deleted the claude/triaged-subject-keyed-initiative-lb0q1p branch July 19, 2026 11:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants