Skip to content
zit Artifact [dc53e482e1]
Login

Artifact [dc53e482e1]

Artifact dc53e482e166e18ea4aeb1e8de55812f46be969f2f8a44de195009ab851ae941:


# Erratum: draft-fossil-artifact-format-01 §4.4, §4.5.1 and §6.2

| | |
|---|---|
| **Document** | `draft-fossil-artifact-format-01` |
| **Section** | 4.4, Card Ordering ("Nothing follows Z"); 4.5.1, Occurrence Classes; 6.2, Z Card: Artifact Self-Checksum |
| **Type** | Editorial (the reason given for the Z card being last is wrong; the verification states are described by card rather than by byte position) |
| **Status** | Applied in `draft-fossil-artifact-format-01` (2026-10-16) |
| **Reported** | 2026-10-16 |
| **Submitter** | Dan Mestas |
| **Found via** | A reader working from the draft text alone; confirmed by running Fossil 2.28 and reading its source |

## 1. Summary

§4.4 and §4.5.1 say the `Z` card's must-be-last property follows from the
checksum framing, which "fixes the Z card as occupying exactly the final 35
bytes". The framing fixes nothing: it reads the final 35 bytes by position
and treats them as a `Z` card when the first two are `Z` and a space,
whether or not a card line begins there. The `Z` card is last for two other
reasons: the ordering rule admits only another `Z` after a `Z`, and every
artifact type requires a `Z` card (Table 4). Its 32-digit token then puts it
in the final 35 bytes.

§6.2's state table names the absent and mismatch states by whether a `Z`
card is present. Fossil decides them by the bytes at offsets n-35 and n-34,
so an artifact with no `Z` card can reach the mismatch state.

## 2. The text in error

Page 14 (§4.4):

> Z is the lexicographically last defined letter, the ordering rule admits
> no letter after it (the N-after-P exception does not involve Z), and the
> checksum-verification framing fixes the Z card as occupying exactly the
> final 35 bytes of the stripped content: Z, space, 32 hexadecimal digits,
> LF. The Z card's must-be-last property is enforced structurally — by
> ordering plus byte-position framing — rather than by an explicit
> last-card check in the Z card grammar, which validates only token syntax.

Page 15 (§4.5.1):

> No separate rule requires the Z card to be exactly one and last; that
> property follows from the framing.

Page 33 (§6.2, the state table):

> absent: no Z card present, or artifact too short to hold one
> mismatch: Z card present but token differs

## 3. What Fossil implements

`verify_z_card()` in `src/manifest.c`:

```
if( n<35 ) return 0;
if( z[n-35]!='Z' || z[n-34]!=' ' ) return 0;
md5sum_init();
md5sum_step_text(z, n-35);
zHash = md5sum_finish(0);
if( memcmp(&z[n-33], zHash, 32)==0 ){
  return 1;
}else{
  ...
  return 2;
}
```

It looks at two bytes, not at line boundaries. `manifestCardTypes` lists
`Z` among the required cards of all eight types, and the ordering check in
`manifest_parse()` rejects any letter that sorts below the one before it.

Checked with Fossil 2.28 [52445a27f1], `fossil test-parse-manifest`, on a
control artifact with no `Z` card whose last line is a `U` card ending in
`Z` and a 32-character token:

| Last line | Result |
|---|---|
| `U aliceZ 0123456789abcdef0123456789abcdef` | `incorrect Z-card cksum` |
| `U aliceZ` followed by the MD5 of the bytes before that `Z` | `line 3: extra characters at end of card` |
| `U alice` | `Z-card missing in tag` |

The first artifact has no `Z` card and still fails the checksum. All three
are rejected; only the reason differs.

## 4. Corrected text

§4.4, replace the bullet with:

> Nothing follows Z. Z is the lexicographically last defined letter and
> the ordering rule admits no letter after it (the N-after-P exception
> does not involve Z), so only another Z card can follow a Z card. Every
> artifact type requires a Z card (Section 5.2), so the last line of an
> accepted artifact is a Z card, and because its token is exactly 32
> hexadecimal digits it occupies exactly the final 35 bytes of the
> stripped content: Z, space, 32 hexadecimal digits, LF. The must-be-last
> property follows from ordering and the required-card rule, not from an
> explicit last-card check in the Z card grammar, which validates only
> token syntax. Checksum verification (Section 6.2) does not establish it
> either: it reads the final 35 bytes by position, whatever card they
> belong to.

§4.5.1, replace the sentence quoted in Section 2 with:

> No separate rule requires the Z card to be exactly one, and that it is
> last follows from the ordering and required-card rules (Section 4.4).

§6.2, replace the absent and mismatch conditions with:

> absent: offsets n-35 and n-34 do not hold Z and a space, or artifact too
> short to hold one
> mismatch: offsets n-35 and n-34 hold Z and a space, but the token differs

and add after the paragraph that follows the table:

> The state is decided by the bytes at those offsets, not by where card
> lines begin: an artifact with no Z card whose last line ends in Z, a
> space and 32 further bytes before its LF takes the match or mismatch
> state rather than absent. Such an artifact is rejected either way, by
> the mismatch here or by the card rules, so only the reported reason
> differs.

## 5. Observable consequence of the uncorrected text

A reader that trusts the framing to keep the `Z` card last can skip the
ordering check after a `Z` card, or the required-card check, and accept an
artifact Fossil rejects. A reader that decides the verification state by
looking for a `Z` card line reports a different reason than Fossil for an
artifact whose last line merely ends in `Z` and 32 characters.

## 6. Implementations

zit checks the same two bytes at the same offsets, applies the ordering
rule after a `Z` card, and requires a `Z` card for every type. No change is
needed.