swarm-controller: post the review classifier's verdict on a review request
Requesting the forge account `review-classifier` as a reviewer now runs the repo's rules against the PR and posts the result as that account's review, on the head commit the run read. A push starts no run; only a fresh review request does. - An instance-wide `pull_request_review_request` system hook delivers to `/webhook/forge/review-request`. A request for any other reviewer, or a removed request, does nothing. - The controller reads the PR, loads the rules with the same reader as `GET /api/review-classifier/rules`, sends one request on the try-out subject, and posts APPROVED or REQUEST_CHANGES with the table as the body. - With no clean verdict (PR or rules don't load, the run fails, or any classification has no answer) it posts a COMMENT saying why, never an approval. The event is one function, `no_clean_verdict_event`. - Table cells are flattened, markup-escaped, and every `@` gets a zero-width space, so a model's reason can't break the table or mention anyone. - At most two requested reviews run at once; the rest wait in memory. - The account is minted the way hive accounts are: a five-minute pass queues a `MintClassifierForgeToken` node when the token at `swarm/services/review-classifier/forge/token` is missing or stale. The token is `write:repository`. The controller's bao policy gains that leaf (create/read/update), pinned in module-eval bao-grants. The classifier never sees the token. The account gets no repo access automatically; a repo admin adds it by hand. A re-request retires the account's earlier review: Forgejo clears `official` on a reviewer's earlier reviews when it is requested again (models/issues/review.go AddReviewRequest, v16.0.5), and approvals and rejection blocks count official reviews only. Refs #5137
This commit is contained in:
parent
ad47664e01
commit
a669cd96b6
17 changed files with 1156 additions and 50 deletions
|
|
@ -14,6 +14,8 @@
|
|||
//!
|
||||
//! The operator is the `Remote-User` header the gateway sets, as for
|
||||
//! `agent_input`; the classifier names them in its journal line.
|
||||
//!
|
||||
//! [`review`] runs the same rules on a review request and posts the result.
|
||||
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
use std::fmt::Write as _;
|
||||
|
|
@ -35,6 +37,8 @@ use utoipa::{IntoParams, ToSchema};
|
|||
use crate::forge::{self, DefaultBranchFiles, PullForReview};
|
||||
use crate::{AppState, error_problem};
|
||||
|
||||
pub(crate) mod review;
|
||||
|
||||
/// How long a finished run's result stays readable.
|
||||
pub(crate) const RESULT_TTL: Duration = Duration::from_hours(1);
|
||||
|
||||
|
|
@ -707,6 +711,12 @@ pub(crate) mod tests {
|
|||
files: Vec<(&'static str, String)>,
|
||||
}
|
||||
|
||||
impl Fake {
|
||||
pub(crate) fn new(reply: Reply, files: Vec<(&'static str, String)>) -> Self {
|
||||
Self { reply, files }
|
||||
}
|
||||
}
|
||||
|
||||
impl PullSource for Fake {
|
||||
async fn pull(
|
||||
&self,
|
||||
|
|
@ -1017,6 +1027,15 @@ pub(crate) mod tests {
|
|||
})
|
||||
}
|
||||
|
||||
/// A JSON rules file holding the one `datetime` classification.
|
||||
pub(crate) fn rules_json() -> (&'static str, String) {
|
||||
let [_, json] = RULES_PATHS;
|
||||
(
|
||||
json,
|
||||
rules_value(&serde_json::json!([datetime()]), &[]).to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
/// `file` as both formats, each with its path.
|
||||
fn both_formats(file: &serde_json::Value) -> [(&'static str, String); 2] {
|
||||
let [yml, json] = RULES_PATHS;
|
||||
|
|
|
|||
Loading…
Reference in a new issue