Artifact befe7488efdc0a51f8bbab6d28453cf9a4be35627d5a0c74f2fe853b380b571e:
- File
conformance/rfcs/erratum/artifact-format-01-s4.7.8-j-card-empty-value.md
— part of check-in
[f61c8174a8]
at
2026-10-17 20:27:53
on branch main
— test(conformance): re-pin the -01 drafts with the 2026-10-14/16 errata (#436)
zit check-in 3f86ff4261 applies six more errata to the -01 drafts. Copy the three drafts and the five new -01 errata files, re-pin their SHA-256 in the requirements tables, and record the check-in in the README.
The draft text changes add six requirements and reword two:
- AF-4.7.3-2: a named delta's delete markers are filtered out of a delta-of-delta's file set and lookups. - AF-4.7.8-1: an absent and a present empty J value decode alike. - AF-6.2-7: the Z verification state is read from the bytes at n-35 and n-34, not from card lines. - RS-5.3-3: a cancel keeps its card's value on the row it writes. - RS-5.3-6: the event table and propagation treat that value as null. - RS-5.3-7: an artifact's tag cards apply in card order. - RS-6.2-4: a branch root carrying *sym-NAME and -sym-NAME keeps the tag. - SP-6.6-4: a client recognizes a refusal by its leading text.
go-libfossil already behaves this way. Each row has a case, checked against fossil 2.28 where the harness does: 421 requirements, 420 covered, 1 partial (SP-9.5-7, as before).
The repo-state §5.3 step 3 change is filed in zit as a -00 erratum and is not pinned, like the other -00 errata; the -01 draft carries its text. (user: [email protected], size: 2629) [annotate] [blame] [check-ins using]
# Erratum: draft-fossil-artifact-format-01 §4.7.8 | | | |---|---| | **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.