diff --git a/src/resources/extensions/sf/commands-todo.js b/src/resources/extensions/sf/commands-todo.js index 524d6cf5b..ec0a5009c 100644 --- a/src/resources/extensions/sf/commands-todo.js +++ b/src/resources/extensions/sf/commands-todo.js @@ -19,7 +19,7 @@ import { import { dirname, join } from "node:path"; import { projectRoot } from "./commands/context.js"; import { sfRoot } from "./paths.js"; -import { addBacklogItem, isDbAvailable, openDatabase } from "./sf-db.js"; +import { addBacklogItem, openDatabase } from "./sf-db.js"; const _EMPTY_TODO = "# TODO\n\nDump anything here.\n"; const MAX_DUMP_CHARS = 48_000; @@ -301,11 +301,9 @@ function appendBacklogItems(basePath, titles, triageRunId) { const date = new Date().toISOString().slice(0, 10); const triagedAt = new Date().toISOString(); const backlogItems = []; - if (!isDbAvailable()) { - const root = sfRoot(basePath); - mkdirSync(root, { recursive: true }); - openDatabase(join(root, "sf.db")); - } + const root = sfRoot(basePath); + mkdirSync(root, { recursive: true }); + openDatabase(join(root, "sf.db")); for (const title of cleanTitles) { const cleanTitle = title.replace(/^['"]|['"]$/g, ""); const id = addBacklogItem({ diff --git a/src/resources/extensions/sf/tests/commands-todo-db.test.mjs b/src/resources/extensions/sf/tests/commands-todo-db.test.mjs new file mode 100644 index 000000000..d86be1406 --- /dev/null +++ b/src/resources/extensions/sf/tests/commands-todo-db.test.mjs @@ -0,0 +1,60 @@ +/** + * commands-todo-db.test.mjs — TODO triage backlog DB binding coverage. + * + * Purpose: prove TODO triage backlog promotion writes to the requested project + * SQLite DB instead of reusing a previously-open DB from another project. + */ +import assert from "node:assert/strict"; +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, test } from "vitest"; +import { triageTodoDump } from "../commands-todo.js"; +import { closeDatabase, listBacklogItems, openDatabase } from "../sf-db.js"; + +const tmpRoots = []; + +afterEach(() => { + closeDatabase(); + for (const root of tmpRoots.splice(0)) { + rmSync(root, { recursive: true, force: true }); + } +}); + +function makeProject() { + const root = mkdtempSync(join(tmpdir(), "sf-todo-db-")); + mkdirSync(join(root, ".sf"), { recursive: true }); + tmpRoots.push(root); + return root; +} + +function backlogTitles(project) { + closeDatabase(); + openDatabase(join(project, ".sf", "sf.db")); + return listBacklogItems().map((item) => item.title); +} + +test("triageTodoDump_with_backlog_when_db_already_open_writes_requested_project_db", async () => { + const first = makeProject(); + const second = makeProject(); + openDatabase(join(first, ".sf", "sf.db")); + writeFileSync(join(second, "TODO.md"), "# TODO\n\nadd project-local task\n"); + const response = JSON.stringify({ + summary: "triaged", + eval_candidates: [], + implementation_tasks: ["add project-local task"], + memory_requirements: [], + harness_suggestions: [], + docs_or_tests: [], + unclear_notes: [], + }); + + await triageTodoDump(second, async () => response, { + clear: false, + backlog: true, + date: new Date("2026-05-07T00:00:00.000Z"), + }); + + assert.deepEqual(backlogTitles(first), []); + assert.deepEqual(backlogTitles(second), ["add project-local task"]); +});