From f3c50de7e746887f42f2870ac8e0706ef654d96d Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Tue, 7 Jul 2026 08:24:35 -0700 Subject: [PATCH] test(engine): stub reconcileSupersededGeneratedFixFeatures on mission-validation-trigger-gap mocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit recoverActiveMissions (mission-execution-loop.ts:263) calls missionStore.reconcileSupersededGeneratedFixFeatures per slice; the 5 MissionExecutionLoop-backed mocks here omitted it, so recovery threw (TypeError) at the slice loop and aborted before processTaskOutcome / ensureFeatureAssertionLinked / startValidatorRun ran — 4 tests failed. Add a no-op stub (matches mission-execution-loop.test.ts reference) with an FNXC:MissionReconcile note. No-op is correct: supersession is not exercised by these tests. --- docs/signals-connectors.md | 2 ++ .../src/__tests__/chat-routes.rooms.test.ts | 7 +++++-- .../__tests__/register-git-github.backfill.test.ts | 2 ++ .../src/__tests__/routes-agent-import.test.ts | 12 +++++++----- .../src/__tests__/routes-run-cited-goals.test.ts | 4 +++- .../src/__tests__/routes-sandbox-audit.test.ts | 4 +++- .../src/__tests__/session-resume-history.test.ts | 2 ++ .../__tests__/task-create-workflow-route.test.ts | 2 +- .../mission-validation-trigger-gap.test.ts | 10 ++++++++++ 9 files changed, 35 insertions(+), 10 deletions(-) diff --git a/docs/signals-connectors.md b/docs/signals-connectors.md index a65059029b..b02d4788d4 100644 --- a/docs/signals-connectors.md +++ b/docs/signals-connectors.md @@ -142,6 +142,8 @@ Configure a GitLab project or group webhook with: Fusion verifies GitLab's `X-Gitlab-Token` header. GitLab's webhook docs now recommend signing tokens for new webhooks, but this connector intentionally supports the documented secret-token compatibility path required by existing GitLab.com and self-managed GitLab installations. This task introduces no GitLab binary, CLI, download, or checksum-managed artifact. +Broader GitHub-to-GitLab parity (issue import, linked tracking, lifecycle automation, and Command Center analytics) is mapped in [GitLab Parity Inventory](./gitlab-parity-inventory.md). These signal webhooks are the GitLab side of that parity surface. + Supported GitLab events: - Project and group **Issue Hook** payloads with `object_kind`/`event_type` of `issue`. diff --git a/packages/dashboard/src/__tests__/chat-routes.rooms.test.ts b/packages/dashboard/src/__tests__/chat-routes.rooms.test.ts index 32937578aa..0eb0ae1474 100644 --- a/packages/dashboard/src/__tests__/chat-routes.rooms.test.ts +++ b/packages/dashboard/src/__tests__/chat-routes.rooms.test.ts @@ -10,8 +10,11 @@ import type { TaskStore } from "@fusion/core"; import { request } from "../test-request.js"; import { createSSE } from "../sse.js"; -class MockStore { - constructor(private readonly rootDir: string, private readonly db: Database) {} +// FNXC:DashboardTests 2026-07-07-08:10: createServer now subscribes via store.on("task:moved") (TaskStore extends EventEmitter) to purge task-planner chats on archive (FN-7337); back the mock store with a real EventEmitter so server startup wiring works instead of throwing "store.on is not a function". +class MockStore extends EventEmitter { + constructor(private readonly rootDir: string, private readonly db: Database) { + super(); + } getRootDir(): string { return this.rootDir; } getFusionDir(): string { return join(this.rootDir, ".fusion"); } diff --git a/packages/dashboard/src/__tests__/register-git-github.backfill.test.ts b/packages/dashboard/src/__tests__/register-git-github.backfill.test.ts index 391fc5fbc3..154f4a29ed 100644 --- a/packages/dashboard/src/__tests__/register-git-github.backfill.test.ts +++ b/packages/dashboard/src/__tests__/register-git-github.backfill.test.ts @@ -16,6 +16,8 @@ function createStore(name: string): TaskStore { getSettings: vi.fn().mockResolvedValue({}), getGlobalSettingsStore: vi.fn(() => ({ getSettings: vi.fn().mockResolvedValue({}) })), logEntry: vi.fn().mockResolvedValue(undefined), + // FNXC:DashboardTests 2026-07-07-08:10: createServer subscribes via store.on("task:moved") to purge task-planner chats on archive (FN-7337); provide a no-op EventEmitter "on" so server startup wiring works instead of throwing "store.on is not a function". + on: vi.fn(), updateTask: vi.fn().mockResolvedValue(undefined), getDatabase: vi.fn().mockReturnValue({ exec: vi.fn(), diff --git a/packages/dashboard/src/__tests__/routes-agent-import.test.ts b/packages/dashboard/src/__tests__/routes-agent-import.test.ts index 9066abd6ec..3d5c874fd9 100644 --- a/packages/dashboard/src/__tests__/routes-agent-import.test.ts +++ b/packages/dashboard/src/__tests__/routes-agent-import.test.ts @@ -57,8 +57,14 @@ vi.mock("node:child_process", async (importOriginal) => { }; }); -vi.mock("@fusion/core", () => { +vi.mock("@fusion/core", async (importOriginal) => { + /* + FNXC:DashboardAgentImportTests 2026-07-07-08:05: + Spread the real @fusion/core module and override only the agent-import seams (AgentStore, ChatStore, the company parsers, and the no-op guard/hook stubs). FN-7444 added planning-summary deepening constants (PLANNING_DEEPEN_PROCEED_OPTION_ID etc.) that src/planning.ts imports from core; a fully hand-written mock omitted them and made createServer fail to load with "No export is defined on the @fusion/core mock". Spreading importOriginal keeps every real export (including future additions) resolvable while the explicit keys below retain the focused mock behavior. This also removes the prior duplicate CLI_AGENT_ADAPTER_IDS / sanitizeCliAgentSettings keys (a merge artifact whose second copy silently won). + */ + const actual = await importOriginal() as Record; return { + ...actual, AgentStore: class MockAgentStore { init = mockInit; listAgents = mockListAgents; @@ -71,11 +77,7 @@ vi.mock("@fusion/core", () => { parseCompanyArchive: (...args: unknown[]) => mockParseCompanyArchive(...args), parseSingleAgentManifest: (...args: unknown[]) => mockParseSingleAgentManifest(...args), prepareAgentCompaniesImport: (...args: unknown[]) => mockPrepareAgentCompaniesImport(...args), - CLI_AGENT_ADAPTER_IDS: ["claude-code", "codex", "droid", "pi", "generic"], - sanitizeCliAgentSettings: (value: unknown) => value, AgentCompaniesParseError: MockAgentCompaniesParseError, - CLI_AGENT_ADAPTER_IDS: ["claude-code", "codex", "droid", "pi", "generic"], - sanitizeCliAgentSettings: () => undefined, isEphemeralAgent: (agent: { metadata?: Record }) => agent?.metadata?.agentKind === "task-worker", deterministicGuardLocks: new Map(), diff --git a/packages/dashboard/src/__tests__/routes-run-cited-goals.test.ts b/packages/dashboard/src/__tests__/routes-run-cited-goals.test.ts index 7f7f9cbd3d..2c4c50b57f 100644 --- a/packages/dashboard/src/__tests__/routes-run-cited-goals.test.ts +++ b/packages/dashboard/src/__tests__/routes-run-cited-goals.test.ts @@ -2,6 +2,7 @@ FNXC:DashboardTests 2026-06-14-09:58: FN-6444 rescues this server route test from the curated skip-list; the fake SQLite statement returns better-sqlite-style mutation metadata so createServer boot sweeps exercise real startup paths. */ +import { EventEmitter } from "node:events"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { request } from "../test-request.js"; @@ -23,7 +24,8 @@ vi.mock("@fusion/core", async () => { }; }); -class MockStore { +// FNXC:DashboardTests 2026-07-07-08:10: createServer now subscribes via store.on("task:moved") (TaskStore extends EventEmitter) to purge task-planner chats on archive (FN-7337); back the mock store with a real EventEmitter so server startup wiring works instead of throwing "store.on is not a function". +class MockStore extends EventEmitter { getRunAuditEvents = mockGetRunAuditEvents; getAgentLogsByTimeRange = vi.fn().mockResolvedValue([]); getMutationsForRun = vi.fn().mockResolvedValue([]); diff --git a/packages/dashboard/src/__tests__/routes-sandbox-audit.test.ts b/packages/dashboard/src/__tests__/routes-sandbox-audit.test.ts index 6cd37b2c77..d76ea498ea 100644 --- a/packages/dashboard/src/__tests__/routes-sandbox-audit.test.ts +++ b/packages/dashboard/src/__tests__/routes-sandbox-audit.test.ts @@ -1,3 +1,4 @@ +import { EventEmitter } from "node:events"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { request } from "../test-request.js"; @@ -20,7 +21,8 @@ vi.mock("../project-store-resolver.js", () => ({ getOrCreateProjectStore: vi.fn(), })); -class MockStore { +// FNXC:DashboardTests 2026-07-07-08:10: createServer now subscribes via store.on("task:moved") (TaskStore extends EventEmitter) to purge task-planner chats on archive (FN-7337); back the mock store with a real EventEmitter so server startup wiring works instead of throwing "store.on is not a function". +class MockStore extends EventEmitter { getRunAuditEvents = mockGetRunAuditEvents; getAgentLogsByTimeRange = vi.fn().mockResolvedValue([]); getMutationsForRun = vi.fn().mockResolvedValue([]); diff --git a/packages/dashboard/src/__tests__/session-resume-history.test.ts b/packages/dashboard/src/__tests__/session-resume-history.test.ts index afd35e1c28..3b9ade10e7 100644 --- a/packages/dashboard/src/__tests__/session-resume-history.test.ts +++ b/packages/dashboard/src/__tests__/session-resume-history.test.ts @@ -49,6 +49,8 @@ vi.mock("@fusion/engine", () => ({ resolvedSkillNames: [], skillSource: "none" as const, })), + // FNXC:DashboardSessionTests 2026-07-07-08:15: planning/mission-interview sessions now resolve MCP servers via resolveMcpServersForStore before createFnAgent; focused engine mock must export it (returning the real empty-runtime shape) so session generation completes instead of throwing on a missing mock export. + resolveMcpServersForStore: vi.fn(async () => ({ servers: [], errors: [] })), createFnAgent: mockCreateFnAgent, })); diff --git a/packages/dashboard/src/routes/__tests__/task-create-workflow-route.test.ts b/packages/dashboard/src/routes/__tests__/task-create-workflow-route.test.ts index 064e383fe4..8b3eab74cd 100644 --- a/packages/dashboard/src/routes/__tests__/task-create-workflow-route.test.ts +++ b/packages/dashboard/src/routes/__tests__/task-create-workflow-route.test.ts @@ -128,7 +128,7 @@ describe("POST /tasks workflowId (U6/R3)", () => { it.each([ ["default coding", "builtin:coding", ["plan-review", "code-review"]], - ["legacy coding", "builtin:legacy-coding", ["code-review"]], + ["legacy coding", "builtin:legacy-coding", ["plan-review", "code-review"]], ["coding per-step review", "builtin:stepwise-coding", ["plan-review", "code-review"]], ])("%s workflow create/select/resolve works end to end", async (_label, workflowId, defaultSteps) => { const res = await post("/api/tasks", { diff --git a/packages/engine/src/__tests__/reliability-interactions/mission-validation-trigger-gap.test.ts b/packages/engine/src/__tests__/reliability-interactions/mission-validation-trigger-gap.test.ts index 2cbfdb9ba9..7c029ddbe0 100644 --- a/packages/engine/src/__tests__/reliability-interactions/mission-validation-trigger-gap.test.ts +++ b/packages/engine/src/__tests__/reliability-interactions/mission-validation-trigger-gap.test.ts @@ -101,6 +101,8 @@ describe("FN-5715 reliability: mission validation trigger gap", () => { listAssertionsForFeature: vi.fn(() => [{ id: "CA-1" }]), getFeature: vi.fn(() => feature), transitionLoopState: vi.fn(), + // FNXC:MissionReconcile 2026-07-07-08:21 real MissionStore method (mission-store.ts:3185); recoverActiveMissions calls it per slice and aborts recovery if missing — stub even when supersession isn't exercised. + reconcileSupersededGeneratedFixFeatures: vi.fn(() => ({ supersededCount: 0, featureIds: [] as string[] })), }; const taskStore = { getTask: vi.fn(async () => ({ id: "FN-001", column: "done" })), @@ -132,6 +134,8 @@ describe("FN-5715 reliability: mission validation trigger gap", () => { listAssertionsForFeature: vi.fn(() => [{ id: "CA-1" }]), getFeature: vi.fn(() => feature), transitionLoopState: vi.fn(), + // FNXC:MissionReconcile 2026-07-07-08:21 real MissionStore method (mission-store.ts:3185); recoverActiveMissions calls it per slice and aborts recovery if missing — stub even when supersession isn't exercised. + reconcileSupersededGeneratedFixFeatures: vi.fn(() => ({ supersededCount: 0, featureIds: [] as string[] })), }; const taskStore = { getTask: vi.fn(async () => ({ id: "FN-001", column: "done" })), @@ -167,6 +171,8 @@ describe("FN-5715 reliability: mission validation trigger gap", () => { listAssertionsForFeature: vi.fn(() => [{ id: "CA-1" }]), getFeature: vi.fn(() => feature), transitionLoopState: vi.fn(), + // FNXC:MissionReconcile 2026-07-07-08:21 real MissionStore method (mission-store.ts:3185); recoverActiveMissions calls it per slice and aborts recovery if missing — stub even when supersession isn't exercised. + reconcileSupersededGeneratedFixFeatures: vi.fn(() => ({ supersededCount: 0, featureIds: [] as string[] })), }; const taskStore = { getTask: vi.fn(async () => ({ id: "FN-001", column: "done" })), @@ -222,6 +228,8 @@ describe("FN-5715 reliability: mission validation trigger gap", () => { getMission: vi.fn(() => ({ id: "M-001", status: "active" })), logMissionEvent: vi.fn(), transitionLoopState: vi.fn(), + // FNXC:MissionReconcile 2026-07-07-08:21 real MissionStore method (mission-store.ts:3185); recoverActiveMissions calls it per slice and aborts recovery if missing — stub even when supersession isn't exercised. + reconcileSupersededGeneratedFixFeatures: vi.fn(() => ({ supersededCount: 0, featureIds: [] as string[] })), setFeatureCurrentTaskRunId: vi.fn(), getFailuresForRun: vi.fn(() => []), }; @@ -289,6 +297,8 @@ describe("FN-5715 reliability: mission validation trigger gap", () => { getMission: vi.fn(() => ({ id: "M-001", status: "active" })), logMissionEvent: vi.fn(), transitionLoopState: vi.fn(), + // FNXC:MissionReconcile 2026-07-07-08:21 real MissionStore method (mission-store.ts:3185); recoverActiveMissions calls it per slice and aborts recovery if missing — stub even when supersession isn't exercised. + reconcileSupersededGeneratedFixFeatures: vi.fn(() => ({ supersededCount: 0, featureIds: [] as string[] })), setFeatureCurrentTaskRunId: vi.fn(), getFailuresForRun: vi.fn(() => []), };