Repository navigation
Bound interview token startup and no-show cleanup - #440
Conversation
jserv
left a comment
There was a problem hiding this comment.
Run make indent before submitting PRs.
| /// | ||
| /// Implementations must be idempotent per room and must not block: this is | ||
| /// called on the request path. | ||
| #[derive(Clone)] |
There was a problem hiding this comment.
NoShowConsent is inserted between the RoomDispatcher doc comment and the trait. As a result, that whole comment, including "Implementations must be idempotent per room and must not block", now documents NoShowConsent, and the trait has no documentation. Move the struct and its impl above the doc comment, and give the struct a line of its own saying what it carries.
| } | ||
| } | ||
| Ok(crate::livekit::RoomRunOutcome::Completed) => {} | ||
| Err(error) => { |
There was a problem hiding this comment.
An Err here also covers rooms the candidate never reached: join_room or publish_opening_attributes failing inside open_session, or candidate_metadata ending with "room closed before a candidate joined". Those paths only log, so the claimed consent stays bound to a room nobody joined, the stuck state this change sets out to clear. Have open_session report failures before a candidate is accepted as their own outcome and release no_show for them as well, while keeping errors after the interview started as spent.
| provider: &crate::config::Provider, | ||
| no_show: Option<NoShowConsent>, | ||
| ) -> Result<(), DispatchRefusal> { | ||
| let _ = no_show; |
There was a problem hiding this comment.
No test reaches this default body, because RecordingDispatcher overrides it: the mutation job reports replacing it with Ok(()) as missed, and that mutant would stop any dispatcher relying on the default from ever staffing a recorded room. The same run also misses LocalDispatcher::ensure_agent_with_no_show_release. Add a test dispatcher that implements only ensure_agent and assert that a recorded /api/token request still staffs the room.
|
I addressed all requested changes in aa3a24c. Locally, |
| let (room, mut events) = match join_room(config, room_name, &agent_identity, now_seconds).await | ||
| { | ||
| Ok(joined) => joined, | ||
| Err(error) => return Ok(OpenSessionOutcome::FailedBeforeCandidate(error)), |
There was a problem hiding this comment.
FailedBeforeCandidate means the agent failed before it saw a candidate, not that no candidate joined. The browser joins the room and calls startRecording() as soon as /api/token answers, without waiting for the agent. So if join_room or publish_opening_attributes fails, the candidate can already be in the room with a recording starting under this interview. The dispatcher then calls release_interview_room, which puts the interview back to pending while its recording row still names the old room. A reload makes create_interview return that same interview, it claims a second room, and "one consent is one interview" no longer holds. Release only on NoCandidate, or check room.remote_participants() / the interview's recording row before treating a failure as a no-show.
| WHERE id = ?1 | ||
| AND account_id = ?2 | ||
| AND room_name = ?3 | ||
| AND NOT EXISTS ( |
There was a problem hiding this comment.
When this NOT EXISTS guard matches, the UPDATE changes nothing, but release_interview_room still returns Ok(()). The dispatcher drops the NoShowConsent and nothing tries again. That is exactly the flow this PR targets: the browser times out after 10 s, the candidate reloads, create_interview inserts a new pending row, and five minutes later the no-show release for the first room is skipped without any trace. That room stays bound for good, even though the description says it no longer is. At minimum, return whether a row changed and log the skip in release_no_show. If the claim really has to come back, retire it explicitly instead (for example, mark it spent) rather than relying on the pending-row index to allow the update.
|
#285 adds a Strict Interview mode that must never fall into offline practice: when |
aa3a24c to
087af9a
Compare
There was a problem hiding this comment.
1 issue found across 8 files (changes from recent commits).
You’re at about 97% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="web/interview.js">
<violation number="1" location="web/interview.js:223">
P2: This 20s browser deadline spans the server's claim and dispatch work, not just the body read. If dispatch runs past 20s, the browser shows "The interview connection timed out. Offline practice is ready." while the server still starts the interview and the agent. Consider making the browser deadline cover only the upload and response headers, and documenting that a timeout can fire during server-side dispatch.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
| // The agent reads the editor once per 2s watch tick, so publishing every | ||
| // keystroke sends ~10x more full-buffer packets than anyone consumes. | ||
| const CODE_PUBLISH_DEBOUNCE_MS = 300; | ||
| const TOKEN_REQUEST_TIMEOUT_MS = 20_000; |
There was a problem hiding this comment.
P2: This 20s browser deadline spans the server's claim and dispatch work, not just the body read. If dispatch runs past 20s, the browser shows "The interview connection timed out. Offline practice is ready." while the server still starts the interview and the agent. Consider making the browser deadline cover only the upload and response headers, and documenting that a timeout can fire during server-side dispatch.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At web/interview.js, line 223:
<comment>This 20s browser deadline spans the server's claim and dispatch work, not just the body read. If dispatch runs past 20s, the browser shows "The interview connection timed out. Offline practice is ready." while the server still starts the interview and the agent. Consider making the browser deadline cover only the upload and response headers, and documenting that a timeout can fire during server-side dispatch.</comment>
<file context>
@@ -220,7 +220,7 @@ let editorPaintFrame = null;
// keystroke sends ~10x more full-buffer packets than anyone consumes.
const CODE_PUBLISH_DEBOUNCE_MS = 300;
-const TOKEN_REQUEST_TIMEOUT_MS = 10_000;
+const TOKEN_REQUEST_TIMEOUT_MS = 20_000;
/// Long enough to read twice, short enough that it is gone before the answer
/// it is about. Measured against the hint text, not chosen round.
</file context>
jserv
left a comment
There was a problem hiding this comment.
Rebase latest main branch and resolve conflicts.
95fb059 to
7e92120
Compare
jserv
left a comment
There was a problem hiding this comment.
Rebase the current branch onto the upstream default branch and rework the series into functionally minimal commits, folding similar ones and enforcing the project's commit message rules.
Bound server and browser waits around token startup. Release consent when a dispatched room never admits a candidate, and preserve successful startup behavior across dispatch boundaries.
Keep claim and dispatch together after a client disconnects. Guard release against pending or recording state, and document when browser fallback can precede completed dispatch.
7e92120 to
b706fc4
Compare
|
Thank @xubo9367 for contributing! |
Summary
/api/tokenrequest body./api/tokenand use the existing offline-practice fallback when it expires.The no-show cleanup is guarded by the room name, matching the existing refused-dispatch cleanup. A lost response may still leave an agent waiting until
CANDIDATE_JOIN_LIMIT, but the agent then exits and the consent is no longer left bound to an unused room.Tests
cargo test token_body_read_has_a_deadline -- --nocapturecargo test --test web token_stalls_before_and_after_dispatch_have_different_side_effects -- --nocapturenode --test tests/browser/dom-contract.test.js tests/browser/recording-consent.test.jsThe integration coverage verifies:
The full local test script reached 1,455 browser tests. One unrelated lobby layout test failed because the VM maps
system-uito Noto Sans and measured 814 px against an 800 px viewport. The branch does not modify the lobby test or its layout files.Closes #380
Summary by cubic
Bounds interview startup so a stalled
/api/tokenrequest can't hold it open forever, and releases recorded-interview consent when a dispatched room never gets a candidate or fails before one joins. Closes #380.408before any room is reserved or consent claimed. A matching 20-second browser deadline falls back to offline practice.Written for commit b706fc4. Summary will update on new commits.