import { z } from "zod"; import { Discipline, ResearchMode } from "./modes.js"; export const RunStatus = z.enum([ "draft", "validated", "queued", "provisioning", "running", "quality_check", "quality_failed", "revising", "ready_to_publish", "awaiting_approval", "publishing", "published", "cancelling", "cancelled", "failed", "retrying", ]); export type RunStatus = z.infer; /** Valid transitions of the run state machine (enforced server-side, appended to the event ledger). */ export const RUN_TRANSITIONS: Record = { draft: ["validated", "cancelled"], validated: ["queued", "cancelled"], queued: ["provisioning", "cancelling", "cancelled", "failed"], provisioning: ["running", "quality_check", "retrying", "cancelling", "cancelled", "failed"], running: ["quality_check", "retrying", "cancelling", "failed", "revising"], quality_check: ["quality_failed", "ready_to_publish", "cancelling", "failed"], quality_failed: ["revising", "quality_check", "cancelled", "failed", "ready_to_publish"], revising: ["queued", "retrying", "cancelling", "failed"], ready_to_publish: ["awaiting_approval", "publishing", "cancelled", "failed"], awaiting_approval: ["publishing", "cancelled", "quality_failed"], publishing: ["published", "failed", "ready_to_publish"], published: [], cancelling: ["cancelled", "failed"], cancelled: [], failed: ["retrying", "queued"], retrying: ["queued", "failed", "cancelled"], }; export const ACTIVE_RUN_STATUSES: readonly RunStatus[] = [ "queued", "provisioning", "running", "quality_check", "revising", "retrying", "publishing", ]; export const TERMINAL_RUN_STATUSES: readonly RunStatus[] = ["published", "cancelled", "failed"]; export const STAGES = [ { index: 0, key: "preflight", name: "Preflight" }, { index: 1, key: "framing", name: "Research framing" }, { index: 2, key: "literature", name: "Literature and prior art" }, { index: 3, key: "design", name: "Research design and foundations" }, { index: 4, key: "execute", name: "Execute research" }, { index: 5, key: "robustness", name: "Robustness and falsification" }, { index: 6, key: "manuscript", name: "Manuscript construction" }, { index: 7, key: "review", name: "Independent critical review" }, { index: 8, key: "reproduction", name: "Reproduction and build" }, { index: 9, key: "quality", name: "Quality decision" }, { index: 10, key: "publication", name: "Publication" }, ] as const; export type StageKey = (typeof STAGES)[number]["key"]; export const StageKey = z.enum(STAGES.map((s) => s.key) as [StageKey, ...StageKey[]]); export const WORKER_STAGES: readonly StageKey[] = STAGES.slice(0, 9).map((s) => s.key); export const StageStatus = z.enum(["pending", "running", "completed", "failed", "skipped", "cancelled"]); export type StageStatus = z.infer; export const ComputeProfile = z.enum(["standard", "compute-heavy", "memory-heavy"]); export type ComputeProfile = z.infer; /** Immutable job manifest. Server-authored, never contains secrets. */ export const RunManifest = z.object({ schemaVersion: z.literal(1), runId: z.string().uuid(), projectId: z.string().uuid(), instructionVersionId: z.string().uuid(), instructionVersion: z.number().int().positive(), instructionSha256: z.string().regex(/^[a-f0-9]{64}$/), researchMode: ResearchMode, secondaryModes: z.array(ResearchMode).default([]), discipline: Discipline, computeProfile: ComputeProfile, provider: z.literal("anthropic"), model: z.string().min(1), reviewerModel: z.string().min(1), maxBudgetUsd: z.number().positive().max(2000), maxRuntimeMinutes: z .number() .int() .positive() .max(24 * 60), maxAttempts: z.number().int().min(1).max(5), autoPublish: z.boolean(), qualityPolicyVersion: z.string(), createdAt: z.string().datetime(), createdBy: z.string().uuid(), title: z.string().min(3).max(300), paperNumber: z .string() .regex(/^AS-WP-\d{4}-\d{4}$/) .optional(), }); export type RunManifest = z.infer; export const LaunchRunRequest = z.object({ projectId: z.string().uuid(), instructionVersionId: z.string().uuid(), idempotencyKey: z.string().min(8).max(128), title: z.string().min(3).max(300), researchMode: ResearchMode, secondaryModes: z.array(ResearchMode).max(3).default([]), discipline: Discipline, computeProfile: ComputeProfile.default("standard"), model: z.string().min(1).optional(), maxBudgetUsd: z.number().positive().max(2000).optional(), maxRuntimeMinutes: z .number() .int() .positive() .max(24 * 60) .optional(), maxAttempts: z.number().int().min(1).max(5).default(2), autoPublish: z.boolean().default(false), }); export type LaunchRunRequest = z.infer;