8000 fix(rcache): an index hit whose artifact is gone is a miss, not a failure by bryan-minimal · Pull Request #2624 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

fix(rcache): an index hit whose artifact is gone is a miss, not a failure - #2624

Merged
bryan-minimal merged 11 commits into
fix/1162-index-retryfrom
fix/535-dangling-index-hit
Oct 28, 2026
Merged

bryan-minimal merged 11 commits into
fix/1162-index-retryfrom
fix/535-dangling-index-hit

Conversation

@bryan-minimal
Copy link
Copy Markdown
Member

Summary

The rcache half of gominimal/inbox#535, per Norrie's split there. Stacked on the inbox#1162 PR (fix/1162-index-retry), which this shares remote.rs with; GitHub retargets it to main when that merges.

An index hit followed by a 404 on the artifact object failed the resolve with a hard Backend(...404...), and after #1162's retries would have retried it as transient. It is a normal state of this architecture, not an outage: a snapshot index keeps every record forever, and the store's retention (or a redistribution fix, which is how the August incident happened) deletes objects out from under them.

fetch_verified now returns Error::NotFound on a 404 after one attempt, with a cache_dangling_hit warning naming the object, so a caller treats it as the miss it is. The patched-build path already does (a dangling dependency is now "could not find", not "could not fetch"); the session package fetch surfaces a NotFound instead of a reqwest dump.

Not in this PR: the orchestrator half. cache_hydrate cannot fall back to build() because CacheFill deliverables carry no dependency edges; that is a graph change and gets its own issue, as Norrie asked on inbox#535.

Testing

  • New materialize_dangling_index_hit_is_a_miss (rcache): index hit, artifact 404 → NotFound after one artifact attempt.
  • op::patched: reports_remote_fetch_failure_distinctly_from_a_miss now serves a corrupt artifact (a real fetch failure); new a_dangling_index_hit_is_reported_as_a_miss covers the 404 case.
  • cargo nextest run -p rcache -p op: all green; just clippy clean; workspace tests through anvil's CI mirror.

🤖 Generated with Claude Code

@bryan-minimal
bryan-minimal requested a review from a team as a code owner October 26, 2026 16:09
@coderabbitai
coderabbitai Bot commented Oct 26, 2026 •
Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: aef042b9-490d-4f07-84bd-0a94bdf5ca15

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@minimal-foundry
minimal-foundry Bot commented Oct 26, 2026 •
Copy link
Copy Markdown
Contributor

Approved: no findings at 1eb8dab3a573.

Nothing to do.

Clobber: none seen.

@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

minimal-review — advisory review

Verdict COMMENT — 2 findings, 2 inline.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

Comment thread crates/rcache/src/remote.rs Resolved
Comment thread crates/rcache/src/remote.rs Resolved
bryan-minimal and others added 2 commits October 26, 2026 09:48
…lure

An index hit followed by a 404 on the artifact object failed the resolve with a hard
`Backend(...404...)`, retried as if transient. It is a normal state of this architecture: a
snapshot index keeps every record forever, and the store's retention (or a redistribution fix)
deletes objects out from under them. `fetch_verified` now returns `Error::NotFound` on a 404,
after one attempt, with a `cache_dangling_hit` warning naming the object, so a caller treats it
as the miss it is: the patched-build path already does; the orchestrator's hydrate is the
other half, filed separately per Norrie's call on inbox#535.

Refs: gominimal/inbox#535

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review on #2624. `NotFound` displayed as its Debug name, so a dangling hit reached the session
fetch and `cache_hydrate` as the bare word `NotFound`, worse than the 404 and URL it replaced. The
variant carries the object now (the spec hash for an index miss, the artifact name for a gone
object) and reads "the remote cache has no artifact for <object>"; the session package fetch and
the orchestrator's hydrate prefix the package name, so the user sees which package and what. The
dangling-hit warning states only the fact, names the object, and carries `kind = "dangling_hit"`
as a field for the COGS counter rather than in the words.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@bryan-minimal
bryan-minimal force-pushed the fix/535-dangling-index-hit branch from 735ae79 to ef9e10c Compare October 26, 2026 18:00
@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

minimal-review — advisory review

Verdict COMMENT — 1 finding, 1 inline.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

Comment thread crates/rcache/src/remote.rs Resolved
bryan-minimal and others added 4 commits October 26, 2026 15:09
From review (#2624). `Error::NotFound` carried a spec hash from the index
lookup and an artifact file name from the store fetch under one wording;
it now says which ("spec hash", "artifact file") before the identifier.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

minimal-review — approved

Verdict APPROVE — No findings.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

@minimal-foundry minimal-foundry Bot added the review:approved minimal-review approved the current head label Oct 27, 2026
bryan-minimal and others added 2 commits October 27, 2026 14:31
Found by the lab (min-regress scenario 800): with the index retry no
longer repeating a 404 (#2623), a prefetch of a package whose artifact the
store no longer has failed the session spawn with "the remote cache has
no artifact for artifact file ...". On main the same case only passed
because the 404 was retried and the stub served the artifact the second
time. The prefetch now treats `NotFound` as the miss it is, commits
nothing for it, and lets the build make the package, as the op path
already does; rcache has logged the dangling hit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… failing it

The planner trusts the remote index: a spec the index lists is scheduled as
a cache fill, which has no build behind it. When the store no longer has the
artifact (a dangling hit, inbox#535) the fill failed the whole build with
"the remote cache has no artifact for ...", which the lab's scenario 800
caught on the session-spawn path after the prefetch path was fixed.

The fill now reports `Error::DanglingCacheHit(bsr)`; the build driver masks
that spec out of every cache provider (the same `MaskingBinProvider` that
`--rebuild` uses) and plans again, so the spec is built like any other miss.
Artifacts completed before the re-plan are committed first, and each pass
masks one more spec, so the loop is bounded by the graph. The re-plan is
logged as `dangling_replan` next to rcache's `dangling_hit`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

minimal-review — advisory review

Verdict COMMENT — 2 findings, 2 inline.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

Comment thread crates/mctx/src/lib.rs Resolved
Comment thread crates/mctx/src/lib.rs Resolved
@minimal-foundry minimal-foundry Bot removed the review:approved minimal-review approved the current head label Oct 27, 2026
@bryan-minimal
Copy link
Copy Markdown
Member Author

Rounds 4 and 5 both come from the lab rather than a review thread. min-regress scenario 800 (a reverse proxy to the real cache that injects one failure per mode) runs this branch against main on debian-12, with #2623's round 5 merged in:

mode main (e8f53f6) this branch after round 4 after round 5
two 500s on the snapshot index 1 fetch, session spawn fails 3 fetches, 2 retrying in lines, success same
snapshot 404 asked once per reader, root index serves same same
body cut mid-record 1 fetch, InvalidData to the user 4 fetches, 3 retries, then "cache server was unreachable" same
first artifact gone from the store (and it stays gone across retries) no dangling_hit line, session spawn fails dangling_hit logged, then "gcc: the remote cache has no artifact for artifact file …" fails the session spawn dangling_hit logged, the package is built locally, session spawn succeeds

The last row is the gap this stack opened: once #2623 stopped retrying a 404, every consumer of materialize had to treat NotFound as the miss it is. There are three. op/patched.rs already did; round 4 fixed the session's prefetch in mctx; the lab then showed the third, the orchestrator's cache fill (local_backend.rs), which is what the session spawn actually builds through.

That one is different: the planner trusts the index, so a listed spec is scheduled as a cache fill with no build behind it. Round 5 makes the fill report Error::DanglingCacheHit(bsr), and build_graph_with_cancel masks that spec out of every cache provider (the same MaskingBinProvider that --rebuild uses) and plans again, so the package is built like any other miss. Artifacts completed before the re-plan are committed first; each pass masks one more spec, so the loop is bounded by the graph. The re-plan logs dangling_replan next to rcache's dangling_hit. 97 orchestrator, mctx and rcache tests pass. Lab job 01394 (candidate cand-r535-b662e474 = this branch at 7f37698, vs main e8f53f6, debian-12): the candidate passes all five modes, main fails three; the sweep reads fixed since base: 800-remote-index-retry, no regression.

…build for it

A dangling cache fill failed the pass at once: `abort_all` killed every
in-flight build, the driver learned one spec, and a retention sweep that
left N dangling specs cost N+1 passes (review on #2624 round 5). Now a
dangling fill fails only itself and what depends on it: the pass runs
everything else to completion, then hands the whole set back as
`Error::DanglingCacheHit(Vec<_>)`, and the driver masks them all for the
next pass. Test with a fake backend: two listed leaves gone from the store
are both reported in one pass, and the masked pass builds all three specs.

Also: the prefetch's comment on a dangling hit says what a prefetch-only
caller (mip add/update/check) gets, which is the rcache warning and no
cached package, not a build.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

minimal-review — approved

Verdict APPROVE — No findings.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

@minimal-foundry minimal-foundry Bot added the review:approved minimal-review approved the current head label Oct 27, 2026
@bryan-minimal
Copy link
Copy Markdown
Member Author

Lab evidence for round 5 (7f37698), as mr lab cite now writes it from the matrix report (the trace links need Viewer on the minimal-observability project; the scenario link is public to the org):

Lab evidence (min-regress batch c5-800-try on anvil, debian-12; job 01394, job trace)

Head: fix/535-dangling-index-hit at 7f37698 (lab name cand-r535-b662e474). Base: main at e8f53f6 (cand-base-e8f53f6e-e8f53f6e).

scenario distro base head
800-remote-index-retry: inbox#1162 / inbox#535: the remote index fetch retries a 5xx and a cut-off body, never a 4xx; an index hit whose artifact is gone is a miss, not an error debian-12 fail pass

Fixed since base: 1. Still reproducing on both: 0. Regressed: 0. Passing on both: 0. A verdict links to the run's trace in Cloud Trace (project access needed); the scenario link is its source with the # issue: it reproduces.

The same scenario is queued against round 6 (cbb5920); its block follows when it lands.

@bryan-minimal
Copy link
Copy Markdown
Member Author

Lab evidence for round 6 (cbb5920), written by mr lab cite --spans: the table as before, then each run's trace as the harness renders it (span tree with durations, log lines under their spans; the first 120 lines, the link has the rest), so the run reads here without Cloud Trace.

Lab evidence (min-regress batch c5-800-try on anvil, debian-12; job 01398, job trace)

Head: fix/535-dangling-index-hit at cbb5920 (lab name cand-r535-79447060). Base: main at e8f53f6 (cand-base-e8f53f6e-e8f53f6e).

scenario distro base head
800-remote-index-retry: inbox#1162 / inbox#535: the remote index fetch retries a 5xx and a cut-off body, never a 4xx; an index hit whose artifact is gone is a miss, not an error debian-12 fail pass

Fixed since base: 1. Still reproducing on both: 0. Regressed: 0. Passing on both: 0. A verdict links to the run's trace in Cloud Trace (project access needed); the scenario link is its source with the # issue: it reproduces.

800-remote-index-retry on debian-12: base fail (trace 813e1fff)
slot wait  [min-regress] +0.0ms  0.2ms  {"min_regress.run_id": "20261028T001238200338-1136060", "min_regress.slot": "2"}
run 800-remote-index-retry  [min-regress] +0.2ms  173634.3ms ERROR  {"min_regress.scenario": "800-remote-index-retry", "min_regress.issue": "inbox#1162 / inbox#535: the remote index fetch retries a 5xx and a cut-off body, never 
  setup (clone, boot, install, verify)  [min-regress] +5.2ms  1766.2ms  
    clone_files  [min-regress] +18.7ms  27.3ms  {"min_regress.seconds": "0.027"}
    restore  [min-regress] +45.9ms  975.3ms  {"min_regress.seconds": "0.975"}
    agent_ready  [min-regress] +1021.2ms  0.9ms  {"min_regress.seconds": "0.001"}
    reidentify  [min-regress] +1022.1ms  190.6ms  {"min_regress.seconds": "0.191"}
    verify  [min-regress] +1224.8ms  515.7ms  
    push  [min-regress] +1746.0ms  25.4ms  
  scenario 800-remote-index-retry  [min-regress] +1778.1ms  171154.8ms ERROR  {"min_regress.verdict": "fail"}
    cmd  [minimal-cli] +2818.6ms  0.1ms  {"trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "3c6f398b130c0f1b", "parent_span_id": "8c9cee7a02299093"}
    cmd  [minimal-cli] +2844.6ms  4781.4ms  {"trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "e02bf11807d45fcc", "parent_span_id": "8c9cee7a02299093"}
      activation_step  [minimal-cli] +2844.7ms  0.0ms  {"step": "check_arguments"}
      activation_step  [minimal-cli] +2844.8ms  0.1ms  {"step": "check_project"}
      activation_step  [minimal-cli] +2845.0ms  108.3ms  {"step": "start_daemon"}
        · +2845.0ms INFO spawning native minimald run --detach
        · +2953.2ms INFO native minimald spawned successfully
      activation_step  [minimal-cli] +2953.4ms  0.3ms  {"step": "resolve_loadouts"}
      activation_step  [minimal-cli] +2953.7ms  0.1ms  {"step": "plan_upload"}
      activation_step  [minimal-cli] +2953.9ms  38.0ms  {"step": "connect_daemon"}
        client.connect  [minimal-cli] +2953.9ms  36.8ms  
        client.rpc  [minimal-cli] +2990.7ms  1.1ms  {"rpc": "minimald-v1-ListSessions", "timeout_s": "60"}
      rpc  [minimald] +2991.0ms  0.7ms  {"rpc": "minimald-v1-ListSessions", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "39cbae7b228ae607", "parent_span_id": "e02bf11807d45fcc"}
        · +2991.2ms INFO rpc dispatched
        · +2991.6ms INFO rpc served
        sessions.manager.message  [minimald] +2991.5ms  0.1ms  {"kind": "List", "queued_behind": "0", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "d887d53b4399e5b3", "parent_span_id": "39cbae7b228ae607"}
      activation_step  [minimal-cli] +2991.9ms  0.0ms  {"step": "register_box"}
      activation_step  [minimal-cli] +2991.9ms  0.1ms  {"step": "mint_grants"}
      activation_step  [minimal-cli] +2992.1ms  31.3ms  {"step": "create_session"}
        client.rpc  [minimal-cli] +2992.1ms  31.2ms  {"rpc": "minimald-v1-CreateSession", "timeout_s": "60"}
      rpc  [minimald] +2992.3ms  30.8ms  {"rpc": "minimald-v1-CreateSession", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "468e902ea014e224", "parent_span_id": "e02bf11807d45fcc"}
        · +2992.4ms INFO rpc dispatched
        · +3020.8ms INFO session created
        · +3023.0ms INFO session-start loopback probe picked the publish surface
        · +3023.0ms INFO rpc served
        sessions.manager.message  [minimald] +2992.5ms  28.1ms  {"kind": "CreateSession", "queued_behind": "0", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "f64ecf1d15f0db11", "parent_span_id": "468e902ea014e2
          · +3020.1ms INFO filed the box record
          · +3020.6ms INFO session starts
      activation_step  [minimal-cli] +3023.4ms  8.6ms  {"step": "report_start"}
        · +3031.5ms INFO session start decided the live name surface for this host, with the range unit's state beside it
      activation_step  [minimal-cli] +3032.0ms  12.4ms  {"step": "upload_files"}
      rpc  [minimald] +3032.4ms  11.8ms  {"rpc": "minimald-v1-WorkspaceFilesTarZst", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "b73c93e172c878c0", "parent_span_id": "e02bf11807d45fcc"}
        · +3032.5ms INFO rpc dispatched
        · +3033.0ms INFO workspace upload started
        · +3044.1ms INFO workspace upload complete
        · +3044.2ms INFO rpc served
        sessions.manager.message  [minimald] +3032.6ms  0.0ms  {"kind": "GetSession", "queued_behind": "0", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "c15b71642e363b9f", "parent_span_id": "b73c93e172c878c0"
        session.message  [minimald] +3032.7ms  0.3ms  {"kind": "GetPaths", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "44edece98500929d", "parent_span_id": "b73c93e172c878c0"}
        session.message  [minimald] +3033.1ms  0.0ms  {"kind": "SetWorkspaceUploadInFlight", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "8e0df01473ade54b", "parent_span_id": "b73c93e172c878c0"}
        session.message  [minimald] +3044.0ms  0.0ms  {"kind": "SetWorkspaceUploadInFlight", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "de08a36b152e2cc5", "parent_span_id": "b73c93e172c878c0"}
      activation_step  [minimal-cli] +3044.5ms  4565.3ms  {"step": "compose_loadout"}
        client.rpc  [minimal-cli] +3044.5ms  4565.3ms  {"rpc": "minimald-v1-ConfigureLoadout", "timeout_s": "60"}
      rpc  [minimald] +3044.7ms  4558.5ms  {"rpc": "minimald-v1-ConfigureLoadout", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "059b44c0f97f1589", "parent_span_id": "e02bf11807d45fcc"}
        · +3044.8ms INFO rpc dispatched
        · +7603.2ms INFO rpc served
        sessions.manager.message  [minimald] +3044.9ms  0.0ms  {"kind": "GetSession", "queued_behind": "0", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "cbdf0ec0785fbc1f", "parent_span_id": "059b44c0f97f1589"
        session.message  [minimald] +3045.0ms  4556.2ms  {"kind": "ConfigureLoadout", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "b9399a32aa8e1305", "parent_span_id": "059b44c0f97f1589"}
          · +7581.9ms INFO compose: walking top-level packages for env_state_wiring + env_dir/file_mappings
          · +7582.2ms INFO compose: daemon-side contribution collected
          compose.resolve_project  [minimald] +3045.7ms  4536.0ms  {"project_path": "/tmp/tmp.BWTsnFYgVK/ok/state/minimal/sessions/9ca0e/tree"}
            compose.graph_from_all_packages  [minimald] +3045.8ms  4535.8ms  
              checkouts.checkout_of  [minimald] +3131.8ms  1644.1ms  {"remote": "https://github.com/gominimal/pkgs"}
      activation_step  [minimal-cli] +7609.9ms  16.0ms  {"step": "finalize"}
        client.rpc  [minimal-cli] +7609.9ms  15.9ms  {"rpc": "minimald-v1-FinalizeSession", "timeout_s": "60"}
      rpc  [minimald] +7610.2ms  15.6ms  {"rpc": "minimald-v1-FinalizeSession", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "98a3febfc68c6864", "parent_span_id": "e02bf11807d45fcc"}
        · +7610.3ms INFO rpc dispatched
        · +7625.7ms INFO rpc served
        sessions.manager.message  [minimald] +7610.3ms  0.0ms  {"kind": "GetSession", "queued_behind": "0", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "c0e75f5f7abceea6", "parent_span_id": "98a3febfc68c6864"
        session.message  [minimald] +7610.4ms  15.2ms  {"kind": "Finalize", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "35e0aed9daa910c4", "parent_span_id": "98a3febfc68c6864"}
          · +7610.9ms INFO registered PTask hostname
      activation_step  [minimal-cli] +7625.9ms  0.0ms  {"step": "hold_name"}
    cmd  [minimal-cli] +7634.5ms  71716.1ms  {"trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "4baaf1ccdf1bb412", "parent_span_id": "8c9cee7a02299093"}
      · +7637.5ms INFO found session
      client.connect  [minimal-cli] +7634.7ms  1.9ms  
      client.rpc  [minimal-cli] +7636.6ms  0.8ms  {"rpc": "minimald-v1-GetSessionRecord", "timeout_s": "60"}
      rpc  [minimald] +7636.8ms  0.5ms  {"rpc": "minimald-v1-GetSessionRecord", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "8b6d0805c8688e23", "parent_span_id": "4baaf1ccdf1bb412"}
        · +7636.9ms INFO rpc dispatched
        · +7637.3ms INFO rpc served
        sessions.manager.message  [minimald] +7637.0ms  0.3ms  {"kind": "GetRecord", "queued_behind": "0", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "2bf531f60f182d57", "parent_span_id": "8b6d0805c8688e23"}
      client.connect  [minimal-cli] +7637.7ms  1.4ms  
      client.rpc  [minimal-cli] +7639.2ms  0.7ms  {"rpc": "minimald-v1-GetSessionRecord", "timeout_s": "60"}
      rpc  [minimald] +7639.3ms  0.5ms  {"rpc": "minimald-v1-GetSessionRecord", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "c617db52fd8d3320", "parent_span_id": "4baaf1ccdf1bb412"}
        · +7639.4ms INFO rpc dispatched
        · +7639.8ms INFO rpc served
        sessions.manager.message  [minimald] +7639.4ms  0.3ms  {"kind": "GetRecord", "queued_behind": "0", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "bf702fd7de375ed7", "parent_span_id": "c617db52fd8d3320"}
      exec  [minimald] +7674.3ms  71672.9ms  {"session_id": "01a17fda-1579-7172-aaa2-3efde6e9ca0e", "command": "min://shell true", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "a02f4674837a05
        sessions.manager.message  [minimald] +7674.3ms  0.0ms  {"kind": "GetSession", "queued_behind": "0", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "d1482a0b90bbef9a", "parent_span_id": "a02f4674837a052a"
        session.message  [minimald] +7674.5ms  71653.8ms  {"kind": "EnsureHost", "trace_id": "813e1fff4412f3657cc925c5c7eb7d4c", "span_id": "f7ce4f5a116f49f6", "parent_span_id": "a02f4674837a052a"}
          · +7675.7ms INFO remote cache read location
          · +7776.2ms INFO the classifier tree is not installed on this host — the placement probe cannot even make its throwaway leaf in it; the installer has to install the tr
          · +7776.2ms INFO the session's host-address box runs unenforced on this host
          · +7776.3ms INFO session content
          · +7776.3ms INFO session content
          · +7776.4ms INFO session content
          · +7776.4ms INFO session content
          · +7776.5ms INFO session content
          · +7776.5ms INFO session content
          · +7776.5ms INFO session content
          · +8215.0ms INFO cache index: auto chain ready
          · +79279.6ms INFO sandbox launch: box execs as the box uid with no capability a box may not hold
          · +79279.7ms INFO sandbox launch: box sealed against the socket families its namespace does not confine
          · +79328.0ms INFO the launch recorded its host-address box's egress enforcement on the session record
          checkouts.checkout_of  [minimald] +774
10BC8
9.1ms  0.3ms  {"remote": "https://github.com/gominimal/pkgs"}
          new_with_offline  [minimald] +8215.1ms  18.2ms  {"cache_dir": "\"/tmp/tmp.BWTsnFYgVK/ok/cache/minimal/downloads\"", "with_gcloud_auth": false, "offline": false}
          materialize  [minimald] +8265.6ms  67868.5ms  {"span_name": "gcc"}
          materialize  [minimald] +8265.6ms  3464.3ms  {"span_name": "findutils"}
          materialize  [minimald] +8265.9ms  59424.1ms  {"span_name": "glibc"}
          materialize  [minimald] +8265.9ms  7278.2ms  {"span_name": "bash"}
          materialize  [minimald] +8265.9ms  614.4ms  {"span_name": "mpfr"}
          materialize  [minimald] +8265.9ms  2731.4ms  {"span_name": "tar"}
          materialize  [minimald] +8266.0ms  3894.7ms  {"span_name": "acl"}
          materialize  [minimald] +8266.0ms  2910.2ms  {"span_name": "grep"}
          materialize  [minimald] +8880.4ms  16532.7ms  {"span_name": "coreutils"}
          materialize  [minimald] +10997.5ms  6196.5ms  {"span_name": "gawk"}
          materialize  [minimald] +11176.4ms  529.3ms  {"span_name": "zlib"}
          materialize  [minimald] +11706.1ms  563.0ms  {"span_name": "mpc"}
          materialize  [minimald] +11730.0ms  3580.8ms  {"span_name": "sed"}
          materialize  [minimald] +12160.9ms  765.9ms  {"span_name": "socat"}
          materialize  [minimald] +12269.3ms  657.5ms  {"span_name": "file"}
          materialize  [minimald] +12926.9ms  1962.4ms  {"span_name": "xz"}
… 612 more lines (the full trace is the link above)
800-remote-index-retry on debian-12: head pass (trace 79cc2c9f)
slot wait  [min-regress] +0.0ms  0.2ms  {"min_regress.run_id": "20261028T001238191268-1136061", "min_regress.slot": "1"}
run 800-remote-index-retry  [min-regress] +0.2ms  355056.6ms  {"min_regress.scenario": "800-remote-index-retry", "min_regress.issue": "inbox#1162 / inbox#535: the remote index fetch retries a 5xx and a cut-off body, never 
  setup (clone, boot, install, verify)  [min-regress] +5.2ms  1641.9ms  
    clone_files  [min-regress] +21.2ms  34.2ms  {"min_regress.seconds": "0.034"}
    restore  [min-regress] +55.4ms  840.1ms  {"min_regress.seconds": "0.840"}
    agent_ready  [min-regress] +895.4ms  0.9ms  {"min_regress.seconds": "0.001"}
    reidentify  [min-regress] +896.3ms  190.7ms  {"min_regress.seconds": "0.191"}
    verify  [min-regress] +1099.0ms  517.3ms  
    push  [min-regress] +1621.8ms  25.2ms  
  scenario 800-remote-index-retry  [min-regress] +1649.8ms  352978.0ms  {"min_regress.verdict": "pass"}
    cmd  [minimal-cli] +2688.7ms  0.1ms  {"trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "90633f448fa468c3", "parent_span_id": "f8327ce22c333695"}
    cmd  [minimal-cli] +2713.9ms  5626.9ms  {"trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "ba07b4ec71614924", "parent_span_id": "f8327ce22c333695"}
      activation_step  [minimal-cli] +2714.1ms  0.0ms  {"step": "check_arguments"}
      activation_step  [minimal-cli] +2714.1ms  0.1ms  {"step": "check_project"}
      activation_step  [minimal-cli] +2714.3ms  108.6ms  {"step": "start_daemon"}
        · +2714.4ms INFO spawning native minimald run --detach
        · +2822.9ms INFO native minimald spawned successfully
      activation_step  [minimal-cli] +2823.0ms  0.3ms  {"step": "resolve_loadouts"}
      activation_step  [minimal-cli] +2823.3ms  0.1ms  {"step": "plan_upload"}
      activation_step  [minimal-cli] +2823.5ms  36.3ms  {"step": "connect_daemon"}
        client.connect  [minimal-cli] +2823.5ms  35.1ms  
        client.rpc  [minimal-cli] +2858.7ms  1.0ms  {"rpc": "minimald-v1-ListSessions", "timeout_s": "60"}
      rpc  [minimald] +2858.9ms  0.6ms  {"rpc": "minimald-v1-ListSessions", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "206299908236bd0e", "parent_span_id": "ba07b4ec71614924"}
        · +2859.0ms INFO rpc dispatched
        · +2859.5ms INFO rpc served
        sessions.manager.message  [minimald] +2859.3ms  0.1ms  {"kind": "List", "queued_behind": "0", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "4571e46411400f29", "parent_span_id": "206299908236bd0e"}
      activation_step  [minimal-cli] +2859.8ms  0.0ms  {"step": "register_box"}
      activation_step  [minimal-cli] +2859.8ms  0.1ms  {"step": "mint_grants"}
      activation_step  [minimal-cli] +2860.0ms  35.1ms  {"step": "create_session"}
        client.rpc  [minimal-cli] +2860.0ms  35.0ms  {"rpc": "minimald-v1-CreateSession", "timeout_s": "60"}
      rpc  [minimald] +2860.2ms  34.6ms  {"rpc": "minimald-v1-CreateSession", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "7394e57aee195ea0", "parent_span_id": "ba07b4ec71614924"}
        · +2860.3ms INFO rpc dispatched
        · +2892.5ms INFO session created
        · +2894.6ms INFO session-start loopback probe picked the publish surface
        · +2894.7ms INFO rpc served
        sessions.manager.message  [minimald] +2860.4ms  32.0ms  {"kind": "CreateSession", "queued_behind": "0", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "3033c46f664e91a7", "parent_span_id": "7394e57aee195e
          box.create  [minimald] +2860.4ms  31.9ms  {"box_type": "session", "box_id": "01a17fda-14ed-737e-989f-121c45431f10", "parent": "none"}
            · +2891.7ms INFO filed the box record
            · +2892.3ms INFO session starts
      activation_step  [minimal-cli] +2895.1ms  8.9ms  {"step": "report_start"}
        · +2903.6ms INFO session start decided the live name surface for this host, with the range unit's state beside it
      activation_step  [minimal-cli] +2904.1ms  12.5ms  {"step": "upload_files"}
      rpc  [minimald] +2904.5ms  11.9ms  {"rpc": "minimald-v1-WorkspaceFilesTarZst", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "7dc066f1e7bc35aa", "parent_span_id": "ba07b4ec71614924"}
        · +2904.6ms INFO rpc dispatched
        · +2905.1ms INFO workspace upload started
        · +2916.3ms INFO workspace upload complete
        · +2916.4ms INFO rpc served
        sessions.manager.message  [minimald] +2904.6ms  0.0ms  {"kind": "GetSession", "queued_behind": "0", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "5304095ce95e6530", "parent_span_id": "7dc066f1e7bc35aa"
        session.message  [minimald] +2904.7ms  0.3ms  {"kind": "GetPaths", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "e18110b5541a7c7b", "parent_span_id": "7dc066f1e7bc35aa"}
        session.message  [minimald] +2905.2ms  0.0ms  {"kind": "SetWorkspaceUploadInFlight", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "cef775f696977c37", "parent_span_id": "7dc066f1e7bc35aa"}
        session.message  [minimald] +2916.2ms  0.0ms  {"kind": "SetWorkspaceUploadInFlight", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "9a9983e92db0fabc", "parent_span_id": "7dc066f1e7bc35aa"}
      activation_step  [minimal-cli] +2916.7ms  5405.6ms  {"step": "compose_loadout"}
        client.rpc  [minimal-cli] +2916.7ms  5405.4ms  {"rpc": "minimald-v1-ConfigureLoadout", "timeout_s": "60"}
      rpc  [minimald] +2916.9ms  5400.4ms  {"rpc": "minimald-v1-ConfigureLoadout", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "14f4b6c0d4e914ca", "parent_span_id": "ba07b4ec71614924"}
        · +2917.0ms INFO rpc dispatched
        · +8317.2ms INFO rpc served
        sessions.manager.message  [minimald] +2917.1ms  0.0ms  {"kind": "GetSession", "queued_behind": "0", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "f5e68ef65e1d81cb", "parent_span_id": "14f4b6c0d4e914ca"
        session.message  [minimald] +2917.1ms  5399.9ms  {"kind": "ConfigureLoadout", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "9c90da0bb69bfebd", "parent_span_id": "14f4b6c0d4e914ca"}
          · +8285.7ms INFO compose: walking top-level packages for env_state_wiring + env_dir/file_mappings
          · +8286.1ms INFO compose: daemon-side contribution collected
          compose.resolve_project  [minimald] +2918.0ms  5367.4ms  {"project_path": "/tmp/tmp.AvuMFHoK98/ok/state/minimal/sessions/31f10/tree"}
            compose.graph_from_all_packages  [minimald] +2918.1ms  5367.2ms  
              checkouts.checkout_of  [minimald] +3009.2ms  1888.5ms  {"remote": "https://github.com/gominimal/pkgs"}
      activation_step  [minimal-cli] +8322.3ms  18.3ms  {"step": "finalize"}
        client.rpc  [minimal-cli] +8322.3ms  18.1ms  {"rpc": "minimald-v1-FinalizeSession", "timeout_s": "60"}
      rpc  [minimald] +8322.8ms  17.4ms  {"rpc": "minimald-v1-FinalizeSession", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "90999ab7e32cebe9", "parent_span_id": "ba07b4ec71614924"}
        · +8323.0ms INFO rpc dispatched
        · +8340.2ms INFO rpc served
        sessions.manager.message  [minimald] +8323.1ms  0.1ms  {"kind": "GetSession", "queued_behind": "0", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "f8680a56b0696477", "parent_span_id": "90999ab7e32cebe9"
        session.message  [minimald] +8323.2ms  16.8ms  {"kind": "Finalize", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "b66faf35be29cadc", "parent_span_id": "90999ab7e32cebe9"}
          · +8323.9ms INFO registered PTask hostname
      activation_step  [minimal-cli] +8340.7ms  0.0ms  {"step": "hold_name"}
    cmd  [minimal-cli] +8358.3ms  72364.5ms  {"trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "aac24d4f4493a3a1", "parent_span_id": "f8327ce22c333695"}
      · +8362.0ms INFO found session
      client.connect  [minimal-cli] +8358.5ms  2.3ms  
      client.rpc  [minimal-cli] +8360.9ms  1.0ms  {"rpc": "minimald-v1-GetSessionRecord", "timeout_s": "60"}
      rpc  [minimald] +8361.2ms  0.6ms  {"rpc": "minimald-v1-GetSessionRecord", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "440dfd10c319ab07", "parent_span_id": "aac24d4f4493a3a1"}
        · +8361.3ms INFO rpc dispatched
        · +8361.8ms INFO rpc served
        sessions.manager.message  [minimald] +8361.4ms  0.3ms  {"kind": "GetRecord", "queued_behind": "0", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "504965a110448da7", "parent_span_id": "440dfd10c319ab07"}
      client.connect  [minimal-cli] +8362.3ms  1.6ms  
      client.rpc  [minimal-cli] +8363.9ms  0.8ms  {"rpc": "minimald-v1-GetSessionRecord", "timeout_s": "60"}
      rpc  [minimald] +8364.1ms  0.5ms  {"rpc": "minimald-v1-GetSessionRecord", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "76dbe95cba924928", "parent_span_id": "aac24d4f4493a3a1"}
        · +8364.2ms INFO rpc dispatched
        · +8364.6ms INFO rpc served
        sessions.manager.message  [minimald] +8364.3ms  0.3ms  {"kind": "GetRecord", "queued_behind": "0", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "c6b3ee9f11f10042", "parent_span_id": "76dbe95cba924928"}
      exec  [minimald] +8403.5ms  72316.5ms  {"session_id": "01a17fda-14ed-737e-989f-121c45431f10", "command": "min://shell true", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "3226d41ece9f4d
        sessions.manager.message  [minimald] +8403.5ms  0.0ms  {"kind": "GetSession", "queued_behind": "0", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "297f6e37d145c83a", "parent_span_id": "3226d41ece9f4dbf"
        session.message  [minimald] +8403.8ms  72301.8ms  {"kind": "EnsureHost", "trace_id": "79cc2c9f2f681d5675352da6e2942416", "span_id": "004d2df85e66d04e", "parent_span_id": "3226d41ece9f4dbf"}
          · +8405.3ms INFO remote cache read location
          · +8521.7ms INFO the classifier tree is not installed on this host — the placement probe cannot even make its throwaway leaf in it; the installer has to install the tr
          · +8521.8ms INFO the session's host-address box runs unenforced on this host
          · +8521.9ms INFO session content
          · +8521.9ms INFO session content
          · +8522.0ms INFO session content
          · +8522.0ms INFO session content
          · +8522.1ms INFO session content
          · +8522.1ms INFO session content
          · +8522.2ms INFO session content
          · +8997.8ms INFO cache index: auto chain ready
          · +80659.5ms INFO sandbox launch: box execs as the box uid with no capability a box may not hold
          · +80659.7ms INFO sandbox launch: box sealed against the socket families its namespace does not confine
          · +80705.3ms INFO the launch recorded its host-address box's egress enforcement on the session record
          checkouts.checkout_of  [minimald] +8487.6ms  0.3ms  {"remote": "https://github.com/gominimal/pkgs"}
          new_with_offline  [minimald] +8997.9ms  18.5ms  {"cache_dir": "\"/tmp/tmp.AvuMFHoK98/ok/cache/minimal/downloads\"", "with_gcloud_auth": false, "offline": false}
          materialize  [minimald] +9070.2ms  3569.6ms  {"span_name": "grep"}
          materialize  [minimald] +9070.2ms  69709.5ms  {"span_name": "gcc"}
          materialize  [minimald] +9070.4ms  1279.3ms  {"span_name": "readline"}
          materialize  [minimald] +9070.5ms  3636.5ms  {"span_name": "diffutils"}
          materialize  [minimald] +9070.5ms  1208.9ms  {"span_name": "gmp"}
          materialize  [minimald] +9070.7ms  17561.2ms  {"span_name": "coreutils"}
          materialize  [minimald] +9070.7ms  7543.9ms  {"span_name": "libcap"}
          materialize  [minimald] +9070.9ms  2132.4ms  {"span_name": "gzip"}
          materialize  [minimald] +10279.6ms  728.3ms  {"span_name": "bzip2"}
          materialize  [minimald] +10349.9ms  4611.4ms  {"span_name": "acl"}
          materialize  [minimald] +11008.0ms  49179.0ms  {"span_name": "openssl"}
          materialize  [minimald] +11203.5ms  1752.4ms  {"span_name": "xz"}
          materialize  [minimald] +12640.1ms  3341.8ms  {"span_name": "tar"}
          materialize  [minimald] +12707.2ms  8118.4ms  {"span_name": "pcre2"}
          materialize  [minimald] +12956.1ms  67361.1ms  {"span_name": "ncurses"}
… 686 more lines (the full trace is the link above)

@bryan-minimal
bryan-minimal merged commit b0ca2de into fix/1162-index-retry Oct 28, 2026
2 checks passed
@bryan-minimal
bryan-minimal deleted the fix/535-dangling-index-hit branch October 28, 2026 19:37
yash-marathe pushed a commit to yash-marathe/minimal that referenced this pull request Oct 29, 2026
…gominimal#2623)

* fix(rcache): retry transient 5xx on the index fetch

One transient 5xx from the cache server on the first index/closure
fetch aborted the whole session spawn, even though artifact
downloads already rode out such failures with bounded retries.

Give the index fetch the same treatment as materialize: retry
transient failures (transport errors, error statuses) up to
DEFAULT_FETCH_RETRIES times with the shared 0.5s/1s/2s exponential
backoff. 404s and SnapshotMissing stay non-transient so the
snapshot-to-root fallback is untouched. When retries run out, the
error names the object and HTTP status (Error::Unavailable) instead
of surfacing the raw reqwest debug dump.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 6b8ad3d)

* chore(worker-iterate): address CodeRabbit findings on PR gominimal#2496

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 855e0b1)

* fix(rcache): retry only a 5xx or 429 on the index fetch, never a 4xx

`is_transient` counted every `Error::Fetch { .. }` as transient, and the index path now maps every
non-success status to it, so a 401, 403 or 404 on a snapshot object was retried three times with
backoff and then reported as "try again shortly": the wrong advice for a refusal, and the whole
backoff added to every such failure. A `Fetch { status }` is transient only for a 5xx or 429;
transport errors and hash mismatches are retried as before. A scripted 403 pins it: one 
ACB9
call,
`Fetch { status: 403 }`. The artifact path still classifies through `error_for_status`; its doc
says so.

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

* fix(rcache): never retry an unbuildable request; classify artifact statuses as the index does

Review on gominimal#2623. `backend.get` can fail before anything is sent (a URL the backend cannot
represent); that came back as `Backend`, was retried three times with backoff, and then read "the
cache server was unreachable". It is `Error::Request` now, deterministic and never retried. The
artifact path classified a refused status through `error_for_status` into `Backend`, so a 404 or
403 on an object was retried where the index path's was not; both paths now return `Fetch { status
}`, and `is_transient` has one rule, in one doc paragraph: transport, 5xx/429 and a hash mismatch
retry; a 4xx, a request that could not be built, local I/O and a missing spec do not.

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

* fix(rcache): box the index retry loop so min's dispatch stays under rustc's query depth

Inlined, the retry loop added one layer to the future of every command that
opens the cache, and the layout of `run_command`'s future then overran
rustc's query depth limit (128) at the `Box::pin` in cmd/mod.rs, on 1.99.0
and 1.99.1 alike: `queries overflow the depth limit!`. Pinned on the heap,
the loop's state is a pointer in the caller's state and the depth stops
there. No behaviour change.

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

* build(minimal): raise the recursion limit where the in-process daemon is compiled

The depth overflow moved: boxed in the cache, it reappeared in the test
crates (`bug`, `git_remote`) while computing the layout of
`minimald::env::BoxChannel::handle`'s async body, the daemon these tests
run in-process. rustc's help says the fix: `#![recursion_limit = "256"]`,
which minimald, mctx, op and mip already declare and the `min` crate roots
did not. Added to the bin, the lib and every integration test root.

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

* fix(rcache): retry an index body cut off mid-record

From review (gominimal#2623). A 200 whose body is not a whole number of records is
a cut-off response, the same class of failure as a transport error, so it
is `UnexpectedEof` and the retry loop treats it as transient; a malformed
index stays `InvalidData` and is not retried. When every attempt comes back
cut off the index is reported unavailable, never accepted as a shorter one.

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

* refactor(rcache): the retry policy is one function, used by the index and the artifact fetch

Review on round 5: the retry loop was mixed into the business logic twice,
once around the index fetch and once around an artifact's. It is
`retrying_transient` now: try `once` until it succeeds, fails in a way
`is_transient` does not cover, or the retry budget is spent, with the same
bounded exponential backoff and log lines; the caller keeps the meaning of
a spent budget (the index reports `Unavailable`, an artifact surfaces the
error). The index's `Box::pin` stays for the query-depth reason it carries.
40 rcache tests unchanged.

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

* fix(rcache): an index hit whose artifact is gone is a miss, not a failure (gominimal#2624)

* fix(rcache): an index hit whose artifact is gone is a miss, not a failure

An index hit followed by a 404 on the artifact object failed the resolve with a hard
`Backend(...404...)`, retried as if transient. It is a normal state of this architecture: a
snapshot index keeps every record forever, and the store's retention (or a redistribution fix)
deletes objects out from under them. `fetch_verified` now returns `Error::NotFound` on a 404,
after one attempt, with a `cache_dangling_hit` warning naming the object, so a caller treats it
as the miss it is: the patched-build path already does; the orchestrator's hydrate is the
other half, filed separately per Norrie's call on inbox#535.

Refs: gominimal/inbox#535

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

* fix(rcache): a miss names its object, and the callers name the package

Review on gominimal#2624. `NotFound` displayed as its Debug name, so a dangling hit reached the session
fetch and `cache_hydrate` as the bare word `NotFound`, worse than the 404 and URL it replaced. The
variant carries the object now (the spec hash for an index miss, the artifact name for a gone
object) and reads "the remote cache has no artifact for <object>"; the session package fetch and
the orchestrator's hydrate prefix the package name, so the user sees which package and what. The
dangling-hit warning states only the fact, names the object, and carries `kind = "dangling_hit"`
as a field for the COGS counter rather than in the words.

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

* fix(rcache): say which identifier a not-found names

From review (gominimal#2624). `Error::NotFound` carried a spec hash from the index
lookup and an artifact file name from the store fetch under one wording;
it now says which ("spec hash", "artifact file") before the identifier.

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

* fix(mctx): a dangling index hit is a miss in the session's prefetch too

Found by the lab (min-regress scenario 800): with the index retry no
longer repeating a 404 (gominimal#2623), a prefetch of a package whose artifact the
store no longer has failed the session spawn with "the remote cache has
no artifact for artifact file ...". On main the same case only passed
because the 404 was retried and the stub served the artifact the second
time. The prefetch now treats `NotFound` as the miss it is, commits
nothing for it, and lets the build make the package, as the op path
already does; rcache has logged the dangling hit.

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

* fix(orchestrator): a dangling index hit re-plans the build instead of failing it

The planner trusts the remote index: a spec the index lists is scheduled as
a cache fill, which has no build behind it. When the store no longer has the
artifact (a dangling hit, inbox#535) the fill failed the whole build with
"the remote cache has no artifact for ...", which the lab's scenario 800
caught on the session-spawn path after the prefetch path was fixed.

The fill now reports `Error::DanglingCacheHit(bsr)`; the build driver masks
that spec out of every cache provider (the same `MaskingBinProvider` that
`--rebuild` uses) and plans again, so the spec is built like any other miss.
Artifacts completed before the re-plan are committed first, and each pass
masks one more spec, so the loop is bounded by the graph. The re-plan is
logged as `dangling_replan` next to rcache's `dangling_hit`.

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

* fix(orchestrator): one pass learns every dangling spec and aborts no build for it

A dangling cache fill failed the pass at once: `abort_all` killed every
in-flight build, the driver learned one spec, and a retention sweep that
left N dangling specs cost N+1 passes (review on gominimal#2624 round 5). Now a
dangling fill fails only itself and what depends on it: the pass runs
everything else to completion, then hands the whole set back as
`Error::DanglingCacheHit(Vec<_>)`, and the driver masks them all for the
next pass. Test with a fake backend: two listed leaves gone from the store
are both reported in one pass, and the masked pass builds all three specs.

Also: the prefetch's comment on a dangling hit says what a prefetch-only
caller (mip add/update/check) gets, which is the rcache warning and no
cached package, not a build.

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

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: gominimal-aw-bot[bot] <281738952+gominimal-aw-bot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review:approved minimal-review approved the current head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0