Skip to content
zit Erratum: draft-fossil-repo-state-00 §5.3 step 3
Login

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

  1. 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.