# Erratum — draft-fossil-artifact-format-00 §4.6.3, §5.3
| | |
|---|---|
| **Document** | `draft-fossil-artifact-format-00` |
| **Section** | 4.6.3 — Reserved Filenames; 5.3 — Type Determination |
| **Type** | Technical (divergence from the reference implementation) |
| **Status** | Applied in `draft-fossil-artifact-format-01` (2026-10-12) |
| **Reported** | 2026-10-11 |
| **Submitter** | Dan Mestas |
| **Found via** | Checking an illustrated reading of the draft against Fossil 2.28 |
## 1. Summary
§5.3 says the F card sets the artifact type to MANIFEST unconditionally, and
that the untyped fallback is therefore reachable only through the C or P card.
In Fossil an F card whose current filename is reserved (§4.6.3) is dropped
**before** it sets the type. Such a card leaves the type unset, so the
artifact can reach the fallback, become a CONTROL artifact and fail the
allowed-card check because it contains an F card. An earlier copy of the
draft said this correctly; the current text removed it.
## 2. Location
§5.3, Table 5 and the paragraph introducing it:
> Thirteen card letters are type-setting; each unconditionally assigns the
> artifact type when processed …
>
> | B, F, Q, R | MANIFEST |
§5.3, fallback paragraph:
> In practice this fallback is reachable only via the C card or P card (the F
> card already sets MANIFEST directly), because the C and P card grammars never
> assign a type.
§4.6.3 says nothing about where the drop falls relative to type assignment.
## 3. The divergence
Verified against Fossil 2.28 (`52445a27f1`) by source reading and by running
its parser.
### 3.1 Source
In `manifest_parse()`'s F-card case (`src/manifest.c:678`), the reserved-name
test (`:710`) decrements `nFile` and `break`s out of the case (`:714-715`).
The type assignment `p->type = CFTYPE_MANIFEST` comes after it (`:721`), so a
dropped card never reaches it. The card's letter is still recorded in
`seenCard` (`:564`), because that happens before the `switch`.
After the loop, the fallback (`:1076-1082`) tests `p->zComment`, `p->nFile`
and `p->nParent`. A dropped card does not count towards `nFile`, so an artifact
whose only F cards were reserved, and which has no C or P card, becomes
CONTROL. The allowed-card check (`:1086`) then rejects it, because F is not in
CONTROL's allowed set `DTUZ` (`:134`).
### 3.2 Experiment
The SHA1 below is a valid hash token. Each file ends with a correct Z card.
| Input cards | `fossil test-parse-manifest` |
|---|---|
| `D`, `F foo.txt <h>`, `Z` | ok (MANIFEST via F) |
| `D`, `F _FOSSIL_ <h>`, `Z` | `ERROR: line 3: F-card in tag` |
| `D`, `F _FOSSIL_ <h>`, `T +sym-x *`, `U u`, `Z` | `ERROR: line 5: F-card in tag` |
| `C x`, `D`, `F _FOSSIL_ <h>`, `Z` | ok (MANIFEST via fallback on C) |
The second and third rows reach the fallback with no C or P card, which §5.3
says cannot happen. The third row also shows that a dropped F card does not
fit cleanly into a control artifact either: F is still a seen card.
## 4. Impact
A parser built to §5.3 sets MANIFEST when it reads a reserved-name F card. It
accepts the second-row artifact as an empty manifest where Fossil rejects it,
so the two disagree on which artifacts are valid. That is an interoperability
failure in the direction that matters: the reimplementation accepts and
cross-links content that every Fossil peer refuses.
The case is narrow (reserved names reach manifests only through old writers),
but the draft states the opposite of the observable behavior as a fact about
reachability.
## 5. Correction
§4.6.3, after "Dropping removes the card from the effective file set.":
```diff
An F card whose decoded current filename is reserved MUST be silently
dropped, not rejected. Dropping removes the card from the effective
- file set. Such names leaked into historical manifests, and existing
+ file set. The drop occurs before the F card would set the artifact
+ type to MANIFEST (Section 5.3): a dropped card assigns no type and
+ records no file entry, though its letter still counts as present
+ for the allowed-card check of Section 5.2. Such names leaked into
+ historical manifests, and existing
documentation contains no mention of this behavior (Appendix C); the
```
§5.3, introduction to Table 5:
```diff
- cards. Thirteen card letters are type-setting; each unconditionally
- assigns the artifact type when processed, with no guard against a
- previously assigned value:
+ cards. Thirteen card letters are type-setting; each assigns the
+ artifact type when processed, with no guard against a previously
+ assigned value. The one exception is an F card dropped for a
+ reserved filename (Section 4.6.3), which assigns nothing:
```
§5.3, fallback paragraph:
```diff
- In practice this fallback is reachable only via the C card or P card
- (the F card already sets MANIFEST directly), because the C and P card
- grammars never assign a type. The allowed-card and required-card
+ In practice this fallback is reachable via the C card or P card,
+ whose grammars never assign a type, and also when every F card was
+ dropped for a reserved filename. In the last case, with no C card
+ and no P card, the artifact resolves to CONTROL and is then rejected
+ because F is not an allowed CONTROL card. The allowed-card and
+ required-card
```
## 6. Rationale
- **The ordering is observable.** It decides whether an artifact is accepted,
so it belongs in the normative text, not only in an implementation note.
- **This restores earlier wording.** The July draft of §4.6.3 and §5.3 said
this, and Fossil 2.28 matches it. The later "reachable only via C or P"
sentence is the regression.
- **Seen-card subtlety stated once.** Without the "still counts as present"
clause a reader could drop the card entirely before the legality checks and
accept the third-row artifact as CONTROL.
## 7. Verification
- Source: `src/manifest.c:564` (seenCard), `:710-715` (drop and break),
`:721` (type assignment), `:1076-1082` (fallback), `:1086` (allowed-card
check), `:134` (CONTROL allowed set), Fossil 2.28.
- Experiment: the four inputs in §3.2, built by hand with an MD5 Z card and
run through `fossil test-parse-manifest` on Fossil 2.28 (`52445a27f1`).
---
*Prepared against `draft-fossil-artifact-format-00`. The RFC remains the oracle;
the reference implementation is used only to confirm the observable behavior.*