Watch
0
0
Fork
You've already forked hyperhive
0

swarm-controller: document every try-out status and row shape in the OpenAPI

The routes' annotations now list every 400 reason (including the JSON
shape rejections), the 503 causes, the 31-minute running window, the
404 after a controller restart, and that an errored row reads
not-applicable with certainty 0 and an empty reason, so the page keys
off error, and that overall can be approve beside errored rows.
This commit is contained in:
atlas 2026-10-12 04:17:18 +02:00 • committed by mara
commit ed15105e6f
2 changed files with 25 additions and 12 deletions

View file

@ -53,6 +53,9 @@ pub(crate) struct TryOutAccepted {
id: String,
}
/// `running` lasts at most the run deadline, 31 minutes: ten
/// classifications at three minutes each, one after another, and a minute
/// more.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, ToSchema)]
#[serde(rename_all = "lowercase")]
pub(crate) enum RunState {
@ -70,10 +73,12 @@ pub(crate) struct TryOutStatus {
/// Set when `done`.
#[serde(skip_serializing_if = "Option::is_none")]
rows: Option<Vec<Row>>,
/// Set when `done`.
/// Set when `done`. Rows with an `error` don't count, so `approve` can
/// stand beside them.
#[serde(skip_serializing_if = "Option::is_none")]
overall: Option<Overall>,
/// Set when `failed`.
/// Set when `failed`: no API key, no classification answered, no
/// classifier listening, or no answer within the run deadline.
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<String>,
}
@ -225,16 +230,19 @@ impl RunSink for async_nats::Client {
responses(
(status = 202, description = "the run started; read it at \
`/api/review-classifier/try-out/{id}`", body = TryOutAccepted),
(status = 400, description = "the body is not a valid run: a repo that is not \
owner/name, no PR number, no classification or more than ten, a classification \
without a name or description, two with one name, or a threshold over 100 \
(problem+json)", body = String),
(status = 400, description = "the body is not a valid run: not JSON or not this \
shape (an unknown polarity, a negative or non-integer number, a threshold over \
255); a repo that is not owner/name with segments of `[A-Za-z0-9._-]` other than \
`.` and `..`; a PR number of 0; no classification or more than ten; a \
classification whose trimmed name or description is empty; two with one trimmed \
name; or a threshold over 100 (problem+json)", body = String),
(status = 401, description = "the request carries no `Remote-User`, so there is no \
operator to name (problem+json)", body = String),
(status = 404, description = "the forge knows no such repo or PR (problem+json)",
body = String),
(status = 503, description = "no forge access or no swarm queue on this host, or the \
forge failed to answer (problem+json)", body = String),
(status = 503, description = "this host has no forge token yet, no swarm queue \
configured, or a queue that is not connected; or the forge failed or did not \
answer within two minutes (problem+json)", body = String),
),
tag = "review-classifier"
)]
@ -291,9 +299,11 @@ pub(crate) async fn post_try_out(
path = "/api/review-classifier/try-out/{id}",
params(("id" = String, Path, description = "the id the POST answered with")),
responses(
(status = 200, description = "the run, running or settled", body = TryOutStatus),
(status = 404, description = "no run has this id, or its result expired \
(problem+json)", body = String),
(status = 200, description = "the run: `running` for at most 31 minutes, then \
`done` or `failed`", body = TryOutStatus),
(status = 404, description = "no run has this id: it never existed, its result \
expired an hour after it settled, or the controller restarted since the POST, \
which loses every run (problem+json)", body = String),
),
tag = "review-classifier"
)]

View file

@ -109,7 +109,10 @@ pub struct Row {
/// polarity. `false` for every row that is not counted.
pub passed: bool,
pub reason: String,
/// Why this classification has no answer. Such a row is never counted.
/// Why this classification has no answer: a runtime error, a timeout, or
/// an answer that did not validate. Such a row reads `not-applicable`,
/// certainty 0 and an empty reason, and is never counted; key off this
/// field, not off the verdict.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}