fix: bind escalate command to project db
This commit is contained in:
parent
87362f27fc
commit
2aed04608c
2 changed files with 104 additions and 0 deletions
|
|
@ -7,6 +7,7 @@
|
|||
// — apply user choice, clear flag, allow loop to continue
|
||||
//
|
||||
// All operations run against the active project's DB (process.cwd()-rooted).
|
||||
import { ensureDbOpen } from "./bootstrap/dynamic-tools.js";
|
||||
import { readEscalationArtifact, resolveEscalation } from "./escalation.js";
|
||||
import {
|
||||
getActiveMilestoneFromDb,
|
||||
|
|
@ -32,6 +33,7 @@ function parseSliceTask(spec) {
|
|||
return { sliceId: m[1], taskId: m[2] };
|
||||
}
|
||||
export async function handleEscalate(args, ctx) {
|
||||
await ensureDbOpen(process.cwd());
|
||||
if (!isDbAvailable()) {
|
||||
ctx.ui.notify("SF database is not available. Run /sf doctor.", "error");
|
||||
return;
|
||||
|
|
|
|||
102
src/resources/extensions/sf/tests/commands-escalate-db.test.mjs
Normal file
102
src/resources/extensions/sf/tests/commands-escalate-db.test.mjs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/**
|
||||
* commands-escalate-db.test.mjs — `/sf escalate` database binding coverage.
|
||||
*
|
||||
* Purpose: prove escalation commands read the current 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 { handleEscalate } from "../commands-escalate.js";
|
||||
import {
|
||||
closeDatabase,
|
||||
insertMilestone,
|
||||
insertSlice,
|
||||
insertTask,
|
||||
openDatabase,
|
||||
setTaskEscalationPending,
|
||||
} from "../sf-db.js";
|
||||
|
||||
const tmpRoots = [];
|
||||
const originalCwd = process.cwd();
|
||||
|
||||
afterEach(() => {
|
||||
process.chdir(originalCwd);
|
||||
closeDatabase();
|
||||
for (const root of tmpRoots.splice(0)) {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
function makeProject() {
|
||||
const root = mkdtempSync(join(tmpdir(), "sf-escalate-db-"));
|
||||
mkdirSync(join(root, ".sf", "runtime"), { recursive: true });
|
||||
tmpRoots.push(root);
|
||||
return root;
|
||||
}
|
||||
|
||||
function makeCtx() {
|
||||
const notifications = [];
|
||||
return {
|
||||
ctx: {
|
||||
ui: {
|
||||
notify(message, level = "info") {
|
||||
notifications.push({ message, level });
|
||||
},
|
||||
},
|
||||
},
|
||||
notifications,
|
||||
};
|
||||
}
|
||||
|
||||
function seedEscalation(project) {
|
||||
openDatabase(join(project, ".sf", "sf.db"));
|
||||
insertMilestone({ id: "M001", title: "First milestone", status: "active" });
|
||||
insertSlice({
|
||||
milestoneId: "M001",
|
||||
id: "S01",
|
||||
title: "First slice",
|
||||
status: "pending",
|
||||
});
|
||||
insertTask({
|
||||
milestoneId: "M001",
|
||||
sliceId: "S01",
|
||||
id: "T01",
|
||||
title: "Needs decision",
|
||||
status: "pending",
|
||||
});
|
||||
const artifactPath = join(
|
||||
project,
|
||||
".sf",
|
||||
"runtime",
|
||||
"M001-S01-T01-ESCALATION.json",
|
||||
);
|
||||
writeFileSync(
|
||||
artifactPath,
|
||||
JSON.stringify({
|
||||
question: "Use path A or B?",
|
||||
options: [{ id: "a", label: "Path A" }],
|
||||
recommendation: "a",
|
||||
recommendationRationale: "lowest risk",
|
||||
}),
|
||||
"utf-8",
|
||||
);
|
||||
setTaskEscalationPending("M001", "S01", "T01", artifactPath);
|
||||
}
|
||||
|
||||
test("handleEscalate_when_process_switches_projects_opens_current_project_db", async () => {
|
||||
const first = makeProject();
|
||||
const second = makeProject();
|
||||
seedEscalation(first);
|
||||
process.chdir(second);
|
||||
|
||||
const { ctx, notifications } = makeCtx();
|
||||
await handleEscalate("list", ctx);
|
||||
|
||||
assert.equal(notifications.length, 1);
|
||||
assert.equal(notifications[0].level, "info");
|
||||
assert.match(notifications[0].message, /No active milestone/);
|
||||
assert.doesNotMatch(notifications[0].message, /Use path A or B/);
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue