mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-30 21:49:18 +01:00
Limit legacy disposition preview to recent issues
Default the legacy execution-disposition preview to a 48-hour lookback and require --all for full historical scans. This keeps repair passes from treating stale blocked/review rows as current cleanup work unless the operator explicitly asks for a backfill. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
4c5eaeadd5
commit
b13c9f5fba
1 file changed
+50
-4
@@ -11,6 +11,8 @@ type IssueSummary = {
|
||||
identifier?: string | null;
|
||||
title: string;
|
||||
status: IssueStatus;
|
||||
createdAt?: string | Date | null;
|
||||
updatedAt?: string | Date | null;
|
||||
assigneeAgentId?: string | null;
|
||||
assigneeUserId?: string | null;
|
||||
executionDisposition?: { kind: string } | InvalidDisposition;
|
||||
@@ -44,6 +46,7 @@ type PreviewGroup = {
|
||||
|
||||
const NON_TERMINAL_STATUSES = ["backlog", "todo", "in_progress", "in_review", "blocked"] as const;
|
||||
const PAGE_LIMIT = 1000;
|
||||
const DEFAULT_LOOKBACK_HOURS = 48;
|
||||
|
||||
function readFlag(name: string): string | null {
|
||||
const index = process.argv.indexOf(name);
|
||||
@@ -60,6 +63,35 @@ function requireConfig(name: string, value: string | null | undefined): string {
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
function readPositiveIntegerFlag(name: string): number | null {
|
||||
const raw = readFlag(name);
|
||||
if (raw === null) return null;
|
||||
const value = Number(raw);
|
||||
if (!Number.isInteger(value) || value <= 0) {
|
||||
throw new Error(`${name} must be a positive integer`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function dateValue(value: string | Date | null | undefined): Date | null {
|
||||
if (!value) return null;
|
||||
if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;
|
||||
const parsed = new Date(value);
|
||||
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
||||
}
|
||||
|
||||
function issueScopeTimestamp(issue: IssueSummary): Date | null {
|
||||
return dateValue(issue.updatedAt) ?? dateValue(issue.createdAt);
|
||||
}
|
||||
|
||||
function filterIssuesByLookback(issues: IssueSummary[], cutoff: Date | null): IssueSummary[] {
|
||||
if (!cutoff) return issues;
|
||||
return issues.filter((issue) => {
|
||||
const timestamp = issueScopeTimestamp(issue);
|
||||
return timestamp ? timestamp >= cutoff : false;
|
||||
});
|
||||
}
|
||||
|
||||
function ownerKey(issue: IssueSummary): string {
|
||||
if (issue.assigneeAgentId && issue.assigneeUserId) {
|
||||
return `dual:agent:${issue.assigneeAgentId}:user:${issue.assigneeUserId}`;
|
||||
@@ -231,7 +263,14 @@ function formatIssue(issue: IssueSummary): string {
|
||||
return `${issue.identifier ?? issue.id} (${issue.status}, ${ownerKey(issue)}): ${issue.title}`;
|
||||
}
|
||||
|
||||
function printPreview(companyId: string, totalScanned: number, groups: PreviewGroup[], appliedCount: number) {
|
||||
function printPreview(
|
||||
companyId: string,
|
||||
totalFetched: number,
|
||||
totalScanned: number,
|
||||
cutoff: Date | null,
|
||||
groups: PreviewGroup[],
|
||||
appliedCount: number,
|
||||
) {
|
||||
const invalidCount = groups.reduce((sum, group) => sum + group.issues.length, 0);
|
||||
const safeCandidateCount = groups.reduce(
|
||||
(sum, group) => sum + group.issues.filter(hasSafeCancelledBlockerCorrection).length,
|
||||
@@ -245,6 +284,8 @@ function printPreview(companyId: string, totalScanned: number, groups: PreviewGr
|
||||
console.log(`# Legacy execution-disposition reconciliation preview`);
|
||||
console.log("");
|
||||
console.log(`Company: ${companyId}`);
|
||||
console.log(`Lookback: ${cutoff ? `updated since ${cutoff.toISOString()}` : "all non-terminal issues"}`);
|
||||
console.log(`Non-terminal issues fetched: ${totalFetched}`);
|
||||
console.log(`Non-terminal issues scanned: ${totalScanned}`);
|
||||
console.log(`Invalid issues found: ${invalidCount}`);
|
||||
console.log(`Safe cancelled-blocker corrections available: ${safeCandidateCount}`);
|
||||
@@ -312,18 +353,23 @@ async function main() {
|
||||
readFlag("--company") ?? process.env.PAPERCLIP_COMPANY_ID,
|
||||
);
|
||||
const applySafe = process.argv.includes("--apply-safe");
|
||||
const allIssues = process.argv.includes("--all");
|
||||
const lookbackHours = allIssues ? null : (readPositiveIntegerFlag("--since-hours") ?? DEFAULT_LOOKBACK_HOURS);
|
||||
const cutoff = lookbackHours === null ? null : new Date(Date.now() - lookbackHours * 60 * 60 * 1000);
|
||||
|
||||
let issues = await listNonTerminalIssues(companyId);
|
||||
let fetchedIssues = await listNonTerminalIssues(companyId);
|
||||
let issues = filterIssuesByLookback(fetchedIssues, cutoff);
|
||||
let groups = buildGroups(issues);
|
||||
let appliedCount = 0;
|
||||
|
||||
if (applySafe) {
|
||||
appliedCount = await applySafeCorrections(groups);
|
||||
issues = await listNonTerminalIssues(companyId);
|
||||
fetchedIssues = await listNonTerminalIssues(companyId);
|
||||
issues = filterIssuesByLookback(fetchedIssues, cutoff);
|
||||
groups = buildGroups(issues);
|
||||
}
|
||||
|
||||
printPreview(companyId, issues.length, groups, appliedCount);
|
||||
printPreview(companyId, fetchedIssues.length, issues.length, cutoff, groups, appliedCount);
|
||||
}
|
||||
|
||||
void main().catch((error) => {
|
||||
|
||||
Reference in new issue
Block a user