Skip to content

Commit 6bdb256

Browse files
Merge pull request #2 from hyperpolymath/feat/trope-build
refactor: Haec example checker to bash+jq (no Python)
2 parents 75cbc2c + cf21348 commit 6bdb256

7 files changed

Lines changed: 68 additions & 78 deletions

File tree

.github/workflows/trope-check.yml

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,19 @@
11
# SPDX-License-Identifier: MPL-2.0
22
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
33
#
4-
# Haec: validate that every example lowers to a well-formed Trope IR document.
4+
# Haec: every example lowers to a well-formed Trope IR document (bash + jq, no Python).
55
name: trope-check
6-
76
on:
87
push:
9-
paths:
10-
- 'design/**'
11-
- 'examples/**'
12-
- 'tests/**'
13-
- '.github/workflows/trope-check.yml'
8+
paths: ['design/**', 'examples/**', 'tests/**', '.github/workflows/trope-check.yml']
149
pull_request:
1510
workflow_dispatch:
16-
1711
permissions:
1812
contents: read
19-
2013
jobs:
2114
examples-lower-to-valid-ir:
2215
runs-on: ubuntu-latest
2316
steps:
2417
- uses: actions/checkout@v4
25-
- uses: actions/setup-python@v5
26-
with:
27-
python-version: '3.12'
28-
- name: Install jsonschema
29-
run: python -m pip install --upgrade jsonschema
3018
- name: Examples lower to valid Trope IR
31-
run: python3 tests/check-examples.py
19+
run: bash tests/check-examples.sh

AUDIT.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ The release-audit gate. Ground-truth by running the tools.
99
[cols="1,2,1",options="header"]
1010
|===
1111
| Check | Command | State 2026-06-22
12-
| Examples lower to valid IR | `just check` (`python3 tests/check-examples.py`) | PASS (3/3)
12+
| Examples lower to valid IR | `just check` (`bash tests/check-examples.sh`) | PASS (3/3)
1313
| Round-trip through the checker | `just haec-roundtrip` (needs sibling trope-checker) | PASS (verdicts + witness match)
1414
| Design artefacts present | grammar.ebnf, elaboration.adoc | PASS
1515
|===

README.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ compiler yet — `compiler/` and `lsp/` are named future work (`STATUS.adoc`).
129129
----
130130
just check # validate examples/*.ir.json against the vendored IR schema
131131
# full round-trip (verdicts) needs a checkout of the sibling trope-checker:
132-
python3 ../trope-checker/src/checker/tropecheck.py examples/survey-collapse.ir.json
132+
../trope-checker/src/idris2/build/exec/tropecheck examples/survey-collapse.ir.json
133133
----
134134

135135
== Licence

build/just/trope.just

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
# SPDX-License-Identifier: MPL-2.0
22
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
33
#
4-
# Haec (trope-particularity front-end) recipes. Imported by the root Justfile via
4+
# Haec (trope front-end) recipes. No Python. Imported by the root Justfile via
55
# import? "build/just/trope.just"
66

7-
# Validate that every example lowers to a well-formed Trope IR document.
7+
# Every example lowers to a well-formed Trope IR document (+ round-trip if the
8+
# sibling trope-checker is checked out/built).
89
check: haec-examples
910

1011
haec-examples:
11-
python3 tests/check-examples.py
12-
13-
# Full round-trip (verdicts) needs a checkout of the sibling trope-checker.
14-
haec-roundtrip:
15-
@for f in examples/*.ir.json; do echo "== $f =="; python3 ../trope-checker/src/checker/tropecheck.py "$f" || true; done
12+
bash tests/check-examples.sh

design/trope-ir.schema.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"$schema": "https://json-schema.org/draft/2020-12/schema",
33
"$id": "https://github.com/hyperpolymath/trope-checker/schemas/trope-ir.schema.json",
44
"title": "Trope IR",
5-
"$comment": "SPDX-FileCopyrightText: © 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>. SPDX-License-Identifier: MPL-2.0. The language-neutral interchange of the trope-particularity calculus (spec/calculus.adoc). This is the canonical PREVENT profile (DEPLOY_VARIANT=prevent, R-2026-06-22): the three deceptive grade values (fate=Falsified, bond=Misbound, merge=Conflated) and the untagged merge are MALFORMED here and are rejected at validation with the offending coordinate named. The grade shape is the dependent record Sigma(fate). Bond(fate.bearer) x Merge; the if/then/else on bond makes bearer-incoherent grades unrepresentable (HC-4). The semantics (Idris2 src/) carries the COMPLETE lattices including the deceptive bottoms; the detect-profile schema (trope-ir.detect.schema.json) permits them and defers the check to the verdict.",
5+
"$comment": "SPDX-FileCopyrightText: © 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>. SPDX-License-Identifier: MPL-2.0. The language-neutral interchange of the trope-particularity calculus (spec/calculus.adoc). This is the canonical PREVENT profile (DEPLOY_VARIANT=prevent, R-2026-06-22): the three deceptive grade values (fate=Falsified, bond=Misbound, merge=Conflated) and the untagged merge are MALFORMED here and are rejected at validation with the offending coordinate named. The grade shape is the dependent record Sigma(fate). Bond(fate.bearer) x Merge; the if/then/else on bond makes bearer-incoherent grades unrepresentable (HC-4). The semantics (Idris2 src/) carries the COMPLETE lattices including the deceptive bottoms; the detect profile (calculus §6.1) permits them and defers the check to the verdict; it is documented in the calculus, not shipped as a separate schema here.",
66
"description": "A labelled DAG: nodes are property-instances (field-presence + type tag), edges are effect applications carrying a loss-shape grade, and a use-model node carries the floor. The checker is a pure function from this document to a verdict.",
77
"type": "object",
88
"required": ["version", "profile", "nodes", "edges", "use_model"],
@@ -14,7 +14,7 @@
1414
"profile": {
1515
"type": "string",
1616
"const": "prevent",
17-
"$comment": "This schema is the prevent profile. A detect-profile document MUST use trope-ir.detect.schema.json and declare profile=detect."
17+
"$comment": "This schema is the prevent profile. The detect profile (calculus §6.1) is documented there, not shipped as a separate schema; this repository serves prevent."
1818
},
1919
"nodes": {
2020
"type": "array",

tests/check-examples.py

Lines changed: 0 additions & 52 deletions
This file was deleted.

tests/check-examples.sh

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: MPL-2.0
3+
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
4+
#
5+
# Every Haec example lowers to a well-formed Trope IR document. Pure bash + jq,
6+
# no Python. Self-contained: structural checks against the vendored IR contract.
7+
# The full round-trip (verdicts) runs when the sibling trope-checker is present.
8+
set -uo pipefail
9+
10+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
11+
EX="$ROOT/examples"
12+
SIB="$ROOT/../trope-checker"
13+
G="\033[32m"; R="\033[31m"; Y="\033[33m"; O="\033[0m"
14+
fail=0
15+
command -v jq >/dev/null || { echo "jq is required"; exit 2; }
16+
17+
# 1. structural: each *.ir.json is well-formed and has the required IR shape.
18+
for f in "$EX"/*.ir.json; do
19+
if ! jq -e '.version and .profile and .nodes and .edges and .use_model' "$f" >/dev/null 2>&1; then
20+
echo -e "${R}FAIL${O} $(basename "$f"): not a well-formed Trope IR document"; fail=1
21+
fi
22+
done
23+
24+
# 2. every example in the manifest pairs a program with its IR.
25+
while read -r e; do
26+
prog=$(jq -r '.program' <<<"$e"); ir=$(jq -r '.ir' <<<"$e")
27+
[ -f "$EX/$prog" ] || { echo -e "${R}FAIL${O} missing program $prog"; fail=1; }
28+
[ -f "$EX/$ir" ] || { echo -e "${R}FAIL${O} missing IR $ir"; fail=1; }
29+
[ "$fail" = 0 ] && echo -e "${G}ok${O} $prog$ir (well-formed IR, expects $(jq -r '.expect' <<<"$e"))"
30+
done < <(jq -c '.examples[]' "$EX/EXAMPLES.json")
31+
32+
# 3. drift guard: the vendored IR schema must match the canonical one if present.
33+
if [ -f "$SIB/schemas/trope-ir.schema.json" ]; then
34+
if diff -q "$ROOT/design/trope-ir.schema.json" "$SIB/schemas/trope-ir.schema.json" >/dev/null; then
35+
echo -e "${G}ok${O} vendored design/trope-ir.schema.json matches the canonical checker schema"
36+
else
37+
echo -e "${R}FAIL${O} vendored IR schema has drifted from trope-checker/schemas/trope-ir.schema.json"; fail=1
38+
fi
39+
else
40+
echo -e "${Y}note${O} sibling trope-checker not checked out; skipping schema-drift guard"
41+
fi
42+
43+
# 4. full round-trip: verdicts via the sibling Idris2 checker, if built.
44+
BIN="$SIB/src/idris2/build/exec/tropecheck"
45+
if [ -x "$BIN" ]; then
46+
while read -r e; do
47+
ir=$(jq -r '.ir' <<<"$e"); exp=$(jq -r '.expect' <<<"$e")
48+
got=$("$BIN" "$EX/$ir" | awk '{print $1}')
49+
if [ "$got" = "$exp" ]; then echo -e "${G}ok${O} round-trip $ir$got"
50+
else echo -e "${R}FAIL${O} round-trip $ir: got $got, expected $exp"; fail=1; fi
51+
done < <(jq -c '.examples[]' "$EX/EXAMPLES.json")
52+
else
53+
echo -e "${Y}note${O} sibling tropecheck binary not built; skipping verdict round-trip (run 'just trope-build' in ../trope-checker)"
54+
fi
55+
56+
if [ "$fail" = 0 ]; then echo -e "\n${G}examples: all lower to valid Trope IR${O}"; else echo -e "\n${R}examples: failures${O}"; fi
57+
exit "$fail"

0 commit comments

Comments
 (0)