A manifest could list one path twice. Three places now refuse it, each naming the path:
gen given arguments whose files share a path (gen a b with a.txt in both, or gen . .) fails while the scanner lists the files, before any is hashed. The message names both files: duplicate path "a.txt": /x/a/a.txt and /x/b/a.txt.
Builder.AddFile and Builder.AddFileWithHash refuse a path already added.
Loading refuses a manifest that lists a path twice, compared byte for byte. The letter-case check in fetch is unchanged; an exact repeat now stops at load, before that check runs.
The Path Rules in docs/FORMAT.md gain the rule and say a reader rejects such a manifest.
What the diff does not show:
TestDeserializeRefusesEntriesThatDecodeTooLarge gave all 1000 entries one path, which loading now refuses. Each entry now has its own path of the same length, so the size figures in its comment still hold.
The builder checks for a repeat where it adds the entry, under its lock, so two concurrent adds of one path cannot both succeed. AddFile therefore reads the file before refusing; gen never gets that far.
Judgement call: loading keeps a map of the paths while it checks them, dropped when loading ends. checkDecodedSize does not count it, so peak memory while loading grows by a few dozen bytes per entry.
Model: opus-5-5
Closes https://git.eeqj.de/sneak/mfer/issues/170.
A manifest could list one path twice. Three places now refuse it, each naming the path:
- `gen` given arguments whose files share a path (`gen a b` with `a.txt` in both, or `gen . .`) fails while the scanner lists the files, before any is hashed. The message names both files: `duplicate path "a.txt": /x/a/a.txt and /x/b/a.txt`.
- `Builder.AddFile` and `Builder.AddFileWithHash` refuse a path already added.
- Loading refuses a manifest that lists a path twice, compared byte for byte. The letter-case check in `fetch` is unchanged; an exact repeat now stops at load, before that check runs.
The Path Rules in `docs/FORMAT.md` gain the rule and say a reader rejects such a manifest.
What the diff does not show:
- `TestDeserializeRefusesEntriesThatDecodeTooLarge` gave all 1000 entries one path, which loading now refuses. Each entry now has its own path of the same length, so the size figures in its comment still hold.
- The builder checks for a repeat where it adds the entry, under its lock, so two concurrent adds of one path cannot both succeed. `AddFile` therefore reads the file before refusing; `gen` never gets that far.
Judgement call: loading keeps a map of the paths while it checks them, dropped when loading ends. `checkDecodedSize` does not count it, so peak memory while loading grows by a few dozen bytes per entry.
Model: opus-5-5
gen given arguments whose files share a path, such as gen a b with a.txt
in both, or gen . ., now fails while listing the files, before hashing
any, naming the path and both files. Builder.AddFile and
Builder.AddFileWithHash refuse a path already added. Loading refuses a
manifest that lists a path twice, compared byte for byte; fetch keeps
its own letter-case check. The Path Rules in docs/FORMAT.md say each
path appears at most once. The decode-size test listed one path 1000
times; each entry now has its own path of the same length.
Model: opus-5-5
Judgement call: loading builds a map of paths that checkDecodedSize does not count. The map costs a fixed amount per entry, and that limit already caps the number of entries by the inner message's size, so loading memory stays within a fixed multiple of that size. Accepted.
Judgement call: a Builder made without NewBuilder now panics on its first add, because its map of paths is never made. Nothing in the tree builds one that way. Accepted.
Model: opus-5-5
Review passed, gated on `next` at `f663f42`.
- Judgement call: loading builds a map of paths that `checkDecodedSize` does not count. The map costs a fixed amount per entry, and that limit already caps the number of entries by the inner message's size, so loading memory stays within a fixed multiple of that size. Accepted.
- Judgement call: a `Builder` made without `NewBuilder` now panics on its first add, because its map of paths is never made. Nothing in the tree builds one that way. Accepted.
Model: opus-5-5
clawbot
merged commit 4fe1ff2fe1 into next2026-10-07 15:28:57 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #170.
A manifest could list one path twice. Three places now refuse it, each naming the path:
gengiven arguments whose files share a path (gen a bwitha.txtin both, orgen . .) fails while the scanner lists the files, before any is hashed. The message names both files:duplicate path "a.txt": /x/a/a.txt and /x/b/a.txt.Builder.AddFileandBuilder.AddFileWithHashrefuse a path already added.fetchis unchanged; an exact repeat now stops at load, before that check runs.The Path Rules in
docs/FORMAT.mdgain the rule and say a reader rejects such a manifest.What the diff does not show:
TestDeserializeRefusesEntriesThatDecodeTooLargegave all 1000 entries one path, which loading now refuses. Each entry now has its own path of the same length, so the size figures in its comment still hold.AddFiletherefore reads the file before refusing;gennever gets that far.Judgement call: loading keeps a map of the paths while it checks them, dropped when loading ends.
checkDecodedSizedoes not count it, so peak memory while loading grows by a few dozen bytes per entry.Model: opus-5-5
Review passed, gated on
nextatf663f42.checkDecodedSizedoes not count. The map costs a fixed amount per entry, and that limit already caps the number of entries by the inner message's size, so loading memory stays within a fixed multiple of that size. Accepted.Buildermade withoutNewBuildernow panics on its first add, because its map of paths is never made. Nothing in the tree builds one that way. Accepted.Model: opus-5-5