Skip to content
zit Artifact [bc4d4cddeb]
Login

Artifact [bc4d4cddeb]

Artifact bc4d4cddeb35a2b018231a6ee7350a69c0f9f338b1cbadae57755f79fc87b492:


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