use std::path::Path;
use onemessagebus::{
Address, Answer, Codec, CodecFailure, CodecName, EnvName, Message, SchemaId, ServeSession,
};
use schemars::{JsonSchema, Schema};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use crate::channel::source::PROPOSAL;
use crate::note::DeliveredNote;
static CODEC_NAME: std::sync::LazyLock<CodecName> = std::sync::LazyLock::new(|| {
CODEC
.parse()
.expect("the linked onejudge codec has a valid name")
});
pub const CODEC: &str = "onejudge";
pub const PROTOCOL_VERSION: u32 = 6;
pub const TRANSCRIBED_FROM: &str = "onejudge 0.8.1";
pub const OP: &str = "op";
pub mod op {
pub const RESPOND: &str = "respond";
pub const USER: &str = "user";
pub const SUPERVISOR: &str = "supervisor";
pub const JUDGE: &str = "judge";
pub const ASSESS: &str = "assess";
pub const ALL: [&str; 5] = [RESPOND, USER, SUPERVISOR, JUDGE, ASSESS];
pub const SERVED: [&str; 2] = [SUPERVISOR, JUDGE];
}
pub mod field {
pub const COMPLETION: &str = "completion";
pub const MESSAGE: &str = "message";
pub const REASON: &str = "reason";
pub const VALUE: &str = "value";
pub const COMMANDS: &str = "commands";
pub const EDIT_OP: &str = "op";
pub const EDIT_TARGET: &str = "id";
pub const REPLY: &str = "reply";
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum Role {
User,
Assistant,
System,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ToolEventKind {
ToolCall,
ToolResult,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ToolEvent {
pub kind: ToolEventKind,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub input: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub output: Option<String>,
#[serde(default)]
pub index: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool_call_id: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ConversationMessage {
pub role: Role,
pub content: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub events: Vec<ToolEvent>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Skill {
pub name: String,
pub path: String,
pub instructions: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Evidence {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub worktree: Option<String>,
pub history_files: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct RespondFrame {
pub skill: Skill,
pub messages: Vec<ConversationMessage>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct UserFrame {
pub persona: String,
pub messages: Vec<ConversationMessage>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SupervisorFrame {
pub task: String,
pub persona: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub done_when: Option<String>,
pub worktree: String,
pub history_name: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub notes: Vec<DeliveredNote>,
pub messages: Vec<ConversationMessage>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "kind", rename_all = "lowercase", deny_unknown_fields)]
pub enum JudgeFrame {
Boolean {
criterion: String,
messages: Vec<ConversationMessage>,
#[serde(default, skip_serializing_if = "Option::is_none")]
evidence: Option<Evidence>,
},
Numeric {
criterion: String,
min: f64,
max: f64,
messages: Vec<ConversationMessage>,
#[serde(default, skip_serializing_if = "Option::is_none")]
evidence: Option<Evidence>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct AssessFrame {
pub prompt: String,
pub messages: Vec<ConversationMessage>,
}
impl Message for RespondFrame {
const SCHEMA: SchemaId = SchemaId::literal("agent", "onejudge-frame.respond", 6);
}
impl Message for UserFrame {
const SCHEMA: SchemaId = SchemaId::literal("agent", "onejudge-frame.user", 6);
}
impl Message for SupervisorFrame {
const SCHEMA: SchemaId = SchemaId::literal("agent", "onejudge-frame.supervisor", 6);
}
impl Message for JudgeFrame {
const SCHEMA: SchemaId = SchemaId::literal("agent", "onejudge-frame.judge", 6);
}
impl Message for AssessFrame {
const SCHEMA: SchemaId = SchemaId::literal("agent", "onejudge-frame.assess", 6);
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "op", rename_all = "lowercase")]
pub enum Frame {
Respond(RespondFrame),
User(UserFrame),
Supervisor(SupervisorFrame),
Judge(JudgeFrame),
Assess(AssessFrame),
}
#[must_use]
pub fn schemas() -> Vec<(SchemaId, Schema)> {
vec![
frame_schema::<RespondFrame>(op::RESPOND),
frame_schema::<UserFrame>(op::USER),
frame_schema::<SupervisorFrame>(op::SUPERVISOR),
frame_schema::<JudgeFrame>(op::JUDGE),
frame_schema::<AssessFrame>(op::ASSESS),
]
}
fn frame_schema<F: Message>(word: &str) -> (SchemaId, Schema) {
let mut document = schemars::schema_for!(F).to_value();
if let Some(object) = document.as_object_mut() {
stamp_frame_op(object, word);
if let Some(Value::Array(variants)) = object.get_mut("oneOf") {
for variant in variants {
if let Some(variant) = variant.as_object_mut() {
stamp_frame_op(variant, word);
}
}
}
}
let schema = Schema::try_from(document).unwrap_or_else(|_| schemars::schema_for!(F));
(F::SCHEMA, schema)
}
fn stamp_frame_op(object: &mut Map<String, Value>, word: &str) {
let mut properties = Map::new();
properties.insert(
OP.to_owned(),
serde_json::json!({
"type": "string",
"const": word,
"description": "The operation this frame asks for."
}),
);
if let Some(Value::Object(existing)) = object.get("properties") {
properties.extend(existing.clone());
}
object.insert("properties".to_owned(), Value::Object(properties));
let mut required = vec![Value::String(OP.to_owned())];
if let Some(Value::Array(existing)) = object.get("required") {
required.extend(existing.iter().cloned());
}
object.insert("required".to_owned(), Value::Array(required));
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct Usage {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub input_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub output_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cache_read_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cache_write_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cost_usd: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SupervisorResponse {
Completed {
reason: String,
},
Continue {
message: String,
reason: String,
},
}
impl Serialize for SupervisorResponse {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeMap;
let mut out = serializer.serialize_map(None)?;
match self {
Self::Completed { reason } => {
out.serialize_entry(field::COMPLETION, &true)?;
out.serialize_entry(field::REASON, reason)?;
}
Self::Continue { message, reason } => {
out.serialize_entry(field::COMPLETION, &false)?;
out.serialize_entry(field::MESSAGE, message)?;
out.serialize_entry(field::REASON, reason)?;
}
}
out.end()
}
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct SupervisorWire {
completion: bool,
#[serde(default)]
message: Option<String>,
#[serde(default)]
reason: String,
#[serde(default)]
#[allow(
dead_code,
reason = "protocol v6 includes usage in supervisor responses even though routing does not inspect it"
)]
usage: Option<Usage>,
}
impl<'de> Deserialize<'de> for SupervisorResponse {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let wire = SupervisorWire::deserialize(deserializer)?;
match (wire.completion, wire.message) {
(true, None) if !wire.reason.trim().is_empty() => Ok(Self::Completed {
reason: wire.reason,
}),
(true, _) => Err(serde::de::Error::custom(
"a completed ruling requires a non-empty `reason` and forbids `message`",
)),
(false, Some(message)) if !message.trim().is_empty() => Ok(Self::Continue {
message,
reason: wire.reason,
}),
(false, _) => Err(serde::de::Error::custom(
"a continuing ruling requires a non-empty `message`",
)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum JudgeValue {
Bool(bool),
Number(f64),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct JudgeResponse {
pub value: JudgeValue,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<Usage>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct RespondResponse {
pub message: String,
#[serde(default)]
pub done: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<Usage>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub events: Option<Vec<ToolEvent>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct UserResponse {
pub message: String,
#[serde(default)]
pub stop: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<Usage>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct AssessResponse {
pub text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<Usage>,
}
pub mod transcript {
pub const METHOD: &str = "method";
pub const RESULT: &str = "result";
pub const PARAMS: &str = "params";
pub const TURN: &str = "turn";
pub const STATUS: &str = "status";
pub const FAILED: &str = "failed";
pub const ERROR: &str = "error";
pub const CODEX_HOME: &str = "codexHome";
pub const CODEX_ERROR_INFO: &str = "codexErrorInfo";
pub const CODE: &str = "code";
pub const MESSAGE: &str = "message";
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TurnError {
pub codex_error_info: Option<String>,
pub code: Option<String>,
pub message: Option<String>,
}
impl TurnError {
fn of(recorded: &Map<String, Value>) -> Self {
let text = |key: &str| recorded.get(key).and_then(Value::as_str).map(str::to_owned);
Self {
codex_error_info: text(transcript::CODEX_ERROR_INFO),
code: text(transcript::CODE),
message: text(transcript::MESSAGE),
}
}
}
pub const CAUSE_LIMIT: usize = 80;
pub const NO_CAUSE_RECORDED: &str = "no cause recorded";
pub const NO_ASSISTANT_CONTENT: &str = "the turn produced no assistant message";
pub const UNIDENTIFIED_HARNESS: &str = "an unidentified harness";
pub const CODEX_IDENTITY: &str = "codex";
pub const CODEX_ALTERNATE_IDENTITY: &str = "codex:alternate";
pub const CODEX_ALT_HOME_ENV: &str = "ORCHESTRATOR_CODEX_ALT_HOME";
#[must_use]
pub fn transcript_frames(spoken: &str) -> Option<Vec<Value>> {
let mut frames = Vec::new();
for line in spoken.lines().filter(|line| !line.trim().is_empty()) {
match serde_json::from_str::<Value>(line) {
Ok(frame @ Value::Object(_)) => frames.push(frame),
_ => return None,
}
}
(!frames.is_empty()).then_some(frames)
}
#[must_use]
pub fn lost_turn_error(frames: &[Value]) -> Option<TurnError> {
for frame in frames.iter().rev() {
let params = frame.get(transcript::PARAMS);
let turn = params.and_then(|params| params.get(transcript::TURN));
let failed = turn
.and_then(|turn| turn.get(transcript::STATUS))
.and_then(Value::as_str)
== Some(transcript::FAILED);
if failed {
return Some(
turn.and_then(|turn| turn.get(transcript::ERROR))
.and_then(Value::as_object)
.map_or_else(TurnError::default, TurnError::of),
);
}
if frame.get(transcript::METHOD).and_then(Value::as_str) == Some(transcript::ERROR) {
return Some(
params
.and_then(|params| params.get(transcript::ERROR))
.and_then(Value::as_object)
.map_or_else(TurnError::default, TurnError::of),
);
}
}
None
}
#[must_use]
pub fn clipped(cause: &str) -> String {
let mut collapsed = String::with_capacity(cause.len());
let mut gap = false;
for character in cause.chars() {
if character.is_whitespace() || character.is_control() {
gap = true;
} else {
if gap && !collapsed.is_empty() {
collapsed.push(' ');
}
gap = false;
collapsed.push(character);
}
}
if collapsed.chars().count() <= CAUSE_LIMIT {
return collapsed;
}
let kept: String = collapsed.chars().take(CAUSE_LIMIT - 1).collect();
format!("{}\u{2026}", kept.trim_end())
}
#[must_use]
pub fn cause_of(error: &TurnError) -> String {
[&error.codex_error_info, &error.code, &error.message]
.into_iter()
.flatten()
.map(|stated| clipped(stated))
.find(|named| !named.is_empty())
.unwrap_or_else(|| NO_CAUSE_RECORDED.to_owned())
}
#[must_use]
pub fn identity_in(frames: &[Value], alternate_home: Option<&str>) -> &'static str {
for frame in frames {
let Some(home) = frame
.get(transcript::RESULT)
.and_then(|result| result.get(transcript::CODEX_HOME))
.and_then(Value::as_str)
.filter(|home| !home.trim().is_empty())
else {
continue;
};
let alternate = alternate_home.is_some_and(|alternate| {
Path::new(alternate)
.components()
.eq(Path::new(home).components())
});
return if alternate {
CODEX_ALTERNATE_IDENTITY
} else {
CODEX_IDENTITY
};
}
UNIDENTIFIED_HARNESS
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Turn {
Taken,
Lost {
cause: String,
identity: &'static str,
transcript_chars: Option<usize>,
},
}
#[must_use]
pub fn liveness(messages: &[ConversationMessage], alternate_home: Option<&str>) -> Turn {
let Some(said) = messages
.iter()
.filter(|message| message.role == Role::Assistant && !message.content.trim().is_empty())
.map(|message| message.content.as_str())
.next_back()
else {
return Turn::Lost {
cause: NO_ASSISTANT_CONTENT.to_owned(),
identity: UNIDENTIFIED_HARNESS,
transcript_chars: None,
};
};
let Some(frames) = transcript_frames(said) else {
return Turn::Taken;
};
match lost_turn_error(&frames) {
Some(error) => Turn::Lost {
cause: cause_of(&error),
identity: identity_in(&frames, alternate_home),
transcript_chars: Some(said.chars().count()),
},
None => Turn::Taken,
}
}
pub const RUN_IN_TASK_OPENS: &str = "onepipeline run `";
pub const RUN_ENV: &str = "ONEPIPELINE_RUN_ID";
pub const ASKER_ENV: &str = crate::channel::ASKER_ENV;
pub const SESSION_ENV: &str = "ONEPIPELINE_SERVE_SESSION_SECONDS";
#[must_use]
pub fn run_in_task(task: &str) -> Option<&str> {
let opening = task.lines().next()?;
let after = &opening[opening.find(RUN_IN_TASK_OPENS)? + RUN_IN_TASK_OPENS.len()..];
let run = &after[..after.find('`')?];
(!run.is_empty()).then_some(run)
}
#[must_use]
pub fn is_safe_run(run: &str) -> bool {
run.bytes()
.next()
.is_some_and(|first| first.is_ascii_alphanumeric() || first == b'_')
&& run
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'.' | b'-'))
}
pub const SURFACE_KIND_OF_A_FAILED_TURN: &str = "monitor-failed";
pub const SURFACE_KIND_OF_A_COMPLETION: &str = "monitor-completion";
pub const TURN_TAKEN_ACKNOWLEDGED: &str = "Your turn was taken and no planner surface was raised for it: prose reaches nobody. A report reaches the planner only as a `finding` op in a reply envelope, which arrives once and carries the node it is about. Your next turn opens after the graph's hold, and reads the detailed stream from your cursor: everything that landed since the timestamp `monitor.cursor` holds, not the last few lines.";
pub const TURN_TAKEN_REASON: &str =
"the monitor took its turn; a report reaches the planner as a finding";
pub const SCORE_ASKED: &str = "The monitor's conversation has ended and onejudge is scoring that conversation against the monitor's own completion bar. You are this member's judge side, so the score is yours: did this watch meet the bar quoted below? Reply `completion: true` if it did, `false` if it did not. THE RUN IS NOT BLOCKED ON THIS and nothing waits for you: the question is non-blocking, no answer is read as `false`, and the run settles either way.\n\n{criterion}";
pub const SCORE_UNEXPLAINED: &str =
"the planner ruled on this member's completion bar over the channel";
pub const ANSWERED_THE_ENGINE: &str = "The planner's answer to this question was a live graph edit addressed to the engine, not a ruling on your watch: {named}. It was routed to the command path when it was sent, so nothing here re-applies it. Your question has not been answered — keep watching the detailed stream and raise what you find.";
pub const PLANNER_ALSO_SAID: &str = " The planner also said: {said}";
pub const UNNAMED_EDIT: &str = "an unnamed edit";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LiveEdit {
pub named: String,
pub said: String,
}
#[must_use]
pub fn live_edit(envelope: &Value) -> Option<LiveEdit> {
let fields = envelope.as_object()?;
if fields.get(field::COMPLETION).is_some_and(Value::is_boolean) {
return None;
}
let commands = fields.get(field::COMMANDS)?.as_array()?;
if commands.is_empty() {
return None;
}
let named: Vec<String> = commands
.iter()
.map(|command| {
let text = |key: &str| {
command
.get(key)
.and_then(Value::as_str)
.map(str::trim)
.filter(|text| !text.is_empty())
};
match (text(field::EDIT_OP), text(field::EDIT_TARGET)) {
(Some(op), Some(target)) => format!("{op} {target}"),
(Some(op), None) => op.to_owned(),
(None, Some(target)) => format!("{UNNAMED_EDIT} {target}"),
(None, None) => UNNAMED_EDIT.to_owned(),
}
})
.collect();
let said: Vec<&str> = [field::MESSAGE, field::REASON]
.into_iter()
.filter_map(|key| fields.get(key).and_then(Value::as_str))
.map(str::trim)
.filter(|text| !text.is_empty())
.collect();
Some(LiveEdit {
named: clipped(&named.join(", ")),
said: said.join(" "),
})
}
pub type AboutCheck = Box<dyn Fn(&str, &Address) -> Result<(), String> + Send>;
pub struct Onejudge {
run_env: EnvName,
run_from_env: Option<String>,
alternate_home: Option<String>,
about_check: Option<AboutCheck>,
}
impl std::fmt::Debug for Onejudge {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Onejudge")
.field("run_env", &self.run_env)
.field("run_from_env", &self.run_from_env)
.field("alternate_home", &self.alternate_home)
.field("about_check", &self.about_check.is_some())
.finish()
}
}
impl Default for Onejudge {
fn default() -> Self {
Self {
run_env: RUN_ENV
.parse()
.expect("RUN_ENV is a valid environment name"),
run_from_env: None,
alternate_home: None,
about_check: None,
}
}
}
impl Onejudge {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_run_env(mut self, name: EnvName, value: Option<String>) -> Self {
self.run_env = name;
self.run_from_env = value;
self
}
#[must_use]
pub fn with_alternate_home(mut self, home: Option<String>) -> Self {
self.alternate_home = home;
self
}
#[must_use]
pub fn with_about_check(mut self, check: AboutCheck) -> Self {
self.about_check = Some(check);
self
}
fn run(&self, task: Option<&str>) -> Result<String, CodecFailure> {
let named = task
.and_then(run_in_task)
.map(str::to_owned)
.or_else(|| self.run_from_env.clone().filter(|run| !run.is_empty()))
.ok_or_else(|| {
CodecFailure::Refused(format!(
"the frame names no run — no task opening `{RUN_IN_TASK_OPENS}<run>`` — and {} names none, so there is no run to serve",
self.run_env
))
})?;
if !is_safe_run(&named) {
return Err(CodecFailure::Refused(format!(
"the run is named {named:?}, which is not a run id: one word of letters, digits, `_`, `.` and `-`"
)));
}
Ok(named)
}
fn about(&self, run: &str, session: &ServeSession<'_>) -> Result<(), CodecFailure> {
match (&self.about_check, &session.options().about) {
(Some(check), Some(about)) => check(run, about).map_err(|why| {
CodecFailure::Refused(format!(
"the questions of run {run} are about {about}, which it refuses: {why}"
))
}),
_ => Ok(()),
}
}
fn supervise(
&self,
frame: &SupervisorFrame,
session: &mut ServeSession<'_>,
) -> Result<Value, CodecFailure> {
let run = self.run(Some(&frame.task))?;
self.about(&run, session)?;
match liveness(&frame.messages, self.alternate_home.as_deref()) {
Turn::Taken => serde_json::to_value(SupervisorResponse::Continue {
message: TURN_TAKEN_ACKNOWLEDGED.to_owned(),
reason: TURN_TAKEN_REASON.to_owned(),
})
.map_err(|failure| CodecFailure::Failed(failure.to_string())),
Turn::Lost {
cause,
identity,
transcript_chars,
} => {
let told = match transcript_chars {
Some(chars) => format!(
"It said nothing, so there is nothing to answer; its {chars}-character transcript is not repeated here."
),
None => "It said nothing, so there is nothing to answer.".to_owned(),
};
let message =
format!("monitor turn failed: {cause} on {identity}. {told} Run {run}.");
session
.raise(serde_json::json!({
"kind": SURFACE_KIND_OF_A_FAILED_TURN,
"message": message,
"source": PROPOSAL,
"blocking": false,
}))
.map_err(|failure| {
CodecFailure::Failed(format!(
"the monitor's turn on run {run} was lost ({cause} on {identity}), and the `{SURFACE_KIND_OF_A_FAILED_TURN}` surface saying so could not be raised: {failure}"
))
})?;
Err(CodecFailure::Failed(format!(
"the monitor's turn on run {run} was lost: {cause} on {identity}; a `{SURFACE_KIND_OF_A_FAILED_TURN}` surface says so on {}",
session.queue()
)))
}
}
}
fn score(
&self,
frame: &JudgeFrame,
session: &mut ServeSession<'_>,
) -> Result<Value, CodecFailure> {
let criterion = match frame {
JudgeFrame::Boolean { criterion, .. } => criterion,
JudgeFrame::Numeric { .. } => {
return Err(CodecFailure::Refused(
"a `numeric` score is not one this codec gives: a planner rules with a `completion` boolean, which is no score on a scale; it scores `boolean` criteria alone"
.to_owned(),
));
}
}
.trim();
if criterion.is_empty() {
return Err(CodecFailure::Refused(
"the `judge` frame's criterion is blank, so there is nothing to put to the planner"
.to_owned(),
));
}
let run = self.run(None)?;
self.about(&run, session)?;
let window = session.options().reply_window.as_secs();
let (correlation, answer) = session
.ask(
serde_json::json!({
"kind": SURFACE_KIND_OF_A_COMPLETION,
"message": SCORE_ASKED.replace("{criterion}", criterion),
"source": PROPOSAL,
}),
false,
)
.map_err(|failure| {
CodecFailure::Failed(format!(
"the completion criterion of run {run} could not be put to the planner: {failure}"
))
})?;
let unsatisfied = |why: String| JudgeResponse {
value: JudgeValue::Bool(false),
reason: Some(why),
usage: None,
};
let score = match answer {
Answer::Reply(record) => {
let envelope = record
.get(field::REPLY)
.filter(|envelope| envelope.is_object())
.unwrap_or(&record);
if let Some(edit) = live_edit(envelope) {
let mut told = ANSWERED_THE_ENGINE.replace("{named}", &edit.named);
if !edit.said.is_empty() {
told.push_str(&PLANNER_ALSO_SAID.replace("{said}", &edit.said));
}
unsatisfied(told)
} else if let Some(completion) =
envelope.get(field::COMPLETION).and_then(Value::as_bool)
{
let said = [field::REASON, field::MESSAGE]
.into_iter()
.filter_map(|key| envelope.get(key).and_then(Value::as_str))
.map(str::trim)
.find(|text| !text.is_empty())
.unwrap_or(SCORE_UNEXPLAINED);
JudgeResponse {
value: JudgeValue::Bool(completion),
reason: Some(said.to_owned()),
usage: None,
}
} else {
return Err(CodecFailure::Failed(format!(
"the answer echoing {correlation} is not a ruling — it carries no boolean `completion` — so no score was relayed: {}",
clipped(&envelope.to_string())
)));
}
}
Answer::Timeout => unsatisfied(format!(
"no ruling on this member's completion bar arrived within {window} seconds, so it is scored unsatisfied; the question stands as {correlation}"
)),
Answer::Abandoned => unsatisfied(format!(
"the question putting this member's completion bar to the planner ({correlation}) was abandoned before anyone ruled, so it is scored unsatisfied"
)),
Answer::Refused(refusal) => {
return Err(CodecFailure::Failed(format!(
"the ruling on run {run}'s completion bar was refused, so no score was relayed: {refusal}"
)))
}
};
serde_json::to_value(score).map_err(|failure| CodecFailure::Failed(failure.to_string()))
}
}
fn refused_op(word: &str) -> CodecFailure {
CodecFailure::Refused(format!(
"`{word}` is not an operation this codec serves: it serves `{}` and `{}`, since a planner rules with a `completion` boolean and nothing else",
op::SUPERVISOR,
op::JUDGE
))
}
impl Codec for Onejudge {
fn name(&self) -> &CodecName {
&CODEC_NAME
}
fn answer(
&mut self,
frame: &str,
session: &mut ServeSession<'_>,
) -> Result<Value, CodecFailure> {
let mut parsed: Value = serde_json::from_str(frame).map_err(|failure| {
CodecFailure::Refused(format!("the frame is not JSON: {failure}"))
})?;
let word = parsed
.get(OP)
.and_then(Value::as_str)
.ok_or_else(|| {
CodecFailure::Refused(format!(
"the frame names no `{OP}`; a onejudge protocol v{PROTOCOL_VERSION} frame names one of {}",
op::ALL.join(", ")
))
})?
.to_owned();
if !op::SERVED.contains(&word.as_str()) {
return Err(refused_op(&word));
}
parsed
.as_object_mut()
.expect("a frame naming an op is an object")
.remove(OP);
let malformed = |failure| {
CodecFailure::Refused(format!(
"the `{word}` frame is not a onejudge protocol v{PROTOCOL_VERSION} frame: {failure}"
))
};
match word.as_str() {
op::SUPERVISOR => serde_json::from_value::<SupervisorFrame>(parsed)
.map_err(malformed)
.and_then(|frame| self.supervise(&frame, session)),
op::JUDGE => serde_json::from_value::<JudgeFrame>(parsed)
.map_err(malformed)
.and_then(|frame| self.score(&frame, session)),
_ => Err(refused_op(&word)),
}
}
}