FN-5755: harden mission validation recovery flow
Ensure mission feature assertions recover and validate consistently across startup and periodic maintenance. - add FN-5755 changeset and AGENTS reliability backstop note - document canonical zero-assertion auto-pass and assertion validation lifecycle updates in missions docs - expand mission execution loop and reliability interaction tests for startup recovery, periodic replay, and idempotency - wire self-healing maintenance to replay active mission validation recovery via runtime callback Files changed: .../fn-5755-mission-validation-end-to-end.md | 5 ++ AGENTS.md | 1 + docs/missions-completion-contract.md | 15 ++-- docs/missions.md | 4 +- .../src/__tests__/mission-execution-loop.test.ts | 88 +++++++++++++++++++++- .../mission-validation-trigger-gap.test.ts | 42 ++++++++++- packages/engine/src/runtimes/in-process-runtime.ts | 6 ++ packages/engine/src/self-healing.ts | 11 +++ 8 files changed, 159 insertions(+), 13 deletions(-) Fusion-Task-Id: FN-5755 Fusion-Task-Lineage: e54ac4dc-7b8c-4fc9-8cd6-18702708546f
This commit is contained in:
@@ -120,6 +120,41 @@ describe("FN-5715 reliability: mission validation trigger gap", () => {
|
||||
loop.stop();
|
||||
});
|
||||
|
||||
it("recovery trigger for done implementing feature is idempotent across subsequent passes", async () => {
|
||||
const feature = makeFeature({ status: "done", lastValidatorStatus: undefined, loopState: "implementing" });
|
||||
const missionStore = {
|
||||
listMissions: vi.fn(() => [{ id: "M-001", status: "active" }]),
|
||||
getMissionWithHierarchy: vi.fn(() => ({
|
||||
id: "M-001",
|
||||
status: "active",
|
||||
milestones: [{ status: "active", slices: [{ status: "active", features: [feature] }] }],
|
||||
})),
|
||||
listAssertionsForFeature: vi.fn(() => [{ id: "CA-1" }]),
|
||||
getFeature: vi.fn(() => feature),
|
||||
transitionLoopState: vi.fn(),
|
||||
};
|
||||
const taskStore = {
|
||||
getTask: vi.fn(async () => ({ id: "FN-001", column: "done" })),
|
||||
};
|
||||
|
||||
const loop = new MissionExecutionLoop({
|
||||
missionStore: missionStore as any,
|
||||
taskStore: taskStore as any,
|
||||
rootDir: process.cwd(),
|
||||
});
|
||||
const processSpy = vi.spyOn(loop, "processTaskOutcome").mockImplementation(async () => {
|
||||
feature.lastValidatorStatus = "passed";
|
||||
feature.loopState = "passed";
|
||||
});
|
||||
loop.start();
|
||||
|
||||
await loop.recoverActiveMissions();
|
||||
await loop.recoverActiveMissions();
|
||||
|
||||
expect(processSpy).toHaveBeenCalledTimes(1);
|
||||
loop.stop();
|
||||
});
|
||||
|
||||
it("is idempotent for already-passed implementing features", async () => {
|
||||
const feature = makeFeature({ status: "done", lastValidatorStatus: "passed" });
|
||||
const missionStore = {
|
||||
@@ -151,7 +186,7 @@ describe("FN-5715 reliability: mission validation trigger gap", () => {
|
||||
loop.stop();
|
||||
});
|
||||
|
||||
it("recovery replays implementing done tasks with zero assertions and advances loop state", async () => {
|
||||
it("periodic recovery pass replays implementing done tasks with zero assertions and advances loop state", async () => {
|
||||
const feature = makeFeature({ status: "done", lastValidatorStatus: undefined, loopState: "implementing" });
|
||||
const currentFeature = { ...feature };
|
||||
const missionStore = {
|
||||
@@ -187,8 +222,9 @@ describe("FN-5715 reliability: mission validation trigger gap", () => {
|
||||
});
|
||||
loop.start();
|
||||
|
||||
await loop.recoverActiveMissions();
|
||||
await loop.recoverActiveMissions();
|
||||
const periodicMaintenancePass = async () => loop.recoverActiveMissions();
|
||||
await periodicMaintenancePass();
|
||||
await periodicMaintenancePass();
|
||||
|
||||
expect(missionStore.updateFeature).toHaveBeenCalledTimes(1);
|
||||
expect(missionStore.updateFeature).toHaveBeenCalledWith(
|
||||
|
||||
Reference in New Issue
Block a user