test(spec): INV-cass-26 cass health --json latency contract #2291
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # .github/workflows/ci.yml | |
| # Continuous Integration: lint, test, audit, and build verification | |
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| env: | |
| CARGO_TERM_COLOR: always | |
| CARGO_INCREMENTAL: 0 | |
| CARGO_PROFILE_DEV_DEBUG: 0 | |
| CARGO_PROFILE_TEST_DEBUG: 0 | |
| RUST_BACKTRACE: 1 | |
| RUST_LOG: debug | |
| jobs: | |
| # No-mock policy audit | |
| no-mock-audit: | |
| name: No-Mock Policy Audit | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Run repository artifact hygiene check | |
| run: ./scripts/validate_ci.sh --artifact-hygiene-only | |
| - name: Install ripgrep | |
| run: sudo apt-get update && sudo apt-get install -y ripgrep | |
| - name: Run no-mock audit | |
| id: audit | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p test-results | |
| ALLOWLIST_FILE="tests/policies/no_mock_allowlist.json" | |
| AUDIT_REPORT="test-results/no_mock_ci_audit.md" | |
| echo "# No-Mock CI Audit" > "$AUDIT_REPORT" | |
| echo "" >> "$AUDIT_REPORT" | |
| echo "**Run:** $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$AUDIT_REPORT" | |
| echo "**Commit:** ${{ github.sha }}" >> "$AUDIT_REPORT" | |
| echo "" >> "$AUDIT_REPORT" | |
| # Search for mock/fake/stub patterns | |
| VIOLATIONS=$(mktemp) | |
| rg -n "(Mock[A-Z][a-z]|Fake[A-Z][a-z]|Stub[A-Z][a-z]|mock_|fake_|stub_)" \ | |
| --glob '!**/node_modules/**' \ | |
| --glob '!target/**' \ | |
| --glob '!.git/**' \ | |
| --glob '!tests/fixtures/**' \ | |
| --glob '!test-results/**' \ | |
| --glob '!*.md' \ | |
| --glob '!*.json' \ | |
| src/ tests/ 2>/dev/null > "$VIOLATIONS" || true | |
| VIOLATION_COUNT=$(wc -l < "$VIOLATIONS" | tr -d ' ') | |
| if [ "$VIOLATION_COUNT" -eq 0 ]; then | |
| echo "## Status: ✅ PASS" >> "$AUDIT_REPORT" | |
| echo "" >> "$AUDIT_REPORT" | |
| echo "No mock/fake/stub patterns found." >> "$AUDIT_REPORT" | |
| echo "status=pass" >> "$GITHUB_OUTPUT" | |
| rm -f "$VIOLATIONS" | |
| exit 0 | |
| fi | |
| echo "Found $VIOLATION_COUNT pattern(s), checking allowlist..." | |
| if [ ! -f "$ALLOWLIST_FILE" ]; then | |
| echo "## Status: ❌ FAIL" >> "$AUDIT_REPORT" | |
| echo "" >> "$AUDIT_REPORT" | |
| echo "Allowlist file not found: $ALLOWLIST_FILE" >> "$AUDIT_REPORT" | |
| echo "status=fail" >> "$GITHUB_OUTPUT" | |
| rm -f "$VIOLATIONS" | |
| exit 1 | |
| fi | |
| ALLOWLIST_ENTRIES=$(jq -r '.entries[] | "\(.path):\(.pattern)"' "$ALLOWLIST_FILE" 2>/dev/null || echo "") | |
| UNALLOWED_COUNT=0 | |
| UNALLOWED_LIST="" | |
| while IFS= read -r line; do | |
| FILE=$(echo "$line" | cut -d: -f1) | |
| PATTERN=$(echo "$line" | grep -oiE "(Mock[A-Z][a-zA-Z]*|Fake[A-Z][a-zA-Z]*|Stub[A-Z][a-zA-Z]*|mock_[a-z_]+|fake_[a-z_]+|stub_[a-z_]+)" | head -1) | |
| ALLOWED=false | |
| for entry in $ALLOWLIST_ENTRIES; do | |
| ENTRY_PATH=$(echo "$entry" | cut -d: -f1) | |
| ENTRY_PATTERN=$(echo "$entry" | cut -d: -f2) | |
| if [[ "$FILE" == *"$ENTRY_PATH"* ]] && [[ "$PATTERN" == *"$ENTRY_PATTERN"* || "$ENTRY_PATTERN" == *"$PATTERN"* ]]; then | |
| ALLOWED=true | |
| break | |
| fi | |
| done | |
| if [ "$ALLOWED" = false ]; then | |
| UNALLOWED_COUNT=$((UNALLOWED_COUNT + 1)) | |
| UNALLOWED_LIST="${UNALLOWED_LIST}\n- \`${line}\`" | |
| fi | |
| done < "$VIOLATIONS" | |
| rm -f "$VIOLATIONS" | |
| if [ "$UNALLOWED_COUNT" -gt 0 ]; then | |
| echo "## Status: ❌ FAIL" >> "$AUDIT_REPORT" | |
| echo "" >> "$AUDIT_REPORT" | |
| echo "**Unapproved patterns:** $UNALLOWED_COUNT" >> "$AUDIT_REPORT" | |
| echo "" >> "$AUDIT_REPORT" | |
| echo "### Violations" >> "$AUDIT_REPORT" | |
| echo -e "$UNALLOWED_LIST" >> "$AUDIT_REPORT" | |
| echo "" >> "$AUDIT_REPORT" | |
| echo "### How to Fix" >> "$AUDIT_REPORT" | |
| echo "" >> "$AUDIT_REPORT" | |
| echo "1. Replace mock/fake/stub with real fixtures (preferred)" >> "$AUDIT_REPORT" | |
| echo "2. OR add to \`tests/policies/no_mock_allowlist.json\` with:" >> "$AUDIT_REPORT" | |
| echo " - \`rationale\`: Why this exception is necessary" >> "$AUDIT_REPORT" | |
| echo " - \`review_date\`: 6-month review date" >> "$AUDIT_REPORT" | |
| echo " - \`permanent: true\` only for true platform boundaries" >> "$AUDIT_REPORT" | |
| echo "" >> "$AUDIT_REPORT" | |
| echo "See TESTING.md 'No-Mock Policy' for details." >> "$AUDIT_REPORT" | |
| echo "status=fail" >> "$GITHUB_OUTPUT" | |
| exit 1 | |
| fi | |
| echo "## Status: ✅ PASS" >> "$AUDIT_REPORT" | |
| echo "" >> "$AUDIT_REPORT" | |
| echo "**Total patterns found:** $VIOLATION_COUNT" >> "$AUDIT_REPORT" | |
| echo "**All patterns allowlisted:** Yes" >> "$AUDIT_REPORT" | |
| echo "" >> "$AUDIT_REPORT" | |
| echo "### Allowlist Summary" >> "$AUDIT_REPORT" | |
| echo "" >> "$AUDIT_REPORT" | |
| jq -r '.entries[] | "- `\(.path)`: \(.pattern)` — \(.rationale)"' "$ALLOWLIST_FILE" >> "$AUDIT_REPORT" | |
| echo "status=pass" >> "$GITHUB_OUTPUT" | |
| - name: Upload audit report | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| if: always() | |
| with: | |
| name: no-mock-audit | |
| path: test-results/no_mock_ci_audit.md | |
| retention-days: 30 | |
| # Rust linting and formatting | |
| lint: | |
| name: Lint | |
| needs: [no-mock-audit] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Clone sibling dependencies | |
| shell: bash | |
| run: | | |
| git config --global core.longpaths true | |
| git clone --depth 1 https://github.com/Dicklesworthstone/asupersync.git ../asupersync | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensqlite.git ../frankensqlite | |
| git clone --depth 1 https://github.com/Dicklesworthstone/franken_agent_detection.git ../franken_agent_detection | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensearch.git ../frankensearch | |
| - name: Install Rust nightly | |
| uses: dtolnay/rust-toolchain@881ba7bf39a41cda34ac9e123fb41b44ed08232f # nightly | |
| with: | |
| components: rustfmt, clippy | |
| - name: Cache cargo | |
| uses: Swatinem/rust-cache@ad397744b0d591a723ab90405b7247fac0e6b8db # v2 | |
| - name: Check formatting | |
| run: cargo fmt --all -- --check | |
| - name: Run clippy | |
| # Explicit feature list excludes `strict-path-dep-validation`, which is | |
| # intentionally opt-in and requires specific sibling-repo git revisions | |
| # (see build.rs CONTRACTS). `--all-features` would activate it and make | |
| # the build fail because CI clones siblings at HEAD, not pinned revs. | |
| run: cargo clippy --all-targets --features "qr encryption backtrace" -- -D warnings | |
| # UBS pre-merge gate per coding_agent_session_search-dpfvr. | |
| # Runs `ubs --format=json --ci <changed-files>` against the diff for the PR | |
| # (or push range on main). UBS scans whole files, so this gate compares the | |
| # current file findings to the exact base versions and fails on regressions. | |
| ubs-changed-files: | |
| name: UBS (Ultimate Bug Scanner) Pre-Merge Gate | |
| needs: [no-mock-audit] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout (full history for diff) | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| # `origin/main` must be reachable for `git diff origin/main...HEAD`. | |
| fetch-depth: 0 | |
| - name: Compute changed files | |
| id: changed | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| # PR runs: diff against the merge-base with the target branch. | |
| # Push runs: diff against main's previous tip. | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| base="$(git merge-base "origin/${{ github.base_ref }}" HEAD)" | |
| range="$base...HEAD" | |
| elif [ "${{ github.event_name }}" = "push" ] && [ "${{ github.ref }}" = "refs/heads/main" ]; then | |
| base="${{ github.event.before }}" | |
| range="$base...HEAD" | |
| else | |
| # workflow_dispatch / other — diff against origin/main. | |
| base="$(git merge-base origin/main HEAD)" | |
| range="$base...HEAD" | |
| fi | |
| echo "Diff range: $range" | |
| echo "base=$base" >> "$GITHUB_OUTPUT" | |
| # Filter to the file extensions UBS supports. Skip deleted files | |
| # because UBS needs paths that exist in the checkout. | |
| mapfile -t files < <(git diff --name-only --diff-filter=ACMR "$range" -- \ | |
| '*.rs' '*.toml' '*.ts' '*.tsx' '*.js' '*.jsx' '*.py' '*.sh' '*.yml' '*.yaml' '*.md' \ | |
| 2>/dev/null | grep -v -E '^test-results/|^target/|^node_modules/' || true) | |
| if [ "${#files[@]}" -eq 0 ]; then | |
| echo "No UBS-relevant files changed; skipping gate." | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| # Persist the existing changed files for the next step. | |
| printf '%s\n' "${files[@]}" > /tmp/ubs-changed-files.txt | |
| echo "files_count=${#files[@]}" >> "$GITHUB_OUTPUT" | |
| echo "Files (first 20):" | |
| head -20 /tmp/ubs-changed-files.txt | |
| - name: Install UBS | |
| if: steps.changed.outputs.skip != 'true' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| # UBS is distributed via the upstream install.sh script. The script | |
| # fetches the actual scanner from the project's main branch — its | |
| # internal VERSION variable is cosmetic. We pin the *installer ref* | |
| # via .github/workflows/ubs-version.txt so CI runs against a known | |
| # installer commit/tag. A value of "latest" (or an empty file) maps | |
| # to the main branch. | |
| UBS_REF="" | |
| if [ -f .github/workflows/ubs-version.txt ]; then | |
| UBS_REF="$(tr -d '[:space:]' < .github/workflows/ubs-version.txt)" | |
| fi | |
| case "$UBS_REF" in | |
| ""|"latest") UBS_REF="main" ;; | |
| esac | |
| echo "Installing UBS using installer from ref: $UBS_REF" | |
| if command -v ubs >/dev/null 2>&1; then | |
| echo "ubs already on PATH: $(command -v ubs)" | |
| ubs --version || true | |
| else | |
| curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_scanner/${UBS_REF}/install.sh" | bash | |
| # install.sh drops the binary in $HOME/.local/bin by default, | |
| # which is not on PATH on GitHub-hosted runners by default. | |
| echo "$HOME/.local/bin" >> "$GITHUB_PATH" | |
| export PATH="$HOME/.local/bin:$PATH" | |
| ubs --version || true | |
| fi | |
| - name: Run UBS on changed files | |
| if: steps.changed.outputs.skip != 'true' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p test-results | |
| # Pass the changed-file list to UBS in JSON mode. UBS scans complete | |
| # files, and this repo has historical file-wide findings in several | |
| # large Rust files, so the blocking gate compares the current scan | |
| # against the exact base versions from this PR/push range and fails | |
| # only when critical/warning counts increase. | |
| xargs -a /tmp/ubs-changed-files.txt \ | |
| ubs --format=json --ci \ | |
| > test-results/ubs-report.json 2> test-results/ubs-stderr.log || true | |
| base="${{ steps.changed.outputs.base }}" | |
| base_dir="$(mktemp -d)" | |
| : > /tmp/ubs-base-files.txt | |
| while IFS= read -r file; do | |
| if git cat-file -e "$base:$file" 2>/dev/null; then | |
| mkdir -p "$base_dir/$(dirname "$file")" | |
| git show "$base:$file" > "$base_dir/$file" | |
| printf '%s\n' "$file" >> /tmp/ubs-base-files.txt | |
| fi | |
| done < /tmp/ubs-changed-files.txt | |
| if [ -s /tmp/ubs-base-files.txt ]; then | |
| ( | |
| cd "$base_dir" | |
| xargs -a /tmp/ubs-base-files.txt \ | |
| ubs --format=json --ci \ | |
| > "$GITHUB_WORKSPACE/test-results/ubs-baseline.json" \ | |
| 2> "$GITHUB_WORKSPACE/test-results/ubs-baseline-stderr.log" || true | |
| ) | |
| else | |
| printf '{"totals":{"critical":0,"warning":0,"info":0,"files":0}}\n' \ | |
| > test-results/ubs-baseline.json | |
| : > test-results/ubs-baseline-stderr.log | |
| fi | |
| current_critical="$(jq -r '.totals.critical // 0' test-results/ubs-report.json)" | |
| current_warning="$(jq -r '.totals.warning // 0' test-results/ubs-report.json)" | |
| base_critical="$(jq -r '.totals.critical // 0' test-results/ubs-baseline.json)" | |
| base_warning="$(jq -r '.totals.warning // 0' test-results/ubs-baseline.json)" | |
| echo "UBS current: critical=$current_critical warning=$current_warning" | |
| echo "UBS baseline: critical=$base_critical warning=$base_warning" | |
| if [ "$current_critical" -gt "$base_critical" ] || \ | |
| [ "$current_warning" -gt "$base_warning" ]; then | |
| echo "UBS regression: critical/warning findings increased on changed files." | |
| echo "::group::UBS stderr (last 50 lines)" | |
| tail -50 test-results/ubs-stderr.log || true | |
| echo "::endgroup::" | |
| echo "::group::UBS current summary" | |
| jq -c '.' test-results/ubs-report.json | head -100 | |
| echo "::endgroup::" | |
| echo "::group::UBS baseline summary" | |
| jq -c '.' test-results/ubs-baseline.json | head -100 | |
| echo "::endgroup::" | |
| exit 1 | |
| fi | |
| echo "UBS PASS — no new critical/warning findings on changed files." | |
| - name: Upload UBS report | |
| if: always() && steps.changed.outputs.skip != 'true' | |
| uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4 | |
| with: | |
| name: ubs-report-${{ github.run_id }} | |
| path: | | |
| test-results/ubs-report.json | |
| test-results/ubs-stderr.log | |
| test-results/ubs-baseline.json | |
| test-results/ubs-baseline-stderr.log | |
| if-no-files-found: ignore | |
| retention-days: 7 | |
| # Rust unit tests | |
| test-rust: | |
| name: Rust Tests (${{ 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@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Clone sibling dependencies | |
| shell: bash | |
| run: | | |
| git config --global core.longpaths true | |
| git clone --depth 1 https://github.com/Dicklesworthstone/asupersync.git ../asupersync | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensqlite.git ../frankensqlite | |
| git clone --depth 1 https://github.com/Dicklesworthstone/franken_agent_detection.git ../franken_agent_detection | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensearch.git ../frankensearch | |
| - name: Install Rust nightly | |
| uses: dtolnay/rust-toolchain@881ba7bf39a41cda34ac9e123fb41b44ed08232f # nightly | |
| - name: Cache cargo | |
| uses: Swatinem/rust-cache@ad397744b0d591a723ab90405b7247fac0e6b8db # v2 | |
| - name: Configure Windows MSVC CRT for cc-rs | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| # esaxx-rs requests /MT via cc-rs, while ONNX Runtime ships /MD objects. | |
| # Disable cc-rs defaults and choose /MD explicitly so all linked objects agree. | |
| "CRATE_CC_NO_DEFAULTS=1" >> $env:GITHUB_ENV | |
| "CFLAGS=/MD" >> $env:GITHUB_ENV | |
| "CXXFLAGS=/MD" >> $env:GITHUB_ENV | |
| - name: Run tests | |
| # Explicit feature list excludes `strict-path-dep-validation` — see the | |
| # Lint job for rationale. | |
| run: cargo test --features "qr encryption backtrace" --verbose -- --nocapture | |
| env: | |
| RUST_LOG: debug | |
| - name: Run doc tests | |
| run: cargo test --doc | |
| - name: Run Rust E2E tests with JSONL logging | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mapfile -t tests < <(git ls-files 'tests/e2e_*.rs' | sed 's#^tests/##; s#\\.rs$##') | |
| if [[ "${#tests[@]}" -eq 0 ]]; then | |
| echo "No e2e_* tests found; skipping." | |
| exit 0 | |
| fi | |
| args=() | |
| for t in "${tests[@]}"; do | |
| args+=(--test "$t") | |
| done | |
| # Explicit feature list excludes `strict-path-dep-validation` — see | |
| # the Lint job for rationale. | |
| E2E_LOG=1 cargo test --features "qr encryption backtrace" --verbose "${args[@]}" -- --nocapture | |
| - name: Validate E2E JSONL logs | |
| if: always() | |
| shell: bash | |
| run: | | |
| if [[ -d "test-results/e2e" ]] && ls test-results/e2e/*.jsonl 1>/dev/null 2>&1; then | |
| ./scripts/validate-e2e-jsonl.sh test-results/e2e/*.jsonl | |
| else | |
| echo "No E2E JSONL logs found to validate" | |
| fi | |
| - name: Upload E2E JSONL logs | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| if: always() | |
| with: | |
| name: e2e-jsonl-${{ matrix.os }} | |
| path: test-results/e2e/*.jsonl | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| ssh-sync-docker: | |
| name: SSH Sync Docker Tests | |
| needs: [no-mock-audit] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Clone sibling dependencies | |
| shell: bash | |
| run: | | |
| git clone --depth 1 https://github.com/Dicklesworthstone/asupersync.git ../asupersync | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensqlite.git ../frankensqlite | |
| git clone --depth 1 https://github.com/Dicklesworthstone/franken_agent_detection.git ../franken_agent_detection | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensearch.git ../frankensearch | |
| - name: Install SSH sync tools | |
| shell: bash | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y openssh-client rsync | |
| docker version | |
| docker info | |
| - name: Install Rust nightly | |
| uses: dtolnay/rust-toolchain@881ba7bf39a41cda34ac9e123fb41b44ed08232f # nightly | |
| - name: Cache cargo | |
| uses: Swatinem/rust-cache@ad397744b0d591a723ab90405b7247fac0e6b8db # v2 | |
| - name: Run SSH sync integration tests | |
| run: cargo test --features "qr encryption backtrace" --test ssh_sync_integration -- --ignored --test-threads=1 --nocapture | |
| env: | |
| RUST_LOG: debug | |
| - name: Run SSH sources E2E tests | |
| run: E2E_LOG=1 cargo test --features "qr encryption backtrace" --test e2e_ssh_sources -- --ignored --test-threads=1 --nocapture | |
| env: | |
| RUST_LOG: debug | |
| - name: Validate SSH E2E JSONL logs | |
| if: always() | |
| shell: bash | |
| run: | | |
| if [[ -d "test-results/e2e" ]] && ls test-results/e2e/*.jsonl 1>/dev/null 2>&1; then | |
| ./scripts/validate-e2e-jsonl.sh test-results/e2e/*.jsonl | |
| else | |
| echo "No SSH E2E JSONL logs found to validate" | |
| fi | |
| - name: Upload SSH E2E logs | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| if: always() | |
| with: | |
| name: ssh-e2e-jsonl | |
| path: test-results/e2e/*.jsonl | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| e2e-orchestrated: | |
| name: E2E Orchestrator (Rust + Shell) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Clone sibling dependencies | |
| shell: bash | |
| run: | | |
| git clone --depth 1 https://github.com/Dicklesworthstone/asupersync.git ../asupersync | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensqlite.git ../frankensqlite | |
| git clone --depth 1 https://github.com/Dicklesworthstone/franken_agent_detection.git ../franken_agent_detection | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensearch.git ../frankensearch | |
| - name: Install Rust nightly | |
| uses: dtolnay/rust-toolchain@881ba7bf39a41cda34ac9e123fb41b44ed08232f # nightly | |
| - name: Cache cargo | |
| uses: Swatinem/rust-cache@ad397744b0d591a723ab90405b7247fac0e6b8db # v2 | |
| - name: Install local rch compatibility shim | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "$RUNNER_TEMP/cass-rch-shim" | |
| cat > "$RUNNER_TEMP/cass-rch-shim/rch" <<'EOF' | |
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| if [[ "${1:-}" != "exec" ]]; then | |
| echo "CI rch shim only supports: rch exec -- <command>" >&2 | |
| exit 2 | |
| fi | |
| shift | |
| if [[ "${1:-}" == "--" ]]; then | |
| shift | |
| fi | |
| exec "$@" | |
| EOF | |
| chmod +x "$RUNNER_TEMP/cass-rch-shim/rch" | |
| echo "$RUNNER_TEMP/cass-rch-shim" >> "$GITHUB_PATH" | |
| - name: Run orchestrated E2E runner (Rust + Shell) | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| RUN_PLAYWRIGHT=0 E2E_LOG=1 ./scripts/tests/run_all.sh | |
| - name: Validate E2E JSONL logs | |
| if: always() | |
| shell: bash | |
| run: | | |
| if [[ -d "test-results/e2e" ]] && ls test-results/e2e/*.jsonl 1>/dev/null 2>&1; then | |
| ./scripts/validate-e2e-jsonl.sh test-results/e2e/*.jsonl | |
| else | |
| echo "No E2E JSONL logs found to validate" | |
| fi | |
| - name: Show E2E summary | |
| if: always() | |
| shell: bash | |
| run: | | |
| if [[ -f "test-results/e2e/summary.md" ]]; then | |
| cat test-results/e2e/summary.md | |
| else | |
| echo "No summary.md found" | |
| fi | |
| - name: Upload orchestrated E2E logs | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| if: always() | |
| with: | |
| name: e2e-orchestrated-logs | |
| path: | | |
| test-results/e2e/combined.jsonl | |
| test-results/e2e/summary.md | |
| test-results/e2e/*.jsonl | |
| test-results/e2e/*.log | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| # TUI E2E matrix: themes × degradation × breakpoints (2dccg.11.4) | |
| e2e-tui-matrix: | |
| name: TUI E2E Matrix | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Clone sibling dependencies | |
| shell: bash | |
| run: | | |
| git clone --depth 1 https://github.com/Dicklesworthstone/asupersync.git ../asupersync | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensqlite.git ../frankensqlite | |
| git clone --depth 1 https://github.com/Dicklesworthstone/franken_agent_detection.git ../franken_agent_detection | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensearch.git ../frankensearch | |
| - name: Install Rust nightly | |
| uses: dtolnay/rust-toolchain@881ba7bf39a41cda34ac9e123fb41b44ed08232f # nightly | |
| - name: Cache cargo | |
| uses: Swatinem/rust-cache@ad397744b0d591a723ab90405b7247fac0e6b8db # v2 | |
| - name: Run TUI stress and E2E scenario tests | |
| run: | | |
| set -euo pipefail | |
| cargo test --lib 'stress_' -- --nocapture 2>&1 | tee test-results-stress.txt | |
| cargo test --lib 'e2e_scenario' -- --nocapture 2>&1 | tee test-results-e2e.txt | |
| cargo test --lib 'cross_theme_degradation' -- --nocapture 2>&1 | tee test-results-matrix.txt | |
| cargo test --lib 'rendering_token_affordance' -- --nocapture 2>&1 | tee test-results-affordance.txt | |
| cargo test --lib 'density_' -- --nocapture 2>&1 | tee test-results-density.txt | |
| env: | |
| RUST_BACKTRACE: 1 | |
| - name: Generate TUI matrix summary | |
| if: always() | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p test-results/tui-matrix | |
| { | |
| echo "# TUI E2E Matrix Summary" | |
| echo "" | |
| echo "**Generated:** $(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| echo "**Commit:** ${{ github.sha }}" | |
| echo "" | |
| echo "## Test Categories" | |
| echo "" | |
| for f in test-results-*.txt; do | |
| category=$(echo "$f" | sed 's/test-results-//; s/\.txt//') | |
| passed=$(grep -c "^test .* ok$" "$f" 2>/dev/null || echo 0) | |
| failed=$(grep -c "^test .* FAILED$" "$f" 2>/dev/null || echo 0) | |
| echo "- **${category}**: ${passed} passed, ${failed} failed" | |
| done | |
| } > test-results/tui-matrix/summary.md | |
| cat test-results/tui-matrix/summary.md | |
| - name: Upload TUI matrix artifacts | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| if: always() | |
| with: | |
| name: tui-e2e-matrix | |
| path: | | |
| test-results-*.txt | |
| test-results/tui-matrix/summary.md | |
| retention-days: 14 | |
| # Crypto test vectors | |
| crypto-vectors: | |
| name: Crypto Test Vectors | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Clone sibling dependencies | |
| shell: bash | |
| run: | | |
| git clone --depth 1 https://github.com/Dicklesworthstone/asupersync.git ../asupersync | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensqlite.git ../frankensqlite | |
| git clone --depth 1 https://github.com/Dicklesworthstone/franken_agent_detection.git ../franken_agent_detection | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensearch.git ../frankensearch | |
| - name: Install Rust nightly | |
| uses: dtolnay/rust-toolchain@881ba7bf39a41cda34ac9e123fb41b44ed08232f # nightly | |
| - name: Cache cargo | |
| uses: Swatinem/rust-cache@ad397744b0d591a723ab90405b7247fac0e6b8db # v2 | |
| - name: Run crypto vector tests | |
| run: cargo test --test crypto_vectors -- --nocapture | |
| env: | |
| RUST_LOG: debug | |
| # Security audit | |
| security: | |
| name: Security Audit | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Clone sibling dependencies | |
| shell: bash | |
| run: | | |
| git clone --depth 1 https://github.com/Dicklesworthstone/asupersync.git ../asupersync | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensqlite.git ../frankensqlite | |
| git clone --depth 1 https://github.com/Dicklesworthstone/franken_agent_detection.git ../franken_agent_detection | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensearch.git ../frankensearch | |
| - name: Install Rust nightly | |
| uses: dtolnay/rust-toolchain@881ba7bf39a41cda34ac9e123fb41b44ed08232f # nightly | |
| - name: Cache cargo | |
| uses: Swatinem/rust-cache@ad397744b0d591a723ab90405b7247fac0e6b8db # v2 | |
| - name: Install cargo-audit | |
| uses: taiki-e/install-action@878643b9fbcb563eeb35c8d9abe2ea9c84cb55bb # cargo-audit | |
| - name: Run cargo audit | |
| run: cargo audit | |
| # Build artifacts (verification only, not for release) | |
| build: | |
| name: Build (${{ matrix.target }}) | |
| needs: [lint, test-rust, ssh-sync-docker, crypto-vectors, security] | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 30 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| - os: macos-15-intel | |
| target: x86_64-apple-darwin | |
| - os: macos-14 | |
| target: aarch64-apple-darwin | |
| - os: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Clone sibling dependencies | |
| shell: bash | |
| run: | | |
| git clone --depth 1 https://github.com/Dicklesworthstone/asupersync.git ../asupersync | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensqlite.git ../frankensqlite | |
| git clone --depth 1 https://github.com/Dicklesworthstone/franken_agent_detection.git ../franken_agent_detection | |
| git clone --depth 1 https://github.com/Dicklesworthstone/frankensearch.git ../frankensearch | |
| - name: Install Rust nightly | |
| uses: dtolnay/rust-toolchain@881ba7bf39a41cda34ac9e123fb41b44ed08232f # nightly | |
| with: | |
| targets: ${{ matrix.target }} | |
| - name: Cache cargo | |
| uses: Swatinem/rust-cache@ad397744b0d591a723ab90405b7247fac0e6b8db # v2 | |
| - name: Configure Windows MSVC CRT for cc-rs | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| # esaxx-rs requests /MT via cc-rs, while ONNX Runtime ships /MD objects. | |
| # Disable cc-rs defaults and choose /MD explicitly so all linked objects agree. | |
| "CRATE_CC_NO_DEFAULTS=1" >> $env:GITHUB_ENV | |
| "CFLAGS=/MD" >> $env:GITHUB_ENV | |
| "CXXFLAGS=/MD" >> $env:GITHUB_ENV | |
| - name: Build release | |
| run: cargo build --release --target ${{ matrix.target }} | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: cass-${{ matrix.target }} | |
| path: target/${{ matrix.target }}/release/cass* | |
| e2e-log-summary: | |
| name: E2E Log Summary | |
| needs: [test-rust] | |
| runs-on: ubuntu-latest | |
| if: always() | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| steps: | |
| - name: Download E2E log artifacts | |
| uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4 | |
| continue-on-error: true | |
| with: | |
| pattern: 'e2e-jsonl-*' | |
| path: artifacts | |
| merge-multiple: true | |
| - name: Aggregate E2E JSONL logs | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p artifacts test-results/e2e | |
| find artifacts -type f -name "*.jsonl" -print0 | sort -z | xargs -0 cat > test-results/e2e/combined.jsonl || true | |
| - name: Generate E2E summary report | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| python3 - <<'PY' | |
| import json | |
| from datetime import datetime, timezone | |
| from pathlib import Path | |
| combined_path = Path("test-results/e2e/combined.jsonl") | |
| summary_path = Path("test-results/e2e/summary.md") | |
| summary_path.parent.mkdir(parents=True, exist_ok=True) | |
| total = passed = failed = skipped = flaky = 0 | |
| durations = {} | |
| failures = [] | |
| if combined_path.exists(): | |
| for line in combined_path.read_text(encoding="utf-8").splitlines(): | |
| line = line.strip() | |
| if not line: | |
| continue | |
| try: | |
| event = json.loads(line) | |
| except json.JSONDecodeError: | |
| continue | |
| if event.get("event") != "test_end": | |
| continue | |
| runner = event.get("runner", "unknown") | |
| result = event.get("result", {}) | |
| status = result.get("status", "unknown") | |
| duration_ms = result.get("duration_ms", 0) | |
| durations[runner] = durations.get(runner, 0) + int(duration_ms or 0) | |
| total += 1 | |
| if status == "pass": | |
| passed += 1 | |
| elif status == "skip": | |
| skipped += 1 | |
| else: | |
| failed += 1 | |
| retries = result.get("retries") | |
| if status == "pass" and retries and int(retries) > 0: | |
| flaky += 1 | |
| if status == "fail": | |
| test = event.get("test", {}) | |
| error = event.get("error", {}) | |
| failures.append({ | |
| "runner": runner, | |
| "suite": test.get("suite", "unknown"), | |
| "name": test.get("name", "unknown"), | |
| "file": test.get("file"), | |
| "line": test.get("line"), | |
| "message": error.get("message", "unknown error"), | |
| }) | |
| now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC") | |
| lines = [ | |
| "# E2E Log Summary (CI)", | |
| "", | |
| f"**Generated:** {now}", | |
| f"**Combined Log:** {combined_path.as_posix()}", | |
| "", | |
| "## Totals", | |
| "", | |
| f"- **Total Tests:** {total}", | |
| f"- **Passed:** {passed}", | |
| f"- **Failed:** {failed}", | |
| f"- **Skipped:** {skipped}", | |
| f"- **Flaky (passed on retry):** {flaky}", | |
| "", | |
| "## Duration by Runner", | |
| "", | |
| "| Runner | Duration (ms) |", | |
| "|--------|---------------|", | |
| ] | |
| if durations: | |
| for runner, duration in sorted(durations.items()): | |
| lines.append(f"| {runner} | {duration} |") | |
| else: | |
| lines.append("| (none) | 0 |") | |
| lines.append("") | |
| lines.append("## Failed Tests") | |
| lines.append("") | |
| if failures: | |
| for f in failures: | |
| location = "" | |
| if f.get("file"): | |
| if f.get("line"): | |
| location = f"{f['file']}:{f['line']}" | |
| else: | |
| location = f"{f['file']}" | |
| detail = f"{f['runner']} :: {f['suite']} :: {f['name']}" | |
| if location: | |
| detail += f" ({location})" | |
| detail += f" — {f['message']}" | |
| lines.append(f"- {detail}") | |
| else: | |
| lines.append("- None") | |
| summary_path.write_text("\n".join(lines) + "\n", encoding="utf-8") | |
| print(f"Wrote {summary_path}") | |
| PY | |
| - name: Upload aggregated E2E logs | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| if: always() | |
| with: | |
| name: e2e-log-summary | |
| path: | | |
| test-results/e2e/combined.jsonl | |
| test-results/e2e/summary.md | |
| retention-days: 14 | |
| if-no-files-found: ignore | |
| - name: Comment summary on PR | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const marker = '<!-- cass-e2e-summary -->'; | |
| const rustStart = '<!-- cass-e2e-rust:start -->'; | |
| const rustEnd = '<!-- cass-e2e-rust:end -->'; | |
| let summary = fs.readFileSync('test-results/e2e/summary.md', 'utf8'); | |
| if (summary.startsWith('# ')) { | |
| summary = summary.replace(/^#\s+.*$/m, '## Rust E2E Summary'); | |
| } else if (!summary.startsWith('## ')) { | |
| summary = `## Rust E2E Summary\n\n${summary}`; | |
| } | |
| const rustSection = `${rustStart}\n${summary.trim()}\n${rustEnd}`; | |
| const { owner, repo } = context.repo; | |
| const issue_number = context.issue.number; | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner, | |
| repo, | |
| issue_number, | |
| per_page: 100, | |
| }); | |
| const existing = comments.find(comment => comment.body.includes(marker)); | |
| const upsertSection = (body, section) => { | |
| if (body.includes(rustStart) && body.includes(rustEnd)) { | |
| const regex = new RegExp(`${rustStart}[\\s\\S]*?${rustEnd}`, 'm'); | |
| return body.replace(regex, section); | |
| } | |
| return `${body.trim()}\n\n${section}`; | |
| }; | |
| let body = existing ? existing.body : marker; | |
| if (!body.includes(marker)) { | |
| body = `${marker}\n${body}`; | |
| } | |
| body = upsertSection(body, rustSection); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner, | |
| repo, | |
| comment_id: existing.id, | |
| body, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner, | |
| repo, | |
| issue_number, | |
| body, | |
| }); | |
| } |