Files
gbrain/.github/workflows/heavy-tests.yml
T
9a3ef3cda7 feat: pgGraph-inspired CI scaffolding wave (v0.37.4.0) (#1228)
Schema-migration matrix + fuzz harness + RSS budget gate + read-latency
under sync + sync lock regression + tests/heavy convention + nightly CI
workflow + BFS frontier cap on traverseGraph.

CI infra (T1-T7):
- tests/heavy/ directory convention + scripts/run-heavy.sh + bun run test:heavy
- tests/heavy/pg_upgrade_matrix.sh: walk pre-v0.13 + pre-v0.18 brain shapes
  forward to head via bootstrap → SCHEMA_SQL → migrations → verifySchema
- test/fuzz/{pure,mixed,filesystem}-validators.test.ts: 1000-run fast-check
  property tests across 8 trust-boundary validators
- scripts/check-fuzz-purity.sh: bun-bundle + grep guard, wired into verify
- tests/heavy/measure_rss.sh: in-memory PGLite workload + peak RSS measurement
  via /proc/self/status (Linux) or process.memoryUsage().rss fallback (macOS,
  refuses to write baseline)
- tests/heavy/read_latency_under_sync.sh: phase A baseline + phase B under
  parallel writer load, reports p50/p95/p99 + delta_pct
- tests/heavy/sync_lock_regression.sh: N concurrent gbrain sync against one
  DB, asserts 1 winner + N-1 lock-busy + zero leaked gbrain_cycle_locks rows
- .github/workflows/heavy-tests.yml: cron '17 8 * * *' + heavy-tests label
  trigger + Postgres service + artifact upload on failure

Engine (T8):
- BrainEngine.traverseGraph opts gain frontierCap?: number + onTruncation?:
  (info: TruncationInfo) => void callback. Return shape preserved
  (Promise<GraphNode[]>) for MCP wire stability.
- Postgres CTE: parenthesized LIMIT N ORDER BY (slug, id) inside recursive term.
- PGLite: same SQL with positional params.
- Per-call callback closure — not engine-instance state — so concurrent
  traversals on the same engine don't cross-talk. 5 contracts pinned in
  test/regressions/v0_36_frontier_cap.test.ts.

Three plan-review passes ran before any code: CEO scope review (Approach C),
Eng dual-voice review (Claude subagent + Codex), and Codex 2nd-pass against
the revised plan. The 2nd pass caught issues the first two missed (Bun ESM
vs require.cache; engine-instance metadata stomping under concurrency;
fixture-size inconsistency). All addressed.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 20:25:41 -07:00

90 lines
3.3 KiB
YAML

name: Heavy Tests
# Heavy ops-shape tests under tests/heavy/. Cost minutes per run; NOT part
# of default PR CI. Two triggers:
# - Nightly schedule (catches regressions within 24h of merge to master).
# - On-demand opt-in via PR label `heavy-tests` (slow loop kept off by default).
# - Manual workflow_dispatch for triage.
#
# See CLAUDE.md "tests/heavy/*.sh" entry and tests/heavy/README.md.
on:
schedule:
- cron: '17 8 * * *' # 08:17 UTC daily — staggered to avoid noisy slots
pull_request:
# `synchronize` + `reopened` fire on subsequent pushes / reopens — without
# them, a PR labeled `heavy-tests` would NEVER re-run heavy on later
# commits. The job-level `if:` below filters to PRs that still carry the
# label so we don't fan out on unrelated label changes.
types: [labeled, synchronize, reopened]
workflow_dispatch:
permissions:
contents: read
# When a PR gets the heavy-tests label, cancel any in-flight heavy-tests run on
# the same ref so we only ever measure the latest commit.
concurrency:
group: heavy-tests-${{ github.ref }}
cancel-in-progress: true
jobs:
heavy:
name: Heavy tests
# On pull_request: only run when the PR currently carries the `heavy-tests`
# label. Works for all three trigger types (labeled, synchronize, reopened)
# because `contains(labels.*.name, ...)` reads the live label set, not the
# event payload's `label.name` (which is only populated for `labeled`).
if: |
github.event_name != 'pull_request' ||
contains(github.event.pull_request.labels.*.name, 'heavy-tests')
runs-on: ubuntu-latest
timeout-minutes: 30
services:
postgres:
image: pgvector/pgvector:pg16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: gbrain_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: latest
- run: bun install
- name: Run heavy tests
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/gbrain_test
run: bun run test:heavy
# The heavy runner writes per-script logs to ~/.gbrain/audit/ on every
# run. Upload those + the rss workload JSON on failure for triage
# without re-running locally.
#
# actions/upload-artifact runs as a node action — `~` is NOT expanded by
# the shell here. Stage logs into the workspace first, then upload from
# the stable workspace-relative path.
- name: Stage heavy-test logs into workspace
if: always()
run: |
mkdir -p heavy-artifacts
cp -r "$HOME/.gbrain/audit"/heavy-* heavy-artifacts/ 2>/dev/null || true
cp tests/heavy/rss-baseline.json heavy-artifacts/ 2>/dev/null || true
- name: Upload heavy-test artifacts
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: heavy-tests-${{ github.run_id }}-${{ github.run_attempt }}
path: heavy-artifacts/
retention-days: 14
if-no-files-found: ignore