import { convexTest } from "convex-test"; import { anyApi, makeFunctionReference } from "convex/server"; import { describe, expect, test } from "vitest"; import type { Id } from "./_generated/dataModel"; import schema from "./schema"; declare global { interface ImportMeta { readonly glob: (pattern: string) => Record Promise>; } } const modules = import.meta.glob("./**/*.ts"); const api = anyApi; const executeFakeAttemptRef = makeFunctionReference< "action", { attemptId: string; scenario: "success" } >("workExecution:executeFakeAttempt"); const identity = { tokenIdentifier: "https://convex.test|planner-user" }; describe("Work planning and execution commands", () => { test("binds approvals to exact versions and admits a fake Run only from Ready", async () => { const t = convexTest({ modules, schema }); const fixture = await t.withIdentity(identity).run(async (ctx) => { const organizationId = await ctx.db.insert("organizations", { createdAt: 1, createdBy: identity.tokenIdentifier, kind: "personal", name: "Personal", }); await ctx.db.insert("organizationMembers", { organizationId, userId: identity.tokenIdentifier, role: "owner", createdAt: 1, }); const projectId = await ctx.db.insert("projects", { createdAt: 1, name: "Zopu", normalizedSourceUrl: "https://example.com/zopu", organizationId, repositoryPath: "puter/zopu", sourceHost: "example.com", sourceUrl: "https://example.com/zopu", updatedAt: 1, }); const workId = await ctx.db.insert("works", { organizationId, projectId, title: "Executable work", objective: "Prove the flow", status: "proposed", createdAt: 1, updatedAt: 1, }); return { projectId, workId }; }); await t .withIdentity(identity) .mutation(api.workPlanning.requestDefinition, { workId: fixture.workId }); const definition = await t .withIdentity(identity) .mutation(api.workPlanning.saveDefinitionProposal, { workId: fixture.workId, payloadJson: JSON.stringify({ problem: "Need a proof", desiredOutcome: "A terminal simulation", affectedUsers: ["user"], inScope: ["fake"], outOfScope: [], acceptanceCriteria: ["terminal"], constraints: [], assumptions: [], questions: [], risk: "low", requiredArtifacts: ["events"], }), }); await t .withIdentity(identity) .mutation(api.workPlanning.approveDefinition, { workId: fixture.workId, version: definition.version, }); const design = await t .withIdentity(identity) .mutation(api.workPlanning.saveDesignProposal, { workId: fixture.workId, payloadJson: JSON.stringify({ impactMap: { files: [], modules: [], risks: [], summary: "small" }, architectureSummary: "fake", fileTreeDelta: [], callFlowDelta: [], keyInterfaces: [], invariants: ["terminal"], concerns: [], slices: [ { id: "slice-1", title: "fake", objective: "fake", observableBehavior: "event", codeBoundaries: ["runtime"], evidenceRequirements: ["event"], verification: ["assert"], reviewRequired: false, dependsOn: [], }, ], evidenceRequirements: ["event"], tradeoffs: [], }), }); await t.withIdentity(identity).mutation(api.workPlanning.approveDesign, { workId: fixture.workId, definitionVersion: definition.version, designVersion: design.version, }); const started = await t .withIdentity(identity) .mutation(api.workExecution.startSimulatedExecution, { workId: fixture.workId, scenario: "success", sliceId: "slice-1", }); expect(started.runId).toBeTruthy(); const state = await t.run(async (ctx) => ctx.db.get(fixture.workId)); expect(state?.status).toBe("executing"); expect(state?.definitionApprovalVersion).toBe(definition.version); expect(state?.designApprovalVersion).toBe(design.version); await t.action(executeFakeAttemptRef, { attemptId: String(started.attemptId), scenario: "success", }); const completed = await t.run(async (ctx) => ({ attempt: await ctx.db .query("workAttempts") .withIndex("by_run_and_number", (q) => q.eq("runId", started.runId)) .unique(), events: await ctx.db.query("workAttemptEvents").collect(), run: await ctx.db.get(started.runId as Id<"workRuns">), work: await ctx.db.get(fixture.workId), })); expect(completed.work?.status).toBe("completed"); expect(completed.run?.terminalClassification).toBe("Succeeded"); expect(completed.attempt?.status).toBe("terminal"); expect(completed.events.length).toBeGreaterThan(0); }); }); const validDefinitionPayload = () => ({ acceptanceCriteria: ["terminal outcome"], affectedUsers: ["user"], assumptions: [], constraints: [], desiredOutcome: "A terminal simulation", inScope: ["fake"], outOfScope: [], problem: "Need a proof", questions: [], requiredArtifacts: ["events"], risk: "low", }); describe("Work planning guards", () => { test("a Definition cannot be revised while a Run is executing", async () => { const t = convexTest({ modules, schema }); const { workId } = await t.withIdentity(identity).run(async (ctx) => { const organizationId = await ctx.db.insert("organizations", { createdAt: 1, createdBy: identity.tokenIdentifier, kind: "personal", name: "Personal", }); await ctx.db.insert("organizationMembers", { createdAt: 1, organizationId, role: "owner", userId: identity.tokenIdentifier, }); const projectId = await ctx.db.insert("projects", { createdAt: 1, name: "Zopu", normalizedSourceUrl: "https://example.com/zopu", organizationId, repositoryPath: "puter/zopu", sourceHost: "example.com", sourceUrl: "https://example.com/zopu", updatedAt: 1, }); const id = await ctx.db.insert("works", { createdAt: 1, definitionApprovalVersion: 1, definitionVersion: 1, designApprovalVersion: 1, designVersion: 1, objective: "Prove the guard", organizationId, projectId, status: "executing", title: "Guarded work", updatedAt: 1, }); return { workId: id }; }); await expect( t .withIdentity(identity) .mutation(api.workPlanning.saveDefinitionProposal, { payloadJson: JSON.stringify(validDefinitionPayload()), workId, }) ).rejects.toThrow(/while a Run is executing/u); }); test("planner questions are persisted as durable Work questions", async () => { const t = convexTest({ modules, schema }); const { workId } = await t.withIdentity(identity).run(async (ctx) => { const organizationId = await ctx.db.insert("organizations", { createdAt: 1, createdBy: identity.tokenIdentifier, kind: "personal", name: "Personal", }); const projectId = await ctx.db.insert("projects", { createdAt: 1, name: "Zopu", normalizedSourceUrl: "https://example.com/zopu", organizationId, repositoryPath: "puter/zopu", sourceHost: "example.com", sourceUrl: "https://example.com/zopu", updatedAt: 1, }); const id = await ctx.db.insert("works", { createdAt: 1, definitionVersion: 1, objective: "Prove questions", organizationId, projectId, status: "awaiting-definition-approval", title: "Questionable work", updatedAt: 1, }); return { workId: id }; }); await t.mutation(api.workPlanning.submitQuestion, { questionJson: JSON.stringify({ alternatives: [], id: "q1", impact: "medium", prompt: "Which runtime?", status: "open", }), token: "test-token", workId, }); const questions = await t.run(async (ctx) => ctx.db.query("workQuestions").collect() ); expect(questions).toHaveLength(1); expect(questions[0]?.questionId).toBe("q1"); expect(questions[0]?.definitionVersion).toBe(1); }); });