Preselect the configured interview length
Problem and behavior
CODETRIAL_DURATION_MIN is documented as the length the lobby preselects, but only check-gemini, the setup probe and the recording cap check read it, so an operator who set 30 still saw 45 for Medium and Hard, and a token request that named no length got the constant 45.
With this change /api/session reports the default as defaultDurationMin, the lobby preselects it for Medium and Hard and at most 30 for Easy, and a token request that names no length is granted it; with the variable unset nothing changes.
For example, with CODETRIAL_DURATION_MIN=60 a Medium lobby preselects 60 and an Easy lobby preselects 30; a value that is not one of the three buttons selects the nearest one in the lobby (20 gives 30, and 75 gives 60 only because there is no 90 button).
Approach
One function, default_duration in src/web/token.rs, clamps the configured value into 10 to 90 and to the recording ceiling, and both /api/session and /api/token call it, so the length that is offered and the length that is granted cannot drift apart.
WebServerConfig and TokenConfig carry the configured value, web/app.js takes the operator value in suggestedDuration, and it re-suggests after /api/session only when that value differs from the markup's 45, so a length or a problem the candidate picked before the session answered is kept.
Snapping to the nearest button happens in the lobby only, and /api/token grants the clamped value as is; the new tests for the token default live in tests/web/token_default.rs because tests/web/token.rs is at its size ceiling.
Validation
cargo test --locked and cargo clippy --locked --workspace --all-targets pass, and node --test --test-name-pattern="operator default" tests/browser/lobby.test.js passes eight tests; I broke the new session, wiring, snapping, latch and manual-problem paths one at a time and each broke a test.
./scripts/test.sh still fails task-authoring-tests, run-budget-tests, interactive-review-hook-tests, git-hooks and shell-syntax on this machine, and the same gates fail on a clean upstream/main at 8f5ad5c, before #581 merged; the javac and ffmpeg tests were skipped.
I did not check the lobby by hand in a browser: the first paint shows 45 until /api/session answers, and a non-button value snaps without a note, so those two are unverified as user experience.
Interactive review
This is one commit of 382 added plus deleted lines, over the 200-line threshold, so I went through the local interactive review on the committed head 8a905a0 and explained the key change in my own words there.
Suggested reading order: src/web/token.rs (default_duration), src/web/auth.rs (/api/session), web/app.js (suggestedDuration and loadAccount), then the tests; the rest is the new field threaded through fixtures.
I am happy to explain the change to a maintainer for the explained label.
Closes #582
Summary by cubic
CODETRIAL_DURATION_MIN now actually preselects the interview length in the lobby and becomes the default granted to a token request that names none; previously it was documented as doing this, but the lobby always started on 45 for Medium and Hard, and a nameless request got the constant 45.
/api/sessionreports the default asdefaultDurationMin, clamped to the allowed range and the recording cap.- The lobby preselects it for Medium and Hard, caps Easy at 30, and snaps non-button values to the nearest enabled button; a length or problem the candidate already chose is never replaced.
- A token request naming no length is granted the configured default; the interview page omits
durationMinfrom the request when the URL sets none, so the server's default is honored. - A
CODETRIAL_DURATION_MINthat is not a whole number prints a warning instead of silently using 45. - With the variable unset, behavior is unchanged.
Closes #582