From 418d3e6ae8d74c58921073b0dfbd1f826765c065 Mon Sep 17 00:00:00 2001 From: Steven Enamakel's Droid Date: Sun, 7 Jun 2026 15:13:10 -0400 Subject: [PATCH] fix(test): stabilize flaky Rust and Vitest tests (#3479) --- .github/workflows/android-compile.yml | 2 ++ .github/workflows/build-desktop.yml | 1 + .github/workflows/build-windows.yml | 1 + .github/workflows/build.yml | 1 + .github/workflows/contributor-rewards.yml | 1 + .github/workflows/coverage.yml | 5 +++- .github/workflows/deploy-smoke.yml | 4 +-- .github/workflows/deploy.yml | 11 +++++--- .github/workflows/docker-ci-image.yml | 1 + .github/workflows/e2e-agent-review.yml | 6 ++--- .github/workflows/e2e-playwright.yml | 2 +- .github/workflows/e2e-reusable.yml | 16 +++++------ .github/workflows/installer-smoke.yml | 2 ++ .github/workflows/ios-appstore.yml | 1 + .github/workflows/ios-compile.yml | 1 + .github/workflows/pr-ci.yml | 14 +++++++--- .github/workflows/pr-quality.yml | 3 +++ .github/workflows/release-packages.yml | 8 ++++++ .github/workflows/release-production.yml | 10 +++++++ .github/workflows/release-staging.yml | 4 +++ .github/workflows/tauri-cef-pin-guard.yml | 1 + .github/workflows/test-reusable.yml | 5 +++- .github/workflows/typecheck.yml | 2 ++ .github/workflows/uptime-monitor.yml | 1 + .../test/mockApiCore.portSelection.test.ts | 12 ++++++++- .../specs/user-journey-full-task.spec.ts | 4 +-- src/openhuman/memory_tree/health/doctor.rs | 12 ++++++++- tests/memory_sync_sources_raw_coverage_e2e.rs | 27 +++++++++++++++++++ 28 files changed, 131 insertions(+), 27 deletions(-) diff --git a/.github/workflows/android-compile.yml b/.github/workflows/android-compile.yml index 1fe4dc099..737946421 100644 --- a/.github/workflows/android-compile.yml +++ b/.github/workflows/android-compile.yml @@ -76,6 +76,7 @@ jobs: build-android: name: Build Android App Bundle runs-on: ubuntu-latest + timeout-minutes: 30 outputs: version_name: ${{ steps.version.outputs.version_name }} version_code: ${{ steps.version.outputs.version_code }} @@ -190,6 +191,7 @@ jobs: name: Publish Google Play if: ${{ inputs.publish_to_play }} runs-on: ubuntu-latest + timeout-minutes: 15 needs: [build-android] environment: Production steps: diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 6c1fdeeb8..bf437b82f 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -98,6 +98,7 @@ jobs: build: name: "Desktop: ${{ matrix.settings.artifact_suffix }}" runs-on: ${{ matrix.settings.platform }} + timeout-minutes: 30 environment: Production strategy: fail-fast: false diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index c60dc9566..dc885625c 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -11,6 +11,7 @@ jobs: build-windows: name: "Desktop: Windows x64" runs-on: windows-latest + timeout-minutes: 30 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 78d62cf80..431b3c8af 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,7 @@ jobs: build: name: Build Tauri App runs-on: ubuntu-22.04 + timeout-minutes: 30 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 steps: diff --git a/.github/workflows/contributor-rewards.yml b/.github/workflows/contributor-rewards.yml index e2a6a9311..2b1f42a60 100644 --- a/.github/workflows/contributor-rewards.yml +++ b/.github/workflows/contributor-rewards.yml @@ -38,6 +38,7 @@ jobs: reward: name: Invite eligible contributor runs-on: ubuntu-latest + timeout-minutes: 5 if: >- github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request_target' && diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 35a4a71b8..1462082e6 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -22,6 +22,7 @@ jobs: frontend-coverage: name: Frontend Coverage (Vitest) runs-on: ubuntu-22.04 + timeout-minutes: 30 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 steps: @@ -67,7 +68,7 @@ jobs: # the lib suite. Coverage instrumentation roughly doubles wall time vs. # the plain test runs; give it enough headroom for cold/warm cache # variance while still surfacing deadlocks with logs. - timeout-minutes: 45 + timeout-minutes: 30 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 env: @@ -108,6 +109,7 @@ jobs: rust-tauri-coverage: name: Rust Tauri Coverage (cargo-llvm-cov) runs-on: ubuntu-22.04 + timeout-minutes: 30 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 env: @@ -150,6 +152,7 @@ jobs: name: Coverage Gate (diff-cover ≥ 80%) needs: [frontend-coverage, rust-core-coverage, rust-tauri-coverage] runs-on: ubuntu-latest + timeout-minutes: 10 if: github.event_name == 'pull_request' steps: - name: Checkout code diff --git a/.github/workflows/deploy-smoke.yml b/.github/workflows/deploy-smoke.yml index 332358932..e63ce331c 100644 --- a/.github/workflows/deploy-smoke.yml +++ b/.github/workflows/deploy-smoke.yml @@ -11,7 +11,7 @@ jobs: docker-image: name: Build & smoke-test core image runs-on: ubuntu-22.04 - timeout-minutes: 45 + timeout-minutes: 30 steps: - name: Checkout uses: actions/checkout@v5 @@ -109,7 +109,7 @@ jobs: docker-volume-permissions: name: Smoke-test core with fresh volume and no pre-set token runs-on: ubuntu-22.04 - timeout-minutes: 45 + timeout-minutes: 30 needs: docker-image steps: - name: Checkout diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 313ed1035..fc0a4c3e9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -108,6 +108,7 @@ jobs: validate: name: Validate deployment context runs-on: ubuntu-latest + timeout-minutes: 10 environment: ${{ inputs.environment || 'staging' }} outputs: platforms: ${{ steps.platforms.outputs.list }} @@ -179,6 +180,7 @@ jobs: resolve-matrix: name: Resolve build matrix runs-on: ubuntu-latest + timeout-minutes: 5 needs: validate outputs: rust_matrix: ${{ steps.resolve.outputs.rust_matrix }} @@ -245,6 +247,7 @@ jobs: pre-checks: name: Pre-deployment checks runs-on: ubuntu-latest + timeout-minutes: 15 needs: validate if: ${{ !inputs.skip_tests }} steps: @@ -277,7 +280,7 @@ jobs: name: Build Rust core (${{ matrix.target }}) runs-on: ${{ matrix.runner }} needs: [validate, resolve-matrix, pre-checks] - timeout-minutes: 60 # [F-7] prevent hung runners blocking the pipeline + timeout-minutes: 30 # [F-7] prevent hung runners blocking the pipeline # [F-2] proceed when pre-checks succeeded OR was intentionally skipped if: | fromJSON(needs.resolve-matrix.outputs.rust_matrix).include[0] != null && @@ -352,7 +355,7 @@ jobs: name: Build desktop (${{ matrix.artifact_suffix }}) runs-on: ${{ matrix.platform }} needs: [validate, resolve-matrix, pre-checks] - timeout-minutes: 90 # [F-7] + timeout-minutes: 30 # [F-7] environment: ${{ inputs.environment || 'staging' }} # [F-2] same skip-guard as build-rust-core if: | @@ -546,7 +549,7 @@ jobs: name: Build and push Docker image runs-on: ubuntu-latest needs: [validate, resolve-matrix, pre-checks] - timeout-minutes: 45 # [F-7] + timeout-minutes: 30 # [F-7] environment: ${{ inputs.environment || 'staging' }} # [F-2] skip guard; also gated by the docker flag from resolve-matrix if: | @@ -596,6 +599,7 @@ jobs: publish-release: name: Publish release assets runs-on: ubuntu-latest + timeout-minutes: 15 needs: [validate, build-rust-core, build-desktop, build-docker] # [F-3] build-docker / build-rust-core / build-desktop may be 'skipped' # when those platform groups were excluded from deploy_platforms — treat @@ -671,6 +675,7 @@ jobs: verify: name: Verify deployment runs-on: ubuntu-latest + timeout-minutes: 10 needs: [validate, publish-release] if: always() steps: diff --git a/.github/workflows/docker-ci-image.yml b/.github/workflows/docker-ci-image.yml index f312d2a8d..82aa5533b 100644 --- a/.github/workflows/docker-ci-image.yml +++ b/.github/workflows/docker-ci-image.yml @@ -9,6 +9,7 @@ jobs: build-image: name: Build and push CI image runs-on: ubuntu-latest + timeout-minutes: 30 environment: Production steps: - name: Checkout code diff --git a/.github/workflows/e2e-agent-review.yml b/.github/workflows/e2e-agent-review.yml index dc953f626..91406f933 100644 --- a/.github/workflows/e2e-agent-review.yml +++ b/.github/workflows/e2e-agent-review.yml @@ -3,8 +3,8 @@ name: E2E (Linux) - agent-review # # Runs the agent-review spec on Linux via the unified Appium Chromium # driver attached to CEF's CDP port. Same driver path as `e2e.yml`; this -# workflow exists because agent-review needs a longer timeout and its own -# artifact retention. +# workflow exists because agent-review needs its own artifact retention. +# Timeout: 30m (recent runs complete in ~10-20m). # # Previously: tauri-driver + WebKitWebDriver, which can't drive a # CEF-backed WebView. That path is dead. @@ -19,7 +19,7 @@ jobs: e2e-agent-review: name: E2E agent-review (Linux / Appium Chromium) runs-on: ubuntu-22.04 - timeout-minutes: 60 + timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v5 diff --git a/.github/workflows/e2e-playwright.yml b/.github/workflows/e2e-playwright.yml index b4c75ee6b..5db93e7e0 100644 --- a/.github/workflows/e2e-playwright.yml +++ b/.github/workflows/e2e-playwright.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-22.04 container: image: ghcr.io/tinyhumansai/openhuman_ci:latest - timeout-minutes: 90 + timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v5 diff --git a/.github/workflows/e2e-reusable.yml b/.github/workflows/e2e-reusable.yml index 0acbb53f4..6d60a422f 100644 --- a/.github/workflows/e2e-reusable.yml +++ b/.github/workflows/e2e-reusable.yml @@ -70,7 +70,7 @@ jobs: runs-on: ubuntu-22.04 container: image: ghcr.io/tinyhumansai/openhuman_ci:latest - timeout-minutes: 90 + timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v5 @@ -180,7 +180,7 @@ jobs: runs-on: ubuntu-22.04 container: image: ghcr.io/tinyhumansai/openhuman_ci:latest - timeout-minutes: 45 + timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v5 @@ -259,7 +259,7 @@ jobs: runs-on: ubuntu-22.04 container: image: ghcr.io/tinyhumansai/openhuman_ci:latest - timeout-minutes: 60 + timeout-minutes: 30 strategy: fail-fast: false matrix: @@ -372,7 +372,7 @@ jobs: runs-on: ubuntu-22.04 container: image: ghcr.io/tinyhumansai/openhuman_ci:latest - timeout-minutes: 60 + timeout-minutes: 30 env: CARGO_INCREMENTAL: "0" RUST_BACKTRACE: "1" @@ -419,7 +419,7 @@ jobs: if: inputs.run_macos && !inputs.full name: E2E (macOS / Appium Chromium) runs-on: macos-latest - timeout-minutes: 90 + timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v5 @@ -517,7 +517,7 @@ jobs: if: inputs.run_windows && !inputs.full name: E2E (Windows / Appium Chromium) runs-on: windows-latest - timeout-minutes: 90 + timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v5 @@ -601,7 +601,7 @@ jobs: if: inputs.run_macos && inputs.full name: E2E (macOS full / ${{ matrix.shard.name }}) runs-on: macos-latest - timeout-minutes: 60 + timeout-minutes: 30 strategy: fail-fast: false matrix: @@ -750,7 +750,7 @@ jobs: if: inputs.run_windows && inputs.full name: E2E (Windows full / ${{ matrix.shard.name }}) runs-on: windows-latest - timeout-minutes: 60 + timeout-minutes: 30 strategy: fail-fast: false matrix: diff --git a/.github/workflows/installer-smoke.yml b/.github/workflows/installer-smoke.yml index 2c09d1a47..ed9e558f4 100644 --- a/.github/workflows/installer-smoke.yml +++ b/.github/workflows/installer-smoke.yml @@ -8,6 +8,7 @@ jobs: smoke-unix: name: Smoke install.sh (${{ matrix.os }}) runs-on: ${{ matrix.os }} + timeout-minutes: 10 strategy: fail-fast: false matrix: @@ -25,6 +26,7 @@ jobs: smoke-windows: name: install.ps1 tests + dry-run (windows-latest) runs-on: windows-latest + timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@v5 diff --git a/.github/workflows/ios-appstore.yml b/.github/workflows/ios-appstore.yml index efe98b40b..06de85483 100644 --- a/.github/workflows/ios-appstore.yml +++ b/.github/workflows/ios-appstore.yml @@ -31,6 +31,7 @@ jobs: build-and-upload: name: Build and upload iOS IPA runs-on: macos-26 + timeout-minutes: 30 environment: App Development Production env: APP_IDENTIFIER: com.tinyhumansai.openhuman diff --git a/.github/workflows/ios-compile.yml b/.github/workflows/ios-compile.yml index a32207fb3..351681e05 100644 --- a/.github/workflows/ios-compile.yml +++ b/.github/workflows/ios-compile.yml @@ -16,6 +16,7 @@ jobs: ios-compile: name: iOS Compile Check runs-on: macos-latest + timeout-minutes: 30 env: # Pin the deployment target so swift-rs invokes the Swift compiler with # `-target arm64-apple-ios16.0`. Matches Package.swift in diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index 60eedf199..93f5b3c85 100644 --- a/.github/workflows/pr-ci.yml +++ b/.github/workflows/pr-ci.yml @@ -26,6 +26,7 @@ jobs: frontend-quality: name: Frontend Quality (typecheck, lint, format) runs-on: ubuntu-22.04 + timeout-minutes: 15 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 steps: @@ -71,6 +72,7 @@ jobs: i18n-coverage: name: i18n Coverage (parity) runs-on: ubuntu-22.04 + timeout-minutes: 10 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 steps: @@ -104,6 +106,7 @@ jobs: rust-quality: name: Rust Quality (fmt, clippy) runs-on: ubuntu-22.04 + timeout-minutes: 20 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 env: @@ -139,7 +142,7 @@ jobs: runs-on: ubuntu-22.04 container: image: ghcr.io/tinyhumansai/openhuman_ci:latest - timeout-minutes: 45 + timeout-minutes: 30 env: CARGO_INCREMENTAL: "0" steps: @@ -235,6 +238,7 @@ jobs: name: Frontend Coverage (Vitest) needs: [frontend-quality] runs-on: ubuntu-22.04 + timeout-minutes: 30 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 steps: @@ -287,7 +291,7 @@ jobs: name: Rust Core Coverage (cargo-llvm-cov) needs: [rust-quality] runs-on: ubuntu-22.04 - timeout-minutes: 45 + timeout-minutes: 30 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 env: @@ -331,7 +335,7 @@ jobs: name: Rust Tauri Coverage (cargo-llvm-cov) needs: [rust-quality] runs-on: ubuntu-22.04 - timeout-minutes: 45 + timeout-minutes: 30 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 env: @@ -395,7 +399,7 @@ jobs: runs-on: ubuntu-22.04 container: image: ghcr.io/tinyhumansai/openhuman_ci:latest - timeout-minutes: 60 + timeout-minutes: 30 env: CARGO_INCREMENTAL: "0" steps: @@ -537,6 +541,7 @@ jobs: needs: [playwright-e2e] if: always() runs-on: ubuntu-latest + timeout-minutes: 5 steps: - name: Require all Playwright shards to pass run: | @@ -557,6 +562,7 @@ jobs: name: Coverage Gate (diff-cover >= 80%) needs: [frontend-coverage, rust-core-coverage, rust-tauri-coverage] runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Checkout code uses: actions/checkout@v5 diff --git a/.github/workflows/pr-quality.yml b/.github/workflows/pr-quality.yml index 611078211..4f6c8e7c2 100644 --- a/.github/workflows/pr-quality.yml +++ b/.github/workflows/pr-quality.yml @@ -18,6 +18,7 @@ jobs: checklist-guard: name: PR Submission Checklist runs-on: ubuntu-22.04 + timeout-minutes: 10 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 continue-on-error: true @@ -34,6 +35,7 @@ jobs: coverage-matrix: name: Coverage Matrix Sync runs-on: ubuntu-22.04 + timeout-minutes: 10 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 continue-on-error: true @@ -48,6 +50,7 @@ jobs: markdown-link-check: name: Markdown Link Check runs-on: ubuntu-latest + timeout-minutes: 10 continue-on-error: true if: ${{ !contains(github.event.pull_request.labels.*.name, 'docs') && !contains(github.event.pull_request.labels.*.name, 'chore') }} steps: diff --git a/.github/workflows/release-packages.yml b/.github/workflows/release-packages.yml index 7cc7e5357..9a052b3e1 100644 --- a/.github/workflows/release-packages.yml +++ b/.github/workflows/release-packages.yml @@ -32,6 +32,7 @@ jobs: build-cli-linux-arm64: name: Build Linux arm64 CLI tarball runs-on: ubuntu-24.04-arm + timeout-minutes: 30 steps: - name: Checkout tag uses: actions/checkout@v5 @@ -91,6 +92,7 @@ jobs: update-homebrew: name: Update Homebrew tap formula runs-on: ubuntu-latest + timeout-minutes: 10 needs: [build-cli-linux-arm64] steps: - name: Checkout main repo (for formula template) @@ -122,6 +124,7 @@ jobs: build-apt-repo: name: Build apt repository runs-on: ubuntu-22.04 + timeout-minutes: 15 needs: [build-cli-linux-arm64] steps: - name: Checkout tag @@ -162,6 +165,7 @@ jobs: publish-npm: name: Publish npm package runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Checkout tag uses: actions/checkout@v5 @@ -187,6 +191,7 @@ jobs: smoke-homebrew: name: Smoke — Homebrew (${{ matrix.os }}) runs-on: ${{ matrix.os }} + timeout-minutes: 15 needs: [update-homebrew] continue-on-error: true strategy: @@ -212,6 +217,7 @@ jobs: smoke-apt: name: Smoke — apt (ubuntu-22.04) runs-on: ubuntu-22.04 + timeout-minutes: 10 needs: [build-apt-repo] continue-on-error: true steps: @@ -235,6 +241,7 @@ jobs: smoke-npm: name: Smoke — npm (${{ matrix.os }}) runs-on: ${{ matrix.os }} + timeout-minutes: 10 needs: [publish-npm] continue-on-error: true strategy: @@ -263,6 +270,7 @@ jobs: create-backlog-issue: name: Create backlog issue (once) runs-on: ubuntu-latest + timeout-minutes: 5 steps: - name: Create issue if it doesn't exist uses: actions/github-script@v8 diff --git a/.github/workflows/release-production.yml b/.github/workflows/release-production.yml index 6501768be..a9497ecc8 100644 --- a/.github/workflows/release-production.yml +++ b/.github/workflows/release-production.yml @@ -72,6 +72,7 @@ jobs: review-approval: name: Manual approval gate runs-on: ubuntu-latest + timeout-minutes: 5 environment: Release-Approval steps: - name: Record approval @@ -85,6 +86,7 @@ jobs: prepare-build: name: Prepare build context runs-on: ubuntu-latest + timeout-minutes: 15 environment: Production needs: [review-approval] outputs: @@ -218,6 +220,7 @@ jobs: create-release: name: Prepare GitHub release runs-on: ubuntu-latest + timeout-minutes: 10 environment: Production needs: [prepare-build] outputs: @@ -341,6 +344,7 @@ jobs: needs: [prepare-build, create-release] if: always() && needs.create-release.result == 'success' runs-on: ubuntu-latest + timeout-minutes: 30 environment: Production env: REGISTRY: ghcr.io @@ -411,6 +415,7 @@ jobs: name: "CLI: ${{ matrix.target }}" needs: [prepare-build, create-release] if: ${{ inputs.create_release && always() && needs.create-release.result == 'success' }} + timeout-minutes: 30 environment: Production runs-on: ${{ matrix.runner }} strategy: @@ -481,6 +486,7 @@ jobs: needs: [prepare-build, create-release, build-desktop] if: ${{ inputs.create_release && always() && needs.build-desktop.result == 'success' }} runs-on: ubuntu-latest + timeout-minutes: 10 environment: Production steps: - name: Checkout build ref @@ -503,6 +509,7 @@ jobs: publish-release: name: Publish draft release runs-on: ubuntu-latest + timeout-minutes: 10 environment: Production needs: - prepare-build @@ -616,6 +623,7 @@ jobs: tag-docker-latest: name: "Docker: tag :latest" runs-on: ubuntu-latest + timeout-minutes: 10 environment: Production needs: [prepare-build, publish-release] if: ${{ inputs.create_release && always() && needs.publish-release.result == 'success' }} @@ -652,6 +660,7 @@ jobs: record-sentry-deploy: name: Record Sentry deploy marker runs-on: ubuntu-latest + timeout-minutes: 10 environment: Production needs: [prepare-build, publish-release] if: ${{ inputs.create_release && always() && needs.publish-release.result == 'success' }} @@ -690,6 +699,7 @@ jobs: cleanup-failed-release: name: Remove release and tag if build failed runs-on: ubuntu-latest + timeout-minutes: 10 environment: Production needs: - prepare-build diff --git a/.github/workflows/release-staging.yml b/.github/workflows/release-staging.yml index b08c80245..db595e8f8 100644 --- a/.github/workflows/release-staging.yml +++ b/.github/workflows/release-staging.yml @@ -55,6 +55,7 @@ jobs: prepare-build: name: Prepare build context runs-on: ubuntu-latest + timeout-minutes: 15 # Reuse the Production GitHub Actions environment so Sentry vars # (`OPENHUMAN_*_SENTRY_DSN`, `SENTRY_PROJECT_*`, `SENTRY_ORG`, # `SENTRY_AUTH_TOKEN`) and `VITE_DEBUG` resolve here too. Staging @@ -257,6 +258,7 @@ jobs: needs: [prepare-build] if: inputs.create_tag runs-on: ubuntu-latest + timeout-minutes: 30 environment: Production steps: - name: Checkout build ref @@ -295,6 +297,7 @@ jobs: record-sentry-deploy: name: Record Sentry deploy marker runs-on: ubuntu-latest + timeout-minutes: 10 environment: Production if: inputs.create_tag needs: [prepare-build, build-desktop, build-docker] @@ -336,6 +339,7 @@ jobs: cleanup-failed-staging: name: Remove staging tag if build failed runs-on: ubuntu-latest + timeout-minutes: 10 environment: Production needs: [prepare-build, build-desktop, build-docker] if: >- diff --git a/.github/workflows/tauri-cef-pin-guard.yml b/.github/workflows/tauri-cef-pin-guard.yml index cb3874f61..0e1aa0789 100644 --- a/.github/workflows/tauri-cef-pin-guard.yml +++ b/.github/workflows/tauri-cef-pin-guard.yml @@ -24,6 +24,7 @@ jobs: verify-pin: name: Verify tauri-cef submodule pin runs-on: ubuntu-latest + timeout-minutes: 5 steps: - name: Checkout uses: actions/checkout@v5 diff --git a/.github/workflows/test-reusable.yml b/.github/workflows/test-reusable.yml index 7c33b1a4b..0ddba2777 100644 --- a/.github/workflows/test-reusable.yml +++ b/.github/workflows/test-reusable.yml @@ -40,6 +40,7 @@ jobs: if: inputs.run_unit name: i18n Coverage runs-on: ubuntu-22.04 + timeout-minutes: 10 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 steps: @@ -64,6 +65,7 @@ jobs: if: inputs.run_unit name: Frontend Unit Tests runs-on: ubuntu-22.04 + timeout-minutes: 30 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 steps: @@ -127,7 +129,7 @@ jobs: if: inputs.run_rust_core name: Rust Core Tests (Windows — secrets ACL) runs-on: windows-latest - timeout-minutes: 35 + timeout-minutes: 30 env: CARGO_INCREMENTAL: '0' SCCACHE_GHA_ENABLED: 'true' @@ -164,6 +166,7 @@ jobs: if: inputs.run_rust_tauri name: Rust Tauri Shell Tests runs-on: ubuntu-22.04 + timeout-minutes: 20 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 env: diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 3338a6b27..0833c7347 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -15,6 +15,7 @@ jobs: typecheck: name: Type Check TypeScript runs-on: ubuntu-22.04 + timeout-minutes: 15 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 steps: @@ -46,6 +47,7 @@ jobs: rust-quality: name: Rust Quality (fmt + clippy) runs-on: ubuntu-22.04 + timeout-minutes: 20 container: image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0 steps: diff --git a/.github/workflows/uptime-monitor.yml b/.github/workflows/uptime-monitor.yml index 459e6d32e..990fd4132 100644 --- a/.github/workflows/uptime-monitor.yml +++ b/.github/workflows/uptime-monitor.yml @@ -16,6 +16,7 @@ jobs: check-uptime: name: Check Backend Health runs-on: ubuntu-latest + timeout-minutes: 5 steps: - name: Uptime Check Script uses: actions/github-script@v7 diff --git a/app/src/test/mockApiCore.portSelection.test.ts b/app/src/test/mockApiCore.portSelection.test.ts index 96e730367..1de09512c 100644 --- a/app/src/test/mockApiCore.portSelection.test.ts +++ b/app/src/test/mockApiCore.portSelection.test.ts @@ -38,7 +38,17 @@ afterEach(async () => { it('falls back to an available local port when the preferred Vitest mock port is occupied', async () => { await stopMockServer(); - const blocker = await listenOn(preferredPort); + + let blocker: net.Server | null = null; + try { + blocker = await listenOn(preferredPort); + } catch (err: unknown) { + if (!(err && typeof err === 'object' && (err as NodeJS.ErrnoException).code === 'EADDRINUSE')) { + throw err; + } + // Port already occupied externally — the precondition is already met, + // proceed without our own blocker. + } try { const started = await startMockServer(preferredPort, { retryIfInUse: true }); diff --git a/app/test/playwright/specs/user-journey-full-task.spec.ts b/app/test/playwright/specs/user-journey-full-task.spec.ts index dbea5de36..a683ad822 100644 --- a/app/test/playwright/specs/user-journey-full-task.spec.ts +++ b/app/test/playwright/specs/user-journey-full-task.spec.ts @@ -137,7 +137,7 @@ test.describe('User journey - full research task', () => { expect(typeof threadId).toBe('string'); await sendMessage(page, PROMPT); - await expect(page.getByText(CANARY_FINAL)).toBeVisible({ timeout: 45_000 }); + await expect(page.getByText(CANARY_FINAL).first()).toBeVisible({ timeout: 45_000 }); await page.goto('/#/home'); await waitForAppReady(page); @@ -147,6 +147,6 @@ test.describe('User journey - full research task', () => { await page.goto('/#/chat'); await waitForAppReady(page); - await expect(page.getByText(CANARY_FINAL)).toBeVisible({ timeout: 15_000 }); + await expect(page.getByText(CANARY_FINAL).first()).toBeVisible({ timeout: 15_000 }); }); }); diff --git a/src/openhuman/memory_tree/health/doctor.rs b/src/openhuman/memory_tree/health/doctor.rs index 41aa16498..337b7a6c3 100644 --- a/src/openhuman/memory_tree/health/doctor.rs +++ b/src/openhuman/memory_tree/health/doctor.rs @@ -345,9 +345,19 @@ mod tests { cfg.local_ai.runtime_enabled = true; cfg.scheduler_gate.mode = SchedulerGateMode::Off; + // Double-reset: guard resets on entry, but a concurrent non-guarded + // code path (e.g. a tokio task draining after its test dropped its + // guard) may have re-set the flags between guard acquisition and here. + super::super::clear_semantic_recall_degraded(); + super::super::clear_structure_degraded(); + let report = run_doctor(&cfg); // Paused is reported but does NOT make the pipeline unhealthy. - assert!(report.healthy); + assert!( + report.healthy, + "expected healthy, failing stages: {:?}", + report.stages.iter().filter(|s| !s.ok).collect::>() + ); let gate = report .stages .iter() diff --git a/tests/memory_sync_sources_raw_coverage_e2e.rs b/tests/memory_sync_sources_raw_coverage_e2e.rs index 65f8ed3bc..f7d563c9e 100644 --- a/tests/memory_sync_sources_raw_coverage_e2e.rs +++ b/tests/memory_sync_sources_raw_coverage_e2e.rs @@ -401,6 +401,7 @@ async fn github_reader_uses_fake_gh_for_list_and_read_paths() { std::fs::create_dir_all(&bin).expect("bin dir"); let script = bin.join("gh"); write_fake_gh(&script); + write_fake_git(&bin.join("git")); let old_path = std::env::var("PATH").unwrap_or_default(); let _path = EnvGuard::set("PATH", format!("{}:{old_path}", bin.display())); @@ -759,3 +760,29 @@ esac std::fs::set_permissions(path, perms).expect("chmod fake gh"); } } + +fn write_fake_git(path: &PathBuf) { + let script = r#"#!/usr/bin/env bash +# Fake git that fails on clone/fetch so the GitHub reader falls through to the +# gh CLI API path (which is intercepted by write_fake_gh). +case "${1:-}" in + clone|fetch) + echo "fatal: repository not found" >&2 + exit 128 + ;; + *) + exec /usr/bin/git "$@" + ;; +esac +"#; + std::fs::write(path, script).expect("write fake git"); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let mut perms = std::fs::metadata(path) + .expect("fake git metadata") + .permissions(); + perms.set_mode(0o755); + std::fs::set_permissions(path, perms).expect("chmod fake git"); + } +}