commit 9f315744f6b5f6658fad5cf6256338ea9f3aeb53
parent 558efa98ab0e8bf9e986662dd84839d89b155896
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Fri, 17 Jul 2026 20:16:59 -0400
parakeet-stitch: validate cached window JSON before reusing it
Resume treated any existing win-NNNN.json as a finished window via existsSync.
A 0-byte or truncated file left by a run interrupted mid-write was then trusted
and blew up the final stitch on JSON.parse. Reuse a cached window only when the
file is non-empty and parses back to a { start, doc } payload; otherwise treat
it as absent so the window is re-transcribed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat:
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/scripts/parakeet-stitch.mjs b/scripts/parakeet-stitch.mjs
@@ -298,6 +298,21 @@ async function main() {
}
await writeFile(metaPath, JSON.stringify(meta));
const winPath = (i) => path.join(workDir, `win-${String(i).padStart(4, "0")}.json`);
+ // A cached window counts only if it's non-empty and parses back to a
+ // { start, doc } payload. A 0-byte or truncated file (e.g. from a prior run
+ // interrupted mid-write) is treated as absent so the window is re-transcribed,
+ // rather than being trusted here and crashing the final stitch on JSON.parse.
+ const cachedWindow = async (i) => {
+ if (!existsSync(winPath(i))) return false;
+ try {
+ const raw = await readFile(winPath(i), "utf8");
+ if (!raw.trim()) return false;
+ const parsed = JSON.parse(raw);
+ return parsed != null && typeof parsed === "object" && "doc" in parsed;
+ } catch {
+ return false;
+ }
+ };
// Graceful pause: on SIGTERM/SIGINT, finish the in-flight window then stop.
// Completed window JSONs stay in the work dir; we do NOT stitch (the final
@@ -317,7 +332,7 @@ async function main() {
for (let i = 0; i < N; i += 1) {
const start = offsets[i];
// Resume: reuse a window already transcribed in a prior run.
- if (existsSync(winPath(i))) {
+ if (await cachedWindow(i)) {
progress(`segment ${i + 1}/${N} @${round3(start)}s — cached`);
done += 1;
continue;