# Erratum — draft-fossil-submanifests-00 §6.3
| | |
|---|---|
| **Document** | `draft-fossil-submanifests-00` |
| **Section** | 6.3 — Parameter Selection and Pinning (automatic selection of k) |
| **Type** | Technical (the selection the implementations perform is not the one the draft describes, so a third implementation would pin a different k) |
| **Status** | Applied in `draft-fossil-submanifests-00` (2026-10-19) |
| **Reported** | 2026-10-18 |
| **Submitter** | Dan Mestas |
| **Found via** | Testing go-libfossil's writer against zit: on four histories zit wrote, the history replay moved the pin off the snapshot result every time |
## 1. Summary
§6.3 selects k from one snapshot, the current check-in's file list,
choosing the lowest C(k). zit takes that snapshot result and then
replays the repository's recent history at the neighbouring values,
keeping the one that leaves the fewest steady-state bytes. go-libfossil
matched zit and pins the same k on every history tried; in all four, the
replay moved the pin. The draft says only that the implementation SHOULD
choose the lowest snapshot C(k). It does not mention the replay, its
window, the candidates, the order of the walk, the cost or the
tie-break. §6.3 requires project scope "so writers given the same file
entries use the same pinned value"; a third implementation following the
text would pin a different value from the two that exist.
## 2. The text in error
§6.3, the paragraph ending:
> The implementation SHOULD choose the candidate with the lowest C(k),
> and MUST choose the lowest k among candidates with equal minimum cost.
> If no value is a candidate, automatic selection has no result and an
> implementation MUST NOT silently substitute a value.
Nothing follows about history.
## 3. What the implementations do
zit (`submanifestReplayK` in `src/cmd/checkout.zig`, `Repository.replay`
in `src/repository.zig`): with k_s the snapshot choice, the candidates
are k_s - 1, k_s and k_s + 1 within 1..12. For each, the last 200
check-ins are replayed in the order they were persisted. Each check-in's
tree is built under the candidate; a node whose bytes equal a node of
the previous check-in costs nothing, and a changed node costs the
smaller of its full form and its delta against the same-position node
of the previous check-in. Roots are re-serialized with the S run in
place of the F run and costed the same way. The candidate with the
lowest total wins; a strict comparison in ascending order gives ties to
the lowest k; a candidate under which every replayed check-in is flat is
skipped. With fewer than two replayable check-ins the snapshot choice
stands. `zit submanifest analyze --replay-all` replays the whole history
for the operator's information. go-libfossil implements the same
procedure, taking the window in persist order approximated by rid.
## 4. Corrected text
Add to §6.3, after the paragraph quoted above:
> The snapshot cost is theory over one file set. An implementation with
> history to replay SHOULD refine the snapshot choice against it as
> follows, so that writers given the same repository pin the same value.
> Let k_s be the snapshot choice. The candidates are k_s - 1, k_s and
> k_s + 1, those within 1 to 12. For each candidate, replay the last 200
> check-ins of the repository in the order they were persisted,
> constructing each check-in's tree under the candidate and charging
> every sub-manifest and root manifest whose bytes differ from the
> previous check-in's: an unchanged node costs nothing, and a changed
> node costs the smaller of its full form and its delta against the
> same-position node of the previous check-in. The pinned value is the
> candidate with the lowest total, the lowest k on a tie. A candidate
> under which every replayed check-in is flat is not chosen. If fewer
> than two check-ins can be replayed, the snapshot choice stands. An
> implementation MAY replay more history for the operator's information;
> the pin follows the rule above.
## 5. Observable consequence of the uncorrected text
Two conforming writers of the same repository pin different values: one
the snapshot optimum, the other the replay winner. Their trees then share
only the sub-manifests that happen to be byte-identical under both,
which §6.4 allows but §6.3's project-scope requirement exists to avoid.
## 6. Implementations
zit and go-libfossil already do this; the text now says what they do.