Artifact 0dfa88162891adb28511aede7a465ccbc2a4a2f99a4d3e60dacc778e6e099591:
- File docs/rfcs/erratum/repo-state-00-s8.3-trailing-punct.md — part of check-in [21e3d381fe] at 2026-10-11 21:47:44 on branch trunk — errata: repo-state singleton walks, branch creation and trailing-punctuation retry, as Fossil does them (user: dmestas, size: 5691) [annotate] [blame] [check-ins using]
Erratum — draft-fossil-repo-state-00 §8.3
| Document | draft-fossil-repo-state-00 |
| Section | 8.3 — Procedure (trailing-punctuation retry) |
| Type | Technical (the text diverges from Fossil's implemented behavior) |
| Status | Proposed — operator decision pending |
| Reported | 2026-10-11 |
| Submitter | Dan Mestas |
| Found via | A three-letter tag that did not resolve with a trailing period; traced to Fossil 2.28 src/name.c and confirmed by running Fossil 2.28 |
1. Summary
§8.3 says that when no form matches and the name ends in exactly one trailing punctuation character, resolution MUST retry without it. Fossil's rule differs in three ways:
- Length. The retry needs a name longer than four characters, so the
stem left after stripping is at least four characters.
release.is retried;rel.is not. - Which characters. Only seven count:
._)>!?,. Others, such as;and:, never trigger a retry. - Two characters. A name ending in exactly two of those characters is retried with both removed (the name must then be longer than five characters). Three or more trailing characters are never stripped.
This is a deliberate heuristic, not a defect: it targets names lifted from prose or a URL, and the length floor keeps short names from being rewritten.
2. Location
§8.3, the paragraph after the numbered dispatch list. Current text:
If none of the preceding forms match and the name ends in exactly one trailing punctuation character (for example, punctuation incidentally captured when a name is lifted from surrounding prose or a URL), resolution MUST retry recursively with that trailing character stripped.
3. The divergence
Verified against Fossil 2.28 by reading the source and by running it.
3.1 Source
is_trailing_punct() (src/name.c:399) defines the set:
return c=='.' || c=='_' || c==')' || c=='>' || c=='!' || c=='?' || c==',';
The retry is the last step of symbolic_name_to_rid() (src/name.c:704-720):
if( nTag>4
&& is_trailing_punct(zTag[nTag-1])
&& !is_trailing_punct(zTag[nTag-2])
){
... symbolic_name_to_rid(<zTag without its last character>, zType);
}else
if( nTag>5
&& is_trailing_punct(zTag[nTag-1])
&& is_trailing_punct(zTag[nTag-2])
&& !is_trailing_punct(zTag[nTag-3])
){
... symbolic_name_to_rid(<zTag without its last two characters>, zType);
}
The stripped name goes through the whole procedure again. Its last character is not in the set, so it is never stripped a second time.
3.2 Run
One check-in tagged release, rel and relx; fossil info NAME:
| NAME | resolves | why |
|---|---|---|
release. |
yes | one trailing character, stem of 7 |
release) |
yes | ) is in the set |
release.. |
yes | two trailing characters, both stripped |
release... |
no | three trailing characters |
release; |
no | ; is not in the set |
release: |
no | : is not in the set |
relx. |
yes | length 5, stem of 4 |
relx., |
yes | length 6, two stripped |
rel. |
no | length 4 is not more than 4 |
rel? |
no | same |
4. Impact
Observable, minor. An implementation that follows §8.3 resolves rel.,
release; and release: where Fossil does not, and refuses release.. where
Fossil resolves it. The same name can therefore name a check-in in one tool and
nothing in the other, and a link pasted from prose behaves differently.
5. Correction
- If none of the preceding forms match and the name ends in exactly one
- trailing punctuation character (for example, punctuation incidentally
- captured when a name is lifted from surrounding prose or a URL),
- resolution MUST retry recursively with that trailing character
- stripped.
+ If none of the preceding forms match, the name may carry punctuation
+ incidentally captured when it was lifted from surrounding prose or a
+ URL. For this rule the trailing punctuation characters are exactly
+ ".", "_", ")", ">", "!", "?" and ",". If the name is longer than
+ four characters and ends in exactly one of these characters,
+ resolution MUST retry the whole procedure with that character
+ removed. Otherwise, if the name is longer than five characters and
+ ends in exactly two of these characters, resolution MUST retry with
+ both removed. A name ending in three or more of these characters,
+ or one too short for either case, MUST NOT be retried. A stripped
+ name is not stripped again.
6. Rationale
- Describe Fossil, not a defect in it. The floor and the fixed set are intended: they keep short names, and names ending in characters that are ordinary inside names, from being rewritten into something else. Nothing here is incoherent, so the draft should state the rule as Fossil applies it.
- The set has to be listed. "Punctuation" is open-ended; two
implementations reading it differently resolve different names.
_in particular is not what most readers would call punctuation. - "Exactly one" is wrong in both directions. It forbids the two-character case that Fossil handles and permits short names that Fossil refuses.
7. Verification
- Source: Fossil 2.28
src/name.c:399-401(is_trailing_punct) andsrc/name.c:700-721(the retry at the end ofsymbolic_name_to_rid). - Run:
fossil new; one commit;fossil tag add release|rel|relx <check-in>;fossil infowith each name in the table above.
Prepared against draft-fossil-repo-state-00. The RFC remains the oracle; the
reference implementation is used only to confirm the observable behavior.