Introduce Phase 1 write-only persistence for merge-request handoff acceptance across core and engine paths. - add persisted merge-request record types, schema/settings plumbing, and store write-path support - update merger/executor/self-healing/run-audit flows to emit and consume the handoff-accepted shadow marker - expand core/engine/roadmap tests and docs to cover the new merge-request shadow contract - add a patch changeset for @runfusion/fusion for this bundled package update Files changed: .changeset/fn-5741-merge-request-shadow.md | 5 + AGENTS.md | 1 + docs/architecture.md | 2 + docs/settings-reference.md | 1 + packages/core/src/__tests__/db-migrate.test.ts | 12 +- packages/core/src/__tests__/db.test.ts | 34 ++-- packages/core/src/__tests__/goals-schema.test.ts | 2 +- packages/core/src/__tests__/insight-store.test.ts | 10 +- .../src/__tests__/merge-request-record.test.ts | 97 +++++++++++ packages/core/src/__tests__/mission-store.test.ts | 2 +- packages/core/src/__tests__/run-audit.test.ts | 2 +- packages/core/src/__tests__/secrets-schema.test.ts | 6 +- .../core/src/__tests__/settings-parity.test.ts | 3 + .../core/src/__tests__/store-merge-queue.test.ts | 2 +- packages/core/src/__tests__/task-documents.test.ts | 2 +- packages/core/src/db.ts | 49 +++++- packages/core/src/index.ts | 4 +- packages/core/src/settings-schema.ts | 30 ++++ packages/core/src/store.ts | 189 ++++++++++++++++++++- packages/core/src/types.ts | 37 ++++ .../src/__tests__/merger-merge-lifecycle.test.ts | 54 ++++++ .../merge-request-shadow-handoff.test.ts | 74 ++++++++ packages/engine/src/executor.ts | 15 +- packages/engine/src/merger.ts | 37 ++++ packages/engine/src/run-audit.ts | 1 + packages/engine/src/self-healing.ts | 16 +- .../src/store/__tests__/roadmap-store.test.ts | 4 +- 27 files changed, 646 insertions(+), 45 deletions(-) Fusion-Task-Id: FN-5741 Fusion-Task-Lineage: 3fec14c3-47ff-4f14-bc02-24021518c992
75 lines
3.0 KiB
TypeScript
75 lines
3.0 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import type { Task } from "@fusion/core";
|
|
import { SelfHealingManager } from "../../self-healing.js";
|
|
|
|
function makeTask(overrides: Partial<Task> = {}): Task {
|
|
return {
|
|
id: "FN-5741-T",
|
|
title: "t",
|
|
description: "d",
|
|
column: "in-progress",
|
|
dependencies: [],
|
|
steps: [{ id: "1", title: "s", status: "done" as const }],
|
|
log: [],
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
...overrides,
|
|
} as Task;
|
|
}
|
|
|
|
describe("FN-5741 reliability interactions: shadow handoff writes", () => {
|
|
it("flag OFF performs no shadow writes", async () => {
|
|
const task = makeTask({ autoMerge: true });
|
|
const store: any = {
|
|
handoffToReview: vi.fn(async () => ({ ...task, column: "in-review" })),
|
|
getSettings: vi.fn(async () => ({ mergeRequestContractShadowEnabled: false })),
|
|
setCompletionHandoffAcceptedMarker: vi.fn(),
|
|
upsertMergeRequestRecord: vi.fn(),
|
|
};
|
|
|
|
const manager = new SelfHealingManager(store, { rootDir: "/repo" });
|
|
await (manager as any).handoffTaskToReview(task.id, "test");
|
|
|
|
expect(store.setCompletionHandoffAcceptedMarker).not.toHaveBeenCalled();
|
|
expect(store.upsertMergeRequestRecord).not.toHaveBeenCalled();
|
|
manager.stop();
|
|
});
|
|
|
|
it("flag ON writes marker + queued record after handoff", async () => {
|
|
const task = makeTask({ autoMerge: true });
|
|
const store: any = {
|
|
handoffToReview: vi.fn(async () => ({ ...task, column: "in-review" })),
|
|
getSettings: vi.fn(async () => ({ mergeRequestContractShadowEnabled: true })),
|
|
setCompletionHandoffAcceptedMarker: vi.fn(),
|
|
upsertMergeRequestRecord: vi.fn(),
|
|
};
|
|
|
|
const manager = new SelfHealingManager(store, { rootDir: "/repo" });
|
|
await (manager as any).handoffTaskToReview(task.id, "test");
|
|
|
|
expect(store.handoffToReview).toHaveBeenCalledOnce();
|
|
expect(store.setCompletionHandoffAcceptedMarker).toHaveBeenCalledWith(task.id, { source: "self-healing:test" });
|
|
expect(store.upsertMergeRequestRecord).toHaveBeenCalledWith(task.id, { state: "queued" });
|
|
expect(store.handoffToReview.mock.invocationCallOrder[0]).toBeLessThan(store.setCompletionHandoffAcceptedMarker.mock.invocationCallOrder[0]);
|
|
manager.stop();
|
|
});
|
|
|
|
it("autoMerge false writes manual-required and never running", async () => {
|
|
const task = makeTask({ autoMerge: false });
|
|
const store: any = {
|
|
handoffToReview: vi.fn(async () => ({ ...task, column: "in-review" })),
|
|
getSettings: vi.fn(async () => ({ mergeRequestContractShadowEnabled: true })),
|
|
setCompletionHandoffAcceptedMarker: vi.fn(),
|
|
upsertMergeRequestRecord: vi.fn(),
|
|
transitionMergeRequestState: vi.fn(),
|
|
};
|
|
|
|
const manager = new SelfHealingManager(store, { rootDir: "/repo" });
|
|
await (manager as any).handoffTaskToReview(task.id, "test");
|
|
|
|
expect(store.upsertMergeRequestRecord).toHaveBeenCalledWith(task.id, { state: "manual-required" });
|
|
expect(store.transitionMergeRequestState).not.toHaveBeenCalled();
|
|
manager.stop();
|
|
});
|
|
});
|