Skip to content
Merged
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
82 changes: 54 additions & 28 deletions src/clawsweeper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3151,6 +3151,7 @@ export function shouldSyncReviewComment(options: {
needsReviewCommentBodySync: boolean;
needsReviewCommentHashSync: boolean;
needsReviewCommentReferenceSync: boolean;
forceReviewCommentBodySync?: boolean;
now?: number;
}): boolean {
if (
Expand All @@ -3161,6 +3162,7 @@ export function shouldSyncReviewComment(options: {
return false;
}
if (!options.syncCommentsOnly || options.isCloseProposal) return true;
if (options.forceReviewCommentBodySync && options.needsReviewCommentBodySync) return true;
if (!options.hasExistingReviewComment || options.needsReviewCommentReferenceSync) return true;
if (options.commentSyncMinAgeDays <= 0) return true;
if (!options.reviewCommentSyncedAt) return true;
Expand Down Expand Up @@ -7920,12 +7922,13 @@ function syncTelegramVisibleProofLabel(options: {
labels: readonly string[];
proof: Pick<TelegramVisibleProof, "status">;
dryRun: boolean;
}): string[] {
}): { labels: string[]; changed: boolean } {
const nextLabels = nextTelegramVisibleProofLabels(options.labels, options.proof);
const hadLabel = options.labels.includes(TELEGRAM_VISIBLE_PROOF_LABEL);
const wantsLabel = nextLabels.includes(TELEGRAM_VISIBLE_PROOF_LABEL);
if (hadLabel === wantsLabel) return nextLabels;
if (options.dryRun) return nextLabels;
const changed = hadLabel !== wantsLabel;
if (!changed) return { labels: nextLabels, changed };
if (options.dryRun) return { labels: nextLabels, changed };
if (wantsLabel) ensureTelegramVisibleProofLabel();
ghWithRetry([
"issue",
Expand All @@ -7934,7 +7937,7 @@ function syncTelegramVisibleProofLabel(options: {
wantsLabel ? "--add-label" : "--remove-label",
TELEGRAM_VISIBLE_PROOF_LABEL,
]);
return nextLabels;
return { labels: nextLabels, changed };
}

function ensurePrRatingLabel(tier: PrRatingTier): void {
Expand Down Expand Up @@ -7984,7 +7987,7 @@ function syncPrRatingLabel(options: {
labels: readonly string[];
rating: Pick<PrRating, "overallTier">;
dryRun: boolean;
}): string[] {
}): { labels: string[]; changed: boolean } {
const nextLabels = nextPrRatingLabels(options.labels, options.rating);
const currentLabelKeys = new Set(options.labels.map((label) => label.toLowerCase()));
const nextLabelKeys = new Set(nextLabels.map((label) => label.toLowerCase()));
Expand All @@ -7994,24 +7997,25 @@ function syncPrRatingLabel(options: {
const labelToAdd = nextLabels.find(
(label) => PR_RATING_LABEL_NAMES.has(label) && !currentLabelKeys.has(label.toLowerCase()),
);
if (!labelsToRemove.length && !labelToAdd) return nextLabels;
if (options.dryRun) return nextLabels;
const changed = labelsToRemove.length > 0 || Boolean(labelToAdd);
if (!changed) return { labels: nextLabels, changed };
if (options.dryRun) return { labels: nextLabels, changed };
if (labelToAdd) ensurePrRatingLabel(options.rating.overallTier);
for (const label of labelsToRemove) {
ghWithRetry(["issue", "edit", String(options.number), "--remove-label", label]);
}
if (labelToAdd) {
ghWithRetry(["issue", "edit", String(options.number), "--add-label", labelToAdd]);
}
return nextLabels;
return { labels: nextLabels, changed };
}

function syncPrStatusLabel(options: {
number: number;
labels: readonly string[];
statusKind: PrStatusLabelKind | null;
dryRun: boolean;
}): string[] {
}): { labels: string[]; changed: boolean } {
const nextLabels = nextPrStatusLabels(options.labels, options.statusKind);
const currentLabelKeys = new Set(options.labels.map((label) => label.toLowerCase()));
const nextLabelKeys = new Set(nextLabels.map((label) => label.toLowerCase()));
Expand All @@ -8021,16 +8025,17 @@ function syncPrStatusLabel(options: {
const labelToAdd = nextLabels.find(
(label) => PR_STATUS_LABEL_NAMES.has(label) && !currentLabelKeys.has(label.toLowerCase()),
);
if (!labelsToRemove.length && !labelToAdd) return nextLabels;
if (options.dryRun) return nextLabels;
const changed = labelsToRemove.length > 0 || Boolean(labelToAdd);
if (!changed) return { labels: nextLabels, changed };
if (options.dryRun) return { labels: nextLabels, changed };
if (options.statusKind && labelToAdd) ensurePrStatusLabel(options.statusKind);
for (const label of labelsToRemove) {
ghWithRetry(["issue", "edit", String(options.number), "--remove-label", label]);
}
if (labelToAdd) {
ghWithRetry(["issue", "edit", String(options.number), "--add-label", labelToAdd]);
}
return nextLabels;
return { labels: nextLabels, changed };
}

function ensureTelegramVisibleProofLabel(): void {
Expand Down Expand Up @@ -8115,13 +8120,16 @@ function syncRealBehaviorProofSufficientLabel(options: {
labels: readonly string[];
proof: Pick<RealBehaviorProof, "status">;
dryRun: boolean;
}): string[] {
}): { labels: string[]; changed: boolean } {
const nextLabels = nextRealBehaviorProofSufficientLabels(options.labels, options.proof);
const hadLabel = options.labels.includes(PROOF_SUFFICIENT_LABEL);
const wantsLabel = nextLabels.includes(PROOF_SUFFICIENT_LABEL);
if (hadLabel === wantsLabel) return nextLabels;
if (options.dryRun) return nextLabels;
if (wantsLabel && !ensureRealBehaviorProofSufficientLabel()) return [...options.labels];
const changed = hadLabel !== wantsLabel;
if (!changed) return { labels: nextLabels, changed };
if (options.dryRun) return { labels: nextLabels, changed };
if (wantsLabel && !ensureRealBehaviorProofSufficientLabel()) {
return { labels: [...options.labels], changed: false };
}
try {
ghWithRetry([
"issue",
Expand All @@ -8137,17 +8145,19 @@ function syncRealBehaviorProofSufficientLabel(options: {
error instanceof Error ? error.message : String(error)
}`,
);
return wantsLabel ? [...options.labels] : nextLabels;
return wantsLabel
? { labels: [...options.labels], changed: false }
: { labels: nextLabels, changed };
}
return nextLabels;
return { labels: nextLabels, changed };
}

function syncRealBehaviorProofMediaLabels(options: {
number: number;
labels: readonly string[];
proof: Pick<RealBehaviorProof, "evidenceKind">;
dryRun: boolean;
}): string[] {
}): { labels: string[]; changed: boolean } {
const nextLabels = nextRealBehaviorProofMediaLabels(options.labels, options.proof);
const currentLabelKeys = new Set(options.labels.map((label) => label.toLowerCase()));
const nextLabelKeys = new Set(nextLabels.map((label) => label.toLowerCase()));
Expand All @@ -8157,10 +8167,12 @@ function syncRealBehaviorProofMediaLabels(options: {
const labelsToRemove = options.labels.filter(
(label) => PROOF_MEDIA_LABEL_NAMES.has(label) && !nextLabelKeys.has(label.toLowerCase()),
);
if (!labelsToAdd.length && !labelsToRemove.length) return nextLabels;
if (options.dryRun) return nextLabels;
const changed = labelsToAdd.length > 0 || labelsToRemove.length > 0;
if (!changed) return { labels: nextLabels, changed };
if (options.dryRun) return { labels: nextLabels, changed };
for (const label of labelsToAdd) {
if (!ensureRealBehaviorProofMediaLabel(label)) return [...options.labels];
if (!ensureRealBehaviorProofMediaLabel(label))
return { labels: [...options.labels], changed: false };
ghWithRetry(["issue", "edit", String(options.number), "--add-label", label]);
}
for (const label of labelsToRemove) {
Expand All @@ -8175,7 +8187,7 @@ function syncRealBehaviorProofMediaLabels(options: {
);
}
}
return nextLabels;
return { labels: nextLabels, changed };
}

function isAutomationReportAuthor(author: string | undefined): boolean {
Expand Down Expand Up @@ -9401,6 +9413,9 @@ function renderKeepOpenCommentFromReport(
labelTransitionJustificationsMarkdown(labelTransitionJustifications),
);
}
if (labelJustifications.length) {
details.push("", "Label justifications:", "", labelJustificationsMarkdown(labelJustifications));
}
if (isPullRequest && reviewFindings.length) {
details.push(
"",
Expand Down Expand Up @@ -11111,41 +11126,51 @@ async function applyDecisionsCommand(args: Args): Promise<void> {
let currentPrStatusKind: PrStatusLabelKind | null = null;
if (state === "open" && item.kind === "pull_request") {
const realBehaviorProof = reportRealBehaviorProof(markdown);
item.labels = syncRealBehaviorProofSufficientLabel({
const proofSufficientSyncResult = syncRealBehaviorProofSufficientLabel({
number,
labels: item.labels,
proof: realBehaviorProof,
dryRun,
});
item.labels = syncRealBehaviorProofMediaLabels({
item.labels = proofSufficientSyncResult.labels;
clawSweeperLabelsChanged ||= proofSufficientSyncResult.changed;
const proofMediaSyncResult = syncRealBehaviorProofMediaLabels({
number,
labels: item.labels,
proof: realBehaviorProof,
dryRun,
});
item.labels = syncPrRatingLabel({
item.labels = proofMediaSyncResult.labels;
clawSweeperLabelsChanged ||= proofMediaSyncResult.changed;
const prRatingSyncResult = syncPrRatingLabel({
number,
labels: item.labels,
rating: reportPrRating(markdown),
dryRun,
});
item.labels = prRatingSyncResult.labels;
clawSweeperLabelsChanged ||= prRatingSyncResult.changed;
currentPrStatusKind = prStatusLabelKindFromReport(
markdown,
currentItemContext(),
item.labels,
);
item.labels = syncPrStatusLabel({
const prStatusSyncResult = syncPrStatusLabel({
number,
labels: item.labels,
statusKind: currentPrStatusKind,
dryRun,
});
item.labels = syncTelegramVisibleProofLabel({
item.labels = prStatusSyncResult.labels;
clawSweeperLabelsChanged ||= prStatusSyncResult.changed;
const telegramVisibleProofSyncResult = syncTelegramVisibleProofLabel({
number,
labels: item.labels,
proof: reportTelegramVisibleProof(markdown),
dryRun,
});
item.labels = telegramVisibleProofSyncResult.labels;
clawSweeperLabelsChanged ||= telegramVisibleProofSyncResult.changed;
}
markdown = replaceFrontMatterValue(markdown, "labels", JSON.stringify(item.labels));
if (
Expand Down Expand Up @@ -11395,6 +11420,7 @@ async function applyDecisionsCommand(args: Args): Promise<void> {
needsReviewCommentBodySync,
needsReviewCommentHashSync,
needsReviewCommentReferenceSync,
forceReviewCommentBodySync: clawSweeperLabelsChanged,
});
if (clawSweeperLabelsChanged && !dryRun) {
markdown = replaceFrontMatterValue(markdown, "labels_synced_at", new Date().toISOString());
Expand Down
Loading