Artifact b76b80f58aa5ca44344aecc30c98e1f98b82fab5677744aa3e908fbb81177b72:
- File docs/rfcs/erratum/submanifests-00-s6.3-history-replay-selection.md — part of check-in [832bc6a391] at 2026-10-19 17:08:30 on branch trunk — submanifests §6.3: automatic selection refines the snapshot choice by replaying the last 200 check-ins at the neighbouring k, as both writers do (user: dmestas, size: 4449) [annotate] [blame] [check-ins using]
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.