mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-23 18:56:38 +00:00
125 lines
3.4 KiB
JavaScript
125 lines
3.4 KiB
JavaScript
import fs from "node:fs/promises";
|
||
|
||
const candidates = JSON.parse(await fs.readFile("candidates.json", "utf8"));
|
||
|
||
function isMaintainerRole(role) {
|
||
return ["MEMBER", "OWNER", "COLLABORATOR"].includes(role || "");
|
||
}
|
||
|
||
function preScore(candidate) {
|
||
let score = 0;
|
||
const hardSignals = [];
|
||
const contradictions = [];
|
||
|
||
for (const t of candidate.timeline) {
|
||
const sourceIssue = t.source?.issue;
|
||
|
||
if (t.event === "cross-referenced" && sourceIssue?.pull_request?.html_url) {
|
||
hardSignals.push({
|
||
type: "merged_pr",
|
||
url: sourceIssue.html_url
|
||
});
|
||
score += 40; // provisional until PR merged state is verified
|
||
}
|
||
|
||
if (["referenced", "connected"].includes(t.event)) {
|
||
score += 10;
|
||
}
|
||
}
|
||
|
||
for (const c of candidate.comments) {
|
||
const body = c.body.toLowerCase();
|
||
|
||
if (
|
||
isMaintainerRole(c.author_association) &&
|
||
/\b(fixed|resolved|duplicate|superseded|closing)\b/.test(body)
|
||
) {
|
||
score += 25;
|
||
hardSignals.push({
|
||
type: "maintainer_comment",
|
||
url: c.html_url
|
||
});
|
||
}
|
||
|
||
if (/\b(still broken|still happening|not fixed|reproducible)\b/.test(body)) {
|
||
score -= 50;
|
||
contradictions.push({
|
||
type: "later_unresolved_comment",
|
||
url: c.html_url
|
||
});
|
||
}
|
||
}
|
||
|
||
return { score, hardSignals, contradictions };
|
||
}
|
||
|
||
async function callGitHubModel(issuePacket) {
|
||
// Replace this stub with the GitHub Models inference call used by your org.
|
||
// The workflow already has models: read permission.
|
||
return {
|
||
decision: "MANUAL_REVIEW",
|
||
reason_code: "likely_fixed_but_unconfirmed",
|
||
confidence: 0.74,
|
||
hard_signals: [],
|
||
contradictions: [],
|
||
summary: "Potential resolution candidate; evidence is not strong enough to close automatically.",
|
||
close_comment: "This appears resolved, so we’re closing it automatically. Reply if this is still reproducible.",
|
||
manual_review_note: "Potential resolution candidate. Please review evidence before closing."
|
||
};
|
||
}
|
||
|
||
function enforcePolicy(modelOut, pre) {
|
||
const approvedReasons = new Set([
|
||
"resolved_by_merged_pr",
|
||
"maintainer_confirmed_resolved",
|
||
"duplicate_confirmed",
|
||
"superseded_confirmed"
|
||
]);
|
||
|
||
const hasHardSignal =
|
||
(modelOut.hard_signals || []).some(s =>
|
||
["merged_pr", "maintainer_comment", "duplicate_reference", "superseded_reference"].includes(s.type)
|
||
) || pre.hardSignals.length > 0;
|
||
|
||
const hasContradiction =
|
||
(modelOut.contradictions || []).length > 0 || pre.contradictions.length > 0;
|
||
|
||
if (
|
||
modelOut.decision === "AUTO_CLOSE" &&
|
||
modelOut.confidence >= 0.97 &&
|
||
approvedReasons.has(modelOut.reason_code) &&
|
||
hasHardSignal &&
|
||
!hasContradiction
|
||
) {
|
||
return "AUTO_CLOSE";
|
||
}
|
||
|
||
if (
|
||
modelOut.decision === "MANUAL_REVIEW" ||
|
||
modelOut.confidence >= 0.60 ||
|
||
pre.score >= 25
|
||
) {
|
||
return "MANUAL_REVIEW";
|
||
}
|
||
|
||
return "KEEP_OPEN";
|
||
}
|
||
|
||
const decisions = [];
|
||
for (const candidate of candidates) {
|
||
const pre = preScore(candidate);
|
||
const modelOut = await callGitHubModel(candidate);
|
||
const finalDecision = enforcePolicy(modelOut, pre);
|
||
|
||
decisions.push({
|
||
repository: candidate.repository,
|
||
issue_number: candidate.issue.number,
|
||
issue_url: candidate.issue.html_url,
|
||
title: candidate.issue.title,
|
||
pre_score: pre.score,
|
||
final_decision: finalDecision,
|
||
model: modelOut
|
||
});
|
||
}
|
||
|
||
await fs.writeFile("decisions.json", JSON.stringify(decisions, null, 2)); |