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.
Files changed (69) hide show
  1. gauntlet_evals-0.1.0/.github/CODEOWNERS +8 -0
  2. gauntlet_evals-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  3. gauntlet_evals-0.1.0/.github/dependabot.yml +16 -0
  4. gauntlet_evals-0.1.0/.github/workflows/ci.yml +202 -0
  5. gauntlet_evals-0.1.0/.github/workflows/pages.yml +64 -0
  6. gauntlet_evals-0.1.0/.github/workflows/release.yml +76 -0
  7. gauntlet_evals-0.1.0/.gitignore +22 -0
  8. gauntlet_evals-0.1.0/.htmlvalidate.mjs +29 -0
  9. gauntlet_evals-0.1.0/CHANGELOG.md +87 -0
  10. gauntlet_evals-0.1.0/CONTRIBUTING.md +62 -0
  11. gauntlet_evals-0.1.0/LICENSE +201 -0
  12. gauntlet_evals-0.1.0/Makefile +57 -0
  13. gauntlet_evals-0.1.0/PKG-INFO +383 -0
  14. gauntlet_evals-0.1.0/README.md +366 -0
  15. gauntlet_evals-0.1.0/SCOPE.md +124 -0
  16. gauntlet_evals-0.1.0/SECURITY.md +52 -0
  17. gauntlet_evals-0.1.0/action.yml +222 -0
  18. gauntlet_evals-0.1.0/docs/california-mapping.md +161 -0
  19. gauntlet_evals-0.1.0/examples/README.md +32 -0
  20. gauntlet_evals-0.1.0/examples/broken_target.py +29 -0
  21. gauntlet_evals-0.1.0/examples/cases/grounding.yaml +34 -0
  22. gauntlet_evals-0.1.0/package-lock.json +743 -0
  23. gauntlet_evals-0.1.0/package.json +17 -0
  24. gauntlet_evals-0.1.0/pyproject.toml +83 -0
  25. gauntlet_evals-0.1.0/src/gauntlet/__init__.py +20 -0
  26. gauntlet_evals-0.1.0/src/gauntlet/builtin_cases/__init__.py +0 -0
  27. gauntlet_evals-0.1.0/src/gauntlet/builtin_cases/adversarial.yaml +131 -0
  28. gauntlet_evals-0.1.0/src/gauntlet/builtin_cases/false_positive.yaml +55 -0
  29. gauntlet_evals-0.1.0/src/gauntlet/builtin_cases/golden.yaml +40 -0
  30. gauntlet_evals-0.1.0/src/gauntlet/builtin_cases/grounding.yaml +69 -0
  31. gauntlet_evals-0.1.0/src/gauntlet/builtin_cases/refusal.yaml +59 -0
  32. gauntlet_evals-0.1.0/src/gauntlet/cases.py +247 -0
  33. gauntlet_evals-0.1.0/src/gauntlet/cli.py +240 -0
  34. gauntlet_evals-0.1.0/src/gauntlet/drift.py +281 -0
  35. gauntlet_evals-0.1.0/src/gauntlet/evidence.py +210 -0
  36. gauntlet_evals-0.1.0/src/gauntlet/gates/__init__.py +10 -0
  37. gauntlet_evals-0.1.0/src/gauntlet/gates/adversarial.py +20 -0
  38. gauntlet_evals-0.1.0/src/gauntlet/gates/base.py +50 -0
  39. gauntlet_evals-0.1.0/src/gauntlet/gates/false_positive.py +26 -0
  40. gauntlet_evals-0.1.0/src/gauntlet/gates/golden.py +25 -0
  41. gauntlet_evals-0.1.0/src/gauntlet/gates/grounding.py +50 -0
  42. gauntlet_evals-0.1.0/src/gauntlet/gates/refusal.py +33 -0
  43. gauntlet_evals-0.1.0/src/gauntlet/inventory.py +150 -0
  44. gauntlet_evals-0.1.0/src/gauntlet/mapping.py +459 -0
  45. gauntlet_evals-0.1.0/src/gauntlet/py.typed +0 -0
  46. gauntlet_evals-0.1.0/src/gauntlet/report.py +476 -0
  47. gauntlet_evals-0.1.0/src/gauntlet/results.py +154 -0
  48. gauntlet_evals-0.1.0/src/gauntlet/site.py +1097 -0
  49. gauntlet_evals-0.1.0/src/gauntlet/targets.py +137 -0
  50. gauntlet_evals-0.1.0/src/gauntlet/toy/__init__.py +13 -0
  51. gauntlet_evals-0.1.0/src/gauntlet/toy/corpus.py +134 -0
  52. gauntlet_evals-0.1.0/src/gauntlet/toy/target.py +244 -0
  53. gauntlet_evals-0.1.0/tests/__init__.py +0 -0
  54. gauntlet_evals-0.1.0/tests/conftest.py +19 -0
  55. gauntlet_evals-0.1.0/tests/test_cases_schema.py +284 -0
  56. gauntlet_evals-0.1.0/tests/test_cli.py +242 -0
  57. gauntlet_evals-0.1.0/tests/test_docs_and_inventory.py +277 -0
  58. gauntlet_evals-0.1.0/tests/test_drift.py +182 -0
  59. gauntlet_evals-0.1.0/tests/test_evidence.py +180 -0
  60. gauntlet_evals-0.1.0/tests/test_gates.py +212 -0
  61. gauntlet_evals-0.1.0/tests/test_mapping.py +103 -0
  62. gauntlet_evals-0.1.0/tests/test_report.py +215 -0
  63. gauntlet_evals-0.1.0/tests/test_results.py +113 -0
  64. gauntlet_evals-0.1.0/tests/test_self_test_doctrine.py +60 -0
  65. gauntlet_evals-0.1.0/tests/test_site.py +686 -0
  66. gauntlet_evals-0.1.0/tests/test_targets.py +127 -0
  67. gauntlet_evals-0.1.0/tests/test_toy.py +140 -0
  68. gauntlet_evals-0.1.0/tools/a11y.mjs +109 -0
  69. gauntlet_evals-0.1.0/uv.lock +527 -0
@@ -0,0 +1,8 @@
1
+ * @ChelseaKR
2
+ /.github/ @ChelseaKR
3
+ /SECURITY.md @ChelseaKR
4
+ /pyproject.toml @ChelseaKR
5
+ /docs/california-mapping.md @ChelseaKR
6
+ /src/gauntlet/cases.py @ChelseaKR
7
+ /src/gauntlet/gates/ @ChelseaKR
8
+ /src/gauntlet/toy/ @ChelseaKR
@@ -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.