OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git-v3 commits / e3690d5

0.1.241: only a hashed overlay entry counts as "known" for repair
seedOverlayServeMap built its `known` set from the resolver callback on PATH presence alone. The overlay persists `contentHash: null` for a file indexed while still hashing, and such an entry seeds the path map but is unservable BY OID -- so adding it to `known` made _repairOverlayRegistrations skip the exact case it was written for. The hash never changes, so nothing ever retried. That is the "no holder for a file that is on disk with a matching hash" bug: 2026-08-08 (136 MiB APK), 2026-08-25 (471 MB zip), 2026-08-29 (168 MB favela-android APK -- on-disk size and blake2b hash both verified EQUAL to _index.json, overlay seeded, peer still told "no holder"). Pairs with hyper-overlay 0.3.13, which passes the index ENTRY to the resolver and stops counting an unhashed entry as seeded. Here we use it: only an entry carrying a contentHash is `known`; the rest fall through to the repair, which re-registers them with the known-good hash from _index.json. Unhashed entries are logged rather than silently absorbed -- the failure being replaced was an unexplained "no holder" with nothing on screen to explain it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
e3690d5b1f7286928884937277c225702998445f
by plankatron <fleeky@gmail.com> · 2026-08-29T17:55:22.000Z

Changed files (2) +18 -3

lib/release-manager.js +17 -2
……@@ -503,11 +503,26 @@ export class ReleaseManager {
503503 // _index.json says what exists, the overlay's FileIndex says what can be
504504 // served, and only registerFile writes the second one.
505505 const known = new Set()
506 const seeded = await this._overlay.seedFilePathsFromIndex((overlayPath) => {
506 let unhashed = 0
507 const seeded = await this._overlay.seedFilePathsFromIndex((overlayPath, entry) => {
507508 if (!overlayPath || !overlayPath.startsWith(RELEASE_OVERLAY_PREFIX)) return null
508 known.add(overlayPath)
509 // ONLY an entry that actually carries a contentHash counts as known.
510 //
511 // The overlay persists `contentHash: null` for a file indexed while still
512 // hashing, and such an entry seeds the PATH map but is unservable BY OID.
513 // Adding it to `known` on path alone made the repair below skip it, so the
514 // repair written for exactly this failure could not see it -- and since the
515 // hash never changes, nothing ever retried. That is the "no holder for a
516 // file that is on disk with a matching hash" bug: 2026-08-08 (136 MiB APK),
517 // 2026-08-25 (471 MB zip), 2026-08-29 (168 MB APK).
518 if (entry && entry.contentHash) known.add(overlayPath)
519 else if (entry) unhashed++
509520 return join(this._releaseDir, overlayPath.slice(RELEASE_OVERLAY_PREFIX.length))
510521 })
522 if (unhashed) {
523 console.log('[releases] overlay index holds ' + unhashed +
524 ' entry(s) with no contentHash - unservable by oid; repairing from the release index')
525 }
511526 await this._repairOverlayRegistrations(known)
512527 return seeded
513528 }
package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "pear-git-v3",
3 "version": "0.1.240",
3 "version": "0.1.241",
44 "description": "P2P git forge — multi-writer issue/org/release/peer-mirror daemon. Built bottom-up after the failed v2.",
55 "type": "module",
66 "main": "index.js",