FN-5897: surface linked goals in mission read paths
Expose mission-linked goals across mission detail surfaces. - load linked goal records into mission hierarchy reads in core and return them from the mission detail API - show linked goals in mission detail views, add goal-chip navigation, and anchor highlighted goal cards in GoalsView - extend CLI mission output, docs, tests, and add a published changeset for the new read-path support Files changed: .changeset/fn-5897-linked-goals-read-paths.md | 5 +++ docs/missions.md | 6 ++- packages/cli/src/__tests__/extension.test.ts | 48 +++++++++++++++++++++- packages/cli/src/extension.ts | 10 +++++ packages/core/src/__tests__/mission-store.test.ts | 14 +++++++ packages/core/src/mission-store.ts | 29 +++++++++++++ packages/core/src/mission-types.ts | 4 ++ packages/dashboard/app/App.tsx | 16 +++++++- packages/dashboard/app/components/GoalsView.css | 6 +++ packages/dashboard/app/components/GoalsView.tsx | 42 +++++++++++++++++-- packages/dashboard/app/components/MissionManager.css | 44 ++++++++++++++++++++ packages/dashboard/app/components/MissionManager.tsx | 31 +++++++++++++- packages/dashboard/app/components/__tests__/GoalsView.test.tsx | 31 ++++++++++++++ packages/dashboard/app/components/__tests__/MissionManager.test.tsx | 45 ++++++++++++++++++++ packages/dashboard/app/components/mission-types.ts | 2 + packages/dashboard/src/__tests__/mission-e2e.test.ts | 4 ++ packages/dashboard/src/mission-routes.ts | 5 ++- 17 files changed, 332 insertions(+), 10 deletions(-) Fusion-Task-Id: FN-5897 Fusion-Task-Lineage: 5176270d-9885-447a-845b-4e0d69d531a0
This commit is contained in:
@@ -1132,9 +1132,10 @@ describe.skipIf(!SHOULD_RUN_LEGACY_EXTENSION_INTEGRATION)("fn pi extension (lega
|
||||
});
|
||||
|
||||
describe("fn_mission_show", () => {
|
||||
it("returns mission with hierarchy", async () => {
|
||||
// Create mission
|
||||
it("returns mission with hierarchy and linked goals", async () => {
|
||||
const createTool = api.tools.get("fn_mission_create")!;
|
||||
const goalTool = api.tools.get("fn_goal_create")!;
|
||||
const linkTool = api.tools.get("fn_mission_link_goal")!;
|
||||
const created = await createTool.execute(
|
||||
"c1",
|
||||
{ title: "Test Mission" },
|
||||
@@ -1142,6 +1143,20 @@ describe.skipIf(!SHOULD_RUN_LEGACY_EXTENSION_INTEGRATION)("fn pi extension (lega
|
||||
undefined,
|
||||
makeCtx(tmpDir),
|
||||
);
|
||||
const goal = await goalTool.execute(
|
||||
"g1",
|
||||
{ title: "Connect mission work to goals" },
|
||||
undefined,
|
||||
undefined,
|
||||
makeCtx(tmpDir),
|
||||
);
|
||||
await linkTool.execute(
|
||||
"link-1",
|
||||
{ missionId: created.details.missionId, goalId: goal.details.goalId },
|
||||
undefined,
|
||||
undefined,
|
||||
makeCtx(tmpDir),
|
||||
);
|
||||
|
||||
const showTool = api.tools.get("fn_mission_show")!;
|
||||
const result = await showTool.execute(
|
||||
@@ -1154,6 +1169,11 @@ describe.skipIf(!SHOULD_RUN_LEGACY_EXTENSION_INTEGRATION)("fn pi extension (lega
|
||||
|
||||
expect(result.details.mission).toBeDefined();
|
||||
expect(result.content[0].text).toContain("Test Mission");
|
||||
expect(result.content[0].text).toContain("Linked Goals:");
|
||||
expect(result.content[0].text).toContain(`- ${goal.details.goalId}: Connect mission work to goals`);
|
||||
expect(result.details.mission.linkedGoals).toEqual([
|
||||
expect.objectContaining({ id: goal.details.goalId, title: "Connect mission work to goals" }),
|
||||
]);
|
||||
});
|
||||
|
||||
it("renders acceptanceCriteria / verification for milestones, slices, and features", async () => {
|
||||
@@ -1212,6 +1232,30 @@ describe.skipIf(!SHOULD_RUN_LEGACY_EXTENSION_INTEGRATION)("fn pi extension (lega
|
||||
expect(result.details.mission.milestones[0].acceptanceCriteria).toBe(longValue);
|
||||
});
|
||||
|
||||
it("renders an empty linked goals state when no goals are linked", async () => {
|
||||
const createTool = api.tools.get("fn_mission_create")!;
|
||||
const created = await createTool.execute(
|
||||
"c1",
|
||||
{ title: "Mission Without Goals" },
|
||||
undefined,
|
||||
undefined,
|
||||
makeCtx(tmpDir),
|
||||
);
|
||||
|
||||
const showTool = api.tools.get("fn_mission_show")!;
|
||||
const result = await showTool.execute(
|
||||
"call-1",
|
||||
{ id: created.details.missionId },
|
||||
undefined,
|
||||
undefined,
|
||||
makeCtx(tmpDir),
|
||||
);
|
||||
|
||||
expect(result.content[0].text).toContain("Linked Goals:");
|
||||
expect(result.content[0].text).toContain("No linked goals.");
|
||||
expect(result.details.mission.linkedGoals).toEqual([]);
|
||||
});
|
||||
|
||||
it("returns error when mission not found", async () => {
|
||||
const showTool = api.tools.get("fn_mission_show")!;
|
||||
const result = await showTool.execute(
|
||||
|
||||
@@ -2644,6 +2644,16 @@ export default function kbExtension(pi: ExtensionAPI) {
|
||||
}
|
||||
lines.push("");
|
||||
|
||||
lines.push("Linked Goals:");
|
||||
if ((mission.linkedGoals?.length ?? 0) === 0) {
|
||||
lines.push("No linked goals.");
|
||||
} else {
|
||||
for (const goal of mission.linkedGoals ?? []) {
|
||||
lines.push(`- ${goal.id}: ${goal.title}`);
|
||||
}
|
||||
}
|
||||
lines.push("");
|
||||
|
||||
if (mission.milestones.length === 0) {
|
||||
lines.push("No milestones yet.");
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user