// Recipe success/failure records (SDD section 7.1). Stored as an entry per recipe canonical_id. // This is the "fuel" for submission-with-proof: real 2xx counts beat self-written tests. import type { Bindings } from '../types'; interface RecipeStat { canonical_id: string; success_count: number; failure_count: number; last_status: string | null; last_at: number | null; } // One entry per recipe: id = recipestat:{canonical_id}, entry_type='recipe_stat', // counters live in metadata_json. Atomic upsert via D1. function statId(canonicalId: string): string { return `recipestat:${canonicalId}`; } export async function recordRecipeResult(db: D1Database, canonicalId: string, ok: boolean, nowMs: number): Promise { const id = statId(canonicalId); const existing = await db.prepare('SELECT metadata_json FROM entries WHERE id = ?').bind(id).first<{ metadata_json: string | null }>(); let stat: RecipeStat; if (existing) { const prev = existing.metadata_json ? (JSON.parse(existing.metadata_json) as RecipeStat) : emptyStat(canonicalId); stat = { canonical_id: canonicalId, success_count: prev.success_count + (ok ? 1 : 0), failure_count: prev.failure_count + (ok ? 0 : 1), last_status: ok ? 'success' : 'failure', last_at: nowMs, }; await db .prepare('UPDATE entries SET metadata_json = ?, updated_at = unixepoch() WHERE id = ?') .bind(JSON.stringify(stat), id) .run(); } else { stat = { canonical_id: canonicalId, success_count: ok ? 1 : 0, failure_count: ok ? 0 : 1, last_status: ok ? 'success' : 'failure', last_at: nowMs, }; await db .prepare('INSERT INTO entries (id, content, entry_type, metadata_json) VALUES (?, ?, ?, ?)') .bind(id, canonicalId, 'recipe_stat', JSON.stringify(stat)) .run(); } return stat; } export async function getRecipeStat(db: D1Database, canonicalId: string): Promise { const row = await db.prepare('SELECT metadata_json FROM entries WHERE id = ?').bind(statId(canonicalId)).first<{ metadata_json: string | null }>(); if (!row || !row.metadata_json) return emptyStat(canonicalId); return JSON.parse(row.metadata_json) as RecipeStat; } function emptyStat(canonicalId: string): RecipeStat { return { canonical_id: canonicalId, success_count: 0, failure_count: 0, last_status: null, last_at: null }; } export type { RecipeStat };