# Erratum: draft-fossil-repo-state-00 §5.3 step 3

| | |
|---|---|
| **Document** | `draft-fossil-repo-state-00` |
| **Section** | 5.3, Direct Application Procedure, step 3 |
| **Type** | Technical (the text says the stored row loses a cancel's value; Fossil keeps it) |
| **Status** | Applied in `draft-fossil-repo-state-01` (2026-10-14) |
| **Reported** | 2026-10-14 |
| **Submitter** | Dan Mestas |
| **Found via** | libfossil's conformance suite (`repostate.cancel-propagation`), then Fossil's `tag_insert()`; confirmed with Fossil 2.28 |

## 1. Summary

Step 3 says that when a cancel is applied, the tag's value is cleared to
null "as well as the stored row". Fossil does not clear the stored row.
It writes the T card's value into the target's tagxref row, and only then
clears its local copy of the value, so the null reaches the derived state
(the event table) and propagation, not the row itself. A cancel may carry a
value: the artifact format allows one on every T card, and some tools use
it. Under the current text a conforming reader drops that value and
diverges from Fossil.

## 2. The text in error

Page 15 (§5.3 step 3):

> If the resolved type is cancel (0), the tag's value is cleared to null
> before any further processing, erasing the value from cached state as
> well as the stored row.

## 3. What Fossil implements

`tag_insert()` in `src/tag.c` (trunk, 2026-10-13):

```
tag.c:188   REPLACE INTO tagxref(tagid,tagtype,srcId,origid,value,mtime,rid)
            ... zValue ...                      /* the row gets the value */
tag.c:196   if( tagtype==0 ){
tag.c:197     zValue = 0;                       /* cleared after the write */
            }
tag.c:222   event updates (bgcolor, comment, user, date) use zValue
tag.c:240   tag_propagate(rid, tagid, tagtype, rid, zValue, mtime);
```

`manifest_crosslink()` passes a T card's value to `tag_insert()` unchanged,
so a cancel card's value is what the REPLACE stores.

Checked with Fossil 2.28 [52445a27f1]. Chain c1 <- c2 <- c3; a propagating
`bgcolor` tag (`#aabbcc`) at c1; then a control artifact carrying
`T -bgcolor <c2> ignored`, followed by `fossil rebuild`:

| Check-in | tagxref (tagtype, value) | event.bgcolor |
|---|---|---|
| c1 | 2, `'#aabbcc'` | `'#aabbcc'` |
| c2 | 0, `'ignored'` | NULL |
| c3 | no row (inherited row deleted) | NULL |

The canceled row keeps `'ignored'`. The event table and the descendant
show the cancel's effect with no value, as the rest of §5.3 and §5.4
expect.

## 4. Corrected text

> 3.  If the resolved type is cancel (0), the row written in step 2 keeps
>     the T-card's value, if one was supplied. For all further processing
>     (cached state such as the event table, and the propagation walk of
>     Section 5.4) the value is treated as null.

## 5. Observable consequence of the uncorrected text

An implementation that follows the text stores NULL where Fossil stores the
cancel card's value. Any view reading a canceled tag's value from tagxref
(a tag listing, a tool that records why a tag was canceled) then differs
from Fossil for the same artifacts, and a repository rebuilt by one
implementation differs from the same repository rebuilt by the other.
Nothing else changes: the cancel still blocks and removes the tag exactly as
§5.4 describes.

## 6. Implementations

libfossil already keeps the value, as Fossil does. Its conformance suite
asserted the text in error and so reported libfossil as failing; the suite
is being corrected to expect the value kept.
