Artifact a4ffe7f067119cb5e435b08746d7c1d0f3d37a40370fbadc43dab9c239734963:
- File
docs/RELEASE-NOTES-0.3.20.md
— part of check-in
[d11082197c]
at
2026-09-17 15:27:18
on branch trunk
— Release 0.3.20
Server error cards are encoded so a fossil client reads a refusal; remote refuses a non-URL. (user: dmestas, size: 3142) [annotate] [blame] [check-ins using]
# zit 0.3.20 zit 0.3.20 fixes a server refusal that a stock fossil client could not read, and a `remote` verb that saved whatever it was handed. No artifact, tag namespace, or on-disk repository format changed. Two error cards change their encoding on the wire, to the encoding the protocol already requires of them. ## A refused push is now refused out loud When a zit server rejects a request card, it resets its response and answers with one error card. The protocol writes those answers as prose, `error bad command: LINE` and `error malformed CARD line`, and zit emitted exactly that prose: the words separated by real spaces. The auth, sequence-number, sparse and content-policy refusals went through the encoder and arrived as one token; these two did not. A fossil client reads an error card as exactly two tokens, the operator and one encoded text. Four tokens is not an error card to it: it parses `error malformed`, reports the trailing words as unknown commands, discards the lot, and prints its success summary. On a forge that ran for fifteen hours: every push said `Artifacts sent: 94`, every push sent the same 94 again, and a fresh clone had none of them. `zit push` against the same server said "the peer refused" in the same minute, because zit's own client tolerates the malformed card. Every server error card is now assembled at one point, with its text encoded. The wire texts the protocol pins are unchanged; their spaces are `\s`, as fossil's own server writes them. A unit test drives every refusal variant through the emitter and fails if any card has a raw space after its operator, so a new fault cannot reintroduce this. The sync draft's prose describes these texts unencoded, which is how they were transcribed here. That is an erratum against the draft's wording, not its meaning: the card grammar in the same document already says TEXT is encoded. ## `zit remote` refuses what is not a URL `zit remote list` saved the word `list` as the remote and reported success. The saved remote was gone, and the mistake surfaced at the next push, with an error that no longer looked related. Git trained everyone that `remote` has subcommands, so this was a natural thing to type. The verb's help already defined its argument as an http:// or https:// address. The parser now holds it to that: any other positional is refused with a usage error, the saved remote is untouched, and the message says plainly that there are no subcommands and which form shows, sets, or clears the remote. Storing a valid URL verbatim is unchanged. ## Compatibility - No on-disk repository or wire format changed; Fossil RFC revision 00 remains the target. The `bad command` and `malformed` error cards are now encoded as ยง11.1 requires; a zit client accepted both forms before and still does. - `zit remote` exits 2 for a non-URL argument where it used to exit 0 and overwrite the remote. Nothing else about the verb changed. ## Qualification `zig build test`, `e2e`, `smoke` (Fossil interop against a real `fossil`), `muscle`, `git-import-e2e`, `git-export-e2e`, `dst`, and `cross`, plus the three-runner CI battery.