/** * TillDone — Condition-based task tracking for Speckl. * * Tasks don't tick boxes. They evaluate predicates. * "Done" means a proven condition is met. */ import type { TillDoneCondition, TaskResult } from "./types.js"; export class TillDoneTracker { private conditions: Map = new Map(); /** Register a completion condition for a task. */ register(taskId: string, description: string, predicate: (result: TaskResult) => boolean): void { this.conditions.set(taskId, { taskId, description, predicate, isDone: false, }); } /** Evaluate a task result against its condition. */ evaluate(taskId: string, result: TaskResult): boolean { const condition = this.conditions.get(taskId); if (!condition) { throw new Error(`No TillDone condition registered for task ${taskId}`); } const satisfied = condition.predicate(result); condition.isDone = satisfied; return satisfied; } /** Check if a task's condition has been met. */ isDone(taskId: string): boolean { return this.conditions.get(taskId)?.isDone ?? false; } /** Get all pending (not done) conditions. */ pending(): TillDoneCondition[] { return [...this.conditions.values()].filter((c) => !c.isDone); } /** Get all completed conditions. */ completed(): TillDoneCondition[] { return [...this.conditions.values()].filter((c) => c.isDone); } /** Built-in predicates for common Speckl conditions. */ static predicates = { /** Z3 verification returned SAT (constraints satisfiable). */ z3Verified: (result: TaskResult): boolean => result.status === "success" && (result.output as Record)?.z3Result === "sat", /** All constraints are satisfiable (no contradictions). */ noContradictions: (result: TaskResult): boolean => result.status === "success" && (result.output as Record)?.contradictions === 0, /** Code generation produced valid output. */ codeGenerated: (result: TaskResult): boolean => result.status === "success" && typeof (result.output as Record)?.code === "string", /** Provenance graph is complete for this Speck. */ provenanceComplete: (result: TaskResult): boolean => result.status === "success" && Array.isArray(result.provenance) && result.provenance.length > 0, }; }