FN-5920: prefer mission detail event count for activity tab

Use mission detail event counts as the authoritative pre-load source for mission activity.

- add mission hierarchy eventCount support in core and dashboard mission types
- return mission eventCount from mission detail APIs and document the field
- prefer the detail eventCount over stale list summaries before activity events load
- add coverage for store, API, and MissionManager pre-load count behavior

Files changed:
 docs/missions.md                                   |  2 +-
 packages/core/src/__tests__/mission-store.test.ts  | 18 +++++++++
 packages/core/src/mission-store.ts                 |  6 +++
 packages/core/src/mission-types.ts                 |  2 +
 packages/dashboard/app/api/legacy.ts               |  2 +
 packages/dashboard/app/components/MissionManager.tsx    | 12 +++++-
 packages/dashboard/app/components/__tests__/MissionManager.test.tsx   | 46 +++++++++++++++++++++-
 packages/dashboard/app/components/mission-types.ts |  1 +
 packages/dashboard/src/__tests__/mission-e2e.test.ts    |  2 +
 9 files changed, 86 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-5920

Fusion-Task-Lineage: a0269499-d34a-495d-89fc-0534bb62a94e
This commit is contained in:
gsxdsm
2026-06-02 23:47:32 -07:00
parent 6d3b1c9810
commit 0971eced30
9 changed files with 86 additions and 5 deletions

View File

@@ -1920,6 +1920,24 @@ describe("MissionStore", () => {
expect(withHierarchy.linkedGoals).toEqual([]);
});
it("reports detail eventCount consistently with mission summaries", () => {
const mission = store.createMission({ title: "Hierarchy event counts" });
const emptyHierarchy = store.getMissionWithHierarchy(mission.id)!;
const emptySummary = store.getMissionSummary(mission.id);
expect(emptyHierarchy.eventCount).toBe(0);
expect(emptyHierarchy.eventCount).toBe(emptySummary.eventCount);
store.logMissionEvent(mission.id, "mission_started", "started");
store.logMissionEvent(mission.id, "warning", "warning");
store.logMissionEvent(mission.id, "error", "error");
const populatedHierarchy = store.getMissionWithHierarchy(mission.id)!;
const populatedSummary = store.getMissionSummary(mission.id);
expect(populatedHierarchy.eventCount).toBe(3);
expect(populatedHierarchy.eventCount).toBe(populatedSummary.eventCount);
});
});
// ── Transaction Tests ────────────────────────────────────────────────

View File

@@ -723,9 +723,15 @@ export class MissionStore extends EventEmitter<MissionStoreEvents> {
};
});
const eventCountRow = this.db
.prepare("SELECT COUNT(*) AS count FROM mission_events WHERE missionId = ?")
.get(id) as { count?: number | bigint } | undefined;
const eventCount = Number(eventCountRow?.count ?? 0);
return {
...mission,
linkedGoals,
eventCount,
milestones: milestonesWithSlices,
};
}

View File

@@ -461,6 +461,8 @@ export interface SliceWithFeatures extends Slice {
export interface MissionWithHierarchy extends Mission {
/** Goals linked to this mission */
linkedGoals?: Goal[];
/** Unfiltered total of all mission lifecycle events, matching `MissionSummary.eventCount` and `getMissionEvents` `total` with no `eventType` filter */
eventCount?: number;
/** Milestones belonging to this mission, each with their slices */
milestones: Array<MilestoneWithSlices & {
/** Slices with their features loaded */