Skip to content

fix(read): resource read miss resolves the announced capsule holder (#1580)#86

Merged
MichaelTaylor3d merged 4 commits into
mainfrom
fix/1580-tier2-resource-read
Jul 24, 2026
Merged

fix(read): resource read miss resolves the announced capsule holder (#1580)#86
MichaelTaylor3d merged 4 commits into
mainfrom
fix/1580-tier2-resource-read

Conversation

@MichaelTaylor3d

Copy link
Copy Markdown
Contributor

Root cause (lead (a) confirmed; (b) ruled out)

The #836 read-leg DATA 404 (#1580, #1062 e2e-proven on v0.56.1 where CONNECT + DISCOVER were already green).

A /s resource read miss (serve_content_plaintextpeer_serve_plaintextdownload::miss_content_for) builds a ContentId::Resource { store, root, retrieval_key } and hands it to dig-download, whose discover step (locate_and_confirm) locates providers by that EXACT resource key. But inventory is announced at STORE and CAPSULE (store_id:root) granularity ONLY — per-resource provider records are deliberately never announced (dht::inventory_content_ids, dht.rs:319: a capsule holder serves every resource in it, so per-resource records would be redundant and explode DHT write volume).

Result: find_providers(resource_id) returned nobody even though the capsule holder was discoverable → Tier-2 peer fetch gave up → the read fell through to the §21 whole-capsule backfill / public RPC and 404'd. The holder saw no inbound fetchRange.

Lead (b) — path ordering — was checked and is NOT the fault: serve_content_plaintext already runs Tier-2 peer fetch (line 417) BEFORE Tier-3 RPC (line 438); maybe_backfill_capsule is a background warm, not a preferred path.

Fix

New CapsuleFallbackLocator (crates/dig-node-core/src/seams/dig_peer/capsule_fallback.rs) wrapping the content engine's provider locator in NodeContent::for_dht. On a ContentId::Resource lookup it ALSO queries the parent ContentId::capsule(store_id, root) — the announced granularity — and unions the holders (deduped by peer_id, resource-key hits first). dig-download then confirms + fetches the specific resource from the capsule holder via dig.getAvailability + dig.fetchRange. Non-resource lookups pass through unchanged; best-effort (an error from either query never removes what the other found).

No dig-download or dig-dht API change needed — the bridge lives entirely in dig-node.

Blast radius (socraticode + read; gitnexus disabled per §2.0 override)

  • NodeContent::for_dht locator wiring — the only construction site; the FFI/in-process path never attaches a content engine, so unaffected.
  • download::miss_content_for / peer_serve_plaintext unchanged.
  • Behaviour delta is strictly ADDITIVE: a resource lookup now finds MORE holders (previously zero); no existing hit path changes.

Tests

  • RED confirmed: resource_lookup_falls_back_to_the_announced_capsule_holder finds 0 holders with a pass-through stub, 1 with the fallback.
  • +2 unit tests (union/dedup, capsule pass-through).
  • cargo test -p dig-node-core --lib download:: (18) + capsule_fallback (3) green; fmt + clippy clean.

Version

patch 0.56.1 → 0.56.2 (behaviour-preserving-for-existing-paths content-read fix).

Correctness tier

read / content-fetch (not custody). Verify-then-decrypt fail-closed path is untouched.

Closes #1580

MichaelTaylor3d and others added 2 commits July 24, 2026 08:33
…#1580)

A `/s` resource read miss builds a `ContentId::Resource` and locates
providers by that exact key. But inventory is announced at STORE and
CAPSULE (`store_id:root`) granularity ONLY — per-resource records are
deliberately never announced (`dht::inventory_content_ids`). So the
resource locate found nobody even when a holder was discoverable, Tier-2
peer fetch gave up, and the read dead-ended at the §21 whole-capsule
backfill / public RPC → DATA 404 (the #836 read-leg blocker, #1062
e2e-proven on v0.56.1 where DISCOVER was already green).

Add `CapsuleFallbackLocator`, wrapping the content engine's provider
locator: on a `ContentId::Resource` lookup it ALSO queries the parent
`ContentId::capsule(store_id, root)` — the granularity holders actually
announce — and unions the holders (deduped by peer_id, resource-key hits
first). A capsule holder serves every resource in it, so dig-download's
next step (`dig.getAvailability` + `dig.fetchRange` for the resource)
confirms and fetches from that peer. Non-resource lookups pass through
unchanged; best-effort throughout.

Regression: `resource_lookup_falls_back_to_the_announced_capsule_holder`
(a resource lookup finds a capsule-only-announcing holder) fails without
the fallback (0 found) and passes with it (1 found).

Co-Authored-By: Claude <noreply@anthropic.com>
…mismatch (#1580)

Co-Authored-By: Claude <noreply@anthropic.com>

@MichaelTaylor3d MichaelTaylor3d left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CHANGES-REQUIRED — the fix design is correct but the Clippy gate is RED.

The CapsuleFallbackLocator logic is right: resource lookups query both the resource key and the parent ContentId::capsule(store,root), union deduped by peer_id (resource-first precedence), non-resource lookups pass through unchanged, wired at the NodeContent::for_dht seam. RED test is non-vacuous, SPEC §19.3 + DEVLOG updated, patch bump correct, no API change.

Blocker: cargo clippy --workspace --all-targets --locked -- -D warnings FAILS with 4x clippy::clone_on_copy on ContentId (which is Copy) at capsule_fallback.rs:100, 131, 185, 208 — all in the test module (.clone() on capsule/resource/content). Drop the .clone() calls (ContentId is Copy). Merge is blocked until Clippy is green.

Comment thread crates/dig-node-core/src/seams/dig_peer/capsule_fallback.rs Outdated
MichaelTaylor3d and others added 2 commits July 24, 2026 15:26
ContentId implements Copy; .clone() is unnecessary on Copy types.
Removed .clone() from four ContentId references:
- Line 100: content.clone() -> *content
- Line 131: capsule.clone() -> capsule
- Line 185: resource.clone() -> resource
- Line 208: capsule.clone() -> capsule

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
@MichaelTaylor3d
MichaelTaylor3d merged commit 00956a9 into main Jul 24, 2026
13 checks passed
@MichaelTaylor3d
MichaelTaylor3d deleted the fix/1580-tier2-resource-read branch July 24, 2026 22:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant