Skip to content

chore(deps-dev): bump typescript from 5.9.3 to 6.0.3 in /apps/workbench #2020

chore(deps-dev): bump typescript from 5.9.3 to 6.0.3 in /apps/workbench

chore(deps-dev): bump typescript from 5.9.3 to 6.0.3 in /apps/workbench #2020

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
security-regressions:
name: Fast Security Regressions
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Run security regression contract tests
run: |
cargo test -p hush-cli --test abuse_harness -- --nocapture
cargo test -p hush-cli --test hunt_e2e -- --nocapture --test-threads=1
cargo test -p clawdstrike --test security_regressions -- --nocapture
cargo test -p hushd --test security_regressions -- --nocapture
cargo test -p hush-cli hush_run::tests::connect_proxy_rejects_ip_target_with_allowlisted_sni_mismatch -- --exact
cargo test -p hush-cli hush_run::tests::connect_proxy_hostname_target_is_ip_pinned_after_policy_check -- --exact
policy-torture:
name: Policy Torture (PR Gate)
runs-on: ubuntu-latest
timeout-minutes: 20
needs: security-regressions
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install protoc
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- name: Run policy torture battery
run: bash rulesets/tests/policy-torture/run.sh
- name: Upload policy torture JSON reports
if: always()
uses: actions/upload-artifact@v7
with:
name: policy-torture-reports
path: rulesets/tests/policy-torture/reports/*.json
if-no-files-found: error
check:
name: Check
runs-on: ubuntu-latest
timeout-minutes: 45
needs: [security-regressions, policy-torture]
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Cache cargo build artifacts
uses: Swatinem/rust-cache@v2
with:
shared-key: check
cache-on-failure: true
- name: Install protoc
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- name: Check formatting
run: cargo fmt --all -- --check
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: "24"
- name: Install TypeScript compiler
run: npm install -g typescript@5.9.3
- name: Smoke test TS file dependency workflows
run: bash scripts/smoke-ts-file-deps.sh
- name: Validate Docker Compose topology
run: docker compose -f infra/docker/docker-compose.services.yaml config > /dev/null
- name: Lint moved paths
run: bash scripts/path-lint.sh
- name: Validate moved path targets
run: bash scripts/move-validation.sh
- name: Validate architecture guardrails
run: bash scripts/architecture-guardrails.sh
- name: Slop audit (critical paths)
run: bash scripts/slop-audit.sh
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install mypy
run: python -m pip install --disable-pip-version-check mypy==1.19.1
- name: Validate docs code blocks
run: tools/scripts/validate-docs
- name: Clippy
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Build
run: cargo build --all-targets
- name: Test
run: cargo test --all --exclude sdr-integration-tests
sdk-conformance:
name: SDK Conformance Vectors
runs-on: ubuntu-latest
timeout-minutes: 30
needs: check
steps:
- uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version-file: packages/sdk/hush-go/go.mod
cache: true
cache-dependency-path: packages/sdk/hush-go/go.sum
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: |
packages/sdk/hush-ts/package-lock.json
packages/adapters/clawdstrike-adapter-core/package-lock.json
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Run SDK conformance runner
run: bash scripts/run-sdk-conformance.sh
terminal-tui:
name: Terminal TUI
runs-on: ubuntu-latest
timeout-minutes: 30
needs: check
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Setup Bun
uses: oven-sh/setup-bun@v2
- name: Install dependencies
working-directory: apps/terminal
run: bun install --frozen-lockfile
- name: Type check
working-directory: apps/terminal
run: bun run typecheck
- name: Unit tests
working-directory: apps/terminal
run: bun test
- name: Wrapper smoke tests
run: cargo test -p hush-cli tui::tests -- --nocapture
- name: Installed wrapper smoke
shell: bash
run: |
set -euo pipefail
cargo build -p hush-cli --bin clawdstrike --bin hush
STAGE="$RUNNER_TEMP/clawdstrike-tui-stage"
mkdir -p "$STAGE/bin" "$STAGE/share/clawdstrike/tui"
cp target/debug/clawdstrike "$STAGE/bin/clawdstrike"
cp target/debug/hush "$STAGE/bin/hush"
cp apps/terminal/package.json "$STAGE/share/clawdstrike/tui/"
cp apps/terminal/bun.lockb "$STAGE/share/clawdstrike/tui/"
cp crates/services/hush-cli/assets/tui/cli.js "$STAGE/share/clawdstrike/tui/"
cp -R apps/terminal/src "$STAGE/share/clawdstrike/tui/src"
(
cd "$STAGE/share/clawdstrike/tui"
bun install --production --frozen-lockfile
)
"$STAGE/bin/clawdstrike" tui doctor --json > "$RUNNER_TEMP/tui-doctor.json"
grep '"runtime"' "$RUNNER_TEMP/tui-doctor.json"
tauri-rust-check:
name: Tauri Rust Crates
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v6
- name: Install Linux dependencies for Tauri
run: |
sudo apt-get update
sudo apt-get install -y \
libgtk-3-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
protobuf-compiler
sudo apt-get install -y libwebkit2gtk-4.1-dev || sudo apt-get install -y libwebkit2gtk-4.0-dev
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo build artifacts
uses: Swatinem/rust-cache@v2
with:
shared-key: tauri
cache-on-failure: true
- name: Check desktop Tauri crate
run: cargo check --manifest-path apps/desktop/src-tauri/Cargo.toml
- name: Check agent macOS packaging sources
shell: bash
run: |
set -euo pipefail
required=(
apps/agent/src-tauri/macos/system-extension/entitlements/agent-app.entitlements
apps/agent/src-tauri/macos/system-extension/entitlements/combined-system-extension.entitlements
apps/agent/src-tauri/macos/system-extension/plists/agent-packaging-template.plist
apps/agent/src-tauri/macos/system-extension/plists/combined-system-extension-template.plist
apps/agent/src-tauri/macos/system-extension/profiles/developer-id-profile-template.plist
)
for path in "${required[@]}"; do
[[ -f "$path" ]] || {
echo "missing macOS packaging asset: $path" >&2
exit 1
}
done
CLAWDSTRIKE_VALIDATE_MACOS_PACKAGING=1 cargo check --manifest-path apps/agent/src-tauri/Cargo.toml
macos-system-extension-swift:
name: macOS System Extension Swift Packages
runs-on: macos-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v6
- name: Test Swift coverage checker
run: python3 tools/scripts/check-changed-swift-coverage-test.py
- name: Test EndpointSecurity extension package
shell: bash
run: |
set -euo pipefail
package_path="apps/agent/src-tauri/macos/system-extension/endpoint-security"
swift test --enable-code-coverage --disable-sandbox --package-path "$package_path"
codecov_path="$(swift test --disable-sandbox --show-codecov-path --package-path "$package_path")"
codecov_dir="$(dirname "$codecov_path")"
debug_dir="$(dirname "$codecov_dir")"
test_bundle="$(find "$debug_dir" -maxdepth 1 -type d -name '*Tests.xctest' -print -quit)"
test_binary="$(find "$test_bundle/Contents/MacOS" -maxdepth 1 -type f -perm -111 -print -quit)"
[[ -x "$test_binary" ]] || {
echo "missing EndpointSecurity XCTest binary under $debug_dir" >&2
exit 1
}
status_tool="$debug_dir/endpoint-security-status-tool"
for scenario in healthy-allow deny-decision deadline-miss dropped-events missing-full-disk-access inactive-provider approval-blocked; do
LLVM_PROFILE_FILE="$codecov_dir/status-tool-%p.profraw" "$status_tool" fixture "$scenario" >/dev/null
done
LLVM_PROFILE_FILE="$codecov_dir/status-tool-extra-%p.profraw" "$status_tool" live >/dev/null || true
LLVM_PROFILE_FILE="$codecov_dir/status-tool-extra-%p.profraw" "$status_tool" >/dev/null 2>/dev/null || true
LLVM_PROFILE_FILE="$codecov_dir/status-tool-extra-%p.profraw" "$status_tool" fixture >/dev/null 2>/dev/null || true
LLVM_PROFILE_FILE="$codecov_dir/status-tool-extra-%p.profraw" "$status_tool" fixture unsupported >/dev/null 2>/dev/null || true
LLVM_PROFILE_FILE="$codecov_dir/status-tool-extra-%p.profraw" "$status_tool" observe-auth-open 0 >/dev/null 2>/dev/null || true
LLVM_PROFILE_FILE="$codecov_dir/status-tool-extra-%p.profraw" "$status_tool" observe-auth-open 1 >/dev/null 2>/dev/null || true
xcrun llvm-profdata merge -sparse "$codecov_dir"/*.profraw -o "$codecov_dir/combined.profdata"
xcrun llvm-cov export \
-format=text \
-instr-profile="$codecov_dir/combined.profdata" \
"$test_binary" \
-object="$status_tool" \
> swift-coverage-endpoint-security.json
- name: Test NetworkExtension package
shell: bash
run: |
set -euo pipefail
package_path="apps/agent/src-tauri/macos/system-extension/network-extension"
swift test --enable-code-coverage --disable-sandbox --package-path "$package_path"
codecov_path="$(swift test --disable-sandbox --show-codecov-path --package-path "$package_path")"
codecov_dir="$(dirname "$codecov_path")"
debug_dir="$(dirname "$codecov_dir")"
test_bundle="$(find "$debug_dir" -maxdepth 1 -type d -name '*Tests.xctest' -print -quit)"
test_binary="$(find "$test_bundle/Contents/MacOS" -maxdepth 1 -type f -perm -111 -print -quit)"
[[ -x "$test_binary" ]] || {
echo "missing NetworkExtension XCTest binary under $debug_dir" >&2
exit 1
}
status_tool="$debug_dir/network-extension-status-tool"
for scenario in selection inactive unavailable approval-blocked; do
LLVM_PROFILE_FILE="$codecov_dir/status-tool-%p.profraw" "$status_tool" fixture "$scenario" >/dev/null
done
LLVM_PROFILE_FILE="$codecov_dir/status-tool-extra-%p.profraw" "$status_tool" live >/dev/null || true
LLVM_PROFILE_FILE="$codecov_dir/status-tool-extra-%p.profraw" "$status_tool" >/dev/null 2>/dev/null || true
LLVM_PROFILE_FILE="$codecov_dir/status-tool-extra-%p.profraw" "$status_tool" fixture >/dev/null 2>/dev/null || true
LLVM_PROFILE_FILE="$codecov_dir/status-tool-extra-%p.profraw" "$status_tool" fixture unsupported >/dev/null 2>/dev/null || true
LLVM_PROFILE_FILE="$codecov_dir/status-tool-extra-%p.profraw" "$status_tool" request-reload >/dev/null 2>/dev/null || true
LLVM_PROFILE_FILE="$codecov_dir/status-tool-extra-%p.profraw" "$status_tool" request-reload /tmp/policy.json abc >/dev/null 2>/dev/null || true
xcrun llvm-profdata merge -sparse "$codecov_dir"/*.profraw -o "$codecov_dir/combined.profdata"
xcrun llvm-cov export \
-format=text \
-instr-profile="$codecov_dir/combined.profdata" \
"$test_binary" \
-object="$status_tool" \
> swift-coverage-network-extension.json
- name: Enforce changed-line Swift coverage floor
env:
GITHUB_BASE_REF: ${{ github.base_ref }}
CHANGED_SWIFT_COVERAGE_THRESHOLD: "60"
shell: bash
run: |
set -euo pipefail
if [ -z "${GITHUB_BASE_REF}" ]; then
echo "No base ref (non-PR run); skipping changed-file Swift coverage gate."
exit 0
fi
git fetch --no-tags --depth=1 origin "${GITHUB_BASE_REF}:refs/remotes/origin/${GITHUB_BASE_REF}"
if ! git diff --name-only "origin/${GITHUB_BASE_REF}...HEAD" -- '*.swift' > changed_swift_files.txt; then
echo "Shallow diff failed; fetching additional history for merge-base resolution"
git fetch --no-tags --prune --unshallow origin || git fetch --no-tags --prune origin
git fetch --no-tags origin "${GITHUB_BASE_REF}:refs/remotes/origin/${GITHUB_BASE_REF}"
git diff --name-only "origin/${GITHUB_BASE_REF}...HEAD" -- '*.swift' > changed_swift_files.txt
fi
grep '^apps/agent/src-tauri/macos/system-extension/' changed_swift_files.txt \
| grep '/Sources/' \
> changed_swift_files.coverage.txt || true
if [ ! -s changed_swift_files.coverage.txt ]; then
echo "No system-extension Swift source files changed; skipping changed-file Swift coverage gate."
exit 0
fi
tools/scripts/check-changed-swift-coverage.py \
--coverage-json swift-coverage-endpoint-security.json \
--coverage-json swift-coverage-network-extension.json \
--changed-files-file changed_swift_files.coverage.txt \
--git-diff-range "origin/${GITHUB_BASE_REF}...HEAD" \
--threshold "${CHANGED_SWIFT_COVERAGE_THRESHOLD}"
desktop-frontend:
name: Desktop Frontend
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
- name: Setup Bun
uses: oven-sh/setup-bun@v2
- name: Install dependencies
working-directory: apps/desktop
run: bun install --frozen-lockfile
- name: Type check
working-directory: apps/desktop
run: bun run typecheck
- name: Unit tests
working-directory: apps/desktop
run: bun run test
- name: Build
working-directory: apps/desktop
run: bun run build
workbench-frontend:
name: Workbench Frontend
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: apps/workbench/package-lock.json
- name: Install dependencies
working-directory: apps/workbench
run: npm ci
- name: Type check
working-directory: apps/workbench
run: npm run typecheck
- name: Unit tests
working-directory: apps/workbench
run: npm test -- --run
- name: Build
working-directory: apps/workbench
run: npm run build
control-console:
name: Control Console
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: apps/control-console/package-lock.json
- name: Install dependencies
working-directory: apps/control-console
run: npm ci
- name: Type check
working-directory: apps/control-console
run: npm run typecheck
- name: Unit tests
working-directory: apps/control-console
run: npm test
- name: Build
working-directory: apps/control-console
run: npm run build
sdr-integration-tests:
name: SDR Integration Tests
runs-on: ubuntu-latest
timeout-minutes: 15
needs: check
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo build artifacts
uses: Swatinem/rust-cache@v2
with:
shared-key: sdr
cache-on-failure: true
- name: Install protoc
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- name: Spine pipeline tests
run: cargo test -p hush-spine --test pipeline_test
- name: Bridge roundtrip tests
run: cargo test -p sdr-integration-tests --test bridge_roundtrip_test
- name: K8s audit bridge reliability tests
run: cargo test -p sdr-integration-tests --test k8s_audit_bridge_reliability -- --nocapture
- name: E2E pipeline tests
run: cargo test -p sdr-integration-tests --test e2e_pipeline
adaptive-control-integration:
name: Adaptive Control Integration (NATS + Postgres)
runs-on: ubuntu-latest
timeout-minutes: 25
needs: check
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install protoc
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- name: Run control-api adaptive integration tests
run: 'cargo test -p clawdstrike-control-api integration_tests:: -- --nocapture'
msrv:
name: Minimum Supported Rust Version
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v6
- name: Install MSRV toolchain
uses: dtolnay/rust-toolchain@1.93
- name: Install protoc
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- name: Build with MSRV
run: cargo build --all-targets
offline:
name: Offline Build/Test (vendored)
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
- name: Free disk space
run: |
echo "Disk before cleanup:"
df -h
sudo rm -rf /usr/local/lib/android
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
echo "Disk after cleanup:"
df -h
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install protoc
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- name: Test offline
run: scripts/cargo-offline.sh test --workspace --all-targets
env:
CARGO_NET_OFFLINE: "true"
docs:
name: Documentation
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install protoc
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler z3 libz3-dev clang libclang-dev
- name: Build documentation
run: cargo doc --no-deps --all-features
env:
RUSTDOCFLAGS: -D warnings
security-audit:
name: Security Audit
runs-on: ubuntu-latest
timeout-minutes: 20
needs: security-regressions
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install cargo-audit
run: cargo install cargo-audit --locked --version 0.22.0
- name: Enforce advisory exception policy metadata
run: tools/scripts/check-advisory-expiry.sh
- name: Run security audits
run: |
# Advisory ignores live in .cargo/audit.toml so the rationale, owner,
# and expiry stay close to the data. cargo-audit auto-discovers that
# file when invoked from the workspace root.
cargo audit --deny warnings
# cargo-audit fails vulnerability advisories by default. App lockfiles still
# carry yanked-only core2 transitives through current libp2p and Stronghold
# stacks, so additionally deny unmaintained and unsound advisories while
# upstreams replace it.
cargo audit --deny unmaintained --deny unsound --file apps/desktop/src-tauri/Cargo.lock
cargo audit --deny unmaintained --deny unsound --file apps/workbench/src-tauri/Cargo.lock
cargo audit --deny unmaintained --deny unsound --file apps/agent/src-tauri/Cargo.lock
license-check:
name: License Check
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install cargo-deny
uses: taiki-e/install-action@cargo-deny
- name: Generate desktop Tauri cargo metadata
run: cargo metadata --manifest-path apps/desktop/src-tauri/Cargo.toml --format-version 1 --locked > desktop-tauri-metadata.json
- name: Run cargo-deny for workspace
run: cargo deny check
- name: Run cargo-deny for desktop Tauri crate
run: cargo deny check --metadata-path desktop-tauri-metadata.json licenses sources
coverage:
name: Code Coverage
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: llvm-tools-preview
- name: Install protoc
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- name: Install Z3 toolchain for all-features coverage
run: sudo apt-get update && sudo apt-get install -y z3 libz3-dev clang libclang-dev
- name: Install Linux dependencies for desktop Tauri coverage
run: |
sudo apt-get update
sudo apt-get install -y \
libgtk-3-dev \
libayatana-appindicator3-dev \
librsvg2-dev
sudo apt-get install -y libwebkit2gtk-4.1-dev || sudo apt-get install -y libwebkit2gtk-4.0-dev
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Generate workspace coverage report
run: cargo llvm-cov --all-features --lcov --output-path lcov.workspace.info
- name: Generate agent Tauri coverage report
run: |
# This job is the changed-line coverage gate, not the full agent test
# gate. Export LCOV from runnable agent tests so agent Rust changes
# participate in coverage while platform/local-listener test failures
# remain isolated to their dedicated validation jobs.
cargo llvm-cov \
--manifest-path apps/agent/src-tauri/Cargo.toml \
--ignore-run-fail \
--lcov \
--output-path lcov.agent.info
- name: Generate desktop Tauri coverage report
run: cargo llvm-cov --manifest-path apps/desktop/src-tauri/Cargo.toml --lcov --output-path lcov.desktop.info
- name: Generate workbench Tauri coverage report
run: cargo llvm-cov --manifest-path apps/workbench/src-tauri/Cargo.toml --lcov --output-path lcov.workbench.info
- name: Combine coverage reports
run: |
{
cat lcov.workspace.info
printf '\n'
cat lcov.agent.info
printf '\n'
cat lcov.desktop.info
printf '\n'
cat lcov.workbench.info
printf '\n'
} > lcov.info
- name: Enforce changed-file Rust coverage floor
env:
GITHUB_BASE_REF: ${{ github.base_ref }}
CHANGED_RUST_COVERAGE_THRESHOLD: "70"
run: |
if [ -z "${GITHUB_BASE_REF}" ]; then
echo "No base ref (non-PR run); skipping changed-file Rust coverage gate."
exit 0
fi
git fetch --no-tags --depth=1 origin "${GITHUB_BASE_REF}:refs/remotes/origin/${GITHUB_BASE_REF}"
# In shallow PR checkouts, merge-base may be missing even when origin/<base> exists.
# Retry after fetching full history so three-dot diff stays stable.
if ! git diff --name-only "origin/${GITHUB_BASE_REF}...HEAD" -- '*.rs' ':(exclude)infra/vendor/**' > changed_rust_files.txt; then
echo "Shallow diff failed; fetching additional history for merge-base resolution"
git fetch --no-tags --prune --unshallow origin || git fetch --no-tags --prune origin
git fetch --no-tags origin "${GITHUB_BASE_REF}:refs/remotes/origin/${GITHUB_BASE_REF}"
git diff --name-only "origin/${GITHUB_BASE_REF}...HEAD" -- '*.rs' ':(exclude)infra/vendor/**' > changed_rust_files.txt
fi
if [ ! -s changed_rust_files.txt ]; then
echo "No changed Rust files; skipping changed-file Rust coverage gate."
exit 0
fi
# This job merges coverage from the root Rust workspace plus the
# agent, desktop, and workbench Tauri crates. Keep excluding the long-lived
# desktop Tauri surface from the changed-line floor until it has a
# dedicated changed-line gate instead of inheriting legacy branch
# churn. Also exclude vendored upstream Rust snapshots, standalone
# crates still outside the combined coverage set, the out-of-workspace
# `logos-z3` crate, and test-only Rust sources that do not emit stable
# LCOV entries in this job. The root workspace coverage run installs
# Z3 and uses `--all-features`, so `clawdstrike-logos` solver-gated
# paths remain in scope here.
grep -v '^apps/desktop/src-tauri/' changed_rust_files.txt \
| grep -v '^apps/workbench/src-tauri/' \
| grep -v '^crates/bridges/hush-go-native/' \
| grep -v '^crates/libs/logos-z3/' \
| grep -v '^examples/autonomous-sandbox/' \
| grep -v '^fuzz/' \
| grep -v '^packages/sdk/hush-py/hush-native/' \
| grep -v '^vendor/hushspec/' \
| grep -Ev '(^|/)build\.rs$' \
| grep -Ev '(^|/)tests/|_test\.rs$|_tests\.rs$|integration_tests\.rs$' \
| grep -Ev '^crates/libs/hunt-(correlate|query|scan)/src/(lib|error)\.rs$' \
| grep -Ev '^crates/libs/(hush-core|clawdstrike)/src/lib\.rs$' \
| grep -Ev '^crates/services/hush-cli/src/tests\.rs$' \
> changed_rust_files.coverage.txt || true
if [ ! -s changed_rust_files.coverage.txt ]; then
echo "No workspace Rust files changed; skipping changed-file Rust coverage gate."
exit 0
fi
tools/scripts/check-changed-rust-coverage.py \
--lcov lcov.info \
--changed-files-file changed_rust_files.coverage.txt \
--git-diff-range "origin/${GITHUB_BASE_REF}...HEAD" \
--threshold "${CHANGED_RUST_COVERAGE_THRESHOLD}"
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
files: lcov.info
fail_ci_if_error: false
wasm:
name: WASM Build
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- name: Cache cargo build artifacts
uses: Swatinem/rust-cache@v2
with:
shared-key: wasm
cache-on-failure: true
- name: Install wasm-pack
run: cargo install wasm-pack --locked --version 0.14.0
- name: Build WASM
run: |
cd crates/libs/hush-wasm
wasm-pack build --target web --release
- name: Build Node.js WASM target
run: |
cd crates/libs/hush-wasm
wasm-pack build --target nodejs --release --out-dir pkg-node
for f in hush_wasm.js hush_wasm.d.ts hush_wasm_bg.wasm.d.ts hush_wasm_bg.wasm; do
if [ ! -s "pkg-node/$f" ]; then
echo "ERROR: pkg-node/$f is missing or empty"
exit 1
fi
done
- name: Check bundle size
run: |
SIZE=$(wc -c < crates/libs/hush-wasm/pkg/hush_wasm_bg.wasm)
echo "Bundle size: $SIZE bytes ($(( SIZE / 1024 ))KB)"
# Limit raised from 1.75MiB after adding policy-event + OCSF conversion
# support to hush-wasm (via clawdstrike-policy-event/clawdstrike-ocsf).
# Current baseline ~2.25MiB; 2.5MiB cap leaves ~10% headroom.
if [ $SIZE -gt 2621440 ]; then
echo "ERROR: Bundle exceeds 2.5MiB limit"
exit 1
fi
echo "Bundle size is within 2.5MiB limit"
proptest:
name: Property Tests
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install protoc
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- name: Cache cargo build artifacts
uses: Swatinem/rust-cache@v2
with:
shared-key: proptest
cache-on-failure: true
- name: Run property tests
run: cargo test --workspace proptest
env:
PROPTEST_CASES: 500
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
timeout-minutes: 45
needs: check
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install protoc
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- name: Cache cargo build artifacts
uses: Swatinem/rust-cache@v2
with:
shared-key: integration
cache-on-failure: true
- name: Build daemon
run: cargo build -p hushd
- name: Start daemon in background
run: |
./target/debug/hushd start &
echo $! > /tmp/hushd.pid
env:
RUST_LOG: info
- name: Wait for daemon health
run: |
for i in {1..30}; do
if curl -s http://127.0.0.1:9876/health | grep -q '"status":"healthy"'; then
echo "Daemon is healthy"
exit 0
fi
echo "Waiting for daemon... (attempt $i/30)"
sleep 1
done
echo "Daemon failed to start"
exit 1
- name: Run integration tests
run: cargo test -p hushd --test integration
env:
HUSHD_TEST_URL: http://127.0.0.1:9876
- name: Stop daemon
if: always()
run: |
if [ -f /tmp/hushd.pid ]; then
kill $(cat /tmp/hushd.pid) 2>/dev/null || true
rm /tmp/hushd.pid
fi
fuzz-check:
name: Fuzz Smoke (PR)
runs-on: ubuntu-latest
timeout-minutes: 30
needs: security-regressions
steps:
- uses: actions/checkout@v6
- name: Install Rust stable for cargo-fuzz
uses: dtolnay/rust-toolchain@stable
- name: Install cargo-fuzz
run: cargo +stable install cargo-fuzz --locked --version 0.13.1
- name: Install Rust nightly
uses: dtolnay/rust-toolchain@nightly
- name: Build fuzz targets
run: |
cd fuzz
cargo +nightly build
- name: Run fuzz smoke targets
run: |
cd fuzz
cargo +nightly fuzz run fuzz_policy_parse -- -max_total_time=30
cargo +nightly fuzz run fuzz_irm_net_parse -- -max_total_time=30
cargo +nightly fuzz run fuzz_remote_extends_parse -- -max_total_time=30
typescript-sdk:
name: TypeScript SDK
runs-on: ubuntu-latest
timeout-minutes: 30
defaults:
run:
working-directory: packages/sdk/hush-ts
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: |
packages/sdk/hush-ts/package-lock.json
packages/adapters/clawdstrike-adapter-core/package-lock.json
- name: Bootstrap file deps (adapter-core)
working-directory: packages/adapters/clawdstrike-adapter-core
run: npm ci
- name: Build file deps (adapter-core)
working-directory: packages/adapters/clawdstrike-adapter-core
run: npm run build
- name: Install dependencies
run: npm ci
- name: Type check
run: npm run typecheck
- name: Build
run: npm run build
- name: Test
run: npm test
- name: Verify package exports
run: |
node -e "const sdk = require('./dist/index.cjs'); console.log('CJS exports:', Object.keys(sdk).slice(0, 10))"
node --input-type=module -e "import * as sdk from './dist/index.js'; console.log('ESM exports:', Object.keys(sdk).slice(0, 10))"
agent-fail-closed-smoke:
name: Agent Fail-Closed Smoke
runs-on: ubuntu-latest
timeout-minutes: 30
needs: security-regressions
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: |
packages/adapters/clawdstrike-adapter-core/package-lock.json
packages/adapters/clawdstrike-broker-client/package-lock.json
packages/adapters/clawdstrike-openai/package-lock.json
packages/adapters/clawdstrike-claude/package-lock.json
packages/adapters/clawdstrike-hush-cli-engine/package-lock.json
packages/adapters/clawdstrike-hushd-engine/package-lock.json
- name: Install dependencies
run: |
npm --prefix packages/adapters/clawdstrike-adapter-core ci
npm --prefix packages/adapters/clawdstrike-broker-client ci
npm --prefix packages/adapters/clawdstrike-openai ci
npm --prefix packages/adapters/clawdstrike-claude ci
npm --prefix packages/adapters/clawdstrike-hush-cli-engine ci
npm --prefix packages/adapters/clawdstrike-hushd-engine ci
- name: Build dependencies and adapters
run: |
npm --prefix packages/adapters/clawdstrike-adapter-core run build
npm --prefix packages/adapters/clawdstrike-broker-client run build
npm --prefix packages/adapters/clawdstrike-openai run build
npm --prefix packages/adapters/clawdstrike-claude run build
npm --prefix packages/adapters/clawdstrike-hush-cli-engine run build
npm --prefix packages/adapters/clawdstrike-hushd-engine run build
- name: Run fail-closed smoke POCs
run: node tools/scripts/agent-fail-closed-smoke.mjs
- name: Upload fail-closed smoke report
if: always()
uses: actions/upload-artifact@v7
with:
name: agent-fail-closed-smoke-report
path: |
docs/reports/agent-fail-closed-smoke.json
docs/reports/agent-fail-closed-smoke.md
if-no-files-found: error
adapter-core-cross-adapter:
name: Adapter Core Cross-Adapter
runs-on: ubuntu-latest
timeout-minutes: 30
defaults:
run:
working-directory: packages/adapters/clawdstrike-adapter-core
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: packages/adapters/clawdstrike-adapter-core/package-lock.json
- name: Install dependencies
run: npm ci
- name: Run cross-adapter decision parity suite
run: npm run test:cross-adapter
openclaw-plugin:
name: OpenClaw Plugin
runs-on: ubuntu-latest
timeout-minutes: 30
defaults:
run:
working-directory: packages/adapters/clawdstrike-openclaw
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: packages/adapters/clawdstrike-openclaw/package-lock.json
- name: Bootstrap file deps (adapter-core)
working-directory: packages/adapters/clawdstrike-adapter-core
run: npm ci
- name: Build file deps (adapter-core)
working-directory: packages/adapters/clawdstrike-adapter-core
run: npm run build
- name: Bootstrap file deps (policy)
working-directory: packages/policy/clawdstrike-policy
run: npm ci
- name: Build file deps (policy)
working-directory: packages/policy/clawdstrike-policy
run: npm run build
- name: Install dependencies
run: npm ci
- name: Type check
run: npm run typecheck
- name: Build
run: npm run build
- name: Test
run: npm test
- name: OpenClaw E2E (simulated runtime)
run: npm run e2e
openclaw-plugin-runtime-matrix:
name: OpenClaw Runtime (${{ matrix.openclaw_version }})
runs-on: ubuntu-latest
timeout-minutes: 40
strategy:
fail-fast: false
matrix:
openclaw_version: ['2026.2.15', '2026.2.25', 'latest']
defaults:
run:
working-directory: packages/adapters/clawdstrike-openclaw
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: packages/adapters/clawdstrike-openclaw/package-lock.json
- name: Bootstrap file deps (adapter-core)
working-directory: packages/adapters/clawdstrike-adapter-core
run: npm ci
- name: Build file deps (adapter-core)
working-directory: packages/adapters/clawdstrike-adapter-core
run: npm run build
- name: Bootstrap file deps (policy)
working-directory: packages/policy/clawdstrike-policy
run: npm ci
- name: Build file deps (policy)
working-directory: packages/policy/clawdstrike-policy
run: npm run build
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Install OpenClaw CLI (${{ matrix.openclaw_version }})
working-directory: .
run: npm install -g openclaw@${{ matrix.openclaw_version }}
- name: Prepare OpenClaw runtime artifact directories
working-directory: .
run: |
mkdir -p \
artifacts/openclaw-install-link-smoke-${{ matrix.openclaw_version }} \
artifacts/openclaw-runtime-smoke-${{ matrix.openclaw_version }} \
artifacts/openclaw-runtime-blocked-e2e-${{ matrix.openclaw_version }}
touch \
artifacts/openclaw-install-link-smoke-${{ matrix.openclaw_version }}/.keep \
artifacts/openclaw-runtime-smoke-${{ matrix.openclaw_version }}/.keep \
artifacts/openclaw-runtime-blocked-e2e-${{ matrix.openclaw_version }}/.keep
- name: OpenClaw install-link smoke
working-directory: .
run: OPENCLAW_RUNTIME_ARTIFACT_DIR=artifacts/openclaw-install-link-smoke-${{ matrix.openclaw_version }} bash scripts/openclaw-plugin-install-link-smoke.sh
- name: Assert install-link summary checks
working-directory: .
run: |
jq -e '
.result == "pass"
and .checks.installCommandSucceeded
and .checks.enableCommandSucceeded
and .checks.pluginInfoJsonPresent
and .checks.pluginIdMatches
and .checks.pluginStatusLoaded
and .checks.configLoadPathContainsLink
and .checks.configEntryEnabled
and .checks.allExpectedHooksPresent
and (.checks.idMismatchWarningPresent | not)
' artifacts/openclaw-install-link-smoke-${{ matrix.openclaw_version }}/summary.json >/dev/null
- name: OpenClaw runtime smoke (hook registration)
working-directory: .
run: OPENCLAW_RUNTIME_ARTIFACT_DIR=artifacts/openclaw-runtime-smoke-${{ matrix.openclaw_version }} bash scripts/openclaw-plugin-runtime-smoke.sh
- name: Assert runtime smoke summary checks
working-directory: .
run: |
jq -e '
.result == "pass"
and .checks.pluginInfoJsonPresent
and .checks.pluginIdMatches
and .checks.pluginStatusLoaded
and .checks.allExpectedHooksPresent
and (.checks.idMismatchWarningPresent | not)
' artifacts/openclaw-runtime-smoke-${{ matrix.openclaw_version }}/summary.json >/dev/null
- name: OpenClaw blocked-call runtime e2e
working-directory: .
run: OPENCLAW_RUNTIME_ARTIFACT_DIR=artifacts/openclaw-runtime-blocked-e2e-${{ matrix.openclaw_version }} bash scripts/openclaw-plugin-blocked-call-e2e.sh
- name: Assert blocked-call summary checks
working-directory: .
run: |
jq -e '
.result == "pass"
and .checks.gatewayHealthOk
and .checks.pluginInfoJsonPresent
and .checks.pluginStatusLoaded
and .checks.hookPreflightPresent
and .checks.hookCuaPresent
and .checks.chatSendStarted
and .checks.historyHasMessages
and (
.checks.assistantBlockSignal
or .checks.assistantAuthMissing
)
and .checks.policyCheckHttpOk
and .checks.policyCheckDenied
and .checks.targetFileAbsent
' artifacts/openclaw-runtime-blocked-e2e-${{ matrix.openclaw_version }}/summary.json >/dev/null
- name: Upload OpenClaw runtime artifacts (${{ matrix.openclaw_version }})
if: always()
uses: actions/upload-artifact@v7
with:
name: openclaw-runtime-artifacts-${{ matrix.openclaw_version }}
path: |
artifacts/openclaw-install-link-smoke-${{ matrix.openclaw_version }}
artifacts/openclaw-runtime-smoke-${{ matrix.openclaw_version }}
artifacts/openclaw-runtime-blocked-e2e-${{ matrix.openclaw_version }}
if-no-files-found: warn
clawdstrike-policy:
name: Canonical Policy Engine (TS)
runs-on: ubuntu-latest
timeout-minutes: 30
defaults:
run:
working-directory: packages/policy/clawdstrike-policy
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo build artifacts
uses: Swatinem/rust-cache@v2
with:
shared-key: policy-parity
cache-on-failure: true
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: |
packages/policy/clawdstrike-policy/package-lock.json
packages/adapters/clawdstrike-adapter-core/package-lock.json
packages/sdk/hush-ts/package-lock.json
- name: Bootstrap file deps (adapter-core)
working-directory: packages/adapters/clawdstrike-adapter-core
run: npm ci
- name: Build file deps (adapter-core)
working-directory: packages/adapters/clawdstrike-adapter-core
run: npm run build
- name: Build hush CLI (for policy parity)
working-directory: .
run: cargo build -p hush-cli --bin hush
- name: Install dependencies
run: npm ci
- name: Type check
run: npm run typecheck
- name: Build
run: npm run build
- name: Bootstrap file deps (hush-ts)
working-directory: packages/sdk/hush-ts
run: npm ci
- name: Build file deps (hush-ts)
working-directory: packages/sdk/hush-ts
run: npm run build
- name: Policy parity (Rust ↔ TS)
working-directory: .
run: node tools/scripts/policy-parity.mjs
- name: Test
run: npm test
agent-framework-integrations:
name: Agent Framework Integrations (${{ matrix.name }})
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- name: Adapter Core
dir: packages/adapters/clawdstrike-adapter-core
bootstrap_adapter_core: false
bootstrap_sdk: false
- name: Hush CLI Engine
dir: packages/adapters/clawdstrike-hush-cli-engine
bootstrap_adapter_core: true
bootstrap_sdk: false
bootstrap_hush: true
- name: OpenAI
dir: packages/adapters/clawdstrike-openai
bootstrap_adapter_core: true
bootstrap_broker_client: true
bootstrap_sdk: false
bootstrap_hush: false
- name: OpenCode
dir: packages/adapters/clawdstrike-opencode
bootstrap_adapter_core: true
bootstrap_broker_client: false
bootstrap_sdk: false
bootstrap_hush: false
- name: Claude
dir: packages/adapters/clawdstrike-claude
bootstrap_adapter_core: true
bootstrap_broker_client: false
bootstrap_sdk: false
bootstrap_hush: false
- name: Vercel AI
dir: packages/adapters/clawdstrike-vercel-ai
bootstrap_adapter_core: true
bootstrap_broker_client: false
bootstrap_sdk: true
bootstrap_hush: false
- name: LangChain
dir: packages/adapters/clawdstrike-langchain
bootstrap_adapter_core: true
bootstrap_broker_client: false
bootstrap_sdk: false
bootstrap_hush: false
- name: Hushd Engine
dir: packages/adapters/clawdstrike-hushd-engine
bootstrap_adapter_core: true
bootstrap_broker_client: false
bootstrap_sdk: false
bootstrap_hush: false
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: |
${{ matrix.dir }}/package-lock.json
packages/adapters/clawdstrike-adapter-core/package-lock.json
packages/adapters/clawdstrike-broker-client/package-lock.json
packages/sdk/hush-ts/package-lock.json
- name: Install Rust toolchain
if: ${{ matrix.bootstrap_hush }}
uses: dtolnay/rust-toolchain@stable
- name: Build hush binary
if: ${{ matrix.bootstrap_hush }}
run: cargo build -p hush-cli
- name: Export HUSH_PATH for e2e tests
if: ${{ matrix.bootstrap_hush }}
run: echo "HUSH_PATH=${{ github.workspace }}/target/debug/hush" >> "$GITHUB_ENV"
- name: Bootstrap file deps (adapter-core)
if: ${{ matrix.bootstrap_adapter_core }}
working-directory: packages/adapters/clawdstrike-adapter-core
run: npm ci
- name: Build file deps (adapter-core)
if: ${{ matrix.bootstrap_adapter_core }}
working-directory: packages/adapters/clawdstrike-adapter-core
run: npm run build
- name: Bootstrap file deps (broker-client)
if: ${{ matrix.bootstrap_broker_client }}
working-directory: packages/adapters/clawdstrike-broker-client
run: npm ci
- name: Build file deps (broker-client)
if: ${{ matrix.bootstrap_broker_client }}
working-directory: packages/adapters/clawdstrike-broker-client
run: npm run build
- name: Bootstrap file deps (sdk)
if: ${{ matrix.bootstrap_sdk }}
working-directory: packages/sdk/hush-ts
run: npm ci
- name: Build file deps (sdk)
if: ${{ matrix.bootstrap_sdk }}
working-directory: packages/sdk/hush-ts
run: npm run build
- name: Install dependencies
working-directory: ${{ matrix.dir }}
run: npm ci
- name: Type check
working-directory: ${{ matrix.dir }}
run: npm run typecheck
- name: Build
working-directory: ${{ matrix.dir }}
run: npm run build
- name: Test
working-directory: ${{ matrix.dir }}
run: npm test
parity-tests:
name: Cross-Language Parity
runs-on: ubuntu-latest
timeout-minutes: 30
needs: check
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo build artifacts
uses: Swatinem/rust-cache@v2
with:
shared-key: parity
cache-on-failure: true
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
- name: Bootstrap file deps (adapter-core)
working-directory: packages/adapters/clawdstrike-adapter-core
run: npm ci
- name: Build file deps (adapter-core)
working-directory: packages/adapters/clawdstrike-adapter-core
run: npm run build
- name: Install TS parity deps
working-directory: packages/adapters/clawdstrike-hush-cli-engine
run: npm ci
- name: Run parity tests
run: scripts/run-parity.sh
python-sdk:
name: Python SDK
runs-on: ubuntu-latest
timeout-minutes: 30
defaults:
run:
working-directory: packages/sdk/hush-py
steps:
- uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: '3.12'
cache: 'pip'
cache-dependency-path: packages/sdk/hush-py/pyproject.toml
- name: Install package (editable)
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
python -m pip install "jsonschema>=4,<5"
- name: Run CUA roadmap fixture harnesses
working-directory: ${{ github.workspace }}
run: |
python docs/roadmaps/cua/research/verify_cua_migration_fixtures.py
python docs/roadmaps/cua/research/verify_remote_desktop_policy_matrix.py
python docs/roadmaps/cua/research/verify_remote_desktop_ruleset_alignment.py
python docs/roadmaps/cua/research/verify_injection_capabilities.py
python docs/roadmaps/cua/research/verify_policy_event_mapping.py
python docs/roadmaps/cua/research/verify_postcondition_probes.py
python docs/roadmaps/cua/research/verify_remote_session_continuity.py
python docs/roadmaps/cua/research/verify_envelope_semantic_equivalence.py
python docs/roadmaps/cua/research/verify_repeatable_latency_harness.py
python docs/roadmaps/cua/research/verify_verification_bundle.py
python docs/roadmaps/cua/research/verify_browser_action_policy.py
python docs/roadmaps/cua/research/verify_session_recording_evidence.py
python docs/roadmaps/cua/research/verify_orchestration_isolation.py
python docs/roadmaps/cua/research/verify_cua_policy_evaluation.py
python docs/roadmaps/cua/research/verify_canonical_adapter_contract.py
python docs/roadmaps/cua/research/verify_provider_conformance.py
python docs/roadmaps/cua/research/verify_openclaw_cua_bridge.py
python docs/roadmaps/cua/research/verify_trycua_connector.py
- name: Run tests
run: python -m pytest
python-native-wheel-smoke:
name: Python Native Wheel Smoke (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Sync Python sources for native wheel build
shell: bash
run: scripts/sync-hush-py-native-sources.sh
- name: Build native wheel
shell: bash
run: |
set -euo pipefail
python -m pip install --upgrade pip
python -m pip install maturin
cd packages/sdk/hush-py/hush-native
maturin build --release --interpreter python --out dist-native
ls -la dist-native
- name: Install native wheel artifact
shell: bash
run: |
set -euo pipefail
python -m pip install --force-reinstall packages/sdk/hush-py/hush-native/dist-native/clawdstrike-*.whl
- name: Verify bundled native backend is active
shell: bash
run: |
set -euo pipefail
python - <<'PY'
import clawdstrike
from clawdstrike import NATIVE_AVAILABLE
assert NATIVE_AVAILABLE is True, "Expected bundled native backend to be available"
import clawdstrike._native as native_mod
assert hasattr(native_mod, "NativeEngine")
engine = native_mod.NativeEngine.from_ruleset("strict")
report = engine.check_file_access("/etc/shadow")
assert report["overall"]["allowed"] is False
print("Native smoke check passed")
PY
- name: Verify explicit pure-Python fallback toggle
shell: bash
run: |
set -euo pipefail
CLAWDSTRIKE_DISABLE_NATIVE=1 python - <<'PY'
from clawdstrike import NATIVE_AVAILABLE, init_native
assert NATIVE_AVAILABLE is False
assert init_native() is False
print("Fallback toggle smoke check passed")
PY
macos-provider-dogfood-contract:
name: macOS Provider Dogfood Contract
runs-on: macos-latest
timeout-minutes: 20
permissions:
contents: read
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Run macOS provider dogfood contract self-test
run: bash scripts/test-macos-provider-dogfood-contract.sh