fix(FN-680): restore TaskStore project resolution and fresh schema writes

- fix TaskStore.getOrCreateForProject to resolve projects through BackwardCompat and support legacy .fusion/.kb detection
- add regression tests for exact-name project lookup, legacy cwd fallback, and fresh-schema task create/update persistence
- sync the tasks schema definition with missionId support so fresh databases match runtime migrations
- add project overview and selector styles for the multi-project dashboard UI
This commit is contained in:
gsxdsm
2026-04-01 13:48:01 -07:00
parent 27d2f25b42
commit 614c710de8
7 changed files with 724 additions and 108 deletions

View File

@@ -1,5 +1,6 @@
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
import { DatabaseSync } from "node:sqlite";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { TaskStore } from "../store.js";
@@ -10,10 +11,13 @@ function createTempDir(): string {
return mkdtempSync(join(tmpdir(), "kb-compat-test-"));
}
// Helper to create a fake kb project structure
function createFakeKbProject(dir: string): void {
mkdirSync(join(dir, ".kb"), { recursive: true });
writeFileSync(join(dir, ".kb", "kb.db"), "");
// Helper to create a fake fusion project structure for the current store implementation
function createFakeFusionProject(dir: string): void {
const fusionDir = join(dir, ".fusion");
mkdirSync(fusionDir, { recursive: true });
const db = new DatabaseSync(join(fusionDir, "kb.db"));
db.exec("CREATE TABLE IF NOT EXISTS sanity (id INTEGER PRIMARY KEY)");
db.close();
}
describe("TaskStore Backward Compatibility", () => {
@@ -58,7 +62,7 @@ describe("TaskStore Backward Compatibility", () => {
expect(settings).toBeDefined();
});
it("should find project by name when ID not found", async () => {
it("should fall back to exact project name lookup when ID lookup misses", async () => {
const projectDir = join(tempDir, "my-project");
mkdirSync(projectDir, { recursive: true });
@@ -115,13 +119,24 @@ describe("TaskStore Backward Compatibility", () => {
});
it("should fall back to legacy mode when no projects registered", async () => {
// No projects registered in central core
process.chdir(tempDir);
it("should fall back to process.cwd() legacy mode against the current .fusion path", async () => {
const projectDir = join(tempDir, "legacy-project");
mkdirSync(projectDir, { recursive: true });
createFakeFusionProject(projectDir);
process.chdir(projectDir);
const centralDb = join(tempDir, "kb-central.db");
await centralCore.close();
rmSync(centralDb, { force: true });
centralCore = new CentralCore(tempDir);
const store = await TaskStore.getOrCreateForProject(undefined, centralCore);
expect(store).toBeInstanceOf(TaskStore);
const task = await store.createTask({ description: "legacy task" });
expect(task.id).toBe("FN-001");
expect(existsSync(join(projectDir, ".fusion", "kb.db"))).toBe(true);
expect(existsSync(join(projectDir, ".fusion", "tasks", task.id, "task.json"))).toBe(true);
});
it("should throw when project ID not found", async () => {
@@ -130,6 +145,20 @@ describe("TaskStore Backward Compatibility", () => {
).rejects.toThrow('Project "non-existent-project" not found');
});
it("should find project by exact registered name", async () => {
const projectDir = join(tempDir, "Casey");
mkdirSync(projectDir, { recursive: true });
await centralCore.registerProject({
name: "Casey",
path: projectDir,
isolationMode: "in-process",
});
const store = await TaskStore.getOrCreateForProject("Casey", centralCore);
expect(store).toBeInstanceOf(TaskStore);
});
it("should auto-initialize central core if not provided", async () => {
const projectDir = join(tempDir, "my-project");
mkdirSync(projectDir, { recursive: true });