diff --git a/docs/swarm/ui.md b/docs/swarm/ui.md
index 34e69c3d..a4f50251 100644
--- a/docs/swarm/ui.md
+++ b/docs/swarm/ui.md
@@ -55,6 +55,14 @@ Give it a repo as `owner/name` and a PR number, then write a classification: a
name, a description, a polarity (positive or negative) and a threshold from 0
to 100. **add a classification** adds more, and **run** starts the run.
+**load the repo's rules** fills the classifications from the rules file on the
+repo's default branch, so you can run the repo's own rules or start from them
+and edit. It reads the file each time, so a rule merged a minute ago shows up.
+The page names the file and the commit it read, and asks before it replaces
+classifications you have written. If the repo has no rules file, the file
+doesn't parse, or it holds more than ten rules, the page shows the reason and
+leaves your classifications as they were.
+
A run takes a minute or more, and up to half an hour for ten classifications.
The page says it's running and shows how long it has been, then shows the review's table: for each classification the verdict,
the model's certainty, the threshold, whether the verdict counted and the
diff --git a/frontend/packages/swarm-ui/src/pages/review-tryout/ReviewTryOutPage.css b/frontend/packages/swarm-ui/src/pages/review-tryout/ReviewTryOutPage.css
index 1d51e603..3d9b683d 100644
--- a/frontend/packages/swarm-ui/src/pages/review-tryout/ReviewTryOutPage.css
+++ b/frontend/packages/swarm-ui/src/pages/review-tryout/ReviewTryOutPage.css
@@ -20,6 +20,14 @@
.ui-tryout-target > * {
flex: 1 1 14em;
}
+.ui-tryout-target > button {
+ flex: none;
+ align-self: flex-end;
+}
+.ui-tryout-loaded {
+ margin: 0;
+ color: var(--muted);
+}
.ui-tryout-classification {
display: flex;
flex-direction: column;
diff --git a/frontend/packages/swarm-ui/src/pages/review-tryout/ReviewTryOutPage.tsx b/frontend/packages/swarm-ui/src/pages/review-tryout/ReviewTryOutPage.tsx
index 8c134dfa..017d21db 100644
--- a/frontend/packages/swarm-ui/src/pages/review-tryout/ReviewTryOutPage.tsx
+++ b/frontend/packages/swarm-ui/src/pages/review-tryout/ReviewTryOutPage.tsx
@@ -6,6 +6,9 @@
// `202 {id}`, and polls `GET .../try-out/{id}` until the run is `done` or
// `failed`. A run takes a minute or more, so the page says it is running.
//
+// **load the repo's rules** fills the rows from the rules on the repo's default
+// branch (`GET /api/review-classifier/rules`), to run as they are or to edit.
+//
// The inputs stay as typed after a run, so the operator can edit and run
// again. The page keeps nothing you typed and no result, and posts nothing to
// the forge; the only thing the browser remembers is the result table's
@@ -14,22 +17,27 @@ import { useEffect, useState } from "preact/hooks";
import { ApiErrorPanel } from "@hive/shared/api-error-panel.js";
import { readApiError, type ProblemDetails } from "@hive/shared/api-error.js";
import { Button } from "../../ui/button/Button.js";
+import { ConfirmDialog } from "../../ui/confirm-dialog/ConfirmDialog.js";
import { Panel } from "../../ui/panel/Panel.js";
import { TextField } from "../../ui/text-field/TextField.js";
import { ClassificationEditor } from "./ClassificationEditor.js";
import { TryOutResult } from "./TryOutResult.js";
import {
+ draftFromRule,
draftProblem,
duplicateName,
+ hasTypedText,
prNumber,
repoProblem,
toBody,
type ClassificationDraft,
+ type RulesAnswer,
type TryOutResult as Result,
} from "./TryOutTypes.js";
import "./ReviewTryOutPage.css";
const TRY_OUT = "/api/review-classifier/try-out";
+const RULES = "/api/review-classifier/rules";
const POLL_MS = 2000;
// More classifications than this are not offered a row. It is the try-out
// endpoint's own cap, which answers a longer list with a 400 that the page
@@ -61,6 +69,14 @@ export function ReviewTryOutPage() {
// Whole seconds since the run started, so a long wait shows it is alive.
const [elapsed, setElapsed] = useState(0);
+ const [loadingRules, setLoadingRules] = useState(false);
+ const [rulesError, setRulesError] = useState
+ Loaded {loaded.classifications.length} from{" "}
+ {loaded.path} on {loaded.branch} of{" "}
+ {loaded.repo}, read at commit{" "}
+ {loaded.commit.slice(0, 12)}. Edit them below, then
+ run.
+
+ Loading the rules of {repo.trim()} replaces the classifications you + have written below. +
+