Compare commits

..
1 Commits
Author SHA1 Message Date
sneak 2326281860 quak backup writes each file's ML data into its JSON (closes #163)
check / check (push) Failing after 3s
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-05 23:26:16 +00:00
+6 -8
View File
@@ -32,13 +32,11 @@
// 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. // running attempt count, and the last-tried time, and the run continues. `result.failed` — and thus the CLI's exit code — stays non-zero
// `result.failed` — and thus the CLI's exit code — stays non-zero while any // while any failure remains unresolved and clears once every one succeeds. Each
// failure remains unresolved and clears once every one succeeds. Each run // run reconciles the ledger against the files it attempted, so an entry for a
// reconciles the ledger against the files it attempted, so an entry for a file // file that has since left the library (deleted) or this run's scope is dropped
// 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.
// rather than counted forever, which would poison a scheduled backup's exit
// code.
import { import {
lstatSync, lstatSync,
@@ -519,7 +517,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. The next run fetches its ML data again because none is cached. // failed, so the next run fetches it again.
if (includeOriginals) { if (includeOriginals) {
let mlDataError: string | undefined; let mlDataError: string | undefined;
try { try {