fix: disable board audit coverage validation

This commit is contained in:
Codex
2026-05-23 03:16:44 +00:00
parent 7ea2f77b70
commit 2bd36e7e9c
4 changed files with 67 additions and 139 deletions
+43 -118
View File
@@ -3893,101 +3893,10 @@ async function issueBoardAudit(repo: string, token: string, options: GitHubOptio
const commandName = "issue board-audit";
const boardIssue = await getIssue(token, repo, options.boardIssue);
if (isGitHubError(boardIssue)) return commandError(commandName, repo, boardIssue, { boardIssue: options.boardIssue });
const [rawOpenIssues, rawClosedIssues] = await Promise.all([
listIssues(token, repo, "open", options.limit),
listIssues(token, repo, "closed", options.limit),
]);
if (isGitHubError(rawOpenIssues)) return commandError(commandName, repo, rawOpenIssues, { phase: "list-open-issues", boardIssue: options.boardIssue, limit: options.limit });
if (isGitHubError(rawClosedIssues)) return commandError(commandName, repo, rawClosedIssues, { phase: "list-closed-issues", boardIssue: options.boardIssue, limit: options.limit });
const openIssues = rawOpenIssues.filter((issue) => issue.pull_request === undefined).slice(0, options.limit).map(boardIssueEntry);
const closedIssues = rawClosedIssues.filter((issue) => issue.pull_request === undefined).slice(0, options.limit).map(boardIssueEntry);
const allListedIssues = [...openIssues, ...closedIssues];
const issueMap = new Map<number, BoardIssueEntry>(allListedIssues.map((issue) => [issue.number, issue]));
const ignoreMap = boardIgnoreMap(options, allListedIssues);
const parsed = parseBoardTables(boardIssue.body ?? "");
const rowValidationWarnings = parsed.warnings.filter((warning) => warning.issueNumber === null || !ignoreMap.has(warning.issueNumber));
const openRows = parsed.sections.filter((section) => section.kind === "open").flatMap((section) => section.rows);
const closedRows = parsed.sections.filter((section) => section.kind === "closed").flatMap((section) => section.rows);
const openRowMap = boardRowsByIssue(openRows);
const closedRowMap = boardRowsByIssue(closedRows);
const openIssueNumbers = new Set(openIssues.map((issue) => issue.number));
const closedIssueNumbers = new Set(closedIssues.map((issue) => issue.number));
const boardIssueNumbers = Array.from(new Set([...openRowMap.keys(), ...closedRowMap.keys()]));
const missingOpenIssues = sortedIssueEntries(openIssues.filter((issue) => !ignoreMap.has(issue.number) && !openRowMap.has(issue.number)));
const closedInOpenRows = sortedIssueEntries(closedIssues.filter((issue) => !ignoreMap.has(issue.number) && openRowMap.has(issue.number))).map((issue) => ({
...issue,
rows: (openRowMap.get(issue.number) ?? []).map((row) => ({ lineNumber: row.lineNumber, rowPreview: preview(row.raw) })),
}));
const missingClosedRows = sortedIssueEntries(closedIssues.filter((issue) => !ignoreMap.has(issue.number) && !closedRowMap.has(issue.number)));
const openInClosedRows = sortedIssueEntries(openIssues.filter((issue) => !ignoreMap.has(issue.number) && closedRowMap.has(issue.number))).map((issue) => ({
...issue,
rows: (closedRowMap.get(issue.number) ?? []).map((row) => ({ lineNumber: row.lineNumber, rowPreview: preview(row.raw) })),
}));
const staleOpenRows = Array.from(openRowMap.entries())
.filter(([issueNumber]) => !ignoreMap.has(issueNumber) && !openIssueNumbers.has(issueNumber))
.map(([issueNumber, rows]) => ({
issueNumber,
knownState: closedIssueNumbers.has(issueNumber) ? "closed" : "not-in-listed-window",
rows: rows.map((row) => ({ lineNumber: row.lineNumber, rowPreview: preview(row.raw) })),
}))
.sort((a, b) => a.issueNumber - b.issueNumber);
const boardOnlyRows = boardIssueNumbers
.filter((issueNumber) => !ignoreMap.has(issueNumber) && !issueMap.has(issueNumber))
.sort((a, b) => a - b)
.map((issueNumber) => ({
issueNumber,
sections: [
...(openRowMap.has(issueNumber) ? ["open"] : []),
...(closedRowMap.has(issueNumber) ? ["closed"] : []),
],
rows: [...(openRowMap.get(issueNumber) ?? []), ...(closedRowMap.get(issueNumber) ?? [])].map((row) => ({
section: row.section,
lineNumber: row.lineNumber,
rowPreview: preview(row.raw),
})),
}));
const ignoredIssues = [
...ignoredIssueList(allListedIssues, ignoreMap),
...ignoredBoardOnlyIssues(boardIssueNumbers, issueMap, ignoreMap),
].sort((a, b) => a.number - b.number);
const recommendedActions = [
...missingOpenIssues.map((issue) => ({
action: "add-open-row",
issueNumber: issue.number,
title: issue.title,
section: "open",
reason: "GitHub issue is open but no #20 OPEN table row was found.",
})),
...closedInOpenRows.map((issue) => ({
action: "move-open-row-to-closed",
issueNumber: issue.number,
title: issue.title,
reason: "GitHub issue is closed but still appears in the #20 OPEN table.",
})),
...missingClosedRows.map((issue) => ({
action: "add-closed-row",
issueNumber: issue.number,
title: issue.title,
section: "closed",
reason: "GitHub issue is closed but no #20 CLOSED table row was found.",
})),
...openInClosedRows.map((issue) => ({
action: "move-closed-row-to-open",
issueNumber: issue.number,
title: issue.title,
reason: "GitHub issue is open but appears in the #20 CLOSED table.",
})),
...rowValidationWarnings.map((warning) => ({
action: "fill-board-row-fields",
issueNumber: warning.issueNumber,
section: warning.section,
lineNumber: warning.lineNumber,
reason: warning.message,
missingColumns: warning.missingColumns ?? [],
})),
];
const body = boardIssue.body ?? "";
return {
ok: true,
@@ -4001,7 +3910,9 @@ async function issueBoardAudit(repo: string, token: string, options: GitHubOptio
title: boardIssue.title,
state: boardIssue.state,
url: boardIssue.html_url,
bodyChars: (boardIssue.body ?? "").length,
bodyChars: body.length,
bodyLines: body.length === 0 ? 0 : normalizeNewlines(body).split("\n").length,
bodySha: bodySha(body),
updatedAt: boardIssue.updated_at ?? null,
},
config: {
@@ -4009,21 +3920,36 @@ async function issueBoardAudit(repo: string, token: string, options: GitHubOptio
knownMetaIssues: mergedKnownMetaIssues(options),
ignoredIssues: options.ignoredIssues,
briefIndexManagedIssuePattern: options.boardIssue === CODE_QUEUE_BOARD_TARGET_ISSUE ? "YYYY-MM-DD 指挥简报(北京时间)" : null,
requiredColumns: BOARD_AUDIT_REQUIRED_COLUMNS.slice(),
openClosedCoverageValidation: false,
requiredColumns: [],
},
summary: {
openIssues: openIssues.length,
closedIssues: closedIssues.length,
openIssues: null,
closedIssues: null,
openRows: openRows.length,
closedRows: closedRows.length,
missingOpenIssues: missingOpenIssues.length,
closedInOpenRows: closedInOpenRows.length,
missingClosedRows: missingClosedRows.length,
openInClosedRows: openInClosedRows.length,
rowValidationWarnings: rowValidationWarnings.length,
ignoredIssues: ignoredIssues.length,
boardOnlyRows: boardOnlyRows.length,
staleOpenRows: staleOpenRows.length,
parsedSections: parsed.sections.length,
parsedRows: parsed.sections.reduce((sum, section) => sum + section.rows.length, 0),
parserWarnings: parsed.warnings.length,
missingOpenIssues: 0,
closedInOpenRows: 0,
missingClosedRows: 0,
openInClosedRows: 0,
rowValidationWarnings: 0,
ignoredIssues: 0,
boardOnlyRows: 0,
staleOpenRows: 0,
recommendedActions: 0,
},
validation: {
openClosedCoverage: {
enabled: false,
reason: "OPEN/CLOSED table coverage validation has been disabled; this command no longer compares GitHub issue state against board rows.",
},
rowRequiredColumns: {
enabled: false,
reason: "board-audit no longer reports missing required row columns; use board-row commands when intentionally maintaining a legacy OPEN/CLOSED table.",
},
},
sections: parsed.sections.map((section) => ({
kind: section.kind,
@@ -4034,25 +3960,24 @@ async function issueBoardAudit(repo: string, token: string, options: GitHubOptio
rows: section.rows.length,
issueNumbers: section.rows.flatMap((row) => row.issueNumbers),
})),
missingOpenIssues,
closedInOpenRows,
missingClosedRows,
openInClosedRows,
staleOpenRows,
boardOnlyRows,
rowValidationWarnings,
ignoredIssues,
recommendedActions,
parserWarnings: parsed.warnings,
missingOpenIssues: [],
closedInOpenRows: [],
missingClosedRows: [],
openInClosedRows: [],
staleOpenRows: [],
boardOnlyRows: [],
rowValidationWarnings: [],
ignoredIssues: [],
recommendedActions: [],
request: {
method: "GET",
paths: [
`/repos/{owner}/{repo}/issues/${options.boardIssue}`,
"/repos/{owner}/{repo}/issues?state=open",
"/repos/{owner}/{repo}/issues?state=closed",
],
query: { per_page: options.limit },
query: {},
},
note: "Read-only board audit; no issue body was edited, no issue was closed, and no comments were written.",
note: "Read-only board structure audit; OPEN/CLOSED coverage validation is disabled, no issue body was edited, no issue was closed, and no comments were written.",
};
}
@@ -4830,7 +4755,7 @@ export function ghHelp(): unknown {
"When staging a body file from a shell, use a quoted heredoc such as cat <<'EOF' > /tmp/body.md so backticks and backslashes are not expanded before --body-file reads the file.",
"For JSON request bodies in other CLI namespaces, prefer --body-file or --body-stdin over long inline shell arguments; GitHub Markdown writes intentionally use --body-file only.",
"issue scan-escape classifies literal \\n findings as suspected-pollution, explanatory-mention, or risk, and emits cleanupSuggestions with body/comment ids plus diff-like previews. cleanup-plan is an alias that remains dry-run/read-only.",
"issue board-audit is read-only and defaults to repo pikasTech/unidesk plus board issue #20. It compares GitHub open/closed issue lists with the board OPEN/CLOSED tables and reports missingOpenIssues, closedInOpenRows, missingClosedRows, rowValidationWarnings, ignoredIssues, and recommendedActions. When an Issue column exists, row.issueNumber is taken from that column; #20 and #24 are known meta issues by default. Daily rolling brief issues titled YYYY-MM-DD 指挥简报(北京时间) are managed by #20's brief index and appear in ignoredIssues with reason=brief-index-managed instead of missingOpenIssues.",
"issue board-audit is read-only and defaults to repo pikasTech/unidesk plus board issue #20. It reads only the board issue body, returns body size/SHA and parsed Markdown board sections, and no longer validates GitHub open/closed issue coverage against OPEN/CLOSED tables. The legacy coverage fields remain present as empty arrays/zero counts for compatibility.",
"issue board-row list/get reuse the board-audit table parser and are read-only. board-row update changes one table cell by issue number, returns old/new row, body SHA, body guard and request plan, and defaults to dry-run unless --expect-updated-at or --expect-body-sha is supplied for the guarded PATCH. Field aliases map status and validation to the 验收状态 column, tasks to 相关 Code Queue 任务, and focus to 当前关注点.",
"issue board-row upsert updates an existing row when the issue is already present, or generates a complete row in --section open|closed when missing. It returns operation=update or operation=add, defaults to dry-run, requires --expect-body-sha or --expect-updated-at before PATCH, and refuses section migration; use board-row move for OPEN/CLOSED migration.",
"issue board-row add/move/delete are row-scoped #20 table mutations. add validates a one-line --row-file against the target table column count, Issue column, and GitHub 状态 column; move refuses duplicate/ambiguous rows and can update GitHub 状态 via --status; delete removes only the matched row. All three default to dry-run and require --expect-body-sha or --expect-updated-at before PATCH. add/move/delete return old/new row, body SHA, and line/section plan details for the parsed table mutation.",