Repository navigation
Releases
GOPAL 2.0.0
Breaking. One input format goes away and three policies get stricter. If you
already send canonical metrics.* names, nothing here affects you.
Rename these
| Send this | Instead of |
|---|---|
metrics.content_safety.score |
evaluation.content_safety.score, evaluation.content_safety_score, content_safety.score, content_safety_score |
metrics.fairness.score |
evaluation.fairness.score, evaluation.fairness_score, fairness_score |
metrics.risk_management.score |
evaluation.risk_management.score, evaluation.risk_management_score, risk_management_score |
metrics.toxicity.score |
evaluation.toxicity_score, content_safety.toxicity_score |
metrics.toxicity.max_toxicity |
summary.toxicity_values.max_toxicity, content_safety.max_toxicity |
metrics.model_card.completeness |
documentation.model_card.completeness_score, documentation.model_card.completeness |
metrics.patient_safety.score |
evaluation.patient_safety.score |
metrics.clinical_validation.score |
evaluation.clinical_validation.score |
metrics.risk_assessment.score |
evaluation.risk_assessment.score |
metrics.audit_logging.completeness |
governance.audit_logging.completeness_score |
A retired name is not an error, it is silence: the metric reads as absent
and a policy that requires it denies. So a missed rename shows up as a system
that stops passing, never one that wrongly passes.
The retirement was the easy half
Carrying two spellings for the same number had been holding disagreeing halves
of the same file in apparent agreement. Removing it turned each disagreement
into a failing test.
international/eu_ai_act/v1/transparencyapproved systems nobody had
tested.allowrequirednot has_high_toxicity, and that rule is undefined
when no reading was supplied. Undefined negates to true, so a fully documented
system that had never been measured returnedallow: trueagainst Article 13.
The policy had one test, on empty input, which denies for want of
documentation long before toxicity is reached. That is how it survived.loan_evaluation/fair_lendingdenied canonical input. Nine reads never
consulted the alias table, so canonical names scored-1and failed
everything.- Three reports contradicted their own verdict, reading a legacy path with a
fallback of0while the decision read the canonical name. - Three reports deleted themselves when a metric was absent, because one
undefined value inside an object makes the whole object undefined.
Unmeasured is now visible, and never a pass
Reports say null where nobody measured. 0 made an unmeasured system
indistinguishable from one that scored zero, and on a toxicity scale reported it
as the cleanest possible.
Removed
global.v1.common.common_rules and global.v1.common.compliance, whole
packages: 16 functions, no importer anywhere. Published policy count is
unchanged at 92, because both were libraries.
Also fixed
Four examples that could not run, a FAQ teaching an input format that no longer
exists plus two opa commands that never worked, and a diagram rendering
PlantUML warnings into the picture.
Honest note on process
docs/COMPATIBILITY.md states a two-minor deprecation window. It was not
served: 1.4.0 and 1.5.0 were never released, so no consumer met the
deprecation in a shipped version. A deliberate call given the adoption level,
recorded as an exception rather than smoothed over. The table above is what you
get instead.
Full detail in CHANGELOG.md.
v1.3.1
Corrections and gates. Two policy fixes, four new checks, and the first release Zenodo will archive.
Fixed
transparency cited the wrong Article. It referenced "Article 52, Transparency obligations for certain AI systems". In the adopted Regulation, Article 52 is "Procedure" (GPAI systemic-risk notification) and transparency obligations are Article 50 — and this repository already cited Article 52(1) correctly elsewhere, so both were present at once.
The correction is not a rename. The policy scores documentation completeness and a toxicity threshold, which is not a test of Article 50 either. It now cites Article 13 alone, the coverage matrix loses a false Article 50 tick, and the Article 13 row drops to a warning noting that Article 13 requires instructions for use with specified content and a completeness score is a proxy rather than a test. Claimed coverage goes down.
FERPA consent was a placeholder. It checked a status flag and a scope list. 34 CFR §99.30 requires consent to be signed and dated by the parent or eligible student and to specify the records, the purpose and the recipient. A consent permitting a transcript to go to a named university for admissions also cleared sending it to a data broker for marketing. All four elements are now checked.
Two vacuous-truth fail-opens in the same file: every over an empty collection is true, so a request for no records at all was approved by both allow branches.
The README banner said 96 policies directly above a panel saying 91, including in the <desc> a screen reader announces.
Added
scripts/extract-input-fields.shderives each policy'sinputfields from its AST. The hand-written comments had drifted to 22 of 98 policies readinginputand declaring nothing. It handles theobject.get(input, ["a","b"], default)form used 284 times here, and infers each field's kind from the literal it is measured against.scripts/check-test-coverage.shfails CI when a policy has no test or no empty-input test. Every policy has had both since v1.3.0; this makes it a gate rather than a snapshot.- Model-card coverage analysis — how far a standard Hugging Face model card gets you against the 184 declared inputs the shipped checks read: 5 directly, 24 partially, 155 not prompted for. Generated, with a written reason per field, and CI-checked.
CITATION.cffand.zenodo.json— GitHub now offers a Cite this repository button.
Changed
OPA and Regal are pinned, in CI and now in the release workflow. Both installed latest, and OPA 1.20.0 panics with illegal value on any ordering comparison against a decimal zero — a pattern four shared helpers in global/v1/common use as a score fallback. opa test aborted with exit code 2 rather than failing a test. Reported upstream with a fix as open-policy-agent/opa#9099.
check-version-refs.sh now also checks published counts against the generated coverage data.
Verification
806 tests, 196 files linted with no violations, coverage data current, all 91 policies carrying a test and an empty-input test, version and count references agreeing, model-card analysis current, all playground sample verdicts matching, and 20 bundles built with 0 failures.
Install
gh release download v1.3.1 --pattern 'gopal-international-eu_ai_act-*.tar.gz'
opa eval -b gopal-international-eu_ai_act-1.3.1.tar.gz \
--input system.json 'data.international.eu_ai_act.v1.transparency.allow'Full changelog: https://github.com/Principled-Evolution/gopal/blob/v1.3.1/CHANGELOG.md
GOPAL 1.3.0
Per-framework OPA bundles are attached to this release. Take the 29 EU AI Act policies without the aviation or FERPA ones:
gh release download v1.3.0 --pattern 'gopal-international-eu_ai_act-*.tar.gz'
opa eval -b gopal-international-eu_ai_act-1.3.0.tar.gz \
--input model_card.json \
'data.international.eu_ai_act.v1.transparency.allow'Each bundle is self-contained: it carries the shared libraries its policies import, so it evaluates with no other GOPAL files present. 19 framework bundles plus gopal-all, with a checksums.txt. The EU AI Act bundle is 24K against 56K for the whole library.
Because each bundle includes the shared libraries, their roots overlap and OPA will not load two side by side — use gopal-all when you need more than one framework.
Three fail-opens fixed
All the same Rego mistake: an undefined value is not false. All three mutation-verified, meaning the defect was reintroduced and named tests were watched to go red.
global/v1/common/fairness produced no verdict at all for a biased system. gender_bias_detected(_) := false was a third complete definition of the function rather than a fallback, so any input satisfying a real rule produced both true and false:
eval_conflict_error: functions must not produce multiple outputs for same inputs
The EU AI Act fairness policy therefore returned nothing, not a denial, for exactly the systems it exists to catch. It survived because the tests mock the two helpers they depend on, so the real functions were never exercised.
eu_ai_act/v1/eu_fairness passed unmeasured systems. default toxicity_below_threshold := true meant an input of {"metrics": {}} was reported compliant with Articles 10 and 15 — and so was a system measured at 0.9 toxicity, because the partial rule is undefined in both cases.
An empty-input test does not catch this: with no input the bias defaults deny first and the toxicity path is unreachable. It takes a partially populated input.
bfs/v1/loan_evaluation/fair_lending reported that nothing had failed. No default is_compliant := false, so on empty input the verdict was undefined and failed_evaluations came back empty. A loan model with no evidence at all produced an empty failure list.
Every policy is now tested
Was 75 of 98 files. All 91 policies that reach a verdict assert they deny empty input, and the suite goes from 604 to 769 tests.
The four shared libraries in global/v1/common and helper_functions have tests for the first time, documenting the fallback direction of each helper. That is what makes the toxicity defect legible: toxicity_score returns 0.0 for unreadable input, which is the permissive answer, while fairness_score returns 0.0, which is the denying one.
Coverage data you can query
docs/coverage/coverage.json is generated from the .rego files and checked in CI, so it cannot disagree with them:
# Which policies need a metric I do not collect yet?
jq -r '.frameworks[].policies[]
| select(.required_metrics | index("metrics.content_safety.score"))
| .path' docs/coverage/coverage.jsonThe policy count went down, on purpose
91 policies plus 7 shared libraries, not 96. Seven files define no decision rule, so they reach no verdict; they are libraries the real policies import, and counting them as policies overstated coverage. Corrected in the README, the diagrams, pyproject.toml, and all four translated READMEs.
Also
- A GitHub Actions example that fails the build on non-compliance, distinguishing compliant, non-compliant, and failed-to-evaluate. A policy that reached no verdict has told you nothing.
- Roadmap: dropped the vetoed California SB-1047 successor; added EU GDPR scoped to the articles an input document can actually evidence rather than the whole regulation.
Full detail in CHANGELOG.md.
GOPAL 1.2.0
Every EU AI Act obligation in the coverage matrix is now implemented, and there are no stub policies left anywhere in the library. The test suite went from 401 to 604.
The stubs are gone
Measured by whether a policy references its input at all, 22 of the 29 policies under international/eu_ai_act/v1/ were placeholders: allow := false, a "not yet implemented" message, no field checks. They were counted in the headline policy total and marked human_oversight.rego or provider_obligations.rego found one line.
All 22 are now real policies with sibling tests. The EU AI Act matrix has 30 implemented rows and no scaffolds.
What the new policies encode
The conditional structure of each Article, because that is where the compliance answer lives and it is what a flattened policy gets confidently wrong.
Article 5 prohibitions are cumulative. 5(1)(b) requires a recognised vulnerability basis, plus material distortion of behaviour, plus significant harm. Serving a vulnerable group is not exploiting one. 5(1)(d) carves out systems supporting a human assessment grounded in objective verifiable facts, and both halves of that carve-out are required. 5(1)(e) turns on untargeted collection rather than on facial recognition as such. 5(1)(g) exempts dataset labelling and law enforcement categorisation.
Article 5(1)(h) plus 5(3). The prohibition admits a closed list of objectives, and Article 5(3) still requires prior judicial or independent administrative authorisation on top of one. A permitted objective without that authorisation is prohibited, which is the case most likely to be got wrong.
Article 26 deployer duties are partly conditional. 26(7) engages on workplace deployment, 26(11) on the system affecting natural persons, and 26(4) applies only so far as the deployer controls the input data.
Article 43 route selection. Internal control under Annex VI is available for Annex III point 1 biometric systems only where the harmonised standards were applied. Otherwise a notified body under Annex VII is required.
Article 48 conditionals. A digital-only system needs a digitally accessible marking rather than a physically legible one, and where a notified body was involved its identification number must appear.
Article 49(2). A provider concluding its Annex III system is not high-risk under Article 6(3) must still register that assessment. It is the easiest limb to skip entirely, precisely because the provider has decided the system falls outside.
Article 53(2) open-source exemption. It reaches the technical documentation and downstream information duties only, falls away completely for a model with systemic risk, and never touches the copyright policy or the training content summary. Each of those three boundaries has its own test.
Article 51(2) systemic risk. The presumption bites above 10^25 cumulative training FLOPs, a Commission decision designates a model regardless of compute, and Article 52(1) notification is tested separately from the four Article 55 obligations.
Positioning
The README now leads with the frameworks covered rather than a policy count, and the count badges are gone. Someone looking for FCA or SRA coverage wants to know whether their domain is there, not how many policies exist in total.
Getting started
git clone --branch v1.2.0 https://github.com/Principled-Evolution/gopal.git
cd gopal/examples/eu-ai-act-transparency
./run.shCheck an Article 5 prohibition:
opa eval -d helper_functions -d international/eu_ai_act \
--input my_system.json \
"data.international.eu_ai_act.v1.prohibited_practices.biometric_identification.report"The report names the specific attributes, bases, sources or outstanding limbs that produced a finding rather than returning a bare denial.
Known gaps, stated plainly
22 of the 96 policies have no sibling test file, down from 44. The remaining gap is almost entirely industry_specific/education/v1/. A policy without a test is not necessarily wrong; it is unverified, which is a different claim from the coverage matrices' implementation marks. CI runs opa test, so anything added from here is enforced.
Not legal advice
GOPAL encodes published regulatory requirements as executable interpretations. A passing policy is not a compliance determination. If you think a rule misreads an Article, open an issue.
GOPAL 1.1.0
Eleven new policies, three fail-open fixes, and every placeholder in the library retired. 85 policies to 96, and the test suite from 226 to 401.
New: UK AI governance
international/uk/v1/ covers the five cross-sectoral principles from the pro-innovation white paper (CP 815), plus the automated decision-making regime in UK GDPR Articles 22A to 22D, substituted by section 80 of the Data (Use and Access) Act 2025 and in force 5 February 2026.
Two things this encodes deliberately:
- The five principles are non-statutory. There is no UK AI Act. They are addressed to regulators rather than imposed on firms, and every policy says so in its metadata. Treat a pass as assurance evidence, not a statutory determination.
- The proportionality qualifiers are real branches. Principle 2 says appropriate transparency, so a documented explainability method is required only for high-impact systems. Principle 5 says where appropriate, so contestability engages only where the system affects individuals or carries material harm potential.
Fairness is anchored to the nine protected characteristics in Equality Act 2010 s.4, and the report names which ones went untested. A US protected-class list would silently pass.
The ADM policy matters because the UK has genuinely diverged: a solely automated significant decision on ordinary personal data is now permitted with the Article 22C safeguards, where GDPR Article 22 prohibited it absent an exception. Special category data stays restricted under 22B.
New: UK financial services
industry_specific/bfs/v1/ gains PRA SS1/23 across all five principles, with independent-validation recency proportionate to the assigned risk tier and vendor models in scope, and FCA Consumer Duty (PRIN 2A) covering the three cross-cutting obligations, the four retail outcomes, plain-language explainability for customer-facing AI, and SM&CR named senior manager accountability.
Neither regulator has introduced AI-specific rules. Both supervise AI through these existing frameworks, so the policies test the existing obligations rather than a separate AI regime.
model_risk.rego is now the US counterpart: SR 11-7 and OCC 2011-12 pillars plus BCBS 239 data lineage. A UK-regulated firm uses uk_ss1_23_model_risk, a US-regulated one uses model_risk.
New: legal services vertical
industry_specific/legal/v1/ is a new vertical, added because legal is one of the few sectors where AI misuse has already produced published regulatory and judicial consequences rather than hypothetical risk.
- Citation verification before filing, recorded and attributable to a named individual. This is the R (Ayinde) v Haringey LBC failure mode.
- Client confidentiality and privilege, including the public-consumer-assistant case that can waive privilege outright. No other control compensates for that one.
- Competence and supervision, with risk assessment before adoption and client disclosure where AI use is material.
Sourced from the SRA warning notice on misuse of AI, the BSB's May 2026 guidance, and the judiciary's guidance for judicial office holders.
Fixed: three fail-opens
Each is the same family of Rego defect, where an undefined value is not the same as false.
international/nist/v1/ai_600_1returned allow for a system with no governance data at all.object.get(input, "governance", {})substituted an empty object, and a bare reference to a defined{}succeeds. The orchestrator now delegates to thegovern,map,measureandmanagepolicies it had always imported and never called, each of which carries its own default deny.industry_specific/healthcare/v1/diagnostic_safetyreturned allow for an input carrying no evaluation scores. A missing score made the threshold helpers undefined, so the metric dropped silently out offailed_evaluations. Score and threshold lookups now use the nestedobject.getform, so absence fails.- Report metrics across the new policies use explicit rule defaults. Without one, a failed control makes
control_passedundefined,compose_reportrejects the metrics, and the report disappears exactly when it is needed.
Changed: no more placeholders
All seven placeholder policies are now real implementations with tests. They had been counted in the headline total while returning an unconditional denial, which overstated coverage. Four had no test file at all.
eu_ai_act/prohibited_practices/manipulation now encodes Article 5(1)(a) as the cumulative test it is: technique, plus material distortion of behaviour, plus significant harm. Recital 29 lawful persuasion is not caught.
Changed: CI actually runs the tests
The workflow ran opa check and regal lint but never opa test, so the repo's policy tests were going unexecuted. That is why the NIST fail-open survived. opa test is now a required step.
Line endings are normalised with a .gitattributes; eight .rego files had CRLF, so opa fmt on any of them produced a whole-file diff. It is now a no-op repo-wide.
Getting started
git clone --branch v1.1.0 https://github.com/Principled-Evolution/gopal.git
cd gopal/examples/eu-ai-act-transparency
./run.shEvaluate a UK automated decision:
opa eval -d helper_functions -d international/uk \
--input my_decision.json \
"data.international.uk.v1.automated_decision_making.report"See the UK coverage matrix for what is implemented, article by article, including a table of where the UK regime diverges from the EU.
Known gaps, stated plainly
44 of the 96 policies still have no sibling test file, mostly in international/eu_ai_act/v1/ and industry_specific/education/v1/. A policy without a test is not necessarily wrong; it is unverified, which is a different claim from the coverage matrices' implementation marks. Now that CI runs opa test, tests added from here are enforced.
Several EU AI Act and NIST rows remain scaffolds. The coverage matrices mark each one honestly.
Not legal advice
GOPAL encodes published regulatory and governance requirements as executable interpretations. A passing policy is not a compliance determination. If you think a rule misreads a regulation, open an issue.
GOPAL 1.0.0
First tagged release. VERSION and the README badge have declared the 1.0.0 line since July 2025, and this tag makes it resolvable, so you can pin to a release instead of a commit SHA.
What's in it
85 policies, plus 37 test files, for 124 .rego files in total.
International frameworks (43)
| Framework | Policies |
|---|---|
| EU AI Act (2024/1689) | 29 |
| NIST AI RMF and AI 600-1 | 5 |
| FAA Part 107 and Remote ID | 2 |
| EASA Regulation 2019/947 and SORA | 2 |
| RTCA DO-365 and ISO 21384 | 2 |
| ICAO Doc 10019 | 1 |
| Brazil AI Governance Bill | 1 |
| India Digital Policy | 1 |
Industry verticals (29)
| Vertical | Policies |
|---|---|
| Education: FERPA, COPPA, proctoring, grading | 12 |
| Aviation: airworthiness, autonomy, data, operations | 12 |
| Healthcare: patient and diagnostic safety | 2 |
| Banking and financial services: model risk, fair lending | 2 |
| Automotive: vehicle safety integration | 1 |
Cross-cutting (13)
9 global policies covering accountability, fairness, transparency, explainability, content safety, risk management and security, and 4 operational policies covering AIOps scalability, cost efficiency, InfoSec and corporate governance.
Getting started
git clone --branch v1.0.0 https://github.com/Principled-Evolution/gopal.git
cd gopal/examples/eu-ai-act-transparency
./run.shOr evaluate your own input against a single framework:
opa eval -d international/eu_ai_act/v1 \
--input my_ai_system.json \
"data.international.eu_ai_act.v1.transparency.allow"The coverage matrices list what each framework implements, article by article.
What pinning to this tag gets you
Every framework lives under v1/. When a regulation is amended, the new reading ships as v2/ alongside the old one rather than replacing it, so a pin keeps evaluating the same way. See COMPATIBILITY.md for the versioning model.
Policies are plain Rego with no wrapper, so they load into whatever OPA deployment you already run: opa eval in CI, Conftest, or an OPA server. Proprietary rules go in a git-ignored custom/ tree that evaluates alongside the public set.
Verified at this tag with OPA 1.2.0 and Regal 0.42.0: opa check clean, and 124 files linted with no violations. Both run in CI on every pull request.
Not legal advice
GOPAL encodes published regulatory and governance requirements as executable interpretations. It is not legal advice, and a passing policy is not a compliance determination. If you think a rule misreads a regulation, open an issue. Those disagreements are the point of doing this in the open.