# Erratum — draft-fossil-sync-protocol-00 §8.5.1
| | |
|---|---|
| **Document** | `draft-fossil-sync-protocol-00` |
| **Section** | 8.5.1 — Unversioned-File Synchronisation (client UV upload phase past the output cap); cross-reference §7.3 |
| **Type** | Compatibility note (the draft names one of the two client-sent `uvigot` forms Fossil produces; no requirement on a conforming implementation changes) |
| **Status** | Applied in `draft-fossil-sync-protocol-01` (2026-10-12) |
| **Reported** | 2026-10-11 |
| **Submitter** | Dan Mestas |
| **Found via** | go-libfossil's sync conformance suite (case `sync.8.5.1.no-short-uvigot`); reproduced with two fossil 2.28 [52445a27f1] processes, `fossil server` and `fossil uv sync --httptrace` with `max-upload` set to 1 |
## 1. Summary
§8.5.1 lists what Fossil's client UV upload step can emit: "a full,
mtime-only, or tombstone uvfile, a reference-specific malformed short
uvigot, a SHA3 error, or no card", and says the short uvigot is a Fossil
source defect that a cleanroom sender MUST NOT emit. That is accurate as
far as it goes, and Fossil 2.28 does send the short form. But the same
code path also sends a well-formed `uvigot NAME MTIME HASH SIZE` from the
client for every non-tombstone row once the request has reached
`max-upload`. That card is equally wrong-direction (§7.3: `uvigot` is a
server-to-client announcement), draws the same `error bad command`, and is
the more common of the two, since every non-tombstone row takes it. The
draft does not mention it, so the MUST NOT reads as covering only the
short form.
Fossil is the defective side here; the draft's requirement stands. This
note completes the description so that a server implementer expects both
forms from Fossil clients and a client implementer does not copy either.
## 2. Location
- §8.5.1 (~lines 1729-1743): "regardless of whether that helper emits a
full, mtime-only, or tombstone uvfile, a reference-specific malformed
short uvigot, a SHA3 error, or no card. That short uvigot is a Fossil
source defect: the server treats it as a wrong-direction request and
emits error bad command; a cleanroom sender MUST NOT emit it."
- For comparison, unchanged: §7.3 (~lines 1293-1306), which describes the
same helper on the server side, where both forms are the right
direction.
## 3. The divergence
Fossil 2.28, `src/xfer.c`, `send_unversioned_file()` (`xfer.c:703-760`),
shared by the server's `uvgimme` answer (`xfer.c:1437`) and the client's
upload phase (`xfer.c:2366`):
- `xfer.c:731-737`: if the output already holds `mxSend` bytes, append
`"uvigot %s %lld %s %d\n"` with the stored hash. For a tombstone the hash
is NULL and the field is empty (the short form); otherwise it is a
well-formed five-token `uvigot`. The comment says "This only happens on
the server side", which is not so: on the client `mxSend` is
`max-upload`.
- `xfer.c:1892`: the server answers any client-sent `uvigot` with
`error bad\scommand:\s...`.
Observed (`--httptrace` request and reply files, `max-upload` 1 on the
client, `nobody` holding `oigy` on the server):
```
request 2: uvigot t.txt 1791754279 0 (tombstone)
reply 2: error bad\scommand:\suvigot\st.txt\s1791754279\s\s0\n
request 3: uvigot v.txt 1791754279 076d3fa1...cd3cf4 6 (new content)
reply 3: error bad\scommand:\suvigot\sv.txt\s1791754279\s076d3fa1...\s6\n
```
Each row is removed from the send table after its helper runs, as §8.5.1
says, so neither change reaches the server; `fossil uv sync` still
reports the sync done.
## 4. Impact
None for a conforming implementation. A server receiving either form from
a Fossil client already answers it as an unknown or wrong-direction card
(§7.3, §10.1). The note matters for test authors and for client
implementers reading §8.5.1 as a description of what to emit: a
conformance check that forbids only the short form (four tokens) passes a
client that copies Fossil's full-form `uvigot`.
## 5. Correction
In §8.5.1, replace the two sentences:
```diff
- full, mtime-only, or tombstone uvfile, a reference-specific malformed
- short uvigot, a SHA3 error, or no card. That short uvigot is a
- Fossil source defect: the server treats it as a wrong-direction
- request and emits error bad command; a cleanroom sender MUST NOT
- emit it.
+ full, mtime-only, or tombstone uvfile, a reference-specific uvigot,
+ a SHA3 error, or no card. The reference client emits that uvigot
+ once the request has reached its upload threshold: the well-formed
+ uvigot NAME MTIME HASH SIZE for a row with content, and a malformed
+ short line with an empty HASH field for a tombstone. Both are a
+ Fossil source defect: uvigot is server-to-client only (Section 7.3),
+ the server answers either with error bad command, and the row's
+ change is not uploaded in that session. A cleanroom client MUST NOT
+ send uvigot in either form.
```
## 6. Rationale
- **The requirement is unchanged in substance.** §7.3 already makes
`uvigot` server-to-client; the correction names both forms the reference
client sends and extends the explicit MUST NOT to match.
- **Says what is lost.** Stating that the change is not uploaded tells a
reader why the defect matters, which the current text leaves implicit.
## 7. Verification
- Fossil 2.28 [52445a27f1], macOS arm64: `fossil server --localhost` on a
scratch repository; a clone with one tombstone (`fossil uv rm`, newer
than the server's copy) and one changed file, `max-upload` set to 1,
`fossil uv sync --httptrace`. Traced requests carried the short form for
the tombstone and the full form for the changed file; both drew
`error bad command`; the server's unversioned table was unchanged after
the sync.
- Source references are to `src/xfer.c` at tag `version-2.28`.
- go-libfossil case `sync.8.5.1.no-short-uvigot` (requirement SP-8.5.1-7)
rejects any client-sent `uvigot` that does not have five tokens; under
this correction it would reject a client-sent `uvigot` of any shape.
---
*Prepared against `draft-fossil-sync-protocol-00`. The RFC remains the oracle; the reference implementation is used only to confirm the observable behavior.*