From 5b45b380724b7d8bc9b2111b6ba7282215e6f7d4 Mon Sep 17 00:00:00 2001
From: Will Pfleger <wpfleger@block.xyz>
Date: Fri, 22 May 2026 11:40:14 -0400
Subject: [PATCH 1/6] perf(ci): add caching, path filtering, and nextest to
 speed up CI
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

CI wall-clock time is bottlenecked by uncached Rust builds and jobs
running unnecessarily on unrelated changes. Server cross-compile
(~7-8 min × 2 targets) and Desktop Build macOS (~7-10 min) had zero
cargo caching; all jobs ran on every PR regardless of what changed.

- Add dorny/paths-filter gating so jobs only run when relevant files
  change (rust, desktop, web, mobile groups); push to main still runs
  everything
- Add Swatinem/rust-cache to server-cross-compile and
  desktop-build-macos (the two most expensive uncached jobs)
- Move docker compose up earlier in desktop-e2e-integration so
  containers boot during pnpm/Playwright install instead of after
- Add Docker image caching for postgres/redis/typesense in E2E job
- Add pnpm store cache to web, desktop, and desktop-e2e-integration
- Switch unit tests to cargo-nextest for parallel test execution
- Update Justfile test-unit to auto-detect nextest with script fallback
---
 .github/workflows/ci.yml | 114 +++++++++++++++++++++++++++++++++++++--
 justfile                 |   7 ++-
 2 files changed, 117 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5dda23bc81..b02c63547a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,10 +9,48 @@ env:
   PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.cache/ms-playwright
 
 jobs:
+  changes:
+    name: Detect Changed Paths
+    runs-on: ubuntu-latest
+    timeout-minutes: 2
+    permissions:
+      contents: read
+      pull-requests: read
+    outputs:
+      rust: ${{ steps.filter.outputs.rust }}
+      desktop: ${{ steps.filter.outputs.desktop }}
+      desktop-rust: ${{ steps.filter.outputs.desktop-rust }}
+      web: ${{ steps.filter.outputs.web }}
+      mobile: ${{ steps.filter.outputs.mobile }}
+    steps:
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
+      - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
+        id: filter
+        with:
+          filters: |
+            rust:
+              - 'crates/**'
+              - 'Cargo.toml'
+              - 'Cargo.lock'
+              - 'scripts/run-tests.sh'
+              - 'Justfile'
+            desktop:
+              - 'desktop/**'
+              - '!desktop/src-tauri/**'
+            desktop-rust:
+              - 'desktop/src-tauri/**'
+            web:
+              - 'web/**'
+              - 'pnpm-lock.yaml'
+            mobile:
+              - 'mobile/**'
+
   rust-lint:
     name: Rust Lint
     runs-on: ubuntu-latest
     timeout-minutes: 30
+    needs: [changes]
+    if: github.event_name == 'push' || needs.changes.outputs.rust == 'true' || needs.changes.outputs.desktop-rust == 'true'
     permissions:
       contents: read
     steps:
@@ -30,19 +68,27 @@ jobs:
     name: Unit Tests
     runs-on: ubuntu-latest
     timeout-minutes: 30
+    needs: [changes]
+    if: github.event_name == 'push' || needs.changes.outputs.rust == 'true'
     permissions:
       contents: read
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
       - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
+      - name: Install cargo-nextest
+        uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2
+        with:
+          tool: cargo-nextest
       - name: Unit tests
-        run: just test-unit
+        run: cargo nextest run -p sprout-core -p sprout-auth --lib
 
   desktop:
     name: Desktop
     runs-on: ubuntu-latest
     timeout-minutes: 45
+    needs: [changes]
+    if: github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true'
     permissions:
       contents: read
     steps:
@@ -76,6 +122,12 @@ jobs:
             libxdo-dev \
             patchelf \
             wget
+      - name: Cache pnpm store
+        uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+        with:
+          path: ~/.local/share/pnpm/store/v3
+          key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
+          restore-keys: pnpm-${{ runner.os }}-
       - name: Install desktop dependencies
         run: just desktop-install-ci
       - name: Get Playwright version
@@ -122,6 +174,8 @@ jobs:
     name: Desktop E2E Integration
     runs-on: ubuntu-latest
     timeout-minutes: 45
+    needs: [changes]
+    if: github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.rust == 'true'
     permissions:
       contents: read
     steps:
@@ -132,6 +186,27 @@ jobs:
           workspaces: |
             .
             desktop/src-tauri
+      - name: Restore Docker image cache
+        uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+        with:
+          path: /tmp/docker-cache
+          key: docker-${{ hashFiles('docker-compose.yml') }}
+          restore-keys: docker-
+      - name: Load cached Docker images
+        run: |
+          if [ -d /tmp/docker-cache ]; then
+            for img in /tmp/docker-cache/*.tar; do
+              docker load < "$img" 2>/dev/null || true
+            done
+          fi
+      - name: Start integration services
+        run: docker compose up -d
+      - name: Cache pnpm store
+        uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+        with:
+          path: ~/.local/share/pnpm/store/v3
+          key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
+          restore-keys: pnpm-${{ runner.os }}-
       - name: Install desktop dependencies
         run: just desktop-install-ci
       - name: Get Playwright version
@@ -156,8 +231,6 @@ jobs:
           key: playwright-${{ runner.os }}-${{ steps.pw-version.outputs.version }}
       - name: Desktop build
         run: just desktop-build
-      - name: Start integration services
-        run: docker compose up -d
       - name: Wait for integration services
         run: |
           wait_healthy() {
@@ -228,16 +301,37 @@ jobs:
             desktop/test-results
             /tmp/sprout-relay.log
           if-no-files-found: ignore
+      - name: Save Docker image cache
+        if: github.event_name == 'push'
+        run: |
+          mkdir -p /tmp/docker-cache
+          docker save postgres:17-alpine > /tmp/docker-cache/postgres.tar
+          docker save redis:7-alpine > /tmp/docker-cache/redis.tar
+          docker save typesense/typesense:27.1 > /tmp/docker-cache/typesense.tar
+      - name: Upload Docker image cache
+        if: github.event_name == 'push'
+        uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+        with:
+          path: /tmp/docker-cache
+          key: docker-${{ hashFiles('docker-compose.yml') }}
 
   web:
     name: Web
     runs-on: ubuntu-latest
     timeout-minutes: 15
+    needs: [changes]
+    if: github.event_name == 'push' || needs.changes.outputs.web == 'true'
     permissions:
       contents: read
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
+      - name: Cache pnpm store
+        uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+        with:
+          path: ~/.local/share/pnpm/store/v3
+          key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
+          restore-keys: pnpm-${{ runner.os }}-
       - name: Install dependencies
         run: pnpm install --frozen-lockfile
       - name: Web lint and format
@@ -249,6 +343,8 @@ jobs:
     name: Mobile
     runs-on: ubuntu-latest
     timeout-minutes: 15
+    needs: [changes]
+    if: github.event_name == 'push' || needs.changes.outputs.mobile == 'true'
     permissions:
       contents: read
     steps:
@@ -269,6 +365,8 @@ jobs:
     name: Security
     runs-on: ubuntu-latest
     timeout-minutes: 20
+    needs: [changes]
+    if: github.event_name == 'push' || needs.changes.outputs.rust == 'true'
     permissions:
       contents: read
     steps:
@@ -302,6 +400,8 @@ jobs:
     name: Server Cross-Compile
     runs-on: ubuntu-latest
     timeout-minutes: 30
+    needs: [changes]
+    if: github.event_name == 'push' || needs.changes.outputs.rust == 'true'
     permissions:
       contents: read
     strategy:
@@ -313,6 +413,9 @@ jobs:
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
+      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
+        with:
+          key: cross-${{ matrix.target }}
       - name: Install cross
         uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2
         with:
@@ -334,11 +437,16 @@ jobs:
     name: Desktop Build (macOS)
     runs-on: macos-latest
     timeout-minutes: 45
+    needs: [changes]
+    if: github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true'
     permissions:
       contents: read
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
+      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
+        with:
+          workspaces: desktop/src-tauri
       - name: Install desktop dependencies
         run: just desktop-install-ci
       - name: Create sidecar placeholders
diff --git a/justfile b/justfile
index 82fd06d31e..4df66f16f3 100644
--- a/justfile
+++ b/justfile
@@ -148,7 +148,12 @@ test:
 
 # Run unit tests only (no infra needed)
 test-unit:
-    ./scripts/run-tests.sh unit
+    #!/usr/bin/env bash
+    if command -v cargo-nextest &>/dev/null; then
+        cargo nextest run -p sprout-core -p sprout-auth --lib
+    else
+        ./scripts/run-tests.sh unit
+    fi
 
 # Run integration tests only (starts services if needed)
 test-integration:

From bc569f95f2c47c5c0b5cb6f9ba2c7756839bfd1e Mon Sep 17 00:00:00 2001
From: Will Pfleger <wpfleger@block.xyz>
Date: Fri, 22 May 2026 12:16:15 -0400
Subject: [PATCH 2/6] fix(ci): address review findings from PR #723

Path filter gaps (missing desktop-rust, wrong Justfile case, missing
rust-toolchain.toml/deny.toml/ci.yml triggers), hardcoded pnpm store
path that missed the v11 directory, cache-poisoning from writing caches
on PR events, Docker tar caching that's slower than direct pulls, and
a diverged unit-test command between CI and Justfile.

Split pnpm caches into restore/save with push-only writes, add save-if
to new rust-cache instances, pin nextest to 0.9.136, and scope docker
compose to the 3 services CI actually needs.
---
 .github/workflows/ci.yml | 81 ++++++++++++++++++++++------------------
 1 file changed, 44 insertions(+), 37 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b02c63547a..21e72a1a73 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -32,11 +32,15 @@ jobs:
               - 'crates/**'
               - 'Cargo.toml'
               - 'Cargo.lock'
+              - 'rust-toolchain.toml'
+              - 'deny.toml'
+              - '.github/workflows/ci.yml'
               - 'scripts/run-tests.sh'
-              - 'Justfile'
+              - 'justfile'
             desktop:
               - 'desktop/**'
               - '!desktop/src-tauri/**'
+              - 'pnpm-lock.yaml'
             desktop-rust:
               - 'desktop/src-tauri/**'
             web:
@@ -79,9 +83,9 @@ jobs:
       - name: Install cargo-nextest
         uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2
         with:
-          tool: cargo-nextest
+          tool: cargo-nextest@0.9.136
       - name: Unit tests
-        run: cargo nextest run -p sprout-core -p sprout-auth --lib
+        run: just test-unit
 
   desktop:
     name: Desktop
@@ -122,10 +126,13 @@ jobs:
             libxdo-dev \
             patchelf \
             wget
-      - name: Cache pnpm store
-        uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+      - name: Get pnpm store directory
+        id: pnpm-cache
+        run: echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
+      - name: Restore pnpm store cache
+        uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
         with:
-          path: ~/.local/share/pnpm/store/v3
+          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
           key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
           restore-keys: pnpm-${{ runner.os }}-
       - name: Install desktop dependencies
@@ -169,13 +176,19 @@ jobs:
             desktop/playwright-report
             desktop/test-results
           if-no-files-found: ignore
+      - name: Save pnpm store cache
+        if: github.event_name == 'push'
+        uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+        with:
+          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
+          key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
 
   desktop-e2e-integration:
     name: Desktop E2E Integration
     runs-on: ubuntu-latest
     timeout-minutes: 45
     needs: [changes]
-    if: github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.rust == 'true'
+    if: github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true'
     permissions:
       contents: read
     steps:
@@ -186,25 +199,15 @@ jobs:
           workspaces: |
             .
             desktop/src-tauri
-      - name: Restore Docker image cache
-        uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
-        with:
-          path: /tmp/docker-cache
-          key: docker-${{ hashFiles('docker-compose.yml') }}
-          restore-keys: docker-
-      - name: Load cached Docker images
-        run: |
-          if [ -d /tmp/docker-cache ]; then
-            for img in /tmp/docker-cache/*.tar; do
-              docker load < "$img" 2>/dev/null || true
-            done
-          fi
       - name: Start integration services
-        run: docker compose up -d
-      - name: Cache pnpm store
-        uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+        run: docker compose up -d postgres redis typesense
+      - name: Get pnpm store directory
+        id: pnpm-cache
+        run: echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
+      - name: Restore pnpm store cache
+        uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
         with:
-          path: ~/.local/share/pnpm/store/v3
+          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
           key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
           restore-keys: pnpm-${{ runner.os }}-
       - name: Install desktop dependencies
@@ -301,19 +304,12 @@ jobs:
             desktop/test-results
             /tmp/sprout-relay.log
           if-no-files-found: ignore
-      - name: Save Docker image cache
-        if: github.event_name == 'push'
-        run: |
-          mkdir -p /tmp/docker-cache
-          docker save postgres:17-alpine > /tmp/docker-cache/postgres.tar
-          docker save redis:7-alpine > /tmp/docker-cache/redis.tar
-          docker save typesense/typesense:27.1 > /tmp/docker-cache/typesense.tar
-      - name: Upload Docker image cache
+      - name: Save pnpm store cache
         if: github.event_name == 'push'
         uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
         with:
-          path: /tmp/docker-cache
-          key: docker-${{ hashFiles('docker-compose.yml') }}
+          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
+          key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
 
   web:
     name: Web
@@ -326,10 +322,13 @@ jobs:
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
-      - name: Cache pnpm store
-        uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+      - name: Get pnpm store directory
+        id: pnpm-cache
+        run: echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
+      - name: Restore pnpm store cache
+        uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
         with:
-          path: ~/.local/share/pnpm/store/v3
+          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
           key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
           restore-keys: pnpm-${{ runner.os }}-
       - name: Install dependencies
@@ -338,6 +337,12 @@ jobs:
         run: just web-check
       - name: Web build
         run: just web-build
+      - name: Save pnpm store cache
+        if: github.event_name == 'push'
+        uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+        with:
+          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
+          key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
 
   mobile:
     name: Mobile
@@ -416,6 +421,7 @@ jobs:
       - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
         with:
           key: cross-${{ matrix.target }}
+          save-if: ${{ github.event_name != 'pull_request' }}
       - name: Install cross
         uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2
         with:
@@ -447,6 +453,7 @@ jobs:
       - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
         with:
           workspaces: desktop/src-tauri
+          save-if: ${{ github.event_name != 'pull_request' }}
       - name: Install desktop dependencies
         run: just desktop-install-ci
       - name: Create sidecar placeholders

From b06162e92892d07110e3c713c0afec186cd94660 Mon Sep 17 00:00:00 2001
From: Will Pfleger <wpfleger@block.xyz>
Date: Fri, 22 May 2026 12:28:40 -0400
Subject: [PATCH 3/6] fix(ci): add save-if to all rust-cache instances, fix
 version tag

Add save-if to the 4 pre-existing Swatinem/rust-cache steps (rust-lint,
unit-tests, desktop, desktop-e2e-integration) so cache writes are
restricted to push events across the board, not just the 2 new instances.

Fix dorny/paths-filter version comment: the pinned SHA is v3.0.2,
not the floating v3 tag.
---
 .github/workflows/ci.yml | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 21e72a1a73..9f7796a87a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,7 +24,7 @@ jobs:
       mobile: ${{ steps.filter.outputs.mobile }}
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
-      - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
+      - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
         id: filter
         with:
           filters: |
@@ -61,6 +61,8 @@ jobs:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
       - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
+        with:
+          save-if: ${{ github.event_name != 'pull_request' }}
       - name: Format check
         run: just fmt-check
       - name: Desktop Tauri format check
@@ -80,6 +82,8 @@ jobs:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
       - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
+        with:
+          save-if: ${{ github.event_name != 'pull_request' }}
       - name: Install cargo-nextest
         uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2
         with:
@@ -101,6 +105,7 @@ jobs:
       - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
         with:
           workspaces: desktop/src-tauri
+          save-if: ${{ github.event_name != 'pull_request' }}
       - name: Install Tauri dependencies (Linux)
         env:
           DEBIAN_FRONTEND: noninteractive
@@ -199,6 +204,7 @@ jobs:
           workspaces: |
             .
             desktop/src-tauri
+          save-if: ${{ github.event_name != 'pull_request' }}
       - name: Start integration services
         run: docker compose up -d postgres redis typesense
       - name: Get pnpm store directory

From c3960353b459719ebca0f1322de9ba83b90cc072 Mon Sep 17 00:00:00 2001
From: Will Pfleger <wpfleger@block.xyz>
Date: Fri, 22 May 2026 15:17:15 -0400
Subject: [PATCH 4/6] fix(ci): suppress zizmor false positives, fix
 install-action version tags

zizmor's cache-poisoning rule only recognizes lookup-only as a
mitigation for Swatinem/rust-cache, not save-if. All 6 instances
already have save-if set to prevent cache writes on PRs, but zizmor
can't introspect this parameter. Add inline ignore comments as a
stopgap until upstream fixes this (to be filed as a zizmor bug).

Fix taiki-e/install-action version comments: the pinned SHA is
v2.79.3, not the floating v2 tag.
---
 .github/workflows/ci.yml | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9f7796a87a..16ee7fe1aa 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -60,7 +60,7 @@ jobs:
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
-      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
+      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 zizmor: ignore[cache-poisoning]
         with:
           save-if: ${{ github.event_name != 'pull_request' }}
       - name: Format check
@@ -81,11 +81,11 @@ jobs:
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
-      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
+      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 zizmor: ignore[cache-poisoning]
         with:
           save-if: ${{ github.event_name != 'pull_request' }}
       - name: Install cargo-nextest
-        uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2
+        uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2.79.3
         with:
           tool: cargo-nextest@0.9.136
       - name: Unit tests
@@ -102,7 +102,7 @@ jobs:
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
-      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
+      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 zizmor: ignore[cache-poisoning]
         with:
           workspaces: desktop/src-tauri
           save-if: ${{ github.event_name != 'pull_request' }}
@@ -199,7 +199,7 @@ jobs:
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
-      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
+      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 zizmor: ignore[cache-poisoning]
         with:
           workspaces: |
             .
@@ -424,12 +424,12 @@ jobs:
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
-      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
+      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 zizmor: ignore[cache-poisoning]
         with:
           key: cross-${{ matrix.target }}
           save-if: ${{ github.event_name != 'pull_request' }}
       - name: Install cross
-        uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2
+        uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2.79.3
         with:
           tool: cross@0.2.5
       - name: Build server binaries
@@ -456,7 +456,7 @@ jobs:
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
-      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
+      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 zizmor: ignore[cache-poisoning]
         with:
           workspaces: desktop/src-tauri
           save-if: ${{ github.event_name != 'pull_request' }}

From 9c0773e1f15092d6e8e8f477e44f21178b0ba930 Mon Sep 17 00:00:00 2001
From: Will Pfleger <wpfleger@block.xyz>
Date: Fri, 22 May 2026 15:35:15 -0400
Subject: [PATCH 5/6] fix(ci): add zizmor config, start minio for e2e, revert
 inline ignores
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The inline zizmor: ignore[cache-poisoning] comments appended to the
version comment didn't suppress the findings — zizmor may require the
directive as the first token after #. Switch to a .github/zizmor.yml
config file which suppresses cache-poisoning for ci.yml at the file
level, with a reference to the upstream bug (zizmorcore/zizmor#2051).

Add minio and minio-init to the selective docker compose up command
in desktop-e2e-integration — the relay now requires S3/MinIO for
the git object-store conformance probe added in #726.
---
 .github/workflows/ci.yml | 15 ++++++++-------
 .github/zizmor.yml       |  7 +++++++
 2 files changed, 15 insertions(+), 7 deletions(-)
 create mode 100644 .github/zizmor.yml

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 16ee7fe1aa..136946b32b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -60,7 +60,7 @@ jobs:
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
-      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 zizmor: ignore[cache-poisoning]
+      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
         with:
           save-if: ${{ github.event_name != 'pull_request' }}
       - name: Format check
@@ -81,7 +81,7 @@ jobs:
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
-      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 zizmor: ignore[cache-poisoning]
+      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
         with:
           save-if: ${{ github.event_name != 'pull_request' }}
       - name: Install cargo-nextest
@@ -102,7 +102,7 @@ jobs:
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
-      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 zizmor: ignore[cache-poisoning]
+      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
         with:
           workspaces: desktop/src-tauri
           save-if: ${{ github.event_name != 'pull_request' }}
@@ -199,14 +199,14 @@ jobs:
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
-      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 zizmor: ignore[cache-poisoning]
+      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
         with:
           workspaces: |
             .
             desktop/src-tauri
           save-if: ${{ github.event_name != 'pull_request' }}
       - name: Start integration services
-        run: docker compose up -d postgres redis typesense
+        run: docker compose up -d postgres redis typesense minio minio-init
       - name: Get pnpm store directory
         id: pnpm-cache
         run: echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
@@ -259,6 +259,7 @@ jobs:
           wait_healthy "Postgres" "sprout-postgres"
           wait_healthy "Redis" "sprout-redis"
           wait_healthy "Typesense" "sprout-typesense"
+          wait_healthy "MinIO" "sprout-minio"
       - name: Apply database schema
         run: ./bin/pgschema apply --file schema/schema.sql --auto-approve
         env:
@@ -424,7 +425,7 @@ jobs:
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
-      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 zizmor: ignore[cache-poisoning]
+      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
         with:
           key: cross-${{ matrix.target }}
           save-if: ${{ github.event_name != 'pull_request' }}
@@ -456,7 +457,7 @@ jobs:
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
       - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
-      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 zizmor: ignore[cache-poisoning]
+      - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
         with:
           workspaces: desktop/src-tauri
           save-if: ${{ github.event_name != 'pull_request' }}
diff --git a/.github/zizmor.yml b/.github/zizmor.yml
new file mode 100644
index 0000000000..82e4f1a0ca
--- /dev/null
+++ b/.github/zizmor.yml
@@ -0,0 +1,7 @@
+rules:
+  cache-poisoning:
+    ignore:
+      # Swatinem/rust-cache uses save-if to prevent cache writes on PR events,
+      # which mitigates cache poisoning. zizmor only recognizes lookup-only as
+      # a mitigation, not save-if. Filed upstream: https://github.com/zizmorcore/zizmor/issues/2051
+      - ci.yml

From ee63c8e4a9b8501a1101cf97266681003a868139 Mon Sep 17 00:00:00 2001
From: Will Pfleger <wpfleger@block.xyz>
Date: Fri, 22 May 2026 16:11:42 -0400
Subject: [PATCH 6/6] chore(ci): remove zizmor.yml, dismiss alerts via API
 instead
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The zizmor scanner runs as an org-level GHAS integration, not from a
repo checkout — it never reads .github/zizmor.yml. Dismissed the
cache-poisoning alerts (#10-13, #32, #33) as false positives via the
code-scanning API instead, with references to the upstream bug
(zizmorcore/zizmor#2051).
---
 .github/zizmor.yml | 7 -------
 1 file changed, 7 deletions(-)
 delete mode 100644 .github/zizmor.yml

diff --git a/.github/zizmor.yml b/.github/zizmor.yml
deleted file mode 100644
index 82e4f1a0ca..0000000000
--- a/.github/zizmor.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-rules:
-  cache-poisoning:
-    ignore:
-      # Swatinem/rust-cache uses save-if to prevent cache writes on PR events,
-      # which mitigates cache poisoning. zizmor only recognizes lookup-only as
-      # a mitigation, not save-if. Filed upstream: https://github.com/zizmorcore/zizmor/issues/2051
-      - ci.yml
