Artifact b5e638341243c5c7ebd96fc7bcb29ef2cd47c1843294e9a053e567cd6fe5ef44:
- File docs/rfcs/erratum/repo-state-01-s4.2-date-tag-event-time.md — part of check-in [100351e26c] at 2026-10-12 18:59:29 on branch trunk — Six errata against the -01 drafts, applied: Julian-day form, cancel cards from the parent, date-tag event time, truncated payload, file card after a refused push, errors N (user: dmestas, size: 5211) [annotate] [blame] [check-ins using]
Erratum — draft-fossil-repo-state-01 §4.2 / §1
| Document | draft-fossil-repo-state-01 |
| Section | 4.2 — Crosslink procedure (the check-in's stored event time); 1 — Introduction |
| Type | Technical (a date tag overrides the D card's time, and the draft does not say so) |
| Status | Applied in draft-fossil-repo-state-01 (2026-10-12) |
| Reported | 2026-10-12 |
| Submitter | Dan Mestas |
| Found via | go-libfossil's conformance follow-ups against Fossil 2.28 on a copy of libfossil.fossil; confirmed by reading Fossil's source |
1. Summary
§1 says "the check-in modification time (mtime) that orders check-ins is
carried by the manifest date", and every ordering rule in the draft reads
that stored time. Fossil lets a singleton tag named date, written by
fossil amend --date, replace it: the stored event time is the newest
active date tag's value when one exists, and the D card's time otherwise.
The draft never mentions the tag, so a reader orders amended check-ins by
their original D cards and disagrees with Fossil on leaves, tips and
symbolic-name resolution for them.
2. Location
§1, the sentence quoted above; §4.2, which has no step for the event time.
3. The reference behavior
Verified against Fossil 2.28.
manifest_crosslink()(src/manifest.c:2394-2409) writes the check-in's event row withmtime = coalesce((SELECT julianday(value) FROM tagxref WHERE tagid=TAG_DATE AND rid=...), D)and keeps the D card's time asomtime.tag_insert()(src/tag.c:229-235) runs, for adatetag applied to an already crosslinked check-in,UPDATE event SET mtime=julianday(value), omtime=coalesce(omtime,mtime). Adatetag is applied under the ordinary tag_insert rule, so a newer tag replaces an older one and the newest value wins.- Neither site filters on tag type: a propagating
datetag sets its target's time (descendants are not retimed, since the update names one rid), and a cancel (tagtype0) reaches the update with a NULL value (src/tag.c:197-199), so a cancel or an unparsable value applied after crosslink stores no time at all, while the same tag applied before crosslink falls back to the D card. A value of0stores a time of 0, since this path has no positivity check.
On a copy of libfossil.fossil, four check-ins carry date tags; an
implementation that read only the D card computed event times different
from Fossil's for all four.
4. Impact
Observable wherever an amended date exists: branch tips, leaves, tip,
and plain-name resolution all order on the stored event time.
5. Correction
§1, the sentence on the check-in modification time:
- names; the check-in modification time (mtime) that orders check-ins
- is carried by the manifest date defined there. It relies on
+ names; the check-in modification time (mtime) that orders check-ins
+ is carried by the manifest date defined there, unless a date tag
+ overrides it (Section 4.2). It relies on
§4.2, a new step after step 5:
6. *Event time.* The check-in's stored event time is its D-card
timestamp, unless a tag named "date" is applied to the
check-in, in which case it is that tag's value parsed under the
timestamp grammar of [FossilArtifact] Section 4.6.4. A date
tag of any type sets the time of its target only; a
propagating date tag does not retime descendants. When a date
tag is applied to a check-in that has already been crosslinked,
the stored event time MUST be updated to the tag's value; when
the tag arrives first, the time MUST be taken from it when the
check-in is crosslinked. A newer date tag replaces an older
one under the rule of Section 5.3, so the newest date tag's
value is the one that orders the check-in. Cancelling the date
tag MUST restore the D-card time, and a date tag whose value
does not parse, or parses to a time that is not positive, MUST
be ignored for this purpose, so that the time is the D-card
time. The D-card timestamp is retained
separately as the original time. (Fossil instead stores no
time at all when a date tag is cancelled or unparsable after
the check-in was crosslinked, and stores a time of zero for the
value 0; its result then depends on arrival order.)
6. Rationale
States the rule the ordering sections silently depend on, in both orders of arrival, and ties the replacement rule to §5.3 rather than restating it. The cancel and unparsable cases are defined to keep a time on every check-in, because a check-in with no time cannot be ordered, and to give the same answer whether the tags arrive before or after the check-in or the repository is rebuilt; Fossil's order-dependent result for them is recorded as a divergence.
7. Verification
- Source: Fossil 2.28
src/manifest.c:2394-2409,src/tag.c:171-184,:197-199,:229-235. - Run: go-libfossil's conformance tests for a date tag arriving after the check-in, before it while the check-in is a phantom, and newest-wins, all agreeing with Fossil 2.28.