Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/interview-contract-versions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ can select it.

## The active bundle

Bundle 30: live prompt 22, report prompt 17, rubric 1, report schema 2.
Bundle 31: live prompt 23, report prompt 18, rubric 1, report schema 2.

| Bundle | Introduced |
|---|---|
| 31 | A candidate may disable code execution for the entire editor interview. Whiteboard sessions ignore this preference because they have no code runner. The signed participant setting persists across languages and reconnects, execution events cannot earn credit, and Test may be recorded from a hand trace of written code with source `candidate_speech`. Live prompts ask for traces rather than Run, preserve completed evidence, and distinguish the choice from a runner outage. Assessment metadata and the server-stamped optional `codeExecution: false` report field record the restriction without inventing executed or passing cases; browser reports, saved history and Markdown retain it. The field is additive under schema 2 and older renderers may ignore it. Older reports without the field retain their existing meaning. |
Comment thread
moon-jam marked this conversation as resolved.
| 30 | Qualifying browser-reported executions record Test at receipt time and publish the checklist without waiting for a model tool call, including failing runs and runs during a thinking hold. Test is also recorded when an earlier credited run covers the editor again, as when the candidate switches back to the language that ran, and the interviewer is then told so without being asked to reply, along with the follow-ups when that completed the coding round. A delayed model call returns the existing execution row without duplicating it or changing its timestamp, and its reply says nothing was added. Setup errors, empty runs, starter code and runs that no longer cover the editor grant no new Test row, and the counts of a run that earned no credit never choose the next step. The checklist is republished until a send succeeds and again when the candidate rejoins, and a reminder of unrecorded earlier steps counts as given only once the reply carrying it is delivered. A phase judge, a side call on the report model a few seconds after each candidate turn, a turn the recognizer grew, or an editor change while a step is open, records Repeat, Example and Algorithm until Coding is recorded, and Coding and Optimizations while there is code, when the candidate's own words, or for Coding what they typed, complete them. The server accepts a step only while it is open and only with a quote found in one recognized candidate turn (adjacent candidate lines the recognizer split count as one), six words for the spoken steps and four for Optimizations, or for Coding in the editor and not in the starter; an approach or analysis the interviewer said before the candidate did (the interviewer saying it back afterwards does not count against it), a paraphrase, an unrecognized turn, or a judgment about code since rewritten records nothing, and claims or requests to mark a step do not count. The row carries the verified quote, the interviewer is told what was recorded without being asked to reply, along with the follow-ups when the step completed the coding round, and the end waits up to 5 seconds for the judge so the report sees the candidate's last answer, while a round transition is held for up to 5 seconds without stopping the room, which keeps forwarding the candidate's audio, so the round decision sees it too. The judge reads the exercise's contract and constraints, a call that fails leaves its window to be read again, a verified quote stands even when a later candidate turn was not recognized, and a held transition also waits for a judgment of anything said after the last one's snapshot, and stays held while the interview is paused. A judgment asked for by the editor alone waits 30 seconds after the last one and takes at most 12 of the 48 calls. At a whiteboard the judge is told there is no editor and the board is not shown, judges Optimizations from speech once the board has work on it, and leaves Coding to the interviewer, since its quote must come from typed code; no run records Test there, and the whiteboard instructions carry the same evidence check. A rate limit on a judgment does not cool the report key. Conversational phase instructions require grounded evidence calls before acknowledging completion, allow independent calls in a batch, and forbid filling earlier phases from later progress. Editor, board and evidence replies carry the recorded phase ids used by the checklist, including on refusals; the interviewer checks that state before claiming a step is marked, and records a missing step only from earlier turns, never because the candidate asked. Live prompt 22, with the phase judge's instruction and prompt in its golden; the report prompt, rubric and schema are unchanged. |
| 29 | Whiteboard interviews: the live prompt is written for the surface the candidate works on, so a whiteboard session is told it has no editor and no test runner, is given the six steps as drawn work ending in the complexity of the approach on the board, is offered `read_board` in place of `read_editor` and a `log_hint` whose clue arrives with the board again, asks a candidate whose speech stays unclear to write it on the board rather than as a code comment, and reads an unclear mark on the board by what the candidate said while drawing it, asking what it stands for rather than guessing; `board_snapshot` joins the evidence sources and is the only one besides candidate speech a whiteboard session may record as observed (`session_timing` stays available to both surfaces for skipped STAR steps), while an editor session may not record it at all; the phases about written work are gated on strokes on the board rather than on characters in the editor. The report prompt follows the same surface: a whiteboard review is sent labeled images of every completed REACTO phase plus a changed final board, so clearing the live surface does not erase earlier evidence; it is told that nothing ran and that the Coding, Test and Optimizations phases were a hand trace, the cases named against the drawing, and the complexity they confirmed. Its interim notes are told the interview has no editor rather than shown an empty one. Its system instruction is its own, so the scoring rules that outrank the brief define both scores at the board, cap an empty board rather than an empty editor, and cite the board where the other cites the code and the test account. The rubric and the report schema are unchanged, so a report from either surface is scored the same way and against the same ten phases. |
| 28 | Language tab changes preserve spoken discussion before any code is typed and repeated choices do not reopen the interview. A candidate turn the recognizer returned in another script is not discussion. The acknowledgement continues from the current context and asks for an opening restatement only if discussion of the exercise has not begun. Live prompt 20; the report prompt, rubric and report schema are unchanged. |
Expand Down
42 changes: 30 additions & 12 deletions src/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,9 @@ pub const THINKING_CHECK_IN_S: u64 = 120;
pub(crate) const THINKING_RELEASE_COOLDOWN: std::time::Duration =
std::time::Duration::from_secs(10);

pub const INTERVIEW_CONTRACT_BUNDLE_VERSION: u32 = 30;
pub const LIVE_PROMPT_VERSION: u32 = 22;
pub const REPORT_PROMPT_VERSION: u32 = 17;
pub const INTERVIEW_CONTRACT_BUNDLE_VERSION: u32 = 31;
pub const LIVE_PROMPT_VERSION: u32 = 23;
pub const REPORT_PROMPT_VERSION: u32 = 18;
pub const RUBRIC_VERSION: u32 = 1;
pub const REPORT_SCHEMA_VERSION: u32 = 2;

Expand Down Expand Up @@ -767,6 +767,7 @@ pub struct RuntimeState {
/// `last_test_run` and `test_runs` below stay at their defaults for its
/// whole life, and every gate that reads them has to ask this first.
pub interview_mode: InterviewMode,
pub code_execution_disabled: bool,
pub coding_minutes: u32,
pub behavioral_minutes: u32,
pub round_transition_seen: bool,
Expand Down Expand Up @@ -994,6 +995,14 @@ impl RuntimeState {
}
}

pub(crate) fn prompt_evidence(&self, purpose: ViewFor) -> Vec<String> {
let mut lines = self.evidence_ledger.prompt_view(purpose);
if self.code_execution_disabled {
lines.push("Code execution: disabled by the candidate for this interview; testing evidence comes from hand traces, not executed cases.".to_string());
}
lines
}

/// A fresh interview of this problem: its hint ladder and its starters,
/// which nothing the browser sends may replace. Built here rather than by
/// whoever starts a room, because a state missing either answers every hint
Expand Down Expand Up @@ -1024,6 +1033,7 @@ impl Default for RuntimeState {
started_at: std::time::Instant::now(),
interview_loop: InterviewLoop::CodingBehavioral,
interview_mode: InterviewMode::Coding,
code_execution_disabled: false,
coding_minutes: 37,
behavioral_minutes: 8,
round_transition_seen: false,
Expand Down Expand Up @@ -1764,6 +1774,8 @@ pub(crate) enum TestSource {
pub(crate) fn test_source(state: &RuntimeState) -> TestSource {
if state.interview_mode.is_whiteboard() {
TestSource::Board
} else if state.code_execution_disabled {
TestSource::Trace
} else if tested_code_is_current(state) {
TestSource::Run
} else if runner_unavailable_on_screen(state) {
Expand Down Expand Up @@ -2240,22 +2252,24 @@ fn record_evidence(
// implementation. Test is the run's evidence, so it is recorded from the
// test event, and only while the editor still holds the code that run
// executed. This also covers model-inferred completion and a model claiming
// a test_event the server never received. The one exception is a platform
// that cannot run tests at all, where the candidate's hand trace is the
// only testing left, and it is recorded as what it is: speech. That outage
// is the browser's claim, as its pass counts are; a Test row from
// `candidate_speech` exists only on this path, so the report shows a reader
// which interviews took it. A run of the code on screen still wins over an
// a test_event the server never received. When execution is disabled or a
// platform cannot run tests, the candidate's hand trace is the only testing
// left, and it is recorded as what it is: speech. That outage is the
// browser's claim, as its pass counts are; a Test row from
// `candidate_speech` identifies tracing, while the session setting tells it
// apart from an outage. A run of the code on screen still wins over an
// outage reported after it.
if phase == FrameworkPhase::Test && kind != EvidenceKind::Skipped {
match test_source(state) {
TestSource::Run if source != EvidenceSource::TestEvent => {
return Err("record Test with source test_event: it is the run's evidence");
}
TestSource::Trace if source != EvidenceSource::CandidateSpeech => {
return Err(
"the runner cannot provide tests, so Test is recorded from the candidate's hand trace, with source candidate_speech",
);
return Err(if state.code_execution_disabled {
"code execution is disabled, so record Test from a hand trace with source candidate_speech"
} else {
"the runner cannot provide tests, so Test is recorded from the candidate's hand trace, with source candidate_speech"
});
}
TestSource::Neither => {
return Err(
Expand Down Expand Up @@ -2589,6 +2603,7 @@ pub struct MetadataConfig {
pub grounding: InterviewGrounding,
/// The candidate hid the worked examples in the preflight.
pub examples_hidden: bool,
pub code_execution_disabled: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Default)]
Expand Down Expand Up @@ -2899,6 +2914,8 @@ pub fn parse_participant_metadata(metadata: Option<&str>) -> MetadataConfig {
let profile = sanitize_interview_profile(value.get("interviewProfile"));
let grounding = sanitize_interview_grounding(value.get("interviewGrounding"));
let examples_hidden = value.get("hideExamples") == Some(&serde_json::Value::Bool(true));
let code_execution_disabled =
value.get("codeExecution") == Some(&serde_json::Value::Bool(false));

MetadataConfig {
problem,
Expand All @@ -2908,6 +2925,7 @@ pub fn parse_participant_metadata(metadata: Option<&str>) -> MetadataConfig {
profile,
grounding,
examples_hidden,
code_execution_disabled,
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/agent/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,10 @@ fn apply_test_results(
since_last_test_reaction_seconds: f64,
receipt_timestamp_ms: u64,
) -> DataEventResult {
if state.code_execution_disabled {
return DataEventResult::default();
}

// The browser timestamp is a claim stored only as evidence metadata. Read
// it before sanitizing, because the prompt-facing test payload deliberately
// drops fields it does not render.
Expand Down
Loading