fix: exclude daily briefs from board audit

This commit is contained in:
Codex
2026-05-21 09:50:00 +00:00
parent a0c79d4673
commit e4e18736f2
4 changed files with 21 additions and 10 deletions
+5 -1
View File
@@ -297,6 +297,7 @@ async function startMockGitHub(): Promise<{ baseUrl: string; requests: MockReque
created_at: "2026-05-20T01:30:00Z",
updated_at: "2026-05-20T02:30:00Z",
},
dailyCommanderBriefIssue,
];
const legacyBoardOpenIssues = [
{
@@ -603,6 +604,7 @@ export async function runGhCliIssueGuardContract(): Promise<JsonRecord> {
const missingOpenIssues = boardAuditData.missingOpenIssues as JsonRecord[];
assertCondition(Array.isArray(missingOpenIssues) && missingOpenIssues.some((issue) => issue.number === 36), "board-audit should report open issue missing from OPEN table", missingOpenIssues);
assertCondition(!missingOpenIssues.some((issue) => issue.number === 45), "board-audit should use the primary markdown issue link as the Issue-column row key", missingOpenIssues);
assertCondition(!missingOpenIssues.some((issue) => issue.number === 46), "board-audit should not require daily commander briefs in the OPEN table", missingOpenIssues);
const closedInOpenRows = boardAuditData.closedInOpenRows as JsonRecord[];
assertCondition(Array.isArray(closedInOpenRows) && closedInOpenRows.some((issue) => issue.number === 40), "board-audit should report closed issue still in OPEN table", closedInOpenRows);
const missingClosedRows = boardAuditData.missingClosedRows as JsonRecord[];
@@ -618,8 +620,10 @@ export async function runGhCliIssueGuardContract(): Promise<JsonRecord> {
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);
assertCondition(ignoredIssues.some((issue) => issue.number === 46 && issue.reason === "brief-index-managed"), "board-audit should ignore daily commander briefs managed by the #20 brief index", ignoredIssues);
const recommendedActions = boardAuditData.recommendedActions as JsonRecord[];
assertCondition(Array.isArray(recommendedActions) && recommendedActions.some((action) => action.action === "add-open-row" && action.issueNumber === 36) && recommendedActions.some((action) => action.action === "move-open-row-to-closed" && action.issueNumber === 40), "board-audit should emit machine-readable recommended actions", recommendedActions);
assertCondition(!recommendedActions.some((action) => action.action === "add-open-row" && action.issueNumber === 46), "board-audit should not recommend adding daily commander briefs to the OPEN table", recommendedActions);
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) });
@@ -1072,7 +1076,7 @@ export async function runGhCliIssueGuardContract(): Promise<JsonRecord> {
"issue list default fields include labels and filter pull requests",
"issue scan-escape classifies pollution, explanatory mentions, and body risks",
"issue cleanup-plan remains dry-run with body/comment cleanup suggestions",
"issue board-audit reports missing open rows, closed/open section mismatches, missing closed rows, meta ignores, and row validation warnings without writes",
"issue board-audit reports missing open rows, closed/open section mismatches, missing closed rows, meta/brief-index ignores, and row validation warnings without writes",
"issue board-row list/get expose parsed #20 rows without writes",
"issue board-row add/delete without guard stay on dry-run and do not PATCH",
"issue board-row update defaults to dry-run, reports old/new row, body SHA, guard result, and does not introduce literal backslash-n",
+14 -7
View File
@@ -58,6 +58,7 @@ type BoardSectionKind = "open" | "closed";
type BoardRequiredColumn = typeof BOARD_AUDIT_REQUIRED_COLUMNS[number];
type BoardColumnKind = BoardRequiredColumn | "focus" | "githubStatus";
type BoardRowField = typeof BOARD_ROW_FIELDS[number];
type BoardIgnoreReason = "known-meta" | "ignored" | "brief-index-managed";
type GitHubDegradedReason =
| "missing-binary"
@@ -218,7 +219,7 @@ interface BoardIssueEntry {
interface BoardIgnoredIssue {
number: number;
reason: "known-meta" | "ignored";
reason: BoardIgnoreReason;
title?: string;
state?: string;
url?: string;
@@ -1285,10 +1286,15 @@ function mergedKnownMetaIssues(options: GitHubOptions): number[] {
return values;
}
function boardIgnoreMap(options: GitHubOptions): Map<number, "known-meta" | "ignored"> {
const ignored = new Map<number, "known-meta" | "ignored">();
function boardIgnoreMap(options: GitHubOptions, issues: BoardIssueEntry[] = []): Map<number, BoardIgnoreReason> {
const ignored = new Map<number, BoardIgnoreReason>();
for (const issueNumber of mergedKnownMetaIssues(options)) ignored.set(issueNumber, "known-meta");
for (const issueNumber of options.ignoredIssues) ignored.set(issueNumber, "ignored");
if (options.boardIssue === CODE_QUEUE_BOARD_TARGET_ISSUE) {
for (const issue of issues) {
if (!ignored.has(issue.number) && isDailyCommanderBriefTitle(issue.title)) ignored.set(issue.number, "brief-index-managed");
}
}
return ignored;
}
@@ -2540,7 +2546,7 @@ async function issueBoardRowDelete(repo: string, token: string, issueNumber: num
);
}
function ignoredIssueList(issues: BoardIssueEntry[], ignoreMap: Map<number, "known-meta" | "ignored">): BoardIgnoredIssue[] {
function ignoredIssueList(issues: BoardIssueEntry[], ignoreMap: Map<number, BoardIgnoreReason>): BoardIgnoredIssue[] {
return sortedIssueEntries(issues.filter((issue) => ignoreMap.has(issue.number))).map((issue) => ({
number: issue.number,
reason: ignoreMap.get(issue.number) ?? "ignored",
@@ -2550,7 +2556,7 @@ function ignoredIssueList(issues: BoardIssueEntry[], ignoreMap: Map<number, "kno
}));
}
function ignoredBoardOnlyIssues(issueNumbers: number[], issueMap: Map<number, BoardIssueEntry>, ignoreMap: Map<number, "known-meta" | "ignored">): BoardIgnoredIssue[] {
function ignoredBoardOnlyIssues(issueNumbers: number[], issueMap: Map<number, BoardIssueEntry>, ignoreMap: Map<number, BoardIgnoreReason>): BoardIgnoredIssue[] {
return issueNumbers
.filter((issueNumber) => ignoreMap.has(issueNumber) && !issueMap.has(issueNumber))
.sort((a, b) => a - b)
@@ -3174,7 +3180,7 @@ async function issueBoardAudit(repo: string, token: string, options: GitHubOptio
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);
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);
@@ -3278,6 +3284,7 @@ async function issueBoardAudit(repo: string, token: string, options: GitHubOptio
limit: options.limit,
knownMetaIssues: mergedKnownMetaIssues(options),
ignoredIssues: options.ignoredIssues,
briefIndexManagedIssuePattern: options.boardIssue === CODE_QUEUE_BOARD_TARGET_ISSUE ? "YYYY-MM-DD 指挥简报(北京时间)" : null,
requiredColumns: BOARD_AUDIT_REQUIRED_COLUMNS.slice(),
},
summary: {
@@ -4092,7 +4099,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.",
"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-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 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.",
"issue edit 24 --notify-claudeqq-brief-diff remains the legacy #24 notification helper: it reads the old issue body, PATCHes the new body, and sends only newly added '## 更新 ... 北京时间' sections to ClaudeQQ; ClaudeQQ failure does not roll back GitHub.",