From d115653432ab22457cf8d700ff87e6c3e2a12bfe Mon Sep 17 00:00:00 2001 From: saladday <1203511142@qq.com> Date: Mon, 21 Sep 2026 13:16:40 +0800 Subject: [PATCH 1/2] Bound native CI disk use and synchronize lease handoff tests --- .github/workflows/agents-harness.yml | 27 +++++++++++--- CONTRIBUTING.md | 12 ++++++ .../store/environment_claim_worker_test.go | 4 ++ .../environment_connection_worker_test.go | 4 +- .../store/execution_lease_handoff_test.go | 37 +++++++++++++++++++ 5 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 services/agents-api/internal/store/execution_lease_handoff_test.go diff --git a/.github/workflows/agents-harness.yml b/.github/workflows/agents-harness.yml index 7678f433a..64ca39d22 100644 --- a/.github/workflows/agents-harness.yml +++ b/.github/workflows/agents-harness.yml @@ -28,14 +28,24 @@ jobs: native-build: runs-on: ubuntu-22.04 timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + target: [check-agents-harness-native, build-agents-harness] env: CARGO_HOME: /home/runner/.parsar/cache/agents-harness-cargo CARGO_TARGET_DIR: /home/runner/.parsar/cache/agents-harness-target AGENTS_HARNESS_NATIVE_SOURCE: /home/runner/.parsar/references/codex-native CARGO_BUILD_JOBS: 4 CARGO_PROFILE_DEV_DEBUG: 0 + CARGO_INCREMENTAL: 0 steps: - uses: actions/checkout@v7 + - name: Reserve disk for native compilation + run: | + # These preinstalled SDKs are unused by the Rust-only job. + sudo rm -rf /usr/share/dotnet /usr/local/lib/android + df -h "$HOME" - name: Check packaging run: make check-agents-harness - name: Install native build prerequisites @@ -51,10 +61,15 @@ jobs: - uses: actions/cache@v6 with: path: | - ~/.parsar/cache/agents-harness-cargo - ~/.parsar/cache/agents-harness-target - key: agents-harness-${{ runner.os }}-1.95.0-${{ hashFiles('packages/codex-harness/source.json') }} - - name: Check native harness and independent release build + ~/.parsar/cache/agents-harness-cargo/registry + ~/.parsar/cache/agents-harness-cargo/git + key: agents-harness-deps-v2-${{ runner.os }}-1.95.0-${{ hashFiles('packages/codex-harness/source.json') }} + - name: Run native target + env: + NATIVE_TARGET: ${{ matrix.target }} + run: make "$NATIVE_TARGET" + - name: Report disk usage + if: always() run: | - make check-agents-harness-native - make build-agents-harness + df -h "$HOME" + du -sh "$CARGO_HOME" "$CARGO_TARGET_DIR" 2>/dev/null || true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3e0b5e81f..cc6cfd8bc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,6 +52,14 @@ Changes to native Harness sources require `make check-agents-harness-native`, checks require real models; do not count omitted prerequisites or mocked responses as live acceptance. Never expand this extraction into unrelated behavioral fixes. +Native Harness CI runs checks and the release build on separate disposable runners +so debug and release artifacts do not compete for disk. It disables incremental +compilation and caches downloaded Cargo dependencies, not target directories. +The Rust-only jobs remove unused preinstalled Android/.NET SDKs and report disk +usage. Both matrix targets must pass; release optimization settings and native +test/Clippy coverage remain unchanged. These resource settings apply to CI, not +operator build defaults. + ## Architecture boundaries The following execution rules are retained from the source contributor guide. @@ -1954,6 +1962,10 @@ replaced; do not carry obsolete compatibility code forward to satisfy this secti Lease Close invalidates its writer and waits for pgx connection cleanup within the caller deadline. A later Close can resume that wait after a timeout. This drains client resources; it does not acknowledge remote advisory-lock release. + Tests that immediately transfer ownership must observe the previous owner's + exact database advisory lock disappearing before starting its successor. Bound + that wait and fail on query errors; do not retry Worker startup to mask competing + owners or change production lease behavior for a test's timing assumption. Worker shutdown retains its existing bounded best-effort close policy. This fences database writes, not already queued daemon commands or native effects. Native quiescence/reconnect and recovery of unreported outcomes remain separate diff --git a/services/agents-api/internal/store/environment_claim_worker_test.go b/services/agents-api/internal/store/environment_claim_worker_test.go index 78772b10f..bc45f7902 100644 --- a/services/agents-api/internal/store/environment_claim_worker_test.go +++ b/services/agents-api/internal/store/environment_claim_worker_test.go @@ -36,18 +36,22 @@ func TestWorkerReconcilesEnvironmentPromotionBeforeStart(t *testing.T) { t.Fatal("promotion did not retain the active claim", turn, err) } // Simulate owner loss after commit, without sending any daemon Start. + awaitRelease := observeExecutionLeaseRelease(t, pool) if err := lease.Close(t.Context()); err != nil { t.Fatal(err) } + awaitRelease() restarted, err := execution.StartWorker(t.Context(), &execution.Dispatcher{Store: s, Registry: gateway.NewRegistry()}) if err != nil { t.Fatal(err) } stopped, cancel := context.WithCancel(t.Context()) cancel() + awaitRelease = observeExecutionLeaseRelease(t, pool) if err := restarted.Run(stopped); !errors.Is(err, context.Canceled) { t.Fatal(err) } + awaitRelease() turn, err = s.GetTurn(t.Context(), tenant, pending.SessionID, turnID) var outcome struct { ErrorCode string `json:"error_code"` diff --git a/services/agents-api/internal/store/environment_connection_worker_test.go b/services/agents-api/internal/store/environment_connection_worker_test.go index dec65a981..3d011b384 100644 --- a/services/agents-api/internal/store/environment_connection_worker_test.go +++ b/services/agents-api/internal/store/environment_connection_worker_test.go @@ -13,7 +13,7 @@ import ( ) func TestEnvironmentConnectionWorkerReconcilesAndClosesBeforeLease(t *testing.T) { - s, _ := store.NewTestStore(t) + s, pool := store.NewTestStore(t) tenant := uuid.NewString() session, err := s.CreateSession(t.Context(), tenant, store.CreateSessionInput{Creator: store.FixtureCreator(), Engine: "codex", IdempotencyKey: "connection-worker", Configuration: []byte(`{"environment":{"type":"self_hosted","workspace_directory":"/workspace"}}`)}) if err != nil { @@ -34,9 +34,11 @@ func TestEnvironmentConnectionWorkerReconcilesAndClosesBeforeLease(t *testing.T) if err := lease.Store().ObserveEnvironmentConnection(t.Context(), tenant, environment.ID, generation, 1, true); err != nil { t.Fatal(err) } + awaitRelease := observeExecutionLeaseRelease(t, pool) if err := lease.Close(t.Context()); err != nil { t.Fatal(err) } + awaitRelease() var worker *execution.Worker closed := false next := uuid.NewString() diff --git a/services/agents-api/internal/store/execution_lease_handoff_test.go b/services/agents-api/internal/store/execution_lease_handoff_test.go new file mode 100644 index 000000000..ffbcf500f --- /dev/null +++ b/services/agents-api/internal/store/execution_lease_handoff_test.go @@ -0,0 +1,37 @@ +package store_test + +import ( + "context" + "testing" + "time" + + "github.com/jackc/pgx/v5/pgxpool" +) + +// observeExecutionLeaseRelease captures the current owner before shutdown. Local +// pgx cleanup does not acknowledge the server's release of its advisory lock. +func observeExecutionLeaseRelease(t *testing.T, pool *pgxpool.Pool) func() { + t.Helper() + // Match the single-bigint key in queries/scheduling.sql, scoped to this DB. + const lock = `locktype='advisory' AND granted AND objsubid=1 + AND classid::bigint * 4294967296 + objid::bigint = 706172736172 + AND database=(SELECT oid FROM pg_database WHERE datname=current_database())` + ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second) + defer cancel() + var owner int32 + if err := pool.QueryRow(ctx, "SELECT pid FROM pg_locks WHERE "+lock).Scan(&owner); err != nil { + t.Fatal("observe execution lease owner", err) + } + return func() { + t.Helper() + ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second) + defer cancel() + awaitDaemonRemoteCondition(t, ctx, 5*time.Second, "previous execution lease release", func() bool { + var held bool + if err := pool.QueryRow(ctx, "SELECT EXISTS (SELECT 1 FROM pg_locks WHERE "+lock+" AND pid=$1)", owner).Scan(&held); err != nil { + t.Fatal("observe execution lease release", err) + } + return !held + }) + } +} From 44b553f49aed464f9cef9355272312614d9e2020 Mon Sep 17 00:00:00 2001 From: saladday <1203511142@qq.com> Date: Mon, 21 Sep 2026 14:21:45 +0800 Subject: [PATCH 2/2] Allow the cold optimized Harness release to finish on hosted runners --- .github/workflows/agents-harness.yml | 8 ++++++-- CONTRIBUTING.md | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/agents-harness.yml b/.github/workflows/agents-harness.yml index 64ca39d22..e9ebd90bd 100644 --- a/.github/workflows/agents-harness.yml +++ b/.github/workflows/agents-harness.yml @@ -27,11 +27,15 @@ concurrency: jobs: native-build: runs-on: ubuntu-22.04 - timeout-minutes: 60 + timeout-minutes: ${{ matrix.timeout }} strategy: fail-fast: false matrix: - target: [check-agents-harness-native, build-agents-harness] + include: + - target: check-agents-harness-native + timeout: 60 + - target: build-agents-harness + timeout: 120 env: CARGO_HOME: /home/runner/.parsar/cache/agents-harness-cargo CARGO_TARGET_DIR: /home/runner/.parsar/cache/agents-harness-target diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cc6cfd8bc..a2fff49af 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,7 +58,9 @@ compilation and caches downloaded Cargo dependencies, not target directories. The Rust-only jobs remove unused preinstalled Android/.NET SDKs and report disk usage. Both matrix targets must pass; release optimization settings and native test/Clippy coverage remain unchanged. These resource settings apply to CI, not -operator build defaults. +operator build defaults. Checks retain a 60-minute limit; cold optimized release +builds receive 120 minutes after the standard runner exceeded one hour with disk +space remaining. A timeout is still a failed build, not a skipped gate. ## Architecture boundaries