AkurAI Build
Menu

popagent

public

Latest change 675427f5d4f07d62c90242bf71bc28d209e2a250 - Harden autonomous workflow and switch to Ornith by AkurAI Build

import { describe, expect, test } from "bun:test";
import type { Agent } from "@mastra/core/agent";
import type { AgentTask } from "./api-types";
import type { AgentExecutionRuntime } from "./agent-execution";
import {
  runSelfUpdateWorkflow,
  type SelfUpdateWorkflowAgents,
  type SelfUpdateWorkflowDependencies,
} from "./self-update-workflow";
import type { SelfUpdateRun } from "./self-update-policy";
const DIFF_DIGEST = "1".repeat(64);

const run: SelfUpdateRun = {
  taskId: "workflow-task",
  workspaceId: "workflow-workspace",
  repositoryPath: "/registered",
  path: "/contained/self-update",
  branch: "autonomous/self-update/test",
  snapshot: { head: "a".repeat(40), branch: "main", remotes: "remote-hash" },
  cloneRemotes: "clone-remote-hash",
  delegations: new Set<string>(),
  deploymentEnabled: false,
};

function task(): AgentTask {
  return {
    id: run.taskId,
    sessionId: "workflow-session",
    scheduleId: "workflow-schedule",
    source: "self-update",
    workspaceId: run.workspaceId,
    prompt: "Improve the contained repository",
    model: "test/model",
    status: "running",
    output: null,
    error: null,
    stepsCompleted: 0,
    progress: null,
    recoveryCount: 0,
    attemptCount: 1,
    maxAttempts: 1,
    nextAttemptAt: null,
    lastErrorClass: null,
    deadLetteredAt: null,
    createdAt: "",
    startedAt: "",
    completedAt: null,
  };
}

function fakeAgent(
  onGenerate: (phase: string) => unknown | Promise<unknown>,
): Agent {
  const fake = {
    __fork: () => fake,
    setBrowser: () => undefined,
    generate: async (_prompt: string, options: { runId?: string }) => ({
      object: await onGenerate(options.runId?.split("-").at(-1) ?? ""),
    }),
  };
  return fake as unknown as Agent;
}

function agents(
  onGenerate: (phase: string) => unknown | Promise<unknown>,
): SelfUpdateWorkflowAgents {
  return {
    researcher: fakeAgent(onGenerate),
    orchistrator: fakeAgent(onGenerate),
    implementer: fakeAgent(onGenerate),
    reviewer: fakeAgent(onGenerate),
  };
}

function dependencies(
  signal: AbortSignal,
  workflowAgents: SelfUpdateWorkflowAgents,
  calls: string[],
  reports: Array<{ state: string; phases: Array<{ id: string; state: string }> }>,
): SelfUpdateWorkflowDependencies {
  let inspections = 0;
  return {
    execution: {
      prepare: async (input: { prompt: string }) => ({
        prompt: input.prompt,
        options: {},
      }),
    } as unknown as AgentExecutionRuntime,
    policy: {
      begin: async () => run,
      observeDelegation: () => undefined,
      inspect: async () => {
        inspections += 1;
        return inspections === 1
          ? {
            changedPaths: [],
            diffCheck: "clean",
            diffEvidence: "",
            diffDigest: DIFF_DIGEST,
            clean: true,
          }
          : {
            changedPaths: ["src/change.ts"],
            diffCheck: "checked",
            diffEvidence: "diff --git a/src/change.ts b/src/change.ts",
            diffDigest: DIFF_DIGEST,
            clean: false,
          };
      },
      verify: async () => {
        calls.push("verify");
        return "verified";
      },
      commit: async () => {
        calls.push("commit");
        return "commit-id";
      },
      accept: async () => {
        calls.push("accept");
      },
      reject: async () => {
        calls.push("reject");
      },
    },
    agents: workflowAgents,
    report: async (detail) => {
      reports.push({
        state: detail.state,
        phases: detail.phases.map((phase) => ({ id: phase.id, state: phase.state })),
      });
    },
    signal,
  };
}

describe("self-update workflow contracts", () => {
  test("no-op completes without implementation, review, or commit", async () => {
    const calls: string[] = [];
    const reports: Array<{ state: string; phases: Array<{ id: string; state: string }> }> = [];
    const result = await runSelfUpdateWorkflow(
      task(),
      "workflow-turn",
      "workflow-trace",
      dependencies(
        new AbortController().signal,
        agents((phase) => {
          if (phase === "research") {
            calls.push("research");
            return { summary: "Nothing requires a source change", evidence: [], relevantPaths: [], noOp: true };
          }
          calls.push("decide");
          return { rationale: "The requested state is already satisfied", noOp: true, verificationCommand: null };
        }),
        calls,
        reports,
      ),
    );

    expect(result).toBe("Self-update completed with no source changes");
    expect(calls).toEqual(["research", "decide", "accept"]);
    expect(reports.at(-1)?.state).toBe("completed");
    expect(reports.at(-1)?.phases.filter((phase) => phase.state === "skipped").map((phase) => phase.id)).toEqual([
      "implement",
      "inspect-change",
      "verify",
      "review",
      "commit",
    ]);
  });

  test("research failure prevents commit", async () => {
    const calls: string[] = [];
    const reports: Array<{ state: string; phases: Array<{ id: string; state: string }> }> = [];
    await expect(runSelfUpdateWorkflow(
      task(),
      "workflow-turn",
      "workflow-trace",
      dependencies(
        new AbortController().signal,
        agents((phase) => {
          calls.push(phase);
          if (phase === "research") throw new Error("research failed");
          throw new Error(`unexpected phase ${phase}`);
        }),
        calls,
        reports,
      ),
    )).rejects.toThrow("Research phase failed: research failed");

    expect(calls).toEqual(["research", "reject"]);
    expect(calls).not.toContain("commit");
    expect(reports.at(-1)?.state).toBe("failed");
    expect(reports.at(-1)?.phases.find((phase) => phase.id === "research")?.state).toBe("failed");
  });

  test("cancellation prevents later phases", async () => {
    const calls: string[] = [];
    const reports: Array<{ state: string; phases: Array<{ id: string; state: string }> }> = [];
    const controller = new AbortController();
    await expect(runSelfUpdateWorkflow(
      task(),
      "workflow-turn",
      "workflow-trace",
      dependencies(
        controller.signal,
        agents((phase) => {
          calls.push(phase);
          if (phase === "research") {
            controller.abort();
            return { summary: "Research complete", evidence: [], relevantPaths: [], noOp: false };
          }
          throw new Error(`unexpected phase ${phase}`);
        }),
        calls,
        reports,
      ),
    )).rejects.toThrow();

    expect(calls).toEqual(["research", "reject"]);
    expect(calls).not.toContain("commit");
    expect(reports.at(-1)?.state).toBe("cancelled");
    expect(reports.at(-1)?.phases.filter((phase) => phase.state === "skipped").map((phase) => phase.id)).toEqual([
      "implement",
      "inspect-change",
      "verify",
      "review",
      "commit",
    ]);
  });
});