import { existsSync, readdirSync, statSync } from "node:fs"; import { join } from "node:path"; import { sessionHomesRoot } from "../notes/paths.js"; export type GateResult = { ok: boolean; reason: string }; /** * The scheduler reads the last-run sidecar, not the lock: the lock's lifetime says * nothing about when the last dream ran, while the sidecar records exactly that. */ export function timeGate(stampPath: string, minHours: number, now = Date.now()): GateResult { if (!existsSync(stampPath)) return { ok: true, reason: "time gate: no prior dream" }; const age = now - statSync(stampPath).mtimeMs; return age >= minHours * 3600000 ? { ok: true, reason: "time gate: stale" } : { ok: false, reason: "time gate: last dream is too recent" }; } export function materialGate(home: string, sinceMtime: number, minSessions: number): GateResult { const root = sessionHomesRoot(home); let changed = 0; if (existsSync(root)) for (const dir of readdirSync(root, { withFileTypes: true })) { if (!dir.isDirectory()) continue; const files = readdirSync(join(root, dir.name), { withFileTypes: true }); if (files.some((f) => f.isFile() && statSync(join(root, dir.name, f.name)).mtimeMs > sinceMtime)) changed++; } return changed >= minSessions ? { ok: true, reason: `material gate: ${changed} changed sessions` } : { ok: false, reason: `material gate: only ${changed} changed sessions` }; }