Skip to content

Commit 2d2535d

Browse files
hyperpolymathclaude
andcommitted
feat(chapel-binding): stand up detachable CI + tests — binding-tier-1 pilot (closes #129)
This is the binding-tier-1 standup pilot for proven (per #129). It takes bindings/chapel/ from "zero CI signal, zero tests, never linked against current libproven" to a load-bearing detachable harness. WAVE 1 PATH (b) — proven#88 confirmed as link blocker. ===================================================== Static symbol audit (bindings/c/include/proven.h vs ffi/zig/src/main.zig vs bindings/chapel/src/LibProven.chpl) shows the Zig FFI exports only 13 symbols, and only 5 of those are both present AND ABI-consistent with the documented C ABI. Path (a) would require landing proven#88 first (multi-week ABI redesign); path (b) trims the Chapel binding's WIRED extern set to what links today. Detachable-harness template (ADR-0001) ===================================== Per the architectural constraint, the binding ships as a self-contained package extractable to a standalone proven-chapel repo: bindings/chapel/Justfile — env-driven (PROVEN_LIB_PATH, PROVEN_INCLUDE_PATH), no reach into root tooling bindings/chapel/symbol-manifest.txt — WIRED extern manifest bindings/chapel/scripts/symbol-audit.sh — fast nm -D gate (<5s) .github/workflows/chapel-ci.yml — 6 strict, SHA-pinned jobs (detachability-guard, build, symbol-audit, smoke, tests, detachable-build) Root Justfile gains chapel-{check,build,test,clean} as ONE-LINE thin forwarders into bindings/chapel/Justfile — no chapel-specific logic at root. The chapel-detachable-build CI job is the executable form of the contract: a sparse-checkout of bindings/chapel/, bindings/c/include/proven.h, and chapel-ci.yml must `just test` cleanly. Detachability cannot regress without a red CI job. WIRED subset (5 symbols, all verified in manifest) ================================================== proven_path_has_traversal SafePath.hasTraversal — pure proven_header_has_crlf SafeHeader.hasCrlf (new) — pure proven_free_string (internal: extractString) proven_version libraryVersion() proven_build_info libraryBuildInfo() Test coverage ============= smoke/hello_smoke.chpl — end-to-end "binding links + runs" tests/TestSafePath.chpl — 8 input cases (safe / traversal / boundary) tests/TestSafeHeader.chpl — 9 input cases (safe / CRLF / boundary) tests/TestLibraryInfo.chpl — version + build-info shape check tests/TestFfiContract.chpl — differential: Chapel wrapper vs raw extern proc, bit-for-bit agreement on the same input CI shape (.github/workflows/chapel-ci.yml) ========================================== Path triggers: bindings/chapel/**, ffi/zig/**, bindings/c/include/proven.h, and the workflow itself — NO other paths. Concurrency-grouped with cancel-in-progress (40-job Pro-plan ceiling guard). Chapel 2.3.0 pinned by URL + SHA-256 (b7c1a48cbf26cfc7ad9af22a84ea4becb1a5e2fd485ec4a1f5f8a69e8a7cd58c). All actions SHA-pinned. Every job is strict (no continue-on-error). GATED block + replication queue =============================== The remaining ~45 extern decls in LibProven.chpl (SafeMath, SafeString, SafeJson, SafeUrl, SafeEmail, SafeDateTime, SafeCrypto, lifecycle, etc.) are kept under a GATED banner as the documented ABI contract per proven.h. They will move to WIRED as proven#88 ramps each Zig export. Calling them today produces a linker error; the README's coverage matrix marks WIRED vs GATED explicitly. Replication queue (sub-issues #130-#134): Crystal, Nim, OCaml, Haskell, Lua. Each gets the same 6-job workflow shape via search/replace; see ADR-0001 for the token map. Files ===== +.github/workflows/chapel-ci.yml — 6-job detachable CI +audits/chapel-symbol-audit-2026-05-30.md — static audit table +bindings/chapel/Justfile — env-driven build/test +bindings/chapel/scripts/symbol-audit.sh — nm -D gate +bindings/chapel/smoke/hello_smoke.chpl — end-to-end smoke +bindings/chapel/src/SafeHeader.chpl — wraps proven_header_has_crlf +bindings/chapel/symbol-manifest.txt — WIRED symbol list +bindings/chapel/tests/TestFfiContract.chpl — differential test +bindings/chapel/tests/TestLibraryInfo.chpl — version probe +bindings/chapel/tests/TestSafeHeader.chpl — per-module test +bindings/chapel/tests/TestSafePath.chpl — per-module test +docs/adr/0001-binding-ci-template.adoc — detachable-harness ADR +docs/adr/0002-chapel-binding-standup.adoc — pilot ADR ~Justfile — chapel-{check,build,test,clean} forwarders ~ROADMAP.adoc — Binding tier-1 standup section ~bindings/chapel/README.adoc — coverage matrix + detachability ~bindings/chapel/src/LibProven.chpl — WIRED / GATED restructure ~bindings/chapel/src/Proven.chpl — umbrella re-exports WIRED only Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 081c570 commit 2d2535d

18 files changed

Lines changed: 1687 additions & 129 deletions

.github/workflows/chapel-ci.yml

Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
#
4+
# Chapel CI — proven binding-tier-1 pilot.
5+
#
6+
# This workflow is the DETACHABLE-HARNESS TEMPLATE for proven binding CI.
7+
# It is intentionally narrow: it references ONLY paths under
8+
# bindings/chapel/, ffi/zig/, and bindings/c/include/proven.h. No
9+
# root-level test fixtures, no sibling-binding tooling, no surprise
10+
# dependencies. See docs/adr/0001-binding-ci-template.md for the full
11+
# detachability contract.
12+
#
13+
# Replication template (search/replace for the next binding):
14+
# chapel -> <lang> (e.g. crystal, nim, ocaml)
15+
# Chapel -> <Lang> (display name)
16+
# bindings/chapel/ -> bindings/<lang>/
17+
# chapel-ci.yml -> <lang>-ci.yml
18+
# Chapel 2.3.0 -> <toolchain pin>
19+
# ubuntu22.amd64.deb -> <toolchain installer>
20+
# chpl --version -> <toolchain version probe>
21+
#
22+
# Quality posture: every job is strict (no continue-on-error). PRs that
23+
# break Chapel are blocked from merge.
24+
25+
name: Chapel CI
26+
27+
permissions:
28+
contents: read
29+
30+
on:
31+
push:
32+
branches: [main]
33+
paths:
34+
- 'bindings/chapel/**'
35+
- 'ffi/zig/**'
36+
- 'bindings/c/include/proven.h'
37+
- '.github/workflows/chapel-ci.yml'
38+
pull_request:
39+
branches: [main]
40+
paths:
41+
- 'bindings/chapel/**'
42+
- 'ffi/zig/**'
43+
- 'bindings/c/include/proven.h'
44+
- '.github/workflows/chapel-ci.yml'
45+
workflow_dispatch:
46+
47+
# Cancel superseded runs on the same ref. Chapel install (cold) is ~30s
48+
# but the full smoke + tests cycle is ~3 min; without cancel-in-progress
49+
# a rapid push sequence will burn into the Pro-plan concurrency ceiling.
50+
concurrency:
51+
group: chapel-ci-${{ github.ref }}
52+
cancel-in-progress: true
53+
54+
env:
55+
# Pinned Chapel deb (Ubuntu 22.04 amd64). SHA-256 verified out-of-band
56+
# against the GitHub Releases asset; mismatch fails the install step.
57+
CHAPEL_VERSION: '2.3.0'
58+
CHAPEL_DEB_URL: 'https://github.com/chapel-lang/chapel/releases/download/2.3.0/chapel-2.3.0-1.ubuntu22.amd64.deb'
59+
CHAPEL_DEB_SHA256: 'b7c1a48cbf26cfc7ad9af22a84ea4becb1a5e2fd485ec4a1f5f8a69e8a7cd58c'
60+
# Pinned Zig version (kept in sync with .github/workflows/zig-ffi.yml).
61+
ZIG_VERSION: '0.13.0'
62+
63+
jobs:
64+
# ==========================================================================
65+
# Job 0 — Detachability guard.
66+
# Fails the PR if chapel-ci.yml references any path outside the allowed
67+
# set (bindings/chapel/, ffi/zig/, bindings/c/include/proven.h, self).
68+
# ==========================================================================
69+
detachability-guard:
70+
runs-on: ubuntu-22.04
71+
timeout-minutes: 2
72+
steps:
73+
- name: Checkout (workflow file only)
74+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
75+
with:
76+
sparse-checkout: |
77+
.github/workflows/chapel-ci.yml
78+
sparse-checkout-cone-mode: false
79+
80+
- name: Detachability grep guard
81+
run: |
82+
set -euo pipefail
83+
# The workflow may reference exactly these path tokens:
84+
# bindings/chapel/...
85+
# ffi/zig/...
86+
# bindings/c/include or bindings/c/include/proven.h
87+
# .github/workflows/chapel-ci.yml
88+
# Anything else under bindings/, or any root-level tests/ token,
89+
# is a detachability violation.
90+
forbidden=0
91+
while IFS= read -r raw; do
92+
# Strip a trailing punctuation char that a path-regex might pick up
93+
# from prose ("see bindings/c/include/proven.h.").
94+
p="${raw%[.,;:)]}"
95+
case "$p" in
96+
bindings/chapel/*|ffi/zig/*|bindings/c/include|bindings/c/include/proven.h|.github/workflows/chapel-ci.yml)
97+
;;
98+
bindings/*/*)
99+
echo "::error file=.github/workflows/chapel-ci.yml::detachability violation: $p"
100+
forbidden=1
101+
;;
102+
tests/*)
103+
echo "::error file=.github/workflows/chapel-ci.yml::detachability violation (root-level tests/): $p"
104+
forbidden=1
105+
;;
106+
esac
107+
done < <(grep -oE '(bindings/[a-zA-Z0-9_-]+/[A-Za-z0-9_./-]*|tests/[A-Za-z0-9_./-]+)' \
108+
.github/workflows/chapel-ci.yml | sort -u)
109+
[ "$forbidden" -eq 0 ]
110+
echo "detachability-guard: OK"
111+
112+
# ==========================================================================
113+
# Job 1 — Build libproven.so + Chapel typecheck.
114+
# Produces the .so artefact every subsequent job consumes via download.
115+
# ==========================================================================
116+
chapel-build:
117+
runs-on: ubuntu-22.04
118+
needs: detachability-guard
119+
timeout-minutes: 20
120+
121+
steps:
122+
- name: Checkout proven
123+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
124+
with:
125+
path: proven
126+
127+
- name: Checkout idris2-zig-ffi dependency
128+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
129+
with:
130+
repository: hyperpolymath/idris2-zig-ffi
131+
path: idris2-zig-ffi
132+
133+
- name: Setup Zig ${{ env.ZIG_VERSION }}
134+
uses: mlugg/setup-zig@e7d1537c378b83b8049f65dda471d87a2f7b2df2 # v1
135+
with:
136+
version: ${{ env.ZIG_VERSION }}
137+
138+
- name: Build libproven.so via Zig
139+
working-directory: proven/ffi/zig
140+
run: zig build -Dtarget=x86_64-linux-gnu
141+
142+
- name: Install Chapel ${{ env.CHAPEL_VERSION }} (SHA-pinned .deb)
143+
run: |
144+
set -euo pipefail
145+
curl -fsSL -o /tmp/chapel.deb "$CHAPEL_DEB_URL"
146+
echo "$CHAPEL_DEB_SHA256 /tmp/chapel.deb" | sha256sum --check
147+
sudo apt-get update
148+
sudo apt-get install -y /tmp/chapel.deb
149+
chpl --version
150+
151+
- name: Chapel module typecheck (--library on every module)
152+
working-directory: proven/bindings/chapel
153+
run: |
154+
set -euo pipefail
155+
for m in src/*.chpl; do
156+
echo "[chapel-build] typecheck $m"
157+
chpl --library --dynamic --print-callstack-on-error \
158+
--no-codegen \
159+
-M src/ \
160+
-I "$GITHUB_WORKSPACE/proven/bindings/c/include" \
161+
"$m"
162+
done
163+
164+
- name: Upload libproven.so artefact
165+
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4
166+
with:
167+
name: libproven-x86_64-linux-gnu
168+
path: proven/ffi/zig/zig-out/lib/libproven.so
169+
if-no-files-found: error
170+
retention-days: 1
171+
172+
# ==========================================================================
173+
# Job 2 — Symbol audit (FFI parity gate).
174+
# The load-bearing check: nm -D libproven.so vs. symbol-manifest.txt.
175+
# ==========================================================================
176+
chapel-symbol-audit:
177+
runs-on: ubuntu-22.04
178+
needs: chapel-build
179+
timeout-minutes: 5
180+
steps:
181+
- name: Checkout (binding only)
182+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
183+
with:
184+
sparse-checkout: |
185+
bindings/chapel
186+
sparse-checkout-cone-mode: false
187+
188+
- name: Download libproven.so
189+
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
190+
with:
191+
name: libproven-x86_64-linux-gnu
192+
path: lib/
193+
194+
- name: Run chapel-symbol-audit
195+
working-directory: bindings/chapel
196+
env:
197+
PROVEN_LIB_PATH: ${{ github.workspace }}/lib
198+
run: ./scripts/symbol-audit.sh
199+
200+
# ==========================================================================
201+
# Job 3 — Smoke (the "binding actually links + runs" gate).
202+
# ==========================================================================
203+
chapel-smoke:
204+
runs-on: ubuntu-22.04
205+
needs: chapel-build
206+
timeout-minutes: 10
207+
steps:
208+
- name: Checkout (binding + C header only)
209+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
210+
with:
211+
sparse-checkout: |
212+
bindings/chapel
213+
bindings/c/include
214+
.github/workflows/chapel-ci.yml
215+
sparse-checkout-cone-mode: false
216+
217+
- name: Install Chapel ${{ env.CHAPEL_VERSION }} (SHA-pinned .deb)
218+
run: |
219+
set -euo pipefail
220+
curl -fsSL -o /tmp/chapel.deb "$CHAPEL_DEB_URL"
221+
echo "$CHAPEL_DEB_SHA256 /tmp/chapel.deb" | sha256sum --check
222+
sudo apt-get update
223+
sudo apt-get install -y /tmp/chapel.deb
224+
225+
- name: Install just
226+
uses: extractions/setup-just@dd310ad5a97d8e7b41793f8ef055398d51ad4de6 # v2
227+
with:
228+
just-version: '1.36.0'
229+
230+
- name: Download libproven.so
231+
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
232+
with:
233+
name: libproven-x86_64-linux-gnu
234+
path: lib/
235+
236+
- name: Build hello_smoke (cd bindings/chapel && just build)
237+
working-directory: bindings/chapel
238+
env:
239+
PROVEN_LIB_PATH: ${{ github.workspace }}/lib
240+
PROVEN_INCLUDE_PATH: ${{ github.workspace }}/bindings/c/include
241+
run: just build
242+
243+
- name: Run hello_smoke
244+
working-directory: bindings/chapel
245+
env:
246+
LD_LIBRARY_PATH: ${{ github.workspace }}/lib
247+
run: ./smoke/hello_smoke
248+
249+
# ==========================================================================
250+
# Job 4 — Per-module tests.
251+
# ==========================================================================
252+
chapel-tests:
253+
runs-on: ubuntu-22.04
254+
needs: chapel-build
255+
timeout-minutes: 15
256+
steps:
257+
- name: Checkout (binding + C header only)
258+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
259+
with:
260+
sparse-checkout: |
261+
bindings/chapel
262+
bindings/c/include
263+
.github/workflows/chapel-ci.yml
264+
sparse-checkout-cone-mode: false
265+
266+
- name: Install Chapel ${{ env.CHAPEL_VERSION }} (SHA-pinned .deb)
267+
run: |
268+
set -euo pipefail
269+
curl -fsSL -o /tmp/chapel.deb "$CHAPEL_DEB_URL"
270+
echo "$CHAPEL_DEB_SHA256 /tmp/chapel.deb" | sha256sum --check
271+
sudo apt-get update
272+
sudo apt-get install -y /tmp/chapel.deb
273+
274+
- name: Install just
275+
uses: extractions/setup-just@dd310ad5a97d8e7b41793f8ef055398d51ad4de6 # v2
276+
with:
277+
just-version: '1.36.0'
278+
279+
- name: Download libproven.so
280+
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
281+
with:
282+
name: libproven-x86_64-linux-gnu
283+
path: lib/
284+
285+
- name: Run all Chapel tests (cd bindings/chapel && just test)
286+
working-directory: bindings/chapel
287+
env:
288+
PROVEN_LIB_PATH: ${{ github.workspace }}/lib
289+
PROVEN_INCLUDE_PATH: ${{ github.workspace }}/bindings/c/include
290+
LD_LIBRARY_PATH: ${{ github.workspace }}/lib
291+
run: just test
292+
293+
# ==========================================================================
294+
# Job 5 — Detachable-checkout proof.
295+
# Validates the harness self-builds with ONLY:
296+
# bindings/chapel/, bindings/c/include/proven.h, the workflow file.
297+
# If this job goes red, detachability has regressed.
298+
# ==========================================================================
299+
chapel-detachable-build:
300+
runs-on: ubuntu-22.04
301+
needs: chapel-build
302+
timeout-minutes: 15
303+
steps:
304+
- name: Sparse checkout (detachable set only)
305+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
306+
with:
307+
sparse-checkout: |
308+
bindings/chapel
309+
bindings/c/include/proven.h
310+
.github/workflows/chapel-ci.yml
311+
sparse-checkout-cone-mode: false
312+
path: proven-chapel
313+
314+
- name: Verify detached layout (no other proven trees present)
315+
working-directory: proven-chapel
316+
run: |
317+
set -euo pipefail
318+
if [ -d "src" ] || [ -d "ffi" ] || [ -d "tests" ] || [ -d "audits" ]; then
319+
echo "::error::detachability violation: non-binding trees present in detached checkout"
320+
ls -la
321+
exit 1
322+
fi
323+
test -d bindings/chapel
324+
test -f bindings/c/include/proven.h
325+
echo "detached layout: OK"
326+
327+
- name: Install Chapel ${{ env.CHAPEL_VERSION }} (SHA-pinned .deb)
328+
run: |
329+
set -euo pipefail
330+
curl -fsSL -o /tmp/chapel.deb "$CHAPEL_DEB_URL"
331+
echo "$CHAPEL_DEB_SHA256 /tmp/chapel.deb" | sha256sum --check
332+
sudo apt-get update
333+
sudo apt-get install -y /tmp/chapel.deb
334+
335+
- name: Install just
336+
uses: extractions/setup-just@dd310ad5a97d8e7b41793f8ef055398d51ad4de6 # v2
337+
with:
338+
just-version: '1.36.0'
339+
340+
- name: Download libproven.so (the only external dep allowed)
341+
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
342+
with:
343+
name: libproven-x86_64-linux-gnu
344+
path: lib/
345+
346+
- name: Build + test from detached checkout
347+
working-directory: proven-chapel/bindings/chapel
348+
env:
349+
PROVEN_LIB_PATH: ${{ github.workspace }}/lib
350+
PROVEN_INCLUDE_PATH: ${{ github.workspace }}/proven-chapel/bindings/c/include
351+
LD_LIBRARY_PATH: ${{ github.workspace }}/lib
352+
run: just test

Justfile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,34 @@ test-ffi:
152152
-Didris-support-lib="$SUPPORT_LIB"
153153
echo "[proven] FFI tests passed."
154154
155+
# ---------------------------------------------------------------------------
156+
# Chapel binding (detachable harness — see docs/adr/0001-binding-ci-template.md)
157+
#
158+
# These recipes are THIN FORWARDERS into bindings/chapel/. Any Chapel-
159+
# specific logic lives in bindings/chapel/Justfile so that the harness
160+
# remains extractable to a standalone proven-chapel repo. The forwarders
161+
# set PROVEN_LIB_PATH to the in-tree libproven.so before delegating.
162+
# ---------------------------------------------------------------------------
163+
164+
# Forward to the Chapel binding's own check recipe (fast symbol audit).
165+
chapel-check: build-ffi
166+
PROVEN_LIB_PATH="$(pwd)/ffi/zig/zig-out/lib" \
167+
just -d bindings/chapel --justfile bindings/chapel/Justfile check
168+
169+
# Forward to the Chapel binding's own build recipe.
170+
chapel-build: build-ffi
171+
PROVEN_LIB_PATH="$(pwd)/ffi/zig/zig-out/lib" \
172+
just -d bindings/chapel --justfile bindings/chapel/Justfile build
173+
174+
# Forward to the Chapel binding's own test recipe.
175+
chapel-test: build-ffi
176+
PROVEN_LIB_PATH="$(pwd)/ffi/zig/zig-out/lib" \
177+
just -d bindings/chapel --justfile bindings/chapel/Justfile test
178+
179+
# Forward to the Chapel binding's own clean recipe.
180+
chapel-clean:
181+
just -d bindings/chapel --justfile bindings/chapel/Justfile clean
182+
155183
# ---------------------------------------------------------------------------
156184
# Cleaning
157185
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)