Skip to content

Data-bound chart components 4/7: serve composite plan tokens - #464

Open
FarhanAliRaza wants to merge 1 commit into
stack/3-plans-and-data-varsfrom
stack/4-composite-serving
Open

Data-bound chart components 4/7: serve composite plan tokens#464
FarhanAliRaza wants to merge 1 commit into
stack/3-plans-and-data-varsfrom
stack/4-composite-serving

Conversation

@FarhanAliRaza

@FarhanAliRaza FarhanAliRaza commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Stacked on #463. Base is stack/3-plans-and-data-vars.

Makes xyp1|<digest>|<data token> a servable figure identity.

Change

The namespace learns one concept — what a token reveals about affinity and rebuildability — instead of branching on prefixes at each call site:

  • a composite enforces affinity through its embedded data token and rebuilds like any state token;
  • a bare xyd1 token names columns, never a figure, so it keeps affinity but is never served or rebuilt as one.

Serving a composite is plan lookup + columns (registry hit, else the data method re-run against session state) + bind into a fresh Chart. Both halves are independently recoverable on any worker, so §3.2's reconnect promise holds for this tier without a central store.

Failures are typed rather than flattened to "unknown figure token": a plan miss (hot-reload digest drift) answers err {resync} naming the digest, and a bind mismatch answers the reason naming both sides.

The registry's error seam is wired to a room-wide err broadcast here — a column republish whose bind fails has no request to answer, and without this subscribers would sit on stale pixels with nothing in the log.

The wire envelope grew no fields. Rooms, versions, mid addressing, and the attachment cap treat a composite as an ordinary fig string.

Spec

reflex-integration.md §3.6 (composite tokens, republish fan-out), file map.

Test plan

  • uv run pytest tests/reflex_adapter tests/test_validation_timing.py — 208 passed, including composite fan-out, plan-miss resync, and bind-error frames against a real uvicorn + socket.io client
  • pre-commit run --all-files, ruff check, ruff format --check, ty check — clean

Review in cubic

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d365e3ca-cb57-4790-962b-0dfcc7e4fc03

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@greptile-apps

greptile-apps Bot commented Aug 5, 2026

Copy link
Copy Markdown

Greptile Summary

The PR makes composite plan/data tokens servable as figures while preserving embedded-session affinity and adding typed plan failures.

  • Classifies tokens by affinity, rebuildability, and optional plan binding.
  • Reconstructs composite figures from registered plans and cached or state-rebuilt columns.
  • Broadcasts republish binding failures to affected rooms.
  • Adds Socket.IO coverage for serving, recovery, fan-out, affinity, and typed errors.

Confidence Score: 5/5

The PR appears safe to merge, with no blocking failure remaining.

No blocking failure remains.

Important Files Changed

Filename Overview
python/reflex_xy/namespace.py Adds composite-token identity handling, plan dependency registration, typed rebuild failures, and room-wide error broadcasts.
python/reflex_xy/state_bridge.py Extends state-backed recovery to data methods and composite plan binding while keeping bare data tokens non-servable.
python/reflex_xy/app.py Wires registry-side asynchronous failures to namespace room broadcasts.
spec/design/reflex-integration.md Documents composite-token recovery, session affinity, and republish fan-out semantics.
tests/reflex_adapter/test_socket_data_plane.py Adds protocol-level integration coverage for composite serving, rebuilding, fan-out, affinity, and failure envelopes.

Reviews (2): Last reviewed commit: "feat(reflex): serve composite plan token..." | Re-trigger Greptile

Comment thread python/reflex_xy/namespace.py
Comment on lines +1665 to +1668


class PlaneSchema(TypedDict):
x: np.ndarray

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Browser rendering evidence is missing

The added tests begin at the Socket.IO client and stop at payload metadata and interaction replies, so they do not exercise the composite-token flow through a browser-mounted Reflex component. Please add the required scratch Reflex example and screenshot evidence to demonstrate that the resulting chart paints correctly.

Rule Used: You need to make an example svg/png, html, jupyter... (source)

Knowledge Base Used: Testing and Benchmarks

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@codspeed-hq

codspeed-hq Bot commented Aug 5, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 109 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing stack/4-composite-serving (25a9fdc) with stack/3-plans-and-data-vars (2f7cb25)2

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on stack/3-plans-and-data-vars (4b1f770) during the generation of this report, so 60e59a2 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

2 issues found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="python/reflex_xy/state_bridge.py">

<violation number="1" location="python/reflex_xy/state_bridge.py:114">
P3: The bind-failure `source` label differs between the two composite bind paths, so the same underlying mismatch produces two different `err` frames. Here `rebuild_plan_figure` truncates the state full name to its last dotted component (e.g. `PlaneData.table`) before passing it to `plan.bind(columns, source=...)`, whereas `registry._rebuild_dependent` (the republish/fan-out path) passes the full `state_full_name` (e.g. `tests...PlaneData.table`). Since this PR's whole point is typed, spec-consistent error frames that "name both sides," aligning the two sources would keep the subscribe-time and republish-time `err` messages consistent. Prefer using the full `state_full_name` here, matching the republish path.</violation>
</file>

<file name="python/reflex_xy/namespace.py">

<violation number="1" location="python/reflex_xy/namespace.py:255">
P2: Each subscribe registers a `(data_token, digest)` binding in the registry's `_digests_by_data_token` index (this new line), but nothing removes that binding on unsubscribe or disconnect. The index is only pruned lazily inside `registry._rebuild_dependent` on the *next republish of that same data token*. Because a data token embeds the session's `client_token`, a session that mounts a data-bound plan and then disconnects will never republish its columns again — so its binding is never reclaimed. In a long-lived worker with many short-lived sessions the index grows with cumulative sessions (not with mounted plans), directly contradicting the spec change's claim that the index "stays bounded by mounted plans." Each stale binding also causes a wasted `_rebuild_dependent` lookup + discard on any future republish of that token. Consider pruning `_digests_by_data_token` from `unsubscribe`/`disconnect` (e.g. discard a binding when neither the composite entry nor `_rebuildable_subscribers[composite]` remains).</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

# Index the mounted plan before serving, so a column
# republish racing this subscribe rebuilds it (the
# re-read below then serves that fresher generation).
self.registry.bind_plan(*identity.plan_bound)

@cubic-dev-ai cubic-dev-ai Bot Aug 5, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Each subscribe registers a (data_token, digest) binding in the registry's _digests_by_data_token index (this new line), but nothing removes that binding on unsubscribe or disconnect. The index is only pruned lazily inside registry._rebuild_dependent on the next republish of that same data token. Because a data token embeds the session's client_token, a session that mounts a data-bound plan and then disconnects will never republish its columns again — so its binding is never reclaimed. In a long-lived worker with many short-lived sessions the index grows with cumulative sessions (not with mounted plans), directly contradicting the spec change's claim that the index "stays bounded by mounted plans." Each stale binding also causes a wasted _rebuild_dependent lookup + discard on any future republish of that token. Consider pruning _digests_by_data_token from unsubscribe/disconnect (e.g. discard a binding when neither the composite entry nor _rebuildable_subscribers[composite] remains).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At python/reflex_xy/namespace.py, line 255:

<comment>Each subscribe registers a `(data_token, digest)` binding in the registry's `_digests_by_data_token` index (this new line), but nothing removes that binding on unsubscribe or disconnect. The index is only pruned lazily inside `registry._rebuild_dependent` on the *next republish of that same data token*. Because a data token embeds the session's `client_token`, a session that mounts a data-bound plan and then disconnects will never republish its columns again — so its binding is never reclaimed. In a long-lived worker with many short-lived sessions the index grows with cumulative sessions (not with mounted plans), directly contradicting the spec change's claim that the index "stays bounded by mounted plans." Each stale binding also causes a wasted `_rebuild_dependent` lookup + discard on any future republish of that token. Consider pruning `_digests_by_data_token` from `unsubscribe`/`disconnect` (e.g. discard a binding when neither the composite entry nor `_rebuildable_subscribers[composite]` remains).</comment>

<file context>
@@ -208,7 +246,13 @@ async def on_sub(self, sid: str, data: Any) -> None:
+                    # Index the mounted plan before serving, so a column
+                    # republish racing this subscribe rebuilds it (the
+                    # re-read below then serves that fresher generation).
+                    self.registry.bind_plan(*identity.plan_bound)
                 # A normal state publish can replace a just-rebuilt entry
                 # while its room-wide broadcast is still completing, before
</file context>
Fix with cubic

# Future binds (other plans over the same data var) hit the cache;
# bind_plan below is the subscribe path's job, not the rebuild's.
registry.publish_columns(composite.data_token, columns)
source = f"{composite.data.state_full_name.rsplit('.', 1)[-1]}.{composite.data.var_name}"

@cubic-dev-ai cubic-dev-ai Bot Aug 5, 2026

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.

P3: The bind-failure source label differs between the two composite bind paths, so the same underlying mismatch produces two different err frames. Here rebuild_plan_figure truncates the state full name to its last dotted component (e.g. PlaneData.table) before passing it to plan.bind(columns, source=...), whereas registry._rebuild_dependent (the republish/fan-out path) passes the full state_full_name (e.g. tests...PlaneData.table). Since this PR's whole point is typed, spec-consistent error frames that "name both sides," aligning the two sources would keep the subscribe-time and republish-time err messages consistent. Prefer using the full state_full_name here, matching the republish path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At python/reflex_xy/state_bridge.py, line 114:

<comment>The bind-failure `source` label differs between the two composite bind paths, so the same underlying mismatch produces two different `err` frames. Here `rebuild_plan_figure` truncates the state full name to its last dotted component (e.g. `PlaneData.table`) before passing it to `plan.bind(columns, source=...)`, whereas `registry._rebuild_dependent` (the republish/fan-out path) passes the full `state_full_name` (e.g. `tests...PlaneData.table`). Since this PR's whole point is typed, spec-consistent error frames that "name both sides," aligning the two sources would keep the subscribe-time and republish-time `err` messages consistent. Prefer using the full `state_full_name` here, matching the republish path.</comment>

<file context>
@@ -40,40 +50,81 @@ def _resolve_state_cls(state_full_name: str) -> Any:
+        # Future binds (other plans over the same data var) hit the cache;
+        # bind_plan below is the subscribe path's job, not the rebuild's.
+        registry.publish_columns(composite.data_token, columns)
+    source = f"{composite.data.state_full_name.rsplit('.', 1)[-1]}.{composite.data.var_name}"
+    return plan.bind(columns, source=source).figure()
+
</file context>
Suggested change
source = f"{composite.data.state_full_name.rsplit('.', 1)[-1]}.{composite.data.var_name}"
source = f"{composite.data.state_full_name}.{composite.data.var_name}"
Fix with cubic

Makes xyp1|<digest>|<data token> a servable figure identity. The namespace
learns one concept — what a token reveals about affinity and
rebuildability — instead of branching on prefixes at each call site: a
composite enforces affinity through its embedded data token and rebuilds
like any state token; a bare xyd1 token names columns, never a figure, so
it keeps affinity but is never served or rebuilt as one.

Serving a composite is plan lookup + columns (registry hit, else the data
method re-run against session state) + bind into a fresh Chart. Both halves
are independently recoverable on any worker, so §3.2's reconnect promise
holds for this tier without a central store.

Failures are typed rather than flattened to "unknown figure token": a plan
miss (hot-reload digest drift) answers err {resync} naming the digest, and
a bind mismatch answers the reason, naming both sides. The registry's
error seam is wired to a room-wide err broadcast here — a column republish
whose bind fails has no request to answer, and without it subscribers would
sit on stale pixels with nothing in the log.

The wire envelope grew no fields: rooms, versions, mid addressing, and the
attachment cap treat a composite as an ordinary fig string.

Spec: reflex-integration.md §3.6 (composite tokens, republish fan-out),
file map.
@FarhanAliRaza
FarhanAliRaza force-pushed the stack/4-composite-serving branch from e35456f to 25a9fdc Compare August 5, 2026 14:51
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