gauntlet-evals 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gauntlet_evals-0.1.0/.github/CODEOWNERS +8 -0
- gauntlet_evals-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +13 -0
- gauntlet_evals-0.1.0/.github/dependabot.yml +16 -0
- gauntlet_evals-0.1.0/.github/workflows/ci.yml +202 -0
- gauntlet_evals-0.1.0/.github/workflows/pages.yml +64 -0
- gauntlet_evals-0.1.0/.github/workflows/release.yml +76 -0
- gauntlet_evals-0.1.0/.gitignore +22 -0
- gauntlet_evals-0.1.0/.htmlvalidate.mjs +29 -0
- gauntlet_evals-0.1.0/CHANGELOG.md +87 -0
- gauntlet_evals-0.1.0/CONTRIBUTING.md +62 -0
- gauntlet_evals-0.1.0/LICENSE +201 -0
- gauntlet_evals-0.1.0/Makefile +57 -0
- gauntlet_evals-0.1.0/PKG-INFO +383 -0
- gauntlet_evals-0.1.0/README.md +366 -0
- gauntlet_evals-0.1.0/SCOPE.md +124 -0
- gauntlet_evals-0.1.0/SECURITY.md +52 -0
- gauntlet_evals-0.1.0/action.yml +222 -0
- gauntlet_evals-0.1.0/docs/california-mapping.md +161 -0
- gauntlet_evals-0.1.0/examples/README.md +32 -0
- gauntlet_evals-0.1.0/examples/broken_target.py +29 -0
- gauntlet_evals-0.1.0/examples/cases/grounding.yaml +34 -0
- gauntlet_evals-0.1.0/package-lock.json +743 -0
- gauntlet_evals-0.1.0/package.json +17 -0
- gauntlet_evals-0.1.0/pyproject.toml +83 -0
- gauntlet_evals-0.1.0/src/gauntlet/__init__.py +20 -0
- gauntlet_evals-0.1.0/src/gauntlet/builtin_cases/__init__.py +0 -0
- gauntlet_evals-0.1.0/src/gauntlet/builtin_cases/adversarial.yaml +131 -0
- gauntlet_evals-0.1.0/src/gauntlet/builtin_cases/false_positive.yaml +55 -0
- gauntlet_evals-0.1.0/src/gauntlet/builtin_cases/golden.yaml +40 -0
- gauntlet_evals-0.1.0/src/gauntlet/builtin_cases/grounding.yaml +69 -0
- gauntlet_evals-0.1.0/src/gauntlet/builtin_cases/refusal.yaml +59 -0
- gauntlet_evals-0.1.0/src/gauntlet/cases.py +247 -0
- gauntlet_evals-0.1.0/src/gauntlet/cli.py +240 -0
- gauntlet_evals-0.1.0/src/gauntlet/drift.py +281 -0
- gauntlet_evals-0.1.0/src/gauntlet/evidence.py +210 -0
- gauntlet_evals-0.1.0/src/gauntlet/gates/__init__.py +10 -0
- gauntlet_evals-0.1.0/src/gauntlet/gates/adversarial.py +20 -0
- gauntlet_evals-0.1.0/src/gauntlet/gates/base.py +50 -0
- gauntlet_evals-0.1.0/src/gauntlet/gates/false_positive.py +26 -0
- gauntlet_evals-0.1.0/src/gauntlet/gates/golden.py +25 -0
- gauntlet_evals-0.1.0/src/gauntlet/gates/grounding.py +50 -0
- gauntlet_evals-0.1.0/src/gauntlet/gates/refusal.py +33 -0
- gauntlet_evals-0.1.0/src/gauntlet/inventory.py +150 -0
- gauntlet_evals-0.1.0/src/gauntlet/mapping.py +459 -0
- gauntlet_evals-0.1.0/src/gauntlet/py.typed +0 -0
- gauntlet_evals-0.1.0/src/gauntlet/report.py +476 -0
- gauntlet_evals-0.1.0/src/gauntlet/results.py +154 -0
- gauntlet_evals-0.1.0/src/gauntlet/site.py +1097 -0
- gauntlet_evals-0.1.0/src/gauntlet/targets.py +137 -0
- gauntlet_evals-0.1.0/src/gauntlet/toy/__init__.py +13 -0
- gauntlet_evals-0.1.0/src/gauntlet/toy/corpus.py +134 -0
- gauntlet_evals-0.1.0/src/gauntlet/toy/target.py +244 -0
- gauntlet_evals-0.1.0/tests/__init__.py +0 -0
- gauntlet_evals-0.1.0/tests/conftest.py +19 -0
- gauntlet_evals-0.1.0/tests/test_cases_schema.py +284 -0
- gauntlet_evals-0.1.0/tests/test_cli.py +242 -0
- gauntlet_evals-0.1.0/tests/test_docs_and_inventory.py +277 -0
- gauntlet_evals-0.1.0/tests/test_drift.py +182 -0
- gauntlet_evals-0.1.0/tests/test_evidence.py +180 -0
- gauntlet_evals-0.1.0/tests/test_gates.py +212 -0
- gauntlet_evals-0.1.0/tests/test_mapping.py +103 -0
- gauntlet_evals-0.1.0/tests/test_report.py +215 -0
- gauntlet_evals-0.1.0/tests/test_results.py +113 -0
- gauntlet_evals-0.1.0/tests/test_self_test_doctrine.py +60 -0
- gauntlet_evals-0.1.0/tests/test_site.py +686 -0
- gauntlet_evals-0.1.0/tests/test_targets.py +127 -0
- gauntlet_evals-0.1.0/tests/test_toy.py +140 -0
- gauntlet_evals-0.1.0/tools/a11y.mjs +109 -0
- gauntlet_evals-0.1.0/uv.lock +527 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## Change
|
|
2
|
+
|
|
3
|
+
Describe the bounded behavior changed and the evidence supporting it.
|
|
4
|
+
|
|
5
|
+
## Gates
|
|
6
|
+
|
|
7
|
+
- [ ] `make verify` (ruff format, ruff lint, mypy strict, pytest with the 90% coverage gate)
|
|
8
|
+
- [ ] `make demo` (the gates run against the toy and a report renders)
|
|
9
|
+
- [ ] New or changed gate has a paired self-test proving it can fail (self-test doctrine)
|
|
10
|
+
- [ ] English and Spanish cases changed as peers, not one bolted onto the other
|
|
11
|
+
- [ ] No em dashes in prose; counts are emitted by the harness, not asserted
|
|
12
|
+
- [ ] Any SIMM/SAM/GC identifier added to docs was read against the source, or omitted and said so
|
|
13
|
+
- [ ] No claim of California approval, endorsement, or compliance
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: pip
|
|
4
|
+
directory: /
|
|
5
|
+
schedule:
|
|
6
|
+
interval: weekly
|
|
7
|
+
open-pull-requests-limit: 5
|
|
8
|
+
cooldown:
|
|
9
|
+
default-days: 7
|
|
10
|
+
- package-ecosystem: github-actions
|
|
11
|
+
directory: /
|
|
12
|
+
schedule:
|
|
13
|
+
interval: weekly
|
|
14
|
+
open-pull-requests-limit: 5
|
|
15
|
+
cooldown:
|
|
16
|
+
default-days: 7
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Merge-blocking CI. Least-privilege GITHUB_TOKEN, SHA-pinned actions, and the
|
|
2
|
+
# same gates run locally (`make verify`) plus supply-chain checks. Every action
|
|
3
|
+
# is pinned to a full 40-char commit SHA with its version in a trailing comment.
|
|
4
|
+
name: ci
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
pull_request:
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: ci-${{ github.ref }}
|
|
16
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
verify:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
23
|
+
with:
|
|
24
|
+
persist-credentials: false
|
|
25
|
+
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
26
|
+
with:
|
|
27
|
+
version: "0.11.29"
|
|
28
|
+
python-version: "3.12"
|
|
29
|
+
enable-cache: true
|
|
30
|
+
cache-dependency-glob: uv.lock
|
|
31
|
+
- name: Install locked environment
|
|
32
|
+
run: uv sync --frozen
|
|
33
|
+
- name: Verify (ruff, mypy strict, pytest with 90% coverage gate)
|
|
34
|
+
run: make verify
|
|
35
|
+
- name: Run the gates against the toy and render a report
|
|
36
|
+
run: make demo
|
|
37
|
+
|
|
38
|
+
# The documentation site, built and then checked without a browser: HTML
|
|
39
|
+
# conformance and the markup-level accessibility rules through html-validate,
|
|
40
|
+
# and the WCAG 2.0/2.1/2.2 A and AA rule sets through axe-core in a headless
|
|
41
|
+
# DOM. Nothing is served and nothing is deployed here; both checkers read files
|
|
42
|
+
# off disk. Deploying is a separate workflow that runs only on main.
|
|
43
|
+
site:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
47
|
+
with:
|
|
48
|
+
persist-credentials: false
|
|
49
|
+
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
50
|
+
with:
|
|
51
|
+
version: "0.11.29"
|
|
52
|
+
python-version: "3.12"
|
|
53
|
+
enable-cache: true
|
|
54
|
+
cache-dependency-glob: uv.lock
|
|
55
|
+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
56
|
+
with:
|
|
57
|
+
node-version: "22"
|
|
58
|
+
cache: npm
|
|
59
|
+
- name: Install locked environment
|
|
60
|
+
run: uv sync --frozen
|
|
61
|
+
- name: Build the site and check it (html-validate, axe-core, npm audit)
|
|
62
|
+
run: make pages
|
|
63
|
+
- name: The site build must produce byte-identical output twice
|
|
64
|
+
run: |
|
|
65
|
+
set -euo pipefail
|
|
66
|
+
find site -type f | sort | xargs shasum -a 256 > /tmp/site-first.txt
|
|
67
|
+
rm -rf site
|
|
68
|
+
make site
|
|
69
|
+
find site -type f | sort | xargs shasum -a 256 > /tmp/site-second.txt
|
|
70
|
+
diff /tmp/site-first.txt /tmp/site-second.txt
|
|
71
|
+
|
|
72
|
+
# Exercises the published composite action the way an external consumer would,
|
|
73
|
+
# from a checkout that is not on PATH and with no local environment set up.
|
|
74
|
+
# A gate failure must block, so this job also proves the blocking path.
|
|
75
|
+
action:
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
79
|
+
with:
|
|
80
|
+
persist-credentials: false
|
|
81
|
+
- name: Run the action against the built-in suites
|
|
82
|
+
id: gauntlet
|
|
83
|
+
uses: ./
|
|
84
|
+
with:
|
|
85
|
+
job-summary: "false"
|
|
86
|
+
- name: The action reported counts the harness produced
|
|
87
|
+
env:
|
|
88
|
+
PASSED: ${{ steps.gauntlet.outputs.passed }}
|
|
89
|
+
GATES_TOTAL: ${{ steps.gauntlet.outputs.gates-total }}
|
|
90
|
+
GATES_FAILED: ${{ steps.gauntlet.outputs.gates-failed }}
|
|
91
|
+
CASES_TOTAL: ${{ steps.gauntlet.outputs.cases-total }}
|
|
92
|
+
REPORT: ${{ steps.gauntlet.outputs.report-path }}
|
|
93
|
+
JSON: ${{ steps.gauntlet.outputs.json-path }}
|
|
94
|
+
run: |
|
|
95
|
+
set -euo pipefail
|
|
96
|
+
test "$PASSED" = "true"
|
|
97
|
+
test "$GATES_FAILED" = "0"
|
|
98
|
+
test "$GATES_TOTAL" -ge 1
|
|
99
|
+
test "$CASES_TOTAL" -ge 1
|
|
100
|
+
test -s "$REPORT"
|
|
101
|
+
test -s "$JSON"
|
|
102
|
+
grep -q "not approved or endorsed by, the State of California" "$REPORT"
|
|
103
|
+
- name: Run the action against the deliberately broken example target
|
|
104
|
+
id: broken
|
|
105
|
+
uses: ./
|
|
106
|
+
with:
|
|
107
|
+
working-directory: examples
|
|
108
|
+
cases: cases
|
|
109
|
+
target-callable: broken_target:make_target
|
|
110
|
+
results-path: broken-results.json
|
|
111
|
+
report-path: broken-evidence.md
|
|
112
|
+
json-path: broken-evidence.json
|
|
113
|
+
fail-on-gate-failure: "false"
|
|
114
|
+
job-summary: "false"
|
|
115
|
+
- name: The failing run is reported as failing, not swallowed
|
|
116
|
+
working-directory: examples
|
|
117
|
+
env:
|
|
118
|
+
PASSED: ${{ steps.broken.outputs.passed }}
|
|
119
|
+
GATES_FAILED: ${{ steps.broken.outputs.gates-failed }}
|
|
120
|
+
CASES_FAILED: ${{ steps.broken.outputs.cases-failed }}
|
|
121
|
+
run: |
|
|
122
|
+
set -euo pipefail
|
|
123
|
+
test "$PASSED" = "false"
|
|
124
|
+
test "$GATES_FAILED" -ge 1
|
|
125
|
+
test "$CASES_FAILED" -ge 1
|
|
126
|
+
grep -q "uncited answer" broken-evidence.md
|
|
127
|
+
|
|
128
|
+
package:
|
|
129
|
+
runs-on: ubuntu-latest
|
|
130
|
+
steps:
|
|
131
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
132
|
+
with:
|
|
133
|
+
persist-credentials: false
|
|
134
|
+
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
135
|
+
with:
|
|
136
|
+
version: "0.11.29"
|
|
137
|
+
python-version: "3.12"
|
|
138
|
+
- name: Build the wheel and sdist
|
|
139
|
+
run: uv build
|
|
140
|
+
|
|
141
|
+
dependency-scan:
|
|
142
|
+
runs-on: ubuntu-latest
|
|
143
|
+
steps:
|
|
144
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
145
|
+
with:
|
|
146
|
+
persist-credentials: false
|
|
147
|
+
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
148
|
+
with:
|
|
149
|
+
version: "0.11.29"
|
|
150
|
+
python-version: "3.12"
|
|
151
|
+
- name: Audit locked runtime dependencies
|
|
152
|
+
run: |
|
|
153
|
+
uv export --frozen --no-dev --no-emit-project --no-hashes > /tmp/runtime-requirements.txt
|
|
154
|
+
if test -s /tmp/runtime-requirements.txt; then
|
|
155
|
+
uvx --from pip-audit==2.10.1 pip-audit --strict --requirement /tmp/runtime-requirements.txt
|
|
156
|
+
else
|
|
157
|
+
echo "No runtime dependencies to audit."
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
secret-scan:
|
|
161
|
+
runs-on: ubuntu-latest
|
|
162
|
+
permissions:
|
|
163
|
+
contents: read
|
|
164
|
+
pull-requests: read # required on private repos: gitleaks-action lists the PR's commits
|
|
165
|
+
steps:
|
|
166
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
167
|
+
with:
|
|
168
|
+
fetch-depth: 0
|
|
169
|
+
persist-credentials: false
|
|
170
|
+
- uses: gitleaks/gitleaks-action@e0c47f4f8be36e29cdc102c57e68cb5cbf0e8d1e # v3.0.0
|
|
171
|
+
env:
|
|
172
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
173
|
+
|
|
174
|
+
sast:
|
|
175
|
+
runs-on: ubuntu-latest
|
|
176
|
+
steps:
|
|
177
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
178
|
+
with:
|
|
179
|
+
persist-credentials: false
|
|
180
|
+
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
181
|
+
with:
|
|
182
|
+
version: "0.11.29"
|
|
183
|
+
python-version: "3.12"
|
|
184
|
+
- name: Semgrep
|
|
185
|
+
run: uvx --from semgrep==1.168.0 semgrep scan --error --metrics off --config p/python src tests examples
|
|
186
|
+
|
|
187
|
+
zizmor:
|
|
188
|
+
runs-on: ubuntu-latest
|
|
189
|
+
permissions:
|
|
190
|
+
contents: read
|
|
191
|
+
steps:
|
|
192
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
193
|
+
with:
|
|
194
|
+
persist-credentials: false
|
|
195
|
+
- uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2
|
|
196
|
+
with:
|
|
197
|
+
# Code scanning / GitHub Advanced Security is not enabled on this
|
|
198
|
+
# private repo, so the default SARIF-upload path fails at the upload
|
|
199
|
+
# step. Emit plain GitHub Actions annotations instead; zizmor's own
|
|
200
|
+
# exit code still fails the job on real findings.
|
|
201
|
+
advanced-security: false
|
|
202
|
+
annotations: true
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Publish the documentation site to GitHub Pages.
|
|
2
|
+
#
|
|
3
|
+
# The site is a build artifact, not a tracked directory: `gauntlet site` renders it from
|
|
4
|
+
# the harness itself. The gate counts come from the suites the package loads, and the
|
|
5
|
+
# evidence excerpts are runs made against the in-repo toy target while the pages build.
|
|
6
|
+
# So this workflow needs no network, no secrets, and no data: the published pages are
|
|
7
|
+
# reproducible from the commit they were built at.
|
|
8
|
+
#
|
|
9
|
+
# The merge-blocking accessibility and conformance checks live in ci.yml, which runs on
|
|
10
|
+
# every pull request. This workflow only deploys what that already checked.
|
|
11
|
+
#
|
|
12
|
+
# Every action is pinned to a full 40-character commit SHA with its version in a
|
|
13
|
+
# trailing comment, resolved 2026-08-07 via `gh api repos/OWNER/REPO/commits/TAG`.
|
|
14
|
+
name: pages
|
|
15
|
+
|
|
16
|
+
on:
|
|
17
|
+
push:
|
|
18
|
+
branches: [main]
|
|
19
|
+
workflow_dispatch:
|
|
20
|
+
|
|
21
|
+
permissions: {}
|
|
22
|
+
|
|
23
|
+
concurrency:
|
|
24
|
+
group: pages
|
|
25
|
+
cancel-in-progress: false
|
|
26
|
+
|
|
27
|
+
jobs:
|
|
28
|
+
build:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
permissions:
|
|
31
|
+
contents: read
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
34
|
+
with:
|
|
35
|
+
persist-credentials: false
|
|
36
|
+
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
37
|
+
with:
|
|
38
|
+
version: "0.11.29"
|
|
39
|
+
python-version: "3.12"
|
|
40
|
+
enable-cache: true
|
|
41
|
+
cache-dependency-glob: uv.lock
|
|
42
|
+
- name: Install locked environment
|
|
43
|
+
run: uv sync --frozen
|
|
44
|
+
- name: Render the site from the harness
|
|
45
|
+
run: uv run gauntlet site --out site --generated "$(date -u +%F)"
|
|
46
|
+
- name: Fail if the render produced nothing
|
|
47
|
+
run: test -s site/index.html
|
|
48
|
+
- uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
|
|
49
|
+
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
|
|
50
|
+
with:
|
|
51
|
+
path: site
|
|
52
|
+
|
|
53
|
+
deploy:
|
|
54
|
+
needs: build
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
permissions:
|
|
57
|
+
pages: write
|
|
58
|
+
id-token: write
|
|
59
|
+
environment:
|
|
60
|
+
name: github-pages
|
|
61
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
62
|
+
steps:
|
|
63
|
+
- id: deployment
|
|
64
|
+
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Publishes a tagged version to PyPI through Trusted Publishing (OIDC), so no
|
|
2
|
+
# API token is ever created, stored, or rotated. PyPI verifies the claim that
|
|
3
|
+
# this workflow, in this repository, on this environment, produced the upload.
|
|
4
|
+
#
|
|
5
|
+
# The build job re-runs the same gates as `make verify` before anything is
|
|
6
|
+
# uploaded: a release that cannot pass CI must not reach PyPI. Artifacts are
|
|
7
|
+
# built once and handed to the publish job, so the files that were verified are
|
|
8
|
+
# byte-for-byte the files that get uploaded.
|
|
9
|
+
#
|
|
10
|
+
# One-time setup on PyPI (nothing to do here, and no secret to add):
|
|
11
|
+
# https://pypi.org/manage/account/publishing/ -> add a pending publisher
|
|
12
|
+
# PyPI project name: gauntlet-evals
|
|
13
|
+
# Owner: ChelseaKR
|
|
14
|
+
# Repository: gauntlet
|
|
15
|
+
# Workflow name: release.yml
|
|
16
|
+
# Environment name: pypi
|
|
17
|
+
name: release
|
|
18
|
+
|
|
19
|
+
on:
|
|
20
|
+
release:
|
|
21
|
+
types: [published]
|
|
22
|
+
workflow_dispatch:
|
|
23
|
+
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
|
|
27
|
+
concurrency:
|
|
28
|
+
group: release-${{ github.ref }}
|
|
29
|
+
cancel-in-progress: false
|
|
30
|
+
|
|
31
|
+
jobs:
|
|
32
|
+
build:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
36
|
+
with:
|
|
37
|
+
persist-credentials: false
|
|
38
|
+
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
39
|
+
with:
|
|
40
|
+
version: "0.11.29"
|
|
41
|
+
python-version: "3.12"
|
|
42
|
+
enable-cache: true
|
|
43
|
+
cache-dependency-glob: uv.lock
|
|
44
|
+
# `--locked` fails when uv.lock has drifted from pyproject.toml. `--frozen`
|
|
45
|
+
# (used in ci.yml) exits 0 in that case, so a release must not rely on it.
|
|
46
|
+
- name: Install locked environment, failing on lockfile drift
|
|
47
|
+
run: uv sync --locked
|
|
48
|
+
- name: Verify (ruff, mypy strict, pytest with 90% coverage gate)
|
|
49
|
+
run: make verify
|
|
50
|
+
- name: Build sdist and wheel
|
|
51
|
+
run: uv build
|
|
52
|
+
- name: Check distribution metadata
|
|
53
|
+
run: uvx --from 'twine@latest' twine check dist/*
|
|
54
|
+
- name: Upload built distributions
|
|
55
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
56
|
+
with:
|
|
57
|
+
name: dist
|
|
58
|
+
path: dist/
|
|
59
|
+
if-no-files-found: error
|
|
60
|
+
|
|
61
|
+
publish:
|
|
62
|
+
needs: build
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
environment:
|
|
65
|
+
name: pypi
|
|
66
|
+
url: https://pypi.org/p/gauntlet-evals
|
|
67
|
+
permissions:
|
|
68
|
+
id-token: write # the OIDC claim Trusted Publishing verifies; no token needed
|
|
69
|
+
steps:
|
|
70
|
+
- name: Download built distributions
|
|
71
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
72
|
+
with:
|
|
73
|
+
name: dist
|
|
74
|
+
path: dist/
|
|
75
|
+
- name: Publish to PyPI
|
|
76
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.pyc
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.venv/
|
|
5
|
+
.DS_Store
|
|
6
|
+
dist/
|
|
7
|
+
.coverage
|
|
8
|
+
coverage.xml
|
|
9
|
+
.mypy_cache/
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
.ruff_cache/
|
|
12
|
+
demo-results.json
|
|
13
|
+
demo-evidence.md
|
|
14
|
+
demo-evidence.json
|
|
15
|
+
demo-evidence-drift.md
|
|
16
|
+
gauntlet-results.json
|
|
17
|
+
gauntlet-evidence.md
|
|
18
|
+
gauntlet-evidence.json
|
|
19
|
+
|
|
20
|
+
# The documentation site is a build artifact, rendered by `make site`.
|
|
21
|
+
site/
|
|
22
|
+
node_modules/
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// HTML conformance and markup-level accessibility rules for the documentation site.
|
|
2
|
+
//
|
|
3
|
+
// Where a rule is waived or tightened, the reason is here rather than in a commit
|
|
4
|
+
// message: a waived rule with no reason beside it is indistinguishable from an
|
|
5
|
+
// oversight.
|
|
6
|
+
export default {
|
|
7
|
+
extends: [
|
|
8
|
+
"html-validate:recommended",
|
|
9
|
+
"html-validate:document",
|
|
10
|
+
"html-validate:a11y",
|
|
11
|
+
],
|
|
12
|
+
rules: {
|
|
13
|
+
// The WHATWG spec writes the doctype lowercase and HTML5 is case-insensitive here.
|
|
14
|
+
"doctype-style": ["error", { style: "lowercase" }],
|
|
15
|
+
// role="list" on a <ul> is redundant per the ARIA-in-HTML mapping, and html-validate
|
|
16
|
+
// is right about that. It is kept anyway: some of these lists carry list-style:none,
|
|
17
|
+
// and Safari with VoiceOver drops list semantics from a list styled that way unless
|
|
18
|
+
// the role is stated explicitly. The redundancy costs nothing; the lost semantics do.
|
|
19
|
+
"no-redundant-role": "off",
|
|
20
|
+
// Strict: every <th> must carry a scope, not only those in a table that mixes row
|
|
21
|
+
// and column headers. Every table here is a data table whose row header names what
|
|
22
|
+
// the row is about, and a cell read out without its row header is the failure this
|
|
23
|
+
// catches.
|
|
24
|
+
"wcag/h63": ["error", { strict: true }],
|
|
25
|
+
// The pages ship no script and no inline style attribute. Both stay errors, which is
|
|
26
|
+
// the default; they are named here so a future page cannot quietly introduce either.
|
|
27
|
+
"no-inline-style": "error",
|
|
28
|
+
},
|
|
29
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes will be documented here.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- California mapping (`docs/california-mapping.md`): a table mapping each gate to
|
|
10
|
+
the SIMM 5305-F (August 2025) items its results inform and the disclosure
|
|
11
|
+
content it supports, built by reading the source page by page. Cites SIMM
|
|
12
|
+
5305-F sections by their document structure, SAM 4986.2 and 4986.9, Government
|
|
13
|
+
Code 11549.64(b), and the genai.ca.gov disclosure page. Lists the identifiers
|
|
14
|
+
it could not verify and therefore omitted, and carries a prominent
|
|
15
|
+
aligned-to-not-approved-by notice.
|
|
16
|
+
- Python package skeleton (`src/gauntlet`), `pyproject.toml` (uv-compatible,
|
|
17
|
+
Apache-2.0, Python 3.12+), a strict YAML case-file schema with validation, and
|
|
18
|
+
a CLI with `gauntlet run` and `gauntlet report`.
|
|
19
|
+
- Five core gates as a library driven by YAML cases: grounding assertion,
|
|
20
|
+
adversarial suite (English and Spanish as peers, across system-prompt
|
|
21
|
+
override, role manipulation, jailbreak, prompt-leak, code-execution, and
|
|
22
|
+
Unicode/obfuscation), refusal and escalation drills at a 100% threshold,
|
|
23
|
+
false-positive guard, and golden-answer regression.
|
|
24
|
+
- Target adapters for any Python callable or HTTP endpoint, with a strict
|
|
25
|
+
response contract and no dependency on any model vendor.
|
|
26
|
+
- A deliberately breakable grounded-RAG toy target and a paired self-test for
|
|
27
|
+
every gate that injects the defect the gate exists to catch and asserts the
|
|
28
|
+
gate fails.
|
|
29
|
+
- Bilingual built-in suites for every gate. The counts are emitted by
|
|
30
|
+
`gauntlet inventory` rather than restated here.
|
|
31
|
+
- CI (SHA-pinned actions): `make verify` with a 90% coverage gate, wheel build,
|
|
32
|
+
dependency audit, secret scan, SAST, and workflow static analysis. Dependabot
|
|
33
|
+
with a 7-day cooldown, CODEOWNERS, SECURITY, CONTRIBUTING, and a PR template.
|
|
34
|
+
- Evidence pack (`gauntlet report`): one versioned structure rendered as
|
|
35
|
+
machine-readable JSON and as a human-readable document suitable for attaching
|
|
36
|
+
to a risk assessment. It states what was tested, what passed, what failed and
|
|
37
|
+
why, case counts per language, and what the harness does not establish, and it
|
|
38
|
+
carries the aligned-to-not-approved-by framing in the artifact itself. A run
|
|
39
|
+
with failures renders through the same sections as a clean one.
|
|
40
|
+
- Framework cross-reference inside the pack: each gate outcome is linked to the
|
|
41
|
+
specific SIMM 5305-F items its results inform, from `src/gauntlet/mapping.py`.
|
|
42
|
+
Only identifiers verified in Milestone 1 are cited, the unverified list is
|
|
43
|
+
reproduced in every pack, and a gate that maps to nothing verified is reported
|
|
44
|
+
as unmapped rather than given an invented link.
|
|
45
|
+
- Whole-run drift (`gauntlet report --baseline`): gates added and removed,
|
|
46
|
+
pass-rate deltas per gate and per language, cases newly failing and newly
|
|
47
|
+
passing, cases added and removed, and threshold changes. Deterministic and
|
|
48
|
+
free of timestamps, plus a `results_digest` that fingerprints behavior while
|
|
49
|
+
excluding the clock.
|
|
50
|
+
- `gauntlet inventory`: the gate inventory counted from the loaded suites, in
|
|
51
|
+
Markdown or JSON, with `--update` to regenerate the README's generated block.
|
|
52
|
+
A test fails if that block goes stale.
|
|
53
|
+
- A composite GitHub Action (`action.yml`) usable from any repository, with
|
|
54
|
+
documented inputs and outputs, SHA-pinned internals, no interpolation of
|
|
55
|
+
inputs into shell commands, and a CI job that exercises it on both the passing
|
|
56
|
+
and the failing path.
|
|
57
|
+
- `examples/`: a minimal external case file and a target factory, serving as
|
|
58
|
+
documentation and as the action's failure-path fixture.
|
|
59
|
+
- Documentation site (`gauntlet site`, `make site`): five static pages rendered
|
|
60
|
+
from the harness rather than typed. The gate inventory comes from
|
|
61
|
+
`build_inventory` over the suites that load, the same function `make inventory`
|
|
62
|
+
uses, so the site cannot carry a stale count. The evidence excerpts are real
|
|
63
|
+
runs made against the toy target while the pages build, healthy and with a
|
|
64
|
+
named defect injected, rendered through the same reporter a real run uses. No
|
|
65
|
+
network, no clock unless a date is passed, byte-identical on rebuild.
|
|
66
|
+
- An accessibility gate over the built pages (`make pages`): html-validate for
|
|
67
|
+
HTML conformance and the markup-level rules, axe-core in a headless DOM for
|
|
68
|
+
the WCAG 2.0/2.1/2.2 A and AA rule sets, plus structure and two-theme colour
|
|
69
|
+
contrast measured in pytest so `make verify` keeps a floor with no node
|
|
70
|
+
toolchain. A CI job runs all of it and proves the build is reproducible.
|
|
71
|
+
- A GitHub Pages workflow (`.github/workflows/pages.yml`) that publishes the
|
|
72
|
+
rendered site from `main`: empty top-level permissions, per-job scoping, and
|
|
73
|
+
SHA-pinned actions. Pages has to be set to build from GitHub Actions once, in
|
|
74
|
+
repository settings, before the first deploy can succeed.
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
|
|
78
|
+
- SCOPE.md placed the contractor GenAI disclosure duty in SAM 4986.2. It is in
|
|
79
|
+
SAM 4986.9; 4986.2 is the definitions section. Corrected, with the correction
|
|
80
|
+
recorded in the document rather than quietly applied.
|
|
81
|
+
|
|
82
|
+
### Notes
|
|
83
|
+
|
|
84
|
+
- Not a compliance certification. The State of California has not reviewed,
|
|
85
|
+
approved, endorsed, or certified this project.
|
|
86
|
+
- Nothing has been published to any package registry, and no badge implies
|
|
87
|
+
otherwise. Publication and any rename remain the owner's decision.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Read [SCOPE.md](SCOPE.md), [SECURITY.md](SECURITY.md), and
|
|
4
|
+
[docs/california-mapping.md](docs/california-mapping.md) before proposing
|
|
5
|
+
changes.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
make install
|
|
9
|
+
make verify # ruff format check, ruff lint, mypy strict, pytest with the coverage gate
|
|
10
|
+
make demo # gates against the toy, then both forms of the evidence pack
|
|
11
|
+
make inventory # regenerate the gate inventory block in the README
|
|
12
|
+
make pages # build the documentation site, then html-validate and axe-core over it
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Run `make verify` before pushing, and `make pages` as well if you touched
|
|
16
|
+
`src/gauntlet/site.py`. CI runs the same things, plus a wheel build, a dependency
|
|
17
|
+
audit, a secret scan, SAST, workflow static analysis, and a job that uses the
|
|
18
|
+
GitHub Action the way an external consumer would.
|
|
19
|
+
|
|
20
|
+
## Rules that are not negotiable
|
|
21
|
+
|
|
22
|
+
- **Every gate must be able to fail.** A new or changed gate needs a paired
|
|
23
|
+
self-test that injects the defect it catches and asserts the gate fails
|
|
24
|
+
(see `tests/test_self_test_doctrine.py`). A check that has never failed is not
|
|
25
|
+
evidence of health.
|
|
26
|
+
- **English and Spanish cases are peers.** Add or change them together; do not
|
|
27
|
+
bolt a translation onto an English-first suite.
|
|
28
|
+
- **Counts are counted.** Case totals, pass thresholds, and coverage are emitted
|
|
29
|
+
by the harness. Do not assert a count in prose that the harness does not
|
|
30
|
+
produce. The README's gate inventory is generated: change a suite, then run
|
|
31
|
+
`make inventory`. A test fails if the block is stale.
|
|
32
|
+
- **The evidence pack stays honest under failure.** A run with failures has to
|
|
33
|
+
read as easily as a clean one, and every section present in one must be
|
|
34
|
+
present in the other. `tests/test_report.py` enforces that.
|
|
35
|
+
- **Drift output stays deterministic.** No timestamps and no unstable ordering:
|
|
36
|
+
two runs that behaved identically must produce a byte-identical comparison.
|
|
37
|
+
- **No em dashes in prose.** A test scans the Markdown and the source for them.
|
|
38
|
+
- **No unverified framework citations.** Any SIMM 5305-F, SAM, or Government Code
|
|
39
|
+
identifier added to the docs must be read against the source first. If you
|
|
40
|
+
cannot verify it, omit it and say so, the way `docs/california-mapping.md`
|
|
41
|
+
already does. Add it to `UNVERIFIED_IDENTIFIERS` in
|
|
42
|
+
`src/gauntlet/mapping.py`, which a test then keeps out of every mapping row
|
|
43
|
+
and every evidence pack.
|
|
44
|
+
- **A gate that maps to nothing verified says so.** Do not invent a framework
|
|
45
|
+
link to make the cross-reference look complete.
|
|
46
|
+
- **No California approval or compliance claims.** The language is "aligned to",
|
|
47
|
+
never "approved by" or "compliant with". A test scans the Markdown, the
|
|
48
|
+
documentation site's source, and the rendered pages for the phrasings that
|
|
49
|
+
would break this.
|
|
50
|
+
- **The documentation site prints nothing it did not compute.** Gate counts come
|
|
51
|
+
from the inventory, evidence excerpts come from runs made while the pages
|
|
52
|
+
build, and the action's inputs and outputs are read from `action.yml`. A number
|
|
53
|
+
in site prose that no run produced fails a test unless it is added, with a
|
|
54
|
+
reason, to the reviewed list in `tests/test_site.py`.
|
|
55
|
+
- **The site stays accessible.** New markup has to pass html-validate and
|
|
56
|
+
axe-core in `make pages`, and any new colour has to be a token in both palettes
|
|
57
|
+
with its contrast pair measured in `tests/test_site.py`.
|
|
58
|
+
- **No network in tests.** The toy runs locally; the HTTP adapter is tested
|
|
59
|
+
against a loopback stub. Do not add a test that reaches the internet.
|
|
60
|
+
|
|
61
|
+
Do not add a model-vendor SDK, network fetching in a gate, or arbitrary code
|
|
62
|
+
execution without an explicit product-scope decision.
|