feat(FN-4124): surface mission interview drafts across mission tooling

- Add dashboard API routes and mission interview store support to list and inspect draft missions
- Update Mission Manager UI to surface draft interview sessions with styling and regression coverage
- Extend CLI mission commands and extension tools to expose draft listings consistently
- Document the draft surfacing behavior and include a changeset for the published CLI package

Fusion-Task-Id: FN-4124
This commit is contained in:
Fusion
2026-05-12 19:38:36 -07:00
committed by gsxdsm
parent e6b1108067
commit af39d474d5
19 changed files with 912 additions and 26 deletions

View File

@@ -526,7 +526,12 @@ describe("bin command routing and fallbacks", () => {
it("routes mission list alias", async () => {
await runBin(["mission", "ls"]);
expect(commandMocks.runMissionList).toHaveBeenCalledWith(undefined);
expect(commandMocks.runMissionList).toHaveBeenCalledWith(undefined, { includeDrafts: true });
});
it("routes mission list with --no-drafts", async () => {
await runBin(["mission", "list", "--no-drafts"]);
expect(commandMocks.runMissionList).toHaveBeenCalledWith(undefined, { includeDrafts: false });
});
it("routes mission show alias", async () => {

View File

@@ -977,7 +977,6 @@ describe.skipIf(!SHOULD_RUN_LEGACY_EXTENSION_INTEGRATION)("fn pi extension (lega
describe("fn_mission_list", () => {
it("returns formatted list of missions", async () => {
// First create a mission
const createTool = api.tools.get("fn_mission_create")!;
await createTool.execute(
"c1",
@@ -1000,6 +999,44 @@ describe.skipIf(!SHOULD_RUN_LEGACY_EXTENSION_INTEGRATION)("fn pi extension (lega
expect(result.content[0].text).toContain("Missions");
expect(result.content[0].text).toContain("Summary:");
});
it("includes mission interview drafts by default and exposes them in details", async () => {
const store = new TaskStore(tmpDir);
await store.init();
store.getDatabase().prepare(
`INSERT INTO ai_sessions (id, type, status, title, inputPayload, conversationHistory, currentQuestion, result, thinkingOutput, error, projectId, createdAt, updatedAt, lockedByTab, lockedAt)
VALUES (?, 'mission_interview', 'awaiting_input', ?, '{}', '[]', NULL, NULL, '', NULL, NULL, ?, ?, NULL, NULL)`,
).run("draft-1", "Draft Mission", "2026-05-12T00:00:00.000Z", "2026-05-12T00:00:00.000Z");
const listTool = api.tools.get("fn_mission_list")!;
const result = await listTool.execute("call-1", {}, undefined, undefined, makeCtx(tmpDir));
expect(result.content[0].text).toContain("Drafts (1)");
expect(result.content[0].text).toContain("draft-1: Draft Mission (draft · interview awaiting_input)");
expect(result.details.drafts).toEqual([
{
id: "draft-1",
title: "Draft Mission",
status: "awaiting_input",
updatedAt: "2026-05-12T00:00:00.000Z",
},
]);
});
it("suppresses mission interview drafts when includeDrafts is false", async () => {
const store = new TaskStore(tmpDir);
await store.init();
store.getDatabase().prepare(
`INSERT INTO ai_sessions (id, type, status, title, inputPayload, conversationHistory, currentQuestion, result, thinkingOutput, error, projectId, createdAt, updatedAt, lockedByTab, lockedAt)
VALUES (?, 'mission_interview', 'error', ?, '{}', '[]', NULL, NULL, '', NULL, NULL, ?, ?, NULL, NULL)`,
).run("draft-2", "Hidden Draft", "2026-05-12T00:00:00.000Z", "2026-05-12T00:00:00.000Z");
const listTool = api.tools.get("fn_mission_list")!;
const result = await listTool.execute("call-1", { includeDrafts: false }, undefined, undefined, makeCtx(tmpDir));
expect(result.content[0].text).not.toContain("Drafts");
expect(result.details.drafts).toEqual([]);
});
});
describe("fn_mission_show", () => {