| Document | draft-fossil-artifact-format-01 |
| Section | 4.7.8, J Card (an absent value and a present empty value) |
| Type | Technical (whether a reader keeps the two forms apart is left open) |
| Status | Applied in draft-fossil-artifact-format-01 (2026-10-16) |
| Reported | 2026-10-16 |
| Submitter | Dan Mestas |
| Found via | A reader working from the draft text alone; confirmed by reading Fossil's source |
1. Summary
§4.7.8 says an absent J value "defaults to the empty string, not null",
and §4.2 makes a present empty final token decode to the empty string too.
The draft does not say whether a reader may still record the two forms
differently, for example as "unset" and "set to empty". Fossil does not:
both reach ticket processing as the same empty string. Fossil also writes
the present empty form itself, so a reader meets it in real repositories.
2. The text in error
Page 24 (§4.7.8):
The value is escape-decoded; the field name is stored verbatim, including any leading +. An absent value defaults to the empty string, not null.
3. What Fossil implements
manifest_parse() in src/manifest.c, case 'J':
zName = next_token(&x,0);
zValue = next_token(&x,0);
if( zName==0 ) SYNTAX("name missing from J-card");
if( zValue==0 ) zValue = "";
defossilize(zValue);
A present empty token returns a pointer to an empty string, and an absent
one is replaced by ""; nothing records which it was. ticket_insert() in
src/tkt.c then writes the value into the ticket tables the same way for
both.
When a ticket field is changed to the empty string, submitTicketCmd() in
src/tkt.c writes
blob_appendf(&tktchng, "J %s %#F\n", aField[i].zName, nValue, zValue);
with nValue 0, which is J, SP, the field name, SP, LF: the present
empty form.
4. Corrected text
Replace the sentence "An absent value defaults to the empty string, not null." with:
An absent value and a present empty one (J, SP, fieldname, SP, LF) both decode to the empty string, not null, and a reader MUST NOT distinguish them; Fossil writes the present empty form when a ticket field is set to the empty string.
5. Observable consequence of the uncorrected text
A reader that keeps the forms apart can treat J name as "leave the field
alone" and J name as "clear it", or the reverse, and build ticket state
that differs from Fossil's for the same artifacts.
6. Implementations
zit's J card parser returns the same empty value for both forms. No
change is needed.