From 1e32bd94624bad5cc0a3b05cb9ed24e7b2e03a82 Mon Sep 17 00:00:00 2001
From: Anthony Ronning <101225832+AnthonyRonning@users.noreply.github.com>
Date: Mon, 26 Oct 2026 04:35:28 +0000
Subject: [PATCH] Recover Agent dependency caches and remove CI setup
bottlenecks
---
.../validate-opensecret/references/checks.md | 6 +-
.github/workflows/agent-ci.yml | 221 ++++-
.github/workflows/agent-desktop-build.yml | 148 +++-
.github/workflows/opensecret-ci.yml | 6 +-
.github/workflows/sdk-integration.yml | 8 +-
.../crates/maple-agent/src/agent.rs | 28 +-
.../maple-agent/src/agent/maintenance.rs | 2 +-
.../crates/maple-agent/src/agent/projects.rs | 51 ++
.../maple-agent/src/agent/shutdown_tests.rs | 18 +-
docs/agent-ci-performance.md | 201 ++++-
docs/repository-workflows.md | 89 +-
flake.nix | 2 +
scripts/ci/agent_cache_scope.py | 262 ++++++
scripts/ci/agent_change_detection.py | 2 +
scripts/ci/agent_dependency_archive.py | 822 ++++++++++++++++++
scripts/ci/agent_master_selection.py | 29 +-
scripts/ci/test_agent_cache_scope.py | 248 ++++++
scripts/ci/test_agent_change_detection.py | 1 +
scripts/ci/test_agent_dependency_archive.py | 575 ++++++++++++
scripts/ci/test_agent_master_selection.py | 82 +-
scripts/ci/test_agent_workflows.py | 296 +++++--
scripts/ci/test_opensecret_workflows.py | 13 +-
services/opensecret/docs/dev-shell.md | 8 +
services/opensecret/flake.nix | 49 +-
24 files changed, 2948 insertions(+), 219 deletions(-)
create mode 100644 scripts/ci/agent_cache_scope.py
create mode 100644 scripts/ci/agent_dependency_archive.py
create mode 100644 scripts/ci/test_agent_cache_scope.py
create mode 100644 scripts/ci/test_agent_dependency_archive.py
diff --git a/.agents/skills/validate-opensecret/references/checks.md b/.agents/skills/validate-opensecret/references/checks.md
index 986f45ae1..3f7f3fe98 100644
--- a/.agents/skills/validate-opensecret/references/checks.md
+++ b/.agents/skills/validate-opensecret/references/checks.md
@@ -28,14 +28,14 @@ side-effect-disabled component Nix environment:
git -C ../.. submodule update --init --recursive -- services/opensecret/privatemode-public
OPENSECRET_DEV_POSTGRES=0 OPENSECRET_DEV_ENV=0 OPENSECRET_DEV_CONTAINERS=0 \
- nix develop --no-write-lock-file '.?submodules=1' -c cargo fmt --all -- --check
+ nix develop --no-write-lock-file '.?submodules=1#ci' -c cargo fmt --all -- --check
OPENSECRET_DEV_POSTGRES=0 OPENSECRET_DEV_ENV=0 OPENSECRET_DEV_CONTAINERS=0 \
- nix develop --no-write-lock-file '.?submodules=1' -c env RUSTFLAGS='-D warnings' \
+ nix develop --no-write-lock-file '.?submodules=1#ci' -c env RUSTFLAGS='-D warnings' \
cargo clippy --locked --all-targets --all-features -- -D warnings
OPENSECRET_DEV_POSTGRES=0 OPENSECRET_DEV_ENV=0 OPENSECRET_DEV_CONTAINERS=0 \
- nix develop --no-write-lock-file '.?submodules=1' -c env RUSTFLAGS='-D warnings' \
+ nix develop --no-write-lock-file '.?submodules=1#ci' -c env RUSTFLAGS='-D warnings' \
cargo test --locked --all-features
```
diff --git a/.github/workflows/agent-ci.yml b/.github/workflows/agent-ci.yml
index 753036e1f..64096499f 100644
--- a/.github/workflows/agent-ci.yml
+++ b/.github/workflows/agent-ci.yml
@@ -33,6 +33,9 @@ jobs:
name: Select Agent checks
runs-on: ubuntu-latest
timeout-minutes: 5
+ permissions:
+ contents: read
+ actions: read
outputs:
agent: ${{ steps.classify.outputs.agent }}
steps:
@@ -41,18 +44,37 @@ jobs:
fetch-depth: 0
persist-credentials: false
+ - name: Retain Agent changes since the last validated master run
+ id: master
+ if: ${{ github.repository == 'MaplePrivacyLabs/Maple' && github.ref == 'refs/heads/master' && github.event_name == 'push' }}
+ working-directory: .
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: python3 scripts/ci/agent_master_selection.py --lane ci >> "$GITHUB_OUTPUT"
+
- name: Classify changed paths
id: classify
working-directory: .
env:
BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
HEAD_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
+ MASTER_AGENT: ${{ steps.master.outputs.agent }}
run: |
set -euo pipefail
enable_agent() { echo 'agent=true' >> "$GITHUB_OUTPUT"; }
case "$GITHUB_EVENT_NAME" in
pull_request) separator='...' ;;
push)
+ # A later unrelated push must not consume Agent work cancelled
+ # before every workspace, provider and release check completed.
+ if [ "$GITHUB_REPOSITORY" = MaplePrivacyLabs/Maple ] && [ "$GITHUB_REF" = refs/heads/master ]; then
+ if [ "${MASTER_AGENT:-}" = false ]; then
+ echo 'agent=false' >> "$GITHUB_OUTPUT"
+ else
+ enable_agent
+ fi
+ exit 0
+ fi
separator='..'
if [[ "$BASE_SHA" =~ ^0+$ ]]; then
enable_agent
@@ -79,11 +101,16 @@ jobs:
# A failed selector must never silently omit validation.
if: ${{ always() && !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.agent != 'false') }}
runs-on: ${{ matrix.os }}
+ permissions:
+ contents: read
+ actions: read
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
os: [blacksmith-8vcpu-ubuntu-2404, macos-26, windows-latest]
+ env:
+ DEPENDENCY_CACHE_JOB: Agent (${{ matrix.os }})
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
@@ -137,13 +164,24 @@ jobs:
id: environment
run: |
if [ "$RUNNER_OS" = Windows ]; then
- python3 ../../scripts/ci/agent_cache.py --profile ci --output "$RUNNER_TEMP/agent-environment.json"
+ python3 ../../scripts/ci/agent_cache_scope.py --profile ci --output "$RUNNER_TEMP/agent-environment.json"
else
- nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_cache.py --profile ci --output "$RUNNER_TEMP/agent-environment.json"
+ nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_cache_scope.py --profile ci --output "$RUNNER_TEMP/agent-environment.json"
fi
+ - name: Restore validated master dependency archive
+ id: dependency-archive
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ python3 ../../scripts/ci/agent_dependency_archive.py restore \
+ --environment "$RUNNER_TEMP/agent-environment.json" --target "target" \
+ --job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-ci.yml \
+ --receipt "$RUNNER_TEMP/agent-dependency-receipt.json"
+
- name: Cache Agent Rust dependencies
id: rust-cache
+ if: ${{ steps.dependency-archive.outputs.restored != 'true' }}
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: apps/maple-agent -> target
@@ -151,26 +189,36 @@ jobs:
# Dependency hashes belong in the action's exact suffix, not its restore prefix.
key: maple-agent-ci-v2-${{ steps.environment.outputs.compatibility }}
cmd-format: python3 "${{ github.workspace }}/scripts/ci/agent_cache_command.py" {0}
- save-if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
+ save-if: "false"
- name: Check whether Cargo targets were restored
id: restored-target
run: |
- if [ -f "$GITHUB_WORKSPACE/apps/maple-agent/target/CACHEDIR.TAG" ]; then
- echo 'present=true' >> "$GITHUB_OUTPUT"
- else
- echo 'present=false' >> "$GITHUB_OUTPUT"
- fi
+ python3 - <<'PYTHON'
+ import os
+ from pathlib import Path
+ target = Path(os.environ["GITHUB_WORKSPACE"]) / "apps/maple-agent/target"
+ present = any(
+ any(fingerprint.iterdir()) and any(
+ output.is_file() and output.stat().st_size > 0
+ for pattern in ("*.rlib", "*.rmeta")
+ for output in (fingerprint.parent / "deps").glob(pattern)
+ )
+ for fingerprint in target.rglob(".fingerprint") if fingerprint.is_dir()
+ )
+ with Path(os.environ["GITHUB_OUTPUT"]).open("a") as output:
+ output.write(f"present={str(present).lower()}\n")
+ PYTHON
- name: Restore compatible legacy Cargo targets on a cache miss
- if: ${{ steps.rust-cache.outputs.cache-hit != 'true' && steps.restored-target.outputs.present != 'true' }}
+ if: ${{ steps.dependency-archive.outputs.restored != 'true' && steps.restored-target.outputs.present != 'true' }}
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: apps/maple-agent -> target
# Read only within the existing actual compiler/native ABI prefix.
- # The primary cache retains its exact dependency suffix and owns saves.
+ # Native bootstrap is read-only; durable artifacts own cache writes.
add-rust-environment-hash-key: "false"
- key: maple-agent-ci-v2-${{ steps.environment.outputs.compatibility }}
+ key: maple-agent-ci-v2-${{ steps.environment.outputs.legacy_compatibility }}
cmd-format: python3 "${{ github.workspace }}/scripts/ci/agent_cache_command.py" {0}
save-if: "false"
@@ -190,8 +238,7 @@ jobs:
- name: Verify the canonical SDK and proxy dependency graph
run: |
- metadata="$(mktemp)"
- trap 'rm -f "$metadata"' EXIT
+ metadata="$RUNNER_TEMP/agent-workspace-metadata.json"
if [ "$RUNNER_OS" = Windows ]; then
cargo metadata --locked --format-version 1 > "$metadata"
else
@@ -242,6 +289,29 @@ jobs:
nix develop --no-update-lock-file . -c bash -c 'cargo metadata --locked --format-version 1 > "$1"' -- "$metadata"
nix develop --no-update-lock-file . -c python3 dependencies/sqlcipher/verify_sqlcipher.py --metadata "$metadata"
+ - name: Prepare trusted dependency archive after validation
+ id: dependency-seed
+ if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' }}
+ run: |
+ python3 ../../scripts/ci/agent_dependency_archive.py pack \
+ --environment "$RUNNER_TEMP/agent-environment.json" --metadata "$RUNNER_TEMP/agent-workspace-metadata.json" \
+ --target "target" --job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-ci.yml \
+ --output-dir "$RUNNER_TEMP/agent-dependency-seed" \
+ --receipt "$RUNNER_TEMP/agent-dependency-receipt.json"
+
+ - name: Upload durable dependency archive
+ if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' && steps.dependency-seed.outputs.needs-upload == 'true' }}
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
+ with:
+ name: ${{ steps.dependency-seed.outputs.artifact-name }}
+ path: |
+ ${{ runner.temp }}/agent-dependency-seed/manifest.json
+ ${{ runner.temp }}/agent-dependency-seed/dependencies.tar.gz
+ if-no-files-found: error
+ compression-level: 0
+ overwrite: true
+ retention-days: 3
+
- name: Summarize phase timings and target size
if: always()
run: python3 ../../scripts/ci/agent_measure.py --summarize target
@@ -260,11 +330,16 @@ jobs:
needs: changes
if: ${{ always() && !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.agent != 'false') }}
runs-on: ${{ matrix.os }}
+ permissions:
+ contents: read
+ actions: read
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
os: [blacksmith-8vcpu-ubuntu-2404, macos-26, windows-latest]
+ env:
+ DEPENDENCY_CACHE_JOB: Agent provider (${{ matrix.os }})
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
@@ -335,17 +410,30 @@ jobs:
id: environment
run: |
if [ "$RUNNER_OS" = Windows ]; then
- python3 ../../scripts/ci/agent_cache.py --profile provider --cargo-config-dir "$PROVIDER_WORK" --output target/ci-metrics/environment.json
+ python3 ../../scripts/ci/agent_cache_scope.py --profile provider --cargo-config-dir "$PROVIDER_WORK" --output target/ci-metrics/environment.json
else
- nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_cache.py --profile provider --cargo-config-dir "$PROVIDER_WORK" --output target/ci-metrics/environment.json
+ nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_cache_scope.py --profile provider --cargo-config-dir "$PROVIDER_WORK" --output target/ci-metrics/environment.json
fi
+ - name: Restore validated master dependency archive
+ id: dependency-archive
+ env:
+ GH_TOKEN: ${{ github.token }}
+ PROVIDER_DEPENDENCY_FINGERPRINT: ${{ steps.provider.outputs.dependency-fingerprint }}
+ run: |
+ python3 ../../scripts/ci/agent_dependency_archive.py restore \
+ --environment "target/ci-metrics/environment.json" --target "$PROVIDER_TARGET" \
+ --job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-ci.yml \
+ --receipt "$RUNNER_TEMP/agent-dependency-receipt.json" \
+ --dependency-fingerprint "$PROVIDER_DEPENDENCY_FINGERPRINT"
+
- name: Restore only matching verified provider artifacts
id: provider-cache
+ if: ${{ steps.dependency-archive.outputs.restored != 'true' }}
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: ${{ env.PROVIDER_TARGET }}
- key: maple-agent-provider-v2-${{ steps.environment.outputs.compatibility }}-${{ steps.provider.outputs.dependency-fingerprint }}
+ key: maple-agent-provider-v2-${{ steps.environment.outputs.legacy_compatibility }}-${{ steps.provider.outputs.dependency-fingerprint }}
- name: Test released rusqlite against the static provider
run: |
@@ -356,12 +444,31 @@ jobs:
nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_measure.py --phase provider-test --target "$PROVIDER_TARGET" -- python3 scripts/test-rusqlite-provider.py "$metadata" --phase run --work-dir "$PROVIDER_WORK" --target-dir "$PROVIDER_TARGET"
fi
- - name: Save trusted provider artifacts
- if: ${{ success() && github.event_name == 'push' && github.ref == 'refs/heads/master' && steps.provider-cache.outputs.cache-hit != 'true' }}
- uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
+ - name: Prepare trusted dependency archive after validation
+ id: dependency-seed
+ if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' }}
+ env:
+ PROVIDER_DEPENDENCY_FINGERPRINT: ${{ steps.provider.outputs.dependency-fingerprint }}
+ run: |
+ python3 ../../scripts/ci/agent_dependency_archive.py pack \
+ --environment "target/ci-metrics/environment.json" --metadata "$PROVIDER_WORK/maple-provider-metadata.json" \
+ --target "$PROVIDER_TARGET" --job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-ci.yml \
+ --output-dir "$RUNNER_TEMP/agent-dependency-seed" \
+ --receipt "$RUNNER_TEMP/agent-dependency-receipt.json" \
+ --dependency-fingerprint "$PROVIDER_DEPENDENCY_FINGERPRINT"
+
+ - name: Upload durable dependency archive
+ if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' && steps.dependency-seed.outputs.needs-upload == 'true' }}
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
- path: ${{ env.PROVIDER_TARGET }}
- key: ${{ steps.provider-cache.outputs.cache-primary-key }}
+ name: ${{ steps.dependency-seed.outputs.artifact-name }}
+ path: |
+ ${{ runner.temp }}/agent-dependency-seed/manifest.json
+ ${{ runner.temp }}/agent-dependency-seed/dependencies.tar.gz
+ if-no-files-found: error
+ compression-level: 0
+ overwrite: true
+ retention-days: 3
- name: Summarize phase timings and target size
if: always()
@@ -381,7 +488,12 @@ jobs:
needs: changes
if: ${{ always() && !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.agent != 'false') }}
runs-on: blacksmith-8vcpu-ubuntu-2404
+ permissions:
+ contents: read
+ actions: read
timeout-minutes: 90
+ env:
+ DEPENDENCY_CACHE_JOB: Agent Linux release binary
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
@@ -403,13 +515,24 @@ jobs:
id: environment
run: |
if [ "$RUNNER_OS" = Windows ]; then
- python3 ../../scripts/ci/agent_cache.py --profile release --output "$RUNNER_TEMP/agent-environment.json"
+ python3 ../../scripts/ci/agent_cache_scope.py --profile release --output "$RUNNER_TEMP/agent-environment.json"
else
- nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_cache.py --profile release --output "$RUNNER_TEMP/agent-environment.json"
+ nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_cache_scope.py --profile release --output "$RUNNER_TEMP/agent-environment.json"
fi
+ - name: Restore validated master dependency archive
+ id: dependency-archive
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ python3 ../../scripts/ci/agent_dependency_archive.py restore \
+ --environment "$RUNNER_TEMP/agent-environment.json" --target "target" \
+ --job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-ci.yml \
+ --receipt "$RUNNER_TEMP/agent-dependency-receipt.json"
+
- name: Cache Agent release dependencies
id: rust-cache
+ if: ${{ steps.dependency-archive.outputs.restored != 'true' }}
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: apps/maple-agent -> target
@@ -417,24 +540,34 @@ jobs:
# Dependency hashes belong in the action's exact suffix, not its restore prefix.
key: maple-agent-release-v2-${{ steps.environment.outputs.compatibility }}
cmd-format: python3 "${{ github.workspace }}/scripts/ci/agent_cache_command.py" {0}
- save-if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
+ save-if: "false"
- name: Check whether Cargo targets were restored
id: restored-target
run: |
- if [ -f "$GITHUB_WORKSPACE/apps/maple-agent/target/CACHEDIR.TAG" ]; then
- echo 'present=true' >> "$GITHUB_OUTPUT"
- else
- echo 'present=false' >> "$GITHUB_OUTPUT"
- fi
+ python3 - <<'PYTHON'
+ import os
+ from pathlib import Path
+ target = Path(os.environ["GITHUB_WORKSPACE"]) / "apps/maple-agent/target"
+ present = any(
+ any(fingerprint.iterdir()) and any(
+ output.is_file() and output.stat().st_size > 0
+ for pattern in ("*.rlib", "*.rmeta")
+ for output in (fingerprint.parent / "deps").glob(pattern)
+ )
+ for fingerprint in target.rglob(".fingerprint") if fingerprint.is_dir()
+ )
+ with Path(os.environ["GITHUB_OUTPUT"]).open("a") as output:
+ output.write(f"present={str(present).lower()}\n")
+ PYTHON
- name: Restore compatible legacy Cargo targets on a cache miss
- if: ${{ steps.rust-cache.outputs.cache-hit != 'true' && steps.restored-target.outputs.present != 'true' }}
+ if: ${{ steps.dependency-archive.outputs.restored != 'true' && steps.restored-target.outputs.present != 'true' }}
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: apps/maple-agent -> target
add-rust-environment-hash-key: "false"
- key: maple-agent-release-v2-${{ steps.environment.outputs.compatibility }}
+ key: maple-agent-release-v2-${{ steps.environment.outputs.legacy_compatibility }}
cmd-format: python3 "${{ github.workspace }}/scripts/ci/agent_cache_command.py" {0}
save-if: "false"
@@ -445,8 +578,7 @@ jobs:
- name: Verify the canonical SDK and proxy dependency graph
run: |
- metadata="$(mktemp)"
- trap 'rm -f "$metadata"' EXIT
+ metadata="$RUNNER_TEMP/agent-workspace-metadata.json"
if [ "$RUNNER_OS" = Windows ]; then
cargo metadata --locked --format-version 1 > "$metadata"
else
@@ -482,6 +614,29 @@ jobs:
if-no-files-found: error
retention-days: 5
+ - name: Prepare trusted dependency archive after validation
+ id: dependency-seed
+ if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' }}
+ run: |
+ python3 ../../scripts/ci/agent_dependency_archive.py pack \
+ --environment "$RUNNER_TEMP/agent-environment.json" --metadata "$RUNNER_TEMP/agent-workspace-metadata.json" \
+ --target "target" --job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-ci.yml \
+ --output-dir "$RUNNER_TEMP/agent-dependency-seed" \
+ --receipt "$RUNNER_TEMP/agent-dependency-receipt.json"
+
+ - name: Upload durable dependency archive
+ if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' && steps.dependency-seed.outputs.needs-upload == 'true' }}
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
+ with:
+ name: ${{ steps.dependency-seed.outputs.artifact-name }}
+ path: |
+ ${{ runner.temp }}/agent-dependency-seed/manifest.json
+ ${{ runner.temp }}/agent-dependency-seed/dependencies.tar.gz
+ if-no-files-found: error
+ compression-level: 0
+ overwrite: true
+ retention-days: 3
+
- name: Summarize phase timings and target size
if: always()
run: python3 ../../scripts/ci/agent_measure.py --summarize target
diff --git a/.github/workflows/agent-desktop-build.yml b/.github/workflows/agent-desktop-build.yml
index dc5300974..ce3124cb3 100644
--- a/.github/workflows/agent-desktop-build.yml
+++ b/.github/workflows/agent-desktop-build.yml
@@ -81,6 +81,9 @@ jobs:
needs: changes
if: ${{ always() && !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.agent != 'false') && (github.event_name == 'pull_request' || (github.repository == 'MaplePrivacyLabs/Maple' && github.ref == 'refs/heads/master' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch'))) }}
runs-on: blacksmith-6vcpu-macos-26
+ permissions:
+ contents: read
+ actions: read
timeout-minutes: 120
strategy:
fail-fast: false
@@ -88,6 +91,7 @@ jobs:
variant: ${{ fromJSON(github.event_name == 'pull_request' && '["dev"]' || '["dev","prod"]') }}
env:
MAPLE_RELEASE_PROFILE: ${{ matrix.variant }}
+ DEPENDENCY_CACHE_JOB: Build macOS binary (${{ matrix.variant }})
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
@@ -107,37 +111,66 @@ jobs:
run: python3 scripts/ci/select-xcode.py
- name: Fingerprint the actual release environment
id: environment
- run: nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_cache.py --profile release --output "$RUNNER_TEMP/agent-environment.json"
+ run: nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_cache_scope.py --profile release --output "$RUNNER_TEMP/agent-environment.json"
+ - name: Restore validated master dependency archive
+ id: dependency-archive
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ python3 ../../scripts/ci/agent_dependency_archive.py restore \
+ --environment "$RUNNER_TEMP/agent-environment.json" --target "target" \
+ --job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-desktop-build.yml \
+ --receipt "$RUNNER_TEMP/agent-dependency-receipt.json"
+
- name: Cache Agent release dependencies
id: rust-cache
+ if: ${{ steps.dependency-archive.outputs.restored != 'true' }}
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32
with:
workspaces: apps/maple-agent -> target
add-rust-environment-hash-key: "true"
key: maple-agent-release-v2-${{ matrix.variant }}-${{ steps.environment.outputs.compatibility }}
cmd-format: python3 "${{ github.workspace }}/scripts/ci/agent_cache_command.py" {0}
- save-if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
+ save-if: "false"
- name: Check whether Cargo targets were restored
id: restored-target
run: |
- if [ -f "$GITHUB_WORKSPACE/apps/maple-agent/target/CACHEDIR.TAG" ]; then
- echo 'present=true' >> "$GITHUB_OUTPUT"
- else
- echo 'present=false' >> "$GITHUB_OUTPUT"
- fi
+ python3 - <<'PYTHON'
+ import os
+ from pathlib import Path
+ target = Path(os.environ["GITHUB_WORKSPACE"]) / "apps/maple-agent/target"
+ present = any(
+ any(fingerprint.iterdir()) and any(
+ output.is_file() and output.stat().st_size > 0
+ for pattern in ("*.rlib", "*.rmeta")
+ for output in (fingerprint.parent / "deps").glob(pattern)
+ )
+ for fingerprint in target.rglob(".fingerprint") if fingerprint.is_dir()
+ )
+ with Path(os.environ["GITHUB_OUTPUT"]).open("a") as output:
+ output.write(f"present={str(present).lower()}\n")
+ PYTHON
+
- name: Restore compatible legacy Cargo targets on a cache miss
- if: ${{ steps.rust-cache.outputs.cache-hit != 'true' && steps.restored-target.outputs.present != 'true' }}
+ if: ${{ steps.dependency-archive.outputs.restored != 'true' && steps.restored-target.outputs.present != 'true' }}
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32
with:
workspaces: apps/maple-agent -> target
add-rust-environment-hash-key: "false"
- key: maple-agent-release-v2-${{ matrix.variant }}-${{ steps.environment.outputs.compatibility }}
+ key: maple-agent-release-v2-${{ matrix.variant }}-${{ steps.environment.outputs.legacy_compatibility }}
cmd-format: python3 "${{ github.workspace }}/scripts/ci/agent_cache_command.py" {0}
save-if: "false"
- name: Record the current release environment after restore
run: |
mkdir -p target/ci-metrics
cp "$RUNNER_TEMP/agent-environment.json" target/ci-metrics/environment.json
+ - name: Verify the canonical SDK and proxy dependency graph
+ run: |
+ metadata="$RUNNER_TEMP/agent-workspace-metadata.json"
+ # shellcheck disable=SC2016 # The child shell expands its own positional argument.
+ nix develop --no-update-lock-file . -c bash -c 'cargo metadata --locked --format-version 1 > "$1"' -- "$metadata"
+ python3 ../../scripts/ci/verify-agent-rust-deps.py "$metadata"
+ nix develop --no-update-lock-file . -c python3 scripts/verify-sqlcipher-build.py "$metadata"
- name: Build optimized Agent with embedded profile
run: nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_measure.py --phase release-build --target target -- ./scripts/build-release.sh "$MAPLE_RELEASE_PROFILE"
- name: Upload prebuilt Agent binary and notices
@@ -151,6 +184,29 @@ jobs:
if-no-files-found: error
retention-days: 7
+ - name: Prepare trusted dependency archive after validation
+ id: dependency-seed
+ if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' }}
+ run: |
+ python3 ../../scripts/ci/agent_dependency_archive.py pack \
+ --environment "$RUNNER_TEMP/agent-environment.json" --metadata "$RUNNER_TEMP/agent-workspace-metadata.json" \
+ --target "target" --job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-desktop-build.yml \
+ --output-dir "$RUNNER_TEMP/agent-dependency-seed" \
+ --receipt "$RUNNER_TEMP/agent-dependency-receipt.json"
+
+ - name: Upload durable dependency archive
+ if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' && steps.dependency-seed.outputs.needs-upload == 'true' }}
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
+ with:
+ name: ${{ steps.dependency-seed.outputs.artifact-name }}
+ path: |
+ ${{ runner.temp }}/agent-dependency-seed/manifest.json
+ ${{ runner.temp }}/agent-dependency-seed/dependencies.tar.gz
+ if-no-files-found: error
+ compression-level: 0
+ overwrite: true
+ retention-days: 3
+
- name: Summarize release phase timings and target size
if: always()
run: python3 ../../scripts/ci/agent_measure.py --summarize target
@@ -291,6 +347,9 @@ jobs:
needs: changes
if: ${{ always() && !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.agent != 'false') && (github.event_name == 'pull_request' || (github.repository == 'MaplePrivacyLabs/Maple' && github.ref == 'refs/heads/master' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch'))) }}
runs-on: blacksmith-8vcpu-ubuntu-2404
+ permissions:
+ contents: read
+ actions: read
timeout-minutes: 120
strategy:
fail-fast: false
@@ -298,6 +357,7 @@ jobs:
variant: ${{ fromJSON(github.event_name == 'pull_request' && '["dev"]' || '["dev","prod"]') }}
env:
MAPLE_RELEASE_PROFILE: ${{ matrix.variant }}
+ DEPENDENCY_CACHE_JOB: Linux bundle (${{ matrix.variant }})
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
@@ -314,37 +374,66 @@ jobs:
run: bash scripts/ci/verify-nix-runtime.sh
- name: Fingerprint the actual release environment
id: environment
- run: nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_cache.py --profile release --output "$RUNNER_TEMP/agent-environment.json"
+ run: nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_cache_scope.py --profile release --output "$RUNNER_TEMP/agent-environment.json"
+ - name: Restore validated master dependency archive
+ id: dependency-archive
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ python3 ../../scripts/ci/agent_dependency_archive.py restore \
+ --environment "$RUNNER_TEMP/agent-environment.json" --target "target" \
+ --job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-desktop-build.yml \
+ --receipt "$RUNNER_TEMP/agent-dependency-receipt.json"
+
- name: Cache Agent release dependencies
id: rust-cache
+ if: ${{ steps.dependency-archive.outputs.restored != 'true' }}
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32
with:
workspaces: apps/maple-agent -> target
add-rust-environment-hash-key: "true"
key: maple-agent-release-v2-${{ matrix.variant }}-${{ steps.environment.outputs.compatibility }}
cmd-format: python3 "${{ github.workspace }}/scripts/ci/agent_cache_command.py" {0}
- save-if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
+ save-if: "false"
- name: Check whether Cargo targets were restored
id: restored-target
run: |
- if [ -f "$GITHUB_WORKSPACE/apps/maple-agent/target/CACHEDIR.TAG" ]; then
- echo 'present=true' >> "$GITHUB_OUTPUT"
- else
- echo 'present=false' >> "$GITHUB_OUTPUT"
- fi
+ python3 - <<'PYTHON'
+ import os
+ from pathlib import Path
+ target = Path(os.environ["GITHUB_WORKSPACE"]) / "apps/maple-agent/target"
+ present = any(
+ any(fingerprint.iterdir()) and any(
+ output.is_file() and output.stat().st_size > 0
+ for pattern in ("*.rlib", "*.rmeta")
+ for output in (fingerprint.parent / "deps").glob(pattern)
+ )
+ for fingerprint in target.rglob(".fingerprint") if fingerprint.is_dir()
+ )
+ with Path(os.environ["GITHUB_OUTPUT"]).open("a") as output:
+ output.write(f"present={str(present).lower()}\n")
+ PYTHON
+
- name: Restore compatible legacy Cargo targets on a cache miss
- if: ${{ steps.rust-cache.outputs.cache-hit != 'true' && steps.restored-target.outputs.present != 'true' }}
+ if: ${{ steps.dependency-archive.outputs.restored != 'true' && steps.restored-target.outputs.present != 'true' }}
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32
with:
workspaces: apps/maple-agent -> target
add-rust-environment-hash-key: "false"
- key: maple-agent-release-v2-${{ matrix.variant }}-${{ steps.environment.outputs.compatibility }}
+ key: maple-agent-release-v2-${{ matrix.variant }}-${{ steps.environment.outputs.legacy_compatibility }}
cmd-format: python3 "${{ github.workspace }}/scripts/ci/agent_cache_command.py" {0}
save-if: "false"
- name: Record the current release environment after restore
run: |
mkdir -p target/ci-metrics
cp "$RUNNER_TEMP/agent-environment.json" target/ci-metrics/environment.json
+ - name: Verify the canonical SDK and proxy dependency graph
+ run: |
+ metadata="$RUNNER_TEMP/agent-workspace-metadata.json"
+ # shellcheck disable=SC2016 # The child shell expands its own positional argument.
+ nix develop --no-update-lock-file . -c bash -c 'cargo metadata --locked --format-version 1 > "$1"' -- "$metadata"
+ python3 ../../scripts/ci/verify-agent-rust-deps.py "$metadata"
+ nix develop --no-update-lock-file . -c python3 scripts/verify-sqlcipher-build.py "$metadata"
- name: Build optimized Agent with embedded profile
run: nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_measure.py --phase release-build --target target -- ./scripts/build-release.sh "$MAPLE_RELEASE_PROFILE"
- name: Package Linux installed bundle
@@ -366,6 +455,29 @@ jobs:
if-no-files-found: error
retention-days: 30
+ - name: Prepare trusted dependency archive after validation
+ id: dependency-seed
+ if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' }}
+ run: |
+ python3 ../../scripts/ci/agent_dependency_archive.py pack \
+ --environment "$RUNNER_TEMP/agent-environment.json" --metadata "$RUNNER_TEMP/agent-workspace-metadata.json" \
+ --target "target" --job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-desktop-build.yml \
+ --output-dir "$RUNNER_TEMP/agent-dependency-seed" \
+ --receipt "$RUNNER_TEMP/agent-dependency-receipt.json"
+
+ - name: Upload durable dependency archive
+ if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' && steps.dependency-seed.outputs.needs-upload == 'true' }}
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
+ with:
+ name: ${{ steps.dependency-seed.outputs.artifact-name }}
+ path: |
+ ${{ runner.temp }}/agent-dependency-seed/manifest.json
+ ${{ runner.temp }}/agent-dependency-seed/dependencies.tar.gz
+ if-no-files-found: error
+ compression-level: 0
+ overwrite: true
+ retention-days: 3
+
- name: Summarize release phase timings and target size
if: always()
run: python3 ../../scripts/ci/agent_measure.py --summarize target
diff --git a/.github/workflows/opensecret-ci.yml b/.github/workflows/opensecret-ci.yml
index 354cd7123..c40281cfc 100644
--- a/.github/workflows/opensecret-ci.yml
+++ b/.github/workflows/opensecret-ci.yml
@@ -67,13 +67,13 @@ jobs:
save-if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
- name: Check formatting
- run: nix develop --no-update-lock-file '.?submodules=1' -c cargo fmt --all -- --check
+ run: nix develop --no-update-lock-file '.?submodules=1#ci' -c cargo fmt --all -- --check
- name: Lint all backend targets and features
- run: nix develop --no-update-lock-file '.?submodules=1' -c cargo clippy --locked --all-targets --all-features -- -D warnings
+ run: nix develop --no-update-lock-file '.?submodules=1#ci' -c cargo clippy --locked --all-targets --all-features -- -D warnings
- name: Run backend tests
- run: nix develop --no-update-lock-file '.?submodules=1' -c cargo test --locked --all-features
+ run: nix develop --no-update-lock-file '.?submodules=1#ci' -c cargo test --locked --all-features
nix:
name: OpenSecret Nix checks and application package
diff --git a/.github/workflows/sdk-integration.yml b/.github/workflows/sdk-integration.yml
index 184caf606..4c3624ebe 100644
--- a/.github/workflows/sdk-integration.yml
+++ b/.github/workflows/sdk-integration.yml
@@ -81,19 +81,19 @@ jobs:
- name: Migrate the disposable database and build OpenSecret
run: |
- nix develop --no-update-lock-file './services/opensecret?submodules=1' -c bash -lc '
+ nix develop --no-update-lock-file './services/opensecret?submodules=1#ci' -c bash -lc '
set -euo pipefail
cd services/opensecret
diesel migration run
cargo build --locked
'
- nix develop --no-update-lock-file './services/opensecret?submodules=1' -c \
+ nix develop --no-update-lock-file './services/opensecret?submodules=1#ci' -c \
psql "${DATABASE_URL}" -X -v ON_ERROR_STOP=1 \
-f sdk/test/integration/bootstrap.sql
- name: Verify OAuth persistence and encrypted callback flows
run: |
- nix develop --no-update-lock-file './services/opensecret?submodules=1' -c bash -lc '
+ nix develop --no-update-lock-file './services/opensecret?submodules=1#ci' -c bash -lc '
set -euo pipefail
cd services/opensecret
bash ../../.agents/skills/validate-opensecret/scripts/disposable_db_tests.sh
@@ -101,7 +101,7 @@ jobs:
- name: Start and verify OpenSecret
run: |
- nix develop --no-update-lock-file './services/opensecret?submodules=1' -c \
+ nix develop --no-update-lock-file './services/opensecret?submodules=1#ci' -c \
services/opensecret/target/debug/opensecret \
> "${RUNNER_TEMP}/opensecret.log" 2>&1 &
echo "$!" > "${RUNNER_TEMP}/opensecret.pid"
diff --git a/apps/maple-agent/crates/maple-agent/src/agent.rs b/apps/maple-agent/crates/maple-agent/src/agent.rs
index 044e4fb64..97f692efa 100644
--- a/apps/maple-agent/crates/maple-agent/src/agent.rs
+++ b/apps/maple-agent/crates/maple-agent/src/agent.rs
@@ -257,7 +257,9 @@ struct ServiceState {
runtime: tokio::sync::Mutex