-
Notifications
You must be signed in to change notification settings - Fork 0
3341 lines (3287 loc) · 183 KB
/
Copy pathci.yml
File metadata and controls
3341 lines (3287 loc) · 183 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
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
CARGO_TERM_COLOR: always
# #910: where scripts/oracle_run.py appends one JSON record per oracle it
# drives. Workflow-level so no oracle job can forget it; jobs that run no
# oracle simply never create the file.
ORACLE_EVIDENCE_JSONL: oracle-evidence.jsonl
jobs:
test:
name: Test
# NOTE: stays on ubuntu-latest for now. It originally moved here from
# [self-hosted, linux, x64, rust-cpu] because z3-sys's C++ build exhausted
# the smithy runners' temp disk — that pressure is gone (#553: synth-verify
# defaults to the pure-Rust ordeal engine, z3 is behind the off-by-default
# `z3-solver` feature), so moving back is a possible follow-up.
runs-on: ubuntu-latest
# Outer wall-clock guard (#849). This job ran 4-6 h and timed out on the
# GitHub default on EVERY run for three days because nothing anywhere
# bounded a solver query. The per-query deadline in synth-verify
# (`SYNTH_ORDEAL_DEADLINE_MS`, #848) is the inner floor; this is the outer
# one, so a future cliff costs one red job in <1 h, not a burned day.
timeout-minutes: 60
env:
RUSTFLAGS: -Dwarnings
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- name: Install llvm-dwarfdump (independent DWARF parser gate, #394)
# The `--debug-line` DWARF gate's Oracle I
# (emitted_dwarf_verifies_with_llvm_dwarfdump_394) validates synth's
# emitted `.debug_info`/`.debug_line` with the INDEPENDENT LLVM parser
# (`llvm-dwarfdump --verify`), not gimli::read. That test FAILS (never
# skips) when the tool is absent, so it must be present here. The `llvm`
# package provides the unversioned `llvm-dwarfdump` on PATH.
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends llvm
llvm-dwarfdump --version
- name: Cache Cargo dependencies
uses: actions/cache@v6
with:
path: |
~/.cargo/registry
~/.cargo/git
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Run tests
# Full workspace, synth-verify included (#553 steps 3/4): with ordeal as
# the default solver there is no z3-sys / C++ build on the default
# feature set, so the long-standing `--exclude synth-verify` (the #306
# disk-exhaustion workaround) is gone. The z3-solver feature path is
# covered by the dedicated `Z3 Verification` differential job below.
run: cargo test --workspace
- name: WCET bound gate (#778 — sound static WCET incl. phase-3/4/5)
# The soundness gate for --emit-wcet: bound >= actual on loop-free +
# const-loop fixtures, EXACT-literal composed bounds over the direct call
# graph (leaf->mid->root; a callee in a proven loop counted trip×), and the
# decline-honesty matrix — recursion (`recursion`), indirect
# (`indirect-call`), external/import (`call`), and declined-callee
# (`callee-unbounded`) still LOUD-DECLINE. Phase 4 (#49): a masked
# single-self-call chain with a VERIFIED depth hint is BOUNDED (derived
# depth 15, frame_count 16, 752 cyc); a too-low hint / tree (fib) / uncapped
# countdown / mutual recursion STAY declined with the specific machine
# reason (hint-below-derived-depth / hint-unverifiable-recursion). Phase 5
# (#778): a DATA-DEPENDENT masked-ceiling loop bound (`i REL (x & K)`) with a
# VERIFIED loop_bounds hint is BOUNDED (derived trip = both-endpoints max,
# source `mask-ceiling`); the count-DOWN case's worst case is the rhs=0
# endpoint (a single-endpoint seed would undercount); unhinted / too-low /
# UNMASKED `i < param` STAY declined (`loop` + hint-below-derived-trip /
# hint-unverifiable-induction). Covered by `cargo test --workspace` above;
# run explicitly so a WCET-soundness regression is unmissable in the log.
run: cargo test -p synth-cli --test wcet_bound_gate
- name: Space-consistency invariant (#77 — execution+memory space)
# The #77 checked property: an inconsistent execution/memory-space
# pairing (bare-metal + OS-mapped, hosted + physical, FP-requiring space
# on an FPU-less core) is REJECTED by synth_memory::space::validate().
# Red-first unit tests (both directions of each invariant); covered by
# `cargo test --workspace` above, run explicitly so a regression in the
# space-consistency model is unmissable in the log.
run: cargo test -p synth-memory --features std 'space::'
- name: P3 async intrinsic honest-degradation gate (#80)
# The #80 gate: the ONE lowered async op (error-context.drop) compiles
# to a field-name BL call site; every declined intrinsic (error-context.new
# /.debug-message, stream, future, waitable-set, task) LOUD-DECLINES the
# compile by name. Runs the classifier unit tests + the CLI end-to-end
# gate so an accidental silent-lower of a declined op is unmissable.
run: |
cargo test -p synth-core --lib async_intrinsics
cargo test -p synth-cli --features riscv --test async_intrinsics_gate
clippy:
name: Clippy
# Same runner note as Test — the z3-sys disk pressure that forced
# ubuntu-latest is gone (#553); moving back to smithy is a follow-up.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Cache Cargo dependencies
uses: actions/cache@v6
with:
path: |
~/.cargo/registry
~/.cargo/git
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Run clippy
run: cargo clippy --workspace --all-targets -- -D warnings
fmt:
name: Format
runs-on: [self-hosted, linux, x64, light]
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Cache Cargo dependencies
uses: actions/cache@v6
with:
path: |
~/.cargo/registry
~/.cargo/git
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Check formatting
run: cargo fmt --all -- --check
pin-sweep:
name: Version Pin Sweep
runs-on: [self-hosted, linux, x64, light]
steps:
- uses: actions/checkout@v7
# Issue #145: fail at PR time if [workspace.package].version drifts from
# any intra-workspace path-dep `version =` pin or MODULE.bazel — the
# v0.7.0-class desync that breaks release.yml + publish at tag-push time.
- name: Check intra-workspace version pins
run: python3 scripts/check_version_pins.py
claim-check:
name: Claim Check
# claim-verification gate: README/CLAUDE.md/coq/STATUS.md load-bearing
# claims (proof counts, "verified" wording, DSL rule coverage, trusted-base
# sizes) are pinned in claims.yaml and RE-DERIVED from source here — a
# hand-maintained number that drifts from the tree fails the build.
# When a proof/rule lands, update the docs AND claims.yaml together.
#
# Since the claim-surface extension, the same script run ALSO
# * staleness-gates the machine-derived artifacts/status.json (the README
# badges' data source) and the GENERATED docs/status/FEATURE_MATRIX.md —
# regenerate with `python3 scripts/claim_check.py claims.yaml
# --emit-status` and commit the result;
# * closes the README linked-doc surface (every relative .md link must be
# claim-covered, generated, or allowlisted with a rationale).
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Ensure PyYAML
run: python3 -c "import yaml" || python3 -m pip install --user pyyaml
- name: Re-derive documentation claims (+ generated-artifact staleness + link surface)
run: python3 scripts/claim_check.py claims.yaml
# #867: coverage OF the ISA semantics model. Re-derives
# artifacts/model-coverage.json (static heuristic, documented in the
# script) and fails if the committed artifact is stale or hand-edited;
# prints the COMPLEMENT — modelled ISA behaviours no proof exercises —
# loudly in the job log. The tier counts flow into status.json via
# claims.yaml, so this step is what keeps those numbers honest.
- name: Model-coverage artifact freshness + uncovered-complement report (867)
run: python3 scripts/model_coverage_audit.py --check
# #890: the ORACLE-WIRING gate. scripts/repro/*.py are the execution
# oracles; 69 of 150 were referenced by no workflow at all and nothing
# distinguished "manual by design" from "forgotten". Every script now
# declares `# ci-status: wired|manual|unwired`, a `wired` declaration is
# VERIFIED against this file (the "green board, inert gate" defect), and
# an undeclared script is a hard failure so new oracles must CHOOSE.
#
# This gate lives in the already-REQUIRED claim-check job on purpose: a
# brand-new job is not a required context on main, so it could be red for
# weeks without blocking anything — which is the same failure mode the
# gate exists to kill.
#
# ANTI-VACUITY: it must not become the thing it polices.
# * `set -euo pipefail` EXPLICITLY. A bare `| tee` reports tee's status
# (always 0), and — found by mutating this very step — `pipefail`
# WITHOUT `-e` is not enough either: the script's status is its LAST
# command's, so the inert-gate mutation greened the step while the
# gate itself printed FAIL and exited 1. Actions' default shell is
# `bash -e`, but this step does not lean on that default.
# * The verdict is re-derived from the summary the gate WROTE, not from
# exit 0: a non-empty script set, a non-zero wired count, zero
# undeclared, zero wired-but-unreferenced, and zero failures.
# #910 adds `--min-emulation-floor`: the summed `# ci-checks: emulations`
# floors, i.e. the emulator entries the wired oracle surface ASSERTS on
# every CI run. It is a RATCHET (direction: up) and a separate population
# from the `Rust-test Line Coverage` percentage — which cannot see any of
# it, because the oracles run the compiler as an uninstrumented
# subprocess from other jobs. The literal below is pinned in claims.yaml
# against the same number in scripts/repro/ORACLE_WIRING.md, so the gate
# and the doc cannot drift apart.
# RQ-56-CITE / #911 — the SAME shape as the oracle-wiring gate below, on
# the artifacts surface, which never had one. `cargo test -- <filter>`
# exits 0 when the filter matches NOTHING, so a rivet artifact could claim
# verification by a test that does not exist. v0.55.0 shipped two such
# citations; a post-release sweep found them, no gate did.
#
# Scoped to CLAIMING statuses (implemented/verified/accepted): under
# draft/proposed a forward-looking citation is a plan, and flagging it
# would make this gate noisy. A noisy gate gets ignored — that is how
# codecov/patch stopped being read (#923).
- name: Artifact citation gate — no artifact claims a test that does not exist (911)
run: |
set -euo pipefail
python3 scripts/artifact_citation_check.py | tee /tmp/artifact-cites.log
# Non-vacuity: assert it examined a NON-EMPTY population rather than
# merely exiting 0 over nothing.
grep -qE '^artifact citations: [1-9][0-9]* cited filters over [1-9][0-9]* test names' \
/tmp/artifact-cites.log
- name: Oracle wiring gate — every repro script declares a CI status (890)
run: |
set -euo pipefail
python3 scripts/oracle_wiring_check.py --json /tmp/oracle-wiring.json --list \
--min-emulation-floor 295421 \
| tee /tmp/oracle-wiring.log
python3 - <<'PY'
import json, sys
s = json.load(open("/tmp/oracle-wiring.json"))["summary"]
bad = [k for k, v in (("total<100", s["total"] < 100),
("wired==0", s["wired"] < 1),
("undeclared", s["undeclared"]),
("wired_unreferenced", s["wired_unreferenced"]),
("checks_undeclared", s["checks_undeclared"]),
("emulation_floor<100000",
s["emulation_floor"] < 100000),
("failures", s["failures"])) if v]
if bad:
sys.exit(f"oracle-wiring gate VACUOUS or DRIFTED {bad}: {s}")
print(f"oracle-wiring gate is non-vacuous: it classified {s['total']} "
f"scripts, {s['wired']} of them wired, {s['manual']} manual, "
f"{s['unwired']} unwired-debt, 0 inert, and every wired oracle "
f"declares a check floor ({s['emulation_floor']} emulator "
f"entries asserted across "
f"{s['checks_by_mode']['emulations']['scripts']} of them).")
PY
# The GitHub About surface (description + topics) is pinned verbatim in
# claims.yaml's repo_metadata section. API/network failure = LOUD SKIP
# (exit 0) so a flake can't block merges; a real mismatch = red.
- name: Repo metadata check (description + topics)
env:
GITHUB_TOKEN: ${{ github.token }}
run: python3 scripts/repo_metadata_check.py claims.yaml
verify:
name: Z3 Verification
# #553 steps 3/4: this is now the ONLY job that pulls in z3 (feature
# `z3-solver`) — the trusted-reference differential oracle, not the default
# engine. SYNTH_SOLVER_DIFF=1 routes every query through BOTH engines
# (ordeal + Z3); any decided-verdict disagreement is a hard error. Do not
# delete this job: it is the standing cross-check that keeps the pure-Rust
# default honest.
# Links the SYSTEM libz3 (`sudo apt-get install -y libz3-dev`): the
# `z3-solver` feature no longer enables `static-link-z3`, so z3-sys links
# `-lz3` (via its `#[link(name="z3")]`) against the apt-installed shared
# library instead of compiling z3 from source OR downloading a prebuilt —
# this removes the flaky `z3-sys build.rs` GitHub download (HTTP 403/502)
# that blocked releases. Stays on ubuntu-latest for `sudo` (smithy runners
# have no sudo); move once libz3-dev is in the smithy toolchains role.
runs-on: ubuntu-latest
# Outer wall-clock guard (#849) — this job hung for 4-6 h alongside `test`
# on the same unbounded 64-bit bvsrem/bvurem queries. Inner floor: the
# per-query deadline (`SYNTH_ORDEAL_DEADLINE_MS`, #848), applied to the Z3
# oracle too.
timeout-minutes: 45
env:
SYNTH_SOLVER_DIFF: "1"
# Point z3-sys bindgen at the apt header (libz3-dev installs it here);
# without this it would fall back to the crate's wrapper.h + pkg-config
# include path. Set explicitly so the system-link path is unambiguous.
Z3_SYS_Z3_HEADER: /usr/include/z3.h
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- name: Cache Cargo dependencies
uses: actions/cache@v6
with:
path: |
~/.cargo/registry
~/.cargo/git
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Install system Z3 (shared lib + headers; no from-source/download)
run: sudo apt-get update && sudo apt-get install -y libz3-dev
- name: Run verification tests (differential ordeal vs Z3)
run: cargo test -p synth-verify --features z3-solver,arm
- name: Run comprehensive verification (differential ordeal vs Z3)
run: cargo test -p synth-verify --test comprehensive_verification --features z3-solver,arm
coverage:
# RENAMED from "Code Coverage" (#910). The old name implied whole-system
# coverage; the job measures ONE population — the Rust test suite, in-process,
# under llvm-cov instrumentation.
#
# What it CANNOT see, structurally: the execution differentials. Those spawn
# `$SYNTH` as a SEPARATE, UNINSTRUMENTED process, from OTHER jobs entirely
# (`repro sweep-*`, `trap-semantics oracle`, the per-issue oracles). An
# uninstrumented subprocess emits no profile data, so none of that execution
# reaches this percentage. That is why backend.rs — almost pure ELF emission,
# exercised end-to-end on every differential — reads ~42 % here.
#
# Consequence, stated so nobody has to rediscover it: this number UNDERSTATES
# the testing that exists, and it is NOT a completeness measure. The
# differential population is reported separately and in its own unit —
# asserted executed checks, see scripts/oracle_run.py and the
# `oracle-evidence-ledger` job. Do not add them together.
#
# Not a required status check on `main` (verified before renaming: renaming a
# required context deadlocks every merge until protection is updated).
name: Rust-test Line Coverage (unit + integration only)
needs: [test]
runs-on: [self-hosted, linux, x64, rust-cpu]
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@nightly
- name: Cache Cargo dependencies
uses: actions/cache@v6
with:
path: |
~/.cargo/registry
~/.cargo/git
target/
key: ${{ runner.os }}-coverage-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-coverage-
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@v2.85.10
with:
tool: cargo-llvm-cov
- name: Generate coverage (LCOV)
# synth-verify is no longer excluded (#553): the default feature set is
# pure Rust (ordeal), so its tests run under llvm-cov like any crate.
run: |
cargo llvm-cov --workspace --lcov --output-path lcov.info \
--exclude synth-qemu \
--exclude synth-backend-awsm --exclude synth-backend-wasker
- name: Say what this number does and does not measure (#910)
# The scope note travels WITH the number. Reading the percentage off the
# job summary and reading this are the same act, which is the only way a
# caveat survives being quoted.
run: |
set -euo pipefail
test -s lcov.info
{
echo "### Rust-test line coverage — SCOPE (#910)"
echo
echo "Measures: \`cargo llvm-cov --workspace\` — the **Rust test suite**, in-process."
echo
echo "Does **not** measure: the execution differentials"
echo "(\`scripts/repro/*.py\`). They spawn \`\$SYNTH\` as a separate,"
echo "UNINSTRUMENTED process, from other jobs — no profile data reaches"
echo "this figure. \`backend.rs\` reads ~42 % here while being exercised"
echo "end-to-end by nearly every differential."
echo
echo "So this number **understates** the testing that exists, and is"
echo "**not** a completeness measure. The differential population is"
echo "reported separately, in executed checks, by the"
echo "\`oracle evidence ledger\` job. **Do not add them together.**"
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload to Codecov
uses: codecov/codecov-action@v7
with:
files: lcov.info
fail_ci_if_error: false
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
kani:
name: Kani Verification
# Stays on ubuntu-latest: Kani-CBMC bundle is not yet provisioned
# in the smithy toolchains role (tracked in playbook out-of-scope
# table). Move once smithy ships kani-verifier + CBMC.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install Kani
run: cargo install --locked kani-verifier && cargo kani setup
- name: Run Kani proofs
run: cargo kani -p synth-backend --tests
timeout-minutes: 30
rivet:
name: Rivet Validation
runs-on: [self-hosted, linux, x64, rust-cpu]
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
# Cache the rivet-cli BINARY keyed on its pinned version, NOT on Cargo.lock.
# The old key (hashFiles Cargo.lock) invalidated on every release version
# bump, forcing a full rebuild of rivet-cli's HiGHS C++ dependency — which
# filled a self-hosted runner's disk and red-failed the v0.14.0 release
# (No space left on device). Version-keying means a Cargo.lock change no
# longer triggers the rebuild; the cached binary is reused.
- name: Cache rivet-cli binary
uses: actions/cache@v6
with:
path: ~/.cargo/bin/rivet
key: ${{ runner.os }}-rivet-cli-v0.23.0
# PIN rivet to a release tag (was `--branch main`, unpinned). Unpinned, an
# upstream schema/behaviour bump silently reddened the gate on unchanged
# artifacts (rivet 0.15.0 promoted a WARN→ERROR, #229). v0.23.0 (adds configurable
# release-readiness ready-when/coverage, check verification-evidence, trace-results) is validated clean on this repo's
# artifacts before pinning — the exact CI gate below run locally under
# v0.23.0 gives non-xref ERROR 0, same as v0.22.0. Skip
# the (expensive) build entirely on a cache hit.
- name: Install rivet (pinned v0.23.0)
run: |
if ! rivet --version 2>/dev/null | grep -q "0.23.0"; then
cargo install --force --git https://github.com/pulseengine/rivet --tag v0.23.0 rivet-cli
fi
- name: Validate artifacts
# THIS GATE HAD TWO HOLES, and v0.55 (#893) found them by finding two
# OURS-not-theirs errors sitting in a green tree. Both are fixed below;
# both fixes are red-first — replay this step's logic against the
# pre-fix artifacts and it reports 2, against the fixed ones 0.
#
# (1) `^ ERROR:` anchored on two-space-indented lines. rivet prefixes
# SOME diagnostics with the source file instead
# (`gale-integration.yaml: ERROR: …`), so that whole class was
# invisible here — including "artifact id X is declared more than
# once … the second definition silently overwrites the first",
# which is how GI-FPU-002 could be `implemented` in one file and
# `proposed` in another with this job green. Now every line
# containing `ERROR:` is considered.
# (2) The exemption `targets '.*:.*' which does not exist` exempted ANY
# target containing a colon — including `synth:396`, i.e. OUR OWN
# repo prefix. A `synth:`-prefixed target that does not resolve is
# not a cross-repo link into an un-rivet'd sibling; it is a broken
# link in our own graph (there, an issue NUMBER written where an
# artifact id belongs). Exemption is now "any prefix EXCEPT ours".
#
# Deliberately still exempt: kiln/gale/sigil/scry/jess/witness/loom/meld
# targets, because those repos have no rivet project at the configured
# path and the errors are unresolvable from this repo. Not an allowlist —
# the rule is structural ("a foreign prefix"), so a new sibling repo does
# not need this file edited, and our own prefix can never slip back in.
run: |
sed -i '/^externals:/,$d' rivet.yaml
rivet validate 2>&1 | tee /tmp/rivet-output.txt
grep -E "ERROR:" /tmp/rivet-output.txt > /tmp/rivet-errors.txt || true
# Everything that is NOT a foreign-prefix broken link, ...
grep -vE "targets '[A-Za-z0-9_.-]+:[^']*' which does not exist" /tmp/rivet-errors.txt \
| grep -v "missing '.*' link to" > /tmp/rivet-ours.txt || true
# ... plus foreign-looking links that actually carry OUR prefix.
grep -E "targets 'synth:[^']*' which does not exist" /tmp/rivet-errors.txt >> /tmp/rivet-ours.txt || true
NON_XREF=$(grep -c . /tmp/rivet-ours.txt || true)
if [ "${NON_XREF:-0}" -gt 0 ]; then
echo "::error::Found $NON_XREF rivet validation errors that are OURS (not cross-repo):"
cat /tmp/rivet-ours.txt
exit 1
fi
if [ -s /tmp/rivet-errors.txt ]; then
echo "::warning::Cross-repo link errors present (expected — external projects need rivet init)"
fi
- name: Check coverage
run: rivet coverage
bazel:
name: Bazel Build & Proofs
# Stays on ubuntu-latest: needs Nix + Bazel + Rocq via Bazel
# (none provisioned on smithy; tracked in playbook out-of-scope
# table for both Bazel and Rocq).
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v7
- name: Install Nix
uses: cachix/install-nix-action@v31
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: Setup Bazel
uses: bazel-contrib/setup-bazel@0.19.0
with:
bazelisk-cache: true
disk-cache: ${{ github.workflow }}
repository-cache: true
- name: Build Rust via Bazel
run: bazel build //crates:synth
- name: Verify Rocq proofs
run: bazel test //coq:verify_proofs
- name: Run Renode emulation tests
run: bazel test //tests/renode/... --test_tag_filters=wast || [ $? -eq 4 ]
timeout-minutes: 10
cmp-select-oracle:
name: cmp-select two-move execution oracle
# VCR-ORACLE-001 (#242, #428): EXECUTE the cmp->select two-move arm under
# unicorn (faithful Thumb-2 IT-block predication) and diff flag-off vs flag-on
# vs wasmtime. This is the runtime validation gale's #428 measurement showed no
# real fixture provides (the two-move arm is reachable but runtime-dead on real
# code). Isolated job: unicorn/wasmtime are pip-installed here ONLY, so the main
# `cargo test --workspace` gate is NOT taxed with a C-library build graph.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- name: Cache Cargo dependencies
uses: actions/cache@v6
with:
path: |
~/.cargo/registry
~/.cargo/git
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Build synth
run: cargo build -p synth-cli
- uses: actions/setup-python@v7
with:
python-version: "3.x"
- name: Install emulation deps
run: pip install wasmtime unicorn pyelftools
- name: Run two-move execution oracle
run: python scripts/oracle_run.py scripts/repro/cmp_select_two_move_differential.py
# #910: close the job with what it EXECUTED. Asserts every oracle
# met its declared floor AND that the expected number of them
# reported at all — a step deleted, commented out, or skipped by an
# early exit leaves the ledger short, which is a red job rather than
# a quietly smaller number. Also writes the measured totals to the
# step summary, per unit, never summed.
- name: Differential evidence ledger (#910)
if: always()
run: |
set -euo pipefail
python3 scripts/oracle_evidence.py "$ORACLE_EVIDENCE_JSONL" --min-oracles 1
provenance-gate:
name: synth-provenance-v1 reconciliation gate (#396)
# VCR-DEC-003 (#396, witness#130): the source-to-object branch-transformation
# map. Compiles a fixture that FOLDS (select->IT predication) and SPLITS
# (br_table) branches, then reconciles every REAL object conditional branch
# (from the BranchMap side-table) back to its source WASM condition. The gate
# is the non-vacuous check that a folded/eliminated condition is recorded AS
# folded with its object realization, never silently dropped. Runs in the
# workspace test set too, but named here so a provenance regression is a
# visible, self-describing red — witness's MC/DC object-certification depends
# on this contract holding.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- name: Cache Cargo dependencies
uses: actions/cache@v6
with:
path: |
~/.cargo/registry
~/.cargo/git
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Run reconciliation gate + emitter unit tests
run: |
cargo test -p synth-cli --test provenance_reconciliation_396
cargo test -p synth-core --lib provenance
aarch64-oracle:
name: aarch64 backend execution + decline oracle (#538 m2–m4)
# #538: the host-native A64 backend, oracle-gated per milestone. EXECUTE
# every covered op's emitted A64 `.text` under unicorn (UC_ARCH_ARM64) and
# diff vs wasmtime ground truth — the "third backend = third oracle"
# property: m1 integer core, m2 full i32+i64 ALU, m3 scalar floats
# (NaN-ordering compares, promote/demote, converts, reinterprets), m4 the
# #709 boundary table (domain-guarded trapping trunc: every case where
# WASM traps must TRAP under emulation, in-range must match bit-exactly)
# + min/max NaN/±0 matrix + copysign. #851 added div/rem (SDIV/UDIV+MSUB
# with WASM ÷0 + INT_MIN/-1 trap guards, execution-verified), popcnt (SIMD
# CNT/ADDV), and f64<->i64 reinterpret. v0.54 L2 completes the SCALAR float
# surface — rounding (FRINT), f32/f64 load/store (bounds-checked), i64->
# float converts, and the DOMAIN-GUARDED trapping i64-target truncations —
# so the decline-matrix probe moved on to the STRUCTURAL declines. v0.54 L3
# then landed call_indirect + globals and v0.55 L6 (VCR-A64-CF-001)
# `br_table` + value-carrying block/loop/if, so what the probe asserts today
# is the NARROWER residue: param writes in a LEAF function, bulk memory,
# SIMD, a `br_table` past 16 targets or with value-carrying targets, a block
# type with params / multi-value results, and a non-leaf FLOAT param. Each
# entry LEAVES this list the day its lowering lands — asserting a decline
# for a capability that now ships is the same doc-honesty defect as claiming
# one that does not.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- name: Cache Cargo dependencies
uses: actions/cache@v6
with:
path: |
~/.cargo/registry
~/.cargo/git
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Build synth
run: cargo build -p synth-cli
- uses: actions/setup-python@v7
with:
python-version: "3.x"
- name: Install emulation deps
run: pip install wasmtime unicorn pyelftools
- name: Run m1 integer-core execution oracle
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/aarch64_add_538_differential.py
- name: Run m2 broadened i32+i64 ALU execution oracle
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/aarch64_m2_538_differential.py
- name: Run m3 scalar-float execution oracle
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/aarch64_m3_floats_538_differential.py
- name: Run m4 trunc boundary-table + min/max NaN/±0 execution oracle
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/aarch64_m4_trunc_minmax_538_differential.py
- name: Run cf void-block br/br_if execution oracle (both branch edges)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/aarch64_cf_538_differential.py
- name: Run #851 linear-memory load/store execution oracle
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/aarch64_mem_851_differential.py
- name: Run #851 div/rem trap + popcnt + reinterpret execution oracle (÷0 + INT_MIN/-1)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/aarch64_divrem_851_differential.py
- name: Run #851 non-param locals execution oracle
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/aarch64_locals_851_differential.py
- name: Run #851 control-flow (if/else + loop back-edge + return) execution oracle
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/aarch64_ctrlflow_851_differential.py
- name: Run #865 linear-memory BOUNDS oracle (OOB traps + modes differ + mask/mpu hard-error)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/aarch64_bounds_865_differential.py
- name: Run #851 v0.53 op-surface oracle (select x4 types + wrap/extends + drop/nop + memory.size/grow)
# The VCR-SEL-005 third-backend closes: select (CSEL/FCSEL) incl.
# NaN/-0 carry, wrap/extends with POISONED upper argument bits (the
# AAPCS64 x-view hazard), drop/nop, fixed-memory size/grow parity
# against a min=max module (growth failure is spec-forced there).
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/aarch64_surface_851_differential.py
- name: Run #851 lane L3 GLOBALS execution oracle (emitted .data region + persistence)
# synth EMITS the globals region (a `.data` image carrying the decoded
# initializers) and reaches it `adrp`+`add :lo12:` — no base register,
# no precondition. The harness places `.text` and `.data` on DIFFERENT
# pages and resolves the relocations itself, so a wrong page delta or
# lo12 lands on the wrong bytes. NON-VACUITY: assert a non-zero check
# count (a harness that compiled nothing would otherwise pass silently)
# AND the summary verdict. `set -euo pipefail` (not bare `pipefail`,
# which leaves the step's status as the LAST command's) makes the
# script's own non-zero exit fail the step (#890).
run: |
set -euo pipefail
SYNTH=./target/debug/synth \
python scripts/oracle_run.py scripts/repro/aarch64_globals_851_differential.py | tee globals851.txt
grep -Eq '^[1-9][0-9]* checks across [1-9][0-9]* exported' globals851.txt
grep -q '^RESULT: PASS' globals851.txt
- name: Run #851 lane L3 CALL_INDIRECT execution oracle (§4.4.8 OOB + null + type traps)
# A64's `blr` is TOTAL; WASM §4.4.8 is not. All three traps must fire
# exactly where wasmtime traps — and the structurally-DUPLICATE type
# must NOT trap (the direction an "always trap" lowering would hide).
# NON-VACUITY: assert non-zero counts in BOTH directions, so a run that
# only trapped (or only returned values) fails, plus the summary
# verdict. `set -euo pipefail` makes the script's own non-zero exit fail
# the step (bare `pipefail` would leave the status as the last command's
# — the #890 class).
run: |
set -euo pipefail
SYNTH=./target/debug/synth \
python scripts/oracle_run.py scripts/repro/aarch64_call_indirect_851_differential.py | tee ci851.txt
grep -Eq '^[1-9][0-9]* checks \([1-9][0-9]* trap, [1-9][0-9]* value\)' ci851.txt
grep -q '^RESULT: PASS' ci851.txt
- name: Run v0.54 L2 float-completion oracle (#851 rounding / i64 converts / GUARDED i64 trunc / FP mem)
# The four classes the VCR-SEL-005 third-backend parity gate listed as
# Err(reason). The soundness-critical one is TRAP_TRUNC_I64: A64
# FCVTZ{S,U} SATURATE where WASM §4.3.3 TRAPS, so the harness runs a
# FULL boundary table (both sides of ±2^63 / 2^64, the nearest float
# inside/outside each bound, ±0, ±inf, NaN) and requires every trap
# case to ACTUALLY TRAP. Rounding is compared bit-exactly over a
# halfway table, which is what makes the "FRINTN is ties-to-EVEN"
# claim real rather than assumed.
#
# ANTI-VACUITY (#890). `set -euo pipefail`, NOT bare `pipefail`: with
# pipefail ALONE the step's exit status is that of the LAST command, so
# an oracle that printed FAIL and exited 1 still went GREEN. `-e` is
# what makes the failing pipeline abort the step — and it is stated
# explicitly here rather than left to the runner's default shell flags.
#
# The verdict is then taken from the script's OWN summary line, not
# from exit 0 alone, and the counts are asserted non-collapsed: an
# oracle that quietly stops exercising anything — or that loses the
# out-of-range half of the #709 boundary table, which is this gate's
# entire purpose — must go RED.
run: |
set -euo pipefail
SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/aarch64_float_completion_851_differential.py \
| tee /tmp/a64_floatdiff.log
grep -q '^RESULT: PASS' /tmp/a64_floatdiff.log
grep -Eq '^[0-9]+ wasmtime cases \([0-9]+ trap cases\)' /tmp/a64_floatdiff.log
n=$(sed -n 's/^\([0-9]*\) wasmtime cases.*/\1/p' /tmp/a64_floatdiff.log)
t=$(sed -n 's/^[0-9]* wasmtime cases (\([0-9]*\) trap cases).*/\1/p' /tmp/a64_floatdiff.log)
echo "float-completion oracle ran $n checks, $t of them trap cases"
[ "$n" -ge 300 ]
[ "$t" -ge 40 ]
- name: Run v0.55 L6 br_table + VALUE-CARRYING block/loop/if oracle (VCR-A64-CF-001)
# The two largest entries in the mechanically-derived aarch64 decline
# complement, now lowered. `br_table` is a compare-and-branch CHAIN, so
# a wrong destination is a wrong RESULT: the harness walks the index
# lattice per table (every arm, the index AT the bound, one OVER it, and
# 0xFFFFFFFF — which a SIGNED compare would mis-dispatch, since WASM
# reads the index UNSIGNED), and puts a LOOP HEADER in the same table as
# a forward block end so a wrong branch DIRECTION shows up as a wrong
# trip count. The value-carrying frames are driven down BOTH edges of
# every join, including a frame containing a `bl` — the one soundness
# claim (a call cannot clobber a live reconciliation slot) that nothing
# else executes.
#
# ANTI-VACUITY (#890). `set -euo pipefail`, NOT bare `pipefail`: with
# pipefail ALONE the step's exit status is the LAST command's, so an
# oracle that printed FAIL and exited 1 still went GREEN. The verdict is
# then taken from the script's OWN summary line, and the counts are
# asserted NON-ZERO in both outcome classes — an oracle that stopped
# trapping (or stopped returning values) must go RED. The relocation
# count is asserted too: the harness APPLIES the `R_AARCH64_CALL26`
# relocations itself, and if they vanished the calls would become
# self-branches and the clobber claim would go untested while the gate
# stayed green.
run: |
set -euo pipefail
SYNTH=./target/debug/synth \
python scripts/oracle_run.py \
scripts/repro/aarch64_brtable_blockvals_851_differential.py \
| tee /tmp/a64_brtable.log
grep -q '^RESULT: PASS' /tmp/a64_brtable.log
grep -Eq '^[1-9][0-9]* checks \([1-9][0-9]* trap, [1-9][0-9]* value\) across [1-9][0-9]* exported' /tmp/a64_brtable.log
grep -Eq '\[[1-9][0-9]* direct-call relocations applied\]' /tmp/a64_brtable.log
n=$(sed -n 's/^\([0-9]*\) checks .*/\1/p' /tmp/a64_brtable.log)
echo "br_table/value-carrying oracle ran $n checks"
[ "$n" -ge 60 ]
- name: Run decline-matrix honesty oracle
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/aarch64_m2_decline_538.py
# #910: close the job with what it EXECUTED. Asserts every oracle
# met its declared floor AND that the expected number of them
# reported at all — a step deleted, commented out, or skipped by an
# early exit leaves the ledger short, which is a red job rather than
# a quietly smaller number. Also writes the measured totals to the
# step summary, per unit, never summed.
- name: Differential evidence ledger (#910)
if: always()
run: |
set -euo pipefail
python3 scripts/oracle_evidence.py "$ORACLE_EVIDENCE_JSONL" --min-oracles 15
aarch64-native-matrix:
name: aarch64 native execution matrix (gale #851 acceptance gate)
# gale's standing execution-differential (#851): compiles a broad op set with
# `synth -b aarch64`, executes each ACCEPTED op NATIVELY on this arm64 runner
# (MAP_JIT), and diffs bit-exact vs wasmtime. Exits non-zero ONLY on a
# MISCOMPILE — a declined op is fine (the gate is "no accepted op is WRONG"),
# so every op auto-flips declined→verified the day its lowering lands. macos
# runners are arm64, so this is the real-silicon acceptance gate for the
# host-native backend (the unicorn job above is the host-independent partner).
runs-on: macos-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- name: Cache Cargo dependencies
uses: actions/cache@v6
with:
path: |
~/.cargo/registry
~/.cargo/git
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Build synth
run: cargo build -p synth-cli
- name: Install wasm-tools + wasmtime + llvm (objcopy)
run: brew install wasm-tools wasmtime llvm
- name: Run gale's aarch64 execution matrix (miscompile → non-zero)
# NON-VACUITY GUARD: the matrix DECLINES an op when `.text` extraction
# fails (empty hex), and declined ops count as PASS — so a missing
# objcopy/objdump would make the whole gate green having verified
# NOTHING. Point OBJCOPY at brew's llvm (not on the default PATH) and
# assert the accepted-op count is at least gale's 0.50.x baseline (32).
run: |
out=$(SYNTH=./target/debug/synth \
OBJCOPY="$(brew --prefix llvm)/bin/llvm-objcopy" \
./scripts/repro/aarch64_matrix.sh); rc=$?
echo "$out"
# Primary purpose: a MISCOMPILE makes the matrix exit non-zero. Check
# that explicitly (don't lean on set -e propagating through $(...)).
if [ "$rc" -ne 0 ]; then
echo "aarch64 matrix reported a miscompile/error (rc=$rc)"; exit "$rc"
fi
# Non-vacuity: extraction failure counts every op as declined (=PASS),
# so a broken objcopy/objdump would green the gate having checked
# nothing. Assert at least gale's 0.50.x accepted-op baseline.
acc=$(echo "$out" | sed -n 's/aarch64: \([0-9]*\) ops accepted.*/\1/p')
if [ "${acc:-0}" -lt 32 ]; then
echo "VACUOUS GATE: only ${acc:-0} ops accepted (expected >= 32) — \
objcopy/objdump/synth extraction likely broke"; exit 1
fi
- uses: actions/setup-python@v7
with:
python-version: "3.x"
- name: Install wasmtime python bindings
run: pip install wasmtime
- name: Run #851 direct-call execution differential
# The matrix above is single-function (loads one function's .text) so it
# cannot exercise CALLS. This harness compiles MULTI-function modules,
# resolves the R_AARCH64_CALL26 relocations itself, JITs the linked blob
# (MAP_JIT), and diffs bit-exact vs wasmtime. It asserts every expected
# symbol is present, so a silently-declined caller fails LOUDLY.
run: |
SYNTH=./target/debug/synth \
WASMTOOLS=wasm-tools WASMTIME=wasmtime \
python scripts/oracle_run.py scripts/repro/aarch64_calls_851.py
# #910: close the job with what it EXECUTED. Asserts every oracle
# met its declared floor AND that the expected number of them
# reported at all — a step deleted, commented out, or skipped by an
# early exit leaves the ledger short, which is a red job rather than
# a quietly smaller number. Also writes the measured totals to the
# step summary, per unit, never summed.
- name: Differential evidence ledger (#910)
if: always()
run: |
set -euo pipefail
python3 scripts/oracle_evidence.py "$ORACLE_EVIDENCE_JSONL" --min-oracles 1
trap-semantics-oracle:
name: trap-semantics oracle (#665 unreachable + #666 rem_s)
# #665: wasm `unreachable` must TRAP (WASM §4.4.5) — it was decoder-dropped
# to a no-op on EVERY backend, falling through panic/abort guards. #666:
# rv32 rem_s wrongly carried div_s's INT_MIN/-1 ebreak guard —
# irem_s(INT_MIN,-1) = 0, no trap (§4.3.2). Both EXECUTION-validated under
# unicorn (thumb2 + rv32) against wasmtime ground truth, including the
# non-vacuity direction (a guarded `unreachable` NOT taken runs normally;
# rem_s zero-divisor + div_s overflow traps are KEPT). Isolated job:
# emulation deps pip-installed here ONLY.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- name: Cache Cargo dependencies
uses: actions/cache@v6
with:
path: |
~/.cargo/registry
~/.cargo/git
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Build synth
run: cargo build -p synth-cli
- uses: actions/setup-python@v7
with:
python-version: "3.x"
- name: Install emulation deps
# capstone: the #846 gpio-thin oracle's mask census disassembles .text
run: pip install wasmtime unicorn pyelftools capstone
- name: Install arm-none-eabi-ld (#881 VFP spill oracle)
# binutils only (NOT the full gcc-arm-none-eabi): the #881 oracle links
# its ET_REL object with a REAL ld so the internal `bl` reloc is proven
# to resolve. Skipping the link would make that half of the gate
# vacuous, so install the linker rather than weaken the check.
run: sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends binutils-arm-none-eabi
- name: Run unreachable trap oracle (#665, thumb2 + rv32)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/unreachable_665_differential.py
- name: Run rem_s trap-table oracle (#666, rv32)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/rem_s_666_differential.py
- name: Run i32 shift-mask oracle (#682)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/i32_shift_mask_682_differential.py
- name: Run ADDW static-offset oracle (#681, incl. software-bounds bypass)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/addw_offset_681_differential.py
# #686: the same oracle under the mask-elision lever (opt-in,
# SYNTH_SHIFT_MASK_ELIDE=1) — const amounts fold to the immediate form
# mod 32, unproven amounts keep the mask; the >= 32 rows pin that the
# elision never fires unsoundly (red-tested at land time).
- name: Run i32 shift-mask oracle with mask elision ON (#686)
run: SYNTH_SHIFT_MASK_ELIDE=1 SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/i32_shift_mask_682_differential.py
# #846/#879: gale's REAL gpio-thin driver (pinned loom.wasm, never a
# synthetic) under the default-ON mask elision — BOTH halves of the
# v0.50.1 headline claim: (a) .text shrinks and the redundant mod-32
# masks strictly drop, (b) 75 mmio (addr,value) traces + returns are
# bit-identical vs wasmtime across a pin sweep INCLUDING pin >= 32 (the
# boundary an unsound elision would corrupt). #879: this gate existed
# for two releases but was never CI-wired. The grep asserts the
# NON-ZERO check count from the script's machine-readable summary —
# exit 0 alone is not trusted (the "0 ops accepted PASS" lesson).
- name: Run gpio-thin size + mmio execution oracle (#846/#879, cortex-m3)
run: |
set -euo pipefail
SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/gpio_thin_846_differential.py | tee gpio846.out
grep -q "^#846 CHECKS=75/75" gpio846.out
# #752: the software bounds guard must trap the top-of-address-space
# wraparound class — the retired shape ADD-computed the end address
# mod 2^32, so `addr >= 2^32 - (offset+size-1)` wrapped small, passed
# the BLO, and the OOB access escaped below the linear-memory base.
# 19 vectors (in-bounds, exact-boundary, first-OOB, far-OOB, and six
# #752 top-of-space rows) x BOTH codegen paths, wasmtime ground truth.
- name: Run software-bounds wraparound oracle (#752/#377, thumb2 both paths)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/safety_bounds_377_differential.py
- name: Run bulk-memory operand-clobber oracle (#677, thumb2)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/bulk_local_clobber_677_differential.py
- name: Run bulk-memory mask-coverage oracle (#679, thumb2)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/bulk_mask_679_differential.py
# f32 hard-float SOUNDNESS oracles (GI-FPU-002). Dev-only before v0.40.0 —
# the exact gap that let TWO silent miscompiles ship in v0.39.0 (trunc
# saturation #709, and every compare returning 0 via a flag-clobber #712).
# CI-gated now so the f32 path can't silently regress. The compare oracle
# covers ALL SIX comparisons (#712: eq/ne/lt/gt/le/ge — the old harness
# exercised only lt/gt, which is how eq/ne/le/ge stayed ungated).
- name: Run f32 arith/compare execution oracle (#619/#712, cortex-m4f)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/f32_vfp_619_differential.py
# #881: the VFP register-file spilling gate (GI-FPU-002 phase 1 S0..S15 +
# phase 2 D0..D7) — the oracle behind the "falcon reaches the M7" claim,
# so it must RUN, not merely exist. The lane wrote this differential and
# never wired it: the #879 shelfware class, recurring in the very release
# whose audit fixed two other instances of it. The greps assert BOTH
# halves of the claim from the script's own summary lines — every export
# actually emitted (nm -> T, so a silent decline cannot read as success)
# AND a non-zero count of execution rows bit-identical to wasmtime. Exit
# 0 alone is not trusted (the "0 ops accepted PASS" lesson).
- name: Run VFP spill execution oracle (#881, cortex-m7dp)
run: |
set -euo pipefail
SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/vfp_spill_881_differential.py | tee vfp881.out
grep -q "^PASS: all 7 exports emitted (nm -> T)" vfp881.out
grep -qE "^PASS: [1-9][0-9]+ execution rows bit-identical to wasmtime" vfp881.out
# #708/#709: f32.load/reinterpret bit-casts + i32.trunc_f32 trap table.
- name: Run f32 load/reinterpret + trunc-trap oracle (#708/#709, m4f)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/f32_mem_trunc_708_709_differential.py
# #719: the falcon f32 residual — f32.store, abs/neg/copysign, local.set/
# tee, and mixed f32/int AAPCS-VFP params — bit-exact vs wasmtime on m4f,
# including the copysign ±0/NaN-sign/±inf sign edges. m3 honest-reject.
# #719 phase 2 extension (same harness): f32 live ACROSS an integer call
# (spill/reload around the bl, non-vacuous — the callee clobbers S0/S1)
# + float-signature-callee loud-decline pinned via symtab absence.
- name: Run f32 store/abs/neg/copysign/local + mixed-param oracle (#719, m4f)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/f32_ops_719_differential.py
# #369 (GI-FPU-002 phase 2): the scalar f64 subset on cortex-m7dp —
# const/promote_f32/arith/compare/load/store + f64-across-call, bit-exact
# (NaN==NaN per Core §4.3.3) vs wasmtime under unicorn. Pins the
# #712-class f64 compare flag-clobber fix (MOVS after VMRS returned
# stale-flag results), the m4f/m3 honest-reject (single-precision / no
# FPU), and the f64-ABI-boundary loud-declines (f64 params, f64-returning
# calls).
- name: Run f64 const/promote/arith/compare/mem + across-call oracle (#369, m7dp)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/f64_369_differential.py
# #782a: the NONTRAPPING trunc_sat family (§4.3.2 — NaN→0, out-of-range
# saturates, NEVER traps). Full boundary table (NaN/±inf/exact
# INT_MIN-INT_MAX bounds/±0.5) vs wasmtime on BOTH prioritized backends:
# ARM32 m7dp+m4f (bare RZ VCVT — the guard-free dual of the #709 trapping
# forms) and aarch64 (bare FCVTZS/FCVTZU, all 8 forms incl. i64 targets).
# Also pins falcon's exact flags (-t cortex-m7dp --relocatable): the i32
# forms must NOT skip, the ARM32 i64 forms MUST decline loudly by name.
# This gate caught the optimized-path silent-NOP drop at land time.
- name: Run trunc_sat boundary oracle (#782a, m7dp+m4f+aarch64)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/trunc_sat_782_differential.py
# #869: the 64-bit integer<->float conversion family (gale's six +
# the #756 i64.trunc_f64 pair) on cortex-m7dp — converts bit-exact
# (incl. the round-to-odd double-rounding killers), the TRAPPING trunc
# forms EXECUTED on every NaN/±inf/2^63/-2^63/2^64 boundary row (a
# wasmtime trap must be an ARM UDF stop — the #709-class silent-
# miscompile gate), m4f loud-decline honesty, falcon-flags symbol
# presence, and >=10k fixed-seed fuzz per direction. This gate caught
# the range-realloc cross-barrier live-in miscompile at land time.
- name: Run i64<->float conversion family oracle (#869, m7dp+m4f)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/i64_float_conv_869_differential.py
# #782(b): float `select` + explicit float `return` — the dominant class
# on the real falcon fused core (12/26 skips incl. run-stabilization was
# "an integer operation popped an f32"): select over two f32/f64 values
# (the clamp idiom) and `return` of an f32 result. Bit-exact (selects
# NaN-payload-STRICT — a select picks, never computes) vs wasmtime on
# m7dp; m3/m4f capability gates pinned. Also pins the WIDE (i64) select
# hi-half SILENT miscompile found adversarially (cond==0 returned val2's
# lo paired with val1's hi — soft-float f64 select rode the same path)
# and the hard-float SIGNATURE-only ABI hole (a float-signature function
# with no float op stayed on the float-naive optimized path: callers
# marshal S0/S1, the body read R0/R1).
- name: Run float select + explicit float return oracle (#782b, m7dp+m3)
run: SYNTH=./target/debug/synth python scripts/oracle_run.py scripts/repro/float_select_return_782_differential.py
# #739: static ABOVE sp_init under --shadow-stack-size — the sub-word
# load/store arms previously BAKED the linmem offset as an un-relocated
# MOVW/MOVT immediate (invisible to the #678 reloc-walking rebase AND to