Watch
0
0
Fork
You've already forked hyperhive
0

review classifier: the most classifications a run carries is a setting

The limit was a constant of ten in swarm-wire, so a repo with eleven rules
could not be reviewed and nothing short of a rebuild changed it. It is now
`max_classifications` in the classifier settings, beside
`max_concurrent_reviews`: 1 to 1000, ten until set, and a document stored
before the field decodes with the default. The classifier settings form in
the swarm UI gets a "rules per review" field, and the try-out's add button
stops at the saved limit instead of a hard-coded ten.

The controller checks a try-out, and a requested review's rules file, against
the setting where the run starts. The rules file itself may hold up to the
1000 the setting allows, so raising the limit needs no change to a repo.
A review whose rules exceed the limit posts a no-verdict comment saying so.

A run's reply deadline was a constant that covered ten classifications, a
minute plus three each. It is now computed from the run's own size, so a
bigger run waits as long as it needs and a default-sized one still waits 31
minutes. The classifier's answer window in the queue permissions covers the
biggest run the setting allows.
This commit is contained in:
iris 2026-11-07 10:37:52 +01:00 • committed by mara
commit cffe8895df
2 changed files with 400 additions and 101 deletions

View file

@ -8,6 +8,7 @@ import {
changed,
draftOf,
maxValue,
rulesValue,
priceValue,
settingsProblem,
settingsSummary,
@ -18,6 +19,7 @@ const apiKey = {
model: "haiku",
effort: null,
max_concurrent_reviews: 2,
max_classifications: 10,
runtime: "api-key",
acp: null,
};
@ -57,6 +59,7 @@ test("the API key request leaves the provider out, so the stored one stays", ()
model: "haiku",
effort: null,
max_concurrent_reviews: 2,
max_classifications: 10,
runtime: "api-key",
});
assert.equal("acp" in body, false);
@ -119,14 +122,33 @@ test("the reviews limit and a price parse as the form needs", () => {
assert.equal(priceValue("1e3"), null);
});
test("the summary names the runtime, model and how many run at once", () => {
assert.equal(settingsSummary(apiKey), "api-key · haiku · 2 at once");
test("the summary names the runtime, model, how many run at once and the rules a run takes", () => {
assert.equal(
settingsSummary(apiKey),
"api-key · haiku · 2 at once · up to 10 rules",
);
});
test("the rules limit parses and checks as the form needs", () => {
assert.equal(rulesValue("1"), 1);
assert.equal(rulesValue(" 1000 "), 1000);
assert.equal(rulesValue("0"), null);
assert.equal(rulesValue("1001"), null);
assert.equal(rulesValue("2.5"), null);
assert.equal(rulesValue(""), null);
const d = { ...draftOf(apiKey), maxRules: "5000" };
assert.match(settingsProblem(d), /rules per review.*1 to 1000/);
assert.equal(
toSettings({ ...draftOf(apiKey), maxRules: "40" }).max_classifications,
40,
);
assert.equal(changed({ ...draftOf(apiKey), maxRules: "11" }, apiKey), true);
});
test("the summary names an effort when one is set", () => {
assert.equal(
settingsSummary({ ...apiKey, effort: "high" }),
"api-key · haiku · high effort · 2 at once",
"api-key · haiku · high effort · 2 at once · up to 10 rules",
);
});
@ -141,6 +163,6 @@ test("under ACP the summary names the provider's model, not the claude one", ()
prices: { input: 1, output: 2 },
},
}),
"acp · glm · 2 at once",
"acp · glm · 2 at once · up to 10 rules",
);
});

View file

@ -5,7 +5,6 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import {
MAX_CLASSIFICATIONS,
addProblem,
draftFromRule,
draftProblem,
@ -19,10 +18,16 @@ import {
toBody,
} from "../src/pages/review-tryout/TryOutTypes.ts";
test("a row is offered until the endpoint's cap is reached", () => {
assert.equal(addProblem(1), null);
assert.equal(addProblem(MAX_CLASSIFICATIONS - 1), null);
assert.match(addProblem(MAX_CLASSIFICATIONS), /at most 10 classifications/);
test("a row is offered until the settings' cap is reached", () => {
assert.equal(addProblem(1, 10), null);
assert.equal(addProblem(9, 10), null);
assert.match(addProblem(10, 10), /at most 10 classifications/);
assert.match(addProblem(25, 25), /at most 25 classifications/);
});
test("with the cap unknown, every row is offered and the endpoint stays the gate", () => {
assert.equal(addProblem(10, null), null);
assert.equal(addProblem(500, null), null);
});
// A row ready to send, with `over` replacing any of its fields.