mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-23 09:07:46 +02:00
Limit legacy disposition preview lookback
Default the legacy execution-disposition preview to recently created issues, keep an explicit --all escape hatch, and cover the lookback behavior with a targeted test. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
b13c9f5fba
commit
229a4c60a2
3 files changed
+58
-17
No files matched your search
@@ -22,6 +22,7 @@
|
||||
"db:migrate": "pnpm --filter @paperclipai/db migrate",
|
||||
"issue-references:backfill": "pnpm run preflight:workspace-links && tsx scripts/backfill-issue-reference-mentions.ts",
|
||||
"execution-dispositions:preview": "tsx scripts/preview-legacy-invalid-execution-dispositions.ts",
|
||||
"execution-dispositions:preview:test": "tsx --test scripts/preview-legacy-invalid-execution-dispositions.test.ts",
|
||||
"secrets:migrate-inline-env": "tsx scripts/migrate-inline-env-secrets.ts",
|
||||
"db:backup": "./scripts/backup-db.sh",
|
||||
"paperclipai": "node cli/node_modules/tsx/dist/cli.mjs cli/src/index.ts",
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
|
||||
import { filterIssuesByLookback, issueLookbackTimestamp } from "./preview-legacy-invalid-execution-dispositions.ts";
|
||||
|
||||
describe("legacy execution-disposition preview lookback", () => {
|
||||
it("scopes default repair candidates by issue creation time, not recent updates", () => {
|
||||
const cutoff = new Date("2026-04-27T12:00:00.000Z");
|
||||
const oldButRecentlyUpdated = {
|
||||
id: "old",
|
||||
title: "old blocked issue",
|
||||
status: "blocked" as const,
|
||||
createdAt: "2026-04-20T12:00:00.000Z",
|
||||
updatedAt: "2026-04-29T12:00:00.000Z",
|
||||
};
|
||||
const newlyCreated = {
|
||||
id: "new",
|
||||
title: "new blocked issue",
|
||||
status: "blocked" as const,
|
||||
createdAt: "2026-04-28T12:00:00.000Z",
|
||||
updatedAt: "2026-04-28T12:00:00.000Z",
|
||||
};
|
||||
|
||||
assert.equal(issueLookbackTimestamp(oldButRecentlyUpdated)?.toISOString(), "2026-04-20T12:00:00.000Z");
|
||||
assert.deepEqual(filterIssuesByLookback([oldButRecentlyUpdated, newlyCreated], cutoff).map((issue) => issue.id), [
|
||||
"new",
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,5 @@
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
type IssueStatus = "backlog" | "todo" | "in_progress" | "in_review" | "done" | "blocked" | "cancelled";
|
||||
|
||||
type InvalidDisposition = {
|
||||
@@ -80,14 +82,14 @@ function dateValue(value: string | Date | null | undefined): Date | null {
|
||||
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
||||
}
|
||||
|
||||
function issueScopeTimestamp(issue: IssueSummary): Date | null {
|
||||
return dateValue(issue.updatedAt) ?? dateValue(issue.createdAt);
|
||||
export function issueLookbackTimestamp(issue: IssueSummary): Date | null {
|
||||
return dateValue(issue.createdAt);
|
||||
}
|
||||
|
||||
function filterIssuesByLookback(issues: IssueSummary[], cutoff: Date | null): IssueSummary[] {
|
||||
export function filterIssuesByLookback(issues: IssueSummary[], cutoff: Date | null): IssueSummary[] {
|
||||
if (!cutoff) return issues;
|
||||
return issues.filter((issue) => {
|
||||
const timestamp = issueScopeTimestamp(issue);
|
||||
const timestamp = issueLookbackTimestamp(issue);
|
||||
return timestamp ? timestamp >= cutoff : false;
|
||||
});
|
||||
}
|
||||
@@ -183,6 +185,19 @@ function proposedAction(reason: string, issue: IssueSummary): string {
|
||||
}
|
||||
}
|
||||
|
||||
let apiUrl: string;
|
||||
let apiKey: string;
|
||||
let runId: string | undefined;
|
||||
|
||||
function configureApiFromEnvironment() {
|
||||
apiUrl = requireConfig(
|
||||
"PAPERCLIP_API_URL or --api-url",
|
||||
readFlag("--api-url") ?? process.env.PAPERCLIP_API_URL,
|
||||
);
|
||||
apiKey = requireConfig("PAPERCLIP_API_KEY", process.env.PAPERCLIP_API_KEY);
|
||||
runId = process.env.PAPERCLIP_RUN_ID?.trim() || undefined;
|
||||
}
|
||||
|
||||
async function requestJson<T>(method: string, path: string, body?: unknown): Promise<T> {
|
||||
const url = new URL(path, apiUrl);
|
||||
const response = await fetch(url, {
|
||||
@@ -284,7 +299,7 @@ function printPreview(
|
||||
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(`Lookback: ${cutoff ? `created 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}`);
|
||||
@@ -340,14 +355,8 @@ async function applySafeCorrections(groups: PreviewGroup[]): Promise<number> {
|
||||
return applied;
|
||||
}
|
||||
|
||||
const apiUrl = requireConfig(
|
||||
"PAPERCLIP_API_URL or --api-url",
|
||||
readFlag("--api-url") ?? process.env.PAPERCLIP_API_URL,
|
||||
);
|
||||
const apiKey = requireConfig("PAPERCLIP_API_KEY", process.env.PAPERCLIP_API_KEY);
|
||||
const runId = process.env.PAPERCLIP_RUN_ID?.trim();
|
||||
|
||||
async function main() {
|
||||
configureApiFromEnvironment();
|
||||
const companyId = requireConfig(
|
||||
"PAPERCLIP_COMPANY_ID or --company",
|
||||
readFlag("--company") ?? process.env.PAPERCLIP_COMPANY_ID,
|
||||
@@ -372,8 +381,10 @@ async function main() {
|
||||
printPreview(companyId, fetchedIssues.length, issues.length, cutoff, groups, appliedCount);
|
||||
}
|
||||
|
||||
void main().catch((error) => {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
console.error(`Legacy execution-disposition reconciliation preview failed: ${message}`);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
void main().catch((error) => {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
console.error(`Legacy execution-disposition reconciliation preview failed: ${message}`);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user