# Erratum — draft-fossil-sync-protocol-00 §8.1

| | |
|---|---|
| **Document** | `draft-fossil-sync-protocol-00` |
| **Section** | 8.1 — Clone Protocol Version (server handling of a SEQNO that is not digit-only) |
| **Type** | Compatibility note (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: fossil 2.28 [52445a27f1] answers `clone 3 -1` differently on macOS arm64 and Linux x64 builds |

## 1. Summary

§8.1 assigns `error invalid clone sequence number` only to a digit-only
SEQNO whose value is zero, and says the rule "does not assign that fatal
outcome to wrong arity, noninteger VERSION, or a SEQNO that fails
digit-only recognition". It says nothing about what such a SEQNO does
get. In Fossil 2.28 the answer is undefined: the server reads an
uninitialized variable. The same request, `clone 3 -1`, gets
`clone_seqno 0` from a macOS arm64 build and
`error invalid clone sequence number` from a Linux x64 build.

The draft's grammar (`clone-card = "clone" SP decimal SP decimal LF`) and
its clone loop (§8.2: SEQNO starts at 1 and is replaced only by a decimal
`clone_seqno`) already keep a conforming client from sending such a card,
so no requirement changes. The note records that Fossil gives no stable
answer, so that neither tests nor implementations treat either observed
reply as the reference behavior.

## 2. Location

- §8.1, second paragraph (~lines 1493-1500): "This rule does not assign
  that fatal outcome to wrong arity, noninteger VERSION, or a SEQNO that
  fails digit-only recognition or does not yield that condition."

## 3. The divergence

Fossil 2.28, `src/xfer.c:1550-1570`, in the `clone` handler:

```c
int seqno, max;                         /* seqno is not initialized */
...
blob_is_int(&xfer.aToken[2], &seqno);   /* return value ignored */
if( seqno<=0 ){
  xfer_fatal_error("invalid clone sequence number");
  ...
}
...
if( seqno>max ) seqno = 0;
@ clone_seqno %d(seqno)
```

`blob_is_int()` (`src/blob.c:1057-1071`) stores a value only when every byte
is an ASCII digit; for `-1`, `abc` or `1x` it returns 0 and leaves `seqno`
as whatever the stack held. A non-positive leftover gives the fatal error
and rollback; a positive one greater than the largest RID gives no `cfile`
and `clone_seqno 0`; a positive one within range would serve artifacts
from an arbitrary RID.

Observed on this machine (fossil 2.28 [52445a27f1], Homebrew, macOS arm64),
`fossil http` with an `application/x-fossil-debug` request, three runs each:

| SEQNO | Reply |
|---|---|
| `0` | `error invalid\sclone\ssequence\snumber` |
| `-1` | `clone_seqno 0`, `push SERVERCODE PROJECTCODE`, timestamp |
| `abc` | same as `-1` |
| `1x` | same as `-1` |

The Linux x64 reply (`error invalid\sclone\ssequence\snumber` for
`clone 3 -1`) is from the go-libfossil suite's run on that platform and was
not reproduced here.

## 4. Impact

None for a conforming client, which cannot send the card. A test that pins
either reply for a non-digit SEQNO passes against one Fossil build and
fails against another, and a server implementer looking to Fossil for the
"compatible" answer finds none.

## 5. Correction

In §8.1, append to the second paragraph:

```diff
    parsing later request cards.  This rule does not assign that fatal
    outcome to wrong arity, noninteger VERSION, or a SEQNO that fails
    digit-only recognition or does not yield that condition.
+   Fossil gives no stable reply to a SEQNO that is not digit-only: it
+   tests an uninitialized value, so builds differ between the fatal
+   error, an empty clone_seqno 0 reply, and serving from an arbitrary
+   RID.  Such a card does not match the clone-card grammar (Appendix B)
+   and is never produced by the client loop of Section 8.2; clients
+   MUST send a positive decimal SEQNO, and neither a client nor a test
+   may rely on any particular reply to another value.
```

## 6. Rationale

- **No new obligation.** The grammar and the clone loop already confine a
  client to a positive decimal SEQNO; the MUST makes that explicit at the
  card's definition, where a reader looks for it.
- **Removes a false oracle.** Recording that Fossil's reply depends on an
  uninitialized variable stops either platform's output from being taken
  as the protocol's answer.

## 7. Verification

- Fossil 2.28 [52445a27f1], macOS arm64: the table above, three runs per
  SEQNO, `nobody` holding `og`.
- Source references are to `src/xfer.c` and `src/blob.c` at tag
  `version-2.28`.
- Not verified here: the Linux x64 reply, taken from the go-libfossil
  suite's report.

---

*Prepared against `draft-fossil-sync-protocol-00`. The RFC remains the oracle; the reference implementation is used only to confirm the observable behavior.*
