# 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** | Applied in `draft-fossil-repo-state-01` (2026-10-12) |
| **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:
1. **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.
2. **Which characters.** Only seven count: `.` `_` `)` `>` `!` `?` `,`. Others,
such as `;` and `:`, never trigger a retry.
3. **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:
```c
return c=='.' || c=='_' || c==')' || c=='>' || c=='!' || c=='?' || c==',';
```
The retry is the last step of `symbolic_name_to_rid()` (`src/name.c:704-720`):
```c
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
```diff
- 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`) and
`src/name.c:700-721` (the retry at the end of `symbolic_name_to_rid`).
- Run: `fossil new`; one commit; `fossil tag add release|rel|relx <check-in>`;
`fossil info` with 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.*