Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

Let two signed-in users run a reciprocal mock interview with each other

Đang mở
#306 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức phù hợp với người mới
15/100
Loại issue
Tính năng
Độ rõ ràng
Khá rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
javascript, rust, sqlite

Hướng nghiên cứu

This is a multi-phase epic, so pick a single sub-task such as P1 and leave the rest. For P1, start at the identity_verified field on SignedInUser in src/accounts.rs and the session query in src/accounts/sessions.rs, then read the session_handler in src/web/auth.rs. Done means /api/session reports identityVerified correctly for OAuth and self-declared sign-ins, with two new cases in tests/web/accounts.rs.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

enhancement

Need

CodeTrial only runs AI-led interviews today. Students who want to practice with a classmate or friend, taking turns as candidate and interviewer, have no way to do it inside CodeTrial: they fall back to a video call plus a shared editor, losing the problem bank, the test runner and any structured feedback. Practicing as the interviewer (asking, hinting, giving concrete feedback) is also a skill the AI mode cannot train.

Desired outcome

Two users who have already agreed on a partner and a time (outside CodeTrial) can run a complete two-person session in CodeTrial:

  1. A signs in with GitHub OAuth, creates a session (topic, difficulty, language, round length, who interviews first) and gets an invite link, which A shares by any means.
  2. B opens the link and signs in with GitHub OAuth if needed, landing back on the peer page, then accepts the invite and becomes the second fixed member.
  3. At the agreed time both click "Enter". Tokens are issued only after both have entered; the countdown starts only after both confirm the start. Both problems are locked at that point and each member can read the guide for the round they interview.
  4. Round 1: A interviews B. B owns the editor and the test runner; A watches read-only and sees the guide, the three hint rungs and private notes. The round is sealed, then feedback.
  5. Both confirm the swap. Round 2 runs with roles reversed on a different problem of the same difficulty, followed by feedback.

Default timing is 25 min per round with a 90 min hard cutoff counted from the actual start of round 1.

Non-goals for phase one

No matchmaking, waiting queue, slot recommendation, public booking, automatic re-pairing, or no-show ranking. No Gemini interviewer in a peer room. No recording, AI post-session review, co-editing, whiteboard sync, video, pause or extend. No peer LiveKit webhook: presence comes from heartbeats and state from polling. A 180-user class is a capacity target to validate, not a reason to add scheduling features. PKCE for the GitHub login is worth doing but is unrelated to this feature and gets its own issue.

Design decisions

  • Creating, accepting, entering and starting are four distinct events. Accepting early creates no room, takes no reservation and starts no timer.
  • Members are fixed; roles belong to a round. Two rounds, each member is candidate once and interviewer once.
  • The candidate holds exclusive write access to the workspace for the round; the interviewer is read-only.
  • HTTP + SQLite is the authority for membership, roles, rounds, deadlines, snapshots and feedback. LiveKit carries audio and disposable previews only; a receiver checks the sender by participant.identity against the member list from the server, never by a field in the payload.
  • No CreateRoom call and no job outbox: LiveKit creates a room on first join, which the AI path already relies on, and each member keeps one opaque identity per session so a reconnect replaces the old connection instead of adding a third participant. Only DeleteRoom is called, for cleanup.
  • Time-based transitions (round expiry, hard deadline, idle lobby, feedback reveal) are computed by one settle(now) inside every peer transaction; one sweeper settles sessions nobody is polling and deletes rooms.
  • Peer mode gets its own API under /api/peer/ and its own pages. The editor and test panel are first extracted from web/interview.js into a module both pages import; no mode branch is added to web/interview.js.
  • Only OAuth-verified accounts may use peer routes. Unsafe peer requests must carry an Origin matching the host; the session cookie is already SameSite=Lax (src/web/auth.rs:548).

Current code, observed at 8394d54

Static reading only; nothing below has been prototyped.

Location Current behavior Consequence
src/accounts.rs:61, src/accounts/sessions.rs:118, src/web/auth.rs:357 OAuth users get a positive github_id, self-declared handles a negative one; SignedInUser does not say which Add a trusted identity_verified derived from github_id > 0, never from the request or from verified_email, and a VerifiedPeer extractor beside Owner
src/web/auth.rs:267 The OAuth callback always redirects to / A signed-out invitee loses the invite; add a fixed-enum next carried in the signed state cookie
src/accounts/schema.rs:14 ACCOUNT_SCHEMA_VERSION = 11, append-only migrations Peer tables arrive as new migrations; existing data untouched
src/web/token.rs, src/web/pool.rs:333, src/dispatch.rs:36 /api/token picks a room name and calls ensure_agent() when a dispatcher exists; nothing calls CreateRoom The peer token route never reaches the dispatcher; RecordingDispatcher (tests/web.rs:321) proves it
src/token.rs:39, src/token.rs:111 Join tokens live 2 h; the admin token carries only roomAdmin Add a 300 s peer token with agent: false, and a server-only roomCreate token for DeleteRoom; existing tokens unchanged
src/livekit/rooms.rs:122 RoomService transport is private and tied to AgentConfig Move it into a credential-based module; agent duplicate handling stays where it is
src/web/pool.rs, src/config.rs:214 Room names follow <prefix>[-<provider>]-<suffix>; ProviderPool::for_room falls back to the primary provider on names it cannot parse Bind provider and room once at Enter, keep the name format, never use fixed_room_name
src/web/mod.rs:47, src/web/mod.rs:52 Each handler bounds its own body: MAX_BODY_BYTES 8 KiB, MAX_REPORT_BYTES 64 KiB Add MAX_PEER_SNAPSHOT_BYTES 64 KiB and MAX_PEER_FEEDBACK_BYTES 16 KiB; existing routes unchanged
src/agent.rs:195, src/agent.rs:211, src/agent.rs:55 ProblemVariant already holds contract, constraints, clarifications, follow-ups and three hint rungs for all 151 problems; Problem holds optimal and pitfalls; variant_for is private Build the interviewer guide from these tables through a presenter; no hand-written guide set and no prompt change
web/interview.js:716, :902, :990, :3433 Editor DOM wiring and the test panel live inside a 4,000-line page script; web/editor.js holds only pure helpers Extract web/workspace.js first, with no behavior change
web/lib.js:275 isAgent() matches agent kind, the agent permission or the interviewer- prefix; topics is tied to TOPIC_ constants by tests/web/contract.rs Peer identities are opaque; peer topics go in a separate web/peer-lib.js
src/web/policy.rs:85 CSP has style-src 'self' and img-src 'self' data: blob: Peer pages use no inline script or style and show the partner by login, not avatar
docs/provider-cost-and-degradation.md "What may be cached" Never persist candidate code or personalized feedback as cache Storing peer snapshots and feedback as the session record must be stated explicitly there (P9, P10)
docs/provider-cost-and-degradation.md "What bounds concurrency" One place says what bounds the server Peer rooms draw on the same LiveKit connection-minute quota; Enter goes through ProviderQuota::verdict_for (src/web/pool.rs:149) and the doc gains a peer paragraph (P6)

Shared conventions for every sub-task

  • All sub-tasks live in this issue; none gets an issue of its own. Each P item is one PR whose title follows the commit subject rules and whose body names the P number and ends with Refs #<this issue>; only the PR that completes the last open item ends with Closes #<this issue>. Tick the item in the checklist below when its PR merges.
  • Migrations append to ACCOUNT_MIGRATIONS and bump ACCOUNT_SCHEMA_VERSION to whatever HEAD has at rebase time.
  • HTTP tests go in a new tests/web/peer.rs, declared in tests/web.rs beside the #[path = "web/token.rs"] block (tests/web.rs:1224); unit test bodies go under tests/unit/ declared via #[path]; browser tests under tests/browser/. Every PR passes ./scripts/test.sh and reports skipped lanes.
  • spawn_github_stub (tests/web.rs:453) always returns id 303 (tests/web.rs:505); P5 makes the id a parameter so two OAuth accounts can be tested.
  • Error shape for all peer routes: 401 not signed in; 403 {"code":"verified_identity_required"} for a self-declared account; 403 {"code":"origin_mismatch"}; 404 for unknown session or non-member; 409 {"code":"version_conflict"} for a stale expectedVersion or expectedRevision; 410 {"code":"invite_unavailable"} for any consumed, expired, revoked or unknown invite; 429 with Retry-After from the per-account peer bucket.
  • Order: P1, P2, P3, P4 and P12 can start in parallel. Then P5, P6, P7; then P8, P9 and P11 in parallel; then P10; then P13, P14, P15.

Progress

  • P1. Tell OAuth accounts from self-declared ones
  • P2. Peer configuration and startup gate
  • P3. Return to the peer page after GitHub sign-in
  • P4. Shared RoomService transport and DeleteRoom
  • P5. Peer sessions, invites and membership
  • P6. Enter, leave, provider binding and the peer token
  • P7. Round state machine
  • P8. Problem assignment and interviewer guide
  • P9. Workspace snapshots, writer lease and final capture
  • P10. Two-way feedback
  • P11. Peer sweeper and room cleanup
  • P12. Extract the workspace from web/interview.js
  • P13. Peer lobby page
  • P14. Peer interview page
  • P15. Runbook and manual end-to-end check

Sub-tasks

P1. Tell OAuth accounts from self-declared ones
  • Deliverable: SignedInUser.identity_verified, reported as identityVerified by /api/session.
  • Touch: src/accounts.rs:61 (field), src/accounts/sessions.rs:118 (also select users.github_id), src/web/auth.rs:297 session_handler.
  • Depends on: nothing.
  • Acceptance: tests/web/accounts.rs gains two cases. An OAuth sign-in through spawn_github_stub whose email list returns 403 gets identityVerified: true; a POST /api/login sign-in gets false.
  • Out of scope: the extractor (P5), any change to /api/token admission or recording email rules.
P2. Peer configuration and startup gate
  • Deliverable: optional peer config, off by default; startup refuses peer without a GitHub OAuth app.
  • Touch: src/web/mod.rs:59 WebServerConfig (peer: Option<PeerConfig>), src/config.rs (PeerConfig and defaults), src/main.rs:257 web_server_config, src/main.rs:972 (add peer_needs_oauth beside recording_needs_verified_identity), src/web/auth.rs:297 (peerEnabled), config/codetrial.env.example.
  • Introduces: CODETRIAL_PEER_ENABLED, CODETRIAL_PEER_MAX_SESSIONS (default 8), CODETRIAL_PEER_ROUND_MINUTES (25), CODETRIAL_PEER_HARD_MAX_MINUTES (90), CODETRIAL_PEER_INVITE_TTL_DAYS (7, capped at 14), CODETRIAL_PEER_RECONNECT_GRACE_SECONDS (120).
  • Depends on: nothing.
  • Acceptance: tests/unit/bin.rs asserts peer_needs_oauth returns Err naming GITHUB_CLIENT_ID only when peer is on and OAuth is off; tests/config.rs asserts the defaults and the 14-day cap; tests/web/accounts.rs asserts peerEnabled is false when unset. Peer works without GOOGLE_API_KEY already (src/main.rs:300); P6's tests run with dispatcher None to keep it so.
  • Out of scope: any /api/peer route, recording settings.
P3. Return to the peer page after GitHub sign-in
  • Deliverable: GET /api/login?next=peer-join returns the user to /peer-lobby after the callback.
  • Touch: src/web/auth.rs:92 login_handler (accept next from a fixed enum and carry it inside the signed state cookie), src/web/auth.rs:200 callback_handler (redirect to the mapped path, otherwise /).
  • Depends on: nothing.
  • Acceptance: tests/web/accounts.rs: next=peer-join gives Location: /peer-lobby; next=https://evil.example and next=//evil.example give /; the existing callback test still gives /.
  • Out of scope: storing the invite server-side, the page itself (P13).
P4. Shared RoomService transport and DeleteRoom
  • Deliverable: a RoomService client usable without AgentConfig, plus delete_room.
  • Touch: move post_room_service and livekit_room_service_request (src/livekit/rooms.rs:122, :138) into new src/livekit/room_service.rs taking (url, api_key, api_secret); agent duplicate handling stays in rooms.rs and calls it. Add livekit_room_create_token beside livekit_room_admin_token (src/token.rs:111) with roomCreate: true. delete_room treats Twirp not_found as success.
  • Depends on: nothing.
  • Acceptance: existing tests/unit/livekit/rooms.rs cases pass unedited. New tests/unit/livekit/room_service.rs runs a local axum stub and asserts DeleteRoom posts to /twirp/livekit.RoomService/DeleteRoom with a bearer token holding roomCreate and not roomAdmin, and that not_found returns Ok. tests/token.rs asserts the new token's claims.
  • Out of scope: CreateRoom, any peer caller (P11).
P5. Peer sessions, invites and membership
  • Deliverable: a verified user creates a session and invite; another verified user accepts; both read the session. Accepting creates no room, reservation or timer.
  • Touch: new src/accounts/peer.rs; new src/web/peer.rs with handlers; src/web/mod.rs:424 router (register /api/peer/* only when config.peer is set); VerifiedPeer beside Owner (src/web/auth.rs:357); an Origin guard on unsafe peer methods; AppState (src/web/mod.rs:164) gains a per-account TokenRateLimit bucket for peer routes.
  • Introduces: tables peer_sessions (opaque id, creator_id, status, version, settings_json, created_at, expires_at), peer_members (session_id, slot 0 or 1, user_id, opaque participant_identity, unique per session and per user), peer_invites (SHA-256 of random_token(32), session_id, expires_at, revoked_at, consumed_by, consumed_at). Routes: POST /api/peer/sessions, GET /api/peer/sessions, GET /api/peer/sessions/{id}, POST /api/peer/invites/accept, POST /api/peer/sessions/{id}/invite (revoke and regenerate), DELETE /api/peer/sessions/{id}/invite, POST /api/peer/sessions/{id}/cancel. The PR description fixes the GET state shape (sessionId, status, version, serverNow, members[{slot, login, identity}], you{slot}, currentRound) that P13 and P14 build on.
  • Depends on: P1, P2.
  • Acceptance: tests/web/peer.rs, with spawn_github_stub taking the GitHub id, asserts: a /api/login account gets 403 verified_identity_required on every route; an OAuth account without email can create; accept fills slot 1 and leaves no reservation; a third account, a reused token, a regenerated-away token and the creator's own accept all get 410 invite_unavailable; Origin: http://evil.example gets 403 origin_mismatch; a non-member gets 404. tests/unit/accounts/schema.migration_tests.rs covers the migration from version 11.
  • Out of scope: Enter, tokens, rounds, pages.
P6. Enter, leave, provider binding and the peer token
  • Deliverable: once both members have entered, each fetches a LiveKit token for one room on one provider; no agent is ever dispatched.
  • Touch: src/accounts/peer.rs, src/web/peer.rs; livekit_peer_token in src/token.rs (300 s, agent: false, canUpdateOwnMetadata: false, no room admin, create or record grants); provider via room_and_provider (src/web/pool.rs:333) and ProviderQuota::verdict_for (src/web/pool.rs:149), never fixed_room_name; a peer paragraph in "What bounds concurrency" of docs/provider-cost-and-degradation.md.
  • Introduces: table peer_active_users (user_id primary key, session_id); columns provider_id, room_name, room_generation on peer_sessions, entered_at, last_seen_at on peer_members. Routes: POST .../enter, .../leave, .../token, .../heartbeat. Enter returns 409 already_active when the user is in another active session and 503 at_capacity at CODETRIAL_PEER_MAX_SESSIONS; token returns 409 waiting_for_partner until both entered and 410 session_closed after cancel or completion.
  • Depends on: P5.
  • Acceptance: tests/web/peer.rs with web_service_with_dispatcher(config, Some(RecordingDispatcher)) asserts the dispatcher list stays empty after both fetch tokens; both tokens name the same room and provider URL; sub equals the stored identity and is identical on a second call; exp - nbf == 300; provider_id_from_room (src/config.rs:232) resolves the room to the bound provider; leave releases peer_active_users. The same flow passes with dispatcher None.
  • Out of scope: room deletion (P11), rounds, UI.
P7. Round state machine
  • Deliverable: start confirmation, two rounds with reversed roles, server deadlines, finish, swap and terminate, all version-checked, with time-based transitions applied by settle(now).
  • Touch: src/accounts/peer.rs, new src/web/peer/rounds.rs; GET state gains currentRound {index, status, role, deadline} and hardDeadline.
  • Introduces: table peer_rounds (session_id, idx 0 or 1, interviewer_slot, candidate_slot, problem_id, status prepared|active|finishing|feedback|closed, version, started_at, deadline, sealed_at); session columns for the start proposal version, per-slot start and swap confirmations, hard_deadline and outcome (complete|partial|no_show|technical_failure). Routes: POST .../ready, .../start (propose or confirm with expectedVersion), .../rounds/{n}/finish, .../advance (swap confirm), .../terminate.
  • Depends on: P6.
  • Acceptance: tests/unit/web/peer/rounds.rs drives settle with an injected clock: a round past its deadline moves to finishing; past hard_deadline the session moves to finalizing; one confirmation does not start round 0; a confirmation with an old proposal version is refused; round 1 roles reverse round 0; no third round. tests/web/peer.rs asserts 409 version_conflict on a stale version and that the deadline equals started_at plus the round minutes.
  • Out of scope: problem choice (P8), snapshots (P9), pause and extend.
P8. Problem assignment and interviewer guide
  • Deliverable: both problems locked at start; the current interviewer reads the guide, the candidate cannot.
  • Touch: src/agent.rs:55 (re-export variant_for as pub(crate)); new src/web/peer/guide.rs presenting {contract, constraints, clarifications, followUps, hints[3], optimal, pitfalls} from find_problem and variant_for; assignment in src/web/peer/rounds.rs picks two distinct problems with the same Problem.difficulty, filtered by topic via topics_for, excluding problems either member has already seen as interviewer while alternatives remain.
  • Introduces: table peer_problem_exposures (user_id, problem_id, kind interviewer|candidate, at). Routes: GET .../rounds/{n}/problem (returns {page, title}; the statement is the public web/problems/<page>.json) and GET .../rounds/{n}/guide.
  • Depends on: P7.
  • Acceptance: tests/unit/web/peer/guide.rs asserts the two-sum presenter output has three hints and the variant contract. tests/web/peer.rs asserts the two rounds get different problems; round 0's candidate gets 404 on its guide and the interviewer 200; nobody gets round 1's problem or guide before the swap; a second session between the same users avoids interviewer exposures. ./scripts/test.sh regenerates no file under tests/golden/.
  • Out of scope: editing problem-bank/ or prompt text, server-only problems. The repository is public, so this only stops early disclosure through the peer UI.
P9. Workspace snapshots, writer lease and final capture
  • Deliverable: only the current candidate holding a valid lease saves snapshots by revision CAS; finishing a round opens a 30 s final-capture window, then seals.
  • Touch: new src/web/peer/snapshots.rs, src/accounts/peer.rs, MAX_PEER_SNAPSHOT_BYTES beside MAX_REPORT_BYTES (src/web/mod.rs:52), a snapshot bucket of 30 per minute per account; "What may be cached" in docs/provider-cost-and-degradation.md gains a sentence that peer snapshots are the session record, visible only to the two members, never an input to any provider call.
  • Introduces: table peer_snapshots (session_id, round_idx, revision, language, code, test_summary_json, updated_at); writer_lease, writer_lease_until, final_capture_incomplete on peer_rounds. Routes: POST .../rounds/{n}/writer (take or take over the lease), PUT .../rounds/{n}/snapshot with {lease, expectedRevision, language, code, tests, final}, GET .../rounds/{n}/snapshot.
  • Depends on: P7.
  • Acceptance: tests/web/peer.rs asserts: the interviewer's PUT gets 403 not_writer; an old revision gets 409; after a second tab takes the lease the first tab gets 409 lease_lost; a 65 KiB body gets 413 and code over 48 KiB of UTF-8 (multi-byte fixture) is refused; during finishing only final: true from the lease holder is accepted; 30 s later settle seals the last revision and sets final_capture_incomplete if no final arrived; after sealing every PUT gets 409 round_sealed; a PUT to round 0 while round 1 is active is refused.
  • Out of scope: the preview channel (P14), whiteboard.
P10. Two-way feedback
  • Deliverable: each member submits feedback on the round they interviewed and on the round they were interviewed in; drafts stay private; both submissions for a round are revealed when both exist or 24 h after the round closes.
  • Touch: new src/web/peer/feedback.rs, src/accounts/peer.rs, MAX_PEER_FEEDBACK_BYTES. docs/observable-delivery-policy.md gains a "Peer feedback" section: the form repeats its "Not assessed" list (accent, filler words, eye contact, nervousness, confidence, personality), peer text is human-authored and unscanned, visible only to the two members, and never feeds a CodeTrial score. Peer feedback is not an agent report: no row in reports, no interviewContract, no golden change.
  • Introduces: table peer_feedback (session_id, round_idx, author_slot, kind candidate|interviewer, rubric_version peer-rubric-v1, payload_json, submitted_at null for drafts). Dimensions: candidate (clarification, reasoning, correctness, testing, complexity, technical explanation) and interviewer (preparation, question quality, hint pacing, follow-up, feedback specificity), each 1 to 4 or not_observed with an example and one improvement. Routes: PUT .../rounds/{n}/feedback, GET .../rounds/{n}/feedback.
  • Depends on: P9.
  • Acceptance: tests/web/peer.rs asserts feedback before sealing gets 409; a draft is not returned to the other member; after one submission the other sees nothing; after both, both see both; with the clock 24 h past close a lone submission becomes visible; missing feedback does not block the swap; an unknown dimension or a score of 5 gets 400; reports stays empty.
  • Out of scope: AI review, report history, teacher access.
P11. Peer sweeper and room cleanup
  • Deliverable: one background task that settles sessions nobody is polling and deletes rooms of closed sessions, with retries.
  • Touch: new src/web/peer/sweeper.rs, registered through BackgroundTasks::absorb beside spawn_session_sweeper (src/web/mod.rs:347); delete_room from P4; database work through accounts::blocking, never held across a network await.
  • Introduces: room_deleted_at, delete_attempts, next_delete_at on peer_sessions. A lobby idle for 10 minutes returns to pending and releases peer_active_users; a pending session past expires_at is cancelled.
  • Depends on: P4, P6, P7.
  • Acceptance: tests/unit/web/peer/sweeper.rs runs one sweep against a stub RoomService and asserts a cancelled session gets exactly one DeleteRoom and room_deleted_at; a 500 leaves it null with delete_attempts = 1 and a later next_delete_at; an idle lobby releases its reservation; a session past hard_deadline is finalized with no client request; dropping the app state stops the task.
  • Out of scope: a jobs table, a webhook, RemoveParticipant.
P12. Extract the workspace from web/interview.js
  • Deliverable: web/workspace.js exporting mountWorkspace({nodes, language, readOnly, onEdit, onRunTests}) for the editor (input, keydown, paint, font, theme) and the test panel; web/interview.js uses it with no behavior change.
  • Touch: web/interview.js (around :716, :902, :990, :3433 and the test-run wiring), new web/workspace.js.
  • Depends on: nothing.
  • Acceptance: tests/browser/editor.test.js, editor-pipeline.test.js, editor-options.test.js, editor-font-size.test.js, editor-theme.test.js, run-shortcut.test.js, test-drawer-resizer.test.js and dom-contract.test.js pass unedited; new tests/browser/workspace.test.js asserts readOnly ignores keystrokes and still paints given text.
  • Out of scope: peer pages, whiteboard.
P13. Peer lobby page
  • Deliverable: web/peer-lobby.html and web/peer-lobby.js for create, copy and regenerate invite, accept from #invite=<token>, sign-in through P3, Enter, Leave, Ready and Start confirmation, polling state every 5 s.
  • Touch: new files, plus one link in web/index.html shown when /api/session returns peerEnabled and identityVerified. No change to src/web/policy.rs or src/web/assets.rs: everything under web/ is embedded already.
  • Introduces: no route. The page clears the fragment after reading it, keeps the token in sessionStorage only until accept succeeds, and uses no inline script or style.
  • Depends on: P3, P5, P6.
  • Acceptance: tests/browser/peer-lobby.test.js, in the style of tests/browser/lobby.test.js, asserts a signed-out visit with #invite=x navigates to /api/login?next=peer-join and keeps x in sessionStorage; a signed-in visit posts accept once and clears the fragment; a 410 shows the unavailable message; Start stays disabled until both are ready. tests/web/assets.rs asserts /peer-lobby.html is served from disk and from the embedded copy with the same CSP as /.
  • Out of scope: the interview page, media.
P14. Peer interview page
  • Deliverable: web/peer-interview.html and web/peer-interview.js. The candidate gets the P12 workspace writable; the interviewer gets it read-only plus the P8 guide and notes kept only in the page. Two-way audio with mute, device change, attach and detach, an explicit "enable sound" on autoplay block, and local tracks stopped on exit. Previews on a peer data topic at most every 300 ms (above 12 KiB of UTF-8 only a revision notice), snapshots every 5 s and on finish, timer from the server deadline, swap confirmation, the P10 feedback form.
  • Touch: new files and new web/peer-lib.js holding the topic name and pure helpers (role from state, preview payload, sender check). Nothing is added to topics in web/lib.js, and isAgent, roomInterviewer and acceptsReport are unchanged.
  • Introduces: no route. On reconnect: new token, then GET state, then GET snapshot, then resume previews; an unsaved local buffer is kept and offered for download when the lease is lost.
  • Depends on: P8, P9, P10, P12, P13.
  • Acceptance: tests/browser/peer-lib.test.js asserts a preview from anyone but the current candidate is ignored whatever its payload claims; an oversized preview becomes a revision notice; a preview for an old round is dropped; role comes only from state. tests/browser/peer-interview.test.js with a fake SDK asserts one attachment per track, detach on unsubscribe, no duplicate tracks after reconnect, and the editor read-only for the interviewer. tests/browser/lib.test.js and observer.test.js pass unedited.
  • Out of scope: recording, video, co-editing, whiteboard.
P15. Runbook and manual end-to-end check
  • Deliverable: docs/peer-interviews.md with the P2 settings, the shared quota note, and a manual check in the style of docs/meet-tab-audio-manual-check.md: two real GitHub accounts in two browser profiles run create, accept, enter, start, round 0, swap, round 1 and feedback; each side hears the other; the interviewer's editor refuses typing; closing one tab for 30 s and reopening restores role, timer and last snapshot; after the session the room is gone. It also lists load targets to validate (90 concurrent sessions: control p95 under 500 ms, snapshot p95 under 1 s, reconnect within 5 s) and rollout stages of 8 to 12, 30 to 40, then 180 users.
  • Touch: docs/peer-interviews.md, a link from README.md.
  • Depends on: P14.
  • Acceptance: a reviewer runs the manual check on a staging deployment and records the result in the PR.
  • Out of scope: an automated load harness (follow-up after the first stage), recording consent, AI review.

Done when

Every item in the Progress checklist is ticked, ./scripts/test.sh passes with no file under tests/golden/ regenerated, and the P15 manual check has been recorded on a staging deployment.

Ngôn ngữ chính
Rust
Star
160
Fork
64
Merge trung bình
1 ngày 9 giờ
Pull request đã merge (30 ngày)
114

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của sysprog21/codetrial

Tất cả issue của sysprog21/codetrial

Issue tương tự

Thêm issue về Rust

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.