942ff4b1a4
Two fixes: 1. cmdThreadRemove: always call both removeThreadEntry and removeThreadHistoryEntries regardless of resolved source, preventing race where thread moves from active to history between resolve and delete. 2. Test: add waitUntilRunningFileAbsent before thread show/rm, matching the pattern used by adjacent test cases. Verified 5x consecutive runs with 0 failures. Closes #265
40 lines
1.3 KiB
TypeScript
40 lines
1.3 KiB
TypeScript
import { unlink } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import {
|
|
garbageCollectCas,
|
|
removeThreadEntry,
|
|
removeThreadHistoryEntries,
|
|
} from "@uncaged/workflow-execute";
|
|
import { err, ok, type Result } from "@uncaged/workflow-protocol";
|
|
|
|
import { resolveThreadRecord } from "../../thread-scan.js";
|
|
|
|
export async function cmdThreadRemove(
|
|
storageRoot: string,
|
|
threadId: string,
|
|
): Promise<Result<void, string>> {
|
|
const resolved = await resolveThreadRecord(storageRoot, threadId);
|
|
if (resolved === null) {
|
|
return err(`thread not found: ${threadId}`);
|
|
}
|
|
|
|
// Always clear both stores: between resolve and delete the worker may finish and
|
|
// move the thread from threads.json into history; branching only on resolved.source
|
|
// would skip history removal and leave a dangling row.
|
|
await removeThreadEntry(resolved.bundleDir, threadId);
|
|
const hist = await removeThreadHistoryEntries(resolved.bundleDir, threadId);
|
|
if (!hist.ok) {
|
|
return hist;
|
|
}
|
|
|
|
const infoPath = join(storageRoot, "logs", resolved.bundleHash, `${threadId}.info.jsonl`);
|
|
const runningPath = join(storageRoot, "logs", resolved.bundleHash, `${threadId}.running`);
|
|
|
|
await unlink(infoPath).catch(() => {});
|
|
await unlink(runningPath).catch(() => {});
|
|
|
|
await garbageCollectCas(storageRoot);
|
|
|
|
return ok(undefined);
|
|
}
|