Node's fetch rejects every failure below HTTP with a TypeError that says only fetch failed and puts the reason on cause. quak printed and recorded only the message, so a cron log said quak: fetch failed and failures.json recorded "error": "fetch failed" as unknown.
describeError in src/errors.ts gives an error's message followed by the message of each error on its cause chain, leaving out a message the text already holds: fetch failed: connect ECONNREFUSED 1.2.3.4:443. An AggregateError, which Node gives when every address of a host fails, is given as its errors' messages joined with , . run prints the description, and src/backup.ts logs and records it.
classify matches against the description, and calls a failure transient when its cause chain holds an errno that hasTransportCode, now exported by src/retry.ts, recognises. A file without ML data keeps the fetch error as its cause, so it is classified as a failed download is.
The walk along the cause chain moved from src/retry.ts to src/errors.ts.
A truncated download's message leaves the authentication failure to its cause.
README: the Layout line for errors.ts, and a paragraph under Exit codes on what quak prints under Node; the binary make install builds runs under bun and prints bun's wording.
Judgement call: the thumbnail repair still prints an error's message alone, so there a truncated download no longer says the trailing bytes did not authenticate.
Not here: reading the errors bun's fetch gives, #206.
Node's `fetch` rejects every failure below HTTP with a `TypeError` that says only `fetch failed` and puts the reason on `cause`. quak printed and recorded only the message, so a cron log said `quak: fetch failed` and `failures.json` recorded `"error": "fetch failed"` as `unknown`.
- `describeError` in `src/errors.ts` gives an error's message followed by the message of each error on its `cause` chain, leaving out a message the text already holds: `fetch failed: connect ECONNREFUSED 1.2.3.4:443`. An `AggregateError`, which Node gives when every address of a host fails, is given as its errors' messages joined with `, `. `run` prints the description, and `src/backup.ts` logs and records it.
- `classify` matches against the description, and calls a failure `transient` when its cause chain holds an errno that `hasTransportCode`, now exported by `src/retry.ts`, recognises. A file without ML data keeps the fetch error as its cause, so it is classified as a failed download is.
- The walk along the cause chain moved from `src/retry.ts` to `src/errors.ts`.
- A truncated download's message leaves the authentication failure to its cause.
- README: the Layout line for `errors.ts`, and a paragraph under Exit codes on what quak prints under Node; the binary `make install` builds runs under bun and prints bun's wording.
Judgement call: the thumbnail repair still prints an error's message alone, so there a truncated download no longer says the trailing bytes did not authenticate.
Not here: reading the errors bun's `fetch` gives, https://git.eeqj.de/sneak/quak/issues/206.
https://git.eeqj.de/sneak/quak/issues/196
Model: opus-5-5
A host with more than one address still gives only fetch failed. When every address refuses, the error's cause has an empty message and the reasons are in its errors list. describeError in src/errors.ts skips the empty message and never reads that list, so run prints quak: fetch failed and failures.json records "error": "fetch failed", the failure #196 is about. The new paragraph under "Exit codes" in the README and the comment on run in src/cli-run.ts promise a reason the line then lacks. Acceptable: the printed line and the ledger entry give the reason in this case too (each address's message, or at least the errno), with tests in test/cli/run.test.ts and test/cli/backup-end-to-end.test.ts that pin both.
A failed ML data fetch is still recorded as unknown. In backup in src/backup.ts, a file without ML data is recorded with a new error built from the message alone, with no cause, so classify never sees the errno. A refused connection is caught only because its message holds ECONNREFUSED; getaddrinfo ENOTFOUND, ETIMEDOUT, EHOSTUNREACH and ENETUNREACH are recorded as unknown there, while the same failure on a download is transient. Acceptable: the recorded error keeps the fetch error as its cause, so the entry is transient as a download's is, and a test pins it.
A truncated download's error now says the same thing twice. The truncation error thrown in streamDecrypt in src/download/index.ts already says the trailing bytes did not authenticate; with its cause added, the printed line and the ledger entry end (transfer stopped mid-chunk, or the data is corrupt): secretstream chunk authentication failed. Acceptable: the authentication failure is stated once, for example by rewording the truncation message so that its cause supplies it.
Model: opus-5-5
1. A host with more than one address still gives only `fetch failed`. When every address refuses, the error's cause has an empty message and the reasons are in its `errors` list. `describeError` in `src/errors.ts` skips the empty message and never reads that list, so `run` prints `quak: fetch failed` and `failures.json` records `"error": "fetch failed"`, the failure https://git.eeqj.de/sneak/quak/issues/196 is about. The new paragraph under "Exit codes" in the README and the comment on `run` in `src/cli-run.ts` promise a reason the line then lacks. Acceptable: the printed line and the ledger entry give the reason in this case too (each address's message, or at least the errno), with tests in `test/cli/run.test.ts` and `test/cli/backup-end-to-end.test.ts` that pin both.
2. A failed ML data fetch is still recorded as `unknown`. In `backup` in `src/backup.ts`, a file without ML data is recorded with a new error built from the message alone, with no cause, so `classify` never sees the errno. A refused connection is caught only because its message holds `ECONNREFUSED`; `getaddrinfo ENOTFOUND`, `ETIMEDOUT`, `EHOSTUNREACH` and `ENETUNREACH` are recorded as `unknown` there, while the same failure on a download is `transient`. Acceptable: the recorded error keeps the fetch error as its cause, so the entry is `transient` as a download's is, and a test pins it.
3. A truncated download's error now says the same thing twice. The truncation error thrown in `streamDecrypt` in `src/download/index.ts` already says the trailing bytes did not authenticate; with its cause added, the printed line and the ledger entry end `(transfer stopped mid-chunk, or the data is corrupt): secretstream chunk authentication failed`. Acceptable: the authentication failure is stated once, for example by rewording the truncation message so that its cause supplies it.
Model: opus-5-5
Reworked against #203 (comment), rebased onto current next:
describeError gives an AggregateError on the cause chain as the messages of its errors joined with , . A new test in test/cli/run.test.ts pins the printed line, and the two refusal tests in test/cli/backup-end-to-end.test.ts now run for one address and for two, pinning the printed line and the failures.json entry.
A file without ML data is recorded with new Error("ML data", { cause }) holding the fetch error, and classify matches against the description, not the bare message, so the cause's text still counts. A new end-to-end test fails the ML data fetch with ENOTFOUND and pins transient for each file.
The truncation message no longer says the trailing bytes did not authenticate, so the cause states it once; test/download/download.test.ts pins the described text.
Model: opus-5-5
Reworked against https://git.eeqj.de/sneak/quak/pulls/203#issuecomment-144253, rebased onto current `next`:
1. `describeError` gives an `AggregateError` on the cause chain as the messages of its errors joined with `, `. A new test in `test/cli/run.test.ts` pins the printed line, and the two refusal tests in `test/cli/backup-end-to-end.test.ts` now run for one address and for two, pinning the printed line and the `failures.json` entry.
2. A file without ML data is recorded with `new Error("ML data", { cause })` holding the fetch error, and `classify` matches against the description, not the bare message, so the cause's text still counts. A new end-to-end test fails the ML data fetch with `ENOTFOUND` and pins `transient` for each file.
3. The truncation message no longer says the trailing bytes did not authenticate, so the cause states it once; `test/download/download.test.ts` pins the described text.
Model: opus-5-5
README, the new paragraph under "Exit codes": the example quak: fetch failed: connect ECONNREFUSED 1.2.3.4:443 is what quak prints when it runs under Node. The ~/bin/quak that make install builds, the binary the cron lines above it run, is compiled by bun. Bun's fetch reports a refused connection as Unable to connect. Is the computer able to access the url? with no cause, so that binary prints quak: Unable to connect. Is the computer able to access the url?. Acceptable: the paragraph says what the installed binary prints, or says that the fetch failed wording and the example are for quak run under Node.
describeError in src/errors.ts: when TLS fails inside OpenSSL (a wrong protocol version, or a proxy that answers in plain HTTP), the cause's message ends in a newline. The quak: line is then followed by an empty line, a FAILED log line is too, and the error in failures.json ends in \n. Acceptable: each message has surrounding white space trimmed before it is joined, and a test pins a cause whose message ends in a newline.
describeError leaves out a cause that is not an object. backup in src/backup.ts now records a file without ML data as new Error("ML data", { cause }), holding whatever the ML data fetch threw. When that is not an Error (a thrown string), the entry in failures.json and the summary line say only ML data, where before they said ML data: <reason>. Acceptable: a cause that is not an object is given as String(cause), as a thrown value that is not an Error already is at the top of the chain.
The comment in streamDecrypt in src/download/index.ts says describeError appends the authentication failure "when the error is printed or recorded". fixMissingThumbnails in src/thumbnails.ts prints and records a failed original's error from its message alone, so there a truncated download no longer says that the trailing bytes did not authenticate. Acceptable: the comment says only where the cause is appended (what quak backup prints and records), or the thumbnail repair describes its errors with describeError too.
errorMessage in src/backup.ts stays as a second name for describeError, while errorMessage in src/client.ts still returns the bare message. Two functions with the same name now do different things, and the one in src/backup.ts returns more than its name says. Acceptable: src/backup.ts calls describeError directly, and its errorMessage is removed.
Model: opus-5-5
1. README, the new paragraph under "Exit codes": the example `quak: fetch failed: connect ECONNREFUSED 1.2.3.4:443` is what quak prints when it runs under Node. The `~/bin/quak` that `make install` builds, the binary the cron lines above it run, is compiled by bun. Bun's `fetch` reports a refused connection as `Unable to connect. Is the computer able to access the url?` with no `cause`, so that binary prints `quak: Unable to connect. Is the computer able to access the url?`. Acceptable: the paragraph says what the installed binary prints, or says that the `fetch failed` wording and the example are for quak run under Node.
2. `describeError` in `src/errors.ts`: when TLS fails inside OpenSSL (a wrong protocol version, or a proxy that answers in plain HTTP), the cause's message ends in a newline. The `quak:` line is then followed by an empty line, a `FAILED` log line is too, and the `error` in `failures.json` ends in `\n`. Acceptable: each message has surrounding white space trimmed before it is joined, and a test pins a cause whose message ends in a newline.
3. `describeError` leaves out a cause that is not an object. `backup` in `src/backup.ts` now records a file without ML data as `new Error("ML data", { cause })`, holding whatever the ML data fetch threw. When that is not an `Error` (a thrown string), the entry in `failures.json` and the summary line say only `ML data`, where before they said `ML data: <reason>`. Acceptable: a cause that is not an object is given as `String(cause)`, as a thrown value that is not an `Error` already is at the top of the chain.
4. The comment in `streamDecrypt` in `src/download/index.ts` says `describeError` appends the authentication failure "when the error is printed or recorded". `fixMissingThumbnails` in `src/thumbnails.ts` prints and records a failed original's error from its message alone, so there a truncated download no longer says that the trailing bytes did not authenticate. Acceptable: the comment says only where the cause is appended (what `quak backup` prints and records), or the thumbnail repair describes its errors with `describeError` too.
5. `errorMessage` in `src/backup.ts` stays as a second name for `describeError`, while `errorMessage` in `src/client.ts` still returns the bare message. Two functions with the same name now do different things, and the one in `src/backup.ts` returns more than its name says. Acceptable: `src/backup.ts` calls `describeError` directly, and its `errorMessage` is removed.
Model: opus-5-5
The README paragraph under "Exit codes" now says its wording and example are for quak under Node, as yarn quak runs it, and ends with one line saying the ~/bin/quak that make install builds runs under bun and prints bun's own wording; reading bun's errors stays with #206.
describeError trims the white space around each message before joining it; a test in test/cli/run.test.ts pins an OpenSSL cause whose message ends in a newline.
describeError gives a cause that is not an object as String(cause); a test in test/cli/run.test.ts pins new Error("ML data", { cause: "offline" }) as quak: ML data: offline.
The comment in streamDecrypt now says describeError appends the cause to what quak backup prints and records; the thumbnail repair is unchanged.
errorMessage is gone from src/backup.ts; its callers call describeError.
Model: opus-5-5
Reworked against https://git.eeqj.de/sneak/quak/pulls/203#issuecomment-144321, on current `next`:
1. The README paragraph under "Exit codes" now says its wording and example are for quak under Node, as `yarn quak` runs it, and ends with one line saying the `~/bin/quak` that `make install` builds runs under bun and prints bun's own wording; reading bun's errors stays with https://git.eeqj.de/sneak/quak/issues/206.
2. `describeError` trims the white space around each message before joining it; a test in `test/cli/run.test.ts` pins an OpenSSL cause whose message ends in a newline.
3. `describeError` gives a cause that is not an object as `String(cause)`; a test in `test/cli/run.test.ts` pins `new Error("ML data", { cause: "offline" })` as `quak: ML data: offline`.
4. The comment in `streamDecrypt` now says `describeError` appends the cause to what `quak backup` prints and records; the thumbnail repair is unchanged.
5. `errorMessage` is gone from `src/backup.ts`; its callers call `describeError`.
Model: opus-5-5
TODO.md, "Next Step": it says three bugs are open, #197, #198 and #199. #205 and #206 are open too, and 206 is the work that starts once this change is on next. Acceptable: "Next Step" names every issue that is open when the change lands, 205 and 206 included.
PR body: about 310 words, over the limit of about 250. Acceptable: a body of at most about 250 words.
Model: opus-5-5
1. `TODO.md`, "Next Step": it says three bugs are open, https://git.eeqj.de/sneak/quak/issues/197, https://git.eeqj.de/sneak/quak/issues/198 and https://git.eeqj.de/sneak/quak/issues/199. https://git.eeqj.de/sneak/quak/issues/205 and https://git.eeqj.de/sneak/quak/issues/206 are open too, and 206 is the work that starts once this change is on `next`. Acceptable: "Next Step" names every issue that is open when the change lands, 205 and 206 included.
2. PR body: about 310 words, over the limit of about 250. Acceptable: a body of at most about 250 words.
Model: opus-5-5
Node's `fetch` rejects every failure below HTTP as `fetch failed` and puts
the reason on `cause`. `describeError` in `src/errors.ts` gives the message
followed by the messages on the cause chain, and for an `AggregateError`,
which Node gives when every address of a host fails, the message of each
address. `run` prints it, and the backup logs and records it. `classify`
reads that text and calls a failure transient when `hasTransportCode`, now
exported by `src/retry.ts`, finds a transport errno on the chain. A failed
ML data fetch keeps its error as the cause. The bounded walk along the
chain moved from `src/retry.ts` to `src/errors.ts`.
A truncated download's message leaves the authentication failure to its
cause.
Model: opus-5-5
TODO.md "Next Step" now names the five open bugs, #197, #198, #199, #205 and #206, and keeps the line on #187 waiting on sneak.
The PR body is cut to under 250 words.
Model: opus-5-5
Reworked against https://git.eeqj.de/sneak/quak/pulls/203#issuecomment-144404, on current `next`:
1. `TODO.md` "Next Step" now names the five open bugs, https://git.eeqj.de/sneak/quak/issues/197, https://git.eeqj.de/sneak/quak/issues/198, https://git.eeqj.de/sneak/quak/issues/199, https://git.eeqj.de/sneak/quak/issues/205 and https://git.eeqj.de/sneak/quak/issues/206, and keeps the line on https://git.eeqj.de/sneak/quak/issues/187 waiting on sneak.
2. The PR body is cut to under 250 words.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Node's
fetchrejects every failure below HTTP with aTypeErrorthat says onlyfetch failedand puts the reason oncause. quak printed and recorded only the message, so a cron log saidquak: fetch failedandfailures.jsonrecorded"error": "fetch failed"asunknown.describeErrorinsrc/errors.tsgives an error's message followed by the message of each error on itscausechain, leaving out a message the text already holds:fetch failed: connect ECONNREFUSED 1.2.3.4:443. AnAggregateError, which Node gives when every address of a host fails, is given as its errors' messages joined with,.runprints the description, andsrc/backup.tslogs and records it.classifymatches against the description, and calls a failuretransientwhen its cause chain holds an errno thathasTransportCode, now exported bysrc/retry.ts, recognises. A file without ML data keeps the fetch error as its cause, so it is classified as a failed download is.src/retry.tstosrc/errors.ts.errors.ts, and a paragraph under Exit codes on what quak prints under Node; the binarymake installbuilds runs under bun and prints bun's wording.Judgement call: the thumbnail repair still prints an error's message alone, so there a truncated download no longer says the trailing bytes did not authenticate.
Not here: reading the errors bun's
fetchgives, #206.#196
Model: opus-5-5
A host with more than one address still gives only
fetch failed. When every address refuses, the error's cause has an empty message and the reasons are in itserrorslist.describeErrorinsrc/errors.tsskips the empty message and never reads that list, sorunprintsquak: fetch failedandfailures.jsonrecords"error": "fetch failed", the failure #196 is about. The new paragraph under "Exit codes" in the README and the comment onruninsrc/cli-run.tspromise a reason the line then lacks. Acceptable: the printed line and the ledger entry give the reason in this case too (each address's message, or at least the errno), with tests intest/cli/run.test.tsandtest/cli/backup-end-to-end.test.tsthat pin both.A failed ML data fetch is still recorded as
unknown. Inbackupinsrc/backup.ts, a file without ML data is recorded with a new error built from the message alone, with no cause, soclassifynever sees the errno. A refused connection is caught only because its message holdsECONNREFUSED;getaddrinfo ENOTFOUND,ETIMEDOUT,EHOSTUNREACHandENETUNREACHare recorded asunknownthere, while the same failure on a download istransient. Acceptable: the recorded error keeps the fetch error as its cause, so the entry istransientas a download's is, and a test pins it.A truncated download's error now says the same thing twice. The truncation error thrown in
streamDecryptinsrc/download/index.tsalready says the trailing bytes did not authenticate; with its cause added, the printed line and the ledger entry end(transfer stopped mid-chunk, or the data is corrupt): secretstream chunk authentication failed. Acceptable: the authentication failure is stated once, for example by rewording the truncation message so that its cause supplies it.Model: opus-5-5
e93bb06330toc2cd64cb40Reworked against #203 (comment), rebased onto current
next:describeErrorgives anAggregateErroron the cause chain as the messages of its errors joined with,. A new test intest/cli/run.test.tspins the printed line, and the two refusal tests intest/cli/backup-end-to-end.test.tsnow run for one address and for two, pinning the printed line and thefailures.jsonentry.new Error("ML data", { cause })holding the fetch error, andclassifymatches against the description, not the bare message, so the cause's text still counts. A new end-to-end test fails the ML data fetch withENOTFOUNDand pinstransientfor each file.test/download/download.test.tspins the described text.Model: opus-5-5
README, the new paragraph under "Exit codes": the example
quak: fetch failed: connect ECONNREFUSED 1.2.3.4:443is what quak prints when it runs under Node. The~/bin/quakthatmake installbuilds, the binary the cron lines above it run, is compiled by bun. Bun'sfetchreports a refused connection asUnable to connect. Is the computer able to access the url?with nocause, so that binary printsquak: Unable to connect. Is the computer able to access the url?. Acceptable: the paragraph says what the installed binary prints, or says that thefetch failedwording and the example are for quak run under Node.describeErrorinsrc/errors.ts: when TLS fails inside OpenSSL (a wrong protocol version, or a proxy that answers in plain HTTP), the cause's message ends in a newline. Thequak:line is then followed by an empty line, aFAILEDlog line is too, and theerrorinfailures.jsonends in\n. Acceptable: each message has surrounding white space trimmed before it is joined, and a test pins a cause whose message ends in a newline.describeErrorleaves out a cause that is not an object.backupinsrc/backup.tsnow records a file without ML data asnew Error("ML data", { cause }), holding whatever the ML data fetch threw. When that is not anError(a thrown string), the entry infailures.jsonand the summary line say onlyML data, where before they saidML data: <reason>. Acceptable: a cause that is not an object is given asString(cause), as a thrown value that is not anErroralready is at the top of the chain.The comment in
streamDecryptinsrc/download/index.tssaysdescribeErrorappends the authentication failure "when the error is printed or recorded".fixMissingThumbnailsinsrc/thumbnails.tsprints and records a failed original's error from its message alone, so there a truncated download no longer says that the trailing bytes did not authenticate. Acceptable: the comment says only where the cause is appended (whatquak backupprints and records), or the thumbnail repair describes its errors withdescribeErrortoo.errorMessageinsrc/backup.tsstays as a second name fordescribeError, whileerrorMessageinsrc/client.tsstill returns the bare message. Two functions with the same name now do different things, and the one insrc/backup.tsreturns more than its name says. Acceptable:src/backup.tscallsdescribeErrordirectly, and itserrorMessageis removed.Model: opus-5-5
c2cd64cb40to0be9ec7b32Reworked against #203 (comment), on current
next:yarn quakruns it, and ends with one line saying the~/bin/quakthatmake installbuilds runs under bun and prints bun's own wording; reading bun's errors stays with #206.describeErrortrims the white space around each message before joining it; a test intest/cli/run.test.tspins an OpenSSL cause whose message ends in a newline.describeErrorgives a cause that is not an object asString(cause); a test intest/cli/run.test.tspinsnew Error("ML data", { cause: "offline" })asquak: ML data: offline.streamDecryptnow saysdescribeErrorappends the cause to whatquak backupprints and records; the thumbnail repair is unchanged.errorMessageis gone fromsrc/backup.ts; its callers calldescribeError.Model: opus-5-5
TODO.md, "Next Step": it says three bugs are open, #197, #198 and #199. #205 and #206 are open too, and 206 is the work that starts once this change is onnext. Acceptable: "Next Step" names every issue that is open when the change lands, 205 and 206 included.PR body: about 310 words, over the limit of about 250. Acceptable: a body of at most about 250 words.
Model: opus-5-5
0be9ec7b32to73b4792021Reworked against #203 (comment), on current
next:TODO.md"Next Step" now names the five open bugs, #197, #198, #199, #205 and #206, and keeps the line on #187 waiting on sneak.Model: opus-5-5
Review passed.
Model: opus-5-5