Skip to content

fix(ci): drop invalid timeout-minutes from reusable-workflow calls (p… #678

fix(ci): drop invalid timeout-minutes from reusable-workflow calls (p…

fix(ci): drop invalid timeout-minutes from reusable-workflow calls (p… #678

# SPDX-License-Identifier: MPL-2.0

Check failure on line 1 in .github/workflows/architecture-enforcement.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/architecture-enforcement.yml

Invalid workflow file

(Line: 106, Col: 5): 'timeout-minutes' is already defined
# Architecture Enforcement: Idris2 ONLY for logic, Zig for FFI, bindings for wrappers
name: Architecture Enforcement
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
on:
pull_request:
push:
branches: [main, develop]
permissions:
contents: read
# Cancel superseded runs on the same ref so a rapid sequence of pushes
# to a feature branch does not pile up jobs against the Pro-plan 40-job
# concurrent ceiling. Each new push to a ref supersedes its predecessor.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
enforce-idris-only:
runs-on: ubuntu-latest
# Fast grep checks; if this exceeds 5 minutes something is wrong.
timeout-minutes: 5
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- name: Block unsafe patterns in Rust safe_*.rs wrappers
run: |
# safe_*.rs files are legitimate FFI wrappers around Proven.Safe.*
# Idris modules (see bindings/rust/README.md). The filename itself
# is not the violation; the violation is reimplemented logic, which
# surfaces as panic-prone Rust idioms in the wrapper body.
#
# This step asserts the body-shape rule from ADR-008: wrappers may
# only call FFI / compose other safe wrappers, never panic.
shopt -s nullglob
violations=0
for f in bindings/rust/src/safe_*.rs; do
if grep -nE '\.unwrap\(\)|\.expect\(|panic!\(|unreachable!\(|todo!\(|unimplemented!\(' "$f"; then
echo "❌ $f contains panic-prone idioms forbidden in FFI wrappers"
violations=$((violations + 1))
fi
done
if [ "$violations" -gt 0 ]; then
echo ""
echo "SOLUTION: FFI wrappers must propagate errors, not panic."
echo "Logic that needs unwrap/expect/panic belongs in src/Proven/*.idr,"
echo "with the binding returning the Result via FFI."
exit 1
fi
echo "✅ Rust safe_*.rs wrappers contain no panic-prone idioms"
- name: Block non-FFI Zig code
run: |
# Zig is allowed in three places only:
# 1. ffi/zig/ — the FFI bridge layer
# 2. bindings/zig/ — the Zig language binding (necessarily Zig)
# 3. bindings/ephapax/ — the ephapax FFI bridge (Zig adapter layer)
# Anywhere else under bindings/*/src is a violation.
STRAY=$(find bindings/*/src -name "*.zig" -type f 2>/dev/null \
| grep -vE '^bindings/(zig|ephapax)/' \
|| true)
if [ -n "$STRAY" ]; then
echo "❌ ERROR: Zig files found outside ffi/zig/, bindings/zig/, bindings/ephapax/"
echo "Zig is restricted to the FFI bridge, the Zig binding, and the ephapax adapter."
echo "$STRAY"
exit 1
fi
echo "✅ Zig code correctly limited to ffi/zig/, bindings/zig/, bindings/ephapax/"
- name: Verify Idris modules exist for claimed features
run: |
# Check that bindings don't claim features without Idris backing
# This is a placeholder - actual check would parse binding manifests
echo "✅ Idris module verification (manual review required)"
- name: Block unwrap/getExn/panic in bindings
run: |
# Scan all language bindings for unsafe patterns
echo "Scanning for unsafe patterns in bindings..."
# Rust: unwrap, expect, panic
if grep -r "\.unwrap()" bindings/rust/src/ 2>/dev/null | grep -v "/tests/"; then
echo "❌ Found unwrap() in Rust bindings (outside tests)"
exit 1
fi
# AffineScript: previously this checked bindings/rescript/src/ for
# ReScript-specific panic idioms (getExn, Obj.magic). ReScript was
# banned 2026-04-30 (estate-wide migration to AffineScript) and
# the binding was ported to bindings/affinescript/src/ with .afs
# files using the Zig FFI directly (no JS-FFI ReScript scaffolding).
# The remaining panic-prone-pattern catches are handled by the
# cross-binding "No panic-prone patterns in bindings" step in
# e2e.yml (Aspect — Safety Invariants).
echo "✅ No unsafe patterns found in bindings"
# The legacy `hypatia-scan` job that bootstrapped its own checkout of
# hyperpolymath/hypatia and built the Elixir scanner has been removed —
# see `.github/workflows/hypatia-scan.yml`, which is the canonical wrapper
# around hyperpolymath/standards's `hypatia-scan-reusable.yml`. The legacy
# job broke after hypatia's `elixir-scanner/` directory was relocated, and
# maintaining two parallel scanner invocations created drift; the reusable
# is the single source of truth.
verify-idris-build:
runs-on: ubuntu-latest
timeout-minutes: 15
# Docker pull (snazzybucket/idris2) + Idris2 totality check across all
# Proven.Safe.* modules. Local oracle runs in ~8 minutes; 20-minute
# budget covers cold cache and the occasional Docker Hub slowdown.
timeout-minutes: 20
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- name: Build Idris2 modules (via container)
run: |
# Use the snazzybucket/idris2 container so we don't pay a 10-minute
# bootstrap on every job. The container is the same one used locally
# to verify proven#9 before merge.
docker run --rm \
-v "$PWD:/proj" \
-w /proj \
snazzybucket/idris2:latest \
sh -c 'idris2 --build proven.ipkg' || {
echo "❌ Idris2 build failed - proofs are broken"
exit 1
}
echo "✅ Idris2 modules build successfully with totality checking"