Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions scripts/repair-dashboard.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -518,8 +518,17 @@ function runLink(record) {
return record.run_url ? link(record.run_id ?? "run", record.run_url) : "_none_";
}

function githubIssueOrPullLink(value) {
const url = String(value ?? "");
const match = url.match(/^https:\/\/github\.com\/[^/]+\/[^/]+\/(?:issues|pull)\/(\d+)$/);
return match ? link(`#${match[1]}`, url) : "";
}

function targetLink(record, action) {
const preferred = githubIssueOrPullLink(action.pr) || githubIssueOrPullLink(action.url);
if (preferred) return preferred;
const target = String(action.target ?? "");
if (!target || target.startsWith("cluster:")) return "";
const match = target.match(/^https:\/\/github\.com\/([^/]+\/[^/]+)\/(issues|pull)\/(\d+)/);
if (match) return link(`#${match[3]}`, target);
const shorthand = target.match(/^#(\d+)$/);
Expand All @@ -530,7 +539,7 @@ function targetLink(record, action) {
const segment = repairActionTargetsPullRequest(action) ? "pull" : "issues";
return link(target, `https://github.com/${repo}/${segment}/${shorthand[1]}`);
}
return target ? link(target, target) : "";
return "";
}

function githubItemUrlForNumber(value, number) {
Expand Down Expand Up @@ -570,15 +579,9 @@ function ownerActionRow(row) {
}

function ownerItemLink(record, action) {
const target = targetLink(record, action);
if (target) return target;
for (const value of [action.pr, action.url]) {
const match = String(value ?? "").match(
/^https:\/\/github\.com\/[^/]+\/[^/]+\/(?:issues|pull)\/(\d+)$/,
);
if (match) return link(`#${match[1]}`, value);
}
return "";
const preferred = githubIssueOrPullLink(action.pr) || githubIssueOrPullLink(action.url);
if (preferred) return preferred;
return targetLink(record, action);
}

function repoLink(record) {
Expand Down
43 changes: 43 additions & 0 deletions test/repair-dashboard.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,49 @@ test("repair dashboard links shorthand targets through the source repo", () => {
);
});

test("repair dashboard links a pull or issue before a cluster target", () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "clawsweeper-state-cluster-"));
const runsDir = path.join(root, "results", "runs");
fs.mkdirSync(runsDir, { recursive: true });
fs.writeFileSync(
path.join(runsDir, "run.json"),
JSON.stringify({
repo: "openclaw/openclaw",
cluster_id: "issue-openclaw-openclaw-5",
run_id: "55",
run_url: "https://github.com/openclaw/clawsweeper/actions/runs/55",
workflow_conclusion: "success",
published_at: "2026-05-02T19:00:00.000Z",
needs_human: ["choose the real item"],
fix_actions: [
{
target: "cluster:issue-openclaw-openclaw-5",
pr: "https://github.com/openclaw/openclaw/pull/9",
url: "https://github.com/openclaw/openclaw/issues/5",
status: "blocked",
reason: "cluster target is not a url",
},
],
apply_actions: [
{
target: "cluster:issue-openclaw-openclaw-8",
action: "close_duplicate",
url: "https://github.com/openclaw/openclaw/issues/8",
status: "executed",
title: "use the issue url",
closed_at: "2026-05-02T19:01:00.000Z",
},
],
}),
"utf8",
);

const dashboard = renderRepairDashboard(root);
assert.match(dashboard, /\[#9\]\(https:\/\/github\.com\/openclaw\/openclaw\/pull\/9\)/);
assert.match(dashboard, /\[#8\]\(https:\/\/github\.com\/openclaw\/openclaw\/issues\/8\)/);
assert.doesNotMatch(dashboard, /\]\(cluster:/);
});

test("repair dashboard groups owner action lanes by operator bucket", () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "clawsweeper-state-owner-"));
const runsDir = path.join(root, "results", "runs");
Expand Down
Loading