CCI report: 10d41156-20b7-4ede-bfdf-e63635954e64

Request message

{
  "request": "trigger",
  "version": 1,
  "event_type": "patch",
  "repository": {
    "id": "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5",
    "name": "heartwood",
    "description": "Radicle Heartwood Protocol & Stack",
    "private": false,
    "default_branch": "master",
    "delegates": [
      "did:key:z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT",
      "did:key:z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW",
      "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
      "did:key:z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz",
      "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz"
    ]
  },
  "action": "Created",
  "patch": {
    "id": "9e557d1ad972dab998c920a82122de8e8b921c73",
    "author": {
      "id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
      "alias": "fintohaps"
    },
    "title": "node/wire: fix stuck repo sync by always reporting fetch results to the service",
    "state": {
      "status": "open",
      "conflicts": []
    },
    "before": "1070b777da7dc540ad7e4fe64dcd23870179945b",
    "after": "d973a548b976f16663d3535ef5187fbbcfaf81d8",
    "commits": [
      "d973a548b976f16663d3535ef5187fbbcfaf81d8",
      "3459ad57cc490001bb75e45b187d7c16583be6c9",
      "665e640fe487ce8e9c1056845046c9a7e52199d2",
      "d230ff72ee190b8818328c1b03fe7b09fef32324",
      "df1e3e6129a181af2d8ef1982da4d061429abc26",
      "4a05e52ba10da742cf195505daab3ed8212f75cc",
      "7b910476c367f70bca91ad8e72195ad712380531"
    ],
    "target": "1070b777da7dc540ad7e4fe64dcd23870179945b",
    "labels": [],
    "assignees": [],
    "revisions": [
      {
        "id": "9e557d1ad972dab998c920a82122de8e8b921c73",
        "author": {
          "id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
          "alias": "fintohaps"
        },
        "description": "## Background \n\nWhile running nodes for Radicle Garden, we've repeatedly seen repositories that\nstop syncing: a repo gets \"stuck\" and won't fetch from *any* peer, and the only\nway to recover is to restart the node. This patch is a suspected fix for that,\nplus tests that reproduce the underlying bug.\n\n## What (I think) was going wrong\n\nWhen a node starts fetching a repo from a peer, it records that fetch as\n\"active\" so it doesn't try to fetch the same repo twice at once. That active\nmarker is supposed to be cleared when the fetch finishes (success or failure).\n\nThe problem is what happens when the peer **disconnects** at the wrong moment.\nThere are two spots where, if the peer was no longer connected, the code would\nquietly give up and never tell the rest of the system the fetch had ended:\n\n1. When a fetch result came back but the peer had already dropped, the result\n   was logged and discarded.\n2. When a fetch was about to start but the peer had already dropped, the fetch\n   was dropped silently.\n\nIn both cases the \"active\" marker for that repo was never cleared. From then on,\nevery future attempt to fetch that repo, even from a perfectly healthy peer,\nwould be blocked because the system still thought a fetch was already in\nprogress. That matches the \"stuck repo until restart\" behavior we've been\nseeing.\n\n## What the fix does\n\n- **Always report the outcome, even on disconnect.** Both spots now notify the\n  service that the fetch ended (as a failure), so the active marker always gets\n  cleared and the repo becomes fetchable again.\n- **Ignore stale/late results.** As a safety guard, when a result comes in we\n  now confirm it came from the *same* peer that started the active fetch. If a\n  late result arrives from an old peer after we've already moved on and started\n  fetching the repo from someone else, we ignore it rather than wrongly\n  cancelling the newer fetch.\n\n## Tests included\n\nMost of the diff is tests that first reproduce the orphaned-fetch behavior and\nthen confirm the fix:\n\n- A test proving the old code discarded the result on disconnect, plus a\n  \"connected\" contrast test so we know it's actually exercising the disconnect\n  path and not passing trivially.\n- A service-layer reproduction of the orphaned fetch.\n- An invariant check on the fetcher state guarding against leftover active\n  entries.\n\n## Caveat\n\nThis is a **suspected** fix. The reasoning and the tests line up with the symptoms we've observed, but since the original bug only showed up intermittently in production under real disconnect timing, we can't be fully certain this covers every cause of the stuck-repo issue. Worth a careful review of the wire.rs changes in particular, since that's where the disconnect handling logic changed, and it's my first time delving into this part of the codebase.",
        "base": "07f748475beacd41463ee5ebc0d7a93539ab8f55",
        "oid": "2ad6cc40f14f79b3769dc00b8e8840ce2de1a264",
        "timestamp": 1781902413
      },
      {
        "id": "b3845fd2dcbbe292ae5ec764521c225414f37676",
        "author": {
          "id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
          "alias": "fintohaps"
        },
        "description": "fetcher: Guard fetched against stale node results\n\n- Clear active[rid] only when the result's node matches the node that\n  started the fetch; mismatched results now report NotFound\n- Prevent a late completion from a disconnected peer clearing a newer\n  fetch for the same repo started by a different node\n- Add test covering the stale-from mismatch path",
        "base": "07f748475beacd41463ee5ebc0d7a93539ab8f55",
        "oid": "44e7cbded222bc0bd45ba246eff793ba2188d152",
        "timestamp": 1781904439
      },
      {
        "id": "4baa4f9815c32935c5f08cae1c4737da8783947c",
        "author": {
          "id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
          "alias": "fintohaps"
        },
        "description": "",
        "base": "07f748475beacd41463ee5ebc0d7a93539ab8f55",
        "oid": "d513158566da0f4d942206ea6503c0e317f23612",
        "timestamp": 1781904594
      },
      {
        "id": "5d831a0a1f194bb328efc9df3c8bc364a094f5df",
        "author": {
          "id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
          "alias": "fintohaps"
        },
        "description": "",
        "base": "07f748475beacd41463ee5ebc0d7a93539ab8f55",
        "oid": "40e415ea843b8665839ae761a7461ce1ad1b6b2b",
        "timestamp": 1781960274
      },
      {
        "id": "a4970e258818a0478aeda401973d05e9a916380b",
        "author": {
          "id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
          "alias": "fintohaps"
        },
        "description": "",
        "base": "07f748475beacd41463ee5ebc0d7a93539ab8f55",
        "oid": "e922ea73858d66f1152b20152ff9c86e74feb26f",
        "timestamp": 1782121156
      },
      {
        "id": "e4e2622180d2c85324391bc737adbec8d7a5ca79",
        "author": {
          "id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
          "alias": "fintohaps"
        },
        "description": "Use image that actually exists\n\nWe discovered that there is no rust alpine image\nfor the 1.85 and 3.22 combination",
        "base": "07f748475beacd41463ee5ebc0d7a93539ab8f55",
        "oid": "7b6f6047f109688d4a53bd53319fcd5c16cc49eb",
        "timestamp": 1782141994
      },
      {
        "id": "356206a9649fdc4645966388386cd708c5b118e9",
        "author": {
          "id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
          "alias": "fintohaps"
        },
        "description": "",
        "base": "07f748475beacd41463ee5ebc0d7a93539ab8f55",
        "oid": "9c06e16513a096dba19cdcabe822454cdd183da4",
        "timestamp": 1782142037
      },
      {
        "id": "0073e58d9fa06c42d09f86332b5a144646001556",
        "author": {
          "id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
          "alias": "fintohaps"
        },
        "description": "Changes:\n- Rebase\n- Squash flipping of test case into a single case, separated in its own commit\n- Remove invariant test in favour of adding to existing test\n- Reorder commits",
        "base": "1070b777da7dc540ad7e4fe64dcd23870179945b",
        "oid": "d973a548b976f16663d3535ef5187fbbcfaf81d8",
        "timestamp": 1782147970
      }
    ]
  }
}

Send response

{
  "response": "triggered",
  "run_id": {
    "id": "10d41156-20b7-4ede-bfdf-e63635954e64"
  },
  "info_url": "https://cci.rad.levitte.org//10d41156-20b7-4ede-bfdf-e63635954e64.html"
}

Checkout the source (in /opt/radcis/ci.rad.levitte.org/cci/state/10d41156-20b7-4ede-bfdf-e63635954e64/w)

Started at: 2026-06-22 19:06:19.344749+02:00

Commands:

Read the repo configuration (.radicle/native.yaml in /opt/radcis/ci.rad.levitte.org/cci/state/10d41156-20b7-4ede-bfdf-e63635954e64/w)

shell: 'export RUSTDOCFLAGS=''-D warnings''


  cargo --version

  rustc --version


  cargo fmt --check

  cargo clippy --all-targets --workspace -- --deny warnings

  cargo build --all-targets --workspace

  cargo doc --workspace --no-deps --all-features

  cargo test --workspace --no-fail-fast

  '

Run the script

Commands:

Send result

{
  "response": "finished",
  "result": "success"
}