# 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** | Proposed — operator decision pending |
| **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.*
