import { describe, expect, it } from "vitest"; import { isSafeGitProjectSourcePath } from "../src/services/gitSourceSync.js"; import { chooseNextAutonomyWork } from "../src/services/autonomyScheduler.js"; import { buildRepositoryAutonomyRoadmap, isRepositoryManagedRoadmap, mergeRepositoryAutonomyRoadmap, } from "../src/services/repositoryAutonomy.js"; const roadmapSource = { path: "ROADMAP.md", content: `# Phoenix roadmap ## M1 — Safe software foundation Build the strict software foundation while lab qualification can happen independently. ## M2 — K3s lab Provision only after the external lab gate is cleared.`, contentSha: "roadmap-sha", }; const backlogSource = (extraTasks: Array> = []) => ({ path: "docs/implementation/backlog.json", contentSha: `backlog-sha-${extraTasks.length}`, content: JSON.stringify({ schema_version: "1.0", project: "Phoenix", execution_enabled: false, tasks: [ { id: "PHX-001", title: "Read-only lab qualification", milestone: "M1", depends_on: [], requirements: ["R02"], plan: "docs/implementation/01-foundation.md", execution: "qualification", status: "blocked_external", evidence: [], verified_commit: null, }, { id: "PHX-002", title: "Strict contracts and development harness", milestone: "M1", depends_on: [], requirements: ["R03"], plan: "docs/implementation/01-foundation.md", execution: "code", status: "planned", evidence: [], verified_commit: null, }, { id: "PHX-003", title: "API, database, durable jobs and outbox", milestone: "M1", depends_on: ["PHX-002"], requirements: ["R04"], plan: "docs/implementation/01-foundation.md", execution: "code", status: "planned", evidence: [], verified_commit: null, }, { id: "PHX-006", title: "Safe K3s bootstrap", milestone: "M2", depends_on: ["PHX-001", "PHX-002"], requirements: ["R06"], plan: "docs/implementation/01-foundation.md", execution: "lab", status: "planned", evidence: [], verified_commit: null, }, ...extraTasks, ], }), }); describe("repository-native Auto Mode", () => { it("syncs planning truth without opening the source filter to arbitrary JSON", () => { expect(isSafeGitProjectSourcePath("ROADMAP.md")).toBe(true); expect(isSafeGitProjectSourcePath("docs/implementation/backlog.json")).toBe(true); expect(isSafeGitProjectSourcePath("docs/implementation/roadmap.json")).toBe(true); expect(isSafeGitProjectSourcePath("docs/implementation/random-data.json")).toBe(false); expect(isSafeGitProjectSourcePath("config/backlog.json")).toBe(false); expect(isSafeGitProjectSourcePath(".env")).toBe(false); }); it("turns a structured repository backlog into dependency-aware autonomous work", () => { const roadmap = buildRepositoryAutonomyRoadmap({ roadmap: roadmapSource, backlog: backlogSource(), importedAt: "2026-09-21T12:00:00.000Z", }); expect(roadmap).toBeDefined(); expect(isRepositoryManagedRoadmap(roadmap)).toBe(true); expect(roadmap?.phases.map((phase) => phase.title)).toEqual([ "M1 — Safe software foundation", "M2 — K3s lab", ]); const items = new Map(roadmap?.phases.flatMap((phase) => phase.items).map((item) => [item.id, item])); expect(items.get("PHX-001")).toMatchObject({ status: "pending", waitingExternal: true, source: "roadmap" }); expect(items.get("PHX-002")).toMatchObject({ status: "pending", scope: "committed" }); expect(items.get("PHX-002")).not.toHaveProperty("waitingExternal"); expect(items.get("PHX-003")).toMatchObject({ dependsOn: ["PHX-002"] }); expect(items.get("PHX-006")).toMatchObject({ waitingExternal: true, dependsOn: ["PHX-001", "PHX-002"] }); expect(roadmap?.phases.every((phase) => phase.brainstormConverged)).toBe(true); const next = chooseNextAutonomyWork(roadmap!.phases, roadmap!.currentPhaseIndex, "balanced"); expect(next?.item.id).toBe("PHX-002"); }); it("does not let a planning refresh newly complete an existing runtime item", () => { const current = buildRepositoryAutonomyRoadmap({ roadmap: roadmapSource, backlog: backlogSource(), importedAt: "2026-09-21T12:00:00.000Z", })!; const currentItem = current.phases.flatMap((phase) => phase.items).find((item) => item.id === "PHX-002")!; currentItem.status = "pending"; const verifiedBacklog = JSON.parse(backlogSource().content) as { tasks: Array> }; const task = verifiedBacklog.tasks.find((entry) => entry.id === "PHX-002")!; task.status = "verified"; task.verified_commit = "deadbeef"; const incoming = buildRepositoryAutonomyRoadmap({ roadmap: { ...roadmapSource, contentSha: "roadmap-sha-verified" }, backlog: { ...backlogSource(), content: JSON.stringify(verifiedBacklog), contentSha: "backlog-sha-verified" }, importedAt: "2026-09-21T13:00:00.000Z", })!; const reconciled = mergeRepositoryAutonomyRoadmap(current, incoming); const item = reconciled.phases.flatMap((phase) => phase.items).find((entry) => entry.id === "PHX-002")!; expect(item.status).toBe("pending"); }); it("preserves verified runtime progress and explicit assignments when repository planning changes", () => { const current = buildRepositoryAutonomyRoadmap({ roadmap: roadmapSource, backlog: backlogSource(), importedAt: "2026-09-21T12:00:00.000Z", })!; const phx002 = current.phases.flatMap((phase) => phase.items).find((item) => item.id === "PHX-002")!; phx002.status = "completed"; phx002.attempts = 2; phx002.deliveryId = "delivery-phx-002"; phx002.completionEvidence = ["merge:abc"]; current.phases[0].items.unshift({ id: "assignment-1", title: "Explicit owner task", instruction: "Keep this assignment across repo refreshes.", source: "user_assignment", status: "pending", attempts: 0, }); const incoming = buildRepositoryAutonomyRoadmap({ roadmap: { ...roadmapSource, contentSha: "roadmap-sha-2" }, backlog: backlogSource([{ id: "PHX-004", title: "Auth and target guard", milestone: "M1", depends_on: ["PHX-003"], requirements: ["R05"], plan: "docs/implementation/01-foundation.md", execution: "code", status: "planned", evidence: [], verified_commit: null, }]), importedAt: "2026-09-21T13:00:00.000Z", })!; const reconciled = mergeRepositoryAutonomyRoadmap(current, incoming); const items = new Map(reconciled.phases.flatMap((phase) => phase.items).map((item) => [item.id, item])); expect(items.get("PHX-002")).toMatchObject({ status: "completed", attempts: 2, deliveryId: "delivery-phx-002", completionEvidence: expect.arrayContaining(["merge:abc"]), }); expect(items.get("assignment-1")).toMatchObject({ source: "user_assignment", status: "pending" }); expect(items.get("PHX-004")).toMatchObject({ dependsOn: ["PHX-003"], status: "pending" }); }); });