Skip to content

SPLIT UP: agentic loop test#4932

Closed
NathanFlurry wants to merge 1 commit into05-03-chore_rivetkit_doc_skip_ready_waitfrom
05-03-split_up_agentic_loop_test
Closed

SPLIT UP: agentic loop test#4932
NathanFlurry wants to merge 1 commit into05-03-chore_rivetkit_doc_skip_ready_waitfrom
05-03-split_up_agentic_loop_test

Conversation

@NathanFlurry
Copy link
Copy Markdown
Member

Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Copy link
Copy Markdown
Member Author

NathanFlurry commented May 4, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 4, 2026

PR #4932 Review: SPLIT UP — Agentic Loop Test

Note: This PR is closed. Review provided for reference.

Overview

This PR adds a browser-based interactive test panel for the mockAgenticLoop actor alongside significant enhancements to the actor itself and the serverless pool configuration path. The main changes are:

  1. New MockAgenticLoopPanel UI — a manual test harness in examples/kitchen-sink for exercising streaming, SQLite durability, forced sleep, reconnects, and gateway bypass.
  2. mock-agentic-loop actor enhancements — debug event recording/replay, sleep state tracking via SQLite, per-connection debug socket broadcasts, and sleepStarted/sleepStartedAt in bypass and pong responses.
  3. configureServerlessPool retry loop — replaces a single fire-and-forget with a configurable retry loop that throws after timeout.
  4. Registry handleRequest refactor — pool configuration is now deferred to start/metadata requests only, with engine-internal metadata requests bypassing the check.

Issues and Observations

Bug: bypassConnectable vs skipReadyWait naming inconsistency

The script (scripts/mock-agentic-loop.ts) renames the gateway option from bypassConnectable to skipReadyWait, but the new frontend panel (App.tsx) still defines and uses bypassConnectable in its AgenticHandle type and call sites:

// App.tsx — uses old name
gateway?: { bypassConnectable?: boolean };
await handle.fetch("/bypass", { gateway: { bypassConnectable: true } });
// scripts/mock-agentic-loop.ts — uses new name
gateway?: { skipReadyWait?: boolean };
handle.webSocket("/bypass", undefined, { gateway: { skipReadyWait: true } });

One of these is wrong. If the underlying client SDK was updated to skipReadyWait, the frontend panel bypass buttons will silently send the wrong option and never actually bypass.

Bug risk: race condition in connect's isConnecting guard

connect uses React state (isConnecting) as a mutex:

const connect = useCallback(async (countReconnect = false) => {
    if (isConnecting) return;  // state read, not ref
    setIsConnecting(true);
    ...
}, [..., isConnecting, ...]);

State updates are async in React. If the user clicks Connect while a 500 ms auto-reconnect timer is pending, both calls can read isConnecting === false before either setIsConnecting(true) takes effect, producing two concurrent connection attempts. A useRef flag (checked and set synchronously) would be a reliable guard here.

Indentation issue in validateAgenticRows

return {
    ok: problems.length === 0,
    problems,
        expectedRows: expectedRequests.reduce(   // over-indented
            (total, request) => total + request.seconds,
            0,
        ) + (activeRequest?.received.length ?? 0),
    };
}

expectedRows is indented to the same level as the inner reduce args, not the sibling fields.

Fragile User-Agent check for engine metadata bypass

const isEngineMetadataRequest =
    request.headers.get("user-agent")?.startsWith("RivetEngine/") ?? false;

Any client that sends User-Agent: RivetEngine/... bypasses the pool-configuration wait on metadata requests. Per CLAUDE.md the client <-> engine boundary is untrusted; if this endpoint is reachable by external clients this check is insufficient. At minimum this warrants a comment explaining the trust assumption (e.g. that the metadata path is only reachable on an internal network).

configureTimeoutMs(0) silently skips all retries

while (Date.now() - startedAt <= timeoutMs) { ... }

A value of 0 causes Date.now() - startedAt to immediately exceed the timeout (even >= 0 is borderline). The function would exit the loop without attempting any configuration and throw lastError (which is undefined at that point). Consider validating >= 1000 or documenting the edge case, and guarding throw lastError for the undefined case.

publicToken used for runner config update

const serverlessToken = config.token ?? config.publicToken;
await updateRunnerConfig(clientConfig, poolName, { ... });

updateRunnerConfig is an engine admin endpoint. Using a public token (which may have read-only or end-user scope) where a service/admin token is expected will fail silently or with a permission error at runtime. The fallback chain should be documented or validated against what updateRunnerConfig actually requires.

Minor: debugSocketsByActorId is process-global state in the actor module

const debugSocketsByActorId = new Map<string, Set<UniversalWebSocket>>();

This is fine for a test actor, but note that it leaks across actor restarts and will accumulate closed sockets if removeDebugSocket cleanup ever fails (e.g. if the "close" event fires before addDebugSocket returns). The cleanup path looks correct in normal flow, but worth a note for future maintainers.

Minor: onSocketMessage has no handler for "pong" or "verified" types

The main WebSocket onSocketMessage switch handles hello, history, debugEvent, started, progress, done, error — but not pong or verified. These arrive on bypass WebSockets, so the main socket shouldn't receive them. The silent drop is correct, but given the CLAUDE.md guideline against _ => fall-throughs, consider using a discriminated union exhaustion check or at least a comment.


What's Working Well

  • The retry loop in configureServerlessPool is a clear improvement — the old code swallowed errors silently; the new code propagates failures and makes retry behavior observable via logs.
  • #ensureServerlessPoolConfigured correctly deduplicates concurrent pool setup calls and resets on failure so the next request can retry.
  • The debug event replay pattern (replay on connect, then stream live events) is solid and covers the reconnect case cleanly.
  • Deferring pool configuration to start/metadata requests is the right call — it avoids blocking health checks and non-actor traffic.
  • CSS is complete with responsive breakpoints.

@NathanFlurry NathanFlurry force-pushed the 05-03-split_up_agentic_loop_test branch from e72d12a to 5c45e9b Compare May 4, 2026 09:06
@NathanFlurry NathanFlurry force-pushed the 05-03-chore_rivetkit_doc_skip_ready_wait branch from 5fbbe77 to 4df4d5a Compare May 4, 2026 09:06
@NathanFlurry
Copy link
Copy Markdown
Member Author

Landed in main via stack-merge fast-forward push. Commits are in main; closing to match.

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