-
Notifications
You must be signed in to change notification settings - Fork 5
476 lines (456 loc) · 20.9 KB
/
Copy pathci.yml
File metadata and controls
476 lines (456 loc) · 20.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
name: CI
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
# `cargo test --workspace` links ~113 separate test binaries, and each one
# emits full debug info. On Windows that is a large share of both link time
# and disk I/O (measured: ~6 min between the last `Compiling` line and the
# first test). Line tables keep the file:line that RUST_BACKTRACE=1 above
# exists to produce, at a fraction of the size. CI-only, set here rather
# than in Cargo.toml so local `cargo test` debugging is untouched.
CARGO_PROFILE_TEST_DEBUG: line-tables-only
CARGO_PROFILE_DEV_DEBUG: line-tables-only
jobs:
test:
name: test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-python@v7
with:
python-version: "3.11"
# Defender scans every file cargo writes, and this job writes a lot of
# them. Best-effort: a future runner image that refuses the exclusion
# must slow the build down, not fail it.
- name: Exclude the build tree from Defender (windows)
if: runner.os == 'Windows'
continue-on-error: true
shell: pwsh
run: |
Add-MpPreference -ExclusionPath "${{ github.workspace }}"
Add-MpPreference -ExclusionPath "$env:USERPROFILE\.cargo"
foreach ($p in 'rustc.exe','cargo.exe','link.exe') {
Add-MpPreference -ExclusionProcess $p
}
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@nextest
# `cargo test` runs test BINARIES one at a time, parallelising only
# within each; with ~113 binaries, most of them small, the cores sit
# idle. Measured on windows-latest: 11.1 min of test execution out of a
# 22 min step. nextest schedules across binaries instead (locally:
# 55s -> 30s for the same already-built suite, 2,535 tests, same result).
#
# Sharding this job was considered and rejected on the same numbers:
# only ~11 of the 22 min is test execution, so every shard would repeat
# the ~11 min of compile+link to split the other half — near-zero
# wall-clock gain for 3x the runner minutes. It would also have to keep
# reporting under the exact name `test (windows-latest)`, which the
# protect-main ruleset requires.
- name: Test workspace
run: cargo nextest run --workspace
# nextest deliberately does not run doctests, so they stay on cargo.
- name: Doctests
run: cargo test --workspace --doc
postgres:
name: postgres backend
runs-on: ubuntu-latest
services:
postgres:
image: pgvector/pgvector:pg16
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: areev_test
ports: ["5432:5432"]
# Mandatory: the service starts concurrently with the job and the
# first connection races it without a health gate.
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 5s
--health-timeout 5s
--health-retries 10
env:
DATABASE_URL: postgres://postgres:postgres@127.0.0.1:5432/areev_test
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: actions/setup-python@v7
with:
python-version: "3.11"
- uses: actions/setup-node@v7
with:
node-version: 22
- uses: Swatinem/rust-cache@v2
# The conformance runner hard-fails (rather than skipping) when
# CI=true and DATABASE_URL is missing, so a broken service can never
# masquerade as a skipped suite.
- name: Conformance + CAL smoke (postgres)
run: cargo test -p areev-conformance --features postgres
- name: Store unit tests with the postgres feature
run: cargo test -p areev-store --features postgres --lib
# `postgres-tls` is a SEPARATE build, not a superset in test terms: the
# refusal path (STO-E003, "asks for encryption, gets none") only exists
# in the build above, and the verifiers only exist in this one. Its
# handshake tests need no database — they stand up a throwaway CA and a
# fake Postgres on loopback — so they belong wherever the feature
# compiles.
- name: Store unit tests with the postgres-tls feature
run: cargo test -p areev-store --features postgres-tls --lib
# The workspace clippy job lints default features only; without this
# the feature-gated backend would accumulate warnings invisibly (and
# docs.rs builds all-features, so it must at least compile clean).
- name: Clippy with the postgres feature
run: cargo clippy -p areev-store -p areev-conformance --features postgres --all-targets -- -D warnings
- name: Clippy with the postgres-tls feature
run: cargo clippy -p areev-store -p areev-conformance --features postgres-tls --all-targets -- -D warnings
# The bindings against a REAL postgres — the deployment surface the
# server tier exists for. (The python/node jobs have no database, so
# their postgres tests skip there; this is where they run.)
- name: Python bindings against postgres
run: |
python -m venv .venv
.venv/bin/pip install maturin pytest
VIRTUAL_ENV=$PWD/.venv .venv/bin/maturin develop -m crates/areev-py/Cargo.toml
.venv/bin/pytest crates/areev-py/tests/ -q
- name: Node bindings against postgres
working-directory: crates/areev-js
run: |
npm install
npx napi build --platform
node --test __test__/smoke.mjs __test__/pg_smoke.mjs
clippy:
name: clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: actions/setup-python@v7
with:
python-version: "3.11"
- uses: Swatinem/rust-cache@v2
- name: Clippy
run: cargo clippy --workspace --all-targets -- -D warnings
# The scheduler core's purity is enforced mechanically (governed-agents
# §4): its dependency tree may not contain a clock, randomness, or IO
# crate — one SystemTime::now() silently invalidates deterministic
# replay. `cargo deny` cannot ban std, so: (a) the dep tree is grepped
# for the crates that would smuggle ambient authority in, and (b) the
# crate-local clippy.toml disallowing SystemTime::now/Instant::now may
# never be silenced with an allow().
- name: areev-run-core purity (no clock/rand/net in the dep tree)
run: |
set -e
BAD=$(cargo tree -p areev-run-core -e normal --prefix none \
| awk '{print $1}' | sort -u \
| grep -E '^(rand|rand_core|getrandom|chrono|time|tokio|ureq|reqwest|hyper|mio)$' || true)
if [ -n "$BAD" ]; then
echo "areev-run-core dependency tree contains ambient-authority crates:"; echo "$BAD"; exit 1
fi
- name: areev-run-core purity (disallowed_methods never silenced)
run: |
if grep -rn "allow(clippy::disallowed_methods)" crates/areev-run-core/src; then
echo "the purity lint may not be silenced in the scheduler core"; exit 1
fi
# NOTE: `cargo fmt --check` is intentionally NOT gated — the tree is not
# uniformly rustfmt-formatted by design (see CONTRIBUTING.md). Contributors
# format only the lines they touch.
msrv:
name: msrv
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@1.90.0
- uses: actions/setup-python@v7
with:
python-version: "3.11"
- uses: Swatinem/rust-cache@v2
- name: Build on minimum supported Rust version
run: cargo build --workspace --locked
docs:
name: doc
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-python@v7
with:
python-version: "3.11"
- uses: Swatinem/rust-cache@v2
- name: Build API docs
run: cargo doc --workspace --no-deps
coverage:
name: coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: llvm-tools-preview
- uses: actions/setup-python@v7
with:
python-version: "3.11"
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@cargo-llvm-cov
- name: Measure coverage
run: cargo llvm-cov --workspace --lcov --output-path lcov.info
# Three gates: the committed figure has not drifted (the README quotes
# it), the scored set is above its global floor, and no individual crate
# has slipped below its own floor. Per-crate is the point — one workspace
# number lets a regression in one crate hide behind a gain in another.
- name: Coverage figure is current and every crate is above its floor
run: python3 scripts/coverage.py --lcov lcov.info --check
- name: Upload coverage report
uses: actions/upload-artifact@v7
with:
name: coverage-lcov
path: lcov.info
if-no-files-found: error
python:
name: python bindings
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-python@v7
with:
python-version: "3.11"
- uses: Swatinem/rust-cache@v2
- name: Build the extension module + run pytest
run: |
python -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip maturin pytest
maturin develop -m crates/areev-py/Cargo.toml
pytest crates/areev-py/tests/ -v
# The Hermes MemoryProvider plugin needs exactly what this job already
# built — a maturin `areev` in .venv — and nothing else. It used to
# ride the `adapters` job's venv for that reason; that job left with
# the adapters (AreevAI/areev-adapters), the plugin did not.
- name: Hermes provider smoke
run: |
. .venv/bin/activate
python examples/hermes/test_provider.py
agent-example:
# The keyless floor, enforced. `examples/agents/` promises every agent
# example runs end to end with no credential, no network and no model
# key — both acts, smoke then improve — and that where an agent ships
# more than one language stack, all of them mint the identical
# content-addressed plan (created_at is pinned; run-smokes.sh compares
# the hashes). `invoice-to-accounting` ships Python + TypeScript + Rust;
# the other agents are Python, and run-smokes.sh skips a stack an agent
# does not have before it ever consults REQUIRE, so adding a language is
# a wrapper plus an agent file and needs no edit here.
# An untested example in the flagship repo is wrong at exactly the
# moment somebody is evaluating, so the promise is a job, not a README
# claim — the SAME entry point a developer runs locally before pushing
# (docs: examples/agents/docs/testing.md).
name: agent examples (keyless, every stack)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-python@v7
with:
python-version: "3.11"
- uses: actions/setup-node@v7
with:
node-version: "22"
- uses: Swatinem/rust-cache@v2
- name: Build the Python binding into a venv (maturin)
run: |
python -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip maturin
maturin develop --release -m crates/areev-py/Cargo.toml
- name: Build the Node binding (napi)
run: cd crates/areev-js && npm ci && npm run build
# Both acts of every agent, in all three stacks, plus the
# cross-language plan-hash comparison. CARGO_TARGET_DIR points the
# detached Rust stack at the cached workspace target so its path deps
# do not recompile from scratch.
- name: Every agent, every language — smoke + improve
run: |
. .venv/bin/activate
CARGO_TARGET_DIR="$PWD/target" REQUIRE="python typescript rust" \
examples/agents/run-smokes.sh
tls:
# The non-default native-TLS feature (the recorded rustls exception):
# round-trip + no-plaintext-downgrade, plus a clean feature build.
name: tls feature
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo test -p areev-server --features tls
- run: cargo check -p areev --features tls
node:
name: node binding (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-node@v7
with:
node-version: "22"
- uses: Swatinem/rust-cache@v2
# areev-js is a DETACHED cargo workspace with its own Cargo.lock, so a
# dependency added to any workspace crate it depends on (areev-run,
# areev-store, …) does not reach this lockfile — and Dependabot's cargo
# entry for "/" does not either. It drifts silently, and since
# release-npm.yml builds `--locked`, the drift surfaces as a failed
# RELEASE rather than a failed build. Assert it here instead.
# Fix: `cd crates/areev-js && cargo metadata >/dev/null` and commit.
- name: Assert the areev-js lockfile is current
working-directory: crates/areev-js
run: cargo metadata --locked --format-version 1 > /dev/null
- name: Build the native addon + run smoke test
working-directory: crates/areev-js
run: |
npm ci
npx napi build --platform --release -- --locked
node --test __test__/smoke.mjs __test__/pg_smoke.mjs
# `index.js` / `index.d.ts` are generated by `napi build`, but they are
# ALSO the files npm ships (package.json `main`/`types`/`files`) — and
# release-npm.yml's publish job never regenerates them: it publishes them
# straight from the checkout, since the build legs upload only `*.node`.
# A stale committed copy therefore ships a loader that does not match the
# toolchain that built the binaries, and nothing else would catch it.
# (This is why they are tracked rather than gitignored — gitignoring them
# would publish a package whose `main` does not exist.)
# Regenerate with `npx napi build --platform --release` and commit.
# napi's output is host-independent, so this holds on every matrix leg.
- name: Assert the generated binding loader is committed
working-directory: crates/areev-js
run: git diff --exit-code -- index.js index.d.ts
# areev-sandbox is a standalone package, deliberately outside the workspace
# (it carries wasmi). `cargo test --workspace` never touches it, so it needs
# its own job or a change here can break it silently — the same reason
# areev-js has one.
sandbox:
name: sandbox (wasm tier C)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- name: build
run: cargo build --manifest-path areev-sandbox/Cargo.toml
- name: test
run: cargo test --manifest-path areev-sandbox/Cargo.toml
- name: clippy
run: cargo clippy --manifest-path areev-sandbox/Cargo.toml --all-targets -- -D warnings
# The version lives in five places and only one of them is inherited
# (`version.workspace`). maturin reads pyproject.toml, npm reads
# package.json, and the GENERATED areev-js/index.js hard-codes it in every
# platform arm. Both drift modes have shipped: a workspace-only bump makes
# the publish workflows skip-existing over the released version (a green run
# that ships nothing), and a package.json bump without regenerating index.js
# breaks require() for anyone with NAPI_RS_ENFORCE_VERSION_CHECK set. Catch
# it on the PR that introduces it, not on release day.
# The self-improvement bench's keyless floor. `--mock` swaps the live model
# for a deterministic agent, so this asserts the PLUMBING of the A/B/A/B
# causal claim — lessons applied raise held-out success, rollback returns it
# to baseline, re-apply recovers it, and rollback really does empty the
# LESSONS prompt section. It is never a learning claim (no model runs); the
# live numbers come from `--agent-cmd` and are published with transcripts.
# `--arms` extends the same floor to the passive-memory providers: keyless
# throughout (m-llm summarizes with the mock summarizer, no API key), and
# PLUMBING-ONLY by construction — the deterministic agent complies with any
# context it is handed, so these arms prove a provider reached the prompt
# and can never rank curation against retrieval. `--workers 4` is the
# parallel path; it must emit byte-identical output to `--workers 1`, which
# `cargo test -p areev-bench` pins separately.
selfimprove:
name: selfimprove A/B/A/B + passive arms (mock)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: A/B/A/B shape holds under the deterministic agent
run: |
cargo run -p areev-bench --bin selfimprove_aba -- \
--workdir "$RUNNER_TEMP/aba" --seed 1 --mock --assert-shape \
--arms m-steel,m-all,m-llm --workers 4
# The governed-only invocation is what SELFIMPROVE.md's "Live pilot"
# block tells a reader to run, and it is a different code path from the
# one above (no providers, no arm bookkeeping). Exercised keylessly here
# so the documented command cannot rot.
- name: The governed-only path still runs standalone
run: |
cargo run -p areev-bench --bin selfimprove_aba -- \
--workdir "$RUNNER_TEMP/aba-governed" --seed 1 --mock --assert-shape
# The loop+LLM arm: the canned loop-LLM authors a lesson, the scripted
# review applies it, rollback removes it, re-apply restores it — the
# authored-lesson lifecycle asserted keylessly (the shape floor, never
# a learning claim; the canned lesson deliberately cannot move the
# mock agent's rates).
- name: The loop+LLM arm applies and restores an authored lesson
run: |
cargo run -p areev-bench --bin selfimprove_aba -- \
--workdir "$RUNNER_TEMP/aba-llm" --seed 1 --mock --mock-llm \
--llm-lessons --assert-shape
# The committed runs are evidence, not decoration: every number in
# RESULTS.md is recomputed from the transcripts that shipped with it,
# and MANIFEST.md's checksums are re-derived, so a published file that
# is renamed or overwritten fails the build instead of review.
- name: Published runs match their own transcripts
run: |
python3 crates/areev-bench/scripts/verify_run.py \
crates/areev-bench/results/selfimprove-3seed-qwen3-30b-2026-08-26
python3 crates/areev-bench/scripts/verify_run.py \
crates/areev-bench/results/selfimprove-llmarm-3seed-qwen3-30b-2026-08-30
python3 crates/areev-bench/scripts/verify_run.py \
crates/areev-bench/results/selfimprove-2x2-qwen3-30b-2026-08-30
# The cross-configuration comparison computes published numbers too, so
# its pairing and discordant-count logic is checked on synthetic runs —
# a silent break there would corrupt a claim, not fail a build.
- name: The cross-configuration statistics tool agrees with itself
run: python3 crates/areev-bench/scripts/aba_arm_stats.py --selftest
versions:
name: versions agree
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- run: python3 scripts/check_versions.py
# README.md quotes repository quality numbers (test vs source lines, test
# count, error codes) from committed artifacts. Regenerate and diff, so the
# published figures cannot quietly drift away from the tree.
stats:
name: repo stats current
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Verify the committed stats match the tree
run: python3 scripts/repo_stats.py --check
- uses: actions/upload-artifact@v7
with:
name: repo-stats
path: |
docs/repo-stats.html
docs/repo-stats.json
docs/assets/repo-stats-*.svg