FN-5741: persist merge-request handoff shadow contract

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
This commit is contained in:
gsxdsm
2026-05-30 14:42:50 -07:00
parent ac72f17cdb
commit 20c1c3261e
27 changed files with 646 additions and 45 deletions

View File

@@ -0,0 +1,74 @@
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();
});
});