fix: exclude daily briefs from board audit
This commit is contained in:
@@ -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
@@ -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.",
|
||||
|
||||
Reference in New Issue
Block a user