Skip to content

Commit d2b3a3d

Browse files
Slice 2: make chapeliser provably-real — CI machine-checks proofs/FFI/Chapel + honest status (#27)
Follow-on to base camp (#26, merged). Goal of this slice: move chapeliser from "asserted in prose" to **machine-verified**, and make the docs honest about what is and isn't checked today. ## Provable-real — `.github/workflows/provable.yml` The Rust CLI/codegen is already green (63 tests via `rust-ci.yml`). This adds a new workflow that verifies everything *outside* Rust: | Job | What it proves | |---|---| | `idris2-proofs` | `idris2 --check` type-checks `Types.idr`, `Layout.idr`, `Foreign.idr` (the dependent-type ABI proofs) | | `zig-ffi` | `zig build test` compiles + runs the Zig FFI reference impl (Zig 0.14) | | `codegen-drift` | regenerates the golden sample and `diff`s against the committed tree (fails on drift) | | `chapel-golden` | compiles **and runs** the generated Chapel via real `chpl`, asserting 8/8 items conserved | ## Golden sample — `examples/golden/` A minimal end-to-end fixture that closes the `STATE.a2ml` action *"generate + compile + run"*: - `echo.toml` — deliberately `per-item`/`merge` so the generated Chapel pulls in **no** optional modules (no BlockDist/DynamicIters/AtomicObjects) → small, stable compile surface. - `generated/` — committed codegen output (reviewable + drift-checked). - `ffi_stub.c` — ~60-line C echo implementation of the 12 `c_*` functions so the Chapel can be linked and run without user code. ## Honest status — stop claiming what isn't checked yet - **`ROADMAP.adoc`**: Phase 1 `(COMPLETE)` → `(IMPLEMENTED — verification gated in CI)`; new **Phase 1b** tracks the four CI jobs (each flips `[x]` only when green); dropped "compilable" from the bare codegen bullet. - **Test-count fix**: the old `15 tests (6 unit + 8 integration + 1 doc)` was wrong — actual is **63 (22 + 40 + 1)**. - **`README.adoc`**: Idris2 *"Formal proofs that…"* → *"proof obligations, machine-checked in CI"*; corrected source paths (`src/interface/abi`, `src/interface/ffi`); honest pre-alpha Status paragraph. - **`STATE.a2ml`**: blockers / next-actions / maintenance now reflect CI-gated verification (`last-result = warn` until `provable.yml` is green). ## Why CI is the verifier (not local) The idris2 / zig / chpl toolchains aren't installable in the dev sandbox (`ziglang.org` and the GitHub releases API are network-blocked here; building idris2 from source is sandbox-restricted). So GitHub runners are the verifier. **Until `provable.yml` is green, these artifacts are "written", not "verified"** — and the docs now say exactly that. Kept as a **draft** for that reason: driving the four jobs to green is the acceptance gate, and a first run may surface real fixes (e.g. Idris2 `DecEq` totality, Chapel `Time`/reduce API specifics) — that iteration *is* the "make it real" work. **Follow-up (not blocking):** pin action/image refs (`actions/checkout`, `mlugg/setup-zig`, `chapel/chapel`, `idris2-pack`) to SHAs per estate policy once the tags are confirmed working. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Mbq6yKF9RhFai6EQ7WqKhQ --- _Generated by [Claude Code](https://claude.ai/code/session_01Mbq6yKF9RhFai6EQ7WqKhQ)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7dc0d11 commit d2b3a3d

16 files changed

Lines changed: 780 additions & 27 deletions

File tree

.github/workflows/provable.yml

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# Provable — machine-checks the claims Chapeliser makes outside the Rust layer.
3+
#
4+
# The Rust CLI/codegen is covered by rust-ci.yml. This workflow verifies the
5+
# four things that are otherwise only asserted in prose:
6+
# 1. idris2-proofs — the Idris2 ABI proofs actually type-check (`idris2 --check`).
7+
# 2. zig-ffi — the Zig FFI reference implementation builds and its tests pass.
8+
# 3. codegen-drift — the committed golden sample matches fresh codegen output.
9+
# 4. chapel-golden — the generated Chapel actually compiles AND runs via `chpl`.
10+
#
11+
# Until every job here is green, the proofs / FFI / Chapel output are "written"
12+
# but NOT "verified" — ROADMAP.adoc and README.adoc are worded accordingly.
13+
name: Provable
14+
15+
on:
16+
push:
17+
branches: [main, master, "claude/**"]
18+
pull_request:
19+
20+
permissions:
21+
contents: read
22+
23+
jobs:
24+
idris2-proofs:
25+
name: Idris2 — machine-check ABI proofs
26+
runs-on: ubuntu-latest
27+
timeout-minutes: 20
28+
container: ghcr.io/stefan-hoeck/idris2-pack:latest
29+
steps:
30+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
31+
- name: idris2 --check the ABI modules
32+
working-directory: src/interface/abi
33+
# Modules are namespaced Chapeliser.ABI.*, so they live under
34+
# Chapeliser/ABI/ (idris2 requires file path to match module name).
35+
# Foreign imports Types + Layout, so checking it checks the lot; we
36+
# check each explicitly for clearer failure attribution.
37+
run: |
38+
idris2 --version
39+
idris2 --check Chapeliser/ABI/Types.idr
40+
idris2 --check Chapeliser/ABI/Layout.idr
41+
idris2 --check Chapeliser/ABI/Foreign.idr
42+
43+
zig-ffi:
44+
name: Zig — build + test FFI reference impl
45+
runs-on: ubuntu-latest
46+
timeout-minutes: 15
47+
steps:
48+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
49+
- uses: mlugg/setup-zig@v1 # SHA-pin pending (advisory Hypatia medium)
50+
with:
51+
version: 0.14.0
52+
- name: zig build + test
53+
working-directory: src/interface/ffi
54+
run: |
55+
zig version
56+
zig build test
57+
zig build
58+
59+
codegen-drift:
60+
name: Codegen — golden sample is up to date
61+
runs-on: ubuntu-latest
62+
timeout-minutes: 15
63+
steps:
64+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
65+
- name: Regenerate golden artifacts and diff against committed tree
66+
run: |
67+
cargo run --quiet -- generate -m examples/golden/echo.toml -o /tmp/golden
68+
diff -ru examples/golden/generated /tmp/golden
69+
70+
chapel-golden:
71+
name: Chapel — compile + run golden sample
72+
runs-on: ubuntu-latest
73+
timeout-minutes: 20
74+
container: docker://chapel/chapel:latest
75+
steps:
76+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
77+
- name: Compile generated Chapel against the echo FFI stub
78+
run: |
79+
chpl --version
80+
chpl --main-module echo_Distributed \
81+
examples/golden/generated/chapel/echo_distributed.chpl \
82+
examples/golden/ffi_stub.c \
83+
-o /tmp/echo_distributed
84+
- name: Run on a single locale and assert it conserves all items
85+
run: |
86+
/tmp/echo_distributed -nl1 | tee /tmp/echo.out
87+
grep -q "Gathered 8/8 results (merge)" /tmp/echo.out
88+
grep -q "complete in" /tmp/echo.out

.machine_readable/6a2/STATE.a2ml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[metadata]
66
project = "chapeliser"
77
version = "0.1.0"
8-
last-updated = "2026-03-21"
8+
last-updated = "2026-06-18"
99
status = "active"
1010
session = "converted from scheme — 2026-04-11"
1111

@@ -15,7 +15,7 @@ purpose = """General-purpose Chapel acceleration framework"""
1515
completion-percentage = 45
1616

1717
[position]
18-
phase = "phase-1-complete" # design | implementation | testing | maintenance | archived
18+
phase = "testing" # design | implementation | testing | maintenance | archived
1919
maturity = "experimental" # experimental | alpha | beta | production | lts
2020

2121
[route-to-mvp]
@@ -25,16 +25,17 @@ milestones = [
2525

2626
[blockers-and-issues]
2727
issues = [
28-
"Chapel compiler not installed on dev machine — generated code verified by structure, not compilation",
28+
"Idris2/Zig/Chapel toolchains unavailable in the local dev sandbox — proofs, FFI, and generated Chapel are now machine-checked in CI (.github/workflows/provable.yml) rather than locally.",
29+
"provable.yml has not yet had a green run — until it does, the Idris2 proofs, Zig FFI build, and golden Chapel compile/run are written but not confirmed (ROADMAP Phase 1b).",
2930
]
3031

3132
[critical-next-actions]
3233
actions = [
33-
"End-to-end test: generate + compile + run with panic-attacker workload",
34-
"Add shell completions (bash, zsh, fish)",
35-
"Write additional examples (Monte Carlo, image processing)",
34+
"Drive .github/workflows/provable.yml to green (idris2-proofs, zig-ffi, codegen-drift, chapel-golden).",
35+
"Add golden samples for the remaining partition/gather strategies and multi-locale runs.",
36+
"Add shell completions (bash, zsh, fish).",
3637
]
3738

3839
[maintenance-status]
39-
last-run-utc = "2026-03-21T00:00:00Z"
40-
last-result = "unknown" # unknown | pass | warn | fail
40+
last-run-utc = "2026-06-18T00:00:00Z"
41+
last-result = "warn" # unknown | pass | warn | fail — Rust layer green (63 tests); provable.yml pending first run

README.adoc

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,15 @@ You write **zero Chapel code**. Chapeliser generates everything.
110110

111111
Chapeliser follows the hyperpolymath ABI-FFI standard:
112112

113-
* **Idris2 ABI** (`src/abi/`) — Formal proofs that:
114-
- Data layouts are consistent across nodes
113+
* **Idris2 ABI** (`src/interface/abi/`) — Dependent-type *proof obligations*,
114+
machine-checked in CI (`.github/workflows/provable.yml`); the Rust mirror in
115+
`src/abi/` carries the matching runtime types and checks:
115116
- Partition functions produce complete, non-overlapping splits
116117
- Gather functions preserve all results
117118
- Serialization round-trips are identity
119+
- Memory layouts are consistent across the FFI boundary
118120

119-
* **Zig FFI** (`ffi/zig/`) — C-ABI bridge between:
121+
* **Zig FFI** (`src/interface/ffi/`) — C-ABI bridge between:
120122
- The user's application (any language with C FFI)
121123
- The Chapel runtime (`chpl_*` functions)
122124
- Memory management across the boundary
@@ -138,7 +140,7 @@ Chapeliser follows the hyperpolymath ABI-FFI standard:
138140

139141
[source,bash]
140142
----
141-
# Install
143+
# Install (once published to crates.io — see ROADMAP Phase 3)
142144
cargo install chapeliser
143145
144146
# In your project directory, create chapeliser.toml (see above)
@@ -158,7 +160,10 @@ hundreds of repositories on a compute cluster.
158160

159161
== Status
160162

161-
**Pre-alpha.** Architecture defined, ABI proofs in progress, codegen planned.
163+
**Pre-alpha.** The Rust CLI and code generator are implemented and tested
164+
(63 tests). The Idris2 proofs, Zig FFI, and a golden Chapel compile-and-run are
165+
wired into CI as the verification gate (`.github/workflows/provable.yml`) — see
166+
`ROADMAP.adoc` Phase 1b for their live (green/red) status. Not yet released.
162167

163168
== License
164169

ROADMAP.adoc

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
1212
* [x] Example manifest (panic-attacker mass-panic)
1313
* [x] README with architecture, SECURITY, CONTRIBUTING, GOVERNANCE
1414

15-
== Phase 1: Core Implementation (COMPLETE)
16-
* [x] Chapel code generator — produces compilable .chpl with real distribution logic
15+
== Phase 1: Core Implementation (IMPLEMENTED — machine-verification gated in CI)
16+
17+
NOTE: "Implemented" = the code is written and the Rust layer is tested. The
18+
Idris2 proofs, Zig FFI, and generated Chapel are checked by `idris2`/`zig`/`chpl`
19+
only in CI (`.github/workflows/provable.yml`); their live status is Phase 1b.
20+
21+
* [x] Chapel code generator — produces .chpl with real distribution logic
1722
** Per-item: even coforall distribution with locale range helper
1823
** Chunk: fixed-size chunks, round-robin across locales
1924
** Adaptive: work-stealing via Chapel DynamicIters
@@ -31,28 +36,39 @@ Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
3136
* [x] Zig FFI bridge generator — 12 Chapel-facing exports delegating to user code
3237
* [x] C header generator — full FFI contract with documentation
3338
* [x] Build script generator — env var overrides, Chapel config passthrough
34-
* [x] Idris2 ABI proofs (Types.idr, Layout.idr, Foreign.idr)
39+
* [x] Idris2 ABI proofs *written* (Types.idr, Layout.idr, Foreign.idr) — machine-checked in Phase 1b
3540
** Partition completeness + disjointness
3641
** Gather conservation
3742
** Serialisation round-trip witness
3843
** Retry isolation
3944
** Memory layout proofs for item/result buffers
40-
* [x] Zig FFI reference implementation (echo processor, FNV-1a hash, concatenation reducer)
45+
* [x] Zig FFI reference implementation *written* (echo processor, FNV-1a hash, concatenation reducer) — compiled in Phase 1b
4146
* [x] Rust ABI types with runtime verification
4247
** Partition, GatherResult, MemoryBudget, FfiResult
4348
** Strategy enums with parsing
44-
** 6 unit tests
49+
** 22 unit tests
4550
* [x] Cluster config parsing (cluster.toml → GASNET/SSH env vars)
4651
* [x] Build command: release/debug mode passthrough
47-
* [x] 15 tests passing (6 unit + 8 integration + 1 doc-test)
52+
* [x] 63 tests passing in the Rust layer (22 unit + 40 integration + 1 doc-test)
53+
54+
== Phase 1b: Machine-verification (gated in CI — `.github/workflows/provable.yml`)
55+
56+
These convert the Phase 1 artifacts from "written" to "verified". Each item
57+
turns `[x]` only once its CI job is green; until then the Idris2/Zig/Chapel
58+
artifacts above are implemented but NOT yet machine-checked.
59+
60+
* [ ] `idris2 --check` passes for Types.idr, Layout.idr, Foreign.idr (job: `idris2-proofs`)
61+
* [ ] `zig build test` passes for the FFI reference impl (job: `zig-ffi`)
62+
* [ ] Committed golden codegen output is drift-free (job: `codegen-drift`)
63+
* [ ] Generated Chapel compiles + runs via `chpl` — echo sample, 8/8 items conserved (job: `chapel-golden`)
4864

4965
== Phase 2: Polish
5066
* [ ] Error messages and diagnostics (human-readable codegen errors)
5167
* [ ] Shell completions (bash, zsh, fish)
5268
* [ ] `chapeliser info` — show memory budget estimate from manifest
5369
* [ ] Performance benchmarks (single-locale vs multi-locale scaling)
54-
* [ ] Additional examples (Monte Carlo, image processing, text search)
55-
* [ ] CI/CD for the generated Chapel artifacts
70+
* [ ] Additional golden samples (other 4 partition × 5 gather strategies; multi-locale)
71+
* [~] CI/CD for the generated Chapel artifacts — wired in `provable.yml` (see Phase 1b)
5672

5773
== Phase 3: Ecosystem
5874
* [ ] PanLL panel integration (distribution visualisation)

examples/golden/README.adoc

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
= Golden sample — end-to-end verification fixture
3+
Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
4+
:icons: font
5+
6+
This directory is the *provable-real* fixture for Chapeliser: the one workload
7+
that CI takes all the way from manifest to a running Chapel binary.
8+
9+
== Why it exists
10+
11+
Most of Chapeliser is exercised by Rust unit/integration tests. But the whole
12+
point of the tool — _generate Chapel, compile it, run it_ — can only be proven
13+
by actually invoking `chpl`. This fixture closes that loop in CI
14+
(`.github/workflows/provable.yml`, job `chapel-golden`).
15+
16+
== Contents
17+
18+
[cols="1,3"]
19+
|===
20+
| `echo.toml` | A deliberately minimal manifest: `per-item` partition,
21+
`merge` gather. These are the simplest strategies, so the generated Chapel
22+
pulls in *no* optional modules (no `BlockDist`, `DynamicIters`, or
23+
`AtomicObjects`) — keeping the compile surface small and stable.
24+
25+
| `generated/` | The Chapel wrapper, Zig FFI bridge, C header and build script
26+
produced by `chapeliser generate -m echo.toml`. Committed so the output is
27+
reviewable and drift-checked. CI regenerates and `diff`s against this tree
28+
(job `codegen-drift`) — if they differ, the build fails.
29+
30+
| `ffi_stub.c` | A ~60-line C implementation of the 12 `c_*` FFI functions for
31+
a trivial echo workload (8 items, copy input to output). Stands in for real
32+
user code so the generated Chapel can be linked and run.
33+
|===
34+
35+
== Reproduce locally
36+
37+
[source,bash]
38+
----
39+
# 1. (Re)generate — must match the committed generated/ tree:
40+
cargo run -- generate -m examples/golden/echo.toml -o examples/golden/generated
41+
42+
# 2. Compile the generated Chapel against the echo FFI stub (needs chpl + cc):
43+
chpl --main-module echo_Distributed \
44+
examples/golden/generated/chapel/echo_distributed.chpl \
45+
examples/golden/ffi_stub.c \
46+
-o /tmp/echo_distributed
47+
48+
# 3. Run on a single locale — expect "Gathered 8/8 results (merge)":
49+
/tmp/echo_distributed -nl1
50+
----
51+
52+
== What this proves
53+
54+
* The Rust codegen emits Chapel that a real `chpl` accepts (not just
55+
"structurally plausible" Chapel).
56+
* The C-ABI contract between the generated Chapel `extern proc` declarations
57+
and the FFI layer links cleanly.
58+
* The per-item / merge distribution path runs to completion and conserves all
59+
items (8 in, 8 gathered).
60+
61+
It does *not* prove multi-locale (`-nl>1`) behaviour or the other four
62+
partition/gather strategies; those are future fixtures (see `ROADMAP.adoc`).

examples/golden/echo.toml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# Golden sample — a trivial "echo" workload whose only purpose is to let CI
3+
# machine-verify the FULL Chapeliser pipeline end to end:
4+
# codegen (Rust) -> Chapel compile (chpl) -> link FFI stub -> run.
5+
#
6+
# It deliberately uses the simplest strategies (per-item / merge) so the
7+
# generated Chapel exercises the core distribution path WITHOUT pulling in
8+
# optional modules (BlockDist, DynamicIters, AtomicObjects). Keeping the
9+
# compile surface minimal makes this a stable verification fixture.
10+
#
11+
# Verified by: .github/workflows/provable.yml (chapel-golden job).
12+
13+
[workload]
14+
name = "echo"
15+
entry = "examples/golden/echo.rs::echo"
16+
partition = "per-item"
17+
gather = "merge"
18+
19+
[data]
20+
input-type = "Vec<Bytes>"
21+
output-type = "Vec<Bytes>"
22+
serialization = "raw"
23+
max-item-bytes = 4096
24+
25+
[scaling]
26+
min-nodes = 1
27+
max-nodes = 4
28+
grain-size = 1
29+
expected-items = 8

0 commit comments

Comments
 (0)