feat(mt#4701): Give the warrant vocabulary a falsifier: detect a hedge that decays across turns #2184
Workflow file for this run
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
| name: Integration Tests | |
| # mt#2634 — CI wiring decision for tests/integration/*.integration.test.ts. | |
| # | |
| # These tests are gated behind RUN_INTEGRATION_TESTS=1 (and, for two of the | |
| # four files, real external credentials/infra). Before this workflow existed | |
| # they were reachable only via `bun run test:integration` / | |
| # `test:integration:docker` run by hand — no CI signal ever ran them. | |
| # | |
| # This workflow is intentionally NOT part of branch protection's required | |
| # `build` check (see ci.yml). It is informational: every job below either | |
| # runs against real external services (flaky by nature) or needs secrets | |
| # that may not be configured on this repo. Treat a red job here as a | |
| # heads-up, not a merge blocker. | |
| # | |
| # Coverage decision per file (see the task spec for the full rationale): | |
| # | |
| # - session-edit-file-cursor-parity.integration.test.ts | |
| # Needs only RUN_INTEGRATION_TESTS=1 (no external creds — internally | |
| # skips its Morph-API-dependent assertions when no Morph key is | |
| # configured). Cheapest candidate; runs unconditionally on every | |
| # matching PR via the `credential-free` job below. | |
| # | |
| # - github-api.integration.test.ts / github-issues-backend.integration.test.ts | |
| # Create/close real issues against a DEDICATED test repo | |
| # (owner/repo default to edobry/minsky-test, overridable via | |
| # GITHUB_TEST_OWNER/GITHUB_TEST_REPO env vars). The built-in | |
| # `secrets.GITHUB_TOKEN` is scoped ONLY to this repo (edobry/minsky) | |
| # and cannot authenticate against a different repo, so a dedicated | |
| # PAT secret is required: `INTEGRATION_TEST_GITHUB_TOKEN` (repo scope | |
| # on the test repo). The `github-api` job below is skipped entirely | |
| # when that secret is not configured (see the `check-secrets` gate | |
| # job — GitHub Actions does not allow referencing `secrets.*` directly | |
| # in a job `if:`, so presence is checked inside a job step and | |
| # exposed as a job output instead). | |
| # | |
| # - postgres-pool-saturation.supabase.integration.test.ts | |
| # Needs a live Supabase branch database (SUPABASE_INTEGRATION_BRANCH_URL). | |
| # Per docs/persistence-configuration.md § Saturation Integration Tests, | |
| # an always-on branch costs ~$10/mo; an ephemeral per-CI-run branch is | |
| # cheap but requires build-out of Supabase-branch-lifecycle automation | |
| # (create before, delete after) that is out of scope for mt#2634. The | |
| # `supabase` job below is skipped when the secret is absent — wiring an | |
| # always-on branch (and setting the secret) is left as an explicit, | |
| # cost-aware operator decision, not something this workflow forces. | |
| # | |
| # - postgres-pool-saturation.testcontainer.integration.test.ts | |
| # Needs a Docker daemon (present by default on GitHub-hosted | |
| # ubuntu-latest runners — no extra provisioning needed) but is NOT run | |
| # automatically on every PR: it pulls a Postgres image cold on every | |
| # run (adds 1-3 minutes) and its own file header documents known | |
| # Bun/testcontainers interaction quirks (see the file for detail) that | |
| # make it a worse fit for unconditional per-PR execution than the | |
| # other three files. It stays available on demand via the | |
| # `run_docker_suite` workflow_dispatch input, or locally via | |
| # `bun run test:integration:docker`. | |
| # | |
| # Manual fallback for everything in this file: `bun run test:integration` | |
| # (credential-free + GitHub-API files, if creds are exported locally) or | |
| # `bun run test:integration:docker` (the Docker suite). | |
| on: | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - "tests/integration/**" | |
| - "src/**" | |
| - "packages/**" | |
| - "package.json" | |
| - "bun.lock" | |
| - ".github/workflows/integration-tests.yml" | |
| workflow_dispatch: | |
| inputs: | |
| run_docker_suite: | |
| description: "Also run the Docker/testcontainer Postgres-saturation suite" | |
| type: boolean | |
| default: false | |
| # Nightly, for the `docker-suite` job ONLY (mt#4347). Every other job in this | |
| # file explicitly opts OUT of this trigger, so their behaviour is unchanged — | |
| # see the `github.event_name != 'schedule'` guards below. | |
| # | |
| # Why: the Docker suite was reachable only by a deliberate workflow_dispatch | |
| # with an explicit input, AND is continue-on-error, so a failure in it had no | |
| # audience at all. It went 119 days with a red test nobody could see (mt#4347). | |
| # A nightly run does not make it a merge blocker — it just means a failure | |
| # lands somewhere with a timestamp. | |
| # | |
| # 07:17 UTC = 03:17 EDT, off-hours for the operator; offset from the hour | |
| # because scheduled runs bunch up and get delayed at :00. | |
| schedule: | |
| - cron: "17 7 * * *" | |
| env: | |
| # Secondary hedge for the mt#3623 install flake: disables the streaming | |
| # tarball path implicated upstream (oven-sh/bun#34821; fix PR #34827 is still | |
| # unmerged, so no release carries a fix). Measured at 0 failures in 10 | |
| # cold-cache installs with the flag set, against a 3-in-10 unmitigated | |
| # baseline -- mt#3623 records both runs and the method. The per-step retry | |
| # loops remain the load-bearing mitigation. | |
| # Remove both when a bun release carries the upstream fix. | |
| BUN_FEATURE_FLAG_DISABLE_STREAMING_INSTALL: "1" | |
| jobs: | |
| # Presence-check gate. GitHub Actions does not support referencing the | |
| # `secrets` context directly in a job-level `if:` (see | |
| # https://github.com/actions/runner/issues/520) — the workaround is to | |
| # read the secret into a step's `env:` inside a job, test THAT with | |
| # `env.X != ''`, and publish the boolean as a job output for downstream | |
| # jobs to consume via `needs.<job>.outputs.<name>`. | |
| check-secrets: | |
| name: Check optional integration-test secrets | |
| # Opt OUT of the nightly schedule (mt#4347): that trigger exists for | |
| # `docker-suite` alone. Skipping this job also skips `github-api` and | |
| # `supabase`, which both `needs:` it — so the nightly run's blast radius | |
| # stays exactly one job. Whether those should ALSO run nightly is a real | |
| # question and deliberately not decided here. | |
| if: github.event_name != 'schedule' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| has_github_token: ${{ steps.check.outputs.has_github_token }} | |
| has_supabase_url: ${{ steps.check.outputs.has_supabase_url }} | |
| steps: | |
| - name: Check secret presence | |
| id: check | |
| env: | |
| GH_TOKEN_VALUE: ${{ secrets.INTEGRATION_TEST_GITHUB_TOKEN }} | |
| SUPABASE_URL_VALUE: ${{ secrets.SUPABASE_INTEGRATION_BRANCH_URL }} | |
| run: | | |
| if [ -n "$GH_TOKEN_VALUE" ]; then | |
| echo "has_github_token=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "has_github_token=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| if [ -n "$SUPABASE_URL_VALUE" ]; then | |
| echo "has_supabase_url=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "has_supabase_url=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| credential-free: | |
| name: Integration (credential-free) | |
| # Opt OUT of the nightly schedule — see check-secrets above (mt#4347). | |
| if: github.event_name != 'schedule' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: Cache Bun install | |
| uses: actions/cache@v4 | |
| with: | |
| path: $HOME/.bun/install/cache | |
| key: bun-install-${{ runner.os }}-${{ hashFiles('bun.lock', 'services/reviewer/bun.lock') }} | |
| restore-keys: | | |
| bun-install-${{ runner.os }}- | |
| - name: Install dependencies | |
| run: for i in 1 2 3; do if bun install --frozen-lockfile --no-progress; then break; fi; if [ "$i" = 3 ]; then exit 1; fi; echo "bun install failed (mt#3623 tarball flake) - retry $i"; sleep 5; done | |
| - name: Run credential-free integration test | |
| env: | |
| RUN_INTEGRATION_TESTS: "1" | |
| run: | | |
| bun test --preload ./tests/setup.ts --timeout=30000 \ | |
| tests/integration/session-edit-file-cursor-parity.integration.test.ts | |
| github-api: | |
| name: Integration (GitHub API — real minsky-test repo) | |
| needs: check-secrets | |
| if: needs.check-secrets.outputs.has_github_token == 'true' | |
| runs-on: ubuntu-latest | |
| # Real external API + a separate repo's issue state — treat as flaky | |
| # by nature. Never a hard block (this workflow isn't a required check | |
| # either way, but continue-on-error keeps the job's own summary honest | |
| # about "flaky external dependency" vs. "real regression"). | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: Cache Bun install | |
| uses: actions/cache@v4 | |
| with: | |
| path: $HOME/.bun/install/cache | |
| key: bun-install-${{ runner.os }}-${{ hashFiles('bun.lock', 'services/reviewer/bun.lock') }} | |
| restore-keys: | | |
| bun-install-${{ runner.os }}- | |
| - name: Install dependencies | |
| run: for i in 1 2 3; do if bun install --frozen-lockfile --no-progress; then break; fi; if [ "$i" = 3 ]; then exit 1; fi; echo "bun install failed (mt#3623 tarball flake) - retry $i"; sleep 5; done | |
| - name: Run GitHub API integration tests | |
| env: | |
| RUN_INTEGRATION_TESTS: "1" | |
| GITHUB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITHUB_TOKEN }} | |
| run: | | |
| bun test --preload ./tests/setup.ts --timeout=30000 \ | |
| tests/integration/github-api.integration.test.ts \ | |
| tests/integration/github-issues-backend.integration.test.ts | |
| supabase: | |
| name: Integration (Supabase branch DB) | |
| needs: check-secrets | |
| if: needs.check-secrets.outputs.has_supabase_url == 'true' | |
| runs-on: ubuntu-latest | |
| # Real external Postgres over the network — flaky by nature; never a | |
| # hard block. | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: Cache Bun install | |
| uses: actions/cache@v4 | |
| with: | |
| path: $HOME/.bun/install/cache | |
| key: bun-install-${{ runner.os }}-${{ hashFiles('bun.lock', 'services/reviewer/bun.lock') }} | |
| restore-keys: | | |
| bun-install-${{ runner.os }}- | |
| - name: Install dependencies | |
| run: for i in 1 2 3; do if bun install --frozen-lockfile --no-progress; then break; fi; if [ "$i" = 3 ]; then exit 1; fi; echo "bun install failed (mt#3623 tarball flake) - retry $i"; sleep 5; done | |
| - name: Run Supabase pool-saturation integration test | |
| env: | |
| RUN_INTEGRATION_TESTS: "1" | |
| SUPABASE_INTEGRATION_BRANCH_URL: ${{ secrets.SUPABASE_INTEGRATION_BRANCH_URL }} | |
| run: | | |
| bun test --preload ./tests/setup.ts --timeout=60000 \ | |
| tests/integration/postgres-pool-saturation.supabase.integration.test.ts | |
| # mt#3349 — integration tests that need a REAL Postgres but NOT Supabase. | |
| # | |
| # Distinct from the `supabase` job above on purpose. That one is gated on the | |
| # SUPABASE_INTEGRATION_BRANCH_URL secret because its test asserts on the | |
| # `XX000 "max clients reached"` shape SUPAVISOR emits under pooler exhaustion | |
| # — the pooler is its system under test. (That secret is currently unset, so | |
| # the job never runs; see mt#3356.) | |
| # | |
| # Tests here need only ordinary Postgres semantics, so they run against a | |
| # service container: free, no secret, and on EVERY pull request. Mirrors | |
| # `.github/workflows/cold-start-migrate.yml`, which established this pattern. | |
| postgres-service: | |
| name: Integration (Postgres service container) | |
| # Opt OUT of the nightly schedule — see check-secrets above (mt#4347). | |
| if: github.event_name != 'schedule' | |
| runs-on: ubuntu-latest | |
| services: | |
| postgres: | |
| # pgvector image, matching cold-start-migrate.yml: the schema uses the | |
| # `vector` type, so `persistence migrate` fails on vanilla postgres:16. | |
| image: pgvector/pgvector:pg16 | |
| env: | |
| POSTGRES_USER: minsky_test | |
| POSTGRES_PASSWORD: minsky_test | |
| POSTGRES_DB: minsky_integration | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 10 | |
| env: | |
| # Composed from parts so gitleaks doesn't flag a hardcoded URL with a | |
| # password (same rationale as cold-start-migrate.yml). Test-only | |
| # credentials with no reach outside this job's ephemeral container. | |
| INTEGRATION_POSTGRES_URL: ${{ format('postgres://{0}:{1}@{2}:5432/{3}', 'minsky_test', 'minsky_test', 'localhost', 'minsky_integration') }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: Cache Bun install | |
| uses: actions/cache@v4 | |
| with: | |
| path: $HOME/.bun/install/cache | |
| key: bun-install-${{ runner.os }}-${{ hashFiles('bun.lock', 'services/reviewer/bun.lock') }} | |
| restore-keys: | | |
| bun-install-${{ runner.os }}- | |
| - name: Install dependencies | |
| run: for i in 1 2 3; do if bun install --frozen-lockfile --no-progress; then break; fi; if [ "$i" = 3 ]; then exit 1; fi; echo "bun install failed (mt#3623 tarball flake) - retry $i"; sleep 5; done | |
| - name: Apply migrations | |
| # The tests write real rows, so the schema has to exist. Run from source | |
| # rather than the bundle — this job is not testing bundle packaging | |
| # (cold-start-migrate.yml owns that), so there is no reason to pay for a | |
| # build here. | |
| env: | |
| MINSKY_PERSISTENCE_BACKEND: postgres | |
| MINSKY_POSTGRES_URL: ${{ format('postgres://{0}:{1}@{2}:5432/{3}', 'minsky_test', 'minsky_test', 'localhost', 'minsky_integration') }} | |
| run: bun src/cli.ts persistence migrate --execute | |
| - name: Verify agent_transcripts table exists | |
| # Belt-and-suspenders: if migrations silently no-op, the test below would | |
| # fail with a confusing driver error instead of a clear one. | |
| # | |
| # Done in bun rather than `psql` (PR #2416 R2). `psql` IS present on | |
| # today's ubuntu-latest — this step ran green with `count 0 (1 row)` in | |
| # run 30564350726 before this change — but the runner image shipping a | |
| # postgres client is not a contract, and this repo already has a real | |
| # Postgres driver in the lockfile. `cold-start-migrate.yml` still uses | |
| # `psql` for its equivalent assertion; the divergence is deliberate, and | |
| # if that one ever breaks on a runner-image change, this is the pattern | |
| # to move it to. | |
| run: | | |
| bun -e ' | |
| const postgres = (await import("postgres")).default; | |
| const sql = postgres(process.env.INTEGRATION_POSTGRES_URL, { max: 1 }); | |
| try { | |
| const [row] = await sql`SELECT count(*)::int AS n FROM agent_transcripts`; | |
| console.log(`agent_transcripts present, ${row.n} rows`); | |
| } catch (err) { | |
| console.error("::error::agent_transcripts missing after migrate:", err.message); | |
| process.exit(1); | |
| } finally { | |
| await sql.end({ timeout: 5 }); | |
| } | |
| ' | |
| - name: Run Postgres-backed integration tests | |
| env: | |
| RUN_INTEGRATION_TESTS: "1" | |
| run: | | |
| bun test --preload ./tests/setup.ts --timeout=60000 \ | |
| tests/integration/transcript-metadata-fill-if-null.integration.test.ts \ | |
| tests/integration/postgres-client-bounded-socket.integration.test.ts | |
| # HELD OUT until mt#3509 lands (mt#3482 / PR #2503 R1): | |
| # | |
| # tests/integration/transcript-attachment-parent-row.integration.test.ts | |
| # bun scripts/verify-transcript-attachment-ingest.ts | |
| # | |
| # Both need `agent_transcript_attachments`, and the migrate step above | |
| # does not create it: on a fresh database `persistence migrate --execute` | |
| # plans 83 pending, reports "Applied 34", exits 0, and leaves that table | |
| # (migration 0039) absent — reproduced locally and by this very job on | |
| # PR #2503. Wiring them in now would make this job red for a reason that | |
| # has nothing to do with what they assert. Re-wire them as part of | |
| # mt#3509 — that task's acceptance tests include the fresh-migrate check | |
| # these two depend on. Until then both remain runnable locally against a | |
| # database that has the table; see the mt#3482 PR body for their output. | |
| docker-suite: | |
| name: Integration (Docker/testcontainer — nightly + manual) | |
| # NOTE: `inputs.run_docker_suite == true` (boolean comparison) is correct | |
| # here, NOT a bug. Since GitHub's June 2022 change, the `inputs` context | |
| # (unlike the legacy `github.event.inputs` context, which stringifies | |
| # everything) preserves the declared `type: boolean` as a real boolean — | |
| # comparing against the string 'true' would be the actual mistake, since | |
| # a real boolean never equals a string. See | |
| # https://github.com/orgs/community/discussions/9343. | |
| # Runs on the nightly schedule, or on a workflow_dispatch that explicitly | |
| # opts in. The schedule half is mt#4347: `continue-on-error` plus | |
| # dispatch-only reachability meant a failure here had no audience, and one | |
| # sat red for 119 days. Nightly gives it one without making it a blocker. | |
| if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.run_docker_suite == true) | |
| runs-on: ubuntu-latest | |
| # Cold image pull + documented Bun/testcontainers quirks (see the test | |
| # file's header) make this the most flake-prone suite; never a hard block. | |
| # | |
| # Keep continue-on-error even now that it runs nightly: the deliberate | |
| # decision is that a flaky container suite must not gate merges. What | |
| # changed is that the run happens at all — visibility, not enforcement. | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: "1.3.14" | |
| - name: Cache Bun install | |
| uses: actions/cache@v4 | |
| with: | |
| path: $HOME/.bun/install/cache | |
| key: bun-install-${{ runner.os }}-${{ hashFiles('bun.lock', 'services/reviewer/bun.lock') }} | |
| restore-keys: | | |
| bun-install-${{ runner.os }}- | |
| - name: Install dependencies | |
| run: for i in 1 2 3; do if bun install --frozen-lockfile --no-progress; then break; fi; if [ "$i" = 3 ]; then exit 1; fi; echo "bun install failed (mt#3623 tarball flake) - retry $i"; sleep 5; done | |
| - name: Run Docker/testcontainer pool-saturation integration test | |
| run: bun run test:integration:docker |