Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 191 additions & 0 deletions .github/workflows/chapel-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
# SPDX-License-Identifier: MPL-2.0
#
# chapel-ci — strict CI gates for the OPTIONAL Chapel mass-panic harness.
#
# The Rust binary stands alone (USB-stick-portable, single-machine). chapel/ is
# a detachable multi-machine harness on top. This workflow exercises the harness
# and the Rust↔Chapel contract surface. Path triggers are scoped so a pure-Rust
# PR that doesn't touch chapel/ or src/main.rs leaves these jobs unrun, and
# removing chapel/ entirely leaves the Rust CI path (rust-ci.yml) green.
#
# Six strict jobs (no continue-on-error):
# 1. chapel-parse-check — chpl --parse-only on every module
# 2. chapel-build — just chapel-build-ci (no toolbox)
# 3. chapel-smoke — chapel/smoke/two_repo_smoke (Chapel data flow)
# 4. chapel-e2e — mass-panic end-to-end (-nl 1) on a synthetic
# 2-repo manifest. True -nl 2 requires CHPL_COMM=gasnet
# which the stock .deb doesn't ship; tracked for Wave 2.
# 5. chapel-cli-contract — assert panic-attack describe-contract matches fixture
# 6. chapel-rust-diff — rayon assemblyline vs Chapel single-locale aggregates
#
# Wave 2 hardening tracker: SHA-pin the Chapel 2.8.0 .deb download. Today the
# workflow trusts the HTTPS endpoint at chapel-lang/chapel releases. Acceptable
# for the harness scaffold; harden before promoting Chapel to a production gate.

name: chapel-ci

on:
push:
branches: [main]
paths:
- 'chapel/**'
- 'Justfile'
- '.github/workflows/chapel-ci.yml'
- 'src/main.rs'
- 'src/types.rs'
- 'Cargo.toml'
- 'Cargo.lock'
pull_request:
paths:
- 'chapel/**'
- 'Justfile'
- '.github/workflows/chapel-ci.yml'
- 'src/main.rs'
- 'src/types.rs'
- 'Cargo.toml'
- 'Cargo.lock'

permissions:
contents: read

concurrency:
group: chapel-ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
CHAPEL_VERSION: "2.8.0"
CHAPEL_DEB_URL: "https://github.com/chapel-lang/chapel/releases/download/2.8.0/chapel-2.8.0-1.ubuntu22.amd64.deb"

jobs:
chapel-parse-check:
name: chapel-parse-check
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install just
run: sudo apt-get update -qq && sudo apt-get install -y just
- name: Install Chapel ${{ env.CHAPEL_VERSION }}
run: |
set -euo pipefail
curl -fsSL --retry 3 -o /tmp/chapel.deb "${{ env.CHAPEL_DEB_URL }}"
sudo apt-get install -y /tmp/chapel.deb
chpl --version
- name: Parse every Chapel module
run: just chapel-parse-check

chapel-build:
name: chapel-build
needs: chapel-parse-check
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install just
run: sudo apt-get update -qq && sudo apt-get install -y just
- name: Install Chapel ${{ env.CHAPEL_VERSION }}
run: |
set -euo pipefail
curl -fsSL --retry 3 -o /tmp/chapel.deb "${{ env.CHAPEL_DEB_URL }}"
sudo apt-get install -y /tmp/chapel.deb
chpl --version
- name: Build mass-panic + smoke (no toolbox)
run: just chapel-build-ci
- name: Upload Chapel artefacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: chapel-binaries
path: |
chapel/mass-panic
chapel/smoke/two_repo_smoke
retention-days: 1

chapel-smoke:
name: chapel-smoke
needs: chapel-build
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install Chapel ${{ env.CHAPEL_VERSION }}
run: |
set -euo pipefail
curl -fsSL --retry 3 -o /tmp/chapel.deb "${{ env.CHAPEL_DEB_URL }}"
sudo apt-get install -y /tmp/chapel.deb
- name: Download Chapel artefacts
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: chapel-binaries
path: chapel/
- name: Restore exec bits
run: chmod +x chapel/mass-panic chapel/smoke/two_repo_smoke
- name: Run two_repo_smoke
run: ./chapel/smoke/two_repo_smoke

chapel-e2e:
name: chapel-e2e
needs: chapel-build
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install just + Chapel ${{ env.CHAPEL_VERSION }}
run: |
set -euo pipefail
sudo apt-get update -qq && sudo apt-get install -y just
curl -fsSL --retry 3 -o /tmp/chapel.deb "${{ env.CHAPEL_DEB_URL }}"
sudo apt-get install -y /tmp/chapel.deb
- name: Download Chapel artefacts
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: chapel-binaries
path: chapel/
- name: Restore exec bits
run: chmod +x chapel/mass-panic
- name: End-to-end -nl 1 exercise
run: just chapel-e2e

chapel-cli-contract:
name: chapel-cli-contract
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: dtolnay/rust-toolchain@4be9e76fd7c4901c61fb841f559994984270fce7 # stable
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-chapel-cli-contract-${{ hashFiles('Cargo.lock') }}
- name: Build panic-attack
run: cargo build --release --locked
- name: Run contract gate
run: ./chapel/tests/contract_check.sh

chapel-rust-diff:
name: chapel-rust-diff
needs: chapel-build
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: dtolnay/rust-toolchain@4be9e76fd7c4901c61fb841f559994984270fce7 # stable
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-chapel-rust-diff-${{ hashFiles('Cargo.lock') }}
- name: Install Chapel ${{ env.CHAPEL_VERSION }}
run: |
set -euo pipefail
curl -fsSL --retry 3 -o /tmp/chapel.deb "${{ env.CHAPEL_DEB_URL }}"
sudo apt-get install -y /tmp/chapel.deb
- name: Download Chapel artefacts
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: chapel-binaries
path: chapel/
- name: Restore exec bits
run: chmod +x chapel/mass-panic
- name: Build panic-attack
run: cargo build --release --locked
- name: rayon vs Chapel single-locale aggregate parity
run: ./chapel/tests/rayon_vs_chapel_diff.sh
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ htmlcov/
# asdf version manager
.tool-versions
chapel/mass-panic
chapel/smoke/two_repo_smoke
target/
node_modules/
_build/
Expand Down
50 changes: 48 additions & 2 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,58 @@ chapel-build:
chapel-build-toolbox:
toolbox run --container chapel-dev bash -c "cd $(pwd)/chapel && chpl src/MassPanic.chpl src/Protocol.chpl src/Imaging.chpl src/Temporal.chpl -o mass-panic"

# CI build path — explicitly toolbox-free. Used by .github/workflows/chapel-ci.yml
# on stock ubuntu-latest runners after installing the Chapel 2.8.0 .deb. Builds
# mass-panic and the smoke binary in one shot.
chapel-build-ci:
cd chapel && chpl src/MassPanic.chpl src/Protocol.chpl src/Imaging.chpl src/Temporal.chpl -o mass-panic
cd chapel && chpl smoke/two_repo_smoke.chpl src/Protocol.chpl src/Imaging.chpl -o smoke/two_repo_smoke

# Chapel-side data-flow smoke test (<5s, single-locale, no Rust binary needed)
chapel-smoke: chapel-build-ci
./chapel/smoke/two_repo_smoke

# Verify the Rust↔Chapel CLI contract surface. Requires
# `cargo build --release` to have produced ./target/release/panic-attack.
chapel-contract-check:
./chapel/tests/contract_check.sh

# Aggregate-parity check: rayon assemblyline vs Chapel single-locale on a
# synthetic 2-repo corpus. Requires both binaries built.
chapel-rust-diff:
./chapel/tests/rayon_vs_chapel_diff.sh

# End-to-end single-locale exercise against a synthetic 2-repo manifest.
#
# True multi-locale (-nl 2 over real cluster nodes) requires Chapel built with
# CHPL_COMM=gasnet — the stock ubuntu .deb ships CHPL_COMM=none and rejects
# -nl >1. The v0 gate verifies the full mass-panic flow (discover → spawn
# panic-attack → write SystemImage JSON) on -nl 1; the cross-locale code path
# is staged for Wave 2 once the .deb story for multilocale Chapel is solved.
# Tracker: see chapel/README.md "Wave 2 follow-up".
chapel-e2e:
#!/usr/bin/env bash
set -euo pipefail
WORK=$(mktemp -d /tmp/chapel-e2e-XXXXXX)
trap 'rm -rf "$WORK"' EXIT
mkdir -p "$WORK/corpus/repo-alpha/src" "$WORK/corpus/repo-beta/src"
echo 'pub unsafe fn a() {}' > "$WORK/corpus/repo-alpha/src/lib.rs"
echo 'pub unsafe fn b() {}' > "$WORK/corpus/repo-beta/src/lib.rs"
for d in repo-alpha repo-beta; do (cd "$WORK/corpus/$d" && git init -q && git add -A && git -c user.email=ci@example.com -c user.name=ci commit -q -m init); done
./chapel/mass-panic --repoDirectory="$WORK/corpus" --numLocales=1 --quiet --outputDir="$WORK/out"
ls "$WORK/out"/system-image-*.json >/dev/null && echo "chapel-e2e: PASS (-nl 1 produced system-image JSON)"

# Parse-check every Chapel module (cheap canary; runs before chapel-build-ci)
chapel-parse-check:
cd chapel && chpl --parse-only src/MassPanic.chpl src/Protocol.chpl src/Imaging.chpl src/Temporal.chpl
cd chapel && chpl --parse-only smoke/two_repo_smoke.chpl src/Protocol.chpl src/Imaging.chpl

# Clean Chapel build artefacts
chapel-clean:
rm -f chapel/mass-panic
rm -f chapel/mass-panic chapel/smoke/two_repo_smoke

# Scan local repo tree with mass-panic (Chapel single-locale)
chapel-scan dir=env("HOME") + "/Documents/hyperpolymath-repos":
chapel-scan dir=(env("HOME") + "/Documents/hyperpolymath-repos"):
./chapel/mass-panic --repoDirectory={{dir}}

# Diff the two most recent mass-panic temporal snapshots
Expand Down
47 changes: 39 additions & 8 deletions chapel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Locale 0 (coordinator) Locale 1..N (workers)

## Prerequisites

- [Chapel](https://chapel-lang.org/) 2.3.0+
- [Chapel](https://chapel-lang.org/) 2.8.0+ (matches `chapel/Mason.toml`)
- `panic-attack` binary on PATH (or specify via `--panicAttackBin`)

## Build
Expand Down Expand Up @@ -70,7 +70,7 @@ chpl src/MassPanic.chpl src/Protocol.chpl src/Imaging.chpl src/Temporal.chpl -o
| `--panicAttackBin` | `panic-attack` | Path to panic-attack binary |
| `--mode` | `assail` | Operation mode (see above) |
| `--scheduler` | `static` | `static` (fast, not resumable) or `queue` (resumable, ~5–15% slower) |
| `--resume` | `false` | Only with `--scheduler=queue`: skip repos already marked "done" in the journal |
| `--resume` | `false` | Requires `--scheduler=queue`; combining with `--scheduler=static` exits with an error (static mode has no journal). Skips repos already marked "done" in the journal |
| `--journalDir` | `<outputDir>/journal` | Directory for queue-scheduler JSONL shards |
| `--incremental` | `true` | Skip unchanged repos via BLAKE3 |
| `--cacheFile` | | Fingerprint cache file path |
Expand Down Expand Up @@ -128,10 +128,12 @@ previously-completed repos and the freshly-scanned ones.
invocation with `--resume` reuses everything completed so far.
A locale crash during a multi-day sweep loses only the
currently-in-flight repo on that locale.
- **~5–15% slower** on clean runs. The dispatch overhead per task
(atomic fetch-add + one journal write) is per-repo instead of
being amortised across a `coforall` range. On a clean 10k-repo
sweep, expect queue mode to finish in ~1.10× the time of static.
- **~5–15% slower** on clean runs (estimate, not yet measured against a
full BoJ-estate corpus). The dispatch overhead per task (atomic
fetch-add + one journal write) is per-repo instead of being amortised
across a `coforall` range. On a clean 10k-repo sweep, expect queue
mode to finish in roughly ~1.10× the time of static. A defensible
empirical measurement is tracked as Wave 2 follow-up work.
- **Right for:** long interactive sweeps (GitHub-account scale or
larger), sweeps where at least one locale is on spot/preemptible
infrastructure, or any run where you expect to want to pause
Expand Down Expand Up @@ -193,7 +195,8 @@ The banner is suppressed under `--quiet`.

## Relationship to Rust assemblyline

The Chapel layer is **optional**. For single-machine scanning, use:
The Chapel layer is **optional** — a detachable harness on top of the
standalone Rust binary. For single-machine scanning, use:

```bash
panic-attack assemblyline /path/to/repos # rayon parallel
Expand All @@ -202,4 +205,32 @@ panic-attack image /path/to/repos # + imaging + temporal

Chapel adds multi-machine distribution for scanning at GitHub-account or
datacenter scale, where hundreds of machines each scan their partition of
repositories simultaneously.
repositories simultaneously. Removing `chapel/` entirely leaves the Rust
build green and the single-machine USB-stick experience intact.

The Chapel↔Rust contract is exposed via `panic-attack describe-contract`
(introduced for the chapel-cli-contract CI gate). Any external orchestrator
— Chapel mass-panic, Nextflow, Airflow, Slurm, a hand-rolled shell script —
can call it to discover accepted flags per mode and the report
`schema_version` without coupling itself to panic-attack source.

## Neuroscience analogy: fNIRS-inspired imaging

panic-attack applies functional Near-Infrared Spectroscopy (fNIRS) concepts
to codebase health mapping. The canonical mapping lives in
[`src/Imaging.chpl`](src/Imaging.chpl) header (lines 4-27) and is mirrored
here so the metaphor doesn't drift:

| fNIRS term | panic-attack equivalent |
|-----------------------|-------------------------------------------------------|
| Cortical region | Repository / directory / file |
| Blood oxygenation | Health score (inverse of risk) |
| Neural activation | Weak point density (findings per KLOC) |
| Hemodynamic response | Change velocity (how fast risk is changing) |
| Optode placement | Scanner coverage (which files were analysed) |
| Channel | Dependency / taint flow edge |
| Functional map | `SystemImage` |
| Time series | Temporal snapshot sequence in VeriSimDB |

When a new health metric is added, update both `Imaging.chpl` and this
table; CI does not enforce the mapping but reviewers should.
Loading
Loading