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", { 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 workId = await ctx.db.insert("works", { createdAt: 1, objective: "Prove the flow", organizationId, projectId, status: "proposed", title: "Executable work", 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, { payloadJson: JSON.stringify({ acceptanceCriteria: ["terminal"], affectedUsers: ["user"], assumptions: [], constraints: [], desiredOutcome: "A terminal simulation", inScope: ["fake"], outOfScope: [], problem: "Need a proof", questions: [], requiredArtifacts: ["events"], risk: "low", }), workId: fixture.workId, }); await t .withIdentity(identity) .mutation(api.workPlanning.approveDefinition, { version: definition.version, workId: fixture.workId, }); const design = await t .withIdentity(identity) .mutation(api.workPlanning.saveDesignProposal, { payloadJson: JSON.stringify({ architectureSummary: "fake", callFlowDelta: [], concerns: [], evidenceRequirements: ["event"], fileTreeDelta: [], impactMap: { files: [], modules: [], risks: [], summary: "small" }, invariants: ["terminal"], keyInterfaces: [], slices: [ { codeBoundaries: ["runtime"], dependsOn: [], evidenceRequirements: ["event"], id: "slice-1", objective: "fake", observableBehavior: "event", reviewRequired: false, title: "fake", verification: ["assert"], }, ], tradeoffs: [], }), workId: fixture.workId, }); await t.withIdentity(identity).mutation(api.workPlanning.approveDesign, { definitionVersion: definition.version, designVersion: design.version, workId: fixture.workId, }); const started = await t .withIdentity(identity) .mutation(api.workExecution.startSimulatedExecution, { scenario: "success", sliceId: "slice-1", workId: fixture.workId, }); 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_runId_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", }); const validDesignPayload = (sliceId = "slice-1") => ({ architectureSummary: "fake", callFlowDelta: [], concerns: [], evidenceRequirements: ["event"], fileTreeDelta: [], impactMap: { files: [], modules: [], risks: [], summary: "small" }, invariants: ["terminal"], keyInterfaces: [], slices: [ { codeBoundaries: ["runtime"], dependsOn: [], evidenceRequirements: ["event"], id: sliceId, objective: "fake", observableBehavior: "event", reviewRequired: false, title: "fake", verification: ["assert"], }, ], tradeoffs: [], }); describe("Work planning guards", () => { test("persists high-impact questions but blocks approval", 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, objective: "Resolve a critical question", organizationId, projectId, status: "defining", title: "Question gate", updatedAt: 1, }); return { workId: id }; }); const saved = await t .withIdentity(identity) .mutation(api.workPlanning.saveDefinitionProposal, { payloadJson: JSON.stringify({ ...validDefinitionPayload(), questions: [ { alternatives: ["public", "private"], id: "visibility", impact: "high", prompt: "Should the endpoint be public?", status: "open", }, ], }), workId, }); await expect( t.withIdentity(identity).mutation(api.workPlanning.approveDefinition, { version: saved.version, workId, }) ).rejects.toThrow(/High-impact open questions/u); const stored = await t.run(async (ctx) => ctx.db.query("workQuestions").collect() ); expect(stored).toHaveLength(1); expect(stored[0]?.status).toBe("open"); }); test("keeps prior Design slices as versioned history", 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, objective: "Preserve Design history", organizationId, projectId, status: "designing", title: "Versioned Design", updatedAt: 1, }); return { workId: id }; }); await t .withIdentity(identity) .mutation(api.workPlanning.saveDesignProposal, { payloadJson: JSON.stringify(validDesignPayload("slice-v1")), workId, }); await t .withIdentity(identity) .mutation(api.workPlanning.saveDesignProposal, { payloadJson: JSON.stringify(validDesignPayload("slice-v2")), workId, }); const slices = await t.run(async (ctx) => ctx.db.query("workSlices").collect() ); expect(slices.map((slice) => slice.designVersion).sort()).toEqual([1, 2]); }); 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); }); });