Skip to content

feat(web): protocol v5, robot hello on an @control stream - #3567

Open
paul-nechifor wants to merge 1 commit into
paul/feat/w2-sdk-servingfrom
paul/feat/w3-protocol-v5
Open

feat(web): protocol v5, robot hello on an @control stream#3567
paul-nechifor wants to merge 1 commit into
paul/feat/w2-sdk-servingfrom
paul/feat/w3-protocol-v5

Conversation

@paul-nechifor

Copy link
Copy Markdown
Contributor

No description provided.

- Protocol v5. The robot's hello moves from lossy datagrams to an @control
  data frame on a fresh one-shot bidi stream, resent until welcome arrives.
- The hello payload keeps the datagram encoding but may now be 64 KiB, which
  frees the manifest from the ~1100 B datagram budget.
- Channel ids beginning with @ are reserved for protocol control. Manifests
  using them and datagram hellos are rejected with explicit reject reasons.
- Shared fixtures regenerated for v5. Relay session/forward/registry and the
  Python bridge client change together, pinned by the golden wire vectors.
- server_test.ts: the v5 handshake updates are applied on top of the deflaked
  backpressure test from main (#3555). This branch adds no flake fix of its own.
@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
4200 1 4199 176
View the top 1 failed test(s) by shortest run time
dimos.codebase_checks.test_no_sections::test_no_section_markers
Stack Traces | 0.797s run time
def test_no_section_markers():
        """
        Fail if any file contains section-style comment markers.
    
        If a file is too complicated to be understood without sections, then the
        sections should be files. We don't need "subfiles".
        """
        violations = find_section_markers()
        if violations:
            report_lines = [
                f"Found {len(violations)} section marker(s). "
                "If a file is too complicated to be understood without sections, "
                'then the sections should be files. We don\'t need "subfiles".',
                "",
            ]
            for path, lineno, text in violations:
                report_lines.append(f"  {path}:{lineno}: {text.strip()}")
>           raise AssertionError("\n".join(report_lines))
E           AssertionError: Found 2 section marker(s). If a file is too complicated to be understood without sections, then the sections should be files. We don't need "subfiles".
E           
E             .../web/relay_bridge/test_relay_bridge_module.py:1208: # --- Teleop (the tele_cmd_vel tx channel) ---
E             .../web/relay_bridge/test_relay_bridge_e2e.py:401: # --- Teleop e2e: viewer datagrams -> relay lease gate -> bridge publishes ---

lineno     = 401
path       = '.../web/relay_bridge/test_relay_bridge_e2e.py'
report_lines = ['Found 2 section marker(s). If a file is too complicated to be understood without sections, then the sections should ...y_bridge/test_relay_bridge_e2e.py:401: # --- Teleop e2e: viewer datagrams -> relay lease gate -> bridge publishes ---']
text       = '# --- Teleop e2e: viewer datagrams -> relay lease gate -> bridge publishes ---'
violations = [('.../web/relay_bridge/test_relay_bridge_module.py', 1208, '# --- Teleop (the tele_cmd_vel tx channel) ---'), ('dim...idge/test_relay_bridge_e2e.py', 401, '# --- Teleop e2e: viewer datagrams -> relay lease gate -> bridge publishes ---')]

dimos/codebase_checks/test_no_sections.py:145: AssertionError

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@greptile-apps

greptile-apps Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This change introduces protocol v5, carries robot hello messages on the reserved control stream, and expands supported manifest sizes. Two reliability issues were reproduced: valid large manifests can generate subscription snapshots that exceed the robot datagram limit, and delayed transport acknowledgements can cause hello retries to reset streams before the relay reads them. These failures can leave robots without current subscription state or prevent them from completing the relay handshake.

Confidence Score: 3/5

Not safe to merge until the subscription-state delivery and hello retry behavior are made reliable under supported large-manifest and delayed-acknowledgement conditions.

There are two independent P1 findings and neither is security-related, which yields a score of 3. Both were reproduced with focused runtime executions using the affected production paths.

Files Needing Attention: web/relay/registry.ts needs a delivery mechanism or size bound for complete subscription snapshots, and dimos/web/relay_bridge/wt_client.py needs retry cleanup that does not reset hello streams solely because they remain unacknowledged.

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex produced a proof for the first posted P1 finding and attached both the focused large-manifest reproduction source and the PR oversized-snapshot runtime output to support the validation.
  • T-Rex produced a proof for the second posted P1 finding and attached the focused delayed-ack robot hello reproduction source along with the delayed-ack handshake and current retry behavior artifacts to support the validation.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "feat(web): protocol v5, robot hello on a..." | Re-trigger Greptile

Comment thread web/relay/registry.ts
Comment on lines 512 to 521
const msg: Msg = { t: "subs", chs, n: ++entry.n };
const size = encodeDatagram(msg).byteLength;
if (size > DATAGRAM_BUDGET_BYTES) {
// Unreachable while subs are manifest-validated (a manifest that fit
// its hello datagram implies a fitting snapshot). Loud if it ever
// happens: an oversized snapshot silently never reaches the robot.
// Reachable since v5: a stream hello can declare a channel set whose
// full snapshot no longer fits one datagram (W4 moves snapshots to the
// reliable carrier). Loud because an oversized snapshot silently never
// reaches the robot.
console.error(`[relay] subs snapshot for ${robotId} is ${size} B (over datagram budget)`);
}
entry.peer.sendMsg(msg);

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.

P1 Oversized subscription snapshots are undeliverable

A valid large manifest can produce a complete subs snapshot larger than the 1200-byte datagram budget. This branch only logs the overflow and still sends the single oversized datagram, so the robot never receives the authoritative subscription state; periodic snapshot retries remain oversized as the active set grows. Send this state on a reliable control stream, fragment it, or enforce a channel-count limit that keeps its encoded snapshot within the datagram budget.

Artifacts

Focused large-manifest reproduction source

  • Executes the registry subscription path with a valid 32-channel manifest, a 1200-byte datagram contract, and a follow-up teleop control message; takeaway: the source directly exercises the claimed oversized-snapshot path.

PR oversized-snapshot runtime output

  • Captured PR execution shows protocol v5 accepting the large manifest and sending a 1211-byte subscription snapshot over the 1200-byte budget while follow-up teleop remains accepted; takeaway: valid v5 manifests can produce an undeliverable subscription snapshot.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines +203 to +219
def retire_hello_stream() -> None:
# In-flight check and reset in the same event-loop turn (the
# aioquic-safe reset rule, web/README.md bug 9); a delivered
# stream is left alone so a reset cannot destroy a hello the
# relay has yet to read.
if hello_stream is not None and self._session.stream_in_flight(hello_stream):
self._session.reset_if_in_flight(hello_stream)

try:
while True:
if control_payload is None:
self._session.send_msg(msg)
else:
retire_hello_stream()
hello_stream = self.send_frame(CONTROL_CHANNEL, control_payload)
with contextlib.suppress(asyncio.TimeoutError):
await asyncio.wait_for(self._session.welcomed.wait(), 0.2)

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.

P1 Hello retries reset live control streams

stream_in_flight() only means the sender has not received a transport ACK; it does not mean the relay has not yet consumed the stream. After each 200 ms wait, this retry path resets that still-deliverable hello stream before resending. When ACK and relay consumption are delayed beyond the retry interval, every hello is reset before the relay reads it and the robot handshake times out. Keep prior hello streams live until a terminal handshake result, connection close, or a protocol-level acknowledgement that proves they can be retired.

Artifacts

Focused delayed-ack robot hello reproduction source

  • The executed Python harness drives RelayClient.hello against a transport that delays ACK and relay consumption to 350 ms, showing whether reset destroys the still-deliverable stream.

Delayed-ack handshake without destructive stream resets

  • The identical delayed transport completed welcome at 351 ms and recorded relay consumption of stream 101 when resets did not cancel delivery, proving the stream remained deliverable.

Current retry behavior with destructive stream resets

  • The current RelayClient.hello behavior reset streams 101 through 104, recorded no relay consumption, and timed out after 803 ms, confirming the claimed failure.

View artifacts

T-Rex Ran code and verified through T-Rex

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