fix: reduce board audit noise

This commit is contained in:
Codex
2026-05-20 21:54:55 +00:00
parent 021a9eef01
commit 27b14bef76
4 changed files with 91 additions and 16 deletions
+70 -9
View File
@@ -93,21 +93,38 @@ async function startMockGitHub(): Promise<{ baseUrl: string; requests: MockReque
"",
"## 看板(OPEN",
"",
"| Issue | Branch | 验收状态 | 相关任务 | 进度 |",
"| --- | --- | --- | --- | --- |",
"| #20 | master | meta | governance | active |",
"| #35 master:补齐 UniDesk CLI gh issue list 与 PR 驱动最小闭环前置能力 | master | pass | cq-35 | doing |",
"| #40 closed issue still in open | | | cq-40 | |",
"| Issue | Branch | 验收状态 | 相关 Code Queue 任务 | 当前关注点 | 进度 |",
"| --- | --- | --- | --- | --- | --- |",
"| #20 | master | meta | governance | 关注:#19 / #26 / #30 | active |",
"| #35 | master | pass | cq-35 | 当前关注点:#19 / #26 / #30 | doing |",
"| #40 | | | cq-40 | 复核:#19 / #26 / #30 | |",
"",
"## 看板(CLOSED",
"",
"| Issue | Branch | 验收状态 | 相关任务 | 进度 |",
"| --- | --- | --- | --- | --- |",
"| #24 | master | meta | brief | active |",
"| #36 second issue | master | pending | cq-36 | misplaced |",
"| Issue | Branch | 验收状态 | 相关 Code Queue 任务 | 当前关注点 | 进度 |",
"| --- | --- | --- | --- | --- | --- |",
"| #24 | master | meta | brief | 常驻:#24 / #20 | active |",
"| #36 | master | pending | cq-36 | 复核:#35 / #40 | done |",
"",
].join("\n"),
};
const legacyBoardIssue = {
...issue,
id: 2600,
number: 60,
title: "遗留总看板",
body: [
"# Code Queue",
"",
"## 看板(OPEN",
"",
"| 当前项 | Branch | 验收状态 | 相关任务 | 进度 |",
"| --- | --- | --- | --- | --- |",
"| #101 / #102 说明:#101 / #102 | master | pass | cq-legacy | doing |",
"",
].join("\n"),
html_url: "https://github.com/pikasTech/unidesk/issues/60",
};
const issueList = [
{
id: 2001,
@@ -221,6 +238,23 @@ async function startMockGitHub(): Promise<{ baseUrl: string; requests: MockReque
updated_at: "2026-05-20T02:00:00Z",
},
];
const legacyBoardOpenIssues = [
{
id: 2600,
number: 60,
title: "遗留总看板",
body: legacyBoardIssue.body,
state: "open",
html_url: "https://github.com/pikasTech/unidesk/issues/60",
comments: 0,
user: { login: "tester" },
labels: [],
created_at: "2026-05-20T01:00:00Z",
updated_at: "2026-05-20T02:00:00Z",
},
issueList[0],
issueList[1],
];
const boardClosedIssues = [
{
id: 2040,
@@ -289,10 +323,18 @@ async function startMockGitHub(): Promise<{ baseUrl: string; requests: MockReque
sendJson(res, 200, boardIssue);
return;
}
if (req.method === "GET" && req.url === "/repos/pikasTech/unidesk/issues/60") {
sendJson(res, 200, legacyBoardIssue);
return;
}
if (req.method === "GET" && req.url === "/repos/pikasTech/unidesk/issues/20/comments?per_page=100") {
sendJson(res, 200, comments);
return;
}
if (req.method === "GET" && req.url === "/repos/pikasTech/unidesk/issues/60/comments?per_page=100") {
sendJson(res, 200, []);
return;
}
if (req.method === "GET" && req.url === "/repos/pikasTech/unidesk/issues?state=open&per_page=2") {
sendJson(res, 200, issueList.slice(0, 2));
return;
@@ -313,6 +355,14 @@ async function startMockGitHub(): Promise<{ baseUrl: string; requests: MockReque
sendJson(res, 200, boardOpenIssues);
return;
}
if (req.method === "GET" && req.url === "/repos/pikasTech/unidesk/issues?state=open&per_page=60") {
sendJson(res, 200, legacyBoardOpenIssues);
return;
}
if (req.method === "GET" && req.url === "/repos/pikasTech/unidesk/issues?state=closed&per_page=60") {
sendJson(res, 200, boardClosedIssues);
return;
}
if (req.method === "GET" && req.url === "/repos/pikasTech/unidesk/issues?state=closed&per_page=100") {
sendJson(res, 200, boardClosedIssues);
return;
@@ -455,6 +505,8 @@ export async function runGhCliIssueGuardContract(): Promise<JsonRecord> {
assertCondition(Array.isArray(openInClosedRows) && openInClosedRows.some((issue) => issue.number === 36), "board-audit should report open issue placed in CLOSED table", openInClosedRows);
const rowValidationWarnings = boardAuditData.rowValidationWarnings as JsonRecord[];
assertCondition(Array.isArray(rowValidationWarnings) && rowValidationWarnings.some((warning) => warning.issueNumber === 40 && Array.isArray(warning.missingColumns) && (warning.missingColumns as unknown[]).includes("branch") && (warning.missingColumns as unknown[]).includes("acceptance") && (warning.missingColumns as unknown[]).includes("progress")), "board-audit should report missing required board columns", rowValidationWarnings);
assertCondition(!rowValidationWarnings.some((warning) => warning.kind === "multiple-issue-references"), "board-audit should not treat explanatory issue references outside the Issue column as row-key conflicts", rowValidationWarnings);
assertCondition(!rowValidationWarnings.some((warning) => Array.isArray(warning.missingColumns) && (warning.missingColumns as unknown[]).includes("relatedTask")), "board-audit should recognize relatedTask from the current header", rowValidationWarnings);
const ignoredIssues = boardAuditData.ignoredIssues as JsonRecord[];
assertCondition(Array.isArray(ignoredIssues) && ignoredIssues.some((issue) => issue.number === 20 && issue.reason === "known-meta") && ignoredIssues.some((issue) => issue.number === 24 && issue.reason === "known-meta"), "board-audit should ignore configured known meta issues", ignoredIssues);
const recommendedActions = boardAuditData.recommendedActions as JsonRecord[];
@@ -462,6 +514,15 @@ export async function runGhCliIssueGuardContract(): Promise<JsonRecord> {
const boardAuditWriteCount = mock.requests.slice(boardAuditRequestCountBefore).filter((request) => request.method === "PATCH" || request.method === "DELETE" || request.method === "POST").length;
assertCondition(boardAuditWriteCount === 0, "board-audit must not write GitHub", { requests: mock.requests.slice(boardAuditRequestCountBefore) });
const legacyBoardAuditRequestCountBefore = mock.requests.length;
const legacyBoardAudit = await runCli(["gh", "issue", "board-audit", "--repo", "pikasTech/unidesk", "--board-issue", "60", "--limit", "60", "--dry-run"], env);
assertCondition(legacyBoardAudit.status === 0, "legacy board-audit fixture should succeed", legacyBoardAudit.json ?? { stdout: legacyBoardAudit.stdout, stderr: legacyBoardAudit.stderr });
const legacyBoardAuditData = dataOf(legacyBoardAudit.json ?? {});
const legacyWarnings = legacyBoardAuditData.rowValidationWarnings as JsonRecord[];
assertCondition(Array.isArray(legacyWarnings) && legacyWarnings.some((warning) => warning.kind === "multiple-issue-references" && warning.issueNumber === 101), "legacy board-audit fixture should keep whole-row fallback when no Issue column exists", legacyWarnings);
const legacyBoardWriteCount = mock.requests.slice(legacyBoardAuditRequestCountBefore).filter((request) => request.method === "PATCH" || request.method === "DELETE" || request.method === "POST").length;
assertCondition(legacyBoardWriteCount === 0, "legacy board-audit must not write GitHub", { requests: mock.requests.slice(legacyBoardAuditRequestCountBefore) });
const badListField = await runCli(["gh", "issue", "list", "--repo", "pikasTech/unidesk", "--json", "number,body"], env);
assertCondition(badListField.status !== 0, "issue list unsupported --json field should fail", badListField.json ?? { stdout: badListField.stdout });
const badListFieldData = failedDataOf(badListField.json ?? {});