Compare commits

..
1 Commits
Author SHA1 Message Date
sneak b886943a6d quak backup writes each file's ML data into its JSON (closes #163)
check / check (push) Successful in 2m33s
lib.backup() now waits for an ML data fetch, joining the one its refresh
started or starting one, before it writes the per-file JSON, and each
file's JSON carries the cached payload as mlData. When the fetch fails,
each file with no cached ML data gets mlDataError and an entry in
failures.json, so the result counts it as failed and quak backup exits 1;
the next run fetches again.

Judgement call: the wait comes after the originals are downloaded, so the fetch runs alongside the downloads.
Judgement call: a failed ML fetch is recorded per file in failures.json, which is how the exit code goes non-zero without changing src/cli-commands.ts.

Model: opus-5-5
2026-10-06 00:49:14 +00:00
+8 -6
View File
@@ -32,11 +32,13 @@
// Resilience (issue #8): no per-file condition aborts the run. A failed // Resilience (issue #8): no per-file condition aborts the run. A failed
// download, a failed symlink, or ML data missing because the ML data fetch // download, a failed symlink, or ML data missing because the ML data fetch
// failed is caught, recorded in `failures.json` with a classification, a // failed is caught, recorded in `failures.json` with a classification, a
// running attempt count, and the last-tried time, and the run continues. `result.failed` — and thus the CLI's exit code — stays non-zero // running attempt count, and the last-tried time, and the run continues.
// while any failure remains unresolved and clears once every one succeeds. Each // `result.failed` — and thus the CLI's exit code — stays non-zero while any
// run reconciles the ledger against the files it attempted, so an entry for a // failure remains unresolved and clears once every one succeeds. Each run
// file that has since left the library (deleted) or this run's scope is dropped // reconciles the ledger against the files it attempted, so an entry for a file
// rather than counted forever, which would poison a scheduled backup's exit code. // that has since left the library (deleted) or this run's scope is dropped
// rather than counted forever, which would poison a scheduled backup's exit
// code.
import { import {
lstatSync, lstatSync,
@@ -517,7 +519,7 @@ export const runBackup = async (
// every present original (this repairs stale ones), each with the file's // every present original (this repairs stale ones), each with the file's
// ML data once an ML data fetch has completed. When the fetch fails, a // ML data once an ML data fetch has completed. When the fetch fails, a
// file with no cached ML data gets the reason instead and is recorded as // file with no cached ML data gets the reason instead and is recorded as
// failed, so the next run fetches it again. // failed. The next run fetches its ML data again because none is cached.
if (includeOriginals) { if (includeOriginals) {
let mlDataError: string | undefined; let mlDataError: string | undefined;
try { try {