fix(FN-4401): address snapshot review feedback
Fusion-Task-Id: FN-4401 Fusion-Task-Lineage: c1b6c497-b22c-48d5-b1c8-299877bf09ac
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { TaskDetail } from "@fusion/core";
|
||||
import type { Task } from "@fusion/core";
|
||||
import { AutoClaimSnapshotManager, extractDescriptionFirstLine } from "../auto-claim-snapshot.js";
|
||||
|
||||
function makeTask(overrides: Partial<TaskDetail> & Pick<TaskDetail, "id">): TaskDetail {
|
||||
function makeTask(overrides: Partial<Task> & Pick<Task, "id">): Task {
|
||||
return {
|
||||
id: overrides.id,
|
||||
title: overrides.title ?? null,
|
||||
@@ -20,7 +20,7 @@ function makeTask(overrides: Partial<TaskDetail> & Pick<TaskDetail, "id">): Task
|
||||
checkedOutBy: overrides.checkedOutBy,
|
||||
paused: overrides.paused,
|
||||
columnMovedAt: overrides.columnMovedAt,
|
||||
} as TaskDetail;
|
||||
} as Task;
|
||||
}
|
||||
|
||||
describe("AutoClaimSnapshotManager", () => {
|
||||
@@ -87,6 +87,22 @@ describe("AutoClaimSnapshotManager", () => {
|
||||
expect(snapshot.tasks.map((t) => t.id)).toEqual(["FN-1", "FN-2", "FN-3"]);
|
||||
});
|
||||
|
||||
it("caps candidate set to 50 and computes capped baseScore", async () => {
|
||||
const tasks = Array.from({ length: 55 }, (_, idx) => makeTask({
|
||||
id: `FN-${idx + 1}`,
|
||||
createdAt: "2025-12-01T00:00:00.000Z",
|
||||
}));
|
||||
const manager = new AutoClaimSnapshotManager({
|
||||
taskStore: { listTasks: vi.fn(async () => tasks) },
|
||||
now: () => Date.parse("2026-01-03T00:00:00.000Z"),
|
||||
});
|
||||
|
||||
const snapshot = await manager.getSnapshot();
|
||||
|
||||
expect(snapshot.tasks).toHaveLength(50);
|
||||
expect(snapshot.tasks[0]?.baseScore).toBe(5);
|
||||
});
|
||||
|
||||
it("extracts first non-empty description line and caps length", () => {
|
||||
expect(extractDescriptionFirstLine("\n\nfirst line\nsecond line")).toBe("first line");
|
||||
expect(extractDescriptionFirstLine(" \n\t\n")).toBe("");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { TaskDetail, TaskStore } from "@fusion/core";
|
||||
import type { Task, TaskStore } from "@fusion/core";
|
||||
import { createLogger, type Logger } from "./logger.js";
|
||||
|
||||
/**
|
||||
@@ -13,7 +13,7 @@ export interface AutoClaimCandidate {
|
||||
createdAt: string;
|
||||
columnMovedAt?: string;
|
||||
baseScore: number;
|
||||
column: TaskDetail["column"];
|
||||
column: Task["column"];
|
||||
}
|
||||
|
||||
export interface AutoClaimSnapshot {
|
||||
@@ -37,6 +37,7 @@ export class AutoClaimSnapshotManager {
|
||||
private readonly now: () => number;
|
||||
private cache: AutoClaimSnapshot | null = null;
|
||||
private staleReason: "ttl" | "invalidate" = "ttl";
|
||||
private invalidatedAt = 0;
|
||||
private inFlight: Promise<AutoClaimSnapshot> | null = null;
|
||||
|
||||
constructor({ taskStore, ttlMs = 30_000, logger = autoClaimSnapshotLog, now = Date.now }: AutoClaimSnapshotManagerOptions) {
|
||||
@@ -49,6 +50,7 @@ export class AutoClaimSnapshotManager {
|
||||
invalidate(reason: string): void {
|
||||
this.cache = null;
|
||||
this.staleReason = "invalidate";
|
||||
this.invalidatedAt = this.now();
|
||||
this.logger.log(`invalidate reason=${reason}`);
|
||||
}
|
||||
|
||||
@@ -61,10 +63,12 @@ export class AutoClaimSnapshotManager {
|
||||
return this.inFlight;
|
||||
}
|
||||
|
||||
const startedAt = this.now();
|
||||
this.inFlight = this.rebuild();
|
||||
try {
|
||||
const next = await this.inFlight;
|
||||
this.cache = next;
|
||||
const invalidatedDuringRebuild = this.invalidatedAt > startedAt;
|
||||
this.cache = invalidatedDuringRebuild ? null : next;
|
||||
return next;
|
||||
} finally {
|
||||
this.inFlight = null;
|
||||
@@ -105,7 +109,7 @@ export class AutoClaimSnapshotManager {
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
private toCandidate(task: TaskDetail, now: number): AutoClaimCandidate {
|
||||
private toCandidate(task: Task, now: number): AutoClaimCandidate {
|
||||
const reference = task.columnMovedAt ?? task.createdAt;
|
||||
const ageMs = Math.max(0, now - Date.parse(reference));
|
||||
const ageHours = ageMs / (1000 * 60 * 60);
|
||||
|
||||
Reference in New Issue
Block a user